KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > gvt > renderer > StaticRenderer


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.gvt.renderer;
19
20 import java.awt.AlphaComposite JavaDoc;
21 import java.awt.Graphics2D JavaDoc;
22 import java.awt.Point JavaDoc;
23 import java.awt.Rectangle JavaDoc;
24 import java.awt.RenderingHints JavaDoc;
25 import java.awt.Shape JavaDoc;
26 import java.awt.geom.AffineTransform JavaDoc;
27 import java.awt.image.BufferedImage JavaDoc;
28 import java.awt.image.ColorModel JavaDoc;
29 import java.awt.image.Raster JavaDoc;
30 import java.awt.image.RenderedImage JavaDoc;
31 import java.awt.image.SampleModel JavaDoc;
32 import java.awt.image.WritableRaster JavaDoc;
33 import java.awt.image.renderable.RenderContext JavaDoc;
34 import java.lang.ref.SoftReference JavaDoc;
35 import java.util.ArrayList JavaDoc;
36 import java.util.Iterator JavaDoc;
37 import java.util.Collection JavaDoc;
38
39 import org.apache.batik.ext.awt.image.GraphicsUtil;
40 import org.apache.batik.ext.awt.image.PadMode;
41 import org.apache.batik.ext.awt.image.renderable.Filter;
42 import org.apache.batik.ext.awt.image.rendered.CachableRed;
43 import org.apache.batik.ext.awt.image.rendered.PadRed;
44 import org.apache.batik.ext.awt.image.rendered.TileCacheRed;
45 import org.apache.batik.ext.awt.image.rendered.TranslateRed;
46 import org.apache.batik.ext.awt.geom.RectListManager;
47 import org.apache.batik.gvt.GraphicsNode;
48 import org.apache.batik.util.HaltingThread;
49
50 /**
51  * Simple implementation of the Renderer that simply does static
52  * rendering in an offscreen buffer image.
53  *
54  * @author <a HREF="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
55  * @version $Id: StaticRenderer.java,v 1.31 2005/03/27 08:58:34 cam Exp $
56  */

57 public class StaticRenderer implements ImageRenderer {
58     /**
59      * Error messages
60      */

61     private static final String JavaDoc ILLEGAL_ARGUMENT_NULL_OFFSCREEN =
62         "offScreen should not be null";
63     private static final String JavaDoc ILLEGAL_ARGUMENT_ZERO_WIDTH_OR_HEIGHT =
64         "offScreen should have positive width/height";
65
66     /**
67      * Tree this Renderer paints.
68      */

69     protected GraphicsNode rootGN;
70     protected Filter rootFilter;
71     protected CachableRed rootCR;
72     protected SoftReference JavaDoc lastCR;
73     protected SoftReference JavaDoc lastCache;
74
75     /**
76      * Flag for double buffering.
77      */

78     protected boolean isDoubleBuffered = false;
79
80     /**
81      * Offscreen image where the Renderer does its rendering
82      */

83     protected WritableRaster JavaDoc currentBaseRaster;
84     protected WritableRaster JavaDoc currentRaster;
85     protected BufferedImage JavaDoc currentOffScreen;
86
87     protected WritableRaster JavaDoc workingBaseRaster;
88     protected WritableRaster JavaDoc workingRaster;
89     protected BufferedImage JavaDoc workingOffScreen;
90
91     protected int offScreenWidth;
92     protected int offScreenHeight;
93
94     /**
95      * Passed to the GVT tree to describe the rendering environment
96      */

97     protected RenderingHints JavaDoc renderingHints;
98     protected AffineTransform JavaDoc usr2dev;
99
100     protected static RenderingHints JavaDoc defaultRenderingHints;
101     static {
102         defaultRenderingHints = new RenderingHints JavaDoc(null);
103         defaultRenderingHints.put(RenderingHints.KEY_ANTIALIASING,
104                                   RenderingHints.VALUE_ANTIALIAS_ON);
105
106         defaultRenderingHints.put(RenderingHints.KEY_INTERPOLATION,
107                                   RenderingHints.VALUE_INTERPOLATION_BILINEAR);
108     }
109
110     /**
111      * @param rh Hints for rendering.
112      * @param at Starting user to device coordinate system transform.
113      */

114     public StaticRenderer(RenderingHints JavaDoc rh,
115                           AffineTransform JavaDoc at){
116         renderingHints = new RenderingHints JavaDoc(rh);
117         usr2dev = new AffineTransform JavaDoc(at);
118     }
119
120     /**
121      * Creates a new StaticRenderer object.
122      */

123     public StaticRenderer(){
124         renderingHints = new RenderingHints JavaDoc(defaultRenderingHints);
125         usr2dev = new AffineTransform JavaDoc();
126     }
127
128
129     /**
130      * Disposes all resources of this renderer.
131      */

132     public void dispose() {
133         rootGN = null;
134         rootFilter = null;
135         rootCR = null;
136         
137         workingOffScreen = null;
138         workingBaseRaster = null;
139         workingRaster = null;
140
141         currentOffScreen = null;
142         currentBaseRaster = null;
143         currentRaster = null;
144
145         renderingHints = null;
146     }
147
148     /**
149      * This associates the given GVT Tree with this renderer.
150      * Any previous tree association is forgotten.
151      * Not certain if this should be just GraphicsNode, or CanvasGraphicsNode.
152      */

153     public void setTree(GraphicsNode rootGN){
154         this.rootGN = rootGN;
155         rootFilter = null;
156         rootCR = null;
157
158         workingOffScreen = null;
159         workingRaster = null;
160
161         currentOffScreen = null;
162         currentRaster = null;
163
164         // renderingHints = new RenderingHints(defaultRenderingHints);
165
}
166
167     /**
168      * @return the GVT tree associated with this renderer
169      */

170     public GraphicsNode getTree(){
171         return rootGN;
172     }
173
174     /**
175      * @param rh Set of rendering hints to use for future renderings
176      */

177     public void setRenderingHints(RenderingHints JavaDoc rh) {
178         renderingHints = new RenderingHints JavaDoc(rh);
179
180         rootFilter = null;
181         rootCR = null;
182
183         workingOffScreen = null;
184         workingRaster = null;
185
186         currentOffScreen = null;
187         currentRaster = null;
188     }
189
190     /**
191      * @return the RenderingHints which the Renderer is using for its
192      * rendering
193      */

194     public RenderingHints JavaDoc getRenderingHints() {
195         return renderingHints;
196     }
197
198     /**
199      * Sets the transform from the current user space (as defined by
200      * the top node of the GVT tree, to the associated device space.
201      *
202      * @param usr2dev the new user space to device space transform. If null,
203      * the identity transform will be set.
204      */

205     public void setTransform(AffineTransform JavaDoc usr2dev){
206         if (this.usr2dev.equals(usr2dev))
207             return;
208
209         if(usr2dev == null)
210             this.usr2dev = new AffineTransform JavaDoc();
211         else
212             this.usr2dev = new AffineTransform JavaDoc(usr2dev);
213
214         rootCR = null;
215     }
216
217     /**
218      * Returns a copy of the transform from the current user space (as
219      * defined by the top node of the GVT tree) to the device space (1
220      * unit = 1/72nd of an inch / 1 pixel, roughly speaking
221      */

222     public AffineTransform JavaDoc getTransform(){
223         return usr2dev;
224     }
225
226     /**
227      * Returns true if the Renderer is currently doubleBuffering is
228      * rendering requests. If it is then getOffscreen will only
229      * return completed renderings (or null if nothing is available).
230      */

231     public boolean isDoubleBuffered(){
232         return isDoubleBuffered;
233     }
234
235     /**
236      * Turns on/off double buffering in renderer. Turning off
237      * double buffering makes it possible to see the ongoing results
238      * of a render operation.
239      *
240      * @param isDoubleBuffered the new value for double buffering
241      */

242     public void setDoubleBuffered(boolean isDoubleBuffered){
243         if (this.isDoubleBuffered == isDoubleBuffered)
244             return;
245
246         this.isDoubleBuffered = isDoubleBuffered;
247         if (isDoubleBuffered) {
248             // Now double buffering, so make sure they can't see work buffers.
249
currentOffScreen = null;
250             currentBaseRaster = null;
251             currentRaster = null;
252         } else {
253             // No longer double buffering so join work and current buffers.
254
currentOffScreen = workingOffScreen;
255             currentBaseRaster = workingBaseRaster;
256             currentRaster = workingRaster;
257         }
258     }
259
260
261     /**
262      * Update the size of the image to be returned by getOffScreen.
263      * Note that this change will not be reflected by calls to
264      * getOffscreen until either clearOffScreen has completed (when
265      * isDoubleBuffered is false) or reapint has completed (when
266      * isDoubleBuffered is true).
267      *
268      */

269     public void updateOffScreen(int width, int height) {
270         offScreenWidth = width;
271         offScreenHeight = height;
272     }
273
274     /**
275      * Returns the current offscreen image.
276      *
277      * The exact symantics of this vary base on the value of
278      * isDoubleBuffered. If isDoubleBuffered is false this will
279      * return the image currently being worked on as soon as it is
280      * available.
281      *
282      * if isDoubleBuffered is false this will return the most recently
283      * completed result of repaint.
284      */

285     public BufferedImage JavaDoc getOffScreen() {
286         if (rootGN == null)
287             return null;
288
289         return currentOffScreen;
290     }
291
292     /**
293      * Sets up and clears the current offscreen buffer.
294      *
295      * When not double buffering one should call this method before
296      * calling getOffscreen to get the offscreen being drawn into.
297      * This ensures the buffer is up to date and doesn't contain junk.
298      *
299      * When double buffering this call can effectively be skipped,
300      * since getOffscreen will only refect the new rendering after
301      * repaint completes.
302      */

303     public void clearOffScreen() {
304
305         // No need to clear in double buffer case people will
306
// only see it when it is done...
307
if (isDoubleBuffered)
308             return;
309
310         updateWorkingBuffers();
311         if ((rootCR == null) ||
312             (workingBaseRaster == null))
313             return;
314         
315         ColorModel JavaDoc cm = rootCR.getColorModel();
316         WritableRaster JavaDoc syncRaster = workingBaseRaster;
317
318         // Ensure only one thread works on baseRaster at a time...
319
synchronized (syncRaster) {
320             BufferedImage JavaDoc bi = new BufferedImage JavaDoc
321                 (cm, workingBaseRaster, cm.isAlphaPremultiplied(), null);
322             Graphics2D JavaDoc g2d = bi.createGraphics();
323             g2d.setComposite(AlphaComposite.Clear);
324             g2d.fillRect(0, 0, bi.getWidth(), bi.getHeight());
325             g2d.dispose();
326         }
327     }
328
329
330     /**
331      * Repaints the associated GVT tree under <tt>area</tt>.
332      *
333      * If double buffered is true and this method completes cleanly it
334      * will set the result of the repaint as the image returned by
335      * getOffscreen otherwise the old image will still be returned.
336      * If double buffered is false it is possible some effects of
337      * the failed rendering will be visible in the image returned
338      * by getOffscreen.
339      *
340      * @param area region to be repainted, in the current user space
341      * coordinate system.
342      */

343     public void repaint(Shape JavaDoc area) {
344         if (area == null) return;
345         RectListManager rlm = new RectListManager();
346         rlm.add(usr2dev.createTransformedShape(area).getBounds());
347         repaint(rlm);
348     }
349
350     /**
351      * Repaints the associated GVT tree under the list of <tt>areas</tt>.
352      *
353      * If double buffered is true and this method completes cleanly it
354      * will set the result of the repaint as the image returned by
355      * getOffscreen otherwise the old image will still be returned.
356      * If double buffered is false it is possible some effects of
357      * the failed rendering will be visible in the image returned
358      * by getOffscreen.
359      *
360      * @param areas a List of regions to be repainted, in the current
361      * user space coordinate system.
362      */

363     public void repaint(RectListManager areas) {
364
365         if (areas == null)
366             return;
367
368         // System.out.println("Renderer Repainting");
369

370         // long t0 = System.currentTimeMillis();
371

372         CachableRed cr;
373         WritableRaster JavaDoc syncRaster;
374         WritableRaster JavaDoc copyRaster;
375
376         // While we are synchronized pull all the relavent info out
377
// of member variables into local variables.
378
updateWorkingBuffers();
379         if ((rootCR == null) ||
380             (workingBaseRaster == null))
381             return;
382
383         cr = rootCR;
384         syncRaster = workingBaseRaster;
385         copyRaster = workingRaster;
386
387         Rectangle JavaDoc srcR = rootCR.getBounds();
388         Rectangle JavaDoc dstR = workingRaster.getBounds();
389         if ((dstR.x < srcR.x) ||
390             (dstR.y < srcR.y) ||
391             (dstR.x+dstR.width > srcR.x+srcR.width) ||
392             (dstR.y+dstR.height > srcR.y+srcR.height))
393             cr = new PadRed(cr, dstR, PadMode.ZERO_PAD, null);
394
395         // Ensure only one thread works on baseRaster at a time...
396
synchronized (syncRaster) {
397             cr.copyData(copyRaster);
398         }
399
400         if (!HaltingThread.hasBeenHalted()) {
401             // Swap the buffers if the rendering completed cleanly.
402
BufferedImage JavaDoc tmpBI = workingOffScreen;
403
404             workingBaseRaster = currentBaseRaster;
405             workingRaster = currentRaster;
406             workingOffScreen = currentOffScreen;
407
408             currentRaster = copyRaster;
409             currentBaseRaster = syncRaster;
410             currentOffScreen = tmpBI;
411
412             // System.out.println("Current offscreen : " + currentOffScreen);
413
}
414     }
415
416     /**
417      * Flush any cached image data.
418      */

419     public void flush() {
420         if (lastCache == null) return;
421         Object JavaDoc o = lastCache.get();
422         if (o == null) return;
423         
424         TileCacheRed tcr = (TileCacheRed)o;
425         tcr.flushCache(tcr.getBounds());
426     }
427
428     /**
429      * Flush a list of rectangles of cached image data.
430      */

431     public void flush(Collection JavaDoc areas) {
432         AffineTransform JavaDoc at = getTransform();
433         Iterator JavaDoc i = areas.iterator();
434         while (i.hasNext()) {
435             Shape JavaDoc s = (Shape JavaDoc)i.next();
436             Rectangle JavaDoc r = at.createTransformedShape(s).getBounds();
437             flush(r);
438         }
439     }
440
441     /**
442      * Flush a rectangle of cached image data.
443      */

444     public void flush(Rectangle JavaDoc r) {
445         if (lastCache == null) return;
446         Object JavaDoc o = lastCache.get();
447         if (o == null) return;
448
449         TileCacheRed tcr = (TileCacheRed)o;
450         r = (Rectangle JavaDoc)r.clone();
451         r.x -= Math.round((float)usr2dev.getTranslateX());
452         r.y -= Math.round((float)usr2dev.getTranslateY());
453         // System.out.println("Flushing Rect:" + r);
454
tcr.flushCache(r);
455     }
456
457     protected CachableRed setupCache(CachableRed img) {
458         if ((lastCR == null) ||
459             (img != lastCR.get())) {
460             lastCR = new SoftReference JavaDoc(img);
461             lastCache = null;
462         }
463
464         Object JavaDoc o = null;
465         if (lastCache != null)
466             o = lastCache.get();
467         if (o != null)
468             return (CachableRed)o;
469
470         img = new TileCacheRed(img);
471         lastCache = new SoftReference JavaDoc(img);
472         return img;
473     }
474
475     protected CachableRed renderGNR() {
476         AffineTransform JavaDoc at, rcAT;
477         at = usr2dev;
478         rcAT = new AffineTransform JavaDoc(at.getScaleX(), at.getShearY(),
479                                    at.getShearX(), at.getScaleY(),
480                                    0, 0);
481
482         RenderContext JavaDoc rc = new RenderContext JavaDoc(rcAT, null, renderingHints);
483             
484         RenderedImage JavaDoc ri = rootFilter.createRendering(rc);
485         if (ri == null)
486             return null;
487
488         CachableRed ret;
489         ret = GraphicsUtil.wrap(ri);
490         ret = setupCache(ret);
491         
492         int dx = Math.round((float)at.getTranslateX());
493         int dy = Math.round((float)at.getTranslateY());
494         ret = new TranslateRed(ret, ret.getMinX()+dx, ret.getMinY()+dy);
495         ret = GraphicsUtil.convertTosRGB(ret);
496
497         return ret;
498     }
499
500
501     /**
502      * Internal method used to synchronize local state in response to
503      * various set methods.
504      */

505     protected void updateWorkingBuffers() {
506         if (rootFilter == null) {
507             rootFilter = rootGN.getGraphicsNodeRable(true);
508             rootCR = null;
509         }
510
511         rootCR = renderGNR();
512         if (rootCR == null) {
513             // No image to display so clear everything out...
514
workingRaster = null;
515             workingOffScreen = null;
516             workingBaseRaster = null;
517             
518             currentOffScreen = null;
519             currentBaseRaster = null;
520             currentRaster = null;
521             return;
522         }
523
524         SampleModel JavaDoc sm = rootCR.getSampleModel();
525         int w = offScreenWidth;
526         int h = offScreenHeight;
527
528         int tw = sm.getWidth();
529         int th = sm.getHeight();
530         w = (((w+tw-1)/tw)+1)*tw;
531         h = (((h+th-1)/th)+1)*th;
532
533         if ((workingBaseRaster == null) ||
534             (workingBaseRaster.getWidth() < w) ||
535             (workingBaseRaster.getHeight() < h)) {
536
537             sm = sm.createCompatibleSampleModel(w, h);
538             
539             workingBaseRaster
540                 = Raster.createWritableRaster(sm, new Point JavaDoc(0,0));
541         }
542
543         int tgx = -rootCR.getTileGridXOffset();
544         int tgy = -rootCR.getTileGridYOffset();
545         int xt, yt;
546         if (tgx>=0) xt = tgx/tw;
547         else xt = (tgx-tw+1)/tw;
548         if (tgy>=0) yt = tgy/th;
549         else yt = (tgy-th+1)/th;
550
551         int xloc = xt*tw - tgx;
552         int yloc = yt*th - tgy;
553         
554         // System.out.println("Info: [" +
555
// xloc + "," + yloc + "] [" +
556
// tgx + "," + tgy + "] [" +
557
// xt + "," + yt + "] [" +
558
// tw + "," + th + "]");
559
// This raster should be aligned with cr's tile grid.
560
workingRaster = workingBaseRaster.createWritableChild
561           (0, 0, w, h, xloc, yloc, null);
562
563         workingOffScreen = new BufferedImage JavaDoc
564           (rootCR.getColorModel(),
565            workingRaster.createWritableChild (0, 0, offScreenWidth,
566                                            offScreenHeight, 0, 0, null),
567            rootCR.getColorModel().isAlphaPremultiplied(), null);
568
569
570         if (!isDoubleBuffered) {
571             currentOffScreen = workingOffScreen;
572             currentBaseRaster = workingBaseRaster;
573             currentRaster = workingRaster;
574         }
575     }
576 }
577
Popular Tags