mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			2095 lines
		
	
	
		
			63 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			2095 lines
		
	
	
		
			63 KiB
		
	
	
	
		
			Java
		
	
	
	
/* AbstractGraphics2D.java -- Abstract Graphics2D implementation
 | 
						|
   Copyright (C) 2006 Free Software Foundation, Inc.
 | 
						|
 | 
						|
This file is part of GNU Classpath.
 | 
						|
 | 
						|
GNU Classpath is free software; you can redistribute it and/or modify
 | 
						|
it under the terms of the GNU General Public License as published by
 | 
						|
the Free Software Foundation; either version 2, or (at your option)
 | 
						|
any later version.
 | 
						|
 | 
						|
GNU Classpath is distributed in the hope that it will be useful, but
 | 
						|
WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
General Public License for more details.
 | 
						|
 | 
						|
You should have received a copy of the GNU General Public License
 | 
						|
along with GNU Classpath; see the file COPYING.  If not, write to the
 | 
						|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | 
						|
02110-1301 USA.
 | 
						|
 | 
						|
Linking this library statically or dynamically with other modules is
 | 
						|
making a combined work based on this library.  Thus, the terms and
 | 
						|
conditions of the GNU General Public License cover the whole
 | 
						|
combination.
 | 
						|
 | 
						|
As a special exception, the copyright holders of this library give you
 | 
						|
permission to link this library with independent modules to produce an
 | 
						|
executable, regardless of the license terms of these independent
 | 
						|
modules, and to copy and distribute the resulting executable under
 | 
						|
terms of your choice, provided that you also meet, for each linked
 | 
						|
independent module, the terms and conditions of the license of that
 | 
						|
module.  An independent module is a module which is not derived from
 | 
						|
or based on this library.  If you modify this library, you may extend
 | 
						|
this exception to your version of the library, but you are not
 | 
						|
obligated to do so.  If you do not wish to do so, delete this
 | 
						|
exception statement from your version. */
 | 
						|
 | 
						|
package gnu.java.awt.java2d;
 | 
						|
 | 
						|
import gnu.java.util.LRUCache;
 | 
						|
 | 
						|
import java.awt.AWTError;
 | 
						|
import java.awt.AlphaComposite;
 | 
						|
import java.awt.AWTPermission;
 | 
						|
import java.awt.BasicStroke;
 | 
						|
import java.awt.Color;
 | 
						|
import java.awt.Composite;
 | 
						|
import java.awt.CompositeContext;
 | 
						|
import java.awt.Dimension;
 | 
						|
import java.awt.Font;
 | 
						|
import java.awt.FontMetrics;
 | 
						|
import java.awt.Graphics;
 | 
						|
import java.awt.Graphics2D;
 | 
						|
import java.awt.Image;
 | 
						|
import java.awt.Paint;
 | 
						|
import java.awt.PaintContext;
 | 
						|
import java.awt.Point;
 | 
						|
import java.awt.Polygon;
 | 
						|
import java.awt.Rectangle;
 | 
						|
import java.awt.RenderingHints;
 | 
						|
import java.awt.Shape;
 | 
						|
import java.awt.Stroke;
 | 
						|
import java.awt.Toolkit;
 | 
						|
import java.awt.RenderingHints.Key;
 | 
						|
import java.awt.font.FontRenderContext;
 | 
						|
import java.awt.font.GlyphVector;
 | 
						|
import java.awt.geom.AffineTransform;
 | 
						|
import java.awt.geom.Arc2D;
 | 
						|
import java.awt.geom.Area;
 | 
						|
import java.awt.geom.Ellipse2D;
 | 
						|
import java.awt.geom.GeneralPath;
 | 
						|
import java.awt.geom.Line2D;
 | 
						|
import java.awt.geom.NoninvertibleTransformException;
 | 
						|
import java.awt.geom.RoundRectangle2D;
 | 
						|
import java.awt.image.BufferedImage;
 | 
						|
import java.awt.image.BufferedImageOp;
 | 
						|
import java.awt.image.ColorModel;
 | 
						|
import java.awt.image.DataBuffer;
 | 
						|
import java.awt.image.FilteredImageSource;
 | 
						|
import java.awt.image.ImageObserver;
 | 
						|
import java.awt.image.ImageProducer;
 | 
						|
import java.awt.image.Raster;
 | 
						|
import java.awt.image.RenderedImage;
 | 
						|
import java.awt.image.ReplicateScaleFilter;
 | 
						|
import java.awt.image.SampleModel;
 | 
						|
import java.awt.image.WritableRaster;
 | 
						|
import java.awt.image.renderable.RenderableImage;
 | 
						|
import java.text.AttributedCharacterIterator;
 | 
						|
import java.util.Collections;
 | 
						|
import java.util.HashMap;
 | 
						|
import java.util.LinkedList;
 | 
						|
import java.util.Map;
 | 
						|
import java.util.WeakHashMap;
 | 
						|
 | 
						|
/**
 | 
						|
 * This is a 100% Java implementation of the Java2D rendering pipeline. It is
 | 
						|
 * meant as a base class for Graphics2D implementations.
 | 
						|
 *
 | 
						|
 * <h2>Backend interface</h2>
 | 
						|
 * <p>
 | 
						|
 * The backend must at the very least provide a Raster which the the rendering
 | 
						|
 * pipeline can paint into. This must be implemented in
 | 
						|
 * {@link #getDestinationRaster()}. For some backends that might be enough, like
 | 
						|
 * when the target surface can be directly access via the raster (like in
 | 
						|
 * BufferedImages). Other targets need some way to synchronize the raster with
 | 
						|
 * the surface, which can be achieved by implementing the
 | 
						|
 * {@link #updateRaster(Raster, int, int, int, int)} method, which always gets
 | 
						|
 * called after a chunk of data got painted into the raster.
 | 
						|
 * </p>
 | 
						|
 * <p>Alternativly the backend can provide a method for filling Shapes by
 | 
						|
 * overriding the protected method fillShape(). This can be accomplished
 | 
						|
 * by a polygon filling function of the backend. Keep in mind though that
 | 
						|
 * Shapes can be quite complex (i.e. non-convex and containing holes, etc)
 | 
						|
 * which is not supported by all polygon fillers. Also it must be noted
 | 
						|
 * that fillShape() is expected to handle painting and compositing as well as
 | 
						|
 * clipping and transformation. If your backend can't support this natively,
 | 
						|
 * then you can fallback to the implementation in this class. You'll need
 | 
						|
 * to provide a writable Raster then, see above.</p>
 | 
						|
 * <p>Another alternative is to implement fillScanline() which only requires
 | 
						|
 * the backend to be able to draw horizontal lines in device space,
 | 
						|
 * which is usually very cheap.
 | 
						|
 * The implementation should still handle painting and compositing,
 | 
						|
 * but no more clipping and transformation is required by the backend.</p>
 | 
						|
 * <p>The backend is free to provide implementations for the various raw*
 | 
						|
 * methods for optimized AWT 1.1 style painting of some primitives. This should
 | 
						|
 * accelerate painting of Swing greatly. When doing so, the backend must also
 | 
						|
 * keep track of the clip and translation, probably by overriding
 | 
						|
 * some clip and translate methods. Don't forget to message super in such a
 | 
						|
 * case.</p>
 | 
						|
 *
 | 
						|
 * <h2>Acceleration options</h2>
 | 
						|
 * <p>
 | 
						|
 * The fact that it is
 | 
						|
 * pure Java makes it a little slow. However, there are several ways of
 | 
						|
 * accelerating the rendering pipeline:
 | 
						|
 * <ol>
 | 
						|
 * <li><em>Optimization hooks for AWT 1.1 - like graphics operations.</em>
 | 
						|
 *   The most important methods from the {@link java.awt.Graphics} class
 | 
						|
 *   have a corresponding <code>raw*</code> method, which get called when
 | 
						|
 *   several optimization conditions are fullfilled. These conditions are
 | 
						|
 *   described below. Subclasses can override these methods and delegate
 | 
						|
 *   it directly to a native backend.</li>
 | 
						|
 * <li><em>Native PaintContexts and CompositeContext.</em> The implementations
 | 
						|
 *   for the 3 PaintContexts and AlphaCompositeContext can be accelerated
 | 
						|
 *   using native code. These have proved to two of the most performance
 | 
						|
 *   critical points in the rendering pipeline and cannot really be done quickly
 | 
						|
 *   in plain Java because they involve lots of shuffling around with large
 | 
						|
 *   arrays. In fact, you really would want to let the graphics card to the
 | 
						|
 *   work, they are made for this.</li>
 | 
						|
 * <li>Provide an accelerated implementation for fillShape(). For instance,
 | 
						|
 * OpenGL can fill shapes very efficiently. There are some considerations
 | 
						|
 * to be made though, see above for details.</li>
 | 
						|
 * </ol>
 | 
						|
 * </p>
 | 
						|
 *
 | 
						|
 * @author Roman Kennke (kennke@aicas.com)
 | 
						|
 */
 | 
						|
public abstract class AbstractGraphics2D
 | 
						|
  extends Graphics2D
 | 
						|
  implements Cloneable, Pixelizer
 | 
						|
{
 | 
						|
  /**
 | 
						|
   * Caches scaled versions of an image.
 | 
						|
   *
 | 
						|
   * @see #drawImage(Image, int, int, int, int, ImageObserver)
 | 
						|
   */
 | 
						|
  protected static final WeakHashMap<Image, HashMap<Dimension,Image>> imageCache =
 | 
						|
    new WeakHashMap<Image, HashMap<Dimension, Image>>();
 | 
						|
 | 
						|
  /**
 | 
						|
   * Wether we use anti aliasing for rendering text by default or not.
 | 
						|
   */
 | 
						|
  private static final boolean DEFAULT_TEXT_AA =
 | 
						|
    Boolean.getBoolean("gnu.java2d.default_text_aa");
 | 
						|
 | 
						|
  /**
 | 
						|
   * The default font to use on the graphics object.
 | 
						|
   */
 | 
						|
  private static final Font FONT = new Font("SansSerif", Font.PLAIN, 12);
 | 
						|
 | 
						|
  /**
 | 
						|
   * The size of the LRU cache used for caching GlyphVectors.
 | 
						|
   */
 | 
						|
  private static final int GV_CACHE_SIZE = 50;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Caches certain shapes to avoid massive creation of such Shapes in
 | 
						|
   * the various draw* and fill* methods.
 | 
						|
   */
 | 
						|
  private static final ShapeCache shapeCache = new ShapeCache();
 | 
						|
 | 
						|
  /**
 | 
						|
   * A pool of scanline converters. It is important to reuse scanline
 | 
						|
   * converters because they keep their datastructures in place. We pool them
 | 
						|
   * for use in multiple threads.
 | 
						|
   */
 | 
						|
  private static final LinkedList<ScanlineConverter> scanlineConverters =
 | 
						|
    new LinkedList<ScanlineConverter>();
 | 
						|
 | 
						|
  /**
 | 
						|
   * Caches glyph vectors for better drawing performance.
 | 
						|
   */
 | 
						|
  private static final Map<TextCacheKey,GlyphVector> gvCache =
 | 
						|
    Collections.synchronizedMap(new LRUCache<TextCacheKey,GlyphVector>(GV_CACHE_SIZE));
 | 
						|
 | 
						|
  /**
 | 
						|
   * This key is used to search in the gvCache without allocating a new
 | 
						|
   * key each time.
 | 
						|
   */
 | 
						|
  private static final TextCacheKey searchTextKey = new TextCacheKey();
 | 
						|
 | 
						|
  /**
 | 
						|
   * The transformation for this Graphics2D instance
 | 
						|
   */
 | 
						|
  protected AffineTransform transform;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The foreground.
 | 
						|
   */
 | 
						|
  private Paint paint;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The paint context during rendering.
 | 
						|
   */
 | 
						|
  private PaintContext paintContext = null;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The background.
 | 
						|
   */
 | 
						|
  private Color background = Color.WHITE;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Foreground color, as set by setColor.
 | 
						|
   */
 | 
						|
  private Color foreground = Color.BLACK;
 | 
						|
  private boolean isForegroundColorNull = true;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The current font.
 | 
						|
   */
 | 
						|
  private Font font;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The current composite setting.
 | 
						|
   */
 | 
						|
  private Composite composite;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The current stroke setting.
 | 
						|
   */
 | 
						|
  private Stroke stroke;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The current clip. This clip is in user coordinate space.
 | 
						|
   */
 | 
						|
  private Shape clip;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The rendering hints.
 | 
						|
   */
 | 
						|
  private RenderingHints renderingHints;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The raster of the destination surface. This is where the painting is
 | 
						|
   * performed.
 | 
						|
   */
 | 
						|
  private WritableRaster destinationRaster;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Indicates if certain graphics primitives can be rendered in an optimized
 | 
						|
   * fashion. This will be the case if the following conditions are met:
 | 
						|
   * - The transform may only be a translation, no rotation, shearing or
 | 
						|
   *   scaling.
 | 
						|
   * - The paint must be a solid color.
 | 
						|
   * - The composite must be an AlphaComposite.SrcOver.
 | 
						|
   * - The clip must be a Rectangle.
 | 
						|
   * - The stroke must be a plain BasicStroke().
 | 
						|
   *
 | 
						|
   * These conditions represent the standard settings of a new
 | 
						|
   * AbstractGraphics2D object and will be the most commonly used setting
 | 
						|
   * in Swing rendering and should therefore be optimized as much as possible.
 | 
						|
   */
 | 
						|
  private boolean isOptimized = true;
 | 
						|
 | 
						|
  private static final BasicStroke STANDARD_STROKE = new BasicStroke();
 | 
						|
 | 
						|
  private static final HashMap<Key, Object> STANDARD_HINTS;
 | 
						|
  static
 | 
						|
    {
 | 
						|
 | 
						|
      HashMap<Key, Object> hints = new HashMap<Key, Object>();
 | 
						|
      hints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
 | 
						|
                RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
 | 
						|
      hints.put(RenderingHints.KEY_ANTIALIASING,
 | 
						|
                RenderingHints.VALUE_ANTIALIAS_DEFAULT);
 | 
						|
 | 
						|
      STANDARD_HINTS = hints;
 | 
						|
    }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a new AbstractGraphics2D instance.
 | 
						|
   */
 | 
						|
  protected AbstractGraphics2D()
 | 
						|
  {
 | 
						|
    transform = new AffineTransform();
 | 
						|
    background = Color.WHITE;
 | 
						|
    composite = AlphaComposite.SrcOver;
 | 
						|
    stroke = STANDARD_STROKE;
 | 
						|
    renderingHints = new RenderingHints(STANDARD_HINTS);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws the specified shape. The shape is passed through the current stroke
 | 
						|
   * and is then forwarded to {@link #fillShape}.
 | 
						|
   *
 | 
						|
   * @param shape the shape to draw
 | 
						|
   */
 | 
						|
  public void draw(Shape shape)
 | 
						|
  {
 | 
						|
    // Stroke the shape.
 | 
						|
    Shape strokedShape = stroke.createStrokedShape(shape);
 | 
						|
    // Fill the stroked shape.
 | 
						|
    fillShape(strokedShape, false);
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws the specified image and apply the transform for image space ->
 | 
						|
   * user space conversion.
 | 
						|
   *
 | 
						|
   * This method is implemented to special case RenderableImages and
 | 
						|
   * RenderedImages and delegate to
 | 
						|
   * {@link #drawRenderableImage(RenderableImage, AffineTransform)} and
 | 
						|
   * {@link #drawRenderedImage(RenderedImage, AffineTransform)} accordingly.
 | 
						|
   * Other image types are not yet handled.
 | 
						|
   *
 | 
						|
   * @param image the image to be rendered
 | 
						|
   * @param xform the transform from image space to user space
 | 
						|
   * @param obs the image observer to be notified
 | 
						|
   */
 | 
						|
  public boolean drawImage(Image image, AffineTransform xform,
 | 
						|
                           ImageObserver obs)
 | 
						|
  {
 | 
						|
    Rectangle areaOfInterest = new Rectangle(0, 0, image.getWidth(obs),
 | 
						|
                                             image.getHeight(obs));
 | 
						|
    return drawImageImpl(image, xform, obs, areaOfInterest);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws the specified image and apply the transform for image space ->
 | 
						|
   * user space conversion. This method only draw the part of the image
 | 
						|
   * specified by <code>areaOfInterest</code>.
 | 
						|
   *
 | 
						|
   * This method is implemented to special case RenderableImages and
 | 
						|
   * RenderedImages and delegate to
 | 
						|
   * {@link #drawRenderableImage(RenderableImage, AffineTransform)} and
 | 
						|
   * {@link #drawRenderedImage(RenderedImage, AffineTransform)} accordingly.
 | 
						|
   * Other image types are not yet handled.
 | 
						|
   *
 | 
						|
   * @param image the image to be rendered
 | 
						|
   * @param xform the transform from image space to user space
 | 
						|
   * @param obs the image observer to be notified
 | 
						|
   * @param areaOfInterest the area in image space that is rendered
 | 
						|
   */
 | 
						|
  private boolean drawImageImpl(Image image, AffineTransform xform,
 | 
						|
                             ImageObserver obs, Rectangle areaOfInterest)
 | 
						|
  {
 | 
						|
    boolean ret;
 | 
						|
    if (image == null)
 | 
						|
      {
 | 
						|
        ret = true;
 | 
						|
      }
 | 
						|
    else if (image instanceof RenderedImage)
 | 
						|
      {
 | 
						|
        // FIXME: Handle the ImageObserver.
 | 
						|
        drawRenderedImageImpl((RenderedImage) image, xform, areaOfInterest);
 | 
						|
        ret = true;
 | 
						|
      }
 | 
						|
    else if (image instanceof RenderableImage)
 | 
						|
      {
 | 
						|
        // FIXME: Handle the ImageObserver.
 | 
						|
        drawRenderableImageImpl((RenderableImage) image, xform, areaOfInterest);
 | 
						|
        ret = true;
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        // FIXME: Implement rendering of other Image types.
 | 
						|
        ret = false;
 | 
						|
      }
 | 
						|
    return ret;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Renders a BufferedImage and applies the specified BufferedImageOp before
 | 
						|
   * to filter the BufferedImage somehow. The resulting BufferedImage is then
 | 
						|
   * passed on to {@link #drawRenderedImage(RenderedImage, AffineTransform)}
 | 
						|
   * to perform the final rendering.
 | 
						|
   *
 | 
						|
   * @param image the source buffered image
 | 
						|
   * @param op the filter to apply to the buffered image before rendering
 | 
						|
   * @param x the x coordinate to render the image to
 | 
						|
   * @param y the y coordinate to render the image to
 | 
						|
   */
 | 
						|
  public void drawImage(BufferedImage image, BufferedImageOp op, int x, int y)
 | 
						|
  {
 | 
						|
    BufferedImage filtered =
 | 
						|
      op.createCompatibleDestImage(image, image.getColorModel());
 | 
						|
    AffineTransform t = new AffineTransform();
 | 
						|
    t.translate(x, y);
 | 
						|
    drawRenderedImage(filtered, t);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Renders the specified image to the destination raster. The specified
 | 
						|
   * transform is used to convert the image into user space. The transform
 | 
						|
   * of this AbstractGraphics2D object is used to transform from user space
 | 
						|
   * to device space.
 | 
						|
   *
 | 
						|
   * The rendering is performed using the scanline algorithm that performs the
 | 
						|
   * rendering of other shapes and a custom Paint implementation, that supplies
 | 
						|
   * the pixel values of the rendered image.
 | 
						|
   *
 | 
						|
   * @param image the image to render to the destination raster
 | 
						|
   * @param xform the transform from image space to user space
 | 
						|
   */
 | 
						|
  public void drawRenderedImage(RenderedImage image, AffineTransform xform)
 | 
						|
  {
 | 
						|
    Rectangle areaOfInterest = new Rectangle(image.getMinX(),
 | 
						|
                                             image.getHeight(),
 | 
						|
                                             image.getWidth(),
 | 
						|
                                             image.getHeight());
 | 
						|
    drawRenderedImageImpl(image, xform, areaOfInterest);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Renders the specified image to the destination raster. The specified
 | 
						|
   * transform is used to convert the image into user space. The transform
 | 
						|
   * of this AbstractGraphics2D object is used to transform from user space
 | 
						|
   * to device space. Only the area specified by <code>areaOfInterest</code>
 | 
						|
   * is finally rendered to the target.
 | 
						|
   *
 | 
						|
   * The rendering is performed using the scanline algorithm that performs the
 | 
						|
   * rendering of other shapes and a custom Paint implementation, that supplies
 | 
						|
   * the pixel values of the rendered image.
 | 
						|
   *
 | 
						|
   * @param image the image to render to the destination raster
 | 
						|
   * @param xform the transform from image space to user space
 | 
						|
   */
 | 
						|
  private void drawRenderedImageImpl(RenderedImage image,
 | 
						|
                                     AffineTransform xform,
 | 
						|
                                     Rectangle areaOfInterest)
 | 
						|
  {
 | 
						|
    // First we compute the transformation. This is made up of 3 parts:
 | 
						|
    // 1. The areaOfInterest -> image space transform.
 | 
						|
    // 2. The image space -> user space transform.
 | 
						|
    // 3. The user space -> device space transform.
 | 
						|
    AffineTransform t = new AffineTransform();
 | 
						|
    t.translate(- areaOfInterest.x - image.getMinX(),
 | 
						|
                - areaOfInterest.y - image.getMinY());
 | 
						|
    t.concatenate(xform);
 | 
						|
    t.concatenate(transform);
 | 
						|
    AffineTransform it = null;
 | 
						|
    try
 | 
						|
      {
 | 
						|
        it = t.createInverse();
 | 
						|
      }
 | 
						|
    catch (NoninvertibleTransformException ex)
 | 
						|
      {
 | 
						|
        // Ignore -- we return if the transform is not invertible.
 | 
						|
      }
 | 
						|
    if (it != null)
 | 
						|
      {
 | 
						|
        // Transform the area of interest into user space.
 | 
						|
        GeneralPath aoi = new GeneralPath(areaOfInterest);
 | 
						|
        aoi.transform(xform);
 | 
						|
        // Render the shape using the standard renderer, but with a temporary
 | 
						|
        // ImagePaint.
 | 
						|
        ImagePaint p = new ImagePaint(image, it);
 | 
						|
        Paint savedPaint = paint;
 | 
						|
        try
 | 
						|
          {
 | 
						|
            paint = p;
 | 
						|
            fillShape(aoi, false);
 | 
						|
          }
 | 
						|
        finally
 | 
						|
          {
 | 
						|
            paint = savedPaint;
 | 
						|
          }
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Renders a renderable image. This produces a RenderedImage, which is
 | 
						|
   * then passed to {@link #drawRenderedImage(RenderedImage, AffineTransform)}
 | 
						|
   * to perform the final rendering.
 | 
						|
   *
 | 
						|
   * @param image the renderable image to be rendered
 | 
						|
   * @param xform the transform from image space to user space
 | 
						|
   */
 | 
						|
  public void drawRenderableImage(RenderableImage image, AffineTransform xform)
 | 
						|
  {
 | 
						|
    Rectangle areaOfInterest = new Rectangle((int) image.getMinX(),
 | 
						|
                                             (int) image.getHeight(),
 | 
						|
                                             (int) image.getWidth(),
 | 
						|
                                             (int) image.getHeight());
 | 
						|
    drawRenderableImageImpl(image, xform, areaOfInterest);
 | 
						|
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Renders a renderable image. This produces a RenderedImage, which is
 | 
						|
   * then passed to {@link #drawRenderedImage(RenderedImage, AffineTransform)}
 | 
						|
   * to perform the final rendering. Only the area of the image specified
 | 
						|
   * by <code>areaOfInterest</code> is rendered.
 | 
						|
   *
 | 
						|
   * @param image the renderable image to be rendered
 | 
						|
   * @param xform the transform from image space to user space
 | 
						|
   */
 | 
						|
  private void drawRenderableImageImpl(RenderableImage image,
 | 
						|
                                       AffineTransform xform,
 | 
						|
                                       Rectangle areaOfInterest)
 | 
						|
  {
 | 
						|
    // TODO: Maybe make more clever usage of a RenderContext here.
 | 
						|
    RenderedImage rendered = image.createDefaultRendering();
 | 
						|
    drawRenderedImageImpl(rendered, xform, areaOfInterest);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws the specified string at the specified location.
 | 
						|
   *
 | 
						|
   * @param text the string to draw
 | 
						|
   * @param x the x location, relative to the bounding rectangle of the text
 | 
						|
   * @param y the y location, relative to the bounding rectangle of the text
 | 
						|
   */
 | 
						|
  public void drawString(String text, int x, int y)
 | 
						|
  {
 | 
						|
    GlyphVector gv;
 | 
						|
    synchronized (searchTextKey)
 | 
						|
      {
 | 
						|
        TextCacheKey tck = searchTextKey;
 | 
						|
        FontRenderContext frc = getFontRenderContext();
 | 
						|
        tck.setString(text);
 | 
						|
        tck.setFont(font);
 | 
						|
        tck.setFontRenderContext(frc);
 | 
						|
        if (gvCache.containsKey(tck))
 | 
						|
          {
 | 
						|
            gv = gvCache.get(tck);
 | 
						|
          }
 | 
						|
        else
 | 
						|
          {
 | 
						|
            gv = font.createGlyphVector(frc, text.toCharArray());
 | 
						|
            gvCache.put(new TextCacheKey(text, font, frc), gv);
 | 
						|
          }
 | 
						|
      }
 | 
						|
    drawGlyphVector(gv, x, y);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws the specified string at the specified location.
 | 
						|
   *
 | 
						|
   * @param text the string to draw
 | 
						|
   * @param x the x location, relative to the bounding rectangle of the text
 | 
						|
   * @param y the y location, relative to the bounding rectangle of the text
 | 
						|
   */
 | 
						|
  public void drawString(String text, float x, float y)
 | 
						|
  {
 | 
						|
    FontRenderContext ctx = getFontRenderContext();
 | 
						|
    GlyphVector gv = font.createGlyphVector(ctx, text.toCharArray());
 | 
						|
    drawGlyphVector(gv, x, y);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws the specified string (as AttributedCharacterIterator) at the
 | 
						|
   * specified location.
 | 
						|
   *
 | 
						|
   * @param iterator the string to draw
 | 
						|
   * @param x the x location, relative to the bounding rectangle of the text
 | 
						|
   * @param y the y location, relative to the bounding rectangle of the text
 | 
						|
   */
 | 
						|
  public void drawString(AttributedCharacterIterator iterator, int x, int y)
 | 
						|
  {
 | 
						|
    FontRenderContext ctx = getFontRenderContext();
 | 
						|
    GlyphVector gv = font.createGlyphVector(ctx, iterator);
 | 
						|
    drawGlyphVector(gv, x, y);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws the specified string (as AttributedCharacterIterator) at the
 | 
						|
   * specified location.
 | 
						|
   *
 | 
						|
   * @param iterator the string to draw
 | 
						|
   * @param x the x location, relative to the bounding rectangle of the text
 | 
						|
   * @param y the y location, relative to the bounding rectangle of the text
 | 
						|
   */
 | 
						|
  public void drawString(AttributedCharacterIterator iterator, float x, float y)
 | 
						|
  {
 | 
						|
    FontRenderContext ctx = getFontRenderContext();
 | 
						|
    GlyphVector gv = font.createGlyphVector(ctx, iterator);
 | 
						|
    drawGlyphVector(gv, x, y);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Fills the specified shape with the current foreground.
 | 
						|
   *
 | 
						|
   * @param shape the shape to fill
 | 
						|
   */
 | 
						|
  public void fill(Shape shape)
 | 
						|
  {
 | 
						|
    fillShape(shape, false);
 | 
						|
  }
 | 
						|
 | 
						|
  public boolean hit(Rectangle rect, Shape text, boolean onStroke)
 | 
						|
  {
 | 
						|
    // FIXME: Implement this.
 | 
						|
    throw new UnsupportedOperationException("Not yet implemented");
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the composite.
 | 
						|
   *
 | 
						|
   * @param comp the composite to set
 | 
						|
   */
 | 
						|
  public void setComposite(Composite comp)
 | 
						|
  {
 | 
						|
    if (! (comp instanceof AlphaComposite))
 | 
						|
      {
 | 
						|
        // FIXME: this check is only required "if this Graphics2D
 | 
						|
        // context is drawing to a Component on the display screen".
 | 
						|
        SecurityManager sm = System.getSecurityManager();
 | 
						|
        if (sm != null)
 | 
						|
          sm.checkPermission(new AWTPermission("readDisplayPixels"));
 | 
						|
      }
 | 
						|
 | 
						|
    composite = comp;
 | 
						|
    if (! (comp.equals(AlphaComposite.SrcOver)))
 | 
						|
      isOptimized = false;
 | 
						|
    else
 | 
						|
      updateOptimization();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the current foreground.
 | 
						|
   *
 | 
						|
   * @param p the foreground to set.
 | 
						|
   */
 | 
						|
  public void setPaint(Paint p)
 | 
						|
  {
 | 
						|
    if (p != null)
 | 
						|
      {
 | 
						|
        paint = p;
 | 
						|
 | 
						|
        if (! (paint instanceof Color))
 | 
						|
          {
 | 
						|
            isOptimized = false;
 | 
						|
          }
 | 
						|
        else
 | 
						|
          {
 | 
						|
            this.foreground = (Color) paint;
 | 
						|
            isForegroundColorNull = false;
 | 
						|
            updateOptimization();
 | 
						|
          }
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        this.foreground = Color.BLACK;
 | 
						|
        isForegroundColorNull = true;
 | 
						|
      }
 | 
						|
 | 
						|
    // free resources if needed, then put the paint context to null
 | 
						|
    if (this.paintContext != null)
 | 
						|
      this.paintContext.dispose();
 | 
						|
 | 
						|
    this.paintContext = null;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the stroke for this graphics object.
 | 
						|
   *
 | 
						|
   * @param s the stroke to set
 | 
						|
   */
 | 
						|
  public void setStroke(Stroke s)
 | 
						|
  {
 | 
						|
    stroke = s;
 | 
						|
    if (! stroke.equals(new BasicStroke()))
 | 
						|
      isOptimized = false;
 | 
						|
    else
 | 
						|
      updateOptimization();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the specified rendering hint.
 | 
						|
   *
 | 
						|
   * @param hintKey the key of the rendering hint
 | 
						|
   * @param hintValue the value
 | 
						|
   */
 | 
						|
  public void setRenderingHint(Key hintKey, Object hintValue)
 | 
						|
  {
 | 
						|
    renderingHints.put(hintKey, hintValue);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the rendering hint for the specified key.
 | 
						|
   *
 | 
						|
   * @param hintKey the rendering hint key
 | 
						|
   *
 | 
						|
   * @return the rendering hint for the specified key
 | 
						|
   */
 | 
						|
  public Object getRenderingHint(Key hintKey)
 | 
						|
  {
 | 
						|
    return renderingHints.get(hintKey);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the specified rendering hints.
 | 
						|
   *
 | 
						|
   * @param hints the rendering hints to set
 | 
						|
   */
 | 
						|
  public void setRenderingHints(Map hints)
 | 
						|
  {
 | 
						|
    renderingHints.clear();
 | 
						|
    renderingHints.putAll(hints);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Adds the specified rendering hints.
 | 
						|
   *
 | 
						|
   * @param hints the rendering hints to add
 | 
						|
   */
 | 
						|
  public void addRenderingHints(Map hints)
 | 
						|
  {
 | 
						|
    renderingHints.putAll(hints);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the current rendering hints.
 | 
						|
   *
 | 
						|
   * @return the current rendering hints
 | 
						|
   */
 | 
						|
  public RenderingHints getRenderingHints()
 | 
						|
  {
 | 
						|
    return (RenderingHints) renderingHints.clone();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Translates the coordinate system by (x, y).
 | 
						|
   *
 | 
						|
   * @param x the translation X coordinate
 | 
						|
   * @param y the translation Y coordinate
 | 
						|
   */
 | 
						|
  public void translate(int x, int y)
 | 
						|
  {
 | 
						|
    transform.translate(x, y);
 | 
						|
 | 
						|
    // Update the clip. We special-case rectangular clips here, because they
 | 
						|
    // are so common (e.g. in Swing).
 | 
						|
    if (clip != null)
 | 
						|
      {
 | 
						|
        if (clip instanceof Rectangle)
 | 
						|
          {
 | 
						|
            Rectangle r = (Rectangle) clip;
 | 
						|
            r.x -= x;
 | 
						|
            r.y -= y;
 | 
						|
            setClip(r);
 | 
						|
          }
 | 
						|
        else
 | 
						|
          {
 | 
						|
            AffineTransform clipTransform = new AffineTransform();
 | 
						|
            clipTransform.translate(-x, -y);
 | 
						|
            updateClip(clipTransform);
 | 
						|
          }
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Translates the coordinate system by (tx, ty).
 | 
						|
   *
 | 
						|
   * @param tx the translation X coordinate
 | 
						|
   * @param ty the translation Y coordinate
 | 
						|
   */
 | 
						|
  public void translate(double tx, double ty)
 | 
						|
  {
 | 
						|
    transform.translate(tx, ty);
 | 
						|
 | 
						|
    // Update the clip. We special-case rectangular clips here, because they
 | 
						|
    // are so common (e.g. in Swing).
 | 
						|
    if (clip != null)
 | 
						|
      {
 | 
						|
        if (clip instanceof Rectangle)
 | 
						|
          {
 | 
						|
            Rectangle r = (Rectangle) clip;
 | 
						|
            r.x -= tx;
 | 
						|
            r.y -= ty;
 | 
						|
          }
 | 
						|
        else
 | 
						|
          {
 | 
						|
            AffineTransform clipTransform = new AffineTransform();
 | 
						|
            clipTransform.translate(-tx, -ty);
 | 
						|
            updateClip(clipTransform);
 | 
						|
          }
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Rotates the coordinate system by <code>theta</code> degrees.
 | 
						|
   *
 | 
						|
   * @param theta the angle be which to rotate the coordinate system
 | 
						|
   */
 | 
						|
  public void rotate(double theta)
 | 
						|
  {
 | 
						|
    transform.rotate(theta);
 | 
						|
    if (clip != null)
 | 
						|
      {
 | 
						|
        AffineTransform clipTransform = new AffineTransform();
 | 
						|
        clipTransform.rotate(-theta);
 | 
						|
        updateClip(clipTransform);
 | 
						|
      }
 | 
						|
    updateOptimization();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Rotates the coordinate system by <code>theta</code> around the point
 | 
						|
   * (x, y).
 | 
						|
   *
 | 
						|
   * @param theta the angle by which to rotate the coordinate system
 | 
						|
   * @param x the point around which to rotate, X coordinate
 | 
						|
   * @param y the point around which to rotate, Y coordinate
 | 
						|
   */
 | 
						|
  public void rotate(double theta, double x, double y)
 | 
						|
  {
 | 
						|
    transform.rotate(theta, x, y);
 | 
						|
    if (clip != null)
 | 
						|
      {
 | 
						|
        AffineTransform clipTransform = new AffineTransform();
 | 
						|
        clipTransform.rotate(-theta, x, y);
 | 
						|
        updateClip(clipTransform);
 | 
						|
      }
 | 
						|
    updateOptimization();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Scales the coordinate system by the factors <code>scaleX</code> and
 | 
						|
   * <code>scaleY</code>.
 | 
						|
   *
 | 
						|
   * @param scaleX the factor by which to scale the X axis
 | 
						|
   * @param scaleY the factor by which to scale the Y axis
 | 
						|
   */
 | 
						|
  public void scale(double scaleX, double scaleY)
 | 
						|
  {
 | 
						|
    transform.scale(scaleX, scaleY);
 | 
						|
    if (clip != null)
 | 
						|
      {
 | 
						|
        AffineTransform clipTransform = new AffineTransform();
 | 
						|
        clipTransform.scale(1 / scaleX, 1 / scaleY);
 | 
						|
        updateClip(clipTransform);
 | 
						|
      }
 | 
						|
    updateOptimization();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Shears the coordinate system by <code>shearX</code> and
 | 
						|
   * <code>shearY</code>.
 | 
						|
   *
 | 
						|
   * @param shearX the X shearing
 | 
						|
   * @param shearY the Y shearing
 | 
						|
   */
 | 
						|
  public void shear(double shearX, double shearY)
 | 
						|
  {
 | 
						|
    transform.shear(shearX, shearY);
 | 
						|
    if (clip != null)
 | 
						|
      {
 | 
						|
        AffineTransform clipTransform = new AffineTransform();
 | 
						|
        clipTransform.shear(-shearX, -shearY);
 | 
						|
        updateClip(clipTransform);
 | 
						|
      }
 | 
						|
    updateOptimization();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Transforms the coordinate system using the specified transform
 | 
						|
   * <code>t</code>.
 | 
						|
   *
 | 
						|
   * @param t the transform
 | 
						|
   */
 | 
						|
  public void transform(AffineTransform t)
 | 
						|
  {
 | 
						|
    transform.concatenate(t);
 | 
						|
    try
 | 
						|
      {
 | 
						|
        AffineTransform clipTransform = t.createInverse();
 | 
						|
        updateClip(clipTransform);
 | 
						|
      }
 | 
						|
    catch (NoninvertibleTransformException ex)
 | 
						|
      {
 | 
						|
        // TODO: How can we deal properly with this?
 | 
						|
        ex.printStackTrace();
 | 
						|
      }
 | 
						|
    updateOptimization();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the transformation for this Graphics object.
 | 
						|
   *
 | 
						|
   * @param t the transformation to set
 | 
						|
   */
 | 
						|
  public void setTransform(AffineTransform t)
 | 
						|
  {
 | 
						|
    // Transform clip into target space using the old transform.
 | 
						|
    updateClip(transform);
 | 
						|
    transform.setTransform(t);
 | 
						|
    // Transform the clip back into user space using the inverse new transform.
 | 
						|
    try
 | 
						|
      {
 | 
						|
        updateClip(transform.createInverse());
 | 
						|
      }
 | 
						|
    catch (NoninvertibleTransformException ex)
 | 
						|
      {
 | 
						|
        // TODO: How can we deal properly with this?
 | 
						|
        ex.printStackTrace();
 | 
						|
      }
 | 
						|
    updateOptimization();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the transformation of this coordinate system.
 | 
						|
   *
 | 
						|
   * @return the transformation of this coordinate system
 | 
						|
   */
 | 
						|
  public AffineTransform getTransform()
 | 
						|
  {
 | 
						|
    return (AffineTransform) transform.clone();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the current foreground.
 | 
						|
   *
 | 
						|
   * @return the current foreground
 | 
						|
   */
 | 
						|
  public Paint getPaint()
 | 
						|
  {
 | 
						|
    return paint;
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the current composite.
 | 
						|
   *
 | 
						|
   * @return the current composite
 | 
						|
   */
 | 
						|
  public Composite getComposite()
 | 
						|
  {
 | 
						|
    return composite;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the current background.
 | 
						|
   *
 | 
						|
   * @param color the background to set.
 | 
						|
   */
 | 
						|
  public void setBackground(Color color)
 | 
						|
  {
 | 
						|
    background = color;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the current background.
 | 
						|
   *
 | 
						|
   * @return the current background
 | 
						|
   */
 | 
						|
  public Color getBackground()
 | 
						|
  {
 | 
						|
    return background;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the current stroke.
 | 
						|
   *
 | 
						|
   * @return the current stroke
 | 
						|
   */
 | 
						|
  public Stroke getStroke()
 | 
						|
  {
 | 
						|
    return stroke;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Intersects the clip of this graphics object with the specified clip.
 | 
						|
   *
 | 
						|
   * @param s the clip with which the current clip should be intersected
 | 
						|
   */
 | 
						|
  public void clip(Shape s)
 | 
						|
  {
 | 
						|
    // Initialize clip if not already present.
 | 
						|
    if (clip == null)
 | 
						|
      setClip(s);
 | 
						|
 | 
						|
    // This is so common, let's optimize this.
 | 
						|
    else if (clip instanceof Rectangle && s instanceof Rectangle)
 | 
						|
      {
 | 
						|
        Rectangle clipRect = (Rectangle) clip;
 | 
						|
        Rectangle r = (Rectangle) s;
 | 
						|
        computeIntersection(r.x, r.y, r.width, r.height, clipRect);
 | 
						|
        // Call setClip so that subclasses get notified.
 | 
						|
        setClip(clipRect);
 | 
						|
      }
 | 
						|
   else
 | 
						|
     {
 | 
						|
       Area current;
 | 
						|
       if (clip instanceof Area)
 | 
						|
         current = (Area) clip;
 | 
						|
       else
 | 
						|
         current = new Area(clip);
 | 
						|
 | 
						|
       Area intersect;
 | 
						|
       if (s instanceof Area)
 | 
						|
         intersect = (Area) s;
 | 
						|
       else
 | 
						|
         intersect = new Area(s);
 | 
						|
 | 
						|
       current.intersect(intersect);
 | 
						|
       clip = current;
 | 
						|
       isOptimized = false;
 | 
						|
       // Call setClip so that subclasses get notified.
 | 
						|
       setClip(clip);
 | 
						|
     }
 | 
						|
  }
 | 
						|
 | 
						|
  public FontRenderContext getFontRenderContext()
 | 
						|
  {
 | 
						|
    // Protect our own transform from beeing modified.
 | 
						|
    AffineTransform tf = new AffineTransform(transform);
 | 
						|
    // TODO: Determine antialias and fractionalmetrics parameters correctly.
 | 
						|
    return new FontRenderContext(tf, false, true);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws the specified glyph vector at the specified location.
 | 
						|
   *
 | 
						|
   * @param gv the glyph vector to draw
 | 
						|
   * @param x the location, x coordinate
 | 
						|
   * @param y the location, y coordinate
 | 
						|
   */
 | 
						|
  public void drawGlyphVector(GlyphVector gv, float x, float y)
 | 
						|
  {
 | 
						|
    translate(x, y);
 | 
						|
    fillShape(gv.getOutline(), true);
 | 
						|
    translate(-x, -y);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a copy of this graphics object.
 | 
						|
   *
 | 
						|
   * @return a copy of this graphics object
 | 
						|
   */
 | 
						|
  public Graphics create()
 | 
						|
  {
 | 
						|
    AbstractGraphics2D copy = (AbstractGraphics2D) clone();
 | 
						|
    return copy;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates and returns a copy of this Graphics object. This should
 | 
						|
   * be overridden by subclasses if additional state must be handled when
 | 
						|
   * cloning. This is called by {@link #create()}.
 | 
						|
   *
 | 
						|
   * @return a copy of this Graphics object
 | 
						|
   */
 | 
						|
  protected Object clone()
 | 
						|
  {
 | 
						|
    try
 | 
						|
      {
 | 
						|
        AbstractGraphics2D copy = (AbstractGraphics2D) super.clone();
 | 
						|
        // Copy the clip. If it's a Rectangle, preserve that for optimization.
 | 
						|
        if (clip instanceof Rectangle)
 | 
						|
          copy.clip = new Rectangle((Rectangle) clip);
 | 
						|
        else if (clip != null)
 | 
						|
          copy.clip = new GeneralPath(clip);
 | 
						|
        else
 | 
						|
          copy.clip = null;
 | 
						|
 | 
						|
        copy.renderingHints = new RenderingHints(null);
 | 
						|
        copy.renderingHints.putAll(renderingHints);
 | 
						|
        copy.transform = new AffineTransform(transform);
 | 
						|
        // The remaining state is inmmutable and doesn't need to be copied.
 | 
						|
        return copy;
 | 
						|
      }
 | 
						|
    catch (CloneNotSupportedException ex)
 | 
						|
      {
 | 
						|
        AWTError err = new AWTError("Unexpected exception while cloning");
 | 
						|
        err.initCause(ex);
 | 
						|
        throw err;
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the current foreground.
 | 
						|
   */
 | 
						|
  public Color getColor()
 | 
						|
  {
 | 
						|
    if (isForegroundColorNull)
 | 
						|
      return null;
 | 
						|
 | 
						|
    return this.foreground;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the current foreground.
 | 
						|
   *
 | 
						|
   * @param color the foreground to set
 | 
						|
   */
 | 
						|
  public void setColor(Color color)
 | 
						|
  {
 | 
						|
    this.setPaint(color);
 | 
						|
  }
 | 
						|
 | 
						|
  public void setPaintMode()
 | 
						|
  {
 | 
						|
    // FIXME: Implement this.
 | 
						|
    throw new UnsupportedOperationException("Not yet implemented");
 | 
						|
  }
 | 
						|
 | 
						|
  public void setXORMode(Color color)
 | 
						|
  {
 | 
						|
    // FIXME: Implement this.
 | 
						|
    throw new UnsupportedOperationException("Not yet implemented");
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the current font.
 | 
						|
   *
 | 
						|
   * @return the current font
 | 
						|
   */
 | 
						|
  public Font getFont()
 | 
						|
  {
 | 
						|
    return font;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the font on this graphics object. When <code>f == null</code>, the
 | 
						|
   * current setting is not changed.
 | 
						|
   *
 | 
						|
   * @param f the font to set
 | 
						|
   */
 | 
						|
  public void setFont(Font f)
 | 
						|
  {
 | 
						|
    if (f != null)
 | 
						|
      font = f;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the font metrics for the specified font.
 | 
						|
   *
 | 
						|
   * @param font the font for which to fetch the font metrics
 | 
						|
   *
 | 
						|
   * @return the font metrics for the specified font
 | 
						|
   */
 | 
						|
  public FontMetrics getFontMetrics(Font font)
 | 
						|
  {
 | 
						|
    return Toolkit.getDefaultToolkit().getFontMetrics(font);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the bounds of the current clip.
 | 
						|
   *
 | 
						|
   * @return the bounds of the current clip
 | 
						|
   */
 | 
						|
  public Rectangle getClipBounds()
 | 
						|
  {
 | 
						|
    Rectangle b = null;
 | 
						|
    if (clip != null)
 | 
						|
      b = clip.getBounds();
 | 
						|
    return b;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Intersects the current clipping region with the specified rectangle.
 | 
						|
   *
 | 
						|
   * @param x the x coordinate of the rectangle
 | 
						|
   * @param y the y coordinate of the rectangle
 | 
						|
   * @param width the width of the rectangle
 | 
						|
   * @param height the height of the rectangle
 | 
						|
   */
 | 
						|
  public void clipRect(int x, int y, int width, int height)
 | 
						|
  {
 | 
						|
    clip(new Rectangle(x, y, width, height));
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the clip to the specified rectangle.
 | 
						|
   *
 | 
						|
   * @param x the x coordinate of the clip rectangle
 | 
						|
   * @param y the y coordinate of the clip rectangle
 | 
						|
   * @param width the width of the clip rectangle
 | 
						|
   * @param height the height of the clip rectangle
 | 
						|
   */
 | 
						|
  public void setClip(int x, int y, int width, int height)
 | 
						|
  {
 | 
						|
    setClip(new Rectangle(x, y, width, height));
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the current clip.
 | 
						|
   *
 | 
						|
   * @return the current clip
 | 
						|
   */
 | 
						|
  public Shape getClip()
 | 
						|
  {
 | 
						|
    return clip;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the current clipping area to <code>clip</code>.
 | 
						|
   *
 | 
						|
   * @param c the clip to set
 | 
						|
   */
 | 
						|
  public void setClip(Shape c)
 | 
						|
  {
 | 
						|
    clip = c;
 | 
						|
    if (! (clip instanceof Rectangle))
 | 
						|
      isOptimized = false;
 | 
						|
    else
 | 
						|
      updateOptimization();
 | 
						|
  }
 | 
						|
 | 
						|
  public void copyArea(int x, int y, int width, int height, int dx, int dy)
 | 
						|
  {
 | 
						|
    if (isOptimized)
 | 
						|
      rawCopyArea(x, y, width, height, dx, dy);
 | 
						|
    else
 | 
						|
      copyAreaImpl(x, y, width, height, dx, dy);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws a line from (x1, y1) to (x2, y2).
 | 
						|
   *
 | 
						|
   * This implementation transforms the coordinates and forwards the call to
 | 
						|
   * {@link #rawDrawLine}.
 | 
						|
   */
 | 
						|
  public void drawLine(int x1, int y1, int x2, int y2)
 | 
						|
  {
 | 
						|
    if (isOptimized)
 | 
						|
      {
 | 
						|
        int tx = (int) transform.getTranslateX();
 | 
						|
        int ty = (int) transform.getTranslateY();
 | 
						|
        rawDrawLine(x1 + tx, y1 + ty, x2 + tx, y2 + ty);
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        ShapeCache sc = shapeCache;
 | 
						|
        if (sc.line == null)
 | 
						|
          sc.line = new Line2D.Float();
 | 
						|
        sc.line.setLine(x1, y1, x2, y2);
 | 
						|
        draw(sc.line);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  public void drawRect(int x, int y, int w, int h)
 | 
						|
  {
 | 
						|
    if (isOptimized)
 | 
						|
      {
 | 
						|
        int tx = (int) transform.getTranslateX();
 | 
						|
        int ty = (int) transform.getTranslateY();
 | 
						|
        rawDrawRect(x + tx, y + ty, w, h);
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        ShapeCache sc = shapeCache;
 | 
						|
        if (sc.rect == null)
 | 
						|
          sc.rect = new Rectangle();
 | 
						|
        sc.rect.setBounds(x, y, w, h);
 | 
						|
        draw(sc.rect);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Fills a rectangle with the current paint.
 | 
						|
   *
 | 
						|
   * @param x the upper left corner, X coordinate
 | 
						|
   * @param y the upper left corner, Y coordinate
 | 
						|
   * @param width the width of the rectangle
 | 
						|
   * @param height the height of the rectangle
 | 
						|
   */
 | 
						|
  public void fillRect(int x, int y, int width, int height)
 | 
						|
  {
 | 
						|
    if (isOptimized)
 | 
						|
      {
 | 
						|
        rawFillRect(x + (int) transform.getTranslateX(),
 | 
						|
                    y + (int) transform.getTranslateY(), width, height);
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        ShapeCache sc = shapeCache;
 | 
						|
        if (sc.rect == null)
 | 
						|
          sc.rect = new Rectangle();
 | 
						|
        sc.rect.setBounds(x, y, width, height);
 | 
						|
        fill(sc.rect);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Fills a rectangle with the current background color.
 | 
						|
   *
 | 
						|
   * This implementation temporarily sets the foreground color to the
 | 
						|
   * background and forwards the call to {@link #fillRect(int, int, int, int)}.
 | 
						|
   *
 | 
						|
   * @param x the upper left corner, X coordinate
 | 
						|
   * @param y the upper left corner, Y coordinate
 | 
						|
   * @param width the width of the rectangle
 | 
						|
   * @param height the height of the rectangle
 | 
						|
   */
 | 
						|
  public void clearRect(int x, int y, int width, int height)
 | 
						|
  {
 | 
						|
    if (isOptimized)
 | 
						|
      rawClearRect(x, y, width, height);
 | 
						|
    else
 | 
						|
      {
 | 
						|
        Paint savedForeground = getPaint();
 | 
						|
        setPaint(getBackground());
 | 
						|
        fillRect(x, y, width, height);
 | 
						|
        setPaint(savedForeground);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws a rounded rectangle.
 | 
						|
   *
 | 
						|
   * @param x the x coordinate of the rectangle
 | 
						|
   * @param y the y coordinate of the rectangle
 | 
						|
   * @param width the width of the rectangle
 | 
						|
   * @param height the height of the rectangle
 | 
						|
   * @param arcWidth the width of the arcs
 | 
						|
   * @param arcHeight the height of the arcs
 | 
						|
   */
 | 
						|
  public void drawRoundRect(int x, int y, int width, int height, int arcWidth,
 | 
						|
                            int arcHeight)
 | 
						|
  {
 | 
						|
    ShapeCache sc = shapeCache;
 | 
						|
    if (sc.roundRect == null)
 | 
						|
      sc.roundRect = new RoundRectangle2D.Float();
 | 
						|
    sc.roundRect.setRoundRect(x, y, width, height, arcWidth, arcHeight);
 | 
						|
    draw(sc.roundRect);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Fills a rounded rectangle.
 | 
						|
   *
 | 
						|
   * @param x the x coordinate of the rectangle
 | 
						|
   * @param y the y coordinate of the rectangle
 | 
						|
   * @param width the width of the rectangle
 | 
						|
   * @param height the height of the rectangle
 | 
						|
   * @param arcWidth the width of the arcs
 | 
						|
   * @param arcHeight the height of the arcs
 | 
						|
   */
 | 
						|
  public void fillRoundRect(int x, int y, int width, int height, int arcWidth,
 | 
						|
                            int arcHeight)
 | 
						|
  {
 | 
						|
    ShapeCache sc = shapeCache;
 | 
						|
    if (sc.roundRect == null)
 | 
						|
      sc.roundRect = new RoundRectangle2D.Float();
 | 
						|
    sc.roundRect.setRoundRect(x, y, width, height, arcWidth, arcHeight);
 | 
						|
    fill(sc.roundRect);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws the outline of an oval.
 | 
						|
   *
 | 
						|
   * @param x the upper left corner of the bounding rectangle of the ellipse
 | 
						|
   * @param y the upper left corner of the bounding rectangle of the ellipse
 | 
						|
   * @param width the width of the ellipse
 | 
						|
   * @param height the height of the ellipse
 | 
						|
   */
 | 
						|
  public void drawOval(int x, int y, int width, int height)
 | 
						|
  {
 | 
						|
    ShapeCache sc = shapeCache;
 | 
						|
    if (sc.ellipse == null)
 | 
						|
      sc.ellipse = new Ellipse2D.Float();
 | 
						|
    sc.ellipse.setFrame(x, y, width, height);
 | 
						|
    draw(sc.ellipse);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Fills an oval.
 | 
						|
   *
 | 
						|
   * @param x the upper left corner of the bounding rectangle of the ellipse
 | 
						|
   * @param y the upper left corner of the bounding rectangle of the ellipse
 | 
						|
   * @param width the width of the ellipse
 | 
						|
   * @param height the height of the ellipse
 | 
						|
   */
 | 
						|
  public void fillOval(int x, int y, int width, int height)
 | 
						|
  {
 | 
						|
    ShapeCache sc = shapeCache;
 | 
						|
    if (sc.ellipse == null)
 | 
						|
      sc.ellipse = new Ellipse2D.Float();
 | 
						|
    sc.ellipse.setFrame(x, y, width, height);
 | 
						|
    fill(sc.ellipse);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws an arc.
 | 
						|
   */
 | 
						|
  public void drawArc(int x, int y, int width, int height, int arcStart,
 | 
						|
                      int arcAngle)
 | 
						|
  {
 | 
						|
    ShapeCache sc = shapeCache;
 | 
						|
    if (sc.arc == null)
 | 
						|
      sc.arc = new Arc2D.Float();
 | 
						|
    sc.arc.setArc(x, y, width, height, arcStart, arcAngle, Arc2D.OPEN);
 | 
						|
    draw(sc.arc);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Fills an arc.
 | 
						|
   */
 | 
						|
  public void fillArc(int x, int y, int width, int height, int arcStart,
 | 
						|
                      int arcAngle)
 | 
						|
  {
 | 
						|
    ShapeCache sc = shapeCache;
 | 
						|
    if (sc.arc == null)
 | 
						|
      sc.arc = new Arc2D.Float();
 | 
						|
    sc.arc.setArc(x, y, width, height, arcStart, arcAngle, Arc2D.PIE);
 | 
						|
    draw(sc.arc);
 | 
						|
  }
 | 
						|
 | 
						|
  public void drawPolyline(int[] xPoints, int[] yPoints, int npoints)
 | 
						|
  {
 | 
						|
    ShapeCache sc = shapeCache;
 | 
						|
    if (sc.polyline == null)
 | 
						|
      sc.polyline = new GeneralPath();
 | 
						|
    GeneralPath p = sc.polyline;
 | 
						|
    p.reset();
 | 
						|
    if (npoints > 0)
 | 
						|
      p.moveTo(xPoints[0], yPoints[0]);
 | 
						|
    for (int i = 1; i < npoints; i++)
 | 
						|
      p.lineTo(xPoints[i], yPoints[i]);
 | 
						|
    fill(p);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws the outline of a polygon.
 | 
						|
   */
 | 
						|
  public void drawPolygon(int[] xPoints, int[] yPoints, int npoints)
 | 
						|
  {
 | 
						|
    ShapeCache sc = shapeCache;
 | 
						|
    if (sc.polygon == null)
 | 
						|
      sc.polygon = new Polygon();
 | 
						|
    sc.polygon.reset();
 | 
						|
    sc.polygon.xpoints = xPoints;
 | 
						|
    sc.polygon.ypoints = yPoints;
 | 
						|
    sc.polygon.npoints = npoints;
 | 
						|
    draw(sc.polygon);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Fills the outline of a polygon.
 | 
						|
   */
 | 
						|
  public void fillPolygon(int[] xPoints, int[] yPoints, int npoints)
 | 
						|
  {
 | 
						|
    ShapeCache sc = shapeCache;
 | 
						|
    if (sc.polygon == null)
 | 
						|
      sc.polygon = new Polygon();
 | 
						|
    sc.polygon.reset();
 | 
						|
    sc.polygon.xpoints = xPoints;
 | 
						|
    sc.polygon.ypoints = yPoints;
 | 
						|
    sc.polygon.npoints = npoints;
 | 
						|
    fill(sc.polygon);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws the specified image at the specified location. This forwards
 | 
						|
   * to {@link #drawImage(Image, AffineTransform, ImageObserver)}.
 | 
						|
   *
 | 
						|
   * @param image the image to render
 | 
						|
   * @param x the x location to render to
 | 
						|
   * @param y the y location to render to
 | 
						|
   * @param observer the image observer to receive notification
 | 
						|
   */
 | 
						|
  public boolean drawImage(Image image, int x, int y, ImageObserver observer)
 | 
						|
  {
 | 
						|
    boolean ret;
 | 
						|
    if (isOptimized)
 | 
						|
      {
 | 
						|
        ret = rawDrawImage(image, x + (int) transform.getTranslateX(),
 | 
						|
                           y + (int) transform.getTranslateY(), observer);
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        AffineTransform t = new AffineTransform();
 | 
						|
        t.translate(x, y);
 | 
						|
        ret = drawImage(image, t, observer);
 | 
						|
      }
 | 
						|
    return ret;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws the specified image at the specified location. The image
 | 
						|
   * is scaled to the specified width and height. This forwards
 | 
						|
   * to {@link #drawImage(Image, AffineTransform, ImageObserver)}.
 | 
						|
   *
 | 
						|
   * @param image the image to render
 | 
						|
   * @param x the x location to render to
 | 
						|
   * @param y the y location to render to
 | 
						|
   * @param width the target width of the image
 | 
						|
   * @param height the target height of the image
 | 
						|
   * @param observer the image observer to receive notification
 | 
						|
   */
 | 
						|
  public boolean drawImage(Image image, int x, int y, int width, int height,
 | 
						|
                           ImageObserver observer)
 | 
						|
  {
 | 
						|
    AffineTransform t = new AffineTransform();
 | 
						|
    int imWidth = image.getWidth(observer);
 | 
						|
    int imHeight = image.getHeight(observer);
 | 
						|
    if (imWidth == width && imHeight == height)
 | 
						|
      {
 | 
						|
        // No need to scale, fall back to non-scaling loops.
 | 
						|
        return drawImage(image, x, y, observer);
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        Image scaled = prepareImage(image, width, height);
 | 
						|
        // Ideally, this should notify the observer about the scaling progress.
 | 
						|
        return drawImage(scaled, x, y, observer);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws the specified image at the specified location. This forwards
 | 
						|
   * to {@link #drawImage(Image, AffineTransform, ImageObserver)}.
 | 
						|
   *
 | 
						|
   * @param image the image to render
 | 
						|
   * @param x the x location to render to
 | 
						|
   * @param y the y location to render to
 | 
						|
   * @param bgcolor the background color to use for transparent pixels
 | 
						|
   * @param observer the image observer to receive notification
 | 
						|
   */
 | 
						|
  public boolean drawImage(Image image, int x, int y, Color bgcolor,
 | 
						|
                           ImageObserver observer)
 | 
						|
  {
 | 
						|
    AffineTransform t = new AffineTransform();
 | 
						|
    t.translate(x, y);
 | 
						|
    // TODO: Somehow implement the background option.
 | 
						|
    return drawImage(image, t, observer);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws the specified image at the specified location. The image
 | 
						|
   * is scaled to the specified width and height. This forwards
 | 
						|
   * to {@link #drawImage(Image, AffineTransform, ImageObserver)}.
 | 
						|
   *
 | 
						|
   * @param image the image to render
 | 
						|
   * @param x the x location to render to
 | 
						|
   * @param y the y location to render to
 | 
						|
   * @param width the target width of the image
 | 
						|
   * @param height the target height of the image
 | 
						|
   * @param bgcolor the background color to use for transparent pixels
 | 
						|
   * @param observer the image observer to receive notification
 | 
						|
   */
 | 
						|
  public boolean drawImage(Image image, int x, int y, int width, int height,
 | 
						|
                           Color bgcolor, ImageObserver observer)
 | 
						|
  {
 | 
						|
    AffineTransform t = new AffineTransform();
 | 
						|
    t.translate(x, y);
 | 
						|
    double scaleX = (double) image.getWidth(observer) / (double) width;
 | 
						|
    double scaleY = (double) image.getHeight(observer) / (double) height;
 | 
						|
    t.scale(scaleX, scaleY);
 | 
						|
    // TODO: Somehow implement the background option.
 | 
						|
    return drawImage(image, t, observer);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws an image fragment to a rectangular area of the target.
 | 
						|
   *
 | 
						|
   * @param image the image to render
 | 
						|
   * @param dx1 the first corner of the destination rectangle
 | 
						|
   * @param dy1 the first corner of the destination rectangle
 | 
						|
   * @param dx2 the second corner of the destination rectangle
 | 
						|
   * @param dy2 the second corner of the destination rectangle
 | 
						|
   * @param sx1 the first corner of the source rectangle
 | 
						|
   * @param sy1 the first corner of the source rectangle
 | 
						|
   * @param sx2 the second corner of the source rectangle
 | 
						|
   * @param sy2 the second corner of the source rectangle
 | 
						|
   * @param observer the image observer to be notified
 | 
						|
   */
 | 
						|
  public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2,
 | 
						|
                           int sx1, int sy1, int sx2, int sy2,
 | 
						|
                           ImageObserver observer)
 | 
						|
  {
 | 
						|
    int sx = Math.min(sx1, sx1);
 | 
						|
    int sy = Math.min(sy1, sy2);
 | 
						|
    int sw = Math.abs(sx1 - sx2);
 | 
						|
    int sh = Math.abs(sy1 - sy2);
 | 
						|
    int dx = Math.min(dx1, dx1);
 | 
						|
    int dy = Math.min(dy1, dy2);
 | 
						|
    int dw = Math.abs(dx1 - dx2);
 | 
						|
    int dh = Math.abs(dy1 - dy2);
 | 
						|
 | 
						|
    AffineTransform t = new AffineTransform();
 | 
						|
    t.translate(sx - dx, sy - dy);
 | 
						|
    double scaleX = (double) sw / (double) dw;
 | 
						|
    double scaleY = (double) sh / (double) dh;
 | 
						|
    t.scale(scaleX, scaleY);
 | 
						|
    Rectangle areaOfInterest = new Rectangle(sx, sy, sw, sh);
 | 
						|
    return drawImageImpl(image, t, observer, areaOfInterest);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws an image fragment to a rectangular area of the target.
 | 
						|
   *
 | 
						|
   * @param image the image to render
 | 
						|
   * @param dx1 the first corner of the destination rectangle
 | 
						|
   * @param dy1 the first corner of the destination rectangle
 | 
						|
   * @param dx2 the second corner of the destination rectangle
 | 
						|
   * @param dy2 the second corner of the destination rectangle
 | 
						|
   * @param sx1 the first corner of the source rectangle
 | 
						|
   * @param sy1 the first corner of the source rectangle
 | 
						|
   * @param sx2 the second corner of the source rectangle
 | 
						|
   * @param sy2 the second corner of the source rectangle
 | 
						|
   * @param bgcolor the background color to use for transparent pixels
 | 
						|
   * @param observer the image observer to be notified
 | 
						|
   */
 | 
						|
  public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2,
 | 
						|
                           int sx1, int sy1, int sx2, int sy2, Color bgcolor,
 | 
						|
                           ImageObserver observer)
 | 
						|
  {
 | 
						|
    // FIXME: Do something with bgcolor.
 | 
						|
    return drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Disposes this graphics object.
 | 
						|
   */
 | 
						|
  public void dispose()
 | 
						|
  {
 | 
						|
    // Nothing special to do here.
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Fills the specified shape. Override this if your backend can efficiently
 | 
						|
   * fill shapes. This is possible on many systems via a polygon fill
 | 
						|
   * method or something similar. But keep in mind that Shapes can be quite
 | 
						|
   * complex (non-convex, with holes etc), which is not necessarily supported
 | 
						|
   * by all polygon fillers. Also note that you must perform clipping
 | 
						|
   * before filling the shape.
 | 
						|
   *
 | 
						|
   * @param s the shape to fill
 | 
						|
   * @param isFont <code>true</code> if the shape is a font outline
 | 
						|
   */
 | 
						|
  protected void fillShape(Shape s, boolean isFont)
 | 
						|
  {
 | 
						|
    // Determine if we need to antialias stuff.
 | 
						|
    boolean antialias = false;
 | 
						|
    if (isFont)
 | 
						|
      {
 | 
						|
        Object v = renderingHints.get(RenderingHints.KEY_TEXT_ANTIALIASING);
 | 
						|
        // We default to antialiasing for text rendering.
 | 
						|
        antialias = v == RenderingHints.VALUE_TEXT_ANTIALIAS_ON
 | 
						|
                    || (v == RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT
 | 
						|
                         && DEFAULT_TEXT_AA);
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        Object v = renderingHints.get(RenderingHints.KEY_ANTIALIASING);
 | 
						|
        antialias = (v == RenderingHints.VALUE_ANTIALIAS_ON);
 | 
						|
      }
 | 
						|
    ScanlineConverter sc = getScanlineConverter();
 | 
						|
    int resolution = 0;
 | 
						|
    int yRes = 0;
 | 
						|
    if (antialias)
 | 
						|
      {
 | 
						|
        // Adjust resolution according to rendering hints.
 | 
						|
        resolution = 2;
 | 
						|
        yRes = 4;
 | 
						|
      }
 | 
						|
    sc.renderShape(this, s, clip, transform, resolution, yRes, renderingHints);
 | 
						|
    freeScanlineConverter(sc);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the color model of this Graphics object.
 | 
						|
   *
 | 
						|
   * @return the color model of this Graphics object
 | 
						|
   */
 | 
						|
  protected abstract ColorModel getColorModel();
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the bounds of the target.
 | 
						|
   *
 | 
						|
   * @return the bounds of the target
 | 
						|
   */
 | 
						|
  protected abstract Rectangle getDeviceBounds();
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws a line in optimization mode. The implementation should respect the
 | 
						|
   * clip and translation. It can assume that the clip is a rectangle and that
 | 
						|
   * the transform is only a translating transform.
 | 
						|
   *
 | 
						|
   * @param x0 the starting point, X coordinate
 | 
						|
   * @param y0 the starting point, Y coordinate
 | 
						|
   * @param x1 the end point, X coordinate
 | 
						|
   * @param y1 the end point, Y coordinate
 | 
						|
   */
 | 
						|
  protected void rawDrawLine(int x0, int y0, int x1, int y1)
 | 
						|
  {
 | 
						|
    ShapeCache sc = shapeCache;
 | 
						|
    if (sc.line == null)
 | 
						|
      sc.line = new Line2D.Float();
 | 
						|
    sc.line.setLine(x0, y0, x1, y1);
 | 
						|
    draw(sc.line);
 | 
						|
  }
 | 
						|
 | 
						|
  protected void rawDrawRect(int x, int y, int w, int h)
 | 
						|
  {
 | 
						|
    ShapeCache sc = shapeCache;
 | 
						|
    if (sc.rect == null)
 | 
						|
      sc.rect = new Rectangle();
 | 
						|
    sc.rect.setBounds(x, y, w, h);
 | 
						|
    draw(sc.rect);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Clears a rectangle in optimization mode. The implementation should respect the
 | 
						|
   * clip and translation. It can assume that the clip is a rectangle and that
 | 
						|
   * the transform is only a translating transform.
 | 
						|
   *
 | 
						|
   * @param x the upper left corner, X coordinate
 | 
						|
   * @param y the upper left corner, Y coordinate
 | 
						|
   * @param w the width
 | 
						|
   * @param h the height
 | 
						|
   */
 | 
						|
  protected void rawClearRect(int x, int y, int w, int h)
 | 
						|
  {
 | 
						|
    Paint savedForeground = getPaint();
 | 
						|
    setPaint(getBackground());
 | 
						|
    rawFillRect(x, y, w, h);
 | 
						|
    setPaint(savedForeground);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Fills a rectangle in optimization mode. The implementation should respect
 | 
						|
   * the clip but can assume that it is a rectangle.
 | 
						|
   *
 | 
						|
   * @param x the upper left corner, X coordinate
 | 
						|
   * @param y the upper left corner, Y coordinate
 | 
						|
   * @param w the width
 | 
						|
   * @param h the height
 | 
						|
   */
 | 
						|
  protected void rawFillRect(int x, int y, int w, int h)
 | 
						|
  {
 | 
						|
    ShapeCache sc = shapeCache;
 | 
						|
    if (sc.rect == null)
 | 
						|
      sc.rect = new Rectangle();
 | 
						|
    sc.rect.setBounds(x, y, w, h);
 | 
						|
    fill(sc.rect);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws an image in optimization mode. The implementation should respect
 | 
						|
   * the clip but can assume that it is a rectangle.
 | 
						|
   *
 | 
						|
   * @param image the image to be painted
 | 
						|
   * @param x the location, X coordinate
 | 
						|
   * @param y the location, Y coordinate
 | 
						|
   * @param obs the image observer to be notified
 | 
						|
   *
 | 
						|
   * @return <code>true</code> when the image is painted completely,
 | 
						|
   *         <code>false</code> if it is still rendered
 | 
						|
   */
 | 
						|
  protected boolean rawDrawImage(Image image, int x, int y, ImageObserver obs)
 | 
						|
  {
 | 
						|
    AffineTransform t = new AffineTransform();
 | 
						|
    t.translate(x, y);
 | 
						|
    return drawImage(image, t, obs);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Copies a rectangular region to another location.
 | 
						|
   *
 | 
						|
   * @param x the upper left corner, X coordinate
 | 
						|
   * @param y the upper left corner, Y coordinate
 | 
						|
   * @param w the width
 | 
						|
   * @param h the height
 | 
						|
   * @param dx
 | 
						|
   * @param dy
 | 
						|
   */
 | 
						|
  protected void rawCopyArea(int x, int y, int w, int h, int dx, int dy)
 | 
						|
  {
 | 
						|
    copyAreaImpl(x, y, w, h, dx, dy);
 | 
						|
  }
 | 
						|
 | 
						|
  // Private implementation methods.
 | 
						|
 | 
						|
  /**
 | 
						|
   * Copies a rectangular area of the target raster to a different location.
 | 
						|
   */
 | 
						|
  private void copyAreaImpl(int x, int y, int w, int h, int dx, int dy)
 | 
						|
  {
 | 
						|
    // FIXME: Implement this properly.
 | 
						|
    throw new UnsupportedOperationException("Not implemented yet.");
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Paints a scanline between x0 and x1. Override this when your backend
 | 
						|
   * can efficiently draw/fill horizontal lines.
 | 
						|
   *
 | 
						|
   * @param x0 the left offset
 | 
						|
   * @param x1 the right offset
 | 
						|
   * @param y the scanline
 | 
						|
   */
 | 
						|
  public void renderScanline(int y, ScanlineCoverage c)
 | 
						|
  {
 | 
						|
    PaintContext pCtx = getPaintContext();
 | 
						|
 | 
						|
    int x0 = c.getMinX();
 | 
						|
    int x1 = c.getMaxX();
 | 
						|
    Raster paintRaster = pCtx.getRaster(x0, y, x1 - x0, 1);
 | 
						|
 | 
						|
    // Do the anti aliasing thing.
 | 
						|
    float coverageAlpha = 0;
 | 
						|
    float maxCoverage = c.getMaxCoverage();
 | 
						|
    ColorModel cm = pCtx.getColorModel();
 | 
						|
    DataBuffer db = paintRaster.getDataBuffer();
 | 
						|
    Point loc = new Point(paintRaster.getMinX(), paintRaster.getMinY());
 | 
						|
    SampleModel sm = paintRaster.getSampleModel();
 | 
						|
    WritableRaster writeRaster = Raster.createWritableRaster(sm, db, loc);
 | 
						|
    WritableRaster alphaRaster = cm.getAlphaRaster(writeRaster);
 | 
						|
    int pixel;
 | 
						|
    ScanlineCoverage.Iterator iter = c.iterate();
 | 
						|
    while (iter.hasNext())
 | 
						|
      {
 | 
						|
        ScanlineCoverage.Range range = iter.next();
 | 
						|
        coverageAlpha = range.getCoverage() / maxCoverage;
 | 
						|
        if (coverageAlpha < 1.0)
 | 
						|
          {
 | 
						|
            for (int x = range.getXPos(); x < range.getXPosEnd(); x++)
 | 
						|
              {
 | 
						|
                pixel = alphaRaster.getSample(x, y, 0);
 | 
						|
                pixel = (int) (pixel * coverageAlpha);
 | 
						|
                alphaRaster.setSample(x, y, 0, pixel);
 | 
						|
              }
 | 
						|
          }
 | 
						|
      }
 | 
						|
    ColorModel paintColorModel = pCtx.getColorModel();
 | 
						|
    CompositeContext cCtx = composite.createContext(paintColorModel,
 | 
						|
                                                    getColorModel(),
 | 
						|
                                                    renderingHints);
 | 
						|
    WritableRaster raster = getDestinationRaster();
 | 
						|
    WritableRaster targetChild = raster.createWritableTranslatedChild(-x0, -y);
 | 
						|
 | 
						|
    cCtx.compose(paintRaster, targetChild, targetChild);
 | 
						|
    updateRaster(raster, x0, y, x1 - x0, 1);
 | 
						|
    cCtx.dispose();
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Initializes this graphics object. This must be called by subclasses in
 | 
						|
   * order to correctly initialize the state of this object.
 | 
						|
   */
 | 
						|
  protected void init()
 | 
						|
  {
 | 
						|
    setPaint(Color.BLACK);
 | 
						|
    setFont(FONT);
 | 
						|
    isOptimized = true;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns a WritableRaster that is used by this class to perform the
 | 
						|
   * rendering in. It is not necessary that the target surface immediately
 | 
						|
   * reflects changes in the raster. Updates to the raster are notified via
 | 
						|
   * {@link #updateRaster}.
 | 
						|
   *
 | 
						|
   * @return the destination raster
 | 
						|
   */
 | 
						|
  protected WritableRaster getDestinationRaster()
 | 
						|
  {
 | 
						|
    // TODO: Ideally we would fetch the xdrawable's surface pixels for
 | 
						|
    // initialization of the raster.
 | 
						|
    Rectangle db = getDeviceBounds();
 | 
						|
    if (destinationRaster == null)
 | 
						|
      {
 | 
						|
        int[] bandMasks = new int[]{ 0xFF0000, 0xFF00, 0xFF };
 | 
						|
        destinationRaster = Raster.createPackedRaster(DataBuffer.TYPE_INT,
 | 
						|
                                                      db.width, db.height,
 | 
						|
                                                      bandMasks, null);
 | 
						|
        // Initialize raster with white.
 | 
						|
        int x0 = destinationRaster.getMinX();
 | 
						|
        int x1 = destinationRaster.getWidth() + x0;
 | 
						|
        int y0 = destinationRaster.getMinY();
 | 
						|
        int y1 = destinationRaster.getHeight() + y0;
 | 
						|
        int numBands = destinationRaster.getNumBands();
 | 
						|
        for (int y = y0; y < y1; y++)
 | 
						|
          {
 | 
						|
            for (int x = x0; x < x1; x++)
 | 
						|
              {
 | 
						|
                for (int b = 0; b < numBands; b++)
 | 
						|
                  destinationRaster.setSample(x, y, b, 255);
 | 
						|
              }
 | 
						|
          }
 | 
						|
      }
 | 
						|
    return destinationRaster;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Notifies the backend that the raster has changed in the specified
 | 
						|
   * rectangular area. The raster that is provided in this method is always
 | 
						|
   * the same as the one returned in {@link #getDestinationRaster}.
 | 
						|
   * Backends that reflect changes to this raster directly don't need to do
 | 
						|
   * anything here.
 | 
						|
   *
 | 
						|
   * @param raster the updated raster, identical to the raster returned
 | 
						|
   *        by {@link #getDestinationRaster()}
 | 
						|
   * @param x the upper left corner of the updated region, X coordinate
 | 
						|
   * @param y the upper lef corner of the updated region, Y coordinate
 | 
						|
   * @param w the width of the updated region
 | 
						|
   * @param h the height of the updated region
 | 
						|
   */
 | 
						|
  protected void updateRaster(Raster raster, int x, int y, int w, int h)
 | 
						|
  {
 | 
						|
    // Nothing to do here. Backends that need to update their surface
 | 
						|
    // to reflect the change should override this method.
 | 
						|
  }
 | 
						|
 | 
						|
  // Some helper methods.
 | 
						|
 | 
						|
  /**
 | 
						|
   * Helper method to check and update the optimization conditions.
 | 
						|
   */
 | 
						|
  private void updateOptimization()
 | 
						|
  {
 | 
						|
    int transformType = transform.getType();
 | 
						|
    boolean optimizedTransform = false;
 | 
						|
    if (transformType == AffineTransform.TYPE_IDENTITY
 | 
						|
        || transformType == AffineTransform.TYPE_TRANSLATION)
 | 
						|
      optimizedTransform = true;
 | 
						|
 | 
						|
    boolean optimizedClip = (clip == null || clip instanceof Rectangle);
 | 
						|
    isOptimized = optimizedClip
 | 
						|
                  && optimizedTransform && paint instanceof Color
 | 
						|
                  && composite == AlphaComposite.SrcOver
 | 
						|
                  && stroke.equals(new BasicStroke());
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Calculates the intersection of two rectangles. The result is stored
 | 
						|
   * in <code>rect</code>. This is basically the same
 | 
						|
   * like {@link Rectangle#intersection(Rectangle)}, only that it does not
 | 
						|
   * create new Rectangle instances. The tradeoff is that you loose any data in
 | 
						|
   * <code>rect</code>.
 | 
						|
   *
 | 
						|
   * @param x upper-left x coodinate of first rectangle
 | 
						|
   * @param y upper-left y coodinate of first rectangle
 | 
						|
   * @param w width of first rectangle
 | 
						|
   * @param h height of first rectangle
 | 
						|
   * @param rect a Rectangle object of the second rectangle
 | 
						|
   *
 | 
						|
   * @throws NullPointerException if rect is null
 | 
						|
   *
 | 
						|
   * @return a rectangle corresponding to the intersection of the
 | 
						|
   *         two rectangles. An empty rectangle is returned if the rectangles
 | 
						|
   *         do not overlap
 | 
						|
   */
 | 
						|
  private static Rectangle computeIntersection(int x, int y, int w, int h,
 | 
						|
                                               Rectangle rect)
 | 
						|
  {
 | 
						|
    int x2 = rect.x;
 | 
						|
    int y2 = rect.y;
 | 
						|
    int w2 = rect.width;
 | 
						|
    int h2 = rect.height;
 | 
						|
 | 
						|
    int dx = (x > x2) ? x : x2;
 | 
						|
    int dy = (y > y2) ? y : y2;
 | 
						|
    int dw = (x + w < x2 + w2) ? (x + w - dx) : (x2 + w2 - dx);
 | 
						|
    int dh = (y + h < y2 + h2) ? (y + h - dy) : (y2 + h2 - dy);
 | 
						|
 | 
						|
    if (dw >= 0 && dh >= 0)
 | 
						|
      rect.setBounds(dx, dy, dw, dh);
 | 
						|
    else
 | 
						|
      rect.setBounds(0, 0, 0, 0);
 | 
						|
 | 
						|
    return rect;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Helper method to transform the clip. This is called by the various
 | 
						|
   * transformation-manipulation methods to update the clip (which is in
 | 
						|
   * userspace) accordingly.
 | 
						|
   *
 | 
						|
   * The transform usually is the inverse transform that was applied to the
 | 
						|
   * graphics object.
 | 
						|
   *
 | 
						|
   * @param t the transform to apply to the clip
 | 
						|
   */
 | 
						|
  private void updateClip(AffineTransform t)
 | 
						|
  {
 | 
						|
    if (! (clip instanceof GeneralPath))
 | 
						|
      clip = new GeneralPath(clip);
 | 
						|
 | 
						|
    GeneralPath p = (GeneralPath) clip;
 | 
						|
    p.transform(t);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns a free scanline converter from the pool.
 | 
						|
   *
 | 
						|
   * @return a scanline converter
 | 
						|
   */
 | 
						|
  private ScanlineConverter getScanlineConverter()
 | 
						|
  {
 | 
						|
    synchronized (scanlineConverters)
 | 
						|
      {
 | 
						|
        ScanlineConverter sc;
 | 
						|
        if (scanlineConverters.size() > 0)
 | 
						|
          {
 | 
						|
            sc = scanlineConverters.removeFirst();
 | 
						|
          }
 | 
						|
        else
 | 
						|
          {
 | 
						|
            sc = new ScanlineConverter();
 | 
						|
          }
 | 
						|
        return sc;
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Puts a scanline converter back in the pool.
 | 
						|
   *
 | 
						|
   * @param sc
 | 
						|
   */
 | 
						|
  private void freeScanlineConverter(ScanlineConverter sc)
 | 
						|
  {
 | 
						|
    synchronized (scanlineConverters)
 | 
						|
      {
 | 
						|
        scanlineConverters.addLast(sc);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  private PaintContext getPaintContext()
 | 
						|
  {
 | 
						|
    if (this.paintContext == null)
 | 
						|
      {
 | 
						|
        this.paintContext =
 | 
						|
          this.foreground.createContext(getColorModel(),
 | 
						|
                                        getDeviceBounds(),
 | 
						|
                                        getClipBounds(),
 | 
						|
                                        getTransform(),
 | 
						|
                                        getRenderingHints());
 | 
						|
      }
 | 
						|
 | 
						|
    return this.paintContext;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Scales an image to the specified width and height. This should also
 | 
						|
   * be used to implement
 | 
						|
   * {@link Toolkit#prepareImage(Image, int, int, ImageObserver)}.
 | 
						|
   * This uses {@link Toolkit#createImage(ImageProducer)} to create the actual
 | 
						|
   * image.
 | 
						|
   *
 | 
						|
   * @param image the image to prepare
 | 
						|
   * @param w the width
 | 
						|
   * @param h the height
 | 
						|
   *
 | 
						|
   * @return the scaled image
 | 
						|
   */
 | 
						|
  public static Image prepareImage(Image image, int w, int h)
 | 
						|
  {
 | 
						|
    // Try to find cached scaled image.
 | 
						|
    HashMap<Dimension,Image> scaledTable = imageCache.get(image);
 | 
						|
    Dimension size = new Dimension(w, h);
 | 
						|
    Image scaled = null;
 | 
						|
    if (scaledTable != null)
 | 
						|
      {
 | 
						|
        scaled = scaledTable.get(size);
 | 
						|
      }
 | 
						|
    if (scaled == null)
 | 
						|
      {
 | 
						|
        // No cached scaled image. Start scaling image now.
 | 
						|
        ImageProducer source = image.getSource();
 | 
						|
        ReplicateScaleFilter scaler = new ReplicateScaleFilter(w, h);
 | 
						|
        FilteredImageSource filteredSource =
 | 
						|
          new FilteredImageSource(source, scaler);
 | 
						|
        // Ideally, this should asynchronously scale the image.
 | 
						|
        Image scaledImage =
 | 
						|
          Toolkit.getDefaultToolkit().createImage(filteredSource);
 | 
						|
        scaled = scaledImage;
 | 
						|
        // Put scaled image in cache.
 | 
						|
        if (scaledTable == null)
 | 
						|
          {
 | 
						|
            scaledTable = new HashMap<Dimension,Image>();
 | 
						|
            imageCache.put(image, scaledTable);
 | 
						|
          }
 | 
						|
        scaledTable.put(size, scaledImage);
 | 
						|
      }
 | 
						|
    return scaled;
 | 
						|
  }
 | 
						|
 | 
						|
}
 |