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: package org.apache.batik.gvt.filter;
020:
021: import java.awt.Shape;
022: import java.awt.geom.AffineTransform;
023: import java.awt.geom.NoninvertibleTransformException;
024: import java.awt.geom.Rectangle2D;
025: import java.awt.image.RenderedImage;
026: import java.awt.image.renderable.RenderContext;
027: import java.util.Iterator;
028: import java.util.List;
029: import java.util.ArrayList;
030:
031: import org.apache.batik.ext.awt.image.CompositeRule;
032: import org.apache.batik.ext.awt.image.PadMode;
033: import org.apache.batik.ext.awt.image.renderable.AbstractRable;
034: import org.apache.batik.ext.awt.image.renderable.AffineRable8Bit;
035: import org.apache.batik.ext.awt.image.renderable.CompositeRable8Bit;
036: import org.apache.batik.ext.awt.image.renderable.Filter;
037: import org.apache.batik.ext.awt.image.renderable.PadRable8Bit;
038: import org.apache.batik.gvt.CompositeGraphicsNode;
039: import org.apache.batik.gvt.GraphicsNode;
040:
041: /**
042: * This implementation of RenderableImage will render its input
043: * GraphicsNode into a BufferedImage upon invokation of one of its
044: * createRendering methods.
045: *
046: * @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
047: * @version $Id: BackgroundRable8Bit.java 479559 2006-11-27 09:46:16Z dvholten $
048: */
049: public class BackgroundRable8Bit extends AbstractRable {
050:
051: /**
052: * GraphicsNode this image can render
053: */
054: private GraphicsNode node;
055:
056: /**
057: * Returns the <tt>GraphicsNode</tt> rendered by this image
058: */
059: public GraphicsNode getGraphicsNode() {
060: return node;
061: }
062:
063: /**
064: * Sets the <tt>GraphicsNode</tt> this image should render
065: */
066: public void setGraphicsNode(GraphicsNode node) {
067: if (node == null) {
068: throw new IllegalArgumentException();
069: }
070:
071: this .node = node;
072: }
073:
074: /**
075: * @param node The GraphicsNode this image should represent
076: */
077: public BackgroundRable8Bit(GraphicsNode node) {
078: if (node == null)
079: throw new IllegalArgumentException();
080:
081: this .node = node;
082: }
083:
084: // This is a utilitiy method that unions the bounds of
085: // cgn upto child (if child is null it does all children).
086: // It unions them with init if provided.
087: static Rectangle2D addBounds(CompositeGraphicsNode cgn,
088: GraphicsNode child, Rectangle2D init) {
089: // System.out.println("CGN: " + cgn);
090: // System.out.println("Child: " + child);
091:
092: List children = cgn.getChildren();
093: Iterator i = children.iterator();
094: Rectangle2D r2d = null;
095: while (i.hasNext()) {
096: GraphicsNode gn = (GraphicsNode) i.next();
097: if (gn == child)
098: break;
099:
100: // System.out.println("GN: " + gn);
101: Rectangle2D cr2d = gn.getBounds();
102: AffineTransform at = gn.getTransform();
103: if (at != null)
104: cr2d = at.createTransformedShape(cr2d).getBounds2D();
105:
106: if (r2d == null)
107: r2d = (Rectangle2D) cr2d.clone();
108: else
109: r2d.add(cr2d);
110: }
111:
112: if (r2d == null) {
113: if (init == null)
114: return CompositeGraphicsNode.VIEWPORT;
115:
116: return init;
117: }
118:
119: if (init == null)
120: return r2d;
121:
122: init.add(r2d);
123:
124: return init;
125: }
126:
127: static Rectangle2D getViewportBounds(GraphicsNode gn,
128: GraphicsNode child) {
129: // See if background is enabled.
130: Rectangle2D r2d = null;
131: if (gn instanceof CompositeGraphicsNode) {
132: CompositeGraphicsNode cgn = (CompositeGraphicsNode) gn;
133: r2d = cgn.getBackgroundEnable();
134: }
135:
136: if (r2d == null)
137: // No background enable so check our parent's value.
138: r2d = getViewportBounds(gn.getParent(), gn);
139:
140: // No background for any ancester (error) return null
141: if (r2d == null)
142: return null;
143:
144: // Background enabled is set, but it has no fixed bounds set.
145: if (r2d == CompositeGraphicsNode.VIEWPORT) {
146: // If we don't have a child then just use our bounds.
147: if (child == null)
148: return (Rectangle2D) gn.getPrimitiveBounds().clone();
149:
150: // gn must be composite so add all it's children's bounds
151: // up to child.
152: CompositeGraphicsNode cgn = (CompositeGraphicsNode) gn;
153: return addBounds(cgn, child, null);
154: }
155:
156: // We have a partial bound from parent, so map it to gn's
157: // coordinate system...
158: AffineTransform at = gn.getTransform();
159: if (at != null) {
160: try {
161: at = at.createInverse();
162: r2d = at.createTransformedShape(r2d).getBounds2D();
163: } catch (NoninvertibleTransformException nte) {
164: // Degenerate case return null;
165: r2d = null;
166: }
167: }
168:
169: if (child != null) {
170: // Add our childrens bounds to it...
171: CompositeGraphicsNode cgn = (CompositeGraphicsNode) gn;
172: r2d = addBounds(cgn, child, r2d);
173: } else {
174: Rectangle2D gnb = gn.getPrimitiveBounds();
175: if (gnb != null)
176: r2d.add(gnb);
177: }
178:
179: return r2d;
180: }
181:
182: // This does the leg work for getBounds().
183: // It traverses the tree figuring out the bounds of the
184: // background image.
185: static Rectangle2D getBoundsRecursive(GraphicsNode gn,
186: GraphicsNode child) {
187:
188: Rectangle2D r2d = null;
189: if (gn == null) {
190: // System.out.println("Null GN Parent: " + child );
191: return null;
192: }
193:
194: if (gn instanceof CompositeGraphicsNode) {
195: CompositeGraphicsNode cgn = (CompositeGraphicsNode) gn;
196: // See if background is enabled.
197: r2d = cgn.getBackgroundEnable();
198: }
199:
200: // background has definite bounds so return them.
201: if (r2d != null)
202: return r2d;
203:
204: // No background enable so check our parent's value.
205: r2d = getBoundsRecursive(gn.getParent(), gn);
206:
207: // No background for any ancester (error) return empty Rect...
208: if (r2d == null) {
209: // System.out.println("Null GetBoundsRec:" + gn + "\n\t" + child);
210: return new Rectangle2D.Float(0, 0, 0, 0);
211: }
212:
213: // Our parent has background but no bounds (and we must
214: // have been the first child so build the new bounds...
215: if (r2d == CompositeGraphicsNode.VIEWPORT)
216: return r2d;
217:
218: AffineTransform at = gn.getTransform();
219: if (at != null) {
220: try {
221: // background has a definite bound so map it to gn's
222: // coordinate system...
223: at = at.createInverse();
224: r2d = at.createTransformedShape(r2d).getBounds2D();
225: } catch (NoninvertibleTransformException nte) {
226: // Degenerate case return null;
227: r2d = null;
228: }
229: }
230:
231: return r2d;
232: }
233:
234: /**
235: * Returns the bounds of this Rable in the user coordinate system.
236: */
237: public Rectangle2D getBounds2D() {
238: // System.out.println("GetBounds2D called");
239: Rectangle2D r2d = getBoundsRecursive(node, null);
240:
241: // System.out.println("BoundsRec: " + r2d);
242:
243: if (r2d == CompositeGraphicsNode.VIEWPORT) {
244: r2d = getViewportBounds(node, null);
245: // System.out.println("BoundsViewport: " + r2d);
246: }
247:
248: return r2d;
249: }
250:
251: /**
252: * Returns a filter that represents the background image
253: * for <tt>child</tt>.
254: * @param gn Node to get background image for.
255: * @param child Child to stop at when compositing children of gn into
256: * the background image.
257: * @param aoi The area of interest for rendering (used to cull
258: * nodes that don't intersect the region to render).
259: */
260: public Filter getBackground(GraphicsNode gn, GraphicsNode child,
261: Rectangle2D aoi) {
262: if (gn == null) {
263: throw new IllegalArgumentException(
264: "BackgroundImage requested yet no parent has "
265: + "'enable-background:new'");
266: }
267:
268: Rectangle2D r2d = null;
269: if (gn instanceof CompositeGraphicsNode) {
270: CompositeGraphicsNode cgn = (CompositeGraphicsNode) gn;
271: r2d = cgn.getBackgroundEnable();
272: }
273:
274: List srcs = new ArrayList(); // this hides a member in a super-class!!
275: if (r2d == null) {
276: Rectangle2D paoi = aoi;
277: AffineTransform at = gn.getTransform();
278: if (at != null)
279: paoi = at.createTransformedShape(aoi).getBounds2D();
280: Filter f = getBackground(gn.getParent(), gn, paoi);
281:
282: // Don't add the nodes unless they will contribute.
283: if ((f != null) && f.getBounds2D().intersects(aoi)) {
284: srcs.add(f);
285: }
286: }
287:
288: if (child != null) {
289: CompositeGraphicsNode cgn = (CompositeGraphicsNode) gn;
290: List children = cgn.getChildren();
291: Iterator i = children.iterator();
292: while (i.hasNext()) {
293: GraphicsNode childGN = (GraphicsNode) i.next();
294: // System.out.println("Parent: " + cgn +
295: // "\n Child: " + child +
296: // "\n ChildGN: " + childGN);
297: if (childGN == child)
298: break;
299:
300: Rectangle2D cbounds = childGN.getBounds();
301: // System.out.println("Child : " + childGN);
302: // System.out.println("Bounds: " + cbounds);
303: // System.out.println(" : " + aoi);
304:
305: AffineTransform at = childGN.getTransform();
306: if (at != null)
307: cbounds = at.createTransformedShape(cbounds)
308: .getBounds2D();
309:
310: if (aoi.intersects(cbounds)) {
311: srcs
312: .add(childGN
313: .getEnableBackgroundGraphicsNodeRable(true));
314: }
315: }
316: }
317:
318: if (srcs.size() == 0)
319: return null;
320:
321: Filter ret = null;
322: if (srcs.size() == 1)
323: ret = (Filter) srcs.get(0);
324: else
325: ret = new CompositeRable8Bit(srcs, CompositeRule.OVER,
326: false);
327:
328: if (child != null) {
329: // We are returning the filter to child so make
330: // sure to map the filter from the parents user space
331: // to the childs user space...
332:
333: AffineTransform at = child.getTransform();
334: if (at != null) {
335: try {
336: at = at.createInverse();
337: ret = new AffineRable8Bit(ret, at);
338: } catch (NoninvertibleTransformException nte) {
339: ret = null;
340: }
341: }
342: }
343:
344: return ret;
345: }
346:
347: /**
348: * Returns true if successive renderings (that is, calls to
349: * createRendering() or createScaledRendering()) with the same arguments
350: * may produce different results. This method may be used to
351: * determine whether an existing rendering may be cached and
352: * reused. It is always safe to return true.
353: */
354: public boolean isDynamic() {
355: return false;
356: }
357:
358: /**
359: * Creates a RenderedImage that represented a rendering of this image
360: * using a given RenderContext. This is the most general way to obtain a
361: * rendering of a RenderableImage.
362: *
363: * <p> The created RenderedImage may have a property identified
364: * by the String HINTS_OBSERVED to indicate which RenderingHints
365: * (from the RenderContext) were used to create the image.
366: * In addition any RenderedImages
367: * that are obtained via the getSources() method on the created
368: * RenderedImage may have such a property.
369: *
370: * @param renderContext the RenderContext to use to produce the rendering.
371: * @return a RenderedImage containing the rendered data.
372: */
373: public RenderedImage createRendering(RenderContext renderContext) {
374:
375: Rectangle2D r2d = getBounds2D();
376:
377: // System.out.println("Rendering called");
378:
379: Shape aoi = renderContext.getAreaOfInterest();
380: if (aoi != null) {
381: Rectangle2D aoiR2d = aoi.getBounds2D();
382: // System.out.println("R2d: " + r2d);
383: // System.out.println("AOI: " + aoiR2d);
384:
385: if (!r2d.intersects(aoiR2d))
386: return null;
387:
388: Rectangle2D.intersect(r2d, aoiR2d, r2d);
389: }
390:
391: Filter background = getBackground(node, null, r2d);
392: // System.out.println("BG: " + background);
393: if (background == null)
394: return null;
395:
396: background = new PadRable8Bit(background, r2d, PadMode.ZERO_PAD);
397:
398: RenderedImage ri = background
399: .createRendering(new RenderContext(renderContext
400: .getTransform(), r2d, renderContext
401: .getRenderingHints()));
402: // System.out.println("RI: [" + ri.getMinX() + ", "
403: // + ri.getMinY() + ", " +
404: // + ri.getWidth() + ", " +
405: // + ri.getHeight() + "]");
406: // org.ImageDisplay.showImage("BG: ", ri);
407: return ri;
408: }
409: }
|