Merged with mainline at revision 128810.
[official-gcc.git] / libjava / classpath / gnu / java / awt / java2d / AbstractGraphics2D.java
blob15ec90da1c024f5007e689f77f519130b4eaba48
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 java.awt.AWTError;
41 import java.awt.AlphaComposite;
42 import java.awt.AWTPermission;
43 import java.awt.BasicStroke;
44 import java.awt.Color;
45 import java.awt.Composite;
46 import java.awt.CompositeContext;
47 import java.awt.Font;
48 import java.awt.FontMetrics;
49 import java.awt.Graphics;
50 import java.awt.Graphics2D;
51 import java.awt.Image;
52 import java.awt.Paint;
53 import java.awt.PaintContext;
54 import java.awt.Point;
55 import java.awt.Polygon;
56 import java.awt.Rectangle;
57 import java.awt.RenderingHints;
58 import java.awt.Shape;
59 import java.awt.Stroke;
60 import java.awt.Toolkit;
61 import java.awt.RenderingHints.Key;
62 import java.awt.font.FontRenderContext;
63 import java.awt.font.GlyphVector;
64 import java.awt.geom.AffineTransform;
65 import java.awt.geom.Arc2D;
66 import java.awt.geom.Area;
67 import java.awt.geom.Ellipse2D;
68 import java.awt.geom.GeneralPath;
69 import java.awt.geom.Line2D;
70 import java.awt.geom.NoninvertibleTransformException;
71 import java.awt.geom.RoundRectangle2D;
72 import java.awt.image.BufferedImage;
73 import java.awt.image.BufferedImageOp;
74 import java.awt.image.ColorModel;
75 import java.awt.image.DataBuffer;
76 import java.awt.image.ImageObserver;
77 import java.awt.image.Raster;
78 import java.awt.image.RenderedImage;
79 import java.awt.image.SampleModel;
80 import java.awt.image.WritableRaster;
81 import java.awt.image.renderable.RenderableImage;
82 import java.text.AttributedCharacterIterator;
83 import java.util.HashMap;
84 import java.util.Map;
86 /**
87 * This is a 100% Java implementation of the Java2D rendering pipeline. It is
88 * meant as a base class for Graphics2D implementations.
90 * <h2>Backend interface</h2>
91 * <p>
92 * The backend must at the very least provide a Raster which the the rendering
93 * pipeline can paint into. This must be implemented in
94 * {@link #getDestinationRaster()}. For some backends that might be enough, like
95 * when the target surface can be directly access via the raster (like in
96 * BufferedImages). Other targets need some way to synchronize the raster with
97 * the surface, which can be achieved by implementing the
98 * {@link #updateRaster(Raster, int, int, int, int)} method, which always gets
99 * called after a chunk of data got painted into the raster.
100 * </p>
101 * <p>Alternativly the backend can provide a method for filling Shapes by
102 * overriding the protected method fillShape(). This can be accomplished
103 * by a polygon filling function of the backend. Keep in mind though that
104 * Shapes can be quite complex (i.e. non-convex and containing holes, etc)
105 * which is not supported by all polygon fillers. Also it must be noted
106 * that fillShape() is expected to handle painting and compositing as well as
107 * clipping and transformation. If your backend can't support this natively,
108 * then you can fallback to the implementation in this class. You'll need
109 * to provide a writable Raster then, see above.</p>
110 * <p>Another alternative is to implement fillScanline() which only requires
111 * the backend to be able to draw horizontal lines in device space,
112 * which is usually very cheap.
113 * The implementation should still handle painting and compositing,
114 * but no more clipping and transformation is required by the backend.</p>
115 * <p>The backend is free to provide implementations for the various raw*
116 * methods for optimized AWT 1.1 style painting of some primitives. This should
117 * accelerate painting of Swing greatly. When doing so, the backend must also
118 * keep track of the clip and translation, probably by overriding
119 * some clip and translate methods. Don't forget to message super in such a
120 * case.</p>
122 * <h2>Acceleration options</h2>
123 * <p>
124 * The fact that it is
125 * pure Java makes it a little slow. However, there are several ways of
126 * accelerating the rendering pipeline:
127 * <ol>
128 * <li><em>Optimization hooks for AWT 1.1 - like graphics operations.</em>
129 * The most important methods from the {@link java.awt.Graphics} class
130 * have a corresponding <code>raw*</code> method, which get called when
131 * several optimization conditions are fullfilled. These conditions are
132 * described below. Subclasses can override these methods and delegate
133 * it directly to a native backend.</li>
134 * <li><em>Native PaintContexts and CompositeContext.</em> The implementations
135 * for the 3 PaintContexts and AlphaCompositeContext can be accelerated
136 * using native code. These have proved to two of the most performance
137 * critical points in the rendering pipeline and cannot really be done quickly
138 * in plain Java because they involve lots of shuffling around with large
139 * arrays. In fact, you really would want to let the graphics card to the
140 * work, they are made for this.</li>
141 * <li>Provide an accelerated implementation for fillShape(). For instance,
142 * OpenGL can fill shapes very efficiently. There are some considerations
143 * to be made though, see above for details.</li>
144 * </ol>
145 * </p>
147 * @author Roman Kennke (kennke@aicas.com)
149 public abstract class AbstractGraphics2D
150 extends Graphics2D
151 implements Cloneable, Pixelizer
155 * The default font to use on the graphics object.
157 private static final Font FONT = new Font("SansSerif", Font.PLAIN, 12);
160 * Caches certain shapes to avoid massive creation of such Shapes in
161 * the various draw* and fill* methods.
163 private static final ThreadLocal<ShapeCache> shapeCache =
164 new ThreadLocal<ShapeCache>();
167 * The scanline converters by thread.
169 private static final ThreadLocal<ScanlineConverter> scanlineConverters =
170 new ThreadLocal<ScanlineConverter>();
173 * The transformation for this Graphics2D instance
175 protected AffineTransform transform;
178 * The foreground.
180 private Paint paint;
183 * The paint context during rendering.
185 private PaintContext paintContext;
188 * The background.
190 private Color background;
193 * The current font.
195 private Font font;
198 * The current composite setting.
200 private Composite composite;
203 * The current stroke setting.
205 private Stroke stroke;
208 * The current clip. This clip is in user coordinate space.
210 private Shape clip;
213 * The rendering hints.
215 private RenderingHints renderingHints;
218 * The raster of the destination surface. This is where the painting is
219 * performed.
221 private WritableRaster destinationRaster;
224 * Indicates if certain graphics primitives can be rendered in an optimized
225 * fashion. This will be the case if the following conditions are met:
226 * - The transform may only be a translation, no rotation, shearing or
227 * scaling.
228 * - The paint must be a solid color.
229 * - The composite must be an AlphaComposite.SrcOver.
230 * - The clip must be a Rectangle.
231 * - The stroke must be a plain BasicStroke().
233 * These conditions represent the standard settings of a new
234 * AbstractGraphics2D object and will be the most commonly used setting
235 * in Swing rendering and should therefore be optimized as much as possible.
237 private boolean isOptimized = true;
239 private static final BasicStroke STANDARD_STROKE = new BasicStroke();
241 private static final HashMap STANDARD_HINTS;
242 static {
243 HashMap hints = new HashMap();
244 hints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
245 RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
246 hints.put(RenderingHints.KEY_ANTIALIASING,
247 RenderingHints.VALUE_ANTIALIAS_DEFAULT);
248 STANDARD_HINTS = hints;
251 * Creates a new AbstractGraphics2D instance.
253 protected AbstractGraphics2D()
255 transform = new AffineTransform();
256 background = Color.WHITE;
257 composite = AlphaComposite.SrcOver;
258 stroke = STANDARD_STROKE;
259 renderingHints = new RenderingHints(STANDARD_HINTS);
263 * Draws the specified shape. The shape is passed through the current stroke
264 * and is then forwarded to {@link #fillShape}.
266 * @param shape the shape to draw
268 public void draw(Shape shape)
270 // Stroke the shape.
271 Shape strokedShape = stroke.createStrokedShape(shape);
272 // Fill the stroked shape.
273 fillShape(strokedShape, false);
278 * Draws the specified image and apply the transform for image space ->
279 * user space conversion.
281 * This method is implemented to special case RenderableImages and
282 * RenderedImages and delegate to
283 * {@link #drawRenderableImage(RenderableImage, AffineTransform)} and
284 * {@link #drawRenderedImage(RenderedImage, AffineTransform)} accordingly.
285 * Other image types are not yet handled.
287 * @param image the image to be rendered
288 * @param xform the transform from image space to user space
289 * @param obs the image observer to be notified
291 public boolean drawImage(Image image, AffineTransform xform,
292 ImageObserver obs)
294 Rectangle areaOfInterest = new Rectangle(0, 0, image.getWidth(obs),
295 image.getHeight(obs));
296 return drawImageImpl(image, xform, obs, areaOfInterest);
300 * Draws the specified image and apply the transform for image space ->
301 * user space conversion. This method only draw the part of the image
302 * specified by <code>areaOfInterest</code>.
304 * This method is implemented to special case RenderableImages and
305 * RenderedImages and delegate to
306 * {@link #drawRenderableImage(RenderableImage, AffineTransform)} and
307 * {@link #drawRenderedImage(RenderedImage, AffineTransform)} accordingly.
308 * Other image types are not yet handled.
310 * @param image the image to be rendered
311 * @param xform the transform from image space to user space
312 * @param obs the image observer to be notified
313 * @param areaOfInterest the area in image space that is rendered
315 private boolean drawImageImpl(Image image, AffineTransform xform,
316 ImageObserver obs, Rectangle areaOfInterest)
318 boolean ret;
319 if (image == null)
321 ret = true;
323 else if (image instanceof RenderedImage)
325 // FIXME: Handle the ImageObserver.
326 drawRenderedImageImpl((RenderedImage) image, xform, areaOfInterest);
327 ret = true;
329 else if (image instanceof RenderableImage)
331 // FIXME: Handle the ImageObserver.
332 drawRenderableImageImpl((RenderableImage) image, xform, areaOfInterest);
333 ret = true;
335 else
337 // FIXME: Implement rendering of other Image types.
338 ret = false;
340 return ret;
344 * Renders a BufferedImage and applies the specified BufferedImageOp before
345 * to filter the BufferedImage somehow. The resulting BufferedImage is then
346 * passed on to {@link #drawRenderedImage(RenderedImage, AffineTransform)}
347 * to perform the final rendering.
349 * @param image the source buffered image
350 * @param op the filter to apply to the buffered image before rendering
351 * @param x the x coordinate to render the image to
352 * @param y the y coordinate to render the image to
354 public void drawImage(BufferedImage image, BufferedImageOp op, int x, int y)
356 BufferedImage filtered =
357 op.createCompatibleDestImage(image, image.getColorModel());
358 AffineTransform t = new AffineTransform();
359 t.translate(x, y);
360 drawRenderedImage(filtered, t);
364 * Renders the specified image to the destination raster. The specified
365 * transform is used to convert the image into user space. The transform
366 * of this AbstractGraphics2D object is used to transform from user space
367 * to device space.
369 * The rendering is performed using the scanline algorithm that performs the
370 * rendering of other shapes and a custom Paint implementation, that supplies
371 * the pixel values of the rendered image.
373 * @param image the image to render to the destination raster
374 * @param xform the transform from image space to user space
376 public void drawRenderedImage(RenderedImage image, AffineTransform xform)
378 Rectangle areaOfInterest = new Rectangle(image.getMinX(),
379 image.getHeight(),
380 image.getWidth(),
381 image.getHeight());
382 drawRenderedImageImpl(image, xform, areaOfInterest);
386 * Renders the specified image to the destination raster. The specified
387 * transform is used to convert the image into user space. The transform
388 * of this AbstractGraphics2D object is used to transform from user space
389 * to device space. Only the area specified by <code>areaOfInterest</code>
390 * is finally rendered to the target.
392 * The rendering is performed using the scanline algorithm that performs the
393 * rendering of other shapes and a custom Paint implementation, that supplies
394 * the pixel values of the rendered image.
396 * @param image the image to render to the destination raster
397 * @param xform the transform from image space to user space
399 private void drawRenderedImageImpl(RenderedImage image,
400 AffineTransform xform,
401 Rectangle areaOfInterest)
403 // First we compute the transformation. This is made up of 3 parts:
404 // 1. The areaOfInterest -> image space transform.
405 // 2. The image space -> user space transform.
406 // 3. The user space -> device space transform.
407 AffineTransform t = new AffineTransform();
408 t.translate(- areaOfInterest.x - image.getMinX(),
409 - areaOfInterest.y - image.getMinY());
410 t.concatenate(xform);
411 t.concatenate(transform);
412 AffineTransform it = null;
415 it = t.createInverse();
417 catch (NoninvertibleTransformException ex)
419 // Ignore -- we return if the transform is not invertible.
421 if (it != null)
423 // Transform the area of interest into user space.
424 GeneralPath aoi = new GeneralPath(areaOfInterest);
425 aoi.transform(xform);
426 // Render the shape using the standard renderer, but with a temporary
427 // ImagePaint.
428 ImagePaint p = new ImagePaint(image, it);
429 Paint savedPaint = paint;
432 paint = p;
433 fillShape(aoi, false);
435 finally
437 paint = savedPaint;
443 * Renders a renderable image. This produces a RenderedImage, which is
444 * then passed to {@link #drawRenderedImage(RenderedImage, AffineTransform)}
445 * to perform the final rendering.
447 * @param image the renderable image to be rendered
448 * @param xform the transform from image space to user space
450 public void drawRenderableImage(RenderableImage image, AffineTransform xform)
452 Rectangle areaOfInterest = new Rectangle((int) image.getMinX(),
453 (int) image.getHeight(),
454 (int) image.getWidth(),
455 (int) image.getHeight());
456 drawRenderableImageImpl(image, xform, areaOfInterest);
461 * Renders a renderable image. This produces a RenderedImage, which is
462 * then passed to {@link #drawRenderedImage(RenderedImage, AffineTransform)}
463 * to perform the final rendering. Only the area of the image specified
464 * by <code>areaOfInterest</code> is rendered.
466 * @param image the renderable image to be rendered
467 * @param xform the transform from image space to user space
469 private void drawRenderableImageImpl(RenderableImage image,
470 AffineTransform xform,
471 Rectangle areaOfInterest)
473 // TODO: Maybe make more clever usage of a RenderContext here.
474 RenderedImage rendered = image.createDefaultRendering();
475 drawRenderedImageImpl(rendered, xform, areaOfInterest);
479 * Draws the specified string at the specified location.
481 * @param text the string to draw
482 * @param x the x location, relative to the bounding rectangle of the text
483 * @param y the y location, relative to the bounding rectangle of the text
485 public void drawString(String text, int x, int y)
487 if (isOptimized)
488 rawDrawString(text, x, y);
489 else
491 FontRenderContext ctx = getFontRenderContext();
492 GlyphVector gv = font.createGlyphVector(ctx, text.toCharArray());
493 drawGlyphVector(gv, x, y);
498 * Draws the specified string at the specified location.
500 * @param text the string to draw
501 * @param x the x location, relative to the bounding rectangle of the text
502 * @param y the y location, relative to the bounding rectangle of the text
504 public void drawString(String text, float x, float y)
506 FontRenderContext ctx = getFontRenderContext();
507 GlyphVector gv = font.createGlyphVector(ctx, text.toCharArray());
508 drawGlyphVector(gv, x, y);
512 * Draws the specified string (as AttributedCharacterIterator) at the
513 * specified location.
515 * @param iterator the string to draw
516 * @param x the x location, relative to the bounding rectangle of the text
517 * @param y the y location, relative to the bounding rectangle of the text
519 public void drawString(AttributedCharacterIterator iterator, int x, int y)
521 FontRenderContext ctx = getFontRenderContext();
522 GlyphVector gv = font.createGlyphVector(ctx, iterator);
523 drawGlyphVector(gv, x, y);
527 * Draws the specified string (as AttributedCharacterIterator) at the
528 * specified location.
530 * @param iterator the string to draw
531 * @param x the x location, relative to the bounding rectangle of the text
532 * @param y the y location, relative to the bounding rectangle of the text
534 public void drawString(AttributedCharacterIterator iterator, float x, float y)
536 FontRenderContext ctx = getFontRenderContext();
537 GlyphVector gv = font.createGlyphVector(ctx, iterator);
538 drawGlyphVector(gv, x, y);
542 * Fills the specified shape with the current foreground.
544 * @param shape the shape to fill
546 public void fill(Shape shape)
548 fillShape(shape, false);
551 public boolean hit(Rectangle rect, Shape text, boolean onStroke)
553 // FIXME: Implement this.
554 throw new UnsupportedOperationException("Not yet implemented");
558 * Sets the composite.
560 * @param comp the composite to set
562 public void setComposite(Composite comp)
564 if (! (comp instanceof AlphaComposite))
566 // FIXME: this check is only required "if this Graphics2D
567 // context is drawing to a Component on the display screen".
568 SecurityManager sm = System.getSecurityManager();
569 if (sm != null)
570 sm.checkPermission(new AWTPermission("readDisplayPixels"));
573 composite = comp;
574 if (! (comp.equals(AlphaComposite.SrcOver)))
575 isOptimized = false;
576 else
577 updateOptimization();
581 * Sets the current foreground.
583 * @param p the foreground to set.
585 public void setPaint(Paint p)
587 if (p != null)
589 paint = p;
591 if (! (paint instanceof Color))
592 isOptimized = false;
593 else
595 updateOptimization();
601 * Sets the stroke for this graphics object.
603 * @param s the stroke to set
605 public void setStroke(Stroke s)
607 stroke = s;
608 if (! stroke.equals(new BasicStroke()))
609 isOptimized = false;
610 else
611 updateOptimization();
615 * Sets the specified rendering hint.
617 * @param hintKey the key of the rendering hint
618 * @param hintValue the value
620 public void setRenderingHint(Key hintKey, Object hintValue)
622 renderingHints.put(hintKey, hintValue);
626 * Returns the rendering hint for the specified key.
628 * @param hintKey the rendering hint key
630 * @return the rendering hint for the specified key
632 public Object getRenderingHint(Key hintKey)
634 return renderingHints.get(hintKey);
638 * Sets the specified rendering hints.
640 * @param hints the rendering hints to set
642 public void setRenderingHints(Map hints)
644 renderingHints.clear();
645 renderingHints.putAll(hints);
649 * Adds the specified rendering hints.
651 * @param hints the rendering hints to add
653 public void addRenderingHints(Map hints)
655 renderingHints.putAll(hints);
659 * Returns the current rendering hints.
661 * @return the current rendering hints
663 public RenderingHints getRenderingHints()
665 return (RenderingHints) renderingHints.clone();
669 * Translates the coordinate system by (x, y).
671 * @param x the translation X coordinate
672 * @param y the translation Y coordinate
674 public void translate(int x, int y)
676 transform.translate(x, y);
678 // Update the clip. We special-case rectangular clips here, because they
679 // are so common (e.g. in Swing).
680 if (clip != null)
682 if (clip instanceof Rectangle)
684 Rectangle r = (Rectangle) clip;
685 r.x -= x;
686 r.y -= y;
687 setClip(r);
689 else
691 AffineTransform clipTransform = new AffineTransform();
692 clipTransform.translate(-x, -y);
693 updateClip(clipTransform);
699 * Translates the coordinate system by (tx, ty).
701 * @param tx the translation X coordinate
702 * @param ty the translation Y coordinate
704 public void translate(double tx, double ty)
706 transform.translate(tx, ty);
708 // Update the clip. We special-case rectangular clips here, because they
709 // are so common (e.g. in Swing).
710 if (clip != null)
712 if (clip instanceof Rectangle)
714 Rectangle r = (Rectangle) clip;
715 r.x -= tx;
716 r.y -= ty;
718 else
720 AffineTransform clipTransform = new AffineTransform();
721 clipTransform.translate(-tx, -ty);
722 updateClip(clipTransform);
728 * Rotates the coordinate system by <code>theta</code> degrees.
730 * @param theta the angle be which to rotate the coordinate system
732 public void rotate(double theta)
734 transform.rotate(theta);
735 if (clip != null)
737 AffineTransform clipTransform = new AffineTransform();
738 clipTransform.rotate(-theta);
739 updateClip(clipTransform);
741 updateOptimization();
745 * Rotates the coordinate system by <code>theta</code> around the point
746 * (x, y).
748 * @param theta the angle by which to rotate the coordinate system
749 * @param x the point around which to rotate, X coordinate
750 * @param y the point around which to rotate, Y coordinate
752 public void rotate(double theta, double x, double y)
754 transform.rotate(theta, x, y);
755 if (clip != null)
757 AffineTransform clipTransform = new AffineTransform();
758 clipTransform.rotate(-theta, x, y);
759 updateClip(clipTransform);
761 updateOptimization();
765 * Scales the coordinate system by the factors <code>scaleX</code> and
766 * <code>scaleY</code>.
768 * @param scaleX the factor by which to scale the X axis
769 * @param scaleY the factor by which to scale the Y axis
771 public void scale(double scaleX, double scaleY)
773 transform.scale(scaleX, scaleY);
774 if (clip != null)
776 AffineTransform clipTransform = new AffineTransform();
777 clipTransform.scale(1 / scaleX, 1 / scaleY);
778 updateClip(clipTransform);
780 updateOptimization();
784 * Shears the coordinate system by <code>shearX</code> and
785 * <code>shearY</code>.
787 * @param shearX the X shearing
788 * @param shearY the Y shearing
790 public void shear(double shearX, double shearY)
792 transform.shear(shearX, shearY);
793 if (clip != null)
795 AffineTransform clipTransform = new AffineTransform();
796 clipTransform.shear(-shearX, -shearY);
797 updateClip(clipTransform);
799 updateOptimization();
803 * Transforms the coordinate system using the specified transform
804 * <code>t</code>.
806 * @param t the transform
808 public void transform(AffineTransform t)
810 transform.concatenate(t);
813 AffineTransform clipTransform = t.createInverse();
814 updateClip(clipTransform);
816 catch (NoninvertibleTransformException ex)
818 // TODO: How can we deal properly with this?
819 ex.printStackTrace();
821 updateOptimization();
825 * Sets the transformation for this Graphics object.
827 * @param t the transformation to set
829 public void setTransform(AffineTransform t)
831 // Transform clip into target space using the old transform.
832 updateClip(transform);
833 transform.setTransform(t);
834 // Transform the clip back into user space using the inverse new transform.
837 updateClip(transform.createInverse());
839 catch (NoninvertibleTransformException ex)
841 // TODO: How can we deal properly with this?
842 ex.printStackTrace();
844 updateOptimization();
848 * Returns the transformation of this coordinate system.
850 * @return the transformation of this coordinate system
852 public AffineTransform getTransform()
854 return (AffineTransform) transform.clone();
858 * Returns the current foreground.
860 * @return the current foreground
862 public Paint getPaint()
864 return paint;
869 * Returns the current composite.
871 * @return the current composite
873 public Composite getComposite()
875 return composite;
879 * Sets the current background.
881 * @param color the background to set.
883 public void setBackground(Color color)
885 background = color;
889 * Returns the current background.
891 * @return the current background
893 public Color getBackground()
895 return background;
899 * Returns the current stroke.
901 * @return the current stroke
903 public Stroke getStroke()
905 return stroke;
909 * Intersects the clip of this graphics object with the specified clip.
911 * @param s the clip with which the current clip should be intersected
913 public void clip(Shape s)
915 // Initialize clip if not already present.
916 if (clip == null)
917 setClip(s);
919 // This is so common, let's optimize this.
920 else if (clip instanceof Rectangle && s instanceof Rectangle)
922 Rectangle clipRect = (Rectangle) clip;
923 Rectangle r = (Rectangle) s;
924 computeIntersection(r.x, r.y, r.width, r.height, clipRect);
925 // Call setClip so that subclasses get notified.
926 setClip(clipRect);
928 else
930 Area current;
931 if (clip instanceof Area)
932 current = (Area) clip;
933 else
934 current = new Area(clip);
936 Area intersect;
937 if (s instanceof Area)
938 intersect = (Area) s;
939 else
940 intersect = new Area(s);
942 current.intersect(intersect);
943 clip = current;
944 isOptimized = false;
945 // Call setClip so that subclasses get notified.
946 setClip(clip);
950 public FontRenderContext getFontRenderContext()
952 return new FontRenderContext(transform, false, true);
956 * Draws the specified glyph vector at the specified location.
958 * @param gv the glyph vector to draw
959 * @param x the location, x coordinate
960 * @param y the location, y coordinate
962 public void drawGlyphVector(GlyphVector gv, float x, float y)
964 translate(x, y);
965 fillShape(gv.getOutline(), true);
966 translate(-x, -y);
970 * Creates a copy of this graphics object.
972 * @return a copy of this graphics object
974 public Graphics create()
976 AbstractGraphics2D copy = (AbstractGraphics2D) clone();
977 return copy;
981 * Creates and returns a copy of this Graphics object. This should
982 * be overridden by subclasses if additional state must be handled when
983 * cloning. This is called by {@link #create()}.
985 * @return a copy of this Graphics object
987 protected Object clone()
991 AbstractGraphics2D copy = (AbstractGraphics2D) super.clone();
992 // Copy the clip. If it's a Rectangle, preserve that for optimization.
993 if (clip instanceof Rectangle)
994 copy.clip = new Rectangle((Rectangle) clip);
995 else
996 copy.clip = new GeneralPath(clip);
998 copy.renderingHints = new RenderingHints(null);
999 copy.renderingHints.putAll(renderingHints);
1000 copy.transform = new AffineTransform(transform);
1001 // The remaining state is inmmutable and doesn't need to be copied.
1002 return copy;
1004 catch (CloneNotSupportedException ex)
1006 AWTError err = new AWTError("Unexpected exception while cloning");
1007 err.initCause(ex);
1008 throw err;
1013 * Returns the current foreground.
1015 public Color getColor()
1017 Color c = null;
1018 if (paint instanceof Color)
1019 c = (Color) paint;
1020 return c;
1024 * Sets the current foreground.
1026 * @param color the foreground to set
1028 public void setColor(Color color)
1030 setPaint(color);
1033 public void setPaintMode()
1035 // FIXME: Implement this.
1036 throw new UnsupportedOperationException("Not yet implemented");
1039 public void setXORMode(Color color)
1041 // FIXME: Implement this.
1042 throw new UnsupportedOperationException("Not yet implemented");
1046 * Returns the current font.
1048 * @return the current font
1050 public Font getFont()
1052 return font;
1056 * Sets the font on this graphics object. When <code>f == null</code>, the
1057 * current setting is not changed.
1059 * @param f the font to set
1061 public void setFont(Font f)
1063 if (f != null)
1064 font = f;
1068 * Returns the font metrics for the specified font.
1070 * @param font the font for which to fetch the font metrics
1072 * @return the font metrics for the specified font
1074 public FontMetrics getFontMetrics(Font font)
1076 return Toolkit.getDefaultToolkit().getFontMetrics(font);
1080 * Returns the bounds of the current clip.
1082 * @return the bounds of the current clip
1084 public Rectangle getClipBounds()
1086 Rectangle b = null;
1087 if (clip != null)
1088 b = clip.getBounds();
1089 return b;
1093 * Intersects the current clipping region with the specified rectangle.
1095 * @param x the x coordinate of the rectangle
1096 * @param y the y coordinate of the rectangle
1097 * @param width the width of the rectangle
1098 * @param height the height of the rectangle
1100 public void clipRect(int x, int y, int width, int height)
1102 clip(new Rectangle(x, y, width, height));
1106 * Sets the clip to the specified rectangle.
1108 * @param x the x coordinate of the clip rectangle
1109 * @param y the y coordinate of the clip rectangle
1110 * @param width the width of the clip rectangle
1111 * @param height the height of the clip rectangle
1113 public void setClip(int x, int y, int width, int height)
1115 setClip(new Rectangle(x, y, width, height));
1119 * Returns the current clip.
1121 * @return the current clip
1123 public Shape getClip()
1125 return clip;
1129 * Sets the current clipping area to <code>clip</code>.
1131 * @param c the clip to set
1133 public void setClip(Shape c)
1135 clip = c;
1136 if (! (clip instanceof Rectangle))
1137 isOptimized = false;
1138 else
1139 updateOptimization();
1142 public void copyArea(int x, int y, int width, int height, int dx, int dy)
1144 if (isOptimized)
1145 rawCopyArea(x, y, width, height, dx, dy);
1146 else
1147 copyAreaImpl(x, y, width, height, dx, dy);
1151 * Draws a line from (x1, y1) to (x2, y2).
1153 * This implementation transforms the coordinates and forwards the call to
1154 * {@link #rawDrawLine}.
1156 public void drawLine(int x1, int y1, int x2, int y2)
1158 if (isOptimized)
1160 int tx = (int) transform.getTranslateX();
1161 int ty = (int) transform.getTranslateY();
1162 rawDrawLine(x1 + tx, y1 + ty, x2 + tx, y2 + ty);
1164 else
1166 ShapeCache sc = getShapeCache();
1167 if (sc.line == null)
1168 sc.line = new Line2D.Float();
1169 sc.line.setLine(x1, y1, x2, y2);
1170 draw(sc.line);
1174 public void drawRect(int x, int y, int w, int h)
1176 if (isOptimized)
1178 rawDrawRect(x, y, w, h);
1180 else
1182 ShapeCache sc = getShapeCache();
1183 if (sc.rect == null)
1184 sc.rect = new Rectangle();
1185 sc.rect.setBounds(x, y, w, h);
1186 draw(sc.rect);
1191 * Fills a rectangle with the current paint.
1193 * @param x the upper left corner, X coordinate
1194 * @param y the upper left corner, Y coordinate
1195 * @param width the width of the rectangle
1196 * @param height the height of the rectangle
1198 public void fillRect(int x, int y, int width, int height)
1200 if (isOptimized)
1202 rawFillRect(x + (int) transform.getTranslateX(),
1203 y + (int) transform.getTranslateY(), width, height);
1205 else
1207 ShapeCache sc = getShapeCache();
1208 if (sc.rect == null)
1209 sc.rect = new Rectangle();
1210 sc.rect.setBounds(x, y, width, height);
1211 fill(sc.rect);
1216 * Fills a rectangle with the current background color.
1218 * This implementation temporarily sets the foreground color to the
1219 * background and forwards the call to {@link #fillRect(int, int, int, int)}.
1221 * @param x the upper left corner, X coordinate
1222 * @param y the upper left corner, Y coordinate
1223 * @param width the width of the rectangle
1224 * @param height the height of the rectangle
1226 public void clearRect(int x, int y, int width, int height)
1228 if (isOptimized)
1229 rawClearRect(x, y, width, height);
1230 else
1232 Paint savedForeground = getPaint();
1233 setPaint(getBackground());
1234 fillRect(x, y, width, height);
1235 setPaint(savedForeground);
1240 * Draws a rounded rectangle.
1242 * @param x the x coordinate of the rectangle
1243 * @param y the y coordinate of the rectangle
1244 * @param width the width of the rectangle
1245 * @param height the height of the rectangle
1246 * @param arcWidth the width of the arcs
1247 * @param arcHeight the height of the arcs
1249 public void drawRoundRect(int x, int y, int width, int height, int arcWidth,
1250 int arcHeight)
1252 ShapeCache sc = getShapeCache();
1253 if (sc.roundRect == null)
1254 sc.roundRect = new RoundRectangle2D.Float();
1255 sc.roundRect.setRoundRect(x, y, width, height, arcWidth, arcHeight);
1256 draw(sc.roundRect);
1260 * Fills a rounded rectangle.
1262 * @param x the x coordinate of the rectangle
1263 * @param y the y coordinate of the rectangle
1264 * @param width the width of the rectangle
1265 * @param height the height of the rectangle
1266 * @param arcWidth the width of the arcs
1267 * @param arcHeight the height of the arcs
1269 public void fillRoundRect(int x, int y, int width, int height, int arcWidth,
1270 int arcHeight)
1272 ShapeCache sc = getShapeCache();
1273 if (sc.roundRect == null)
1274 sc.roundRect = new RoundRectangle2D.Float();
1275 sc.roundRect.setRoundRect(x, y, width, height, arcWidth, arcHeight);
1276 fill(sc.roundRect);
1280 * Draws the outline of an oval.
1282 * @param x the upper left corner of the bounding rectangle of the ellipse
1283 * @param y the upper left corner of the bounding rectangle of the ellipse
1284 * @param width the width of the ellipse
1285 * @param height the height of the ellipse
1287 public void drawOval(int x, int y, int width, int height)
1289 ShapeCache sc = getShapeCache();
1290 if (sc.ellipse == null)
1291 sc.ellipse = new Ellipse2D.Float();
1292 sc.ellipse.setFrame(x, y, width, height);
1293 draw(sc.ellipse);
1297 * Fills an oval.
1299 * @param x the upper left corner of the bounding rectangle of the ellipse
1300 * @param y the upper left corner of the bounding rectangle of the ellipse
1301 * @param width the width of the ellipse
1302 * @param height the height of the ellipse
1304 public void fillOval(int x, int y, int width, int height)
1306 ShapeCache sc = getShapeCache();
1307 if (sc.ellipse == null)
1308 sc.ellipse = new Ellipse2D.Float();
1309 sc.ellipse.setFrame(x, y, width, height);
1310 fill(sc.ellipse);
1314 * Draws an arc.
1316 public void drawArc(int x, int y, int width, int height, int arcStart,
1317 int arcAngle)
1319 ShapeCache sc = getShapeCache();
1320 if (sc.arc == null)
1321 sc.arc = new Arc2D.Float();
1322 sc.arc.setArc(x, y, width, height, arcStart, arcAngle, Arc2D.OPEN);
1323 draw(sc.arc);
1327 * Fills an arc.
1329 public void fillArc(int x, int y, int width, int height, int arcStart,
1330 int arcAngle)
1332 ShapeCache sc = getShapeCache();
1333 if (sc.arc == null)
1334 sc.arc = new Arc2D.Float();
1335 sc.arc.setArc(x, y, width, height, arcStart, arcAngle, Arc2D.PIE);
1336 draw(sc.arc);
1339 public void drawPolyline(int[] xPoints, int[] yPoints, int npoints)
1341 ShapeCache sc = getShapeCache();
1342 if (sc.polyline == null)
1343 sc.polyline = new GeneralPath();
1344 GeneralPath p = sc.polyline;
1345 p.reset();
1346 if (npoints > 0)
1347 p.moveTo(xPoints[0], yPoints[0]);
1348 for (int i = 1; i < npoints; i++)
1349 p.lineTo(xPoints[i], yPoints[i]);
1350 fill(p);
1354 * Draws the outline of a polygon.
1356 public void drawPolygon(int[] xPoints, int[] yPoints, int npoints)
1358 ShapeCache sc = getShapeCache();
1359 if (sc.polygon == null)
1360 sc.polygon = new Polygon();
1361 sc.polygon.reset();
1362 sc.polygon.xpoints = xPoints;
1363 sc.polygon.ypoints = yPoints;
1364 sc.polygon.npoints = npoints;
1365 draw(sc.polygon);
1369 * Fills the outline of a polygon.
1371 public void fillPolygon(int[] xPoints, int[] yPoints, int npoints)
1373 ShapeCache sc = getShapeCache();
1374 if (sc.polygon == null)
1375 sc.polygon = new Polygon();
1376 sc.polygon.reset();
1377 sc.polygon.xpoints = xPoints;
1378 sc.polygon.ypoints = yPoints;
1379 sc.polygon.npoints = npoints;
1380 fill(sc.polygon);
1384 * Draws the specified image at the specified location. This forwards
1385 * to {@link #drawImage(Image, AffineTransform, ImageObserver)}.
1387 * @param image the image to render
1388 * @param x the x location to render to
1389 * @param y the y location to render to
1390 * @param observer the image observer to receive notification
1392 public boolean drawImage(Image image, int x, int y, ImageObserver observer)
1394 boolean ret;
1395 if (isOptimized)
1397 ret = rawDrawImage(image, x + (int) transform.getTranslateX(),
1398 y + (int) transform.getTranslateY(), observer);
1400 else
1402 AffineTransform t = new AffineTransform();
1403 t.translate(x, y);
1404 ret = drawImage(image, t, observer);
1406 return ret;
1410 * Draws the specified image at the specified location. The image
1411 * is scaled to the specified width and height. This forwards
1412 * to {@link #drawImage(Image, AffineTransform, ImageObserver)}.
1414 * @param image the image to render
1415 * @param x the x location to render to
1416 * @param y the y location to render to
1417 * @param width the target width of the image
1418 * @param height the target height of the image
1419 * @param observer the image observer to receive notification
1421 public boolean drawImage(Image image, int x, int y, int width, int height,
1422 ImageObserver observer)
1424 AffineTransform t = new AffineTransform();
1425 t.translate(x, y);
1426 double scaleX = (double) width / (double) image.getWidth(observer);
1427 double scaleY = (double) height / (double) image.getHeight(observer);
1428 t.scale(scaleX, scaleY);
1429 return drawImage(image, t, observer);
1433 * Draws the specified image at the specified location. This forwards
1434 * to {@link #drawImage(Image, AffineTransform, ImageObserver)}.
1436 * @param image the image to render
1437 * @param x the x location to render to
1438 * @param y the y location to render to
1439 * @param bgcolor the background color to use for transparent pixels
1440 * @param observer the image observer to receive notification
1442 public boolean drawImage(Image image, int x, int y, Color bgcolor,
1443 ImageObserver observer)
1445 AffineTransform t = new AffineTransform();
1446 t.translate(x, y);
1447 // TODO: Somehow implement the background option.
1448 return drawImage(image, t, observer);
1452 * Draws the specified image at the specified location. The image
1453 * is scaled to the specified width and height. This forwards
1454 * to {@link #drawImage(Image, AffineTransform, ImageObserver)}.
1456 * @param image the image to render
1457 * @param x the x location to render to
1458 * @param y the y location to render to
1459 * @param width the target width of the image
1460 * @param height the target height of the image
1461 * @param bgcolor the background color to use for transparent pixels
1462 * @param observer the image observer to receive notification
1464 public boolean drawImage(Image image, int x, int y, int width, int height,
1465 Color bgcolor, ImageObserver observer)
1467 AffineTransform t = new AffineTransform();
1468 t.translate(x, y);
1469 double scaleX = (double) image.getWidth(observer) / (double) width;
1470 double scaleY = (double) image.getHeight(observer) / (double) height;
1471 t.scale(scaleX, scaleY);
1472 // TODO: Somehow implement the background option.
1473 return drawImage(image, t, observer);
1477 * Draws an image fragment to a rectangular area of the target.
1479 * @param image the image to render
1480 * @param dx1 the first corner of the destination rectangle
1481 * @param dy1 the first corner of the destination rectangle
1482 * @param dx2 the second corner of the destination rectangle
1483 * @param dy2 the second corner of the destination rectangle
1484 * @param sx1 the first corner of the source rectangle
1485 * @param sy1 the first corner of the source rectangle
1486 * @param sx2 the second corner of the source rectangle
1487 * @param sy2 the second corner of the source rectangle
1488 * @param observer the image observer to be notified
1490 public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2,
1491 int sx1, int sy1, int sx2, int sy2,
1492 ImageObserver observer)
1494 int sx = Math.min(sx1, sx1);
1495 int sy = Math.min(sy1, sy2);
1496 int sw = Math.abs(sx1 - sx2);
1497 int sh = Math.abs(sy1 - sy2);
1498 int dx = Math.min(dx1, dx1);
1499 int dy = Math.min(dy1, dy2);
1500 int dw = Math.abs(dx1 - dx2);
1501 int dh = Math.abs(dy1 - dy2);
1503 AffineTransform t = new AffineTransform();
1504 t.translate(sx - dx, sy - dy);
1505 double scaleX = (double) sw / (double) dw;
1506 double scaleY = (double) sh / (double) dh;
1507 t.scale(scaleX, scaleY);
1508 Rectangle areaOfInterest = new Rectangle(sx, sy, sw, sh);
1509 return drawImageImpl(image, t, observer, areaOfInterest);
1513 * Draws an image fragment to a rectangular area of the target.
1515 * @param image the image to render
1516 * @param dx1 the first corner of the destination rectangle
1517 * @param dy1 the first corner of the destination rectangle
1518 * @param dx2 the second corner of the destination rectangle
1519 * @param dy2 the second corner of the destination rectangle
1520 * @param sx1 the first corner of the source rectangle
1521 * @param sy1 the first corner of the source rectangle
1522 * @param sx2 the second corner of the source rectangle
1523 * @param sy2 the second corner of the source rectangle
1524 * @param bgcolor the background color to use for transparent pixels
1525 * @param observer the image observer to be notified
1527 public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2,
1528 int sx1, int sy1, int sx2, int sy2, Color bgcolor,
1529 ImageObserver observer)
1531 // FIXME: Do something with bgcolor.
1532 return drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer);
1536 * Disposes this graphics object.
1538 public void dispose()
1540 // Nothing special to do here.
1544 * Fills the specified shape. Override this if your backend can efficiently
1545 * fill shapes. This is possible on many systems via a polygon fill
1546 * method or something similar. But keep in mind that Shapes can be quite
1547 * complex (non-convex, with holes etc), which is not necessarily supported
1548 * by all polygon fillers. Also note that you must perform clipping
1549 * before filling the shape.
1551 * @param s the shape to fill
1552 * @param isFont <code>true</code> if the shape is a font outline
1554 protected void fillShape(Shape s, boolean isFont)
1556 // Determine if we need to antialias stuff.
1557 boolean antialias = false;
1558 if (isFont)
1560 Object v = renderingHints.get(RenderingHints.KEY_TEXT_ANTIALIASING);
1561 // We default to antialiasing for text rendering.
1562 antialias = (v == RenderingHints.VALUE_TEXT_ANTIALIAS_ON
1563 || v == RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
1565 else
1567 Object v = renderingHints.get(RenderingHints.KEY_ANTIALIASING);
1568 antialias = (v == RenderingHints.VALUE_ANTIALIAS_ON);
1570 ScanlineConverter sc = getScanlineConverter();
1571 int resolution = 0;
1572 if (antialias)
1574 // Adjust resolution according to rendering hints.
1575 resolution = 2;
1577 sc.renderShape(this, s, clip, transform, resolution, renderingHints);
1581 * Returns the color model of this Graphics object.
1583 * @return the color model of this Graphics object
1585 protected abstract ColorModel getColorModel();
1588 * Returns the bounds of the target.
1590 * @return the bounds of the target
1592 protected Rectangle getDeviceBounds()
1594 return destinationRaster.getBounds();
1598 * Draws a line in optimization mode. The implementation should respect the
1599 * clip and translation. It can assume that the clip is a rectangle and that
1600 * the transform is only a translating transform.
1602 * @param x0 the starting point, X coordinate
1603 * @param y0 the starting point, Y coordinate
1604 * @param x1 the end point, X coordinate
1605 * @param y1 the end point, Y coordinate
1607 protected void rawDrawLine(int x0, int y0, int x1, int y1)
1609 ShapeCache sc = getShapeCache();
1610 if (sc.line == null)
1611 sc.line = new Line2D.Float();
1612 sc.line.setLine(x0, y0, x1, y1);
1613 draw(sc.line);
1616 protected void rawDrawRect(int x, int y, int w, int h)
1618 ShapeCache sc = getShapeCache();
1619 if (sc.rect == null)
1620 sc.rect = new Rectangle();
1621 sc.rect.setBounds(x, y, w, h);
1622 draw(sc.rect);
1626 * Draws a string in optimization mode. The implementation should respect the
1627 * clip and translation. It can assume that the clip is a rectangle and that
1628 * the transform is only a translating transform.
1630 * @param text the string to be drawn
1631 * @param x the start of the baseline, X coordinate
1632 * @param y the start of the baseline, Y coordinate
1634 protected void rawDrawString(String text, int x, int y)
1636 FontRenderContext ctx = getFontRenderContext();
1637 GlyphVector gv = font.createGlyphVector(ctx, text.toCharArray());
1638 drawGlyphVector(gv, x, y);
1642 * Clears a rectangle in optimization mode. The implementation should respect the
1643 * clip and translation. It can assume that the clip is a rectangle and that
1644 * the transform is only a translating transform.
1646 * @param x the upper left corner, X coordinate
1647 * @param y the upper left corner, Y coordinate
1648 * @param w the width
1649 * @param h the height
1651 protected void rawClearRect(int x, int y, int w, int h)
1653 Paint savedForeground = getPaint();
1654 setPaint(getBackground());
1655 rawFillRect(x, y, w, h);
1656 setPaint(savedForeground);
1660 * Fills a rectangle in optimization mode. The implementation should respect
1661 * the clip but can assume that it is a rectangle.
1663 * @param x the upper left corner, X coordinate
1664 * @param y the upper left corner, Y coordinate
1665 * @param w the width
1666 * @param h the height
1668 protected void rawFillRect(int x, int y, int w, int h)
1670 ShapeCache sc = getShapeCache();
1671 if (sc.rect == null)
1672 sc.rect = new Rectangle();
1673 sc.rect.setBounds(x, y, w, h);
1674 fill(sc.rect);
1678 * Draws an image in optimization mode. The implementation should respect
1679 * the clip but can assume that it is a rectangle.
1681 * @param image the image to be painted
1682 * @param x the location, X coordinate
1683 * @param y the location, Y coordinate
1684 * @param obs the image observer to be notified
1686 * @return <code>true</code> when the image is painted completely,
1687 * <code>false</code> if it is still rendered
1689 protected boolean rawDrawImage(Image image, int x, int y, ImageObserver obs)
1691 AffineTransform t = new AffineTransform();
1692 t.translate(x, y);
1693 return drawImage(image, t, obs);
1697 * Copies a rectangular region to another location.
1699 * @param x the upper left corner, X coordinate
1700 * @param y the upper left corner, Y coordinate
1701 * @param w the width
1702 * @param h the height
1703 * @param dx
1704 * @param dy
1706 protected void rawCopyArea(int x, int y, int w, int h, int dx, int dy)
1708 copyAreaImpl(x, y, w, h, dx, dy);
1711 // Private implementation methods.
1714 * Copies a rectangular area of the target raster to a different location.
1716 private void copyAreaImpl(int x, int y, int w, int h, int dx, int dy)
1718 // FIXME: Implement this properly.
1719 throw new UnsupportedOperationException("Not implemented yet.");
1723 * Paints a scanline between x0 and x1. Override this when your backend
1724 * can efficiently draw/fill horizontal lines.
1726 * @param x0 the left offset
1727 * @param x1 the right offset
1728 * @param y the scanline
1730 public void renderScanline(int y, ScanlineCoverage c)
1732 PaintContext pCtx = paintContext;
1733 int x0 = c.getMinX();
1734 int x1 = c.getMaxX();
1735 Raster paintRaster = pCtx.getRaster(x0, y, x1 - x0, 1);
1737 // Do the anti aliasing thing.
1738 float coverageAlpha = 0;
1739 float maxCoverage = c.getMaxCoverage();
1740 ColorModel cm = pCtx.getColorModel();
1741 DataBuffer db = paintRaster.getDataBuffer();
1742 Point loc = new Point(paintRaster.getMinX(), paintRaster.getMinY());
1743 SampleModel sm = paintRaster.getSampleModel();
1744 WritableRaster writeRaster = Raster.createWritableRaster(sm, db, loc);
1745 WritableRaster alphaRaster = cm.getAlphaRaster(writeRaster);
1746 int pixel;
1747 ScanlineCoverage.Iterator iter = c.iterate();
1748 while (iter.hasNext())
1750 ScanlineCoverage.Range range = iter.next();
1751 coverageAlpha = range.getCoverage() / maxCoverage;
1752 if (coverageAlpha < 1.0)
1754 for (int x = range.getXPos(); x < range.getXPosEnd(); x++)
1756 pixel = alphaRaster.getSample(x, y, 0);
1757 pixel = (int) (pixel * coverageAlpha);
1758 alphaRaster.setSample(x, y, 0, pixel);
1762 ColorModel paintColorModel = pCtx.getColorModel();
1763 CompositeContext cCtx = composite.createContext(paintColorModel,
1764 getColorModel(),
1765 renderingHints);
1766 WritableRaster targetChild = destinationRaster.createWritableTranslatedChild(-x0,- y);
1767 cCtx.compose(paintRaster, targetChild, targetChild);
1768 updateRaster(destinationRaster, x0, y, x1 - x0, 1);
1769 cCtx.dispose();
1774 * Initializes this graphics object. This must be called by subclasses in
1775 * order to correctly initialize the state of this object.
1777 protected void init()
1779 setPaint(Color.BLACK);
1780 setFont(FONT);
1781 isOptimized = true;
1785 * Returns a WritableRaster that is used by this class to perform the
1786 * rendering in. It is not necessary that the target surface immediately
1787 * reflects changes in the raster. Updates to the raster are notified via
1788 * {@link #updateRaster}.
1790 * @return the destination raster
1792 protected WritableRaster getDestinationRaster()
1794 // TODO: Ideally we would fetch the xdrawable's surface pixels for
1795 // initialization of the raster.
1796 Rectangle db = getDeviceBounds();
1797 if (destinationRaster == null)
1799 int[] bandMasks = new int[]{ 0xFF0000, 0xFF00, 0xFF };
1800 destinationRaster = Raster.createPackedRaster(DataBuffer.TYPE_INT,
1801 db.width, db.height,
1802 bandMasks, null);
1803 // Initialize raster with white.
1804 int x0 = destinationRaster.getMinX();
1805 int x1 = destinationRaster.getWidth() + x0;
1806 int y0 = destinationRaster.getMinY();
1807 int y1 = destinationRaster.getHeight() + y0;
1808 int numBands = destinationRaster.getNumBands();
1809 for (int y = y0; y < y1; y++)
1811 for (int x = x0; x < x1; x++)
1813 for (int b = 0; b < numBands; b++)
1814 destinationRaster.setSample(x, y, b, 255);
1818 return destinationRaster;
1822 * Notifies the backend that the raster has changed in the specified
1823 * rectangular area. The raster that is provided in this method is always
1824 * the same as the one returned in {@link #getDestinationRaster}.
1825 * Backends that reflect changes to this raster directly don't need to do
1826 * anything here.
1828 * @param raster the updated raster, identical to the raster returned
1829 * by {@link #getDestinationRaster()}
1830 * @param x the upper left corner of the updated region, X coordinate
1831 * @param y the upper lef corner of the updated region, Y coordinate
1832 * @param w the width of the updated region
1833 * @param h the height of the updated region
1835 protected void updateRaster(Raster raster, int x, int y, int w, int h)
1837 // Nothing to do here. Backends that need to update their surface
1838 // to reflect the change should override this method.
1841 // Some helper methods.
1844 * Helper method to check and update the optimization conditions.
1846 private void updateOptimization()
1848 int transformType = transform.getType();
1849 boolean optimizedTransform = false;
1850 if (transformType == AffineTransform.TYPE_IDENTITY
1851 || transformType == AffineTransform.TYPE_TRANSLATION)
1852 optimizedTransform = true;
1854 boolean optimizedClip = (clip == null || clip instanceof Rectangle);
1855 isOptimized = optimizedClip
1856 && optimizedTransform && paint instanceof Color
1857 && composite == AlphaComposite.SrcOver
1858 && stroke.equals(new BasicStroke());
1862 * Calculates the intersection of two rectangles. The result is stored
1863 * in <code>rect</code>. This is basically the same
1864 * like {@link Rectangle#intersection(Rectangle)}, only that it does not
1865 * create new Rectangle instances. The tradeoff is that you loose any data in
1866 * <code>rect</code>.
1868 * @param x upper-left x coodinate of first rectangle
1869 * @param y upper-left y coodinate of first rectangle
1870 * @param w width of first rectangle
1871 * @param h height of first rectangle
1872 * @param rect a Rectangle object of the second rectangle
1874 * @throws NullPointerException if rect is null
1876 * @return a rectangle corresponding to the intersection of the
1877 * two rectangles. An empty rectangle is returned if the rectangles
1878 * do not overlap
1880 private static Rectangle computeIntersection(int x, int y, int w, int h,
1881 Rectangle rect)
1883 int x2 = rect.x;
1884 int y2 = rect.y;
1885 int w2 = rect.width;
1886 int h2 = rect.height;
1888 int dx = (x > x2) ? x : x2;
1889 int dy = (y > y2) ? y : y2;
1890 int dw = (x + w < x2 + w2) ? (x + w - dx) : (x2 + w2 - dx);
1891 int dh = (y + h < y2 + h2) ? (y + h - dy) : (y2 + h2 - dy);
1893 if (dw >= 0 && dh >= 0)
1894 rect.setBounds(dx, dy, dw, dh);
1895 else
1896 rect.setBounds(0, 0, 0, 0);
1898 return rect;
1902 * Helper method to transform the clip. This is called by the various
1903 * transformation-manipulation methods to update the clip (which is in
1904 * userspace) accordingly.
1906 * The transform usually is the inverse transform that was applied to the
1907 * graphics object.
1909 * @param t the transform to apply to the clip
1911 private void updateClip(AffineTransform t)
1913 if (! (clip instanceof GeneralPath))
1914 clip = new GeneralPath(clip);
1916 GeneralPath p = (GeneralPath) clip;
1917 p.transform(t);
1921 * Returns the ShapeCache for the calling thread.
1923 * @return the ShapeCache for the calling thread
1925 private ShapeCache getShapeCache()
1927 ShapeCache sc = shapeCache.get();
1928 if (sc == null)
1930 sc = new ShapeCache();
1931 shapeCache.set(sc);
1933 return sc;
1937 * Returns the scanline converter for this thread.
1939 * @return the scanline converter for this thread
1941 private ScanlineConverter getScanlineConverter()
1943 ScanlineConverter sc = scanlineConverters.get();
1944 if (sc == null)
1946 sc = new ScanlineConverter();
1947 scanlineConverters.set(sc);
1949 return sc;