Imported GNU Classpath 0.90
[official-gcc.git] / libjava / classpath / gnu / java / awt / java2d / AbstractGraphics2D.java
blobe93c43e08afa6b4fc9810eda183dcb55373a221e
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.BasicStroke;
43 import java.awt.Color;
44 import java.awt.Composite;
45 import java.awt.CompositeContext;
46 import java.awt.Font;
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;
84 import java.util.Map;
86 /**
87 * Implements general and shared behaviour for Graphics2D implementation.
89 * @author Roman Kennke (kennke@aicas.com)
91 public abstract class AbstractGraphics2D
92 extends Graphics2D
93 implements Cloneable
96 /**
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;
109 * The foreground.
111 private Paint paint;
114 * The background.
116 private Color background;
119 * The current font.
121 private Font font;
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.
136 private Shape clip;
139 * The rendering hints.
141 private RenderingHints renderingHints;
144 * The paint raster.
146 private Raster paintRaster;
149 * A cached pixel array.
151 private int[] pixel;
154 * The raster of the destination surface. This is where the painting is
155 * performed.
157 private WritableRaster destinationRaster;
160 * Stores the alpha values for a scanline in the anti-aliasing shape
161 * renderer.
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
174 * scaling.
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);
202 pixel = new int[4];
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)
213 // Stroke the shape.
214 Shape strokedShape = stroke.createStrokedShape(shape);
216 // Clip the stroked shape.
217 // Shape clipped = clipShape(strokedShape);
218 // if (clipped != null)
219 // {
220 // // Fill the shape.
221 // fillShape(clipped, false);
222 // }
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)
335 composite = comp;
336 if (! (comp.equals(AlphaComposite.SrcOver)))
337 isOptimized = false;
338 else
339 updateOptimization();
343 * Sets the current foreground.
345 * @param p the foreground to set.
347 public void setPaint(Paint p)
349 if (p != null)
351 paint = p;
353 if (! (paint instanceof Color))
354 isOptimized = false;
355 else
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)
370 stroke = s;
371 if (! stroke.equals(new BasicStroke()))
372 isOptimized = false;
373 else
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).
443 if (clip != null)
445 if (clip instanceof Rectangle)
447 Rectangle r = (Rectangle) clip;
448 r.x -= x;
449 r.y -= y;
450 setClip(r);
452 else
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).
473 if (clip != null)
475 if (clip instanceof Rectangle)
477 Rectangle r = (Rectangle) clip;
478 r.x -= tx;
479 r.y -= ty;
481 else
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);
498 if (clip != null)
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
509 * (x, y).
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);
518 if (clip != null)
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);
537 if (clip != null)
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);
556 if (clip != null)
558 AffineTransform clipTransform = new AffineTransform();
559 clipTransform.shear(-shearX, -shearY);
560 updateClip(clipTransform);
562 updateOptimization();
566 * Transforms the coordinate system using the specified transform
567 * <code>t</code>.
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()
627 return paint;
632 * Returns the current composite.
634 * @return the current composite
636 public Composite getComposite()
638 return composite;
642 * Sets the current background.
644 * @param color the background to set.
646 public void setBackground(Color color)
648 background = color;
652 * Returns the current background.
654 * @return the current background
656 public Color getBackground()
658 return background;
662 * Returns the current stroke.
664 * @return the current stroke
666 public Stroke getStroke()
668 return stroke;
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.
679 if (clip == null)
680 clip = s;
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.
689 setClip(clipRect);
691 else
693 Area current;
694 if (clip instanceof Area)
695 current = (Area) clip;
696 else
697 current = new Area(clip);
699 Area intersect;
700 if (s instanceof Area)
701 intersect = (Area) s;
702 else
703 intersect = new Area(s);
705 current.intersect(intersect);
706 clip = current;
707 isOptimized = false;
708 // Call setClip so that subclasses get notified.
709 setClip(clip);
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();
730 t.translate(x, y);
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));
738 p.transform(t);
739 //Shape clipped = clipShape(p);
740 //if (clipped != null)
741 // fillShape(clipped, true);
742 // FIXME: Clipping doesn't seem to work correctly.
743 fillShape(p, true);
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();
755 return copy;
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);
773 else
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.
779 return copy;
781 catch (CloneNotSupportedException ex)
783 AWTError err = new AWTError("Unexpected exception while cloning");
784 err.initCause(ex);
785 throw err;
790 * Returns the current foreground.
792 public Color getColor()
794 Color c = null;
795 if (paint instanceof Color)
796 c = (Color) paint;
797 return c;
801 * Sets the current foreground.
803 * @param color the foreground to set
805 public void setColor(Color color)
807 setPaint(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()
829 return font;
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)
840 if (f != null)
841 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()
863 Rectangle b = null;
864 if (clip != null)
865 b = clip.getBounds();
866 return b;
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()
902 return clip;
906 * Sets the current clipping area to <code>clip</code>.
908 * @param c the clip to set
910 public void setClip(Shape c)
912 clip = c;
913 if (! (clip instanceof Rectangle))
914 isOptimized = false;
915 else
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)
933 if (isOptimized)
935 int tx = (int) transform.getTranslateX();
936 int ty = (int) transform.getTranslateY();
937 rawDrawLine(x1 + tx, y1 + ty, x2 + tx, y2 + ty);
939 else
941 Line2D line = new Line2D.Double(x1, y1, x2, y2);
942 draw(line);
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)
956 if (isOptimized)
958 int tx = (int) transform.getTranslateX();
959 int ty = (int) transform.getTranslateY();
960 rawFillRect(x + tx, y + ty, width, height);
962 else
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,
999 int arcHeight)
1001 draw(new RoundRectangle2D.Double(x, y, width, height, arcWidth,
1002 arcHeight));
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,
1016 int arcHeight)
1018 fill(new RoundRectangle2D.Double(x, y, width, height, arcWidth,
1019 arcHeight));
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));
1036 * Fills an oval.
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));
1049 * Draws an arc.
1051 public void drawArc(int x, int y, int width, int height, int arcStart,
1052 int arcAngle)
1054 draw(new Arc2D.Double(x, y, width, height, arcStart, arcAngle,
1055 Arc2D.OPEN));
1059 * Fills an arc.
1061 public void fillArc(int x, int y, int width, int height, int arcStart,
1062 int arcAngle)
1064 fill(new Arc2D.Double(x, y, width, height, arcStart, arcAngle,
1065 Arc2D.OPEN));
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
1143 * current clip.
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;
1152 if (isFont)
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);
1160 else
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.
1180 double segY = 0.;
1181 double polyX = 0.; // The start point of the current polygon.
1182 double polyY = 0.;
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)
1201 segX = seg[0];
1202 segY = seg[1];
1203 polyX = seg[0];
1204 polyY = seg[1];
1206 else if (segType == PathIterator.SEG_CLOSE)
1208 // Close the polyline.
1209 PolyEdge edge = new PolyEdge(segX, segY, polyX, polyY);
1210 segs.add(edge);
1212 else if (segType == PathIterator.SEG_LINETO)
1214 PolyEdge edge = new PolyEdge(segX, segY, seg[0], seg[1]);
1215 segs.add(edge);
1216 segX = seg[0];
1217 segY = seg[1];
1219 path.next();
1221 if (segs.size() > 0)
1223 if (antialias)
1224 fillShapeAntialias(segs, minX, minY, maxX, maxY, userBounds);
1225 else
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);
1248 rawSetPixel(x, y);
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.
1319 int dy = y1 - y0;
1320 int dx = x1 - x0;
1321 int stepx, stepy;
1323 if (dy < 0)
1325 dy = -dy;
1326 stepy = -1;
1328 else
1330 stepy = 1;
1332 if (dx < 0)
1334 dx = -dx;
1335 stepx = -1;
1337 else
1339 stepx = 1;
1341 dy <<= 1;
1342 dx <<= 1;
1344 drawPixel(x0, y0);
1345 if (dx > dy)
1347 int fraction = dy - (dx >> 1); // same as 2*dy - dx
1348 while (x0 != x1)
1350 if (fraction >= 0)
1352 y0 += stepy;
1353 fraction -= dx;
1355 x0 += stepx;
1356 fraction += dy;
1357 drawPixel(x0, y0);
1360 else
1362 int fraction = dx - (dy >> 1);
1363 while (y0 != y1)
1365 if (fraction >= 0)
1367 x0 += stepx;
1368 fraction -= dy;
1370 y0 += stepy;
1371 fraction += dx;
1372 drawPixel(x0, y0);
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)
1388 int x2 = x + w;
1389 int y2 = y + h;
1390 for (int xc = x; xc < x2; xc++)
1392 for (int yc = y; yc < y2; yc++)
1394 drawPixel(xc, 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
1410 // described here:
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++)
1437 // {
1438 // if (edgeTable[y] != null)
1439 // Collections.sort(edgeTable[y]);
1440 // }
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();
1457 if (y > edge.y1)
1458 i.remove();
1459 else
1461 edge.xIntersection += edge.slope;
1462 //edge.xIntersection = edge.x0 + edge.slope * (y - edge.y0);
1463 //System.err.println("edge.xIntersection: " + edge.xIntersection);
1467 if (bucket != null)
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();
1474 if (size > 1)
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'.
1483 int j = i;
1486 activeEdges.set(j, e1);
1487 activeEdges.set(j - 1, e2);
1488 j--;
1489 if (j >= 1)
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);
1509 if (active)
1511 if (edge.y1 > y)
1513 int x0 = (int) previous.xIntersection;
1514 int x1 = (int) edge.xIntersection;
1515 fillScanline(pCtx, x0, x1, y);
1516 previous = edge;
1517 active = false;
1520 else
1522 if (edge.y1 > y)
1524 previous = edge;
1525 active = true;
1530 pCtx.dispose();
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,
1545 getColorModel(),
1546 renderingHints);
1547 cCtx.compose(paintRaster, destinationRaster, destinationRaster);
1548 updateRaster(destinationRaster, x0, y, x1 - x0, 1);
1549 cCtx.dispose();
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
1566 // described here:
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,
1576 renderingHints);
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);
1609 else
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();
1629 // Scan all lines.
1630 int yindex = 0;
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)
1645 i.remove();
1646 else
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);
1655 if (bucket != null)
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();
1665 if (size > 1)
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'.
1674 int j = i;
1677 activeEdges.set(j, e1);
1678 activeEdges.set(j - 1, e2);
1679 j--;
1680 if (j >= 1)
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);
1700 if (active)
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]--;
1713 previous = edge;
1714 active = false;
1717 else
1719 // TODO: Use integer arithmetics here.
1720 if (edge.y1 > (y + (subY / (double) AA_SAMPLING)))
1722 //System.err.println(edge);
1723 previous = edge;
1724 active = true;
1728 yindex++;
1730 firstSubline = 0;
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);
1738 pCtx.dispose();
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
1744 * to the next.
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,
1751 PaintContext pCtx)
1753 // FIXME: This doesn't work. Fixit.
1754 CompositeContext cCtx = composite.createContext(pCtx.getColorModel(),
1755 getColorModel(),
1756 renderingHints);
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++)
1771 if (alpha[i] != 0)
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);
1785 cCtx.dispose();
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));
1797 isOptimized = true;
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
1820 * anything here.
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
1872 * do not overlap
1874 private static Rectangle computeIntersection(int x, int y, int w, int h,
1875 Rectangle rect)
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);
1889 else
1890 rect.setBounds(0, 0, 0, 0);
1892 return rect;
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
1901 * graphics object.
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;
1911 p.transform(t);
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.
1927 if (clip != null)
1929 Area a;
1930 if (! (s instanceof Area))
1931 a = new Area(s);
1932 else
1933 a = (Area) s;
1935 Area clipArea;
1936 if (! (clip instanceof Area))
1937 clipArea = new Area(clip);
1938 else
1939 clipArea = (Area) clip;
1941 a.intersect(clipArea);
1942 if (! a.isEmpty())
1943 clipped = a;
1945 else
1947 clipped = s;
1949 return clipped;