OSDN Git Service

libjava/
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / java / awt / java2d / AbstractGraphics2D.java
1 /* AbstractGraphics2D.java -- Abstract Graphics2D implementation
2    Copyright (C) 2006 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
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.
10
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.
15
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.
20
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.
25
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. */
37
38 package gnu.java.awt.java2d;
39
40 import java.awt.AWTError;
41 import java.awt.AlphaComposite;
42 import java.awt.AWTPermission;
43 import java.awt.BasicStroke;
44 import java.awt.Color;
45 import java.awt.Composite;
46 import java.awt.CompositeContext;
47 import java.awt.Font;
48 import java.awt.FontMetrics;
49 import java.awt.Graphics;
50 import java.awt.Graphics2D;
51 import java.awt.Image;
52 import java.awt.Paint;
53 import java.awt.PaintContext;
54 import java.awt.Point;
55 import java.awt.Polygon;
56 import java.awt.Rectangle;
57 import java.awt.RenderingHints;
58 import java.awt.Shape;
59 import java.awt.Stroke;
60 import java.awt.Toolkit;
61 import java.awt.RenderingHints.Key;
62 import java.awt.font.FontRenderContext;
63 import java.awt.font.GlyphVector;
64 import java.awt.geom.AffineTransform;
65 import java.awt.geom.Arc2D;
66 import java.awt.geom.Area;
67 import java.awt.geom.Ellipse2D;
68 import java.awt.geom.GeneralPath;
69 import java.awt.geom.Line2D;
70 import java.awt.geom.NoninvertibleTransformException;
71 import java.awt.geom.RoundRectangle2D;
72 import java.awt.image.BufferedImage;
73 import java.awt.image.BufferedImageOp;
74 import java.awt.image.ColorModel;
75 import java.awt.image.DataBuffer;
76 import java.awt.image.ImageObserver;
77 import java.awt.image.Raster;
78 import java.awt.image.RenderedImage;
79 import java.awt.image.SampleModel;
80 import java.awt.image.WritableRaster;
81 import java.awt.image.renderable.RenderableImage;
82 import java.text.AttributedCharacterIterator;
83 import java.util.HashMap;
84 import java.util.Map;
85
86 /**
87  * This is a 100% Java implementation of the Java2D rendering pipeline. It is
88  * meant as a base class for Graphics2D implementations.
89  *
90  * <h2>Backend interface</h2>
91  * <p>
92  * The backend must at the very least provide a Raster which the the rendering
93  * pipeline can paint into. This must be implemented in
94  * {@link #getDestinationRaster()}. For some backends that might be enough, like
95  * when the target surface can be directly access via the raster (like in
96  * BufferedImages). Other targets need some way to synchronize the raster with
97  * the surface, which can be achieved by implementing the
98  * {@link #updateRaster(Raster, int, int, int, int)} method, which always gets
99  * called after a chunk of data got painted into the raster.
100  * </p>
101  * <p>Alternativly the backend can provide a method for filling Shapes by
102  * overriding the protected method fillShape(). This can be accomplished
103  * by a polygon filling function of the backend. Keep in mind though that
104  * Shapes can be quite complex (i.e. non-convex and containing holes, etc)
105  * which is not supported by all polygon fillers. Also it must be noted
106  * that fillShape() is expected to handle painting and compositing as well as
107  * clipping and transformation. If your backend can't support this natively,
108  * then you can fallback to the implementation in this class. You'll need
109  * to provide a writable Raster then, see above.</p>
110  * <p>Another alternative is to implement fillScanline() which only requires
111  * the backend to be able to draw horizontal lines in device space,
112  * which is usually very cheap.
113  * The implementation should still handle painting and compositing,
114  * but no more clipping and transformation is required by the backend.</p>
115  * <p>The backend is free to provide implementations for the various raw*
116  * methods for optimized AWT 1.1 style painting of some primitives. This should
117  * accelerate painting of Swing greatly. When doing so, the backend must also
118  * keep track of the clip and translation, probably by overriding
119  * some clip and translate methods. Don't forget to message super in such a
120  * case.</p>
121  *
122  * <h2>Acceleration options</h2>
123  * <p>
124  * The fact that it is
125  * pure Java makes it a little slow. However, there are several ways of
126  * accelerating the rendering pipeline:
127  * <ol>
128  * <li><em>Optimization hooks for AWT 1.1 - like graphics operations.</em>
129  *   The most important methods from the {@link java.awt.Graphics} class
130  *   have a corresponding <code>raw*</code> method, which get called when
131  *   several optimization conditions are fullfilled. These conditions are
132  *   described below. Subclasses can override these methods and delegate
133  *   it directly to a native backend.</li>
134  * <li><em>Native PaintContexts and CompositeContext.</em> The implementations
135  *   for the 3 PaintContexts and AlphaCompositeContext can be accelerated
136  *   using native code. These have proved to two of the most performance
137  *   critical points in the rendering pipeline and cannot really be done quickly
138  *   in plain Java because they involve lots of shuffling around with large
139  *   arrays. In fact, you really would want to let the graphics card to the
140  *   work, they are made for this.</li>
141  * <li>Provide an accelerated implementation for fillShape(). For instance,
142  * OpenGL can fill shapes very efficiently. There are some considerations
143  * to be made though, see above for details.</li>
144  * </ol>
145  * </p>
146  *
147  * @author Roman Kennke (kennke@aicas.com)
148  */
149 public abstract class AbstractGraphics2D
150   extends Graphics2D
151   implements Cloneable, Pixelizer
152 {
153
154   /**
155    * The default font to use on the graphics object.
156    */
157   private static final Font FONT = new Font("SansSerif", Font.PLAIN, 12);
158
159   /**
160    * Caches certain shapes to avoid massive creation of such Shapes in
161    * the various draw* and fill* methods.
162    */
163   private static final ThreadLocal<ShapeCache> shapeCache =
164     new ThreadLocal<ShapeCache>();
165
166   /**
167    * The scanline converters by thread.
168    */
169   private static final ThreadLocal<ScanlineConverter> scanlineConverters =
170     new ThreadLocal<ScanlineConverter>();
171
172   /**
173    * The transformation for this Graphics2D instance
174    */
175   protected AffineTransform transform;
176
177   /**
178    * The foreground.
179    */
180   private Paint paint;
181
182   /**
183    * The paint context during rendering.
184    */
185   private PaintContext paintContext;
186
187   /**
188    * The background.
189    */
190   private Color background;
191
192   /**
193    * The current font.
194    */
195   private Font font;
196
197   /**
198    * The current composite setting.
199    */
200   private Composite composite;
201
202   /**
203    * The current stroke setting.
204    */
205   private Stroke stroke;
206
207   /**
208    * The current clip. This clip is in user coordinate space.
209    */
210   private Shape clip;
211
212   /**
213    * The rendering hints.
214    */
215   private RenderingHints renderingHints;
216
217   /**
218    * The raster of the destination surface. This is where the painting is
219    * performed.
220    */
221   private WritableRaster destinationRaster;
222
223   /**
224    * Indicates if certain graphics primitives can be rendered in an optimized
225    * fashion. This will be the case if the following conditions are met:
226    * - The transform may only be a translation, no rotation, shearing or
227    *   scaling.
228    * - The paint must be a solid color.
229    * - The composite must be an AlphaComposite.SrcOver.
230    * - The clip must be a Rectangle.
231    * - The stroke must be a plain BasicStroke().
232    *
233    * These conditions represent the standard settings of a new
234    * AbstractGraphics2D object and will be the most commonly used setting
235    * in Swing rendering and should therefore be optimized as much as possible.
236    */
237   private boolean isOptimized = true;
238
239   private static final BasicStroke STANDARD_STROKE = new BasicStroke();
240
241   private static final HashMap STANDARD_HINTS;
242   static {
243     HashMap hints = new HashMap();
244   hints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
245             RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
246   hints.put(RenderingHints.KEY_ANTIALIASING,
247             RenderingHints.VALUE_ANTIALIAS_DEFAULT);
248   STANDARD_HINTS = hints;
249   }
250   /**
251    * Creates a new AbstractGraphics2D instance.
252    */
253   protected AbstractGraphics2D()
254   {
255     transform = new AffineTransform();
256     background = Color.WHITE;
257     composite = AlphaComposite.SrcOver;
258     stroke = STANDARD_STROKE;
259     renderingHints = new RenderingHints(STANDARD_HINTS);
260   }
261
262   /**
263    * Draws the specified shape. The shape is passed through the current stroke
264    * and is then forwarded to {@link #fillShape}.
265    *
266    * @param shape the shape to draw
267    */
268   public void draw(Shape shape)
269   {
270     // Stroke the shape.
271     Shape strokedShape = stroke.createStrokedShape(shape);
272     // Fill the stroked shape.
273     fillShape(strokedShape, false);
274   }
275
276
277   /**
278    * Draws the specified image and apply the transform for image space ->
279    * user space conversion.
280    *
281    * This method is implemented to special case RenderableImages and
282    * RenderedImages and delegate to
283    * {@link #drawRenderableImage(RenderableImage, AffineTransform)} and
284    * {@link #drawRenderedImage(RenderedImage, AffineTransform)} accordingly.
285    * Other image types are not yet handled.
286    *
287    * @param image the image to be rendered
288    * @param xform the transform from image space to user space
289    * @param obs the image observer to be notified
290    */
291   public boolean drawImage(Image image, AffineTransform xform,
292                            ImageObserver obs)
293   {
294     Rectangle areaOfInterest = new Rectangle(0, 0, image.getWidth(obs),
295                                              image.getHeight(obs));
296     return drawImageImpl(image, xform, obs, areaOfInterest);
297   }
298
299   /**
300    * Draws the specified image and apply the transform for image space ->
301    * user space conversion. This method only draw the part of the image
302    * specified by <code>areaOfInterest</code>.
303    *
304    * This method is implemented to special case RenderableImages and
305    * RenderedImages and delegate to
306    * {@link #drawRenderableImage(RenderableImage, AffineTransform)} and
307    * {@link #drawRenderedImage(RenderedImage, AffineTransform)} accordingly.
308    * Other image types are not yet handled.
309    *
310    * @param image the image to be rendered
311    * @param xform the transform from image space to user space
312    * @param obs the image observer to be notified
313    * @param areaOfInterest the area in image space that is rendered
314    */
315   private boolean drawImageImpl(Image image, AffineTransform xform,
316                              ImageObserver obs, Rectangle areaOfInterest)
317   {
318     boolean ret;
319     if (image == null)
320       {
321         ret = true;
322       }
323     else if (image instanceof RenderedImage)
324       {
325         // FIXME: Handle the ImageObserver.
326         drawRenderedImageImpl((RenderedImage) image, xform, areaOfInterest);
327         ret = true;
328       }
329     else if (image instanceof RenderableImage)
330       {
331         // FIXME: Handle the ImageObserver.
332         drawRenderableImageImpl((RenderableImage) image, xform, areaOfInterest);
333         ret = true;
334       }
335     else
336       {
337         // FIXME: Implement rendering of other Image types.
338         ret = false;
339       }
340     return ret;
341   }
342
343   /**
344    * Renders a BufferedImage and applies the specified BufferedImageOp before
345    * to filter the BufferedImage somehow. The resulting BufferedImage is then
346    * passed on to {@link #drawRenderedImage(RenderedImage, AffineTransform)}
347    * to perform the final rendering.
348    *
349    * @param image the source buffered image
350    * @param op the filter to apply to the buffered image before rendering
351    * @param x the x coordinate to render the image to 
352    * @param y the y coordinate to render the image to 
353    */
354   public void drawImage(BufferedImage image, BufferedImageOp op, int x, int y)
355   {
356     BufferedImage filtered =
357       op.createCompatibleDestImage(image, image.getColorModel());
358     AffineTransform t = new AffineTransform();
359     t.translate(x, y);
360     drawRenderedImage(filtered, t);
361   }
362
363   /**
364    * Renders the specified image to the destination raster. The specified
365    * transform is used to convert the image into user space. The transform
366    * of this AbstractGraphics2D object is used to transform from user space
367    * to device space.
368    * 
369    * The rendering is performed using the scanline algorithm that performs the
370    * rendering of other shapes and a custom Paint implementation, that supplies
371    * the pixel values of the rendered image.
372    *
373    * @param image the image to render to the destination raster
374    * @param xform the transform from image space to user space
375    */
376   public void drawRenderedImage(RenderedImage image, AffineTransform xform)
377   {
378     Rectangle areaOfInterest = new Rectangle(image.getMinX(),
379                                              image.getHeight(),
380                                              image.getWidth(),
381                                              image.getHeight());
382     drawRenderedImageImpl(image, xform, areaOfInterest);
383   }
384
385   /**
386    * Renders the specified image to the destination raster. The specified
387    * transform is used to convert the image into user space. The transform
388    * of this AbstractGraphics2D object is used to transform from user space
389    * to device space. Only the area specified by <code>areaOfInterest</code>
390    * is finally rendered to the target.
391    * 
392    * The rendering is performed using the scanline algorithm that performs the
393    * rendering of other shapes and a custom Paint implementation, that supplies
394    * the pixel values of the rendered image.
395    *
396    * @param image the image to render to the destination raster
397    * @param xform the transform from image space to user space
398    */
399   private void drawRenderedImageImpl(RenderedImage image,
400                                      AffineTransform xform,
401                                      Rectangle areaOfInterest)
402   {
403     // First we compute the transformation. This is made up of 3 parts:
404     // 1. The areaOfInterest -> image space transform.
405     // 2. The image space -> user space transform.
406     // 3. The user space -> device space transform.
407     AffineTransform t = new AffineTransform();
408     t.translate(- areaOfInterest.x - image.getMinX(),
409                 - areaOfInterest.y - image.getMinY());
410     t.concatenate(xform);
411     t.concatenate(transform);
412     AffineTransform it = null;
413     try
414       {
415         it = t.createInverse();
416       }
417     catch (NoninvertibleTransformException ex)
418       {
419         // Ignore -- we return if the transform is not invertible.
420       }
421     if (it != null)
422       {
423         // Transform the area of interest into user space.
424         GeneralPath aoi = new GeneralPath(areaOfInterest);
425         aoi.transform(xform);
426         // Render the shape using the standard renderer, but with a temporary
427         // ImagePaint.
428         ImagePaint p = new ImagePaint(image, it);
429         Paint savedPaint = paint;
430         try
431           {
432             paint = p;
433             fillShape(aoi, false);
434           }
435         finally
436           {
437             paint = savedPaint;
438           }
439       }
440   }
441
442   /**
443    * Renders a renderable image. This produces a RenderedImage, which is
444    * then passed to {@link #drawRenderedImage(RenderedImage, AffineTransform)}
445    * to perform the final rendering.
446    *
447    * @param image the renderable image to be rendered
448    * @param xform the transform from image space to user space
449    */
450   public void drawRenderableImage(RenderableImage image, AffineTransform xform)
451   {
452     Rectangle areaOfInterest = new Rectangle((int) image.getMinX(),
453                                              (int) image.getHeight(),
454                                              (int) image.getWidth(),
455                                              (int) image.getHeight());
456     drawRenderableImageImpl(image, xform, areaOfInterest);
457                                                        
458   }
459
460   /**
461    * Renders a renderable image. This produces a RenderedImage, which is
462    * then passed to {@link #drawRenderedImage(RenderedImage, AffineTransform)}
463    * to perform the final rendering. Only the area of the image specified
464    * by <code>areaOfInterest</code> is rendered.
465    *
466    * @param image the renderable image to be rendered
467    * @param xform the transform from image space to user space
468    */
469   private void drawRenderableImageImpl(RenderableImage image,
470                                        AffineTransform xform,
471                                        Rectangle areaOfInterest)
472   {
473     // TODO: Maybe make more clever usage of a RenderContext here.
474     RenderedImage rendered = image.createDefaultRendering();
475     drawRenderedImageImpl(rendered, xform, areaOfInterest);
476   }
477
478   /**
479    * Draws the specified string at the specified location.
480    *
481    * @param text the string to draw
482    * @param x the x location, relative to the bounding rectangle of the text
483    * @param y the y location, relative to the bounding rectangle of the text
484    */
485   public void drawString(String text, int x, int y)
486   {
487     if (isOptimized)
488       rawDrawString(text, x, y);
489     else
490       {
491         FontRenderContext ctx = getFontRenderContext();
492         GlyphVector gv = font.createGlyphVector(ctx, text.toCharArray());
493         drawGlyphVector(gv, x, y);
494       }
495   }
496
497   /**
498    * Draws the specified string at the specified location.
499    *
500    * @param text the string to draw
501    * @param x the x location, relative to the bounding rectangle of the text
502    * @param y the y location, relative to the bounding rectangle of the text
503    */
504   public void drawString(String text, float x, float y)
505   {
506     FontRenderContext ctx = getFontRenderContext();
507     GlyphVector gv = font.createGlyphVector(ctx, text.toCharArray());
508     drawGlyphVector(gv, x, y);
509   }
510
511   /**
512    * Draws the specified string (as AttributedCharacterIterator) at the
513    * specified location.
514    *
515    * @param iterator the string to draw
516    * @param x the x location, relative to the bounding rectangle of the text
517    * @param y the y location, relative to the bounding rectangle of the text
518    */
519   public void drawString(AttributedCharacterIterator iterator, int x, int y)
520   {
521     FontRenderContext ctx = getFontRenderContext();
522     GlyphVector gv = font.createGlyphVector(ctx, iterator);
523     drawGlyphVector(gv, x, y);
524   }
525
526   /**
527    * Draws the specified string (as AttributedCharacterIterator) at the
528    * specified location.
529    *
530    * @param iterator the string to draw
531    * @param x the x location, relative to the bounding rectangle of the text
532    * @param y the y location, relative to the bounding rectangle of the text
533    */
534   public void drawString(AttributedCharacterIterator iterator, float x, float y)
535   {
536     FontRenderContext ctx = getFontRenderContext();
537     GlyphVector gv = font.createGlyphVector(ctx, iterator);
538     drawGlyphVector(gv, x, y);
539   }
540
541   /**
542    * Fills the specified shape with the current foreground.
543    *
544    * @param shape the shape to fill
545    */
546   public void fill(Shape shape)
547   {
548     fillShape(shape, false);
549   }
550
551   public boolean hit(Rectangle rect, Shape text, boolean onStroke)
552   {
553     // FIXME: Implement this.
554     throw new UnsupportedOperationException("Not yet implemented");
555   }
556
557   /**
558    * Sets the composite.
559    *
560    * @param comp the composite to set
561    */
562   public void setComposite(Composite comp)
563   {
564     if (! (comp instanceof AlphaComposite))
565       {
566         // FIXME: this check is only required "if this Graphics2D
567         // context is drawing to a Component on the display screen".
568         SecurityManager sm = System.getSecurityManager();
569         if (sm != null)
570           sm.checkPermission(new AWTPermission("readDisplayPixels"));
571       }
572
573     composite = comp;
574     if (! (comp.equals(AlphaComposite.SrcOver)))
575       isOptimized = false;
576     else
577       updateOptimization();
578   }
579
580   /**
581    * Sets the current foreground.
582    *
583    * @param p the foreground to set.
584    */
585   public void setPaint(Paint p)
586   {
587     if (p != null)
588       {
589         paint = p;
590
591         if (! (paint instanceof Color))
592           isOptimized = false;
593         else
594           {
595             updateOptimization();
596           }
597       }
598   }
599
600   /**
601    * Sets the stroke for this graphics object.
602    *
603    * @param s the stroke to set
604    */
605   public void setStroke(Stroke s)
606   {
607     stroke = s;
608     if (! stroke.equals(new BasicStroke()))
609       isOptimized = false;
610     else
611       updateOptimization();
612   }
613
614   /**
615    * Sets the specified rendering hint.
616    *
617    * @param hintKey the key of the rendering hint
618    * @param hintValue the value
619    */
620   public void setRenderingHint(Key hintKey, Object hintValue)
621   {
622     renderingHints.put(hintKey, hintValue);
623   }
624
625   /**
626    * Returns the rendering hint for the specified key.
627    *
628    * @param hintKey the rendering hint key
629    *
630    * @return the rendering hint for the specified key
631    */
632   public Object getRenderingHint(Key hintKey)
633   {
634     return renderingHints.get(hintKey);
635   }
636
637   /**
638    * Sets the specified rendering hints.
639    *
640    * @param hints the rendering hints to set
641    */
642   public void setRenderingHints(Map hints)
643   {
644     renderingHints.clear();
645     renderingHints.putAll(hints);
646   }
647
648   /**
649    * Adds the specified rendering hints.
650    *
651    * @param hints the rendering hints to add
652    */
653   public void addRenderingHints(Map hints)
654   {
655     renderingHints.putAll(hints);
656   }
657
658   /**
659    * Returns the current rendering hints.
660    *
661    * @return the current rendering hints
662    */
663   public RenderingHints getRenderingHints()
664   {
665     return (RenderingHints) renderingHints.clone();
666   }
667
668   /**
669    * Translates the coordinate system by (x, y).
670    *
671    * @param x the translation X coordinate
672    * @param y the translation Y coordinate 
673    */
674   public void translate(int x, int y)
675   {
676     transform.translate(x, y);
677
678     // Update the clip. We special-case rectangular clips here, because they
679     // are so common (e.g. in Swing).
680     if (clip != null)
681       {
682         if (clip instanceof Rectangle)
683           {
684             Rectangle r = (Rectangle) clip;
685             r.x -= x;
686             r.y -= y;
687             setClip(r);
688           }
689         else
690           {
691             AffineTransform clipTransform = new AffineTransform();
692             clipTransform.translate(-x, -y);
693             updateClip(clipTransform);
694           }
695       }
696   }
697
698   /**
699    * Translates the coordinate system by (tx, ty).
700    *
701    * @param tx the translation X coordinate
702    * @param ty the translation Y coordinate 
703    */
704   public void translate(double tx, double ty)
705   {
706     transform.translate(tx, ty);
707
708     // Update the clip. We special-case rectangular clips here, because they
709     // are so common (e.g. in Swing).
710     if (clip != null)
711       {
712         if (clip instanceof Rectangle)
713           {
714             Rectangle r = (Rectangle) clip;
715             r.x -= tx;
716             r.y -= ty;
717           }
718         else
719           {
720             AffineTransform clipTransform = new AffineTransform();
721             clipTransform.translate(-tx, -ty);
722             updateClip(clipTransform);
723           }
724       }
725   }
726
727   /**
728    * Rotates the coordinate system by <code>theta</code> degrees.
729    *
730    * @param theta the angle be which to rotate the coordinate system
731    */
732   public void rotate(double theta)
733   {
734     transform.rotate(theta);
735     if (clip != null)
736       {
737         AffineTransform clipTransform = new AffineTransform();
738         clipTransform.rotate(-theta);
739         updateClip(clipTransform);
740       }
741     updateOptimization();
742   }
743
744   /**
745    * Rotates the coordinate system by <code>theta</code> around the point
746    * (x, y).
747    *
748    * @param theta the angle by which to rotate the coordinate system
749    * @param x the point around which to rotate, X coordinate
750    * @param y the point around which to rotate, Y coordinate
751    */
752   public void rotate(double theta, double x, double y)
753   {
754     transform.rotate(theta, x, y);
755     if (clip != null)
756       {
757         AffineTransform clipTransform = new AffineTransform();
758         clipTransform.rotate(-theta, x, y);
759         updateClip(clipTransform);
760       }
761     updateOptimization();
762   }
763
764   /**
765    * Scales the coordinate system by the factors <code>scaleX</code> and
766    * <code>scaleY</code>.
767    *
768    * @param scaleX the factor by which to scale the X axis
769    * @param scaleY the factor by which to scale the Y axis
770    */
771   public void scale(double scaleX, double scaleY)
772   {
773     transform.scale(scaleX, scaleY);
774     if (clip != null)
775       {
776         AffineTransform clipTransform = new AffineTransform();
777         clipTransform.scale(1 / scaleX, 1 / scaleY);
778         updateClip(clipTransform);
779       }
780     updateOptimization();
781   }
782
783   /**
784    * Shears the coordinate system by <code>shearX</code> and
785    * <code>shearY</code>.
786    *
787    * @param shearX the X shearing
788    * @param shearY the Y shearing
789    */
790   public void shear(double shearX, double shearY)
791   {
792     transform.shear(shearX, shearY);
793     if (clip != null)
794       {
795         AffineTransform clipTransform = new AffineTransform();
796         clipTransform.shear(-shearX, -shearY);
797         updateClip(clipTransform);
798       }
799     updateOptimization();
800   }
801
802   /**
803    * Transforms the coordinate system using the specified transform
804    * <code>t</code>.
805    *
806    * @param t the transform
807    */
808   public void transform(AffineTransform t)
809   {
810     transform.concatenate(t);
811     try
812       {
813         AffineTransform clipTransform = t.createInverse();
814         updateClip(clipTransform);
815       }
816     catch (NoninvertibleTransformException ex)
817       {
818         // TODO: How can we deal properly with this?
819         ex.printStackTrace();
820       }
821     updateOptimization();
822   }
823
824   /**
825    * Sets the transformation for this Graphics object.
826    *
827    * @param t the transformation to set
828    */
829   public void setTransform(AffineTransform t)
830   {
831     // Transform clip into target space using the old transform.
832     updateClip(transform);
833     transform.setTransform(t);
834     // Transform the clip back into user space using the inverse new transform.
835     try
836       {
837         updateClip(transform.createInverse());
838       }
839     catch (NoninvertibleTransformException ex)
840       {
841         // TODO: How can we deal properly with this?
842         ex.printStackTrace();
843       }
844     updateOptimization();
845   }
846
847   /**
848    * Returns the transformation of this coordinate system.
849    *
850    * @return the transformation of this coordinate system
851    */
852   public AffineTransform getTransform()
853   {
854     return (AffineTransform) transform.clone();
855   }
856
857   /**
858    * Returns the current foreground.
859    *
860    * @return the current foreground
861    */
862   public Paint getPaint()
863   {
864     return paint;
865   }
866
867
868   /**
869    * Returns the current composite.
870    *
871    * @return the current composite
872    */
873   public Composite getComposite()
874   {
875     return composite;
876   }
877
878   /**
879    * Sets the current background.
880    *
881    * @param color the background to set.
882    */
883   public void setBackground(Color color)
884   {
885     background = color;
886   }
887
888   /**
889    * Returns the current background.
890    *
891    * @return the current background
892    */
893   public Color getBackground()
894   {
895     return background;
896   }
897
898   /**
899    * Returns the current stroke.
900    *
901    * @return the current stroke
902    */
903   public Stroke getStroke()
904   {
905     return stroke;
906   }
907
908   /**
909    * Intersects the clip of this graphics object with the specified clip.
910    *
911    * @param s the clip with which the current clip should be intersected
912    */
913   public void clip(Shape s)
914   {
915     // Initialize clip if not already present.
916     if (clip == null)
917       setClip(s);
918
919     // This is so common, let's optimize this. 
920     else if (clip instanceof Rectangle && s instanceof Rectangle)
921       {
922         Rectangle clipRect = (Rectangle) clip;
923         Rectangle r = (Rectangle) s;
924         computeIntersection(r.x, r.y, r.width, r.height, clipRect);
925         // Call setClip so that subclasses get notified.
926         setClip(clipRect);
927       }
928    else
929      {
930        Area current;
931        if (clip instanceof Area)
932          current = (Area) clip;
933        else
934          current = new Area(clip);
935
936        Area intersect;
937        if (s instanceof Area)
938          intersect = (Area) s;
939        else
940          intersect = new Area(s);
941
942        current.intersect(intersect);
943        clip = current;
944        isOptimized = false;
945        // Call setClip so that subclasses get notified.
946        setClip(clip);
947      }
948   }
949
950   public FontRenderContext getFontRenderContext()
951   {
952     return new FontRenderContext(transform, false, true);
953   }
954
955   /**
956    * Draws the specified glyph vector at the specified location.
957    *
958    * @param gv the glyph vector to draw
959    * @param x the location, x coordinate
960    * @param y the location, y coordinate
961    */
962   public void drawGlyphVector(GlyphVector gv, float x, float y)
963   {
964     translate(x, y);
965     fillShape(gv.getOutline(), true);
966     translate(-x, -y);
967   }
968
969   /**
970    * Creates a copy of this graphics object.
971    *
972    * @return a copy of this graphics object
973    */
974   public Graphics create()
975   {
976     AbstractGraphics2D copy = (AbstractGraphics2D) clone();
977     return copy;
978   }
979
980   /**
981    * Creates and returns a copy of this Graphics object. This should
982    * be overridden by subclasses if additional state must be handled when
983    * cloning. This is called by {@link #create()}.
984    *
985    * @return a copy of this Graphics object
986    */
987   protected Object clone()
988   {
989     try
990       {
991         AbstractGraphics2D copy = (AbstractGraphics2D) super.clone();
992         // Copy the clip. If it's a Rectangle, preserve that for optimization.
993         if (clip instanceof Rectangle)
994           copy.clip = new Rectangle((Rectangle) clip);
995         else
996           copy.clip = new GeneralPath(clip);
997
998         copy.renderingHints = new RenderingHints(null);
999         copy.renderingHints.putAll(renderingHints);
1000         copy.transform = new AffineTransform(transform);
1001         // The remaining state is inmmutable and doesn't need to be copied.
1002         return copy;
1003       }
1004     catch (CloneNotSupportedException ex)
1005       {
1006         AWTError err = new AWTError("Unexpected exception while cloning");
1007         err.initCause(ex);
1008         throw err;
1009       }
1010   }
1011
1012   /**
1013    * Returns the current foreground.
1014    */
1015   public Color getColor()
1016   {
1017     Color c = null;
1018     if (paint instanceof Color)
1019       c = (Color) paint;
1020     return c;
1021   }
1022
1023   /**
1024    * Sets the current foreground.
1025    *
1026    * @param color the foreground to set
1027    */
1028   public void setColor(Color color)
1029   {
1030     setPaint(color);
1031   }
1032
1033   public void setPaintMode()
1034   {
1035     // FIXME: Implement this.
1036     throw new UnsupportedOperationException("Not yet implemented");
1037   }
1038
1039   public void setXORMode(Color color)
1040   {
1041     // FIXME: Implement this.
1042     throw new UnsupportedOperationException("Not yet implemented");
1043   }
1044
1045   /**
1046    * Returns the current font.
1047    *
1048    * @return the current font
1049    */
1050   public Font getFont()
1051   {
1052     return font;
1053   }
1054
1055   /**
1056    * Sets the font on this graphics object. When <code>f == null</code>, the
1057    * current setting is not changed.
1058    *
1059    * @param f the font to set
1060    */
1061   public void setFont(Font f)
1062   {
1063     if (f != null)
1064       font = f;
1065   }
1066
1067   /**
1068    * Returns the font metrics for the specified font.
1069    *
1070    * @param font the font for which to fetch the font metrics
1071    *
1072    * @return the font metrics for the specified font
1073    */
1074   public FontMetrics getFontMetrics(Font font)
1075   {
1076     return Toolkit.getDefaultToolkit().getFontMetrics(font);
1077   }
1078
1079   /**
1080    * Returns the bounds of the current clip.
1081    *
1082    * @return the bounds of the current clip
1083    */
1084   public Rectangle getClipBounds()
1085   {
1086     Rectangle b = null;
1087     if (clip != null)
1088       b = clip.getBounds();
1089     return b;
1090   }
1091
1092   /**
1093    * Intersects the current clipping region with the specified rectangle.
1094    *
1095    * @param x the x coordinate of the rectangle
1096    * @param y the y coordinate of the rectangle
1097    * @param width the width of the rectangle
1098    * @param height the height of the rectangle
1099    */
1100   public void clipRect(int x, int y, int width, int height)
1101   {
1102     clip(new Rectangle(x, y, width, height));
1103   }
1104
1105   /**
1106    * Sets the clip to the specified rectangle.
1107    *
1108    * @param x the x coordinate of the clip rectangle
1109    * @param y the y coordinate of the clip rectangle
1110    * @param width the width of the clip rectangle
1111    * @param height the height of the clip rectangle
1112    */
1113   public void setClip(int x, int y, int width, int height)
1114   {
1115     setClip(new Rectangle(x, y, width, height));
1116   }
1117
1118   /**
1119    * Returns the current clip.
1120    *
1121    * @return the current clip
1122    */
1123   public Shape getClip()
1124   {
1125     return clip;
1126   }
1127
1128   /**
1129    * Sets the current clipping area to <code>clip</code>.
1130    *
1131    * @param c the clip to set
1132    */
1133   public void setClip(Shape c)
1134   {
1135     clip = c;
1136     if (! (clip instanceof Rectangle))
1137       isOptimized = false;
1138     else
1139       updateOptimization();
1140   }
1141
1142   public void copyArea(int x, int y, int width, int height, int dx, int dy)
1143   {
1144     if (isOptimized)
1145       rawCopyArea(x, y, width, height, dx, dy);
1146     else
1147       copyAreaImpl(x, y, width, height, dx, dy);
1148   }
1149
1150   /**
1151    * Draws a line from (x1, y1) to (x2, y2).
1152    *
1153    * This implementation transforms the coordinates and forwards the call to
1154    * {@link #rawDrawLine}.
1155    */
1156   public void drawLine(int x1, int y1, int x2, int y2)
1157   {
1158     if (isOptimized)
1159       {
1160         int tx = (int) transform.getTranslateX();
1161         int ty = (int) transform.getTranslateY();
1162         rawDrawLine(x1 + tx, y1 + ty, x2 + tx, y2 + ty);
1163       }
1164     else
1165       {
1166         ShapeCache sc = getShapeCache();
1167         if (sc.line == null)
1168           sc.line = new Line2D.Float();
1169         sc.line.setLine(x1, y1, x2, y2);
1170         draw(sc.line);
1171       }
1172   }
1173
1174   public void drawRect(int x, int y, int w, int h)
1175   {
1176     if (isOptimized)
1177       {
1178         rawDrawRect(x, y, w, h);
1179       }
1180     else
1181       {
1182         ShapeCache sc = getShapeCache();
1183         if (sc.rect == null)
1184           sc.rect = new Rectangle();
1185         sc.rect.setBounds(x, y, w, h);
1186         draw(sc.rect);
1187       }
1188   }
1189
1190   /**
1191    * Fills a rectangle with the current paint.
1192    *
1193    * @param x the upper left corner, X coordinate
1194    * @param y the upper left corner, Y coordinate
1195    * @param width the width of the rectangle
1196    * @param height the height of the rectangle
1197    */
1198   public void fillRect(int x, int y, int width, int height)
1199   {
1200     if (isOptimized)
1201       {
1202         rawFillRect(x + (int) transform.getTranslateX(),
1203                     y + (int) transform.getTranslateY(), width, height);
1204       }
1205     else
1206       {
1207         ShapeCache sc = getShapeCache();
1208         if (sc.rect == null)
1209           sc.rect = new Rectangle();
1210         sc.rect.setBounds(x, y, width, height);
1211         fill(sc.rect);
1212       }
1213   }
1214
1215   /**
1216    * Fills a rectangle with the current background color.
1217    *
1218    * This implementation temporarily sets the foreground color to the 
1219    * background and forwards the call to {@link #fillRect(int, int, int, int)}.
1220    *
1221    * @param x the upper left corner, X coordinate
1222    * @param y the upper left corner, Y coordinate
1223    * @param width the width of the rectangle
1224    * @param height the height of the rectangle
1225    */
1226   public void clearRect(int x, int y, int width, int height)
1227   {
1228     if (isOptimized)
1229       rawClearRect(x, y, width, height);
1230     else
1231       {
1232         Paint savedForeground = getPaint();
1233         setPaint(getBackground());
1234         fillRect(x, y, width, height);
1235         setPaint(savedForeground);
1236       }
1237   }
1238
1239   /**
1240    * Draws a rounded rectangle.
1241    *
1242    * @param x the x coordinate of the rectangle
1243    * @param y the y coordinate of the rectangle
1244    * @param width the width of the rectangle
1245    * @param height the height of the rectangle
1246    * @param arcWidth the width of the arcs
1247    * @param arcHeight the height of the arcs
1248    */
1249   public void drawRoundRect(int x, int y, int width, int height, int arcWidth,
1250                             int arcHeight)
1251   {
1252     ShapeCache sc = getShapeCache();
1253     if (sc.roundRect == null)
1254       sc.roundRect = new RoundRectangle2D.Float();
1255     sc.roundRect.setRoundRect(x, y, width, height, arcWidth, arcHeight);
1256     draw(sc.roundRect);
1257   }
1258
1259   /**
1260    * Fills a rounded rectangle.
1261    *
1262    * @param x the x coordinate of the rectangle
1263    * @param y the y coordinate of the rectangle
1264    * @param width the width of the rectangle
1265    * @param height the height of the rectangle
1266    * @param arcWidth the width of the arcs
1267    * @param arcHeight the height of the arcs
1268    */
1269   public void fillRoundRect(int x, int y, int width, int height, int arcWidth,
1270                             int arcHeight)
1271   {
1272     ShapeCache sc = getShapeCache();
1273     if (sc.roundRect == null)
1274       sc.roundRect = new RoundRectangle2D.Float();
1275     sc.roundRect.setRoundRect(x, y, width, height, arcWidth, arcHeight);
1276     fill(sc.roundRect);
1277   }
1278
1279   /**
1280    * Draws the outline of an oval.
1281    *
1282    * @param x the upper left corner of the bounding rectangle of the ellipse
1283    * @param y the upper left corner of the bounding rectangle of the ellipse
1284    * @param width the width of the ellipse
1285    * @param height the height of the ellipse
1286    */
1287   public void drawOval(int x, int y, int width, int height)
1288   {
1289     ShapeCache sc = getShapeCache();
1290     if (sc.ellipse == null)
1291       sc.ellipse = new Ellipse2D.Float();
1292     sc.ellipse.setFrame(x, y, width, height);
1293     draw(sc.ellipse);
1294   }
1295
1296   /**
1297    * Fills an oval.
1298    *
1299    * @param x the upper left corner of the bounding rectangle of the ellipse
1300    * @param y the upper left corner of the bounding rectangle of the ellipse
1301    * @param width the width of the ellipse
1302    * @param height the height of the ellipse
1303    */
1304   public void fillOval(int x, int y, int width, int height)
1305   {
1306     ShapeCache sc = getShapeCache();
1307     if (sc.ellipse == null)
1308       sc.ellipse = new Ellipse2D.Float();
1309     sc.ellipse.setFrame(x, y, width, height);
1310     fill(sc.ellipse);
1311   }
1312
1313   /**
1314    * Draws an arc.
1315    */
1316   public void drawArc(int x, int y, int width, int height, int arcStart,
1317                       int arcAngle)
1318   {
1319     ShapeCache sc = getShapeCache();
1320     if (sc.arc == null)
1321       sc.arc = new Arc2D.Float();
1322     sc.arc.setArc(x, y, width, height, arcStart, arcAngle, Arc2D.OPEN);
1323     draw(sc.arc);
1324   }
1325
1326   /**
1327    * Fills an arc.
1328    */
1329   public void fillArc(int x, int y, int width, int height, int arcStart,
1330                       int arcAngle)
1331   {
1332     ShapeCache sc = getShapeCache();
1333     if (sc.arc == null)
1334       sc.arc = new Arc2D.Float();
1335     sc.arc.setArc(x, y, width, height, arcStart, arcAngle, Arc2D.PIE);
1336     draw(sc.arc);
1337   }
1338
1339   public void drawPolyline(int[] xPoints, int[] yPoints, int npoints)
1340   {
1341     ShapeCache sc = getShapeCache();
1342     if (sc.polyline == null)
1343       sc.polyline = new GeneralPath();
1344     GeneralPath p = sc.polyline;
1345     p.reset();
1346     if (npoints > 0)
1347       p.moveTo(xPoints[0], yPoints[0]);
1348     for (int i = 1; i < npoints; i++)
1349       p.lineTo(xPoints[i], yPoints[i]);
1350     fill(p);
1351   }
1352
1353   /**
1354    * Draws the outline of a polygon.
1355    */
1356   public void drawPolygon(int[] xPoints, int[] yPoints, int npoints)
1357   {
1358     ShapeCache sc = getShapeCache();
1359     if (sc.polygon == null)
1360       sc.polygon = new Polygon();
1361     sc.polygon.reset();
1362     sc.polygon.xpoints = xPoints;
1363     sc.polygon.ypoints = yPoints;
1364     sc.polygon.npoints = npoints;
1365     draw(sc.polygon);
1366   }
1367
1368   /**
1369    * Fills the outline of a polygon.
1370    */
1371   public void fillPolygon(int[] xPoints, int[] yPoints, int npoints)
1372   {
1373     ShapeCache sc = getShapeCache();
1374     if (sc.polygon == null)
1375       sc.polygon = new Polygon();
1376     sc.polygon.reset();
1377     sc.polygon.xpoints = xPoints;
1378     sc.polygon.ypoints = yPoints;
1379     sc.polygon.npoints = npoints;
1380     fill(sc.polygon);
1381   }
1382
1383   /**
1384    * Draws the specified image at the specified location. This forwards
1385    * to {@link #drawImage(Image, AffineTransform, ImageObserver)}.
1386    *
1387    * @param image the image to render
1388    * @param x the x location to render to
1389    * @param y the y location to render to
1390    * @param observer the image observer to receive notification
1391    */
1392   public boolean drawImage(Image image, int x, int y, ImageObserver observer)
1393   {
1394     boolean ret;
1395     if (isOptimized)
1396       {
1397         ret = rawDrawImage(image, x + (int) transform.getTranslateX(),
1398                            y + (int) transform.getTranslateY(), observer);
1399       }
1400     else
1401       {
1402         AffineTransform t = new AffineTransform();
1403         t.translate(x, y);
1404         ret = drawImage(image, t, observer);
1405       }
1406     return ret;
1407   }
1408
1409   /**
1410    * Draws the specified image at the specified location. The image
1411    * is scaled to the specified width and height. This forwards
1412    * to {@link #drawImage(Image, AffineTransform, ImageObserver)}.
1413    *
1414    * @param image the image to render
1415    * @param x the x location to render to
1416    * @param y the y location to render to
1417    * @param width the target width of the image
1418    * @param height the target height of the image
1419    * @param observer the image observer to receive notification
1420    */
1421   public boolean drawImage(Image image, int x, int y, int width, int height,
1422                            ImageObserver observer)
1423   {
1424     AffineTransform t = new AffineTransform();
1425     t.translate(x, y);
1426     double scaleX = (double) width / (double) image.getWidth(observer);
1427     double scaleY =  (double) height / (double) image.getHeight(observer);
1428     t.scale(scaleX, scaleY);
1429     return drawImage(image, t, observer);
1430   }
1431
1432   /**
1433    * Draws the specified image at the specified location. This forwards
1434    * to {@link #drawImage(Image, AffineTransform, ImageObserver)}.
1435    *
1436    * @param image the image to render
1437    * @param x the x location to render to
1438    * @param y the y location to render to
1439    * @param bgcolor the background color to use for transparent pixels
1440    * @param observer the image observer to receive notification
1441    */
1442   public boolean drawImage(Image image, int x, int y, Color bgcolor,
1443                            ImageObserver observer)
1444   {
1445     AffineTransform t = new AffineTransform();
1446     t.translate(x, y);
1447     // TODO: Somehow implement the background option.
1448     return drawImage(image, t, observer);
1449   }
1450
1451   /**
1452    * Draws the specified image at the specified location. The image
1453    * is scaled to the specified width and height. This forwards
1454    * to {@link #drawImage(Image, AffineTransform, ImageObserver)}.
1455    *
1456    * @param image the image to render
1457    * @param x the x location to render to
1458    * @param y the y location to render to
1459    * @param width the target width of the image
1460    * @param height the target height of the image
1461    * @param bgcolor the background color to use for transparent pixels
1462    * @param observer the image observer to receive notification
1463    */
1464   public boolean drawImage(Image image, int x, int y, int width, int height,
1465                            Color bgcolor, ImageObserver observer)
1466   {
1467     AffineTransform t = new AffineTransform();
1468     t.translate(x, y);
1469     double scaleX = (double) image.getWidth(observer) / (double) width;
1470     double scaleY = (double) image.getHeight(observer) / (double) height;
1471     t.scale(scaleX, scaleY);
1472     // TODO: Somehow implement the background option.
1473     return drawImage(image, t, observer);
1474   }
1475
1476   /**
1477    * Draws an image fragment to a rectangular area of the target.
1478    *
1479    * @param image the image to render
1480    * @param dx1 the first corner of the destination rectangle
1481    * @param dy1 the first corner of the destination rectangle
1482    * @param dx2 the second corner of the destination rectangle
1483    * @param dy2 the second corner of the destination rectangle
1484    * @param sx1 the first corner of the source rectangle
1485    * @param sy1 the first corner of the source rectangle
1486    * @param sx2 the second corner of the source rectangle
1487    * @param sy2 the second corner of the source rectangle
1488    * @param observer the image observer to be notified
1489    */
1490   public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2,
1491                            int sx1, int sy1, int sx2, int sy2,
1492                            ImageObserver observer)
1493   {
1494     int sx = Math.min(sx1, sx1);
1495     int sy = Math.min(sy1, sy2);
1496     int sw = Math.abs(sx1 - sx2);
1497     int sh = Math.abs(sy1 - sy2);
1498     int dx = Math.min(dx1, dx1);
1499     int dy = Math.min(dy1, dy2);
1500     int dw = Math.abs(dx1 - dx2);
1501     int dh = Math.abs(dy1 - dy2);
1502     
1503     AffineTransform t = new AffineTransform();
1504     t.translate(sx - dx, sy - dy);
1505     double scaleX = (double) sw / (double) dw;
1506     double scaleY = (double) sh / (double) dh;
1507     t.scale(scaleX, scaleY);
1508     Rectangle areaOfInterest = new Rectangle(sx, sy, sw, sh);
1509     return drawImageImpl(image, t, observer, areaOfInterest);
1510   }
1511
1512   /**
1513    * Draws an image fragment to a rectangular area of the target.
1514    *
1515    * @param image the image to render
1516    * @param dx1 the first corner of the destination rectangle
1517    * @param dy1 the first corner of the destination rectangle
1518    * @param dx2 the second corner of the destination rectangle
1519    * @param dy2 the second corner of the destination rectangle
1520    * @param sx1 the first corner of the source rectangle
1521    * @param sy1 the first corner of the source rectangle
1522    * @param sx2 the second corner of the source rectangle
1523    * @param sy2 the second corner of the source rectangle
1524    * @param bgcolor the background color to use for transparent pixels
1525    * @param observer the image observer to be notified
1526    */
1527   public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2,
1528                            int sx1, int sy1, int sx2, int sy2, Color bgcolor,
1529                            ImageObserver observer)
1530   {
1531     // FIXME: Do something with bgcolor.
1532     return drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer);
1533   }
1534
1535   /**
1536    * Disposes this graphics object.
1537    */
1538   public void dispose()
1539   {
1540     // Nothing special to do here.
1541   }
1542
1543   /**
1544    * Fills the specified shape. Override this if your backend can efficiently
1545    * fill shapes. This is possible on many systems via a polygon fill
1546    * method or something similar. But keep in mind that Shapes can be quite
1547    * complex (non-convex, with holes etc), which is not necessarily supported
1548    * by all polygon fillers. Also note that you must perform clipping
1549    * before filling the shape.
1550    *
1551    * @param s the shape to fill
1552    * @param isFont <code>true</code> if the shape is a font outline
1553    */
1554   protected void fillShape(Shape s, boolean isFont)
1555   {
1556     // Determine if we need to antialias stuff.
1557     boolean antialias = false;
1558     if (isFont)
1559       {
1560         Object v = renderingHints.get(RenderingHints.KEY_TEXT_ANTIALIASING);
1561         // We default to antialiasing for text rendering.
1562         antialias = (v == RenderingHints.VALUE_TEXT_ANTIALIAS_ON
1563                      || v == RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
1564       }
1565     else
1566       {
1567         Object v = renderingHints.get(RenderingHints.KEY_ANTIALIASING);
1568         antialias = (v == RenderingHints.VALUE_ANTIALIAS_ON);
1569       }
1570     ScanlineConverter sc = getScanlineConverter();
1571     int resolution = 0;
1572     if (antialias)
1573       {
1574         // Adjust resolution according to rendering hints.
1575         resolution = 2;
1576       }
1577     sc.renderShape(this, s, clip, transform, resolution, renderingHints);
1578   }
1579
1580   /**
1581    * Returns the color model of this Graphics object.
1582    *
1583    * @return the color model of this Graphics object
1584    */
1585   protected abstract ColorModel getColorModel();
1586
1587   /**
1588    * Returns the bounds of the target.
1589    *
1590    * @return the bounds of the target
1591    */
1592   protected Rectangle getDeviceBounds()
1593   {
1594     return destinationRaster.getBounds();
1595   }
1596
1597   /**
1598    * Draws a line in optimization mode. The implementation should respect the
1599    * clip and translation. It can assume that the clip is a rectangle and that
1600    * the transform is only a translating transform.
1601    *
1602    * @param x0 the starting point, X coordinate
1603    * @param y0 the starting point, Y coordinate
1604    * @param x1 the end point, X coordinate 
1605    * @param y1 the end point, Y coordinate
1606    */
1607   protected void rawDrawLine(int x0, int y0, int x1, int y1)
1608   {
1609     ShapeCache sc = getShapeCache();
1610     if (sc.line == null)
1611       sc.line = new Line2D.Float();
1612     sc.line.setLine(x0, y0, x1, y1);
1613     draw(sc.line);
1614   }
1615
1616   protected void rawDrawRect(int x, int y, int w, int h)
1617   {
1618     ShapeCache sc = getShapeCache();
1619     if (sc.rect == null)
1620       sc.rect = new Rectangle();
1621     sc.rect.setBounds(x, y, w, h);
1622     draw(sc.rect);
1623   }
1624
1625   /**
1626    * Draws a string in optimization mode. The implementation should respect the
1627    * clip and translation. It can assume that the clip is a rectangle and that
1628    * the transform is only a translating transform.
1629    *
1630    * @param text the string to be drawn
1631    * @param x the start of the baseline, X coordinate
1632    * @param y the start of the baseline, Y coordinate
1633    */
1634   protected void rawDrawString(String text, int x, int y)
1635   {
1636     FontRenderContext ctx = getFontRenderContext();
1637     GlyphVector gv = font.createGlyphVector(ctx, text.toCharArray());
1638     drawGlyphVector(gv, x, y);
1639   }
1640
1641   /**
1642    * Clears a rectangle in optimization mode. The implementation should respect the
1643    * clip and translation. It can assume that the clip is a rectangle and that
1644    * the transform is only a translating transform.
1645    *
1646    * @param x the upper left corner, X coordinate
1647    * @param y the upper left corner, Y coordinate
1648    * @param w the width
1649    * @param h the height
1650    */
1651   protected void rawClearRect(int x, int y, int w, int h)
1652   {
1653     Paint savedForeground = getPaint();
1654     setPaint(getBackground());
1655     rawFillRect(x, y, w, h);
1656     setPaint(savedForeground);
1657   }
1658
1659   /**
1660    * Fills a rectangle in optimization mode. The implementation should respect
1661    * the clip but can assume that it is a rectangle.
1662    *
1663    * @param x the upper left corner, X coordinate
1664    * @param y the upper left corner, Y coordinate
1665    * @param w the width
1666    * @param h the height
1667    */
1668   protected void rawFillRect(int x, int y, int w, int h)
1669   {
1670     ShapeCache sc = getShapeCache();
1671     if (sc.rect == null)
1672       sc.rect = new Rectangle();
1673     sc.rect.setBounds(x, y, w, h);
1674     fill(sc.rect);
1675   }
1676
1677   /**
1678    * Draws an image in optimization mode. The implementation should respect
1679    * the clip but can assume that it is a rectangle.
1680    *
1681    * @param image the image to be painted
1682    * @param x the location, X coordinate
1683    * @param y the location, Y coordinate
1684    * @param obs the image observer to be notified
1685    *
1686    * @return <code>true</code> when the image is painted completely,
1687    *         <code>false</code> if it is still rendered
1688    */
1689   protected boolean rawDrawImage(Image image, int x, int y, ImageObserver obs)
1690   {
1691     AffineTransform t = new AffineTransform();
1692     t.translate(x, y);
1693     return drawImage(image, t, obs);
1694   }
1695
1696   /**
1697    * Copies a rectangular region to another location.
1698    *
1699    * @param x the upper left corner, X coordinate
1700    * @param y the upper left corner, Y coordinate
1701    * @param w the width
1702    * @param h the height
1703    * @param dx
1704    * @param dy
1705    */
1706   protected void rawCopyArea(int x, int y, int w, int h, int dx, int dy)
1707   {
1708     copyAreaImpl(x, y, w, h, dx, dy);
1709   }
1710
1711   // Private implementation methods.
1712
1713   /**
1714    * Copies a rectangular area of the target raster to a different location.
1715    */
1716   private void copyAreaImpl(int x, int y, int w, int h, int dx, int dy)
1717   {
1718     // FIXME: Implement this properly.
1719     throw new UnsupportedOperationException("Not implemented yet.");
1720   }
1721
1722   /**
1723    * Paints a scanline between x0 and x1. Override this when your backend
1724    * can efficiently draw/fill horizontal lines.
1725    *
1726    * @param x0 the left offset
1727    * @param x1 the right offset
1728    * @param y the scanline
1729    */
1730   public void renderScanline(int y, ScanlineCoverage c)
1731   {
1732     PaintContext pCtx = paintContext;
1733     int x0 = c.getMinX();
1734     int x1 = c.getMaxX();
1735     Raster paintRaster = pCtx.getRaster(x0, y, x1 - x0, 1);
1736
1737     // Do the anti aliasing thing.
1738     float coverageAlpha = 0;
1739     float maxCoverage = c.getMaxCoverage();
1740     ColorModel cm = pCtx.getColorModel();
1741     DataBuffer db = paintRaster.getDataBuffer();
1742     Point loc = new Point(paintRaster.getMinX(), paintRaster.getMinY());
1743     SampleModel sm = paintRaster.getSampleModel();
1744     WritableRaster writeRaster = Raster.createWritableRaster(sm, db, loc);
1745     WritableRaster alphaRaster = cm.getAlphaRaster(writeRaster);
1746     int pixel;
1747     ScanlineCoverage.Iterator iter = c.iterate();
1748     while (iter.hasNext())
1749       {
1750         ScanlineCoverage.Range range = iter.next();
1751         coverageAlpha = range.getCoverage() / maxCoverage;
1752         if (coverageAlpha < 1.0)
1753           {
1754             for (int x = range.getXPos(); x < range.getXPosEnd(); x++)
1755               {
1756                 pixel = alphaRaster.getSample(x, y, 0);
1757                 pixel = (int) (pixel * coverageAlpha);
1758                 alphaRaster.setSample(x, y, 0, pixel);
1759               }
1760           }
1761       }
1762     ColorModel paintColorModel = pCtx.getColorModel();
1763     CompositeContext cCtx = composite.createContext(paintColorModel,
1764                                                     getColorModel(),
1765                                                     renderingHints);
1766     WritableRaster targetChild = destinationRaster.createWritableTranslatedChild(-x0,- y);
1767     cCtx.compose(paintRaster, targetChild, targetChild);
1768     updateRaster(destinationRaster, x0, y, x1 - x0, 1);
1769     cCtx.dispose();
1770   }
1771
1772
1773   /**
1774    * Initializes this graphics object. This must be called by subclasses in
1775    * order to correctly initialize the state of this object.
1776    */
1777   protected void init()
1778   {
1779     setPaint(Color.BLACK);
1780     setFont(FONT);
1781     isOptimized = true;
1782   }
1783
1784   /**
1785    * Returns a WritableRaster that is used by this class to perform the
1786    * rendering in. It is not necessary that the target surface immediately
1787    * reflects changes in the raster. Updates to the raster are notified via
1788    * {@link #updateRaster}.
1789    *
1790    * @return the destination raster
1791    */
1792   protected WritableRaster getDestinationRaster()
1793   {
1794     // TODO: Ideally we would fetch the xdrawable's surface pixels for
1795     // initialization of the raster.
1796     Rectangle db = getDeviceBounds();
1797     if (destinationRaster == null)
1798       {
1799         int[] bandMasks = new int[]{ 0xFF0000, 0xFF00, 0xFF };
1800         destinationRaster = Raster.createPackedRaster(DataBuffer.TYPE_INT,
1801                                                       db.width, db.height,
1802                                                       bandMasks, null);
1803         // Initialize raster with white.
1804         int x0 = destinationRaster.getMinX();
1805         int x1 = destinationRaster.getWidth() + x0;
1806         int y0 = destinationRaster.getMinY();
1807         int y1 = destinationRaster.getHeight() + y0;
1808         int numBands = destinationRaster.getNumBands();
1809         for (int y = y0; y < y1; y++)
1810           {
1811             for (int x = x0; x < x1; x++)
1812               {
1813                 for (int b = 0; b < numBands; b++)
1814                   destinationRaster.setSample(x, y, b, 255);
1815               }
1816           }
1817       }
1818     return destinationRaster;
1819   }
1820
1821   /**
1822    * Notifies the backend that the raster has changed in the specified
1823    * rectangular area. The raster that is provided in this method is always
1824    * the same as the one returned in {@link #getDestinationRaster}.
1825    * Backends that reflect changes to this raster directly don't need to do
1826    * anything here.
1827    *
1828    * @param raster the updated raster, identical to the raster returned
1829    *        by {@link #getDestinationRaster()}
1830    * @param x the upper left corner of the updated region, X coordinate
1831    * @param y the upper lef corner of the updated region, Y coordinate
1832    * @param w the width of the updated region
1833    * @param h the height of the updated region
1834    */
1835   protected void updateRaster(Raster raster, int x, int y, int w, int h)
1836   {
1837     // Nothing to do here. Backends that need to update their surface
1838     // to reflect the change should override this method.
1839   }
1840
1841   // Some helper methods.
1842
1843   /**
1844    * Helper method to check and update the optimization conditions.
1845    */
1846   private void updateOptimization()
1847   {
1848     int transformType = transform.getType();
1849     boolean optimizedTransform = false;
1850     if (transformType == AffineTransform.TYPE_IDENTITY
1851         || transformType == AffineTransform.TYPE_TRANSLATION)
1852       optimizedTransform = true;
1853
1854     boolean optimizedClip = (clip == null || clip instanceof Rectangle);
1855     isOptimized = optimizedClip
1856                   && optimizedTransform && paint instanceof Color
1857                   && composite == AlphaComposite.SrcOver
1858                   && stroke.equals(new BasicStroke());
1859   }
1860
1861   /**
1862    * Calculates the intersection of two rectangles. The result is stored
1863    * in <code>rect</code>. This is basically the same
1864    * like {@link Rectangle#intersection(Rectangle)}, only that it does not
1865    * create new Rectangle instances. The tradeoff is that you loose any data in
1866    * <code>rect</code>.
1867    *
1868    * @param x upper-left x coodinate of first rectangle
1869    * @param y upper-left y coodinate of first rectangle
1870    * @param w width of first rectangle
1871    * @param h height of first rectangle
1872    * @param rect a Rectangle object of the second rectangle
1873    *
1874    * @throws NullPointerException if rect is null
1875    *
1876    * @return a rectangle corresponding to the intersection of the
1877    *         two rectangles. An empty rectangle is returned if the rectangles
1878    *         do not overlap
1879    */
1880   private static Rectangle computeIntersection(int x, int y, int w, int h,
1881                                                Rectangle rect)
1882   {
1883     int x2 = rect.x;
1884     int y2 = rect.y;
1885     int w2 = rect.width;
1886     int h2 = rect.height;
1887
1888     int dx = (x > x2) ? x : x2;
1889     int dy = (y > y2) ? y : y2;
1890     int dw = (x + w < x2 + w2) ? (x + w - dx) : (x2 + w2 - dx);
1891     int dh = (y + h < y2 + h2) ? (y + h - dy) : (y2 + h2 - dy);
1892
1893     if (dw >= 0 && dh >= 0)
1894       rect.setBounds(dx, dy, dw, dh);
1895     else
1896       rect.setBounds(0, 0, 0, 0);
1897
1898     return rect;
1899   }
1900
1901   /**
1902    * Helper method to transform the clip. This is called by the various
1903    * transformation-manipulation methods to update the clip (which is in
1904    * userspace) accordingly.
1905    *
1906    * The transform usually is the inverse transform that was applied to the
1907    * graphics object.
1908    *
1909    * @param t the transform to apply to the clip
1910    */
1911   private void updateClip(AffineTransform t)
1912   {
1913     if (! (clip instanceof GeneralPath))
1914       clip = new GeneralPath(clip);
1915
1916     GeneralPath p = (GeneralPath) clip;
1917     p.transform(t);
1918   }
1919
1920   /**
1921    * Returns the ShapeCache for the calling thread.
1922    *
1923    * @return the ShapeCache for the calling thread
1924    */
1925   private ShapeCache getShapeCache()
1926   {
1927     ShapeCache sc = shapeCache.get();
1928     if (sc == null)
1929       {
1930         sc = new ShapeCache();
1931         shapeCache.set(sc);
1932       }
1933     return sc;
1934   }
1935
1936   /**
1937    * Returns the scanline converter for this thread.
1938    *
1939    * @return the scanline converter for this thread
1940    */
1941   private ScanlineConverter getScanlineConverter()
1942   {
1943     ScanlineConverter sc = scanlineConverters.get();
1944     if (sc == null)
1945       {
1946         sc = new ScanlineConverter();
1947         scanlineConverters.set(sc);
1948       }
1949     return sc;
1950   }
1951
1952 }