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)
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
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
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
.BasicStroke
;
43 import java
.awt
.Color
;
44 import java
.awt
.Composite
;
45 import java
.awt
.CompositeContext
;
47 import java
.awt
.FontMetrics
;
48 import java
.awt
.Graphics
;
49 import java
.awt
.Graphics2D
;
50 import java
.awt
.Image
;
51 import java
.awt
.Paint
;
52 import java
.awt
.PaintContext
;
53 import java
.awt
.Polygon
;
54 import java
.awt
.Rectangle
;
55 import java
.awt
.RenderingHints
;
56 import java
.awt
.Shape
;
57 import java
.awt
.Stroke
;
58 import java
.awt
.Toolkit
;
59 import java
.awt
.RenderingHints
.Key
;
60 import java
.awt
.font
.FontRenderContext
;
61 import java
.awt
.font
.GlyphVector
;
62 import java
.awt
.geom
.AffineTransform
;
63 import java
.awt
.geom
.Arc2D
;
64 import java
.awt
.geom
.Area
;
65 import java
.awt
.geom
.Ellipse2D
;
66 import java
.awt
.geom
.GeneralPath
;
67 import java
.awt
.geom
.Line2D
;
68 import java
.awt
.geom
.NoninvertibleTransformException
;
69 import java
.awt
.geom
.PathIterator
;
70 import java
.awt
.geom
.Rectangle2D
;
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
.ImageObserver
;
76 import java
.awt
.image
.Raster
;
77 import java
.awt
.image
.RenderedImage
;
78 import java
.awt
.image
.WritableRaster
;
79 import java
.awt
.image
.renderable
.RenderableImage
;
80 import java
.text
.AttributedCharacterIterator
;
81 import java
.util
.ArrayList
;
82 import java
.util
.HashMap
;
83 import java
.util
.Iterator
;
87 * Implements general and shared behaviour for Graphics2D implementation.
89 * @author Roman Kennke (kennke@aicas.com)
91 public abstract class AbstractGraphics2D
97 * Accuracy of the sampling in the anti-aliasing shape filler.
98 * Lower values give more speed, while higher values give more quality.
99 * It is advisable to choose powers of two.
101 private static final int AA_SAMPLING
= 8;
104 * The transformation for this Graphics2D instance
106 private AffineTransform transform
;
116 private Color background
;
124 * The current composite setting.
126 private Composite composite
;
129 * The current stroke setting.
131 private Stroke stroke
;
134 * The current clip. This clip is in user coordinate space.
139 * The rendering hints.
141 private RenderingHints renderingHints
;
146 private Raster paintRaster
;
149 * A cached pixel array.
154 * The raster of the destination surface. This is where the painting is
157 private WritableRaster destinationRaster
;
160 * Stores the alpha values for a scanline in the anti-aliasing shape
163 private transient int[] alpha
;
166 * The edge table for the scanline conversion algorithms.
168 private transient ArrayList
[] edgeTable
;
171 * Indicates if cerain graphics primitives can be rendered in an optimized
172 * fashion. This will be the case if the following conditions are met:
173 * - The transform may only be a translation, no rotation, shearing or
175 * - The paint must be a solid color.
176 * - The composite must be an AlphaComposite.SrcOver.
177 * - The clip must be a Rectangle.
178 * - The stroke must be a plain BasicStroke().
180 * These conditions represent the standard settings of a new
181 * AbstractGraphics2D object and will be the most commonly used setting
182 * in Swing rendering and should therefore be optimized as much as possible.
184 private boolean isOptimized
;
187 * Creates a new AbstractGraphics2D instance.
189 protected AbstractGraphics2D()
191 transform
= new AffineTransform();
192 background
= Color
.WHITE
;
193 composite
= AlphaComposite
.SrcOver
;
194 stroke
= new BasicStroke();
195 HashMap hints
= new HashMap();
196 hints
.put(RenderingHints
.KEY_TEXT_ANTIALIASING
,
197 RenderingHints
.VALUE_TEXT_ANTIALIAS_DEFAULT
);
198 hints
.put(RenderingHints
.KEY_ANTIALIASING
,
199 RenderingHints
.VALUE_ANTIALIAS_DEFAULT
);
200 renderingHints
= new RenderingHints(hints
);
206 * Draws the specified shape. The shape is passed through the current stroke
207 * and is then forwarded to {@link #fillShape}.
209 * @param shape the shape to draw
211 public void draw(Shape shape
)
214 Shape strokedShape
= stroke
.createStrokedShape(shape
);
216 // Clip the stroked shape.
217 // Shape clipped = clipShape(strokedShape);
218 // if (clipped != null)
220 // // Fill the shape.
221 // fillShape(clipped, false);
223 // FIXME: Clipping doesn't seem to work.
224 fillShape(strokedShape
, false);
227 public boolean drawImage(Image image
, AffineTransform xform
, ImageObserver obs
)
229 // FIXME: Implement this.
230 throw new UnsupportedOperationException("Not yet implemented");
233 public void drawImage(BufferedImage image
, BufferedImageOp op
, int x
, int y
)
235 // FIXME: Implement this.
236 throw new UnsupportedOperationException("Not yet implemented");
239 public void drawRenderedImage(RenderedImage image
, AffineTransform xform
)
241 // FIXME: Implement this.
242 throw new UnsupportedOperationException("Not yet implemented");
245 public void drawRenderableImage(RenderableImage image
, AffineTransform xform
)
247 // FIXME: Implement this.
248 throw new UnsupportedOperationException("Not yet implemented");
252 * Draws the specified string at the specified location.
254 * @param text the string to draw
255 * @param x the x location, relative to the bounding rectangle of the text
256 * @param y the y location, relative to the bounding rectangle of the text
258 public void drawString(String text
, int x
, int y
)
260 FontRenderContext ctx
= getFontRenderContext();
261 GlyphVector gv
= font
.createGlyphVector(ctx
, text
.toCharArray());
262 drawGlyphVector(gv
, x
, y
);
266 * Draws the specified string at the specified location.
268 * @param text the string to draw
269 * @param x the x location, relative to the bounding rectangle of the text
270 * @param y the y location, relative to the bounding rectangle of the text
272 public void drawString(String text
, float x
, float y
)
274 FontRenderContext ctx
= getFontRenderContext();
275 GlyphVector gv
= font
.createGlyphVector(ctx
, text
.toCharArray());
276 drawGlyphVector(gv
, x
, y
);
280 * Draws the specified string (as AttributedCharacterIterator) at the
281 * specified location.
283 * @param iterator the string to draw
284 * @param x the x location, relative to the bounding rectangle of the text
285 * @param y the y location, relative to the bounding rectangle of the text
287 public void drawString(AttributedCharacterIterator iterator
, int x
, int y
)
289 FontRenderContext ctx
= getFontRenderContext();
290 GlyphVector gv
= font
.createGlyphVector(ctx
, iterator
);
291 drawGlyphVector(gv
, x
, y
);
295 * Draws the specified string (as AttributedCharacterIterator) at the
296 * specified location.
298 * @param iterator the string to draw
299 * @param x the x location, relative to the bounding rectangle of the text
300 * @param y the y location, relative to the bounding rectangle of the text
302 public void drawString(AttributedCharacterIterator iterator
, float x
, float y
)
304 FontRenderContext ctx
= getFontRenderContext();
305 GlyphVector gv
= font
.createGlyphVector(ctx
, iterator
);
306 drawGlyphVector(gv
, x
, y
);
310 * Fills the specified shape with the current foreground.
312 * @param shape the shape to fill
314 public void fill(Shape shape
)
316 // Shape clipped = clipShape(shape);
317 // if (clipped != null)
318 // fillShape(clipped, false);
319 fillShape(shape
, false);
322 public boolean hit(Rectangle rect
, Shape text
, boolean onStroke
)
324 // FIXME: Implement this.
325 throw new UnsupportedOperationException("Not yet implemented");
329 * Sets the composite.
331 * @param comp the composite to set
333 public void setComposite(Composite comp
)
336 if (! (comp
.equals(AlphaComposite
.SrcOver
)))
339 updateOptimization();
343 * Sets the current foreground.
345 * @param p the foreground to set.
347 public void setPaint(Paint p
)
353 if (! (paint
instanceof Color
))
357 updateOptimization();
358 rawSetForeground((Color
) paint
);
364 * Sets the stroke for this graphics object.
366 * @param s the stroke to set
368 public void setStroke(Stroke s
)
371 if (! stroke
.equals(new BasicStroke()))
374 updateOptimization();
378 * Sets the specified rendering hint.
380 * @param hintKey the key of the rendering hint
381 * @param hintValue the value
383 public void setRenderingHint(Key hintKey
, Object hintValue
)
385 renderingHints
.put(hintKey
, hintValue
);
389 * Returns the rendering hint for the specified key.
391 * @param hintKey the rendering hint key
393 * @return the rendering hint for the specified key
395 public Object
getRenderingHint(Key hintKey
)
397 return renderingHints
.get(hintKey
);
401 * Sets the specified rendering hints.
403 * @param hints the rendering hints to set
405 public void setRenderingHints(Map hints
)
407 renderingHints
.clear();
408 renderingHints
.putAll(hints
);
412 * Adds the specified rendering hints.
414 * @param hints the rendering hints to add
416 public void addRenderingHints(Map hints
)
418 renderingHints
.putAll(hints
);
422 * Returns the current rendering hints.
424 * @return the current rendering hints
426 public RenderingHints
getRenderingHints()
428 return (RenderingHints
) renderingHints
.clone();
432 * Translates the coordinate system by (x, y).
434 * @param x the translation X coordinate
435 * @param y the translation Y coordinate
437 public void translate(int x
, int y
)
439 transform
.translate(x
, y
);
441 // Update the clip. We special-case rectangular clips here, because they
442 // are so common (e.g. in Swing).
445 if (clip
instanceof Rectangle
)
447 Rectangle r
= (Rectangle
) clip
;
454 AffineTransform clipTransform
= new AffineTransform();
455 clipTransform
.translate(-x
, -y
);
456 updateClip(clipTransform
);
462 * Translates the coordinate system by (tx, ty).
464 * @param tx the translation X coordinate
465 * @param ty the translation Y coordinate
467 public void translate(double tx
, double ty
)
469 transform
.translate(tx
, ty
);
471 // Update the clip. We special-case rectangular clips here, because they
472 // are so common (e.g. in Swing).
475 if (clip
instanceof Rectangle
)
477 Rectangle r
= (Rectangle
) clip
;
483 AffineTransform clipTransform
= new AffineTransform();
484 clipTransform
.translate(-tx
, -ty
);
485 updateClip(clipTransform
);
491 * Rotates the coordinate system by <code>theta</code> degrees.
493 * @param theta the angle be which to rotate the coordinate system
495 public void rotate(double theta
)
497 transform
.rotate(theta
);
500 AffineTransform clipTransform
= new AffineTransform();
501 clipTransform
.rotate(-theta
);
502 updateClip(clipTransform
);
504 updateOptimization();
508 * Rotates the coordinate system by <code>theta</code> around the point
511 * @param theta the angle by which to rotate the coordinate system
512 * @param x the point around which to rotate, X coordinate
513 * @param y the point around which to rotate, Y coordinate
515 public void rotate(double theta
, double x
, double y
)
517 transform
.rotate(theta
, x
, y
);
520 AffineTransform clipTransform
= new AffineTransform();
521 clipTransform
.rotate(-theta
, x
, y
);
522 updateClip(clipTransform
);
524 updateOptimization();
528 * Scales the coordinate system by the factors <code>scaleX</code> and
529 * <code>scaleY</code>.
531 * @param scaleX the factor by which to scale the X axis
532 * @param scaleY the factor by which to scale the Y axis
534 public void scale(double scaleX
, double scaleY
)
536 transform
.scale(scaleX
, scaleY
);
539 AffineTransform clipTransform
= new AffineTransform();
540 clipTransform
.scale(-scaleX
, -scaleY
);
541 updateClip(clipTransform
);
543 updateOptimization();
547 * Shears the coordinate system by <code>shearX</code> and
548 * <code>shearY</code>.
550 * @param shearX the X shearing
551 * @param shearY the Y shearing
553 public void shear(double shearX
, double shearY
)
555 transform
.shear(shearX
, shearY
);
558 AffineTransform clipTransform
= new AffineTransform();
559 clipTransform
.shear(-shearX
, -shearY
);
560 updateClip(clipTransform
);
562 updateOptimization();
566 * Transforms the coordinate system using the specified transform
569 * @param t the transform
571 public void transform(AffineTransform t
)
573 transform
.concatenate(t
);
576 AffineTransform clipTransform
= t
.createInverse();
577 updateClip(clipTransform
);
579 catch (NoninvertibleTransformException ex
)
581 // TODO: How can we deal properly with this?
582 ex
.printStackTrace();
584 updateOptimization();
588 * Sets the transformation for this Graphics object.
590 * @param t the transformation to set
592 public void setTransform(AffineTransform t
)
594 // Transform clip into target space using the old transform.
595 updateClip(transform
);
596 transform
.setTransform(t
);
597 // Transform the clip back into user space using the inverse new transform.
600 updateClip(transform
.createInverse());
602 catch (NoninvertibleTransformException ex
)
604 // TODO: How can we deal properly with this?
605 ex
.printStackTrace();
607 updateOptimization();
611 * Returns the transformation of this coordinate system.
613 * @return the transformation of this coordinate system
615 public AffineTransform
getTransform()
617 return (AffineTransform
) transform
.clone();
621 * Returns the current foreground.
623 * @return the current foreground
625 public Paint
getPaint()
632 * Returns the current composite.
634 * @return the current composite
636 public Composite
getComposite()
642 * Sets the current background.
644 * @param color the background to set.
646 public void setBackground(Color color
)
652 * Returns the current background.
654 * @return the current background
656 public Color
getBackground()
662 * Returns the current stroke.
664 * @return the current stroke
666 public Stroke
getStroke()
672 * Intersects the clip of this graphics object with the specified clip.
674 * @param s the clip with which the current clip should be intersected
676 public void clip(Shape s
)
678 // Initialize clip if not already present.
682 // This is so common, let's optimize this.
683 else if (clip
instanceof Rectangle
&& s
instanceof Rectangle
)
685 Rectangle clipRect
= (Rectangle
) clip
;
686 Rectangle r
= (Rectangle
) s
;
687 computeIntersection(r
.x
, r
.y
, r
.width
, r
.height
, clipRect
);
688 // Call setClip so that subclasses get notified.
694 if (clip
instanceof Area
)
695 current
= (Area
) clip
;
697 current
= new Area(clip
);
700 if (s
instanceof Area
)
701 intersect
= (Area
) s
;
703 intersect
= new Area(s
);
705 current
.intersect(intersect
);
708 // Call setClip so that subclasses get notified.
713 public FontRenderContext
getFontRenderContext()
715 //return new FontRenderContext(transform, false, false);
716 return new FontRenderContext(new AffineTransform(), false, false);
720 * Draws the specified glyph vector at the specified location.
722 * @param gv the glyph vector to draw
723 * @param x the location, x coordinate
724 * @param y the location, y coordinate
726 public void drawGlyphVector(GlyphVector gv
, float x
, float y
)
728 int numGlyphs
= gv
.getNumGlyphs();
729 AffineTransform t
= new AffineTransform();
732 // // TODO: We could use fill(gv.getOutline()), but that seems to be
733 // slightly more inefficient.
734 for (int i
= 0; i
< numGlyphs
; i
++)
736 //fill(gv.getGlyphVisualBounds(i));
737 GeneralPath p
= new GeneralPath(gv
.getGlyphOutline(i
));
739 //Shape clipped = clipShape(p);
740 //if (clipped != null)
741 // fillShape(clipped, true);
742 // FIXME: Clipping doesn't seem to work correctly.
748 * Creates a copy of this graphics object.
750 * @return a copy of this graphics object
752 public Graphics
create()
754 AbstractGraphics2D copy
= (AbstractGraphics2D
) clone();
759 * Creates and returns a copy of this Graphics object. This should
760 * be overridden by subclasses if additional state must be handled when
761 * cloning. This is called by {@link #create()}.
763 * @return a copy of this Graphics object
765 protected Object
clone()
769 AbstractGraphics2D copy
= (AbstractGraphics2D
) super.clone();
770 // Copy the clip. If it's a Rectangle, preserve that for optimization.
771 if (clip
instanceof Rectangle
)
772 copy
.clip
= new Rectangle((Rectangle
) clip
);
774 copy
.clip
= new GeneralPath(clip
);
776 copy
.renderingHints
= new RenderingHints(renderingHints
);
777 copy
.transform
= new AffineTransform(transform
);
778 // The remaining state is inmmutable and doesn't need to be copied.
781 catch (CloneNotSupportedException ex
)
783 AWTError err
= new AWTError("Unexpected exception while cloning");
790 * Returns the current foreground.
792 public Color
getColor()
795 if (paint
instanceof Color
)
801 * Sets the current foreground.
803 * @param color the foreground to set
805 public void setColor(Color color
)
810 public void setPaintMode()
812 // FIXME: Implement this.
813 throw new UnsupportedOperationException("Not yet implemented");
816 public void setXORMode(Color color
)
818 // FIXME: Implement this.
819 throw new UnsupportedOperationException("Not yet implemented");
823 * Returns the current font.
825 * @return the current font
827 public Font
getFont()
833 * Sets the font on this graphics object. When <code>f == null</code>, the
834 * current setting is not changed.
836 * @param f the font to set
838 public void setFont(Font f
)
845 * Returns the font metrics for the specified font.
847 * @param font the font for which to fetch the font metrics
849 * @return the font metrics for the specified font
851 public FontMetrics
getFontMetrics(Font font
)
853 return Toolkit
.getDefaultToolkit().getFontMetrics(font
);
857 * Returns the bounds of the current clip.
859 * @return the bounds of the current clip
861 public Rectangle
getClipBounds()
865 b
= clip
.getBounds();
870 * Intersects the current clipping region with the specified rectangle.
872 * @param x the x coordinate of the rectangle
873 * @param y the y coordinate of the rectangle
874 * @param width the width of the rectangle
875 * @param height the height of the rectangle
877 public void clipRect(int x
, int y
, int width
, int height
)
879 clip(new Rectangle(x
, y
, width
, height
));
883 * Sets the clip to the specified rectangle.
885 * @param x the x coordinate of the clip rectangle
886 * @param y the y coordinate of the clip rectangle
887 * @param width the width of the clip rectangle
888 * @param height the height of the clip rectangle
890 public void setClip(int x
, int y
, int width
, int height
)
892 setClip(new Rectangle(x
, y
, width
, height
));
896 * Returns the current clip.
898 * @return the current clip
900 public Shape
getClip()
906 * Sets the current clipping area to <code>clip</code>.
908 * @param c the clip to set
910 public void setClip(Shape c
)
913 if (! (clip
instanceof Rectangle
))
916 updateOptimization();
919 public void copyArea(int x
, int y
, int width
, int height
, int dx
, int dy
)
921 // FIXME: Implement this.
922 throw new UnsupportedOperationException("Not yet implemented");
926 * Draws a line from (x1, y1) to (x2, y2).
928 * This implementation transforms the coordinates and forwards the call to
929 * {@link #rawDrawLine}.
931 public void drawLine(int x1
, int y1
, int x2
, int y2
)
935 int tx
= (int) transform
.getTranslateX();
936 int ty
= (int) transform
.getTranslateY();
937 rawDrawLine(x1
+ tx
, y1
+ ty
, x2
+ tx
, y2
+ ty
);
941 Line2D line
= new Line2D
.Double(x1
, y1
, x2
, y2
);
947 * Fills a rectangle with the current paint.
949 * @param x the upper left corner, X coordinate
950 * @param y the upper left corner, Y coordinate
951 * @param width the width of the rectangle
952 * @param height the height of the rectangle
954 public void fillRect(int x
, int y
, int width
, int height
)
958 int tx
= (int) transform
.getTranslateX();
959 int ty
= (int) transform
.getTranslateY();
960 rawFillRect(x
+ tx
, y
+ ty
, width
, height
);
964 fill(new Rectangle(x
, y
, width
, height
));
969 * Fills a rectangle with the current background color.
971 * This implementation temporarily sets the foreground color to the
972 * background and forwards the call to {@link #fillRect(int, int, int, int)}.
974 * @param x the upper left corner, X coordinate
975 * @param y the upper left corner, Y coordinate
976 * @param width the width of the rectangle
977 * @param height the height of the rectangle
979 public void clearRect(int x
, int y
, int width
, int height
)
981 Paint savedForeground
= getPaint();
982 setPaint(getBackground());
983 //System.err.println("clearRect transform type: " + transform.getType());
984 fillRect(x
, y
, width
, height
);
985 setPaint(savedForeground
);
989 * Draws a rounded rectangle.
991 * @param x the x coordinate of the rectangle
992 * @param y the y coordinate of the rectangle
993 * @param width the width of the rectangle
994 * @param height the height of the rectangle
995 * @param arcWidth the width of the arcs
996 * @param arcHeight the height of the arcs
998 public void drawRoundRect(int x
, int y
, int width
, int height
, int arcWidth
,
1001 draw(new RoundRectangle2D
.Double(x
, y
, width
, height
, arcWidth
,
1006 * Fills a rounded rectangle.
1008 * @param x the x coordinate of the rectangle
1009 * @param y the y coordinate of the rectangle
1010 * @param width the width of the rectangle
1011 * @param height the height of the rectangle
1012 * @param arcWidth the width of the arcs
1013 * @param arcHeight the height of the arcs
1015 public void fillRoundRect(int x
, int y
, int width
, int height
, int arcWidth
,
1018 fill(new RoundRectangle2D
.Double(x
, y
, width
, height
, arcWidth
,
1023 * Draws the outline of an oval.
1025 * @param x the upper left corner of the bounding rectangle of the ellipse
1026 * @param y the upper left corner of the bounding rectangle of the ellipse
1027 * @param width the width of the ellipse
1028 * @param height the height of the ellipse
1030 public void drawOval(int x
, int y
, int width
, int height
)
1032 draw(new Ellipse2D
.Double(x
, y
, width
, height
));
1038 * @param x the upper left corner of the bounding rectangle of the ellipse
1039 * @param y the upper left corner of the bounding rectangle of the ellipse
1040 * @param width the width of the ellipse
1041 * @param height the height of the ellipse
1043 public void fillOval(int x
, int y
, int width
, int height
)
1045 fill(new Ellipse2D
.Double(x
, y
, width
, height
));
1051 public void drawArc(int x
, int y
, int width
, int height
, int arcStart
,
1054 draw(new Arc2D
.Double(x
, y
, width
, height
, arcStart
, arcAngle
,
1061 public void fillArc(int x
, int y
, int width
, int height
, int arcStart
,
1064 fill(new Arc2D
.Double(x
, y
, width
, height
, arcStart
, arcAngle
,
1068 public void drawPolyline(int[] xPoints
, int[] yPoints
, int npoints
)
1070 // FIXME: Implement this.
1071 throw new UnsupportedOperationException("Not yet implemented");
1075 * Draws the outline of a polygon.
1077 public void drawPolygon(int[] xPoints
, int[] yPoints
, int npoints
)
1079 draw(new Polygon(xPoints
, yPoints
, npoints
));
1083 * Fills the outline of a polygon.
1085 public void fillPolygon(int[] xPoints
, int[] yPoints
, int npoints
)
1087 fill(new Polygon(xPoints
, yPoints
, npoints
));
1090 public boolean drawImage(Image image
, int x
, int y
, ImageObserver observer
)
1092 // FIXME: Implement this.
1093 throw new UnsupportedOperationException("Not yet implemented");
1096 public boolean drawImage(Image image
, int x
, int y
, int width
, int height
,
1097 ImageObserver observer
)
1099 // FIXME: Implement this.
1100 throw new UnsupportedOperationException("Not yet implemented");
1103 public boolean drawImage(Image image
, int x
, int y
, Color bgcolor
,
1104 ImageObserver observer
)
1106 // FIXME: Implement this.
1107 throw new UnsupportedOperationException("Not yet implemented");
1110 public boolean drawImage(Image image
, int x
, int y
, int width
, int height
,
1111 Color bgcolor
, ImageObserver observer
)
1113 // FIXME: Implement this.
1114 throw new UnsupportedOperationException("Not yet implemented");
1117 public boolean drawImage(Image image
, int dx1
, int dy1
, int dx2
, int dy2
,
1118 int sx1
, int sy1
, int sx2
, int sy2
,
1119 ImageObserver observer
)
1121 // FIXME: Implement this.
1122 throw new UnsupportedOperationException("Not yet implemented");
1125 public boolean drawImage(Image image
, int dx1
, int dy1
, int dx2
, int dy2
,
1126 int sx1
, int sy1
, int sx2
, int sy2
, Color bgcolor
,
1127 ImageObserver observer
)
1129 // FIXME: Implement this.
1130 throw new UnsupportedOperationException("Not yet implemented");
1134 * Disposes this graphics object.
1136 public void dispose()
1138 // Nothing special to do here.
1142 * Fills the specified shape. The shape has already been clipped against the
1145 * @param s the shape to fill
1146 * @param isFont <code>true</code> if the shape is a font outline
1148 protected void fillShape(Shape s
, boolean isFont
)
1150 // Determine if we need to antialias stuff.
1151 boolean antialias
= false;
1154 Object v
= renderingHints
.get(RenderingHints
.KEY_TEXT_ANTIALIASING
);
1155 // We default to antialiasing on for text as long as we have no
1156 // good hinting implemented.
1157 antialias
= (v
== RenderingHints
.VALUE_TEXT_ANTIALIAS_ON
1158 || v
== RenderingHints
.VALUE_TEXT_ANTIALIAS_DEFAULT
);
1162 Object v
= renderingHints
.get(RenderingHints
.KEY_ANTIALIASING
);
1163 antialias
= (v
== RenderingHints
.VALUE_ANTIALIAS_ON
);
1166 Rectangle2D userBounds
= s
.getBounds2D();
1168 // Flatten the path. TODO: Determine the best flattening factor
1169 // wrt to speed and quality.
1170 PathIterator path
= s
.getPathIterator(getTransform(), 1.0);
1172 // Build up polygons and let the native backend render this using
1173 // rawFillShape() which would provide a default implementation for
1174 // drawPixel using a PolyScan algorithm.
1175 double[] seg
= new double[6];
1177 // TODO: Use ArrayList<PolyEdge> here when availble.
1178 ArrayList segs
= new ArrayList();
1179 double segX
= 0.; // The start point of the current edge.
1181 double polyX
= 0.; // The start point of the current polygon.
1184 double minX
= Integer
.MAX_VALUE
;
1185 double maxX
= Integer
.MIN_VALUE
;
1186 double minY
= Integer
.MAX_VALUE
;
1187 double maxY
= Integer
.MIN_VALUE
;
1189 //System.err.println("fill polygon");
1190 while (! path
.isDone())
1192 int segType
= path
.currentSegment(seg
);
1193 minX
= Math
.min(minX
, seg
[0]);
1194 maxX
= Math
.max(maxX
, seg
[0]);
1195 minY
= Math
.min(minY
, seg
[1]);
1196 maxY
= Math
.max(maxY
, seg
[1]);
1198 //System.err.println("segment: " + segType + ", " + seg[0] + ", " + seg[1]);
1199 if (segType
== PathIterator
.SEG_MOVETO
)
1206 else if (segType
== PathIterator
.SEG_CLOSE
)
1208 // Close the polyline.
1209 PolyEdge edge
= new PolyEdge(segX
, segY
, polyX
, polyY
);
1212 else if (segType
== PathIterator
.SEG_LINETO
)
1214 PolyEdge edge
= new PolyEdge(segX
, segY
, seg
[0], seg
[1]);
1221 if (segs
.size() > 0)
1224 fillShapeAntialias(segs
, minX
, minY
, maxX
, maxY
, userBounds
);
1226 rawFillShape(segs
, minX
, minY
, maxX
, maxY
, userBounds
);
1231 * Draws one pixel in the target coordinate space. This method draws the
1232 * specified pixel by getting the painting pixel for that coordinate
1233 * from the paintContext and compositing the pixel with the compositeContext.
1234 * The resulting pixel is then set by calling {@link #rawSetPixel}.
1236 * @param x the x coordinate
1237 * @param y the y coordinate
1239 protected void drawPixel(int x
, int y
)
1241 // FIXME: Implement efficient compositing.
1242 if (! (paint
instanceof Color
))
1244 int[] paintPixel
= paintRaster
.getPixel(x
, y
, pixel
);
1245 Color c
= new Color(paintPixel
[0], paintPixel
[1], paintPixel
[2]);
1246 rawSetForeground(c
);
1252 * Draws a pixel in the target coordinate space using the specified color.
1254 * @param x the x coordinate
1255 * @param y the y coordinate
1257 protected void rawSetPixel(int x
, int y
)
1259 // FIXME: Provide default implementation or remove method.
1263 * Sets the foreground color for drawing.
1265 * @param c the color to set
1267 protected void rawSetForeground(Color c
)
1269 // Probably remove method.
1272 protected void rawSetForeground(int r
, int g
, int b
)
1274 rawSetForeground(new Color(r
, g
, b
));
1278 * Returns the color model of this Graphics object.
1280 * @return the color model of this Graphics object
1282 protected abstract ColorModel
getColorModel();
1285 * Returns the bounds of the target.
1287 * @return the bounds of the target
1289 protected Rectangle
getDeviceBounds()
1291 return destinationRaster
.getBounds();
1295 * Returns the bounds of the drawing area in user space.
1297 * @return the bounds of the drawing area in user space
1299 protected Rectangle2D
getUserBounds()
1301 PathIterator pathIter
= getDeviceBounds().getPathIterator(getTransform());
1302 GeneralPath path
= new GeneralPath();
1303 path
.append(pathIter
, true);
1304 return path
.getBounds();
1308 * Draws a line in optimization mode. The implementation should respect the
1309 * clip but can assume that it is a rectangle.
1311 * @param x0 the starting point, X coordinate
1312 * @param y0 the starting point, Y coordinate
1313 * @param x1 the end point, X coordinate
1314 * @param y1 the end point, Y coordinate
1316 protected void rawDrawLine(int x0
, int y0
, int x1
, int y1
)
1318 // This is an implementation of Bresenham's line drawing algorithm.
1347 int fraction
= dy
- (dx
>> 1); // same as 2*dy - dx
1362 int fraction
= dx
- (dy
>> 1);
1378 * Fills a rectangle in optimization mode. The implementation should respect
1379 * the clip but can assume that it is a rectangle.
1381 * @param x the upper left corner, X coordinate
1382 * @param y the upper left corner, Y coordinate
1383 * @param w the width
1384 * @param h the height
1386 protected void rawFillRect(int x
, int y
, int w
, int h
)
1390 for (int xc
= x
; xc
< x2
; xc
++)
1392 for (int yc
= y
; yc
< y2
; yc
++)
1400 * Fills the specified polygon. This should be overridden by backends
1401 * that support accelerated (native) polygon filling, which is the
1402 * case for most toolkit window and offscreen image implementations.
1404 * The polygon is already clipped when this method is called.
1406 protected void rawFillShape(ArrayList segs
, double minX
, double minY
,
1407 double maxX
, double maxY
, Rectangle2D userBounds
)
1409 // This is an implementation of a polygon scanline conversion algorithm
1411 // http://www.cs.berkeley.edu/~ug/slide/pipeline/assignments/scan/
1413 // Create table of all edges.
1414 // The edge buckets, sorted and indexed by their Y values.
1416 Rectangle deviceBounds
= new Rectangle((int) minX
, (int) minY
,
1417 (int) Math
.ceil(maxX
) - (int) minX
,
1418 (int) Math
.ceil(maxY
) - (int) minY
);
1419 PaintContext pCtx
= paint
.createContext(getColorModel(), deviceBounds
,
1420 userBounds
, transform
, renderingHints
);
1422 ArrayList
[] edgeTable
= new ArrayList
[(int) Math
.ceil(maxY
)
1423 - (int) Math
.ceil(minY
) + 1];
1425 for (Iterator i
= segs
.iterator(); i
.hasNext();)
1427 PolyEdge edge
= (PolyEdge
) i
.next();
1428 int yindex
= (int) ((int) Math
.ceil(edge
.y0
) - (int) Math
.ceil(minY
));
1429 if (edgeTable
[yindex
] == null) // Create bucket when needed.
1430 edgeTable
[yindex
] = new ArrayList();
1431 edgeTable
[yindex
].add(edge
); // Add edge to the bucket of its line.
1434 // TODO: The following could be useful for a future optimization.
1435 // // Sort all the edges in the edge table within their buckets.
1436 // for (int y = 0; y < edgeTable.length; y++)
1438 // if (edgeTable[y] != null)
1439 // Collections.sort(edgeTable[y]);
1442 // The activeEdges list contains all the edges of the current scanline
1443 // ordered by their intersection points with this scanline.
1444 ArrayList activeEdges
= new ArrayList();
1445 PolyEdgeComparator comparator
= new PolyEdgeComparator();
1447 // Scan all relevant lines.
1448 int minYInt
= (int) Math
.ceil(minY
);
1449 for (int y
= minYInt
; y
<= maxY
; y
++)
1451 ArrayList bucket
= edgeTable
[y
- minYInt
];
1452 // Update all the x intersections in the current activeEdges table
1453 // and remove entries that are no longer in the scanline.
1454 for (Iterator i
= activeEdges
.iterator(); i
.hasNext();)
1456 PolyEdge edge
= (PolyEdge
) i
.next();
1461 edge
.xIntersection
+= edge
.slope
;
1462 //edge.xIntersection = edge.x0 + edge.slope * (y - edge.y0);
1463 //System.err.println("edge.xIntersection: " + edge.xIntersection);
1468 activeEdges
.addAll(bucket
);
1470 // Sort current edges. We are using a bubble sort, because the order
1471 // of the intersections will not change in most situations. They
1472 // will only change, when edges intersect each other.
1473 int size
= activeEdges
.size();
1476 for (int i
= 1; i
< size
; i
++)
1478 PolyEdge e1
= (PolyEdge
) activeEdges
.get(i
- 1);
1479 PolyEdge e2
= (PolyEdge
) activeEdges
.get(i
);
1480 if (comparator
.compare(e1
, e2
) > 0)
1482 // Swap e2 with its left neighbor until it 'fits'.
1486 activeEdges
.set(j
, e1
);
1487 activeEdges
.set(j
- 1, e2
);
1490 e1
= (PolyEdge
) activeEdges
.get(j
- 1);
1491 } while (j
>= 1 && comparator
.compare(e1
, e2
) > 0);
1496 // Now draw all pixels inside the polygon.
1497 // This is the last edge that intersected the scanline.
1498 PolyEdge previous
= null; // Gets initialized below.
1499 boolean active
= false;
1500 //System.err.println("scanline: " + y);
1501 for (Iterator i
= activeEdges
.iterator(); i
.hasNext();)
1503 PolyEdge edge
= (PolyEdge
) i
.next();
1504 // Only fill scanline, if the current edge actually intersects
1505 // the scanline. There may be edges that lie completely
1506 // within the current scanline.
1507 //System.err.println("previous: " + previous);
1508 //System.err.println("edge: " + edge);
1513 int x0
= (int) previous
.xIntersection
;
1514 int x1
= (int) edge
.xIntersection
;
1515 fillScanline(pCtx
, x0
, x1
, y
);
1534 * Paints a scanline between x0 and x1.
1536 * @param x0 the left offset
1537 * @param x1 the right offset
1538 * @param y the scanline
1540 protected void fillScanline(PaintContext pCtx
, int x0
, int x1
, int y
)
1542 Raster paintRaster
= pCtx
.getRaster(x0
, y
, x1
- x0
, 1);
1543 ColorModel paintColorModel
= pCtx
.getColorModel();
1544 CompositeContext cCtx
= composite
.createContext(paintColorModel
,
1547 cCtx
.compose(paintRaster
, destinationRaster
, destinationRaster
);
1548 updateRaster(destinationRaster
, x0
, y
, x1
- x0
, 1);
1553 * Fills arbitrary shapes in an anti-aliased fashion.
1555 * @param segs the line segments which define the shape which is to be filled
1556 * @param minX the bounding box, left X
1557 * @param minY the bounding box, upper Y
1558 * @param maxX the bounding box, right X
1559 * @param maxY the bounding box, lower Y
1561 private void fillShapeAntialias(ArrayList segs
, double minX
, double minY
,
1562 double maxX
, double maxY
,
1563 Rectangle2D userBounds
)
1565 // This is an implementation of a polygon scanline conversion algorithm
1567 // http://www.cs.berkeley.edu/~ug/slide/pipeline/assignments/scan/
1568 // The antialiasing is implemented using a sampling technique, we do
1569 // not scan whole lines but fractions of the line.
1571 Rectangle deviceBounds
= new Rectangle((int) minX
, (int) minY
,
1572 (int) Math
.ceil(maxX
) - (int) minX
,
1573 (int) Math
.ceil(maxY
) - (int) minY
);
1574 PaintContext pCtx
= paint
.createContext(getColorModel(), deviceBounds
,
1575 userBounds
, transform
,
1578 // This array will contain the oversampled transparency values for
1579 // each pixel in the scanline.
1580 int numScanlines
= (int) Math
.ceil(maxY
) - (int) minY
;
1581 int numScanlinePixels
= (int) Math
.ceil(maxX
) - (int) minX
+ 1;
1582 if (alpha
== null || alpha
.length
< (numScanlinePixels
+ 1))
1583 alpha
= new int[numScanlinePixels
+ 1];
1585 int firstLine
= (int) minY
;
1586 //System.err.println("minY: " + minY);
1587 int firstSubline
= (int) (Math
.ceil((minY
- Math
.floor(minY
)) * AA_SAMPLING
));
1588 double firstLineDouble
= firstLine
+ firstSubline
/ (double) AA_SAMPLING
;
1589 //System.err.println("firstSubline: " + firstSubline);
1591 // Create table of all edges.
1592 // The edge buckets, sorted and indexed by their Y values.
1593 //System.err.println("numScanlines: " + numScanlines);
1594 if (edgeTable
== null
1595 || edgeTable
.length
< numScanlines
* AA_SAMPLING
+ AA_SAMPLING
)
1596 edgeTable
= new ArrayList
[numScanlines
* AA_SAMPLING
+ AA_SAMPLING
];
1598 //System.err.println("firstLineDouble: " + firstLineDouble);
1600 for (Iterator i
= segs
.iterator(); i
.hasNext();)
1602 PolyEdge edge
= (PolyEdge
) i
.next();
1603 int yindex
= (int) (Math
.ceil((edge
.y0
- firstLineDouble
) * AA_SAMPLING
));
1604 //System.err.println("yindex: " + yindex + " for y0: " + edge.y0);
1605 // Initialize edge's slope and initial xIntersection.
1606 edge
.slope
= ((edge
.x1
- edge
.x0
) / (edge
.y1
- edge
.y0
)) / AA_SAMPLING
;
1607 if (edge
.y0
== edge
.y1
) // Horizontal edge.
1608 edge
.xIntersection
= Math
.min(edge
.x0
, edge
.x1
);
1611 double alignedFirst
= Math
.ceil(edge
.y0
* AA_SAMPLING
) / AA_SAMPLING
;
1612 edge
.xIntersection
= edge
.x0
+ (edge
.slope
* AA_SAMPLING
) * (alignedFirst
- edge
.y0
);
1614 //System.err.println(edge);
1615 // FIXME: Sanity check should not be needed when clipping works.
1616 if (yindex
>= 0 && yindex
< edgeTable
.length
)
1618 if (edgeTable
[yindex
] == null) // Create bucket when needed.
1619 edgeTable
[yindex
] = new ArrayList();
1620 edgeTable
[yindex
].add(edge
); // Add edge to the bucket of its line.
1624 // The activeEdges list contains all the edges of the current scanline
1625 // ordered by their intersection points with this scanline.
1626 ArrayList activeEdges
= new ArrayList();
1627 PolyEdgeComparator comparator
= new PolyEdgeComparator();
1631 //System.err.println("firstLine: " + firstLine + ", maxY: " + maxY + ", firstSubline: " + firstSubline);
1632 for (int y
= firstLine
; y
<= maxY
; y
++)
1634 for (int subY
= firstSubline
; subY
< AA_SAMPLING
; subY
++)
1636 //System.err.println("scanline: " + y + ", subScanline: " + subY);
1637 ArrayList bucket
= edgeTable
[yindex
];
1638 // Update all the x intersections in the current activeEdges table
1639 // and remove entries that are no longer in the scanline.
1640 for (Iterator i
= activeEdges
.iterator(); i
.hasNext();)
1642 PolyEdge edge
= (PolyEdge
) i
.next();
1643 // TODO: Do the following using integer arithmetics.
1644 if ((y
+ ((double) subY
/ (double) AA_SAMPLING
)) > edge
.y1
)
1648 edge
.xIntersection
+= edge
.slope
;
1649 //System.err.println("edge: " + edge);
1650 //edge.xIntersection = edge.x0 + edge.slope * (y - edge.y0);
1651 //System.err.println("edge.xIntersection: " + edge.xIntersection);
1657 activeEdges
.addAll(bucket
);
1658 edgeTable
[yindex
].clear();
1661 // Sort current edges. We are using a bubble sort, because the order
1662 // of the intersections will not change in most situations. They
1663 // will only change, when edges intersect each other.
1664 int size
= activeEdges
.size();
1667 for (int i
= 1; i
< size
; i
++)
1669 PolyEdge e1
= (PolyEdge
) activeEdges
.get(i
- 1);
1670 PolyEdge e2
= (PolyEdge
) activeEdges
.get(i
);
1671 if (comparator
.compare(e1
, e2
) > 0)
1673 // Swap e2 with its left neighbor until it 'fits'.
1677 activeEdges
.set(j
, e1
);
1678 activeEdges
.set(j
- 1, e2
);
1681 e1
= (PolyEdge
) activeEdges
.get(j
- 1);
1682 } while (j
>= 1 && comparator
.compare(e1
, e2
) > 0);
1687 // Now draw all pixels inside the polygon.
1688 // This is the last edge that intersected the scanline.
1689 PolyEdge previous
= null; // Gets initialized below.
1690 boolean active
= false;
1691 //System.err.println("scanline: " + y + ", subscanline: " + subY);
1692 for (Iterator i
= activeEdges
.iterator(); i
.hasNext();)
1694 PolyEdge edge
= (PolyEdge
) i
.next();
1695 // Only fill scanline, if the current edge actually intersects
1696 // the scanline. There may be edges that lie completely
1697 // within the current scanline.
1698 //System.err.println("previous: " + previous);
1699 //System.err.println("edge: " + edge);
1702 // TODO: Use integer arithmetics here.
1703 if (edge
.y1
> (y
+ (subY
/ (double) AA_SAMPLING
)))
1705 //System.err.println(edge);
1706 // TODO: Eliminate the aligments.
1707 int x0
= (int) Math
.min(Math
.max(previous
.xIntersection
, minX
), maxX
);
1708 int x1
= (int) Math
.min(Math
.max(edge
.xIntersection
, minX
), maxX
);
1709 //System.err.println("minX: " + minX + ", x0: " + x0 + ", x1: " + x1 + ", maxX: " + maxX);
1710 // TODO: Pull out cast.
1711 alpha
[x0
- (int) minX
]++;
1712 alpha
[x1
- (int) minX
+ 1]--;
1719 // TODO: Use integer arithmetics here.
1720 if (edge
.y1
> (y
+ (subY
/ (double) AA_SAMPLING
)))
1722 //System.err.println(edge);
1731 // Render full scanline.
1732 //System.err.println("scanline: " + y);
1733 fillScanlineAA(alpha
, (int) minX
, (int) y
, numScanlinePixels
, pCtx
);
1735 if (paint
instanceof Color
&& composite
== AlphaComposite
.SrcOver
)
1736 rawSetForeground((Color
) paint
);
1742 * Fills a horizontal line between x0 and x1 for anti aliased rendering.
1743 * the alpha array contains the deltas of the alpha values from one pixel
1746 * @param alpha the alpha values in the scanline
1747 * @param x0 the beginning of the scanline
1748 * @param y the y coordinate of the line
1750 private void fillScanlineAA(int[] alpha
, int x0
, int y
, int numScanlinePixels
,
1753 // FIXME: This doesn't work. Fixit.
1754 CompositeContext cCtx
= composite
.createContext(pCtx
.getColorModel(),
1757 Raster paintRaster
= pCtx
.getRaster(x0
, y
, numScanlinePixels
, 1);
1758 System
.err
.println("paintColorModel: " + pCtx
.getColorModel());
1759 WritableRaster aaRaster
= paintRaster
.createCompatibleWritableRaster();
1760 int numBands
= paintRaster
.getNumBands();
1761 int[] pixels
= new int[numScanlinePixels
+ paintRaster
.getNumBands()];
1762 pixels
= paintRaster
.getPixels(x0
, y
, numScanlinePixels
, 1, pixels
);
1763 ColorModel cm
= pCtx
.getColorModel();
1765 double lastAlpha
= 0.;
1766 int lastAlphaInt
= 0;
1767 int[] components
= new int[4];
1769 for (int i
= 0; i
< pixels
.length
; i
++)
1773 lastAlphaInt
+= alpha
[i
];
1774 lastAlpha
= lastAlphaInt
/ AA_SAMPLING
;
1776 components
= cm
.getComponents(pixel
[i
], components
, 0);
1777 components
[0] = (int) (components
[0] * lastAlpha
);
1778 pixel
[i
] = cm
.getDataElement(components
, 0);
1781 aaRaster
.setPixels(0, 0, numScanlinePixels
, 1, pixels
);
1782 cCtx
.compose(aaRaster
, destinationRaster
, destinationRaster
);
1783 updateRaster(destinationRaster
, x0
, y
, numScanlinePixels
, 1);
1790 * Initializes this graphics object. This must be called by subclasses in
1791 * order to correctly initialize the state of this object.
1793 protected void init()
1795 setPaint(Color
.BLACK
);
1796 setFont(new Font("SansSerif", Font
.PLAIN
, 12));
1799 // FIXME: Should not be necessary. A clip of null should mean
1800 // 'clip against device bounds.
1801 clip
= getDeviceBounds();
1802 destinationRaster
= getDestinationRaster();
1806 * Returns a WritableRaster that is used by this class to perform the
1807 * rendering in. It is not necessary that the target surface immediately
1808 * reflects changes in the raster. Updates to the raster are notified via
1809 * {@link #updateRaster}.
1811 * @return the destination raster
1813 protected abstract WritableRaster
getDestinationRaster();
1816 * Notifies the backend that the raster has changed in the specified
1817 * rectangular area. The raster that is provided in this method is always
1818 * the same as the one returned in {@link #getDestinationRaster}.
1819 * Backends that reflect changes to this raster directly don't need to do
1822 * @param raster the updated raster, identical to the raster returned
1823 * by {@link #getDestinationRaster()}
1824 * @param x the upper left corner of the updated region, X coordinate
1825 * @param y the upper lef corner of the updated region, Y coordinate
1826 * @param w the width of the updated region
1827 * @param h the height of the updated region
1829 protected void updateRaster(Raster raster
, int x
, int y
, int w
, int h
)
1831 // Nothing to do here. Backends that need to update their surface
1832 // to reflect the change should override this method.
1835 // Some helper methods.
1838 * Helper method to check and update the optimization conditions.
1840 private void updateOptimization()
1842 int transformType
= transform
.getType();
1843 boolean optimizedTransform
= false;
1844 if (transformType
== AffineTransform
.TYPE_IDENTITY
1845 || transformType
== AffineTransform
.TYPE_TRANSLATION
)
1846 optimizedTransform
= true;
1848 boolean optimizedClip
= (clip
== null || clip
instanceof Rectangle
);
1849 isOptimized
= optimizedClip
1850 && optimizedTransform
&& paint
instanceof Color
1851 && composite
== AlphaComposite
.SrcOver
1852 && stroke
.equals(new BasicStroke());
1856 * Calculates the intersection of two rectangles. The result is stored
1857 * in <code>rect</code>. This is basically the same
1858 * like {@link Rectangle#intersection(Rectangle)}, only that it does not
1859 * create new Rectangle instances. The tradeoff is that you loose any data in
1860 * <code>rect</code>.
1862 * @param x upper-left x coodinate of first rectangle
1863 * @param y upper-left y coodinate of first rectangle
1864 * @param w width of first rectangle
1865 * @param h height of first rectangle
1866 * @param rect a Rectangle object of the second rectangle
1868 * @throws NullPointerException if rect is null
1870 * @return a rectangle corresponding to the intersection of the
1871 * two rectangles. An empty rectangle is returned if the rectangles
1874 private static Rectangle
computeIntersection(int x
, int y
, int w
, int h
,
1877 int x2
= (int) rect
.x
;
1878 int y2
= (int) rect
.y
;
1879 int w2
= (int) rect
.width
;
1880 int h2
= (int) rect
.height
;
1882 int dx
= (x
> x2
) ? x
: x2
;
1883 int dy
= (y
> y2
) ? y
: y2
;
1884 int dw
= (x
+ w
< x2
+ w2
) ?
(x
+ w
- dx
) : (x2
+ w2
- dx
);
1885 int dh
= (y
+ h
< y2
+ h2
) ?
(y
+ h
- dy
) : (y2
+ h2
- dy
);
1887 if (dw
>= 0 && dh
>= 0)
1888 rect
.setBounds(dx
, dy
, dw
, dh
);
1890 rect
.setBounds(0, 0, 0, 0);
1896 * Helper method to transform the clip. This is called by the various
1897 * transformation-manipulation methods to update the clip (which is in
1898 * userspace) accordingly.
1900 * The transform usually is the inverse transform that was applied to the
1903 * @param t the transform to apply to the clip
1905 private void updateClip(AffineTransform t
)
1907 if (! (clip
instanceof GeneralPath
))
1908 clip
= new GeneralPath(clip
);
1910 GeneralPath p
= (GeneralPath
) clip
;
1915 * Clips the specified shape using the current clip. If the resulting shape
1916 * is empty, this will return <code>null</code>.
1918 * @param s the shape to clip
1920 * @return the clipped shape or <code>null</code> if the result is empty
1922 private Shape
clipShape(Shape s
)
1924 Shape clipped
= null;
1926 // Clip the shape if necessary.
1930 if (! (s
instanceof Area
))
1936 if (! (clip
instanceof Area
))
1937 clipArea
= new Area(clip
);
1939 clipArea
= (Area
) clip
;
1941 a
.intersect(clipArea
);