KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > ext > awt > image > renderable > FilterResRable8Bit


1 /*
2
3    Copyright 2001,2003 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17  */

18 package org.apache.batik.ext.awt.image.renderable;
19
20 import java.awt.Composite JavaDoc;
21 import java.awt.Graphics2D JavaDoc;
22 import java.awt.Rectangle JavaDoc;
23 import java.awt.RenderingHints JavaDoc;
24 import java.awt.Shape JavaDoc;
25 import java.awt.color.ColorSpace JavaDoc;
26 import java.awt.geom.AffineTransform JavaDoc;
27 import java.awt.geom.Rectangle2D JavaDoc;
28 import java.awt.image.RenderedImage JavaDoc;
29 import java.awt.image.renderable.RenderContext JavaDoc;
30 import java.awt.image.renderable.RenderableImage JavaDoc;
31 import java.lang.ref.Reference JavaDoc;
32 import java.lang.ref.SoftReference JavaDoc;
33 import java.util.Iterator JavaDoc;
34 import java.util.ListIterator JavaDoc;
35 import java.util.Vector JavaDoc;
36
37 import org.apache.batik.ext.awt.image.CompositeRule;
38 import org.apache.batik.ext.awt.image.GraphicsUtil;
39 import org.apache.batik.ext.awt.image.SVGComposite;
40 import org.apache.batik.ext.awt.image.rendered.AffineRed;
41 import org.apache.batik.ext.awt.image.rendered.TileCacheRed;
42
43 /**
44  * Interface for implementing filter resolution.
45  *
46  * @author <a HREF="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
47  * @version $Id: FilterResRable8Bit.java,v 1.11 2004/08/18 07:13:59 vhardy Exp $
48  */

49 public class FilterResRable8Bit extends AbstractRable
50     implements FilterResRable, PaintRable {
51
52     /**
53      * Filter resolution along the x-axis
54      */

55     private int filterResolutionX = -1;
56
57     /**
58      * Filter resolution along the y-axis
59      */

60     private int filterResolutionY = -1;
61
62     public FilterResRable8Bit() {
63         // System.out.println("Using FilterResRable8bit...");
64
}
65         
66
67     public FilterResRable8Bit(Filter src, int filterResX, int filterResY) {
68         init(src, null);
69         setFilterResolutionX(filterResX);
70         setFilterResolutionY(filterResY);
71     }
72
73     /**
74      * Returns the source to be cropped.
75      */

76     public Filter getSource() {
77         return (Filter)srcs.get(0);
78     }
79     
80     /**
81      * Sets the source to be cropped
82      * @param src image to offset.
83      */

84     public void setSource(Filter src){
85         init(src, null);
86     }
87
88     /**
89      * Returns the resolution along the X axis.
90      */

91     public int getFilterResolutionX(){
92         return filterResolutionX;
93     }
94
95     /**
96      * Sets the resolution along the X axis, i.e., the maximum
97      * size for intermediate images along that axis.
98      * The value should be greater than zero to have an effect.
99      * Negative values are illegal.
100      */

101     public void setFilterResolutionX(int filterResolutionX){
102         if(filterResolutionX < 0){
103             throw new IllegalArgumentException JavaDoc();
104         }
105         touch();
106         this.filterResolutionX = filterResolutionX;
107     }
108     
109     /**
110      * Returns the resolution along the Y axis.
111      */

112     public int getFilterResolutionY(){
113         return filterResolutionY;
114     }
115
116     /**
117      * Sets the resolution along the Y axis, i.e., the maximum
118      * size for intermediate images along that axis.
119      * If the Y-value is less than zero, the scale applied to
120      * the rendered images is computed to preserve the image's aspect ratio
121      */

122     public void setFilterResolutionY(int filterResolutionY){
123         touch();
124         this.filterResolutionY = filterResolutionY;
125     }
126     
127
128     /**
129      * This returns true if <tt>ri</tt> and all of <tt>ri</tt>'s
130      * sources implement the PaintRable interface. This is used to
131      * indicate that the chain has a good potential for bypassing the
132      * filterRes operation entirely.
133      *
134      * Ideally there would be a checkPaintRable method in PaintRable
135      * that could be used to get a definate answer about a filters
136      * ability to draw directly to a Graphics2D (this can sometimes
137      * 'fail' because of the way the Graphics2D is currently
138      * configured).
139      */

140     public boolean allPaintRable(RenderableImage JavaDoc ri) {
141         if (!(ri instanceof PaintRable))
142             return false;
143
144         Vector JavaDoc v = ri.getSources();
145         // No sources and we are PaintRable so the chain is PaintRable.
146
if (v == null) return true;
147         
148         Iterator JavaDoc i = v.iterator();
149         while (i.hasNext()) {
150             RenderableImage JavaDoc nri = (RenderableImage JavaDoc)i.next();
151             // A source is not paintRable so we are not 100% paintRable.
152
if (!allPaintRable(nri)) return false;
153         }
154         
155         return true;
156     }
157
158     /**
159      * This function attempts to distribute the filterRes operation
160      * across src. Right now it knows about two operations, pad and
161      * composite. It's main target is the composite but often pad
162      * operations are sprinked in the chain so it needs to know about
163      * them. This list could be extended however if it gets much
164      * longer it should probably be rolled into a new 'helper interface'
165      * like PaintRable.
166      *
167      * NOTE: This is essentially a bad hack, but it is a hack that is
168      * recomended by the SVG specification so I do it.
169      */

170     public boolean distributeAcross(RenderableImage JavaDoc src, Graphics2D JavaDoc g2d) {
171         boolean ret;
172         if (src instanceof PadRable) {
173             PadRable pad = (PadRable)src;
174             Shape JavaDoc clip = g2d.getClip();
175             g2d.clip(pad.getPadRect());
176             ret = distributeAcross(pad.getSource(), g2d);
177             g2d.setClip(clip);
178             return ret;
179         }
180
181         if (src instanceof CompositeRable) {
182             CompositeRable comp = (CompositeRable)src;
183             if (comp.getCompositeRule() != CompositeRule.OVER)
184                 return false;
185
186             if (false) {
187                 // To check colorspaces or to not check colorspaces
188
// _that_ is the question...
189
ColorSpace JavaDoc crCS = comp.getOperationColorSpace();
190                 ColorSpace JavaDoc g2dCS = GraphicsUtil.getDestinationColorSpace(g2d);
191                 if ((g2dCS == null) || (g2dCS != crCS))
192                     return false;
193             }
194
195             Vector JavaDoc v = comp.getSources();
196             if (v == null) return true;
197             ListIterator JavaDoc li = v.listIterator(v.size());
198             while (li.hasPrevious()) {
199                 RenderableImage JavaDoc csrc = (RenderableImage JavaDoc)li.previous();
200                 if (!allPaintRable(csrc)) {
201                     li.next();
202                     break;
203                 }
204             }
205
206             if (!li.hasPrevious()) {
207                 // All inputs are PaintRable so just draw directly to
208
// the graphics ignore filter res all togeather...
209
GraphicsUtil.drawImage(g2d, comp);
210                 return true;
211             }
212             
213             if (!li.hasNext())
214                 // None of the trailing inputs are PaintRable so we don't
215
// distribute across this at all.
216
return false;
217
218             // Now we are in the case where some are paintRable and
219
// some aren't. In this case we create a new
220
// CompositeRable with the first ones, to which we apply
221
// ourselves (limiting the resolution), and after that
222
// we simply draw the remainder...
223
int idx = li.nextIndex(); // index of first PaintRable...
224
Filter f = new CompositeRable8Bit(v.subList(0, idx),
225                                               comp.getCompositeRule(),
226                                               comp.isColorSpaceLinear());
227             f = new FilterResRable8Bit(f, getFilterResolutionX(),
228                                        getFilterResolutionY());
229             GraphicsUtil.drawImage(g2d, f);
230             while (li.hasNext()) {
231                 PaintRable pr = (PaintRable)li.next();
232                 if (!pr.paintRable(g2d)) {
233                     // Ugg it failed to paint so we need to filterRes it...
234
Filter prf = (Filter)pr;
235                     prf = new FilterResRable8Bit(prf, getFilterResolutionX(),
236                                                  getFilterResolutionY());
237                     GraphicsUtil.drawImage(g2d, prf);
238                 }
239             }
240             return true;
241         }
242         return false;
243     }
244
245     /**
246      * Should perform the equivilent action as
247      * createRendering followed by drawing the RenderedImage.
248      *
249      * @param g2d The Graphics2D to draw to.
250      * @return true if the paint call succeeded, false if
251      * for some reason the paint failed (in which
252      * case a createRendering should be used).
253      */

254     public boolean paintRable(Graphics2D JavaDoc g2d) {
255         // This is a bit of a hack to implement the suggestion of SVG
256
// specification that if the last operation in a filter chain
257
// is a SRC_OVER composite and the source is SourceGraphic it
258
// should be rendered directly to the canvas (by passing
259
// filterRes). We are actually much more aggressive in
260
// implementing this suggestion since we will bypass filterRes
261
// for all the trailing elements in a SRC_OVER composite that
262
// can be drawn directly to the canvas.
263

264         // System.out.println("Calling FilterResRable paintRable");
265

266         // This optimization only apply if we are using
267
// SrcOver. Otherwise things break...
268
Composite JavaDoc c = g2d.getComposite();
269         if (!SVGComposite.OVER.equals(c))
270             return false;
271
272         Filter src = getSource();
273         return distributeAcross(src, g2d);
274     }
275
276     /**
277      * Cached Rendered image at filterRes.
278      */

279     Reference JavaDoc resRed = null;
280     float resScale = 0;
281
282     private float getResScale() {
283         return resScale;
284     }
285
286     private RenderedImage JavaDoc getResRed(RenderingHints JavaDoc hints) {
287         Rectangle2D JavaDoc imageRect = getBounds2D();
288         double resScaleX = getFilterResolutionX()/imageRect.getWidth();
289         double resScaleY = getFilterResolutionY()/imageRect.getHeight();
290
291         
292         // System.out.println("filterRes X " + filterResolutionX +
293
// " Y : " + filterResolutionY);
294

295         float resScale = (float)Math.min(resScaleX, resScaleY);
296
297         RenderedImage JavaDoc ret;
298         if (resScale == this.resScale) {
299             // System.out.println("Matched");
300
ret = (RenderedImage JavaDoc)resRed.get();
301             if (ret != null)
302                 return ret;
303         }
304
305         AffineTransform JavaDoc resUsr2Dev;
306         resUsr2Dev = AffineTransform.getScaleInstance(resScale, resScale);
307         
308         //
309
// Create a new RenderingContext
310
//
311
RenderContext JavaDoc newRC = new RenderContext JavaDoc(resUsr2Dev, null, hints);
312
313         ret = getSource().createRendering(newRC);
314
315         // This is probably justified since the whole reason to use
316
// The filterRes attribute is because the filter chain is
317
// expensive, otherwise you should let it evaluate at
318
// screen resolution always - right?
319
ret = new TileCacheRed(GraphicsUtil.wrap(ret));
320         this.resScale = resScale;
321         this.resRed = new SoftReference JavaDoc(ret);
322
323         return ret;
324     }
325
326     
327
328     /**
329      *
330      */

331     public RenderedImage JavaDoc createRendering(RenderContext JavaDoc renderContext) {
332         // Get user space to device space transform
333
AffineTransform JavaDoc usr2dev = renderContext.getTransform();
334         if(usr2dev == null){
335             usr2dev = new AffineTransform JavaDoc();
336         }
337
338         RenderingHints JavaDoc hints = renderContext.getRenderingHints();
339         
340         // As per specification, a value of zero for the
341
// x-axis or y-axis causes the filter to produce
342
// nothing.
343
// The processing is done as follows:
344
// + if the x resolution is zero, this is a no-op
345
// else compute the x scale.
346
// + if the y resolution is zero, this is a no-op
347
// else compute the y resolution from the x scale
348
// and compute the corresponding y scale.
349
// + if the y or x scale is less than one, insert
350
// an AffineRable.
351
// Else, return the source as is.
352
int filterResolutionX = getFilterResolutionX();
353         int filterResolutionY = getFilterResolutionY();
354         // System.out.println("FilterResRable: " + filterResolutionX + "x" +
355
// filterResolutionY);
356

357         if ((filterResolutionX <= 0) || (filterResolutionY == 0))
358             return null;
359         
360         // Find out the renderable area
361
Rectangle2D JavaDoc imageRect = getBounds2D();
362         Rectangle JavaDoc devRect;
363         devRect = usr2dev.createTransformedShape(imageRect).getBounds();
364
365         // Now, compare the devRect with the filter
366
// resolution hints
367
float scaleX = 1;
368         if(filterResolutionX < devRect.width)
369             scaleX = filterResolutionX / (float)devRect.width;
370
371         float scaleY = 1;
372         if(filterResolutionY < 0)
373             scaleY = scaleX;
374         else if(filterResolutionY < devRect.height)
375             scaleY = filterResolutionY / (float)devRect.height;
376
377         // Only resample if either scaleX or scaleY is
378
// smaller than 1
379
if ((scaleX >= 1) && (scaleY >= 1))
380             return getSource().createRendering(renderContext);
381
382         // System.out.println("Using Fixed Resolution...");
383

384         // Using fixed resolution image since we need an image larger
385
// than this.
386
RenderedImage JavaDoc resRed = getResRed(hints);
387         float resScale = getResScale();
388
389         AffineTransform JavaDoc residualAT;
390         residualAT = new AffineTransform JavaDoc(usr2dev.getScaleX()/resScale,
391                                          usr2dev.getShearY()/resScale,
392                                          usr2dev.getShearX()/resScale,
393                                          usr2dev.getScaleY()/resScale,
394                                          usr2dev.getTranslateX(),
395                                          usr2dev.getTranslateY());
396
397         // org.ImageDisplay.showImage("AT: " + newUsr2Dev, result);
398

399         return new AffineRed(GraphicsUtil.wrap(resRed), residualAT, hints);
400     }
401 }
402
403
Popular Tags