/*

   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */

package org.apache.batik.gvt;

import java.awt.Shape;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.batik.gvt.event.GraphicsNodeChangeAdapter;
import org.apache.batik.gvt.event.GraphicsNodeChangeEvent;
import org.apache.batik.ext.awt.image.renderable.Filter;

This class tracks the changes on a GVT tree
Author:Thomas DeWeese
Version:$Id: UpdateTracker.java 1831635 2018-05-15 13:33:47Z ssteiner $
/** * This class tracks the changes on a GVT tree * * @author <a href="mailto:Thomas.DeWeeese@Kodak.com">Thomas DeWeese</a> * @version $Id: UpdateTracker.java 1831635 2018-05-15 13:33:47Z ssteiner $ */
public class UpdateTracker extends GraphicsNodeChangeAdapter { Map dirtyNodes = null; Map fromBounds = new HashMap(); protected static Rectangle2D NULL_RECT = new Rectangle(); public UpdateTracker(){ }
Tells whether the GVT tree has changed.
/** * Tells whether the GVT tree has changed. */
public boolean hasChanged() { return (dirtyNodes != null); }
Returns the list of dirty areas on GVT.
/** * Returns the list of dirty areas on GVT. */
public List getDirtyAreas() { if (dirtyNodes == null) return null; List ret = new LinkedList(); Set keys = dirtyNodes.keySet(); for (Object key : keys) { WeakReference gnWRef = (WeakReference) key; GraphicsNode gn = (GraphicsNode) gnWRef.get(); // GraphicsNode srcGN = gn; // if the weak ref has been cleared then this node is no // longer part of the GVT tree (and the change should be // reflected in some ancestor that should also be in the // dirty list). if (gn == null) continue; AffineTransform oat; oat = (AffineTransform) dirtyNodes.get(gnWRef); if (oat != null) { oat = new AffineTransform(oat); } Rectangle2D srcORgn = (Rectangle2D) fromBounds.remove(gnWRef); Rectangle2D srcNRgn = null; AffineTransform nat = null; if (!(srcORgn instanceof ChngSrcRect)) { // For change srcs don't use the new bounds of parent node. srcNRgn = gn.getBounds(); nat = gn.getTransform(); if (nat != null) nat = new AffineTransform(nat); } // System.out.println("Rgns: " + srcORgn + " - " + srcNRgn); // System.out.println("ATs: " + oat + " - " + nat); do { // f.invalidateCache(oRng); // f.invalidateCache(nRng); // f = gn.getEnableBackgroundGraphicsNodeRable(false); // (need to push rgn through filter chain if any...) // f.invalidateCache(oRng); // f.invalidateCache(nRng); gn = gn.getParent(); if (gn == null) break; // We reached the top of the tree Filter f = gn.getFilter(); if (f != null) { srcNRgn = f.getBounds2D(); nat = null; } // Get the parent's current Affine AffineTransform at = gn.getTransform(); // Get the parent's Affine last time we rendered. gnWRef = gn.getWeakReference(); AffineTransform poat = (AffineTransform) dirtyNodes.get(gnWRef); if (poat == null) poat = at; if (poat != null) { if (oat != null) oat.preConcatenate(poat); else oat = new AffineTransform(poat); } if (at != null) { if (nat != null) nat.preConcatenate(at); else nat = new AffineTransform(at); } } while (true); if (gn == null) { // We made it to the root graphics node so add them. // System.out.println // ("Adding: " + oat + " - " + nat + "\n" + // srcORgn + "\n" + srcNRgn + "\n"); // <!> Shape oRgn = srcORgn; if ((oRgn != null) && (oRgn != NULL_RECT)) { if (oat != null) oRgn = oat.createTransformedShape(srcORgn); // System.err.println("GN: " + srcGN); // System.err.println("Src: " + oRgn.getBounds2D()); ret.add(oRgn); } if (srcNRgn != null) { Shape nRgn = srcNRgn; if (nat != null) nRgn = nat.createTransformedShape(srcNRgn); if (nRgn != null) ret.add(nRgn); } } } fromBounds.clear(); dirtyNodes.clear(); return ret; }
This returns the dirty region for gn in the coordinate system given by at.
Params:
  • gn – Node tree to return dirty region for.
  • at – Affine transform to coordinate space to accumulate dirty regions in.
/** * This returns the dirty region for gn in the coordinate system * given by <code>at</code>. * @param gn Node tree to return dirty region for. * @param at Affine transform to coordinate space to accumulate * dirty regions in. */
public Rectangle2D getNodeDirtyRegion(GraphicsNode gn, AffineTransform at) { WeakReference gnWRef = gn.getWeakReference(); AffineTransform nat = (AffineTransform)dirtyNodes.get(gnWRef); if (nat == null) nat = gn.getTransform(); if (nat != null) { at = new AffineTransform(at); at.concatenate(nat); } Filter f= gn.getFilter(); Rectangle2D ret = null; if (gn instanceof CompositeGraphicsNode) { CompositeGraphicsNode cgn = (CompositeGraphicsNode)gn; for (Object aCgn : cgn) { GraphicsNode childGN = (GraphicsNode) aCgn; Rectangle2D r2d = getNodeDirtyRegion(childGN, at); if (r2d != null) { if (f != null) { // If we have a filter and a change region // Update our full filter extents. Shape s = at.createTransformedShape(f.getBounds2D()); ret = s.getBounds2D(); break; } if ((ret == null) || (ret == NULL_RECT)) ret = r2d; //else ret = ret.createUnion(r2d); else ret.add(r2d); } } } else { ret = (Rectangle2D)fromBounds.remove(gnWRef); if (ret == null) { if (f != null) ret = f.getBounds2D(); else ret = gn.getBounds(); } else if (ret == NULL_RECT) ret = null; if (ret != null) ret = at.createTransformedShape(ret).getBounds2D(); } return ret; } public Rectangle2D getNodeDirtyRegion(GraphicsNode gn) { return getNodeDirtyRegion(gn, new AffineTransform()); }
Receives notification of a change to a GraphicsNode.
Params:
  • gnce – The event object describing the GraphicsNode change.
/** * Receives notification of a change to a GraphicsNode. * @param gnce The event object describing the GraphicsNode change. */
public void changeStarted(GraphicsNodeChangeEvent gnce) { // System.out.println("A node has changed for: " + this); GraphicsNode gn = gnce.getGraphicsNode(); WeakReference gnWRef = gn.getWeakReference(); boolean doPut = false; if (dirtyNodes == null) { dirtyNodes = new HashMap(); doPut = true; } else if (!dirtyNodes.containsKey(gnWRef)) doPut = true; if (doPut) { AffineTransform at = gn.getTransform(); if (at != null) at = (AffineTransform)at.clone(); else at = new AffineTransform(); dirtyNodes.put(gnWRef, at); } GraphicsNode chngSrc = gnce.getChangeSrc(); Rectangle2D rgn = null; if (chngSrc != null) { // A child node is moving in the tree so assign it's dirty // regions to this node before it moves. Rectangle2D drgn = getNodeDirtyRegion(chngSrc); if (drgn != null) rgn = new ChngSrcRect(drgn); } else { // Otherwise just use gn's current region. rgn = gn.getBounds(); } // Add this dirty region to any existing dirty region. Rectangle2D r2d = (Rectangle2D)fromBounds.remove(gnWRef); if (rgn != null) { if ((r2d != null) && (r2d != NULL_RECT)) { // System.err.println("GN: " + gn); // System.err.println("R2d: " + r2d); // System.err.println("Rgn: " + rgn); //r2d = r2d.createUnion(rgn); r2d.add(rgn); // System.err.println("Union: " + r2d); } else r2d = rgn; } // if ((gn instanceof CompositeGraphicsNode) && // (r2d.getWidth() > 200)) { // new Exception("Adding Large: " + gn).printStackTrace(); // } // Store the bounds for the future. if (r2d == null) r2d = NULL_RECT; fromBounds.put(gnWRef, r2d); } static class ChngSrcRect extends Rectangle2D.Float { ChngSrcRect(Rectangle2D r2d) { super((float)r2d.getX(), (float)r2d.getY(), (float)r2d.getWidth(), (float)r2d.getHeight()); } }
Clears the tracker.
/** * Clears the tracker. */
public void clear() { dirtyNodes = null; } }