001: /*
002:
003: Licensed to the Apache Software Foundation (ASF) under one or more
004: contributor license agreements. See the NOTICE file distributed with
005: this work for additional information regarding copyright ownership.
006: The ASF licenses this file to You under the Apache License, Version 2.0
007: (the "License"); you may not use this file except in compliance with
008: the License. You may obtain a copy of the License at
009:
010: http://www.apache.org/licenses/LICENSE-2.0
011:
012: Unless required by applicable law or agreed to in writing, software
013: distributed under the License is distributed on an "AS IS" BASIS,
014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: See the License for the specific language governing permissions and
016: limitations under the License.
017:
018: */
019:
020: package org.apache.batik.gvt;
021:
022: import java.awt.Shape;
023: import java.awt.Rectangle;
024: import java.awt.geom.AffineTransform;
025: import java.awt.geom.Rectangle2D;
026: import java.lang.ref.WeakReference;
027: import java.util.HashMap;
028: import java.util.Iterator;
029: import java.util.LinkedList;
030: import java.util.List;
031: import java.util.Map;
032: import java.util.Set;
033:
034: import org.apache.batik.gvt.event.GraphicsNodeChangeAdapter;
035: import org.apache.batik.gvt.event.GraphicsNodeChangeEvent;
036: import org.apache.batik.ext.awt.image.renderable.Filter;
037:
038: /**
039: * This class tracks the changes on a GVT tree
040: *
041: * @author <a href="mailto:Thomas.DeWeeese@Kodak.com">Thomas DeWeese</a>
042: * @version $Id: UpdateTracker.java 479559 2006-11-27 09:46:16Z dvholten $
043: */
044: public class UpdateTracker extends GraphicsNodeChangeAdapter {
045:
046: Map dirtyNodes = null;
047: Map fromBounds = new HashMap();
048: protected static Rectangle2D NULL_RECT = new Rectangle();
049:
050: public UpdateTracker() {
051: }
052:
053: /**
054: * Tells whether the GVT tree has changed.
055: */
056: public boolean hasChanged() {
057: return (dirtyNodes != null);
058: }
059:
060: /**
061: * Returns the list of dirty areas on GVT.
062: */
063: public List getDirtyAreas() {
064: if (dirtyNodes == null)
065: return null;
066:
067: List ret = new LinkedList();
068: Set keys = dirtyNodes.keySet();
069: Iterator i = keys.iterator();
070: while (i.hasNext()) {
071: WeakReference gnWRef = (WeakReference) i.next();
072: GraphicsNode gn = (GraphicsNode) gnWRef.get();
073: // GraphicsNode srcGN = gn;
074:
075: // if the weak ref has been cleared then this node is no
076: // longer part of the GVT tree (and the change should be
077: // reflected in some ancestor that should also be in the
078: // dirty list).
079: if (gn == null)
080: continue;
081:
082: AffineTransform oat;
083: oat = (AffineTransform) dirtyNodes.get(gnWRef);
084: if (oat != null) {
085: oat = new AffineTransform(oat);
086: }
087:
088: Rectangle2D srcORgn = (Rectangle2D) fromBounds
089: .remove(gnWRef);
090:
091: Rectangle2D srcNRgn = null;
092: AffineTransform nat = null;
093: if (!(srcORgn instanceof ChngSrcRect)) {
094: // For change srcs don't use the new bounds of parent node.
095: srcNRgn = gn.getBounds();
096: nat = gn.getTransform();
097: if (nat != null)
098: nat = new AffineTransform(nat);
099: }
100:
101: // System.out.println("Rgns: " + srcORgn + " - " + srcNRgn);
102: // System.out.println("ATs: " + oat + " - " + nat);
103: do {
104: // f.invalidateCache(oRng);
105: // f.invalidateCache(nRng);
106:
107: // f = gn.getEnableBackgroundGraphicsNodeRable(false);
108: // (need to push rgn through filter chain if any...)
109: // f.invalidateCache(oRng);
110: // f.invalidateCache(nRng);
111:
112: gn = gn.getParent();
113: if (gn == null)
114: break; // We reached the top of the tree
115:
116: Filter f = gn.getFilter();
117: if (f != null) {
118: srcNRgn = f.getBounds2D();
119: nat = null;
120: }
121:
122: // Get the parent's current Affine
123: AffineTransform at = gn.getTransform();
124: // Get the parent's Affine last time we rendered.
125: gnWRef = gn.getWeakReference();
126: AffineTransform poat = (AffineTransform) dirtyNodes
127: .get(gnWRef);
128: if (poat == null)
129: poat = at;
130: if (poat != null) {
131: if (oat != null)
132: oat.preConcatenate(poat);
133: else
134: oat = new AffineTransform(poat);
135: }
136:
137: if (at != null) {
138: if (nat != null)
139: nat.preConcatenate(at);
140: else
141: nat = new AffineTransform(at);
142: }
143: } while (true);
144:
145: if (gn == null) {
146: // We made it to the root graphics node so add them.
147: // System.out.println
148: // ("Adding: " + oat + " - " + nat + "\n" +
149: // srcORgn + "\n" + srcNRgn + "\n");
150: // <!>
151: Shape oRgn = srcORgn;
152: if ((oRgn != null) && (oRgn != NULL_RECT)) {
153: if (oat != null)
154: oRgn = oat.createTransformedShape(srcORgn);
155: // System.err.println("GN: " + srcGN);
156: // System.err.println("Src: " + oRgn.getBounds2D());
157: ret.add(oRgn);
158: }
159:
160: if (srcNRgn != null) {
161: Shape nRgn = srcNRgn;
162: if (nat != null)
163: nRgn = nat.createTransformedShape(srcNRgn);
164: if (nRgn != null)
165: ret.add(nRgn);
166: }
167: }
168: }
169:
170: fromBounds.clear();
171: dirtyNodes.clear();
172: return ret;
173: }
174:
175: /**
176: * This returns the dirty region for gn in the coordinate system
177: * given by <code>at</code>.
178: * @param gn Node tree to return dirty region for.
179: * @param at Affine transform to coordinate space to accumulate
180: * dirty regions in.
181: */
182: public Rectangle2D getNodeDirtyRegion(GraphicsNode gn,
183: AffineTransform at) {
184: WeakReference gnWRef = gn.getWeakReference();
185: AffineTransform nat = (AffineTransform) dirtyNodes.get(gnWRef);
186: if (nat == null)
187: nat = gn.getTransform();
188: if (nat != null) {
189: at = new AffineTransform(at);
190: at.concatenate(nat);
191: }
192:
193: Filter f = gn.getFilter();
194: Rectangle2D ret = null;
195: if (gn instanceof CompositeGraphicsNode) {
196: CompositeGraphicsNode cgn = (CompositeGraphicsNode) gn;
197: Iterator iter = cgn.iterator();
198:
199: while (iter.hasNext()) {
200: GraphicsNode childGN = (GraphicsNode) iter.next();
201: Rectangle2D r2d = getNodeDirtyRegion(childGN, at);
202: if (r2d != null) {
203: if (f != null) {
204: // If we have a filter and a change region
205: // Update our full filter extents.
206: Shape s = at.createTransformedShape(f
207: .getBounds2D());
208: ret = s.getBounds2D();
209: break;
210: }
211: if ((ret == null) || (ret == NULL_RECT))
212: ret = r2d;
213: //else ret = ret.createUnion(r2d);
214: else
215: ret.add(r2d);
216: }
217: }
218: } else {
219: ret = (Rectangle2D) fromBounds.remove(gnWRef);
220: if (ret == null) {
221: if (f != null)
222: ret = f.getBounds2D();
223: else
224: ret = gn.getBounds();
225: } else if (ret == NULL_RECT)
226: ret = null;
227: if (ret != null)
228: ret = at.createTransformedShape(ret).getBounds2D();
229: }
230: return ret;
231: }
232:
233: public Rectangle2D getNodeDirtyRegion(GraphicsNode gn) {
234: return getNodeDirtyRegion(gn, new AffineTransform());
235: }
236:
237: /**
238: * Receives notification of a change to a GraphicsNode.
239: * @param gnce The event object describing the GraphicsNode change.
240: */
241: public void changeStarted(GraphicsNodeChangeEvent gnce) {
242: // System.out.println("A node has changed for: " + this);
243: GraphicsNode gn = gnce.getGraphicsNode();
244: WeakReference gnWRef = gn.getWeakReference();
245:
246: boolean doPut = false;
247: if (dirtyNodes == null) {
248: dirtyNodes = new HashMap();
249: doPut = true;
250: } else if (!dirtyNodes.containsKey(gnWRef))
251: doPut = true;
252:
253: if (doPut) {
254: AffineTransform at = gn.getTransform();
255: if (at != null)
256: at = (AffineTransform) at.clone();
257: else
258: at = new AffineTransform();
259: dirtyNodes.put(gnWRef, at);
260: }
261:
262: GraphicsNode chngSrc = gnce.getChangeSrc();
263: Rectangle2D rgn = null;
264: if (chngSrc != null) {
265: // A child node is moving in the tree so assign it's dirty
266: // regions to this node before it moves.
267: Rectangle2D drgn = getNodeDirtyRegion(chngSrc);
268: if (drgn != null)
269: rgn = new ChngSrcRect(drgn);
270: } else {
271: // Otherwise just use gn's current region.
272: rgn = gn.getBounds();
273: }
274: // Add this dirty region to any existing dirty region.
275: Rectangle2D r2d = (Rectangle2D) fromBounds.remove(gnWRef);
276: if (rgn != null) {
277: if ((r2d != null) && (r2d != NULL_RECT)) {
278: // System.err.println("GN: " + gn);
279: // System.err.println("R2d: " + r2d);
280: // System.err.println("Rgn: " + rgn);
281: //r2d = r2d.createUnion(rgn);
282: r2d.add(rgn);
283: // System.err.println("Union: " + r2d);
284: } else
285: r2d = rgn;
286: }
287:
288: // if ((gn instanceof CompositeGraphicsNode) &&
289: // (r2d.getWidth() > 200)) {
290: // new Exception("Adding Large: " + gn).printStackTrace();
291: // }
292:
293: // Store the bounds for the future.
294: if (r2d == null)
295: r2d = NULL_RECT;
296: fromBounds.put(gnWRef, r2d);
297: }
298:
299: class ChngSrcRect extends Rectangle2D.Float {
300: ChngSrcRect(Rectangle2D r2d) {
301: super ((float) r2d.getX(), (float) r2d.getY(), (float) r2d
302: .getWidth(), (float) r2d.getHeight());
303: }
304: }
305:
306: /**
307: * Clears the tracker.
308: */
309: public void clear() {
310: dirtyNodes = null;
311: }
312: }
|