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.Composite;
022: import java.awt.Graphics2D;
023: import java.awt.color.ColorSpace;
024: import java.awt.geom.AffineTransform;
025: import java.awt.geom.Rectangle2D;
026: import java.awt.image.RenderedImage;
027: import java.awt.image.renderable.RenderContext;
028: import java.util.Map;
029:
030: import org.apache.batik.ext.awt.image.GraphicsUtil;
031: import org.apache.batik.ext.awt.image.SVGComposite;
032: import org.apache.batik.ext.awt.image.renderable.AbstractRable;
033: import org.apache.batik.ext.awt.image.renderable.Filter;
034: import org.apache.batik.ext.awt.image.renderable.PaintRable;
035: import org.apache.batik.ext.awt.image.rendered.CachableRed;
036: import org.apache.batik.ext.awt.image.rendered.TranslateRed;
037: import org.apache.batik.gvt.GraphicsNode;
038:
039: /**
040: * This implementation of RenderableImage will render its input
041: * GraphicsNode into a BufferedImage upon invokation of one of its
042: * createRendering methods.
043: *
044: * @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
045: * @version $Id: GraphicsNodeRable8Bit.java 475477 2006-11-15 22:44:28Z cam $
046: */
047: public class GraphicsNodeRable8Bit extends AbstractRable implements
048: GraphicsNodeRable, PaintRable {
049:
050: private AffineTransform cachedGn2dev = null;
051: private AffineTransform cachedUsr2dev = null;
052: private CachableRed cachedRed = null;
053: private Rectangle2D cachedBounds = null;
054: /**
055: * Should GraphicsNodeRable call primitivePaint or Paint.
056: */
057: private boolean usePrimitivePaint = true;
058:
059: /**
060: * Returns true if this Rable get's it's contents by calling
061: * primitivePaint on the associated <tt>GraphicsNode</tt> or
062: * false if it uses paint.
063: */
064: public boolean getUsePrimitivePaint() {
065: return usePrimitivePaint;
066: }
067:
068: /**
069: * Set to true if this Rable should get it's contents by calling
070: * primitivePaint on the associated <tt>GraphicsNode</tt> or false
071: * if it should use paint.
072: */
073: public void setUsePrimitivePaint(boolean usePrimitivePaint) {
074: this .usePrimitivePaint = usePrimitivePaint;
075: }
076:
077: /**
078: * GraphicsNode this image can render
079: */
080: private GraphicsNode node;
081:
082: /**
083: * Returns the <tt>GraphicsNode</tt> rendered by this image
084: */
085: public GraphicsNode getGraphicsNode() {
086: return node;
087: }
088:
089: /**
090: * Sets the <tt>GraphicsNode</tt> this image should render
091: */
092: public void setGraphicsNode(GraphicsNode node) {
093: if (node == null) {
094: throw new IllegalArgumentException();
095: }
096:
097: this .node = node;
098: }
099:
100: /**
101: * Clear any cached Red.
102: */
103: public void clearCache() {
104: cachedRed = null;
105: cachedUsr2dev = null;
106: cachedGn2dev = null;
107: cachedBounds = null;
108: }
109:
110: /**
111: * @param node The GraphicsNode this image should represent
112: */
113: public GraphicsNodeRable8Bit(GraphicsNode node) {
114: if (node == null)
115: throw new IllegalArgumentException();
116:
117: this .node = node;
118: this .usePrimitivePaint = true;
119: }
120:
121: /**
122: * @param node The GraphicsNode this image should represent
123: * @param props The Properties for this image.
124: */
125: public GraphicsNodeRable8Bit(GraphicsNode node, Map props) {
126: super ((Filter) null, props);
127:
128: if (node == null)
129: throw new IllegalArgumentException();
130:
131: this .node = node;
132: this .usePrimitivePaint = true;
133: }
134:
135: /**
136: * @param node the GraphicsNode this image should represent
137: * @param usePrimitivePaint indicates if the image should
138: * include any filters or mask operations on <tt>node</tt>
139: */
140: public GraphicsNodeRable8Bit(GraphicsNode node,
141: boolean usePrimitivePaint) {
142: if (node == null)
143: throw new IllegalArgumentException();
144:
145: this .node = node;
146: this .usePrimitivePaint = usePrimitivePaint;
147: }
148:
149: /**
150: * Returns the bounds of this Rable in the user coordinate system.
151: */
152: public Rectangle2D getBounds2D() {
153: if (usePrimitivePaint) {
154: Rectangle2D primitiveBounds = node.getPrimitiveBounds();
155: if (primitiveBounds == null)
156: return new Rectangle2D.Double(0, 0, 0, 0);
157:
158: return (Rectangle2D) (primitiveBounds.clone());
159: }
160:
161: // When not using Primitive paint we return out bounds in our
162: // parent's user space. This makes sense since this is the
163: // space that we will draw our selves into (since paint unlike
164: // primitivePaint incorporates the transform from our user
165: // space to our parents user space).
166: Rectangle2D bounds = node.getBounds();
167: if (bounds == null) {
168: return new Rectangle2D.Double(0, 0, 0, 0);
169: }
170:
171: AffineTransform at = node.getTransform();
172: if (at != null) {
173: bounds = at.createTransformedShape(bounds).getBounds2D();
174: }
175: return bounds;
176: }
177:
178: /**
179: * Returns true if successive renderings (that is, calls to
180: * createRendering() or createScaledRendering()) with the same arguments
181: * may produce different results. This method may be used to
182: * determine whether an existing rendering may be cached and
183: * reused. It is always safe to return true.
184: */
185: public boolean isDynamic() {
186: return false;
187: }
188:
189: /**
190: * Should perform the equivilent action as
191: * createRendering followed by drawing the RenderedImage to
192: * Graphics2D, or return false.
193: *
194: * @param g2d The Graphics2D to draw to.
195: * @return true if the paint call succeeded, false if
196: * for some reason the paint failed (in which
197: * case a createRendering should be used).
198: */
199: public boolean paintRable(Graphics2D g2d) {
200: // This optimization only apply if we are using
201: // SrcOver. Otherwise things break...
202: Composite c = g2d.getComposite();
203: if (!SVGComposite.OVER.equals(c))
204: return false;
205:
206: ColorSpace g2dCS = GraphicsUtil.getDestinationColorSpace(g2d);
207: if ((g2dCS == null)
208: || (g2dCS != ColorSpace.getInstance(ColorSpace.CS_sRGB))) {
209: // Only draw directly into sRGB destinations...
210: return false;
211: }
212:
213: // System.out.println("drawImage GNR: " + g2dCS);
214: GraphicsNode gn = getGraphicsNode();
215: if (getUsePrimitivePaint()) {
216: gn.primitivePaint(g2d);
217: } else {
218: gn.paint(g2d);
219: }
220:
221: // Paint did the work...
222: return true;
223: }
224:
225: /**
226: * Creates a RenderedImage that represented a rendering of this image
227: * using a given RenderContext. This is the most general way to obtain a
228: * rendering of a RenderableImage.
229: *
230: * <p> The created RenderedImage may have a property identified
231: * by the String HINTS_OBSERVED to indicate which RenderingHints
232: * (from the RenderContext) were used to create the image.
233: * In addition any RenderedImages
234: * that are obtained via the getSources() method on the created
235: * RenderedImage may have such a property.
236: *
237: * @param renderContext the RenderContext to use to produce the rendering.
238: * @return a RenderedImage containing the rendered data.
239: */
240: public RenderedImage createRendering(RenderContext renderContext) {
241: // Get user space to device space transform
242: AffineTransform usr2dev = renderContext.getTransform();
243:
244: AffineTransform gn2dev;
245: if (usr2dev == null) {
246: usr2dev = new AffineTransform();
247: gn2dev = usr2dev;
248: } else {
249: gn2dev = (AffineTransform) usr2dev.clone();
250: }
251:
252: // Get the nodes transform (so we can pick up changes in this.
253: AffineTransform gn2usr = node.getTransform();
254: if (gn2usr != null) {
255: gn2dev.concatenate(gn2usr);
256: }
257:
258: Rectangle2D bounds2D = getBounds2D();
259:
260: if ((cachedBounds != null) && (cachedGn2dev != null)
261: && (cachedBounds.equals(bounds2D))
262: && (gn2dev.getScaleX() == cachedGn2dev.getScaleX())
263: && (gn2dev.getScaleY() == cachedGn2dev.getScaleY())
264: && (gn2dev.getShearX() == cachedGn2dev.getShearX())
265: && (gn2dev.getShearY() == cachedGn2dev.getShearY())) {
266: // Just some form of Translation
267: double deltaX = (usr2dev.getTranslateX() - cachedUsr2dev
268: .getTranslateX());
269: double deltaY = (usr2dev.getTranslateY() - cachedUsr2dev
270: .getTranslateY());
271:
272: // System.out.println("Using Cached Red!!! " +
273: // deltaX + "x" + deltaY);
274: if ((deltaX == 0) && (deltaY == 0))
275: // Actually no translation
276: return cachedRed;
277:
278: // System.out.println("Delta: [" + deltaX + ", " + deltaY + "]");
279:
280: // Integer translation in device space..
281: if ((deltaX == (int) deltaX) && (deltaY == (int) deltaY)) {
282: return new TranslateRed(cachedRed, (int) Math
283: .round(cachedRed.getMinX() + deltaX),
284: (int) Math.round(cachedRed.getMinY() + deltaY));
285: }
286: }
287:
288: // Fell through let's do a new rendering...
289: if (false) {
290: System.out.println("Not using Cached Red: " + usr2dev);
291: System.out
292: .println("Old: " + cachedUsr2dev);
293: }
294:
295: if ((bounds2D.getWidth() > 0) && (bounds2D.getHeight() > 0)) {
296: cachedUsr2dev = (AffineTransform) usr2dev.clone();
297: cachedGn2dev = gn2dev;
298: cachedBounds = bounds2D;
299: cachedRed = new GraphicsNodeRed8Bit(node, usr2dev,
300: usePrimitivePaint, renderContext
301: .getRenderingHints());
302: return cachedRed;
303: }
304:
305: cachedUsr2dev = null;
306: cachedGn2dev = null;
307: cachedBounds = null;
308: cachedRed = null;
309: return null;
310: }
311: }
|