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;
 | |
|   }
 | |
| 
 | |
| }
 |