libjava/ChangeLog:
[official-gcc.git] / libjava / classpath / gnu / java / awt / java2d / AbstractGraphics2D.java
blob8504659388ca687febf793170634f722c2d6c0be
1 /* AbstractGraphics2D.java -- Abstract Graphics2D implementation
2 Copyright (C) 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 package gnu.java.awt.java2d;
40 import gnu.java.util.LRUCache;
42 import java.awt.AWTError;
43 import java.awt.AlphaComposite;
44 import java.awt.AWTPermission;
45 import java.awt.BasicStroke;
46 import java.awt.Color;
47 import java.awt.Composite;
48 import java.awt.CompositeContext;
49 import java.awt.Dimension;
50 import java.awt.Font;
51 import java.awt.FontMetrics;
52 import java.awt.Graphics;
53 import java.awt.Graphics2D;
54 import java.awt.Image;
55 import java.awt.Paint;
56 import java.awt.PaintContext;
57 import java.awt.Point;
58 import java.awt.Polygon;
59 import java.awt.Rectangle;
60 import java.awt.RenderingHints;
61 import java.awt.Shape;
62 import java.awt.Stroke;
63 import java.awt.Toolkit;
64 import java.awt.RenderingHints.Key;
65 import java.awt.font.FontRenderContext;
66 import java.awt.font.GlyphVector;
67 import java.awt.geom.AffineTransform;
68 import java.awt.geom.Arc2D;
69 import java.awt.geom.Area;
70 import java.awt.geom.Ellipse2D;
71 import java.awt.geom.GeneralPath;
72 import java.awt.geom.Line2D;
73 import java.awt.geom.NoninvertibleTransformException;
74 import java.awt.geom.RoundRectangle2D;
75 import java.awt.image.BufferedImage;
76 import java.awt.image.BufferedImageOp;
77 import java.awt.image.ColorModel;
78 import java.awt.image.DataBuffer;
79 import java.awt.image.FilteredImageSource;
80 import java.awt.image.ImageObserver;
81 import java.awt.image.ImageProducer;
82 import java.awt.image.Raster;
83 import java.awt.image.RenderedImage;
84 import java.awt.image.ReplicateScaleFilter;
85 import java.awt.image.SampleModel;
86 import java.awt.image.WritableRaster;
87 import java.awt.image.renderable.RenderableImage;
88 import java.text.AttributedCharacterIterator;
89 import java.util.Collections;
90 import java.util.HashMap;
91 import java.util.LinkedList;
92 import java.util.Map;
93 import java.util.WeakHashMap;
95 /**
96 * This is a 100% Java implementation of the Java2D rendering pipeline. It is
97 * meant as a base class for Graphics2D implementations.
99 * <h2>Backend interface</h2>
100 * <p>
101 * The backend must at the very least provide a Raster which the the rendering
102 * pipeline can paint into. This must be implemented in
103 * {@link #getDestinationRaster()}. For some backends that might be enough, like
104 * when the target surface can be directly access via the raster (like in
105 * BufferedImages). Other targets need some way to synchronize the raster with
106 * the surface, which can be achieved by implementing the
107 * {@link #updateRaster(Raster, int, int, int, int)} method, which always gets
108 * called after a chunk of data got painted into the raster.
109 * </p>
110 * <p>Alternativly the backend can provide a method for filling Shapes by
111 * overriding the protected method fillShape(). This can be accomplished
112 * by a polygon filling function of the backend. Keep in mind though that
113 * Shapes can be quite complex (i.e. non-convex and containing holes, etc)
114 * which is not supported by all polygon fillers. Also it must be noted
115 * that fillShape() is expected to handle painting and compositing as well as
116 * clipping and transformation. If your backend can't support this natively,
117 * then you can fallback to the implementation in this class. You'll need
118 * to provide a writable Raster then, see above.</p>
119 * <p>Another alternative is to implement fillScanline() which only requires
120 * the backend to be able to draw horizontal lines in device space,
121 * which is usually very cheap.
122 * The implementation should still handle painting and compositing,
123 * but no more clipping and transformation is required by the backend.</p>
124 * <p>The backend is free to provide implementations for the various raw*
125 * methods for optimized AWT 1.1 style painting of some primitives. This should
126 * accelerate painting of Swing greatly. When doing so, the backend must also
127 * keep track of the clip and translation, probably by overriding
128 * some clip and translate methods. Don't forget to message super in such a
129 * case.</p>
131 * <h2>Acceleration options</h2>
132 * <p>
133 * The fact that it is
134 * pure Java makes it a little slow. However, there are several ways of
135 * accelerating the rendering pipeline:
136 * <ol>
137 * <li><em>Optimization hooks for AWT 1.1 - like graphics operations.</em>
138 * The most important methods from the {@link java.awt.Graphics} class
139 * have a corresponding <code>raw*</code> method, which get called when
140 * several optimization conditions are fullfilled. These conditions are
141 * described below. Subclasses can override these methods and delegate
142 * it directly to a native backend.</li>
143 * <li><em>Native PaintContexts and CompositeContext.</em> The implementations
144 * for the 3 PaintContexts and AlphaCompositeContext can be accelerated
145 * using native code. These have proved to two of the most performance
146 * critical points in the rendering pipeline and cannot really be done quickly
147 * in plain Java because they involve lots of shuffling around with large
148 * arrays. In fact, you really would want to let the graphics card to the
149 * work, they are made for this.</li>
150 * <li>Provide an accelerated implementation for fillShape(). For instance,
151 * OpenGL can fill shapes very efficiently. There are some considerations
152 * to be made though, see above for details.</li>
153 * </ol>
154 * </p>
156 * @author Roman Kennke (kennke@aicas.com)
158 public abstract class AbstractGraphics2D
159 extends Graphics2D
160 implements Cloneable, Pixelizer
163 * Caches scaled versions of an image.
165 * @see #drawImage(Image, int, int, int, int, ImageObserver)
167 protected static final WeakHashMap<Image, HashMap<Dimension,Image>> imageCache =
168 new WeakHashMap<Image, HashMap<Dimension, Image>>();
171 * Wether we use anti aliasing for rendering text by default or not.
173 private static final boolean DEFAULT_TEXT_AA =
174 Boolean.getBoolean("gnu.java2d.default_text_aa");
177 * The default font to use on the graphics object.
179 private static final Font FONT = new Font("SansSerif", Font.PLAIN, 12);
182 * The size of the LRU cache used for caching GlyphVectors.
184 private static final int GV_CACHE_SIZE = 50;
187 * Caches certain shapes to avoid massive creation of such Shapes in
188 * the various draw* and fill* methods.
190 private static final ShapeCache shapeCache = new ShapeCache();
193 * A pool of scanline converters. It is important to reuse scanline
194 * converters because they keep their datastructures in place. We pool them
195 * for use in multiple threads.
197 private static final LinkedList<ScanlineConverter> scanlineConverters =
198 new LinkedList<ScanlineConverter>();
201 * Caches glyph vectors for better drawing performance.
203 private static final Map<TextCacheKey,GlyphVector> gvCache =
204 Collections.synchronizedMap(new LRUCache<TextCacheKey,GlyphVector>(GV_CACHE_SIZE));
207 * This key is used to search in the gvCache without allocating a new
208 * key each time.
210 private static final TextCacheKey searchTextKey = new TextCacheKey();
213 * The transformation for this Graphics2D instance
215 protected AffineTransform transform;
218 * The foreground.
220 private Paint paint;
223 * The paint context during rendering.
225 private PaintContext paintContext = null;
228 * The background.
230 private Color background = Color.WHITE;
233 * Foreground color, as set by setColor.
235 private Color foreground = Color.BLACK;
236 private boolean isForegroundColorNull = true;
239 * The current font.
241 private Font font;
244 * The current composite setting.
246 private Composite composite;
249 * The current stroke setting.
251 private Stroke stroke;
254 * The current clip. This clip is in user coordinate space.
256 private Shape clip;
259 * The rendering hints.
261 private RenderingHints renderingHints;
264 * The raster of the destination surface. This is where the painting is
265 * performed.
267 private WritableRaster destinationRaster;
270 * Indicates if certain graphics primitives can be rendered in an optimized
271 * fashion. This will be the case if the following conditions are met:
272 * - The transform may only be a translation, no rotation, shearing or
273 * scaling.
274 * - The paint must be a solid color.
275 * - The composite must be an AlphaComposite.SrcOver.
276 * - The clip must be a Rectangle.
277 * - The stroke must be a plain BasicStroke().
279 * These conditions represent the standard settings of a new
280 * AbstractGraphics2D object and will be the most commonly used setting
281 * in Swing rendering and should therefore be optimized as much as possible.
283 private boolean isOptimized = true;
285 private static final BasicStroke STANDARD_STROKE = new BasicStroke();
287 private static final HashMap<Key, Object> STANDARD_HINTS;
288 static
291 HashMap<Key, Object> hints = new HashMap<Key, Object>();
292 hints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
293 RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
294 hints.put(RenderingHints.KEY_ANTIALIASING,
295 RenderingHints.VALUE_ANTIALIAS_DEFAULT);
297 STANDARD_HINTS = hints;
301 * Creates a new AbstractGraphics2D instance.
303 protected AbstractGraphics2D()
305 transform = new AffineTransform();
306 background = Color.WHITE;
307 composite = AlphaComposite.SrcOver;
308 stroke = STANDARD_STROKE;
309 renderingHints = new RenderingHints(STANDARD_HINTS);
313 * Draws the specified shape. The shape is passed through the current stroke
314 * and is then forwarded to {@link #fillShape}.
316 * @param shape the shape to draw
318 public void draw(Shape shape)
320 // Stroke the shape.
321 Shape strokedShape = stroke.createStrokedShape(shape);
322 // Fill the stroked shape.
323 fillShape(strokedShape, false);
328 * Draws the specified image and apply the transform for image space ->
329 * user space conversion.
331 * This method is implemented to special case RenderableImages and
332 * RenderedImages and delegate to
333 * {@link #drawRenderableImage(RenderableImage, AffineTransform)} and
334 * {@link #drawRenderedImage(RenderedImage, AffineTransform)} accordingly.
335 * Other image types are not yet handled.
337 * @param image the image to be rendered
338 * @param xform the transform from image space to user space
339 * @param obs the image observer to be notified
341 public boolean drawImage(Image image, AffineTransform xform,
342 ImageObserver obs)
344 Rectangle areaOfInterest = new Rectangle(0, 0, image.getWidth(obs),
345 image.getHeight(obs));
346 return drawImageImpl(image, xform, obs, areaOfInterest);
350 * Draws the specified image and apply the transform for image space ->
351 * user space conversion. This method only draw the part of the image
352 * specified by <code>areaOfInterest</code>.
354 * This method is implemented to special case RenderableImages and
355 * RenderedImages and delegate to
356 * {@link #drawRenderableImage(RenderableImage, AffineTransform)} and
357 * {@link #drawRenderedImage(RenderedImage, AffineTransform)} accordingly.
358 * Other image types are not yet handled.
360 * @param image the image to be rendered
361 * @param xform the transform from image space to user space
362 * @param obs the image observer to be notified
363 * @param areaOfInterest the area in image space that is rendered
365 private boolean drawImageImpl(Image image, AffineTransform xform,
366 ImageObserver obs, Rectangle areaOfInterest)
368 boolean ret;
369 if (image == null)
371 ret = true;
373 else if (image instanceof RenderedImage)
375 // FIXME: Handle the ImageObserver.
376 drawRenderedImageImpl((RenderedImage) image, xform, areaOfInterest);
377 ret = true;
379 else if (image instanceof RenderableImage)
381 // FIXME: Handle the ImageObserver.
382 drawRenderableImageImpl((RenderableImage) image, xform, areaOfInterest);
383 ret = true;
385 else
387 // FIXME: Implement rendering of other Image types.
388 ret = false;
390 return ret;
394 * Renders a BufferedImage and applies the specified BufferedImageOp before
395 * to filter the BufferedImage somehow. The resulting BufferedImage is then
396 * passed on to {@link #drawRenderedImage(RenderedImage, AffineTransform)}
397 * to perform the final rendering.
399 * @param image the source buffered image
400 * @param op the filter to apply to the buffered image before rendering
401 * @param x the x coordinate to render the image to
402 * @param y the y coordinate to render the image to
404 public void drawImage(BufferedImage image, BufferedImageOp op, int x, int y)
406 BufferedImage filtered =
407 op.createCompatibleDestImage(image, image.getColorModel());
408 AffineTransform t = new AffineTransform();
409 t.translate(x, y);
410 drawRenderedImage(filtered, t);
414 * Renders the specified image to the destination raster. The specified
415 * transform is used to convert the image into user space. The transform
416 * of this AbstractGraphics2D object is used to transform from user space
417 * to device space.
419 * The rendering is performed using the scanline algorithm that performs the
420 * rendering of other shapes and a custom Paint implementation, that supplies
421 * the pixel values of the rendered image.
423 * @param image the image to render to the destination raster
424 * @param xform the transform from image space to user space
426 public void drawRenderedImage(RenderedImage image, AffineTransform xform)
428 Rectangle areaOfInterest = new Rectangle(image.getMinX(),
429 image.getHeight(),
430 image.getWidth(),
431 image.getHeight());
432 drawRenderedImageImpl(image, xform, areaOfInterest);
436 * Renders the specified image to the destination raster. The specified
437 * transform is used to convert the image into user space. The transform
438 * of this AbstractGraphics2D object is used to transform from user space
439 * to device space. Only the area specified by <code>areaOfInterest</code>
440 * is finally rendered to the target.
442 * The rendering is performed using the scanline algorithm that performs the
443 * rendering of other shapes and a custom Paint implementation, that supplies
444 * the pixel values of the rendered image.
446 * @param image the image to render to the destination raster
447 * @param xform the transform from image space to user space
449 private void drawRenderedImageImpl(RenderedImage image,
450 AffineTransform xform,
451 Rectangle areaOfInterest)
453 // First we compute the transformation. This is made up of 3 parts:
454 // 1. The areaOfInterest -> image space transform.
455 // 2. The image space -> user space transform.
456 // 3. The user space -> device space transform.
457 AffineTransform t = new AffineTransform();
458 t.translate(- areaOfInterest.x - image.getMinX(),
459 - areaOfInterest.y - image.getMinY());
460 t.concatenate(xform);
461 t.concatenate(transform);
462 AffineTransform it = null;
465 it = t.createInverse();
467 catch (NoninvertibleTransformException ex)
469 // Ignore -- we return if the transform is not invertible.
471 if (it != null)
473 // Transform the area of interest into user space.
474 GeneralPath aoi = new GeneralPath(areaOfInterest);
475 aoi.transform(xform);
476 // Render the shape using the standard renderer, but with a temporary
477 // ImagePaint.
478 ImagePaint p = new ImagePaint(image, it);
479 Paint savedPaint = paint;
482 paint = p;
483 fillShape(aoi, false);
485 finally
487 paint = savedPaint;
493 * Renders a renderable image. This produces a RenderedImage, which is
494 * then passed to {@link #drawRenderedImage(RenderedImage, AffineTransform)}
495 * to perform the final rendering.
497 * @param image the renderable image to be rendered
498 * @param xform the transform from image space to user space
500 public void drawRenderableImage(RenderableImage image, AffineTransform xform)
502 Rectangle areaOfInterest = new Rectangle((int) image.getMinX(),
503 (int) image.getHeight(),
504 (int) image.getWidth(),
505 (int) image.getHeight());
506 drawRenderableImageImpl(image, xform, areaOfInterest);
511 * Renders a renderable image. This produces a RenderedImage, which is
512 * then passed to {@link #drawRenderedImage(RenderedImage, AffineTransform)}
513 * to perform the final rendering. Only the area of the image specified
514 * by <code>areaOfInterest</code> is rendered.
516 * @param image the renderable image to be rendered
517 * @param xform the transform from image space to user space
519 private void drawRenderableImageImpl(RenderableImage image,
520 AffineTransform xform,
521 Rectangle areaOfInterest)
523 // TODO: Maybe make more clever usage of a RenderContext here.
524 RenderedImage rendered = image.createDefaultRendering();
525 drawRenderedImageImpl(rendered, xform, areaOfInterest);
529 * Draws the specified string at the specified location.
531 * @param text the string to draw
532 * @param x the x location, relative to the bounding rectangle of the text
533 * @param y the y location, relative to the bounding rectangle of the text
535 public void drawString(String text, int x, int y)
537 GlyphVector gv;
538 synchronized (searchTextKey)
540 TextCacheKey tck = searchTextKey;
541 FontRenderContext frc = getFontRenderContext();
542 tck.setString(text);
543 tck.setFont(font);
544 tck.setFontRenderContext(frc);
545 if (gvCache.containsKey(tck))
547 gv = gvCache.get(tck);
549 else
551 gv = font.createGlyphVector(frc, text.toCharArray());
552 gvCache.put(new TextCacheKey(text, font, frc), gv);
555 drawGlyphVector(gv, x, y);
559 * Draws the specified string at the specified location.
561 * @param text the string to draw
562 * @param x the x location, relative to the bounding rectangle of the text
563 * @param y the y location, relative to the bounding rectangle of the text
565 public void drawString(String text, float x, float y)
567 FontRenderContext ctx = getFontRenderContext();
568 GlyphVector gv = font.createGlyphVector(ctx, text.toCharArray());
569 drawGlyphVector(gv, x, y);
573 * Draws the specified string (as AttributedCharacterIterator) at the
574 * specified location.
576 * @param iterator the string to draw
577 * @param x the x location, relative to the bounding rectangle of the text
578 * @param y the y location, relative to the bounding rectangle of the text
580 public void drawString(AttributedCharacterIterator iterator, int x, int y)
582 FontRenderContext ctx = getFontRenderContext();
583 GlyphVector gv = font.createGlyphVector(ctx, iterator);
584 drawGlyphVector(gv, x, y);
588 * Draws the specified string (as AttributedCharacterIterator) at the
589 * specified location.
591 * @param iterator the string to draw
592 * @param x the x location, relative to the bounding rectangle of the text
593 * @param y the y location, relative to the bounding rectangle of the text
595 public void drawString(AttributedCharacterIterator iterator, float x, float y)
597 FontRenderContext ctx = getFontRenderContext();
598 GlyphVector gv = font.createGlyphVector(ctx, iterator);
599 drawGlyphVector(gv, x, y);
603 * Fills the specified shape with the current foreground.
605 * @param shape the shape to fill
607 public void fill(Shape shape)
609 fillShape(shape, false);
612 public boolean hit(Rectangle rect, Shape text, boolean onStroke)
614 // FIXME: Implement this.
615 throw new UnsupportedOperationException("Not yet implemented");
619 * Sets the composite.
621 * @param comp the composite to set
623 public void setComposite(Composite comp)
625 if (! (comp instanceof AlphaComposite))
627 // FIXME: this check is only required "if this Graphics2D
628 // context is drawing to a Component on the display screen".
629 SecurityManager sm = System.getSecurityManager();
630 if (sm != null)
631 sm.checkPermission(new AWTPermission("readDisplayPixels"));
634 composite = comp;
635 if (! (comp.equals(AlphaComposite.SrcOver)))
636 isOptimized = false;
637 else
638 updateOptimization();
642 * Sets the current foreground.
644 * @param p the foreground to set.
646 public void setPaint(Paint p)
648 if (p != null)
650 paint = p;
652 if (! (paint instanceof Color))
654 isOptimized = false;
656 else
658 this.foreground = (Color) paint;
659 isForegroundColorNull = false;
660 updateOptimization();
663 else
665 this.foreground = Color.BLACK;
666 isForegroundColorNull = true;
669 // free resources if needed, then put the paint context to null
670 if (this.paintContext != null)
671 this.paintContext.dispose();
673 this.paintContext = null;
677 * Sets the stroke for this graphics object.
679 * @param s the stroke to set
681 public void setStroke(Stroke s)
683 stroke = s;
684 if (! stroke.equals(new BasicStroke()))
685 isOptimized = false;
686 else
687 updateOptimization();
691 * Sets the specified rendering hint.
693 * @param hintKey the key of the rendering hint
694 * @param hintValue the value
696 public void setRenderingHint(Key hintKey, Object hintValue)
698 renderingHints.put(hintKey, hintValue);
702 * Returns the rendering hint for the specified key.
704 * @param hintKey the rendering hint key
706 * @return the rendering hint for the specified key
708 public Object getRenderingHint(Key hintKey)
710 return renderingHints.get(hintKey);
714 * Sets the specified rendering hints.
716 * @param hints the rendering hints to set
718 public void setRenderingHints(Map hints)
720 renderingHints.clear();
721 renderingHints.putAll(hints);
725 * Adds the specified rendering hints.
727 * @param hints the rendering hints to add
729 public void addRenderingHints(Map hints)
731 renderingHints.putAll(hints);
735 * Returns the current rendering hints.
737 * @return the current rendering hints
739 public RenderingHints getRenderingHints()
741 return (RenderingHints) renderingHints.clone();
745 * Translates the coordinate system by (x, y).
747 * @param x the translation X coordinate
748 * @param y the translation Y coordinate
750 public void translate(int x, int y)
752 transform.translate(x, y);
754 // Update the clip. We special-case rectangular clips here, because they
755 // are so common (e.g. in Swing).
756 if (clip != null)
758 if (clip instanceof Rectangle)
760 Rectangle r = (Rectangle) clip;
761 r.x -= x;
762 r.y -= y;
763 setClip(r);
765 else
767 AffineTransform clipTransform = new AffineTransform();
768 clipTransform.translate(-x, -y);
769 updateClip(clipTransform);
775 * Translates the coordinate system by (tx, ty).
777 * @param tx the translation X coordinate
778 * @param ty the translation Y coordinate
780 public void translate(double tx, double ty)
782 transform.translate(tx, ty);
784 // Update the clip. We special-case rectangular clips here, because they
785 // are so common (e.g. in Swing).
786 if (clip != null)
788 if (clip instanceof Rectangle)
790 Rectangle r = (Rectangle) clip;
791 r.x -= tx;
792 r.y -= ty;
794 else
796 AffineTransform clipTransform = new AffineTransform();
797 clipTransform.translate(-tx, -ty);
798 updateClip(clipTransform);
804 * Rotates the coordinate system by <code>theta</code> degrees.
806 * @param theta the angle be which to rotate the coordinate system
808 public void rotate(double theta)
810 transform.rotate(theta);
811 if (clip != null)
813 AffineTransform clipTransform = new AffineTransform();
814 clipTransform.rotate(-theta);
815 updateClip(clipTransform);
817 updateOptimization();
821 * Rotates the coordinate system by <code>theta</code> around the point
822 * (x, y).
824 * @param theta the angle by which to rotate the coordinate system
825 * @param x the point around which to rotate, X coordinate
826 * @param y the point around which to rotate, Y coordinate
828 public void rotate(double theta, double x, double y)
830 transform.rotate(theta, x, y);
831 if (clip != null)
833 AffineTransform clipTransform = new AffineTransform();
834 clipTransform.rotate(-theta, x, y);
835 updateClip(clipTransform);
837 updateOptimization();
841 * Scales the coordinate system by the factors <code>scaleX</code> and
842 * <code>scaleY</code>.
844 * @param scaleX the factor by which to scale the X axis
845 * @param scaleY the factor by which to scale the Y axis
847 public void scale(double scaleX, double scaleY)
849 transform.scale(scaleX, scaleY);
850 if (clip != null)
852 AffineTransform clipTransform = new AffineTransform();
853 clipTransform.scale(1 / scaleX, 1 / scaleY);
854 updateClip(clipTransform);
856 updateOptimization();
860 * Shears the coordinate system by <code>shearX</code> and
861 * <code>shearY</code>.
863 * @param shearX the X shearing
864 * @param shearY the Y shearing
866 public void shear(double shearX, double shearY)
868 transform.shear(shearX, shearY);
869 if (clip != null)
871 AffineTransform clipTransform = new AffineTransform();
872 clipTransform.shear(-shearX, -shearY);
873 updateClip(clipTransform);
875 updateOptimization();
879 * Transforms the coordinate system using the specified transform
880 * <code>t</code>.
882 * @param t the transform
884 public void transform(AffineTransform t)
886 transform.concatenate(t);
889 AffineTransform clipTransform = t.createInverse();
890 updateClip(clipTransform);
892 catch (NoninvertibleTransformException ex)
894 // TODO: How can we deal properly with this?
895 ex.printStackTrace();
897 updateOptimization();
901 * Sets the transformation for this Graphics object.
903 * @param t the transformation to set
905 public void setTransform(AffineTransform t)
907 // Transform clip into target space using the old transform.
908 updateClip(transform);
909 transform.setTransform(t);
910 // Transform the clip back into user space using the inverse new transform.
913 updateClip(transform.createInverse());
915 catch (NoninvertibleTransformException ex)
917 // TODO: How can we deal properly with this?
918 ex.printStackTrace();
920 updateOptimization();
924 * Returns the transformation of this coordinate system.
926 * @return the transformation of this coordinate system
928 public AffineTransform getTransform()
930 return (AffineTransform) transform.clone();
934 * Returns the current foreground.
936 * @return the current foreground
938 public Paint getPaint()
940 return paint;
945 * Returns the current composite.
947 * @return the current composite
949 public Composite getComposite()
951 return composite;
955 * Sets the current background.
957 * @param color the background to set.
959 public void setBackground(Color color)
961 background = color;
965 * Returns the current background.
967 * @return the current background
969 public Color getBackground()
971 return background;
975 * Returns the current stroke.
977 * @return the current stroke
979 public Stroke getStroke()
981 return stroke;
985 * Intersects the clip of this graphics object with the specified clip.
987 * @param s the clip with which the current clip should be intersected
989 public void clip(Shape s)
991 // Initialize clip if not already present.
992 if (clip == null)
993 setClip(s);
995 // This is so common, let's optimize this.
996 else if (clip instanceof Rectangle && s instanceof Rectangle)
998 Rectangle clipRect = (Rectangle) clip;
999 Rectangle r = (Rectangle) s;
1000 computeIntersection(r.x, r.y, r.width, r.height, clipRect);
1001 // Call setClip so that subclasses get notified.
1002 setClip(clipRect);
1004 else
1006 Area current;
1007 if (clip instanceof Area)
1008 current = (Area) clip;
1009 else
1010 current = new Area(clip);
1012 Area intersect;
1013 if (s instanceof Area)
1014 intersect = (Area) s;
1015 else
1016 intersect = new Area(s);
1018 current.intersect(intersect);
1019 clip = current;
1020 isOptimized = false;
1021 // Call setClip so that subclasses get notified.
1022 setClip(clip);
1026 public FontRenderContext getFontRenderContext()
1028 // Protect our own transform from beeing modified.
1029 AffineTransform tf = new AffineTransform(transform);
1030 // TODO: Determine antialias and fractionalmetrics parameters correctly.
1031 return new FontRenderContext(tf, false, true);
1035 * Draws the specified glyph vector at the specified location.
1037 * @param gv the glyph vector to draw
1038 * @param x the location, x coordinate
1039 * @param y the location, y coordinate
1041 public void drawGlyphVector(GlyphVector gv, float x, float y)
1043 translate(x, y);
1044 fillShape(gv.getOutline(), true);
1045 translate(-x, -y);
1049 * Creates a copy of this graphics object.
1051 * @return a copy of this graphics object
1053 public Graphics create()
1055 AbstractGraphics2D copy = (AbstractGraphics2D) clone();
1056 return copy;
1060 * Creates and returns a copy of this Graphics object. This should
1061 * be overridden by subclasses if additional state must be handled when
1062 * cloning. This is called by {@link #create()}.
1064 * @return a copy of this Graphics object
1066 protected Object clone()
1070 AbstractGraphics2D copy = (AbstractGraphics2D) super.clone();
1071 // Copy the clip. If it's a Rectangle, preserve that for optimization.
1072 if (clip instanceof Rectangle)
1073 copy.clip = new Rectangle((Rectangle) clip);
1074 else if (clip != null)
1075 copy.clip = new GeneralPath(clip);
1076 else
1077 copy.clip = null;
1079 copy.renderingHints = new RenderingHints(null);
1080 copy.renderingHints.putAll(renderingHints);
1081 copy.transform = new AffineTransform(transform);
1082 // The remaining state is inmmutable and doesn't need to be copied.
1083 return copy;
1085 catch (CloneNotSupportedException ex)
1087 AWTError err = new AWTError("Unexpected exception while cloning");
1088 err.initCause(ex);
1089 throw err;
1094 * Returns the current foreground.
1096 public Color getColor()
1098 if (isForegroundColorNull)
1099 return null;
1101 return this.foreground;
1105 * Sets the current foreground.
1107 * @param color the foreground to set
1109 public void setColor(Color color)
1111 this.setPaint(color);
1114 public void setPaintMode()
1116 // FIXME: Implement this.
1117 throw new UnsupportedOperationException("Not yet implemented");
1120 public void setXORMode(Color color)
1122 // FIXME: Implement this.
1123 throw new UnsupportedOperationException("Not yet implemented");
1127 * Returns the current font.
1129 * @return the current font
1131 public Font getFont()
1133 return font;
1137 * Sets the font on this graphics object. When <code>f == null</code>, the
1138 * current setting is not changed.
1140 * @param f the font to set
1142 public void setFont(Font f)
1144 if (f != null)
1145 font = f;
1149 * Returns the font metrics for the specified font.
1151 * @param font the font for which to fetch the font metrics
1153 * @return the font metrics for the specified font
1155 public FontMetrics getFontMetrics(Font font)
1157 return Toolkit.getDefaultToolkit().getFontMetrics(font);
1161 * Returns the bounds of the current clip.
1163 * @return the bounds of the current clip
1165 public Rectangle getClipBounds()
1167 Rectangle b = null;
1168 if (clip != null)
1169 b = clip.getBounds();
1170 return b;
1174 * Intersects the current clipping region with the specified rectangle.
1176 * @param x the x coordinate of the rectangle
1177 * @param y the y coordinate of the rectangle
1178 * @param width the width of the rectangle
1179 * @param height the height of the rectangle
1181 public void clipRect(int x, int y, int width, int height)
1183 clip(new Rectangle(x, y, width, height));
1187 * Sets the clip to the specified rectangle.
1189 * @param x the x coordinate of the clip rectangle
1190 * @param y the y coordinate of the clip rectangle
1191 * @param width the width of the clip rectangle
1192 * @param height the height of the clip rectangle
1194 public void setClip(int x, int y, int width, int height)
1196 setClip(new Rectangle(x, y, width, height));
1200 * Returns the current clip.
1202 * @return the current clip
1204 public Shape getClip()
1206 return clip;
1210 * Sets the current clipping area to <code>clip</code>.
1212 * @param c the clip to set
1214 public void setClip(Shape c)
1216 clip = c;
1217 if (! (clip instanceof Rectangle))
1218 isOptimized = false;
1219 else
1220 updateOptimization();
1223 public void copyArea(int x, int y, int width, int height, int dx, int dy)
1225 if (isOptimized)
1226 rawCopyArea(x, y, width, height, dx, dy);
1227 else
1228 copyAreaImpl(x, y, width, height, dx, dy);
1232 * Draws a line from (x1, y1) to (x2, y2).
1234 * This implementation transforms the coordinates and forwards the call to
1235 * {@link #rawDrawLine}.
1237 public void drawLine(int x1, int y1, int x2, int y2)
1239 if (isOptimized)
1241 int tx = (int) transform.getTranslateX();
1242 int ty = (int) transform.getTranslateY();
1243 rawDrawLine(x1 + tx, y1 + ty, x2 + tx, y2 + ty);
1245 else
1247 ShapeCache sc = shapeCache;
1248 if (sc.line == null)
1249 sc.line = new Line2D.Float();
1250 sc.line.setLine(x1, y1, x2, y2);
1251 draw(sc.line);
1255 public void drawRect(int x, int y, int w, int h)
1257 if (isOptimized)
1259 int tx = (int) transform.getTranslateX();
1260 int ty = (int) transform.getTranslateY();
1261 rawDrawRect(x + tx, y + ty, w, h);
1263 else
1265 ShapeCache sc = shapeCache;
1266 if (sc.rect == null)
1267 sc.rect = new Rectangle();
1268 sc.rect.setBounds(x, y, w, h);
1269 draw(sc.rect);
1274 * Fills a rectangle with the current paint.
1276 * @param x the upper left corner, X coordinate
1277 * @param y the upper left corner, Y coordinate
1278 * @param width the width of the rectangle
1279 * @param height the height of the rectangle
1281 public void fillRect(int x, int y, int width, int height)
1283 if (isOptimized)
1285 rawFillRect(x + (int) transform.getTranslateX(),
1286 y + (int) transform.getTranslateY(), width, height);
1288 else
1290 ShapeCache sc = shapeCache;
1291 if (sc.rect == null)
1292 sc.rect = new Rectangle();
1293 sc.rect.setBounds(x, y, width, height);
1294 fill(sc.rect);
1299 * Fills a rectangle with the current background color.
1301 * This implementation temporarily sets the foreground color to the
1302 * background and forwards the call to {@link #fillRect(int, int, int, int)}.
1304 * @param x the upper left corner, X coordinate
1305 * @param y the upper left corner, Y coordinate
1306 * @param width the width of the rectangle
1307 * @param height the height of the rectangle
1309 public void clearRect(int x, int y, int width, int height)
1311 if (isOptimized)
1312 rawClearRect(x, y, width, height);
1313 else
1315 Paint savedForeground = getPaint();
1316 setPaint(getBackground());
1317 fillRect(x, y, width, height);
1318 setPaint(savedForeground);
1323 * Draws a rounded rectangle.
1325 * @param x the x coordinate of the rectangle
1326 * @param y the y coordinate of the rectangle
1327 * @param width the width of the rectangle
1328 * @param height the height of the rectangle
1329 * @param arcWidth the width of the arcs
1330 * @param arcHeight the height of the arcs
1332 public void drawRoundRect(int x, int y, int width, int height, int arcWidth,
1333 int arcHeight)
1335 ShapeCache sc = shapeCache;
1336 if (sc.roundRect == null)
1337 sc.roundRect = new RoundRectangle2D.Float();
1338 sc.roundRect.setRoundRect(x, y, width, height, arcWidth, arcHeight);
1339 draw(sc.roundRect);
1343 * Fills a rounded rectangle.
1345 * @param x the x coordinate of the rectangle
1346 * @param y the y coordinate of the rectangle
1347 * @param width the width of the rectangle
1348 * @param height the height of the rectangle
1349 * @param arcWidth the width of the arcs
1350 * @param arcHeight the height of the arcs
1352 public void fillRoundRect(int x, int y, int width, int height, int arcWidth,
1353 int arcHeight)
1355 ShapeCache sc = shapeCache;
1356 if (sc.roundRect == null)
1357 sc.roundRect = new RoundRectangle2D.Float();
1358 sc.roundRect.setRoundRect(x, y, width, height, arcWidth, arcHeight);
1359 fill(sc.roundRect);
1363 * Draws the outline of an oval.
1365 * @param x the upper left corner of the bounding rectangle of the ellipse
1366 * @param y the upper left corner of the bounding rectangle of the ellipse
1367 * @param width the width of the ellipse
1368 * @param height the height of the ellipse
1370 public void drawOval(int x, int y, int width, int height)
1372 ShapeCache sc = shapeCache;
1373 if (sc.ellipse == null)
1374 sc.ellipse = new Ellipse2D.Float();
1375 sc.ellipse.setFrame(x, y, width, height);
1376 draw(sc.ellipse);
1380 * Fills an oval.
1382 * @param x the upper left corner of the bounding rectangle of the ellipse
1383 * @param y the upper left corner of the bounding rectangle of the ellipse
1384 * @param width the width of the ellipse
1385 * @param height the height of the ellipse
1387 public void fillOval(int x, int y, int width, int height)
1389 ShapeCache sc = shapeCache;
1390 if (sc.ellipse == null)
1391 sc.ellipse = new Ellipse2D.Float();
1392 sc.ellipse.setFrame(x, y, width, height);
1393 fill(sc.ellipse);
1397 * Draws an arc.
1399 public void drawArc(int x, int y, int width, int height, int arcStart,
1400 int arcAngle)
1402 ShapeCache sc = shapeCache;
1403 if (sc.arc == null)
1404 sc.arc = new Arc2D.Float();
1405 sc.arc.setArc(x, y, width, height, arcStart, arcAngle, Arc2D.OPEN);
1406 draw(sc.arc);
1410 * Fills an arc.
1412 public void fillArc(int x, int y, int width, int height, int arcStart,
1413 int arcAngle)
1415 ShapeCache sc = shapeCache;
1416 if (sc.arc == null)
1417 sc.arc = new Arc2D.Float();
1418 sc.arc.setArc(x, y, width, height, arcStart, arcAngle, Arc2D.PIE);
1419 draw(sc.arc);
1422 public void drawPolyline(int[] xPoints, int[] yPoints, int npoints)
1424 ShapeCache sc = shapeCache;
1425 if (sc.polyline == null)
1426 sc.polyline = new GeneralPath();
1427 GeneralPath p = sc.polyline;
1428 p.reset();
1429 if (npoints > 0)
1430 p.moveTo(xPoints[0], yPoints[0]);
1431 for (int i = 1; i < npoints; i++)
1432 p.lineTo(xPoints[i], yPoints[i]);
1433 fill(p);
1437 * Draws the outline of a polygon.
1439 public void drawPolygon(int[] xPoints, int[] yPoints, int npoints)
1441 ShapeCache sc = shapeCache;
1442 if (sc.polygon == null)
1443 sc.polygon = new Polygon();
1444 sc.polygon.reset();
1445 sc.polygon.xpoints = xPoints;
1446 sc.polygon.ypoints = yPoints;
1447 sc.polygon.npoints = npoints;
1448 draw(sc.polygon);
1452 * Fills the outline of a polygon.
1454 public void fillPolygon(int[] xPoints, int[] yPoints, int npoints)
1456 ShapeCache sc = shapeCache;
1457 if (sc.polygon == null)
1458 sc.polygon = new Polygon();
1459 sc.polygon.reset();
1460 sc.polygon.xpoints = xPoints;
1461 sc.polygon.ypoints = yPoints;
1462 sc.polygon.npoints = npoints;
1463 fill(sc.polygon);
1467 * Draws the specified image at the specified location. This forwards
1468 * to {@link #drawImage(Image, AffineTransform, ImageObserver)}.
1470 * @param image the image to render
1471 * @param x the x location to render to
1472 * @param y the y location to render to
1473 * @param observer the image observer to receive notification
1475 public boolean drawImage(Image image, int x, int y, ImageObserver observer)
1477 boolean ret;
1478 if (isOptimized)
1480 ret = rawDrawImage(image, x + (int) transform.getTranslateX(),
1481 y + (int) transform.getTranslateY(), observer);
1483 else
1485 AffineTransform t = new AffineTransform();
1486 t.translate(x, y);
1487 ret = drawImage(image, t, observer);
1489 return ret;
1493 * Draws the specified image at the specified location. The image
1494 * is scaled to the specified width and height. This forwards
1495 * to {@link #drawImage(Image, AffineTransform, ImageObserver)}.
1497 * @param image the image to render
1498 * @param x the x location to render to
1499 * @param y the y location to render to
1500 * @param width the target width of the image
1501 * @param height the target height of the image
1502 * @param observer the image observer to receive notification
1504 public boolean drawImage(Image image, int x, int y, int width, int height,
1505 ImageObserver observer)
1507 AffineTransform t = new AffineTransform();
1508 int imWidth = image.getWidth(observer);
1509 int imHeight = image.getHeight(observer);
1510 if (imWidth == width && imHeight == height)
1512 // No need to scale, fall back to non-scaling loops.
1513 return drawImage(image, x, y, observer);
1515 else
1517 Image scaled = prepareImage(image, width, height);
1518 // Ideally, this should notify the observer about the scaling progress.
1519 return drawImage(scaled, x, y, observer);
1524 * Draws the specified image at the specified location. This forwards
1525 * to {@link #drawImage(Image, AffineTransform, ImageObserver)}.
1527 * @param image the image to render
1528 * @param x the x location to render to
1529 * @param y the y location to render to
1530 * @param bgcolor the background color to use for transparent pixels
1531 * @param observer the image observer to receive notification
1533 public boolean drawImage(Image image, int x, int y, Color bgcolor,
1534 ImageObserver observer)
1536 AffineTransform t = new AffineTransform();
1537 t.translate(x, y);
1538 // TODO: Somehow implement the background option.
1539 return drawImage(image, t, observer);
1543 * Draws the specified image at the specified location. The image
1544 * is scaled to the specified width and height. This forwards
1545 * to {@link #drawImage(Image, AffineTransform, ImageObserver)}.
1547 * @param image the image to render
1548 * @param x the x location to render to
1549 * @param y the y location to render to
1550 * @param width the target width of the image
1551 * @param height the target height of the image
1552 * @param bgcolor the background color to use for transparent pixels
1553 * @param observer the image observer to receive notification
1555 public boolean drawImage(Image image, int x, int y, int width, int height,
1556 Color bgcolor, ImageObserver observer)
1558 AffineTransform t = new AffineTransform();
1559 t.translate(x, y);
1560 double scaleX = (double) image.getWidth(observer) / (double) width;
1561 double scaleY = (double) image.getHeight(observer) / (double) height;
1562 t.scale(scaleX, scaleY);
1563 // TODO: Somehow implement the background option.
1564 return drawImage(image, t, observer);
1568 * Draws an image fragment to a rectangular area of the target.
1570 * @param image the image to render
1571 * @param dx1 the first corner of the destination rectangle
1572 * @param dy1 the first corner of the destination rectangle
1573 * @param dx2 the second corner of the destination rectangle
1574 * @param dy2 the second corner of the destination rectangle
1575 * @param sx1 the first corner of the source rectangle
1576 * @param sy1 the first corner of the source rectangle
1577 * @param sx2 the second corner of the source rectangle
1578 * @param sy2 the second corner of the source rectangle
1579 * @param observer the image observer to be notified
1581 public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2,
1582 int sx1, int sy1, int sx2, int sy2,
1583 ImageObserver observer)
1585 int sx = Math.min(sx1, sx1);
1586 int sy = Math.min(sy1, sy2);
1587 int sw = Math.abs(sx1 - sx2);
1588 int sh = Math.abs(sy1 - sy2);
1589 int dx = Math.min(dx1, dx1);
1590 int dy = Math.min(dy1, dy2);
1591 int dw = Math.abs(dx1 - dx2);
1592 int dh = Math.abs(dy1 - dy2);
1594 AffineTransform t = new AffineTransform();
1595 t.translate(sx - dx, sy - dy);
1596 double scaleX = (double) sw / (double) dw;
1597 double scaleY = (double) sh / (double) dh;
1598 t.scale(scaleX, scaleY);
1599 Rectangle areaOfInterest = new Rectangle(sx, sy, sw, sh);
1600 return drawImageImpl(image, t, observer, areaOfInterest);
1604 * Draws an image fragment to a rectangular area of the target.
1606 * @param image the image to render
1607 * @param dx1 the first corner of the destination rectangle
1608 * @param dy1 the first corner of the destination rectangle
1609 * @param dx2 the second corner of the destination rectangle
1610 * @param dy2 the second corner of the destination rectangle
1611 * @param sx1 the first corner of the source rectangle
1612 * @param sy1 the first corner of the source rectangle
1613 * @param sx2 the second corner of the source rectangle
1614 * @param sy2 the second corner of the source rectangle
1615 * @param bgcolor the background color to use for transparent pixels
1616 * @param observer the image observer to be notified
1618 public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2,
1619 int sx1, int sy1, int sx2, int sy2, Color bgcolor,
1620 ImageObserver observer)
1622 // FIXME: Do something with bgcolor.
1623 return drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer);
1627 * Disposes this graphics object.
1629 public void dispose()
1631 // Nothing special to do here.
1635 * Fills the specified shape. Override this if your backend can efficiently
1636 * fill shapes. This is possible on many systems via a polygon fill
1637 * method or something similar. But keep in mind that Shapes can be quite
1638 * complex (non-convex, with holes etc), which is not necessarily supported
1639 * by all polygon fillers. Also note that you must perform clipping
1640 * before filling the shape.
1642 * @param s the shape to fill
1643 * @param isFont <code>true</code> if the shape is a font outline
1645 protected void fillShape(Shape s, boolean isFont)
1647 // Determine if we need to antialias stuff.
1648 boolean antialias = false;
1649 if (isFont)
1651 Object v = renderingHints.get(RenderingHints.KEY_TEXT_ANTIALIASING);
1652 // We default to antialiasing for text rendering.
1653 antialias = v == RenderingHints.VALUE_TEXT_ANTIALIAS_ON
1654 || (v == RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT
1655 && DEFAULT_TEXT_AA);
1657 else
1659 Object v = renderingHints.get(RenderingHints.KEY_ANTIALIASING);
1660 antialias = (v == RenderingHints.VALUE_ANTIALIAS_ON);
1662 ScanlineConverter sc = getScanlineConverter();
1663 int resolution = 0;
1664 int yRes = 0;
1665 if (antialias)
1667 // Adjust resolution according to rendering hints.
1668 resolution = 2;
1669 yRes = 4;
1671 sc.renderShape(this, s, clip, transform, resolution, yRes, renderingHints);
1672 freeScanlineConverter(sc);
1676 * Returns the color model of this Graphics object.
1678 * @return the color model of this Graphics object
1680 protected abstract ColorModel getColorModel();
1683 * Returns the bounds of the target.
1685 * @return the bounds of the target
1687 protected abstract Rectangle getDeviceBounds();
1690 * Draws a line in optimization mode. The implementation should respect the
1691 * clip and translation. It can assume that the clip is a rectangle and that
1692 * the transform is only a translating transform.
1694 * @param x0 the starting point, X coordinate
1695 * @param y0 the starting point, Y coordinate
1696 * @param x1 the end point, X coordinate
1697 * @param y1 the end point, Y coordinate
1699 protected void rawDrawLine(int x0, int y0, int x1, int y1)
1701 ShapeCache sc = shapeCache;
1702 if (sc.line == null)
1703 sc.line = new Line2D.Float();
1704 sc.line.setLine(x0, y0, x1, y1);
1705 draw(sc.line);
1708 protected void rawDrawRect(int x, int y, int w, int h)
1710 ShapeCache sc = shapeCache;
1711 if (sc.rect == null)
1712 sc.rect = new Rectangle();
1713 sc.rect.setBounds(x, y, w, h);
1714 draw(sc.rect);
1718 * Clears a rectangle in optimization mode. The implementation should respect the
1719 * clip and translation. It can assume that the clip is a rectangle and that
1720 * the transform is only a translating transform.
1722 * @param x the upper left corner, X coordinate
1723 * @param y the upper left corner, Y coordinate
1724 * @param w the width
1725 * @param h the height
1727 protected void rawClearRect(int x, int y, int w, int h)
1729 Paint savedForeground = getPaint();
1730 setPaint(getBackground());
1731 rawFillRect(x, y, w, h);
1732 setPaint(savedForeground);
1736 * Fills a rectangle in optimization mode. The implementation should respect
1737 * the clip but can assume that it is a rectangle.
1739 * @param x the upper left corner, X coordinate
1740 * @param y the upper left corner, Y coordinate
1741 * @param w the width
1742 * @param h the height
1744 protected void rawFillRect(int x, int y, int w, int h)
1746 ShapeCache sc = shapeCache;
1747 if (sc.rect == null)
1748 sc.rect = new Rectangle();
1749 sc.rect.setBounds(x, y, w, h);
1750 fill(sc.rect);
1754 * Draws an image in optimization mode. The implementation should respect
1755 * the clip but can assume that it is a rectangle.
1757 * @param image the image to be painted
1758 * @param x the location, X coordinate
1759 * @param y the location, Y coordinate
1760 * @param obs the image observer to be notified
1762 * @return <code>true</code> when the image is painted completely,
1763 * <code>false</code> if it is still rendered
1765 protected boolean rawDrawImage(Image image, int x, int y, ImageObserver obs)
1767 AffineTransform t = new AffineTransform();
1768 t.translate(x, y);
1769 return drawImage(image, t, obs);
1773 * Copies a rectangular region to another location.
1775 * @param x the upper left corner, X coordinate
1776 * @param y the upper left corner, Y coordinate
1777 * @param w the width
1778 * @param h the height
1779 * @param dx
1780 * @param dy
1782 protected void rawCopyArea(int x, int y, int w, int h, int dx, int dy)
1784 copyAreaImpl(x, y, w, h, dx, dy);
1787 // Private implementation methods.
1790 * Copies a rectangular area of the target raster to a different location.
1792 private void copyAreaImpl(int x, int y, int w, int h, int dx, int dy)
1794 // FIXME: Implement this properly.
1795 throw new UnsupportedOperationException("Not implemented yet.");
1799 * Paints a scanline between x0 and x1. Override this when your backend
1800 * can efficiently draw/fill horizontal lines.
1802 * @param x0 the left offset
1803 * @param x1 the right offset
1804 * @param y the scanline
1806 public void renderScanline(int y, ScanlineCoverage c)
1808 PaintContext pCtx = getPaintContext();
1810 int x0 = c.getMinX();
1811 int x1 = c.getMaxX();
1812 Raster paintRaster = pCtx.getRaster(x0, y, x1 - x0, 1);
1814 // Do the anti aliasing thing.
1815 float coverageAlpha = 0;
1816 float maxCoverage = c.getMaxCoverage();
1817 ColorModel cm = pCtx.getColorModel();
1818 DataBuffer db = paintRaster.getDataBuffer();
1819 Point loc = new Point(paintRaster.getMinX(), paintRaster.getMinY());
1820 SampleModel sm = paintRaster.getSampleModel();
1821 WritableRaster writeRaster = Raster.createWritableRaster(sm, db, loc);
1822 WritableRaster alphaRaster = cm.getAlphaRaster(writeRaster);
1823 int pixel;
1824 ScanlineCoverage.Iterator iter = c.iterate();
1825 while (iter.hasNext())
1827 ScanlineCoverage.Range range = iter.next();
1828 coverageAlpha = range.getCoverage() / maxCoverage;
1829 if (coverageAlpha < 1.0)
1831 for (int x = range.getXPos(); x < range.getXPosEnd(); x++)
1833 pixel = alphaRaster.getSample(x, y, 0);
1834 pixel = (int) (pixel * coverageAlpha);
1835 alphaRaster.setSample(x, y, 0, pixel);
1839 ColorModel paintColorModel = pCtx.getColorModel();
1840 CompositeContext cCtx = composite.createContext(paintColorModel,
1841 getColorModel(),
1842 renderingHints);
1843 WritableRaster raster = getDestinationRaster();
1844 WritableRaster targetChild = raster.createWritableTranslatedChild(-x0, -y);
1846 cCtx.compose(paintRaster, targetChild, targetChild);
1847 updateRaster(raster, x0, y, x1 - x0, 1);
1848 cCtx.dispose();
1853 * Initializes this graphics object. This must be called by subclasses in
1854 * order to correctly initialize the state of this object.
1856 protected void init()
1858 setPaint(Color.BLACK);
1859 setFont(FONT);
1860 isOptimized = true;
1864 * Returns a WritableRaster that is used by this class to perform the
1865 * rendering in. It is not necessary that the target surface immediately
1866 * reflects changes in the raster. Updates to the raster are notified via
1867 * {@link #updateRaster}.
1869 * @return the destination raster
1871 protected WritableRaster getDestinationRaster()
1873 // TODO: Ideally we would fetch the xdrawable's surface pixels for
1874 // initialization of the raster.
1875 Rectangle db = getDeviceBounds();
1876 if (destinationRaster == null)
1878 int[] bandMasks = new int[]{ 0xFF0000, 0xFF00, 0xFF };
1879 destinationRaster = Raster.createPackedRaster(DataBuffer.TYPE_INT,
1880 db.width, db.height,
1881 bandMasks, null);
1882 // Initialize raster with white.
1883 int x0 = destinationRaster.getMinX();
1884 int x1 = destinationRaster.getWidth() + x0;
1885 int y0 = destinationRaster.getMinY();
1886 int y1 = destinationRaster.getHeight() + y0;
1887 int numBands = destinationRaster.getNumBands();
1888 for (int y = y0; y < y1; y++)
1890 for (int x = x0; x < x1; x++)
1892 for (int b = 0; b < numBands; b++)
1893 destinationRaster.setSample(x, y, b, 255);
1897 return destinationRaster;
1901 * Notifies the backend that the raster has changed in the specified
1902 * rectangular area. The raster that is provided in this method is always
1903 * the same as the one returned in {@link #getDestinationRaster}.
1904 * Backends that reflect changes to this raster directly don't need to do
1905 * anything here.
1907 * @param raster the updated raster, identical to the raster returned
1908 * by {@link #getDestinationRaster()}
1909 * @param x the upper left corner of the updated region, X coordinate
1910 * @param y the upper lef corner of the updated region, Y coordinate
1911 * @param w the width of the updated region
1912 * @param h the height of the updated region
1914 protected void updateRaster(Raster raster, int x, int y, int w, int h)
1916 // Nothing to do here. Backends that need to update their surface
1917 // to reflect the change should override this method.
1920 // Some helper methods.
1923 * Helper method to check and update the optimization conditions.
1925 private void updateOptimization()
1927 int transformType = transform.getType();
1928 boolean optimizedTransform = false;
1929 if (transformType == AffineTransform.TYPE_IDENTITY
1930 || transformType == AffineTransform.TYPE_TRANSLATION)
1931 optimizedTransform = true;
1933 boolean optimizedClip = (clip == null || clip instanceof Rectangle);
1934 isOptimized = optimizedClip
1935 && optimizedTransform && paint instanceof Color
1936 && composite == AlphaComposite.SrcOver
1937 && stroke.equals(new BasicStroke());
1941 * Calculates the intersection of two rectangles. The result is stored
1942 * in <code>rect</code>. This is basically the same
1943 * like {@link Rectangle#intersection(Rectangle)}, only that it does not
1944 * create new Rectangle instances. The tradeoff is that you loose any data in
1945 * <code>rect</code>.
1947 * @param x upper-left x coodinate of first rectangle
1948 * @param y upper-left y coodinate of first rectangle
1949 * @param w width of first rectangle
1950 * @param h height of first rectangle
1951 * @param rect a Rectangle object of the second rectangle
1953 * @throws NullPointerException if rect is null
1955 * @return a rectangle corresponding to the intersection of the
1956 * two rectangles. An empty rectangle is returned if the rectangles
1957 * do not overlap
1959 private static Rectangle computeIntersection(int x, int y, int w, int h,
1960 Rectangle rect)
1962 int x2 = rect.x;
1963 int y2 = rect.y;
1964 int w2 = rect.width;
1965 int h2 = rect.height;
1967 int dx = (x > x2) ? x : x2;
1968 int dy = (y > y2) ? y : y2;
1969 int dw = (x + w < x2 + w2) ? (x + w - dx) : (x2 + w2 - dx);
1970 int dh = (y + h < y2 + h2) ? (y + h - dy) : (y2 + h2 - dy);
1972 if (dw >= 0 && dh >= 0)
1973 rect.setBounds(dx, dy, dw, dh);
1974 else
1975 rect.setBounds(0, 0, 0, 0);
1977 return rect;
1981 * Helper method to transform the clip. This is called by the various
1982 * transformation-manipulation methods to update the clip (which is in
1983 * userspace) accordingly.
1985 * The transform usually is the inverse transform that was applied to the
1986 * graphics object.
1988 * @param t the transform to apply to the clip
1990 private void updateClip(AffineTransform t)
1992 if (! (clip instanceof GeneralPath))
1993 clip = new GeneralPath(clip);
1995 GeneralPath p = (GeneralPath) clip;
1996 p.transform(t);
2000 * Returns a free scanline converter from the pool.
2002 * @return a scanline converter
2004 private ScanlineConverter getScanlineConverter()
2006 synchronized (scanlineConverters)
2008 ScanlineConverter sc;
2009 if (scanlineConverters.size() > 0)
2011 sc = scanlineConverters.removeFirst();
2013 else
2015 sc = new ScanlineConverter();
2017 return sc;
2022 * Puts a scanline converter back in the pool.
2024 * @param sc
2026 private void freeScanlineConverter(ScanlineConverter sc)
2028 synchronized (scanlineConverters)
2030 scanlineConverters.addLast(sc);
2034 private PaintContext getPaintContext()
2036 if (this.paintContext == null)
2038 this.paintContext =
2039 this.foreground.createContext(getColorModel(),
2040 getDeviceBounds(),
2041 getClipBounds(),
2042 getTransform(),
2043 getRenderingHints());
2046 return this.paintContext;
2050 * Scales an image to the specified width and height. This should also
2051 * be used to implement
2052 * {@link Toolkit#prepareImage(Image, int, int, ImageObserver)}.
2053 * This uses {@link Toolkit#createImage(ImageProducer)} to create the actual
2054 * image.
2056 * @param image the image to prepare
2057 * @param w the width
2058 * @param h the height
2060 * @return the scaled image
2062 public static Image prepareImage(Image image, int w, int h)
2064 // Try to find cached scaled image.
2065 HashMap<Dimension,Image> scaledTable = imageCache.get(image);
2066 Dimension size = new Dimension(w, h);
2067 Image scaled = null;
2068 if (scaledTable != null)
2070 scaled = scaledTable.get(size);
2072 if (scaled == null)
2074 // No cached scaled image. Start scaling image now.
2075 ImageProducer source = image.getSource();
2076 ReplicateScaleFilter scaler = new ReplicateScaleFilter(w, h);
2077 FilteredImageSource filteredSource =
2078 new FilteredImageSource(source, scaler);
2079 // Ideally, this should asynchronously scale the image.
2080 Image scaledImage =
2081 Toolkit.getDefaultToolkit().createImage(filteredSource);
2082 scaled = scaledImage;
2083 // Put scaled image in cache.
2084 if (scaledTable == null)
2086 scaledTable = new HashMap<Dimension,Image>();
2087 imageCache.put(image, scaledTable);
2089 scaledTable.put(size, scaledImage);
2091 return scaled;