jdk/src/share/classes/sun/java2d/SunGraphics2D.java
changeset 2 90ce3da70b43
child 438 2ae294e4518c
child 551 6a51745b2784
equal deleted inserted replaced
0:fd16c54261b3 2:90ce3da70b43
       
     1 /*
       
     2  * Copyright 1996-2007 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 package sun.java2d;
       
    27 
       
    28 import java.awt.Graphics;
       
    29 import java.awt.Graphics2D;
       
    30 import java.awt.RenderingHints;
       
    31 import java.awt.RenderingHints.Key;
       
    32 import java.awt.geom.Area;
       
    33 import java.awt.geom.AffineTransform;
       
    34 import java.awt.geom.NoninvertibleTransformException;
       
    35 import java.awt.AlphaComposite;
       
    36 import java.awt.BasicStroke;
       
    37 import java.awt.image.BufferedImage;
       
    38 import java.awt.image.BufferedImageOp;
       
    39 import java.awt.image.RenderedImage;
       
    40 import java.awt.image.renderable.RenderableImage;
       
    41 import java.awt.image.renderable.RenderContext;
       
    42 import java.awt.image.AffineTransformOp;
       
    43 import java.awt.image.Raster;
       
    44 import java.awt.image.SampleModel;
       
    45 import java.awt.image.VolatileImage;
       
    46 import java.awt.image.WritableRaster;
       
    47 import java.awt.Image;
       
    48 import java.awt.Composite;
       
    49 import java.awt.Color;
       
    50 import java.awt.color.ColorSpace;
       
    51 import java.awt.image.DataBuffer;
       
    52 import java.awt.image.ColorModel;
       
    53 import java.awt.image.IndexColorModel;
       
    54 import java.awt.image.DirectColorModel;
       
    55 import java.awt.GraphicsConfiguration;
       
    56 import java.awt.Paint;
       
    57 import java.awt.GradientPaint;
       
    58 import java.awt.LinearGradientPaint;
       
    59 import java.awt.RadialGradientPaint;
       
    60 import java.awt.TexturePaint;
       
    61 import java.awt.geom.Point2D;
       
    62 import java.awt.geom.Rectangle2D;
       
    63 import java.awt.geom.PathIterator;
       
    64 import java.awt.geom.GeneralPath;
       
    65 import java.awt.Shape;
       
    66 import java.awt.Stroke;
       
    67 import java.awt.FontMetrics;
       
    68 import java.awt.Rectangle;
       
    69 import java.text.AttributedCharacterIterator;
       
    70 import java.awt.Font;
       
    71 import java.awt.image.ImageObserver;
       
    72 import java.awt.image.ColorConvertOp;
       
    73 import java.awt.Transparency;
       
    74 import java.awt.font.GlyphVector;
       
    75 import java.awt.font.TextLayout;
       
    76 import sun.font.FontDesignMetrics;
       
    77 import sun.font.StandardGlyphVector;
       
    78 import sun.java2d.pipe.PixelDrawPipe;
       
    79 import sun.java2d.pipe.PixelFillPipe;
       
    80 import sun.java2d.pipe.ShapeDrawPipe;
       
    81 import sun.java2d.pipe.ValidatePipe;
       
    82 import sun.java2d.pipe.ShapeSpanIterator;
       
    83 import sun.java2d.pipe.Region;
       
    84 import sun.java2d.pipe.RegionIterator;
       
    85 import sun.java2d.pipe.TextPipe;
       
    86 import sun.java2d.pipe.DrawImagePipe;
       
    87 import sun.java2d.pipe.LoopPipe;
       
    88 import sun.java2d.loops.FontInfo;
       
    89 import sun.java2d.loops.RenderLoops;
       
    90 import sun.java2d.loops.CompositeType;
       
    91 import sun.java2d.loops.SurfaceType;
       
    92 import sun.java2d.loops.Blit;
       
    93 import sun.java2d.loops.BlitBg;
       
    94 import sun.java2d.loops.MaskFill;
       
    95 import sun.font.FontManager;
       
    96 import java.awt.font.FontRenderContext;
       
    97 import sun.java2d.loops.XORComposite;
       
    98 import sun.awt.ConstrainableGraphics;
       
    99 import sun.awt.SunHints;
       
   100 import java.util.Map;
       
   101 import java.util.Iterator;
       
   102 import sun.awt.image.OffScreenImage;
       
   103 import sun.misc.PerformanceLogger;
       
   104 
       
   105 /**
       
   106  * This is a the master Graphics2D superclass for all of the Sun
       
   107  * Graphics implementations.  This class relies on subclasses to
       
   108  * manage the various device information, but provides an overall
       
   109  * general framework for performing all of the requests in the
       
   110  * Graphics and Graphics2D APIs.
       
   111  *
       
   112  * @author Jim Graham
       
   113  */
       
   114 public final class SunGraphics2D
       
   115     extends Graphics2D
       
   116     implements ConstrainableGraphics, Cloneable
       
   117 {
       
   118     /*
       
   119      * Attribute States
       
   120      */
       
   121     /* Paint */
       
   122     public static final int PAINT_CUSTOM       = 6; /* Any other Paint object */
       
   123     public static final int PAINT_TEXTURE      = 5; /* Tiled Image */
       
   124     public static final int PAINT_RAD_GRADIENT = 4; /* Color RadialGradient */
       
   125     public static final int PAINT_LIN_GRADIENT = 3; /* Color LinearGradient */
       
   126     public static final int PAINT_GRADIENT     = 2; /* Color Gradient */
       
   127     public static final int PAINT_ALPHACOLOR   = 1; /* Non-opaque Color */
       
   128     public static final int PAINT_OPAQUECOLOR  = 0; /* Opaque Color */
       
   129 
       
   130     /* Composite*/
       
   131     public static final int COMP_CUSTOM = 3;/* Custom Composite */
       
   132     public static final int COMP_XOR    = 2;/* XOR Mode Composite */
       
   133     public static final int COMP_ALPHA  = 1;/* AlphaComposite */
       
   134     public static final int COMP_ISCOPY = 0;/* simple stores into destination,
       
   135                                              * i.e. Src, SrcOverNoEa, and other
       
   136                                              * alpha modes which replace
       
   137                                              * the destination.
       
   138                                              */
       
   139 
       
   140     /* Stroke */
       
   141     public static final int STROKE_CUSTOM = 3; /* custom Stroke */
       
   142     public static final int STROKE_WIDE   = 2; /* BasicStroke */
       
   143     public static final int STROKE_THINDASHED   = 1; /* BasicStroke */
       
   144     public static final int STROKE_THIN   = 0; /* BasicStroke */
       
   145 
       
   146     /* Transform */
       
   147     public static final int TRANSFORM_GENERIC = 4; /* any 3x2 */
       
   148     public static final int TRANSFORM_TRANSLATESCALE = 3; /* scale XY */
       
   149     public static final int TRANSFORM_ANY_TRANSLATE = 2; /* non-int translate */
       
   150     public static final int TRANSFORM_INT_TRANSLATE = 1; /* int translate */
       
   151     public static final int TRANSFORM_ISIDENT = 0; /* Identity */
       
   152 
       
   153     /* Clipping */
       
   154     public static final int CLIP_SHAPE       = 2; /* arbitrary clip */
       
   155     public static final int CLIP_RECTANGULAR = 1; /* rectangular clip */
       
   156     public static final int CLIP_DEVICE      = 0; /* no clipping set */
       
   157 
       
   158     /* The following fields are used when the current Paint is a Color. */
       
   159     public int eargb;  // ARGB value with ExtraAlpha baked in
       
   160     public int pixel;  // pixel value for eargb
       
   161 
       
   162     public SurfaceData surfaceData;
       
   163 
       
   164     public PixelDrawPipe drawpipe;
       
   165     public PixelFillPipe fillpipe;
       
   166     public DrawImagePipe imagepipe;
       
   167     public ShapeDrawPipe shapepipe;
       
   168     public TextPipe textpipe;
       
   169     public MaskFill alphafill;
       
   170 
       
   171     public RenderLoops loops;
       
   172 
       
   173     public CompositeType imageComp;     /* Image Transparency checked on fly */
       
   174 
       
   175     public int paintState;
       
   176     public int compositeState;
       
   177     public int strokeState;
       
   178     public int transformState;
       
   179     public int clipState;
       
   180 
       
   181     public Color foregroundColor;
       
   182     public Color backgroundColor;
       
   183 
       
   184     public AffineTransform transform;
       
   185     public int transX;
       
   186     public int transY;
       
   187 
       
   188     protected static final Stroke defaultStroke = new BasicStroke();
       
   189     protected static final Composite defaultComposite = AlphaComposite.SrcOver;
       
   190     private static final Font defaultFont =
       
   191         new Font(Font.DIALOG, Font.PLAIN, 12);
       
   192 
       
   193     public Paint paint;
       
   194     public Stroke stroke;
       
   195     public Composite composite;
       
   196     protected Font font;
       
   197     protected FontMetrics fontMetrics;
       
   198 
       
   199     public int renderHint;
       
   200     public int antialiasHint;
       
   201     public int textAntialiasHint;
       
   202     private int fractionalMetricsHint;
       
   203 
       
   204     /* A gamma adjustment to the colour used in lcd text blitting */
       
   205     public int lcdTextContrast;
       
   206     private static int lcdTextContrastDefaultValue = 140;
       
   207 
       
   208     private int interpolationHint;      // raw value of rendering Hint
       
   209     public int strokeHint;
       
   210 
       
   211     public int interpolationType;       // algorithm choice based on
       
   212                                         // interpolation and render Hints
       
   213 
       
   214     public RenderingHints hints;
       
   215 
       
   216     public Region constrainClip;                // lightweight bounds
       
   217     public int constrainX;
       
   218     public int constrainY;
       
   219 
       
   220     public Region clipRegion;
       
   221     public Shape usrClip;
       
   222     protected Region devClip;           // Actual physical drawable
       
   223 
       
   224     // cached state for text rendering
       
   225     private boolean validFontInfo;
       
   226     private FontInfo fontInfo;
       
   227     private FontInfo glyphVectorFontInfo;
       
   228     private FontRenderContext glyphVectorFRC;
       
   229 
       
   230     private final static int slowTextTransformMask =
       
   231                             AffineTransform.TYPE_GENERAL_TRANSFORM
       
   232                         |   AffineTransform.TYPE_MASK_ROTATION
       
   233                         |   AffineTransform.TYPE_FLIP;
       
   234 
       
   235     static {
       
   236         if (PerformanceLogger.loggingEnabled()) {
       
   237             PerformanceLogger.setTime("SunGraphics2D static initialization");
       
   238         }
       
   239     }
       
   240 
       
   241     public SunGraphics2D(SurfaceData sd, Color fg, Color bg, Font f) {
       
   242         surfaceData = sd;
       
   243         foregroundColor = fg;
       
   244         backgroundColor = bg;
       
   245 
       
   246         transform = new AffineTransform();
       
   247         stroke = defaultStroke;
       
   248         composite = defaultComposite;
       
   249         paint = foregroundColor;
       
   250 
       
   251         imageComp = CompositeType.SrcOverNoEa;
       
   252 
       
   253         renderHint = SunHints.INTVAL_RENDER_DEFAULT;
       
   254         antialiasHint = SunHints.INTVAL_ANTIALIAS_OFF;
       
   255         textAntialiasHint = SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT;
       
   256         fractionalMetricsHint = SunHints.INTVAL_FRACTIONALMETRICS_OFF;
       
   257         lcdTextContrast = lcdTextContrastDefaultValue;
       
   258         interpolationHint = -1;
       
   259         strokeHint = SunHints.INTVAL_STROKE_DEFAULT;
       
   260 
       
   261         interpolationType = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
       
   262 
       
   263         validateColor();
       
   264 
       
   265         font = f;
       
   266         if (font == null) {
       
   267             font = defaultFont;
       
   268         }
       
   269 
       
   270         loops = sd.getRenderLoops(this);
       
   271         setDevClip(sd.getBounds());
       
   272         invalidatePipe();
       
   273     }
       
   274 
       
   275     protected Object clone() {
       
   276         try {
       
   277             SunGraphics2D g = (SunGraphics2D) super.clone();
       
   278             g.transform = new AffineTransform(this.transform);
       
   279             if (hints != null) {
       
   280                 g.hints = (RenderingHints) this.hints.clone();
       
   281             }
       
   282             /* FontInfos are re-used, so must be cloned too, if they
       
   283              * are valid, and be nulled out if invalid.
       
   284              * The implied trade-off is that there is more to be gained
       
   285              * from re-using these objects than is lost by having to
       
   286              * clone them when the SG2D is cloned.
       
   287              */
       
   288             if (this.fontInfo != null) {
       
   289                 if (this.validFontInfo) {
       
   290                     g.fontInfo = (FontInfo)this.fontInfo.clone();
       
   291                 } else {
       
   292                     g.fontInfo = null;
       
   293                 }
       
   294             }
       
   295             if (this.glyphVectorFontInfo != null) {
       
   296                 g.glyphVectorFontInfo =
       
   297                     (FontInfo)this.glyphVectorFontInfo.clone();
       
   298                 g.glyphVectorFRC = this.glyphVectorFRC;
       
   299             }
       
   300             //g.invalidatePipe();
       
   301             return g;
       
   302         } catch (CloneNotSupportedException e) {
       
   303         }
       
   304         return null;
       
   305     }
       
   306 
       
   307     /**
       
   308      * Create a new SunGraphics2D based on this one.
       
   309      */
       
   310     public Graphics create() {
       
   311         return (Graphics) clone();
       
   312     }
       
   313 
       
   314     public void setDevClip(int x, int y, int w, int h) {
       
   315         Region c = constrainClip;
       
   316         if (c == null) {
       
   317             devClip = Region.getInstanceXYWH(x, y, w, h);
       
   318         } else {
       
   319             devClip = c.getIntersectionXYWH(x, y, w, h);
       
   320         }
       
   321         validateCompClip();
       
   322     }
       
   323 
       
   324     public void setDevClip(Rectangle r) {
       
   325         setDevClip(r.x, r.y, r.width, r.height);
       
   326     }
       
   327 
       
   328     /**
       
   329      * Constrain rendering for lightweight objects.
       
   330      *
       
   331      * REMIND: This method will back off to the "workaround"
       
   332      * of using translate and clipRect if the Graphics
       
   333      * to be constrained has a complex transform.  The
       
   334      * drawback of the workaround is that the resulting
       
   335      * clip and device origin cannot be "enforced".
       
   336      *
       
   337      * @exception IllegalStateException If the Graphics
       
   338      * to be constrained has a complex transform.
       
   339      */
       
   340     public void constrain(int x, int y, int w, int h) {
       
   341         if ((x|y) != 0) {
       
   342             translate(x, y);
       
   343         }
       
   344         if (transformState >= TRANSFORM_TRANSLATESCALE) {
       
   345             clipRect(0, 0, w, h);
       
   346             return;
       
   347         }
       
   348         x = constrainX = transX;
       
   349         y = constrainY = transY;
       
   350         w = Region.dimAdd(x, w);
       
   351         h = Region.dimAdd(y, h);
       
   352         Region c = constrainClip;
       
   353         if (c == null) {
       
   354             c = Region.getInstanceXYXY(x, y, w, h);
       
   355         } else {
       
   356             c = c.getIntersectionXYXY(x, y, w, h);
       
   357             if (c == constrainClip) {
       
   358                 // Common case to ignore
       
   359                 return;
       
   360             }
       
   361         }
       
   362         constrainClip = c;
       
   363         if (!devClip.isInsideQuickCheck(c)) {
       
   364             devClip = devClip.getIntersection(c);
       
   365             validateCompClip();
       
   366         }
       
   367     }
       
   368 
       
   369     protected static ValidatePipe invalidpipe = new ValidatePipe();
       
   370 
       
   371     /*
       
   372      * Invalidate the pipeline
       
   373      */
       
   374     protected void invalidatePipe() {
       
   375         drawpipe = invalidpipe;
       
   376         fillpipe = invalidpipe;
       
   377         shapepipe = invalidpipe;
       
   378         textpipe = invalidpipe;
       
   379         imagepipe = invalidpipe;
       
   380     }
       
   381 
       
   382     public void validatePipe() {
       
   383         surfaceData.validatePipe(this);
       
   384     }
       
   385 
       
   386     /*
       
   387      * Intersect two Shapes by the simplest method, attempting to produce
       
   388      * a simplified result.
       
   389      * The boolean arguments keep1 and keep2 specify whether or not
       
   390      * the first or second shapes can be modified during the operation
       
   391      * or whether that shape must be "kept" unmodified.
       
   392      */
       
   393     Shape intersectShapes(Shape s1, Shape s2, boolean keep1, boolean keep2) {
       
   394         if (s1 instanceof Rectangle && s2 instanceof Rectangle) {
       
   395             return ((Rectangle) s1).intersection((Rectangle) s2);
       
   396         }
       
   397         if (s1 instanceof Rectangle2D) {
       
   398             return intersectRectShape((Rectangle2D) s1, s2, keep1, keep2);
       
   399         } else if (s2 instanceof Rectangle2D) {
       
   400             return intersectRectShape((Rectangle2D) s2, s1, keep2, keep1);
       
   401         }
       
   402         return intersectByArea(s1, s2, keep1, keep2);
       
   403     }
       
   404 
       
   405     /*
       
   406      * Intersect a Rectangle with a Shape by the simplest method,
       
   407      * attempting to produce a simplified result.
       
   408      * The boolean arguments keep1 and keep2 specify whether or not
       
   409      * the first or second shapes can be modified during the operation
       
   410      * or whether that shape must be "kept" unmodified.
       
   411      */
       
   412     Shape intersectRectShape(Rectangle2D r, Shape s,
       
   413                              boolean keep1, boolean keep2) {
       
   414         if (s instanceof Rectangle2D) {
       
   415             Rectangle2D r2 = (Rectangle2D) s;
       
   416             Rectangle2D outrect;
       
   417             if (!keep1) {
       
   418                 outrect = r;
       
   419             } else if (!keep2) {
       
   420                 outrect = r2;
       
   421             } else {
       
   422                 outrect = new Rectangle2D.Float();
       
   423             }
       
   424             double x1 = Math.max(r.getX(), r2.getX());
       
   425             double x2 = Math.min(r.getX()  + r.getWidth(),
       
   426                                  r2.getX() + r2.getWidth());
       
   427             double y1 = Math.max(r.getY(), r2.getY());
       
   428             double y2 = Math.min(r.getY()  + r.getHeight(),
       
   429                                  r2.getY() + r2.getHeight());
       
   430 
       
   431             if (((x2 - x1) < 0) || ((y2 - y1) < 0))
       
   432                 // Width or height is negative. No intersection.
       
   433                 outrect.setFrameFromDiagonal(0, 0, 0, 0);
       
   434             else
       
   435                 outrect.setFrameFromDiagonal(x1, y1, x2, y2);
       
   436             return outrect;
       
   437         }
       
   438         if (r.contains(s.getBounds2D())) {
       
   439             if (keep2) {
       
   440                 s = cloneShape(s);
       
   441             }
       
   442             return s;
       
   443         }
       
   444         return intersectByArea(r, s, keep1, keep2);
       
   445     }
       
   446 
       
   447     protected static Shape cloneShape(Shape s) {
       
   448         return new GeneralPath(s);
       
   449     }
       
   450 
       
   451     /*
       
   452      * Intersect two Shapes using the Area class.  Presumably other
       
   453      * attempts at simpler intersection methods proved fruitless.
       
   454      * The boolean arguments keep1 and keep2 specify whether or not
       
   455      * the first or second shapes can be modified during the operation
       
   456      * or whether that shape must be "kept" unmodified.
       
   457      * @see #intersectShapes
       
   458      * @see #intersectRectShape
       
   459      */
       
   460     Shape intersectByArea(Shape s1, Shape s2, boolean keep1, boolean keep2) {
       
   461         Area a1, a2;
       
   462 
       
   463         // First see if we can find an overwriteable source shape
       
   464         // to use as our destination area to avoid duplication.
       
   465         if (!keep1 && (s1 instanceof Area)) {
       
   466             a1 = (Area) s1;
       
   467         } else if (!keep2 && (s2 instanceof Area)) {
       
   468             a1 = (Area) s2;
       
   469             s2 = s1;
       
   470         } else {
       
   471             a1 = new Area(s1);
       
   472         }
       
   473 
       
   474         if (s2 instanceof Area) {
       
   475             a2 = (Area) s2;
       
   476         } else {
       
   477             a2 = new Area(s2);
       
   478         }
       
   479 
       
   480         a1.intersect(a2);
       
   481         if (a1.isRectangular()) {
       
   482             return a1.getBounds();
       
   483         }
       
   484 
       
   485         return a1;
       
   486     }
       
   487 
       
   488     /*
       
   489      * Intersect usrClip bounds and device bounds to determine the composite
       
   490      * rendering boundaries.
       
   491      */
       
   492     public Region getCompClip() {
       
   493         if (!surfaceData.isValid()) {
       
   494             // revalidateAll() implicitly recalculcates the composite clip
       
   495             revalidateAll();
       
   496         }
       
   497 
       
   498         return clipRegion;
       
   499     }
       
   500 
       
   501     public Font getFont() {
       
   502         if (font == null) {
       
   503             font = defaultFont;
       
   504         }
       
   505         return font;
       
   506     }
       
   507 
       
   508     private static final double[] IDENT_MATRIX = {1, 0, 0, 1};
       
   509     private static final AffineTransform IDENT_ATX =
       
   510         new AffineTransform();
       
   511 
       
   512     private static final int MINALLOCATED = 8;
       
   513     private static final int TEXTARRSIZE = 17;
       
   514     private static double[][] textTxArr = new double[TEXTARRSIZE][];
       
   515     private static AffineTransform[] textAtArr =
       
   516         new AffineTransform[TEXTARRSIZE];
       
   517 
       
   518     static {
       
   519         for (int i=MINALLOCATED;i<TEXTARRSIZE;i++) {
       
   520           textTxArr[i] = new double [] {i, 0, 0, i};
       
   521           textAtArr[i] = new AffineTransform( textTxArr[i]);
       
   522         }
       
   523     }
       
   524 
       
   525     // cached state for various draw[String,Char,Byte] optimizations
       
   526     public FontInfo checkFontInfo(FontInfo info, Font font,
       
   527                                   FontRenderContext frc) {
       
   528         /* Do not create a FontInfo object as part of construction of an
       
   529          * SG2D as its possible it may never be needed - ie if no text
       
   530          * is drawn using this SG2D.
       
   531          */
       
   532         if (info == null) {
       
   533             info = new FontInfo();
       
   534         }
       
   535 
       
   536         float ptSize = font.getSize2D();
       
   537         int txFontType;
       
   538         AffineTransform devAt, textAt=null;
       
   539         if (font.isTransformed()) {
       
   540             textAt = font.getTransform();
       
   541             textAt.scale(ptSize, ptSize);
       
   542             txFontType = textAt.getType();
       
   543             info.originX = (float)textAt.getTranslateX();
       
   544             info.originY = (float)textAt.getTranslateY();
       
   545             textAt.translate(-info.originX, -info.originY);
       
   546             if (transformState >= TRANSFORM_TRANSLATESCALE) {
       
   547                 transform.getMatrix(info.devTx = new double[4]);
       
   548                 devAt = new AffineTransform(info.devTx);
       
   549                 textAt.preConcatenate(devAt);
       
   550             } else {
       
   551                 info.devTx = IDENT_MATRIX;
       
   552                 devAt = IDENT_ATX;
       
   553             }
       
   554             textAt.getMatrix(info.glyphTx = new double[4]);
       
   555             double shearx = textAt.getShearX();
       
   556             double scaley = textAt.getScaleY();
       
   557             if (shearx != 0) {
       
   558                 scaley = Math.sqrt(shearx * shearx + scaley * scaley);
       
   559             }
       
   560             info.pixelHeight = (int)(Math.abs(scaley)+0.5);
       
   561         } else {
       
   562             txFontType = AffineTransform.TYPE_IDENTITY;
       
   563             info.originX = info.originY = 0;
       
   564             if (transformState >= TRANSFORM_TRANSLATESCALE) {
       
   565                 transform.getMatrix(info.devTx = new double[4]);
       
   566                 devAt = new AffineTransform(info.devTx);
       
   567                 info.glyphTx = new double[4];
       
   568                 for (int i = 0; i < 4; i++) {
       
   569                     info.glyphTx[i] = info.devTx[i] * ptSize;
       
   570                 }
       
   571                 textAt = new AffineTransform(info.glyphTx);
       
   572                 double shearx = transform.getShearX();
       
   573                 double scaley = transform.getScaleY();
       
   574                 if (shearx != 0) {
       
   575                     scaley = Math.sqrt(shearx * shearx + scaley * scaley);
       
   576                 }
       
   577                 info.pixelHeight = (int)(Math.abs(scaley * ptSize)+0.5);
       
   578             } else {
       
   579                 /* If the double represents a common integral, we
       
   580                  * may have pre-allocated objects.
       
   581                  * A "sparse" array be seems to be as fast as a switch
       
   582                  * even for 3 or 4 pt sizes, and is more flexible.
       
   583                  * This should perform comparably in single-threaded
       
   584                  * rendering to the old code which synchronized on the
       
   585                  * class and scale better on MP systems.
       
   586                  */
       
   587                 int pszInt = (int)ptSize;
       
   588                 if (ptSize == pszInt &&
       
   589                     pszInt >= MINALLOCATED && pszInt < TEXTARRSIZE) {
       
   590                     info.glyphTx = textTxArr[pszInt];
       
   591                     textAt = textAtArr[pszInt];
       
   592                     info.pixelHeight = pszInt;
       
   593                 } else {
       
   594                     info.pixelHeight = (int)(ptSize+0.5);
       
   595                 }
       
   596                 if (textAt == null) {
       
   597                     info.glyphTx = new double[] {ptSize, 0, 0, ptSize};
       
   598                     textAt = new AffineTransform(info.glyphTx);
       
   599                 }
       
   600 
       
   601                 info.devTx = IDENT_MATRIX;
       
   602                 devAt = IDENT_ATX;
       
   603             }
       
   604         }
       
   605 
       
   606         info.font2D = FontManager.getFont2D(font);
       
   607 
       
   608         int fmhint = fractionalMetricsHint;
       
   609         if (fmhint == SunHints.INTVAL_FRACTIONALMETRICS_DEFAULT) {
       
   610             fmhint = SunHints.INTVAL_FRACTIONALMETRICS_OFF;
       
   611         }
       
   612         info.lcdSubPixPos = false; // conditionally set true in LCD mode.
       
   613 
       
   614         /* The text anti-aliasing hints that are set by the client need
       
   615          * to be interpreted for the current state and stored in the
       
   616          * FontInfo.aahint which is what will actually be used and
       
   617          * will be one of OFF, ON, LCD_HRGB or LCD_VRGB.
       
   618          * This is what pipe selection code should typically refer to, not
       
   619          * textAntialiasHint. This means we are now evaluating the meaning
       
   620          * of "default" here. Any pipe that really cares about that will
       
   621          * also need to consult that variable.
       
   622          * Otherwise these are being used only as args to getStrike,
       
   623          * and are encapsulated in that object which is part of the
       
   624          * FontInfo, so we do not need to store them directly as fields
       
   625          * in the FontInfo object.
       
   626          * That could change if FontInfo's were more selectively
       
   627          * revalidated when graphics state changed. Presently this
       
   628          * method re-evaluates all fields in the fontInfo.
       
   629          * The strike doesn't need to know the RGB subpixel order. Just
       
   630          * if its H or V orientation, so if an LCD option is specified we
       
   631          * always pass in the RGB hint to the strike.
       
   632          * frc is non-null only if this is a GlyphVector. For reasons
       
   633          * which are probably a historical mistake the AA hint in a GV
       
   634          * is honoured when we render, overriding the Graphics setting.
       
   635          */
       
   636         int aahint;
       
   637         if (frc == null) {
       
   638             aahint = textAntialiasHint;
       
   639         } else {
       
   640             aahint = ((SunHints.Value)frc.getAntiAliasingHint()).getIndex();
       
   641         }
       
   642         if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT) {
       
   643             if (antialiasHint == SunHints.INTVAL_ANTIALIAS_ON) {
       
   644                 aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON;
       
   645             } else {
       
   646                 aahint = SunHints.INTVAL_TEXT_ANTIALIAS_OFF;
       
   647             }
       
   648         } else {
       
   649             /* If we are in checkFontInfo because a rendering hint has been
       
   650              * set then all pipes are revalidated. But we can also
       
   651              * be here because setFont() has been called when the 'gasp'
       
   652              * hint is set, as then the font size determines the text pipe.
       
   653              * See comments in SunGraphics2d.setFont(Font).
       
   654              */
       
   655             if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_GASP) {
       
   656                 if (info.font2D.useAAForPtSize(info.pixelHeight)) {
       
   657                     aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON;
       
   658                 } else {
       
   659                     aahint = SunHints.INTVAL_TEXT_ANTIALIAS_OFF;
       
   660                 }
       
   661             } else if (aahint >= SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB) {
       
   662                 /* loops for default rendering modes are installed in the SG2D
       
   663                  * constructor. If there are none this will be null.
       
   664                  * Not all compositing modes update the render loops, so
       
   665                  * we also test that this is a mode we know should support
       
   666                  * this. One minor issue is that the loops aren't necessarily
       
   667                  * installed for a new rendering mode until after this
       
   668                  * method is called during pipeline validation. So it is
       
   669                  * theoretically possible that it was set to null for a
       
   670                  * compositing mode, the composite is then set back to Src,
       
   671                  * but the loop is still null when this is called and AA=ON
       
   672                  * is installed instead of an LCD mode.
       
   673                  * However this is done in the right order in SurfaceData.java
       
   674                  * so this is not likely to be a problem - but not
       
   675                  * guaranteed.
       
   676                  */
       
   677                 if (
       
   678                     !surfaceData.canRenderLCDText(this)
       
   679 //                    loops.drawGlyphListLCDLoop == null ||
       
   680 //                    compositeState > COMP_ISCOPY ||
       
   681 //                    paintState > PAINT_ALPHACOLOR
       
   682                       ) {
       
   683                     aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON;
       
   684                 } else {
       
   685                     info.lcdRGBOrder = true;
       
   686                     /* Collapse these into just HRGB or VRGB.
       
   687                      * Pipe selection code needs only to test for these two.
       
   688                      * Since these both select the same pipe anyway its
       
   689                      * tempting to collapse into one value. But they are
       
   690                      * different strikes (glyph caches) so the distinction
       
   691                      * needs to be made for that purpose.
       
   692                      */
       
   693                     if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HBGR) {
       
   694                         aahint = SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB;
       
   695                         info.lcdRGBOrder = false;
       
   696                     } else if
       
   697                         (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_LCD_VBGR) {
       
   698                         aahint = SunHints.INTVAL_TEXT_ANTIALIAS_LCD_VRGB;
       
   699                         info.lcdRGBOrder = false;
       
   700                     }
       
   701                     /* Support subpixel positioning only for the case in
       
   702                      * which the horizontal resolution is increased
       
   703                      */
       
   704                     info.lcdSubPixPos =
       
   705                         fmhint == SunHints.INTVAL_FRACTIONALMETRICS_ON &&
       
   706                         aahint == SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB;
       
   707                 }
       
   708             }
       
   709         }
       
   710         info.aaHint = aahint;
       
   711         info.fontStrike = info.font2D.getStrike(font, devAt, textAt,
       
   712                                                 aahint, fmhint);
       
   713         return info;
       
   714     }
       
   715 
       
   716     public static boolean isRotated(double [] mtx) {
       
   717         if ((mtx[0] == mtx[3]) &&
       
   718             (mtx[1] == 0.0) &&
       
   719             (mtx[2] == 0.0) &&
       
   720             (mtx[0] > 0.0))
       
   721         {
       
   722             return false;
       
   723         }
       
   724 
       
   725         return true;
       
   726     }
       
   727 
       
   728     public void setFont(Font font) {
       
   729         /* replacing the reference equality test font != this.font with
       
   730          * !font.equals(this.font) did not yield any measurable difference
       
   731          * in testing, but there may be yet to be identified cases where it
       
   732          * is beneficial.
       
   733          */
       
   734         if (font != null && font!=this.font/*!font.equals(this.font)*/) {
       
   735             /* In the GASP AA case the textpipe depends on the glyph size
       
   736              * as determined by graphics and font transforms as well as the
       
   737              * font size, and information in the font. But we may invalidate
       
   738              * the pipe only to find that it made no difference.
       
   739              * Deferring pipe invalidation to checkFontInfo won't work because
       
   740              * when called we may already be rendering to the wrong pipe.
       
   741              * So, if the font is transformed, or the graphics has more than
       
   742              * a simple scale, we'll take that as enough of a hint to
       
   743              * revalidate everything. But if they aren't we will
       
   744              * use the font's point size to query the gasp table and see if
       
   745              * what it says matches what's currently being used, in which
       
   746              * case there's no need to invalidate the textpipe.
       
   747              * This should be sufficient for all typical uses cases.
       
   748              */
       
   749             if (textAntialiasHint == SunHints.INTVAL_TEXT_ANTIALIAS_GASP &&
       
   750                 textpipe != invalidpipe &&
       
   751                 (transformState > TRANSFORM_ANY_TRANSLATE ||
       
   752                  font.isTransformed() ||
       
   753                  fontInfo == null || // Precaution, if true shouldn't get here
       
   754                  (fontInfo.aaHint == SunHints.INTVAL_TEXT_ANTIALIAS_ON) !=
       
   755                  FontManager.getFont2D(font).useAAForPtSize(font.getSize()))) {
       
   756                 textpipe = invalidpipe;
       
   757             }
       
   758             this.font = font;
       
   759             this.fontMetrics = null;
       
   760             this.validFontInfo = false;
       
   761         }
       
   762     }
       
   763 
       
   764     public FontInfo getFontInfo() {
       
   765         if (!validFontInfo) {
       
   766             this.fontInfo = checkFontInfo(this.fontInfo, font, null);
       
   767             validFontInfo = true;
       
   768         }
       
   769         return this.fontInfo;
       
   770     }
       
   771 
       
   772     /* Used by drawGlyphVector which specifies its own font. */
       
   773     public FontInfo getGVFontInfo(Font font, FontRenderContext frc) {
       
   774         if (glyphVectorFontInfo != null &&
       
   775             glyphVectorFontInfo.font == font &&
       
   776             glyphVectorFRC == frc) {
       
   777             return glyphVectorFontInfo;
       
   778         } else {
       
   779             glyphVectorFRC = frc;
       
   780             return glyphVectorFontInfo =
       
   781                 checkFontInfo(glyphVectorFontInfo, font, frc);
       
   782         }
       
   783     }
       
   784 
       
   785     public FontMetrics getFontMetrics() {
       
   786         if (this.fontMetrics != null) {
       
   787             return this.fontMetrics;
       
   788         }
       
   789         /* NB the constructor and the setter disallow "font" being null */
       
   790         return this.fontMetrics =
       
   791            FontDesignMetrics.getMetrics(font, getFontRenderContext());
       
   792     }
       
   793 
       
   794     public FontMetrics getFontMetrics(Font font) {
       
   795         if ((this.fontMetrics != null) && (font == this.font)) {
       
   796             return this.fontMetrics;
       
   797         }
       
   798         FontMetrics fm =
       
   799           FontDesignMetrics.getMetrics(font, getFontRenderContext());
       
   800 
       
   801         if (this.font == font) {
       
   802             this.fontMetrics = fm;
       
   803         }
       
   804         return fm;
       
   805     }
       
   806 
       
   807     /**
       
   808      * Checks to see if a Path intersects the specified Rectangle in device
       
   809      * space.  The rendering attributes taken into account include the
       
   810      * clip, transform, and stroke attributes.
       
   811      * @param rect The area in device space to check for a hit.
       
   812      * @param p The path to check for a hit.
       
   813      * @param onStroke Flag to choose between testing the stroked or
       
   814      * the filled path.
       
   815      * @return True if there is a hit, false otherwise.
       
   816      * @see #setStroke
       
   817      * @see #fillPath
       
   818      * @see #drawPath
       
   819      * @see #transform
       
   820      * @see #setTransform
       
   821      * @see #clip
       
   822      * @see #setClip
       
   823      */
       
   824     public boolean hit(Rectangle rect, Shape s, boolean onStroke) {
       
   825         if (onStroke) {
       
   826             s = stroke.createStrokedShape(s);
       
   827         }
       
   828 
       
   829         s = transformShape(s);
       
   830         if ((constrainX|constrainY) != 0) {
       
   831             rect = new Rectangle(rect);
       
   832             rect.translate(constrainX, constrainY);
       
   833         }
       
   834 
       
   835         return s.intersects(rect);
       
   836     }
       
   837 
       
   838     /**
       
   839      * Return the ColorModel associated with this Graphics2D.
       
   840      */
       
   841     public ColorModel getDeviceColorModel() {
       
   842         return surfaceData.getColorModel();
       
   843     }
       
   844 
       
   845     /**
       
   846      * Return the device configuration associated with this Graphics2D.
       
   847      */
       
   848     public GraphicsConfiguration getDeviceConfiguration() {
       
   849         return surfaceData.getDeviceConfiguration();
       
   850     }
       
   851 
       
   852     /**
       
   853      * Return the SurfaceData object assigned to manage the destination
       
   854      * drawable surface of this Graphics2D.
       
   855      */
       
   856     public final SurfaceData getSurfaceData() {
       
   857         return surfaceData;
       
   858     }
       
   859 
       
   860     /**
       
   861      * Sets the Composite in the current graphics state. Composite is used
       
   862      * in all drawing methods such as drawImage, drawString, drawPath,
       
   863      * and fillPath.  It specifies how new pixels are to be combined with
       
   864      * the existing pixels on the graphics device in the rendering process.
       
   865      * @param comp The Composite object to be used for drawing.
       
   866      * @see java.awt.Graphics#setXORMode
       
   867      * @see java.awt.Graphics#setPaintMode
       
   868      * @see AlphaComposite
       
   869      */
       
   870     public void setComposite(Composite comp) {
       
   871         if (composite == comp) {
       
   872             return;
       
   873         }
       
   874         int newCompState;
       
   875         CompositeType newCompType;
       
   876         if (comp instanceof AlphaComposite) {
       
   877             AlphaComposite alphacomp = (AlphaComposite) comp;
       
   878             newCompType = CompositeType.forAlphaComposite(alphacomp);
       
   879             if (newCompType == CompositeType.SrcOverNoEa) {
       
   880                 if (paintState == PAINT_OPAQUECOLOR ||
       
   881                     (paintState > PAINT_ALPHACOLOR &&
       
   882                      paint.getTransparency() == Transparency.OPAQUE))
       
   883                 {
       
   884                     newCompState = COMP_ISCOPY;
       
   885                 } else {
       
   886                     newCompState = COMP_ALPHA;
       
   887                 }
       
   888             } else if (newCompType == CompositeType.SrcNoEa ||
       
   889                        newCompType == CompositeType.Src ||
       
   890                        newCompType == CompositeType.Clear)
       
   891             {
       
   892                 newCompState = COMP_ISCOPY;
       
   893             } else if (surfaceData.getTransparency() == Transparency.OPAQUE &&
       
   894                        newCompType == CompositeType.SrcIn)
       
   895             {
       
   896                 newCompState = COMP_ISCOPY;
       
   897             } else {
       
   898                 newCompState = COMP_ALPHA;
       
   899             }
       
   900         } else if (comp instanceof XORComposite) {
       
   901             newCompState = COMP_XOR;
       
   902             newCompType = CompositeType.Xor;
       
   903         } else if (comp == null) {
       
   904             throw new IllegalArgumentException("null Composite");
       
   905         } else {
       
   906             surfaceData.checkCustomComposite();
       
   907             newCompState = COMP_CUSTOM;
       
   908             newCompType = CompositeType.General;
       
   909         }
       
   910         if (compositeState != newCompState ||
       
   911             imageComp != newCompType)
       
   912         {
       
   913             compositeState = newCompState;
       
   914             imageComp = newCompType;
       
   915             invalidatePipe();
       
   916             validFontInfo = false;
       
   917         }
       
   918         composite = comp;
       
   919         if (paintState <= PAINT_ALPHACOLOR) {
       
   920             validateColor();
       
   921         }
       
   922     }
       
   923 
       
   924     /**
       
   925      * Sets the Paint in the current graphics state.
       
   926      * @param paint The Paint object to be used to generate color in
       
   927      * the rendering process.
       
   928      * @see java.awt.Graphics#setColor
       
   929      * @see GradientPaint
       
   930      * @see TexturePaint
       
   931      */
       
   932     public void setPaint(Paint paint) {
       
   933         if (paint instanceof Color) {
       
   934             setColor((Color) paint);
       
   935             return;
       
   936         }
       
   937         if (paint == null || this.paint == paint) {
       
   938             return;
       
   939         }
       
   940         this.paint = paint;
       
   941         if (imageComp == CompositeType.SrcOverNoEa) {
       
   942             // special case where compState depends on opacity of paint
       
   943             if (paint.getTransparency() == Transparency.OPAQUE) {
       
   944                 if (compositeState != COMP_ISCOPY) {
       
   945                     compositeState = COMP_ISCOPY;
       
   946                 }
       
   947             } else {
       
   948                 if (compositeState == COMP_ISCOPY) {
       
   949                     compositeState = COMP_ALPHA;
       
   950                 }
       
   951             }
       
   952         }
       
   953         Class paintClass = paint.getClass();
       
   954         if (paintClass == GradientPaint.class) {
       
   955             paintState = PAINT_GRADIENT;
       
   956         } else if (paintClass == LinearGradientPaint.class) {
       
   957             paintState = PAINT_LIN_GRADIENT;
       
   958         } else if (paintClass == RadialGradientPaint.class) {
       
   959             paintState = PAINT_RAD_GRADIENT;
       
   960         } else if (paintClass == TexturePaint.class) {
       
   961             paintState = PAINT_TEXTURE;
       
   962         } else {
       
   963             paintState = PAINT_CUSTOM;
       
   964         }
       
   965         validFontInfo = false;
       
   966         invalidatePipe();
       
   967     }
       
   968 
       
   969     static final int NON_UNIFORM_SCALE_MASK =
       
   970         (AffineTransform.TYPE_GENERAL_TRANSFORM |
       
   971          AffineTransform.TYPE_GENERAL_SCALE);
       
   972     public static final double MinPenSizeAA =
       
   973         sun.java2d.pipe.RenderingEngine.getInstance().getMinimumAAPenSize();
       
   974     public static final double MinPenSizeAASquared =
       
   975         (MinPenSizeAA * MinPenSizeAA);
       
   976     // Since inaccuracies in the trig package can cause us to
       
   977     // calculated a rotated pen width of just slightly greater
       
   978     // than 1.0, we add a fudge factor to our comparison value
       
   979     // here so that we do not misclassify single width lines as
       
   980     // wide lines under certain rotations.
       
   981     public static final double MinPenSizeSquared = 1.000000001;
       
   982 
       
   983     private void validateBasicStroke(BasicStroke bs) {
       
   984         boolean aa = (antialiasHint == SunHints.INTVAL_ANTIALIAS_ON);
       
   985         if (transformState < TRANSFORM_TRANSLATESCALE) {
       
   986             if (aa) {
       
   987                 if (bs.getLineWidth() <= MinPenSizeAA) {
       
   988                     if (bs.getDashArray() == null) {
       
   989                         strokeState = STROKE_THIN;
       
   990                     } else {
       
   991                         strokeState = STROKE_THINDASHED;
       
   992                     }
       
   993                 } else {
       
   994                     strokeState = STROKE_WIDE;
       
   995                 }
       
   996             } else {
       
   997                 if (bs == defaultStroke) {
       
   998                     strokeState = STROKE_THIN;
       
   999                 } else if (bs.getLineWidth() <= 1.0f) {
       
  1000                     if (bs.getDashArray() == null) {
       
  1001                         strokeState = STROKE_THIN;
       
  1002                     } else {
       
  1003                         strokeState = STROKE_THINDASHED;
       
  1004                     }
       
  1005                 } else {
       
  1006                     strokeState = STROKE_WIDE;
       
  1007                 }
       
  1008             }
       
  1009         } else {
       
  1010             double widthsquared;
       
  1011             if ((transform.getType() & NON_UNIFORM_SCALE_MASK) == 0) {
       
  1012                 /* sqrt omitted, compare to squared limits below. */
       
  1013                 widthsquared = Math.abs(transform.getDeterminant());
       
  1014             } else {
       
  1015                 /* First calculate the "maximum scale" of this transform. */
       
  1016                 double A = transform.getScaleX();       // m00
       
  1017                 double C = transform.getShearX();       // m01
       
  1018                 double B = transform.getShearY();       // m10
       
  1019                 double D = transform.getScaleY();       // m11
       
  1020 
       
  1021                 /*
       
  1022                  * Given a 2 x 2 affine matrix [ A B ] such that
       
  1023                  *                             [ C D ]
       
  1024                  * v' = [x' y'] = [Ax + Cy, Bx + Dy], we want to
       
  1025                  * find the maximum magnitude (norm) of the vector v'
       
  1026                  * with the constraint (x^2 + y^2 = 1).
       
  1027                  * The equation to maximize is
       
  1028                  *     |v'| = sqrt((Ax+Cy)^2+(Bx+Dy)^2)
       
  1029                  * or  |v'| = sqrt((AA+BB)x^2 + 2(AC+BD)xy + (CC+DD)y^2).
       
  1030                  * Since sqrt is monotonic we can maximize |v'|^2
       
  1031                  * instead and plug in the substitution y = sqrt(1 - x^2).
       
  1032                  * Trigonometric equalities can then be used to get
       
  1033                  * rid of most of the sqrt terms.
       
  1034                  */
       
  1035                 double EA = A*A + B*B;          // x^2 coefficient
       
  1036                 double EB = 2*(A*C + B*D);      // xy coefficient
       
  1037                 double EC = C*C + D*D;          // y^2 coefficient
       
  1038 
       
  1039                 /*
       
  1040                  * There is a lot of calculus omitted here.
       
  1041                  *
       
  1042                  * Conceptually, in the interests of understanding the
       
  1043                  * terms that the calculus produced we can consider
       
  1044                  * that EA and EC end up providing the lengths along
       
  1045                  * the major axes and the hypot term ends up being an
       
  1046                  * adjustment for the additional length along the off-axis
       
  1047                  * angle of rotated or sheared ellipses as well as an
       
  1048                  * adjustment for the fact that the equation below
       
  1049                  * averages the two major axis lengths.  (Notice that
       
  1050                  * the hypot term contains a part which resolves to the
       
  1051                  * difference of these two axis lengths in the absence
       
  1052                  * of rotation.)
       
  1053                  *
       
  1054                  * In the calculus, the ratio of the EB and (EA-EC) terms
       
  1055                  * ends up being the tangent of 2*theta where theta is
       
  1056                  * the angle that the long axis of the ellipse makes
       
  1057                  * with the horizontal axis.  Thus, this equation is
       
  1058                  * calculating the length of the hypotenuse of a triangle
       
  1059                  * along that axis.
       
  1060                  */
       
  1061                 double hypot = Math.sqrt(EB*EB + (EA-EC)*(EA-EC));
       
  1062 
       
  1063                 /* sqrt omitted, compare to squared limits below. */
       
  1064                 widthsquared = ((EA + EC + hypot)/2.0);
       
  1065             }
       
  1066             if (bs != defaultStroke) {
       
  1067                 widthsquared *= bs.getLineWidth() * bs.getLineWidth();
       
  1068             }
       
  1069             if (widthsquared <=
       
  1070                 (aa ? MinPenSizeAASquared : MinPenSizeSquared))
       
  1071             {
       
  1072                 if (bs.getDashArray() == null) {
       
  1073                     strokeState = STROKE_THIN;
       
  1074                 } else {
       
  1075                     strokeState = STROKE_THINDASHED;
       
  1076                 }
       
  1077             } else {
       
  1078                 strokeState = STROKE_WIDE;
       
  1079             }
       
  1080         }
       
  1081     }
       
  1082 
       
  1083     /*
       
  1084      * Sets the Stroke in the current graphics state.
       
  1085      * @param s The Stroke object to be used to stroke a Path in
       
  1086      * the rendering process.
       
  1087      * @see BasicStroke
       
  1088      */
       
  1089     public void setStroke(Stroke s) {
       
  1090         if (s == null) {
       
  1091             throw new IllegalArgumentException("null Stroke");
       
  1092         }
       
  1093         int saveStrokeState = strokeState;
       
  1094         stroke = s;
       
  1095         if (s instanceof BasicStroke) {
       
  1096             validateBasicStroke((BasicStroke) s);
       
  1097         } else {
       
  1098             strokeState = STROKE_CUSTOM;
       
  1099         }
       
  1100         if (strokeState != saveStrokeState) {
       
  1101             invalidatePipe();
       
  1102         }
       
  1103     }
       
  1104 
       
  1105     /**
       
  1106      * Sets the preferences for the rendering algorithms.
       
  1107      * Hint categories include controls for rendering quality and
       
  1108      * overall time/quality trade-off in the rendering process.
       
  1109      * @param hintKey The key of hint to be set. The strings are
       
  1110      * defined in the RenderingHints class.
       
  1111      * @param hintValue The value indicating preferences for the specified
       
  1112      * hint category. These strings are defined in the RenderingHints
       
  1113      * class.
       
  1114      * @see RenderingHints
       
  1115      */
       
  1116     public void setRenderingHint(Key hintKey, Object hintValue) {
       
  1117         // If we recognize the key, we must recognize the value
       
  1118         //     otherwise throw an IllegalArgumentException
       
  1119         //     and do not change the Hints object
       
  1120         // If we do not recognize the key, just pass it through
       
  1121         //     to the Hints object untouched
       
  1122         if (!hintKey.isCompatibleValue(hintValue)) {
       
  1123             throw new IllegalArgumentException
       
  1124                 (hintValue+" is not compatible with "+hintKey);
       
  1125         }
       
  1126         if (hintKey instanceof SunHints.Key) {
       
  1127             boolean stateChanged;
       
  1128             boolean textStateChanged = false;
       
  1129             boolean recognized = true;
       
  1130             SunHints.Key sunKey = (SunHints.Key) hintKey;
       
  1131             int newHint;
       
  1132             if (sunKey == SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST) {
       
  1133                 newHint = ((Integer)hintValue).intValue();
       
  1134             } else {
       
  1135                 newHint = ((SunHints.Value) hintValue).getIndex();
       
  1136             }
       
  1137             switch (sunKey.getIndex()) {
       
  1138             case SunHints.INTKEY_RENDERING:
       
  1139                 stateChanged = (renderHint != newHint);
       
  1140                 if (stateChanged) {
       
  1141                     renderHint = newHint;
       
  1142                     if (interpolationHint == -1) {
       
  1143                         interpolationType =
       
  1144                             (newHint == SunHints.INTVAL_RENDER_QUALITY
       
  1145                              ? AffineTransformOp.TYPE_BILINEAR
       
  1146                              : AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
       
  1147                     }
       
  1148                 }
       
  1149                 break;
       
  1150             case SunHints.INTKEY_ANTIALIASING:
       
  1151                 stateChanged = (antialiasHint != newHint);
       
  1152                 antialiasHint = newHint;
       
  1153                 if (stateChanged) {
       
  1154                     textStateChanged =
       
  1155                         (textAntialiasHint ==
       
  1156                          SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT);
       
  1157                     if (strokeState != STROKE_CUSTOM) {
       
  1158                         validateBasicStroke((BasicStroke) stroke);
       
  1159                     }
       
  1160                 }
       
  1161                 break;
       
  1162             case SunHints.INTKEY_TEXT_ANTIALIASING:
       
  1163                 stateChanged = (textAntialiasHint != newHint);
       
  1164                 textStateChanged = stateChanged;
       
  1165                 textAntialiasHint = newHint;
       
  1166                 break;
       
  1167             case SunHints.INTKEY_FRACTIONALMETRICS:
       
  1168                 stateChanged = (fractionalMetricsHint != newHint);
       
  1169                 textStateChanged = stateChanged;
       
  1170                 fractionalMetricsHint = newHint;
       
  1171                 break;
       
  1172             case SunHints.INTKEY_AATEXT_LCD_CONTRAST:
       
  1173                 stateChanged = false;
       
  1174                 /* Already have validated it is an int 100 <= newHint <= 250 */
       
  1175                 lcdTextContrast = newHint;
       
  1176                 break;
       
  1177             case SunHints.INTKEY_INTERPOLATION:
       
  1178                 interpolationHint = newHint;
       
  1179                 switch (newHint) {
       
  1180                 case SunHints.INTVAL_INTERPOLATION_BICUBIC:
       
  1181                     newHint = AffineTransformOp.TYPE_BICUBIC;
       
  1182                     break;
       
  1183                 case SunHints.INTVAL_INTERPOLATION_BILINEAR:
       
  1184                     newHint = AffineTransformOp.TYPE_BILINEAR;
       
  1185                     break;
       
  1186                 default:
       
  1187                 case SunHints.INTVAL_INTERPOLATION_NEAREST_NEIGHBOR:
       
  1188                     newHint = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
       
  1189                     break;
       
  1190                 }
       
  1191                 stateChanged = (interpolationType != newHint);
       
  1192                 interpolationType = newHint;
       
  1193                 break;
       
  1194             case SunHints.INTKEY_STROKE_CONTROL:
       
  1195                 stateChanged = (strokeHint != newHint);
       
  1196                 strokeHint = newHint;
       
  1197                 break;
       
  1198             default:
       
  1199                 recognized = false;
       
  1200                 stateChanged = false;
       
  1201                 break;
       
  1202             }
       
  1203             if (recognized) {
       
  1204                 if (stateChanged) {
       
  1205                     invalidatePipe();
       
  1206                     if (textStateChanged) {
       
  1207                         fontMetrics = null;
       
  1208                         this.cachedFRC = null;
       
  1209                         validFontInfo = false;
       
  1210                         this.glyphVectorFontInfo = null;
       
  1211                     }
       
  1212                 }
       
  1213                 if (hints != null) {
       
  1214                     hints.put(hintKey, hintValue);
       
  1215                 }
       
  1216                 return;
       
  1217             }
       
  1218         }
       
  1219         // Nothing we recognize so none of "our state" has changed
       
  1220         if (hints == null) {
       
  1221             hints = makeHints(null);
       
  1222         }
       
  1223         hints.put(hintKey, hintValue);
       
  1224     }
       
  1225 
       
  1226 
       
  1227     /**
       
  1228      * Returns the preferences for the rendering algorithms.
       
  1229      * @param hintCategory The category of hint to be set. The strings
       
  1230      * are defined in the RenderingHints class.
       
  1231      * @return The preferences for rendering algorithms. The strings
       
  1232      * are defined in the RenderingHints class.
       
  1233      * @see RenderingHints
       
  1234      */
       
  1235     public Object getRenderingHint(Key hintKey) {
       
  1236         if (hints != null) {
       
  1237             return hints.get(hintKey);
       
  1238         }
       
  1239         if (!(hintKey instanceof SunHints.Key)) {
       
  1240             return null;
       
  1241         }
       
  1242         int keyindex = ((SunHints.Key)hintKey).getIndex();
       
  1243         switch (keyindex) {
       
  1244         case SunHints.INTKEY_RENDERING:
       
  1245             return SunHints.Value.get(SunHints.INTKEY_RENDERING,
       
  1246                                       renderHint);
       
  1247         case SunHints.INTKEY_ANTIALIASING:
       
  1248             return SunHints.Value.get(SunHints.INTKEY_ANTIALIASING,
       
  1249                                       antialiasHint);
       
  1250         case SunHints.INTKEY_TEXT_ANTIALIASING:
       
  1251             return SunHints.Value.get(SunHints.INTKEY_TEXT_ANTIALIASING,
       
  1252                                       textAntialiasHint);
       
  1253         case SunHints.INTKEY_FRACTIONALMETRICS:
       
  1254             return SunHints.Value.get(SunHints.INTKEY_FRACTIONALMETRICS,
       
  1255                                       fractionalMetricsHint);
       
  1256         case SunHints.INTKEY_AATEXT_LCD_CONTRAST:
       
  1257             return new Integer(lcdTextContrast);
       
  1258         case SunHints.INTKEY_INTERPOLATION:
       
  1259             switch (interpolationHint) {
       
  1260             case SunHints.INTVAL_INTERPOLATION_NEAREST_NEIGHBOR:
       
  1261                 return SunHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;
       
  1262             case SunHints.INTVAL_INTERPOLATION_BILINEAR:
       
  1263                 return SunHints.VALUE_INTERPOLATION_BILINEAR;
       
  1264             case SunHints.INTVAL_INTERPOLATION_BICUBIC:
       
  1265                 return SunHints.VALUE_INTERPOLATION_BICUBIC;
       
  1266             }
       
  1267             return null;
       
  1268         case SunHints.INTKEY_STROKE_CONTROL:
       
  1269             return SunHints.Value.get(SunHints.INTKEY_STROKE_CONTROL,
       
  1270                                       strokeHint);
       
  1271         }
       
  1272         return null;
       
  1273     }
       
  1274 
       
  1275     /**
       
  1276      * Sets the preferences for the rendering algorithms.
       
  1277      * Hint categories include controls for rendering quality and
       
  1278      * overall time/quality trade-off in the rendering process.
       
  1279      * @param hints The rendering hints to be set
       
  1280      * @see RenderingHints
       
  1281      */
       
  1282     public void setRenderingHints(Map<?,?> hints) {
       
  1283         this.hints = null;
       
  1284         renderHint = SunHints.INTVAL_RENDER_DEFAULT;
       
  1285         antialiasHint = SunHints.INTVAL_ANTIALIAS_OFF;
       
  1286         textAntialiasHint = SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT;
       
  1287         fractionalMetricsHint = SunHints.INTVAL_FRACTIONALMETRICS_OFF;
       
  1288         lcdTextContrast = lcdTextContrastDefaultValue;
       
  1289         interpolationHint = -1;
       
  1290         interpolationType = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
       
  1291         boolean customHintPresent = false;
       
  1292         Iterator iter = hints.keySet().iterator();
       
  1293         while (iter.hasNext()) {
       
  1294             Object key = iter.next();
       
  1295             if (key == SunHints.KEY_RENDERING ||
       
  1296                 key == SunHints.KEY_ANTIALIASING ||
       
  1297                 key == SunHints.KEY_TEXT_ANTIALIASING ||
       
  1298                 key == SunHints.KEY_FRACTIONALMETRICS ||
       
  1299                 key == SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST ||
       
  1300                 key == SunHints.KEY_STROKE_CONTROL ||
       
  1301                 key == SunHints.KEY_INTERPOLATION)
       
  1302             {
       
  1303                 setRenderingHint((Key) key, hints.get(key));
       
  1304             } else {
       
  1305                 customHintPresent = true;
       
  1306             }
       
  1307         }
       
  1308         if (customHintPresent) {
       
  1309             this.hints = makeHints(hints);
       
  1310         }
       
  1311         invalidatePipe();
       
  1312     }
       
  1313 
       
  1314     /**
       
  1315      * Adds a number of preferences for the rendering algorithms.
       
  1316      * Hint categories include controls for rendering quality and
       
  1317      * overall time/quality trade-off in the rendering process.
       
  1318      * @param hints The rendering hints to be set
       
  1319      * @see RenderingHints
       
  1320      */
       
  1321     public void addRenderingHints(Map<?,?> hints) {
       
  1322         boolean customHintPresent = false;
       
  1323         Iterator iter = hints.keySet().iterator();
       
  1324         while (iter.hasNext()) {
       
  1325             Object key = iter.next();
       
  1326             if (key == SunHints.KEY_RENDERING ||
       
  1327                 key == SunHints.KEY_ANTIALIASING ||
       
  1328                 key == SunHints.KEY_TEXT_ANTIALIASING ||
       
  1329                 key == SunHints.KEY_FRACTIONALMETRICS ||
       
  1330                 key == SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST ||
       
  1331                 key == SunHints.KEY_STROKE_CONTROL ||
       
  1332                 key == SunHints.KEY_INTERPOLATION)
       
  1333             {
       
  1334                 setRenderingHint((Key) key, hints.get(key));
       
  1335             } else {
       
  1336                 customHintPresent = true;
       
  1337             }
       
  1338         }
       
  1339         if (customHintPresent) {
       
  1340             if (this.hints == null) {
       
  1341                 this.hints = makeHints(hints);
       
  1342             } else {
       
  1343                 this.hints.putAll(hints);
       
  1344             }
       
  1345         }
       
  1346     }
       
  1347 
       
  1348     /**
       
  1349      * Gets the preferences for the rendering algorithms.
       
  1350      * Hint categories include controls for rendering quality and
       
  1351      * overall time/quality trade-off in the rendering process.
       
  1352      * @see RenderingHints
       
  1353      */
       
  1354     public RenderingHints getRenderingHints() {
       
  1355         if (hints == null) {
       
  1356             return makeHints(null);
       
  1357         } else {
       
  1358             return (RenderingHints) hints.clone();
       
  1359         }
       
  1360     }
       
  1361 
       
  1362     RenderingHints makeHints(Map hints) {
       
  1363         RenderingHints model = new RenderingHints(hints);
       
  1364         model.put(SunHints.KEY_RENDERING,
       
  1365                   SunHints.Value.get(SunHints.INTKEY_RENDERING,
       
  1366                                      renderHint));
       
  1367         model.put(SunHints.KEY_ANTIALIASING,
       
  1368                   SunHints.Value.get(SunHints.INTKEY_ANTIALIASING,
       
  1369                                      antialiasHint));
       
  1370         model.put(SunHints.KEY_TEXT_ANTIALIASING,
       
  1371                   SunHints.Value.get(SunHints.INTKEY_TEXT_ANTIALIASING,
       
  1372                                      textAntialiasHint));
       
  1373         model.put(SunHints.KEY_FRACTIONALMETRICS,
       
  1374                   SunHints.Value.get(SunHints.INTKEY_FRACTIONALMETRICS,
       
  1375                                      fractionalMetricsHint));
       
  1376         model.put(SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST,
       
  1377                   new Integer(lcdTextContrast));
       
  1378         Object value;
       
  1379         switch (interpolationHint) {
       
  1380         case SunHints.INTVAL_INTERPOLATION_NEAREST_NEIGHBOR:
       
  1381             value = SunHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;
       
  1382             break;
       
  1383         case SunHints.INTVAL_INTERPOLATION_BILINEAR:
       
  1384             value = SunHints.VALUE_INTERPOLATION_BILINEAR;
       
  1385             break;
       
  1386         case SunHints.INTVAL_INTERPOLATION_BICUBIC:
       
  1387             value = SunHints.VALUE_INTERPOLATION_BICUBIC;
       
  1388             break;
       
  1389         default:
       
  1390             value = null;
       
  1391             break;
       
  1392         }
       
  1393         if (value != null) {
       
  1394             model.put(SunHints.KEY_INTERPOLATION, value);
       
  1395         }
       
  1396         model.put(SunHints.KEY_STROKE_CONTROL,
       
  1397                   SunHints.Value.get(SunHints.INTKEY_STROKE_CONTROL,
       
  1398                                      strokeHint));
       
  1399         return model;
       
  1400     }
       
  1401 
       
  1402     /**
       
  1403      * Concatenates the current transform of this Graphics2D with a
       
  1404      * translation transformation.
       
  1405      * This is equivalent to calling transform(T), where T is an
       
  1406      * AffineTransform represented by the following matrix:
       
  1407      * <pre>
       
  1408      *          [   1    0    tx  ]
       
  1409      *          [   0    1    ty  ]
       
  1410      *          [   0    0    1   ]
       
  1411      * </pre>
       
  1412      */
       
  1413     public void translate(double tx, double ty) {
       
  1414         transform.translate(tx, ty);
       
  1415         invalidateTransform();
       
  1416     }
       
  1417 
       
  1418     /**
       
  1419      * Concatenates the current transform of this Graphics2D with a
       
  1420      * rotation transformation.
       
  1421      * This is equivalent to calling transform(R), where R is an
       
  1422      * AffineTransform represented by the following matrix:
       
  1423      * <pre>
       
  1424      *          [   cos(theta)    -sin(theta)    0   ]
       
  1425      *          [   sin(theta)     cos(theta)    0   ]
       
  1426      *          [       0              0         1   ]
       
  1427      * </pre>
       
  1428      * Rotating with a positive angle theta rotates points on the positive
       
  1429      * x axis toward the positive y axis.
       
  1430      * @param theta The angle of rotation in radians.
       
  1431      */
       
  1432     public void rotate(double theta) {
       
  1433         transform.rotate(theta);
       
  1434         invalidateTransform();
       
  1435     }
       
  1436 
       
  1437     /**
       
  1438      * Concatenates the current transform of this Graphics2D with a
       
  1439      * translated rotation transformation.
       
  1440      * This is equivalent to the following sequence of calls:
       
  1441      * <pre>
       
  1442      *          translate(x, y);
       
  1443      *          rotate(theta);
       
  1444      *          translate(-x, -y);
       
  1445      * </pre>
       
  1446      * Rotating with a positive angle theta rotates points on the positive
       
  1447      * x axis toward the positive y axis.
       
  1448      * @param theta The angle of rotation in radians.
       
  1449      * @param x The x coordinate of the origin of the rotation
       
  1450      * @param y The x coordinate of the origin of the rotation
       
  1451      */
       
  1452     public void rotate(double theta, double x, double y) {
       
  1453         transform.rotate(theta, x, y);
       
  1454         invalidateTransform();
       
  1455     }
       
  1456 
       
  1457     /**
       
  1458      * Concatenates the current transform of this Graphics2D with a
       
  1459      * scaling transformation.
       
  1460      * This is equivalent to calling transform(S), where S is an
       
  1461      * AffineTransform represented by the following matrix:
       
  1462      * <pre>
       
  1463      *          [   sx   0    0   ]
       
  1464      *          [   0    sy   0   ]
       
  1465      *          [   0    0    1   ]
       
  1466      * </pre>
       
  1467      */
       
  1468     public void scale(double sx, double sy) {
       
  1469         transform.scale(sx, sy);
       
  1470         invalidateTransform();
       
  1471     }
       
  1472 
       
  1473     /**
       
  1474      * Concatenates the current transform of this Graphics2D with a
       
  1475      * shearing transformation.
       
  1476      * This is equivalent to calling transform(SH), where SH is an
       
  1477      * AffineTransform represented by the following matrix:
       
  1478      * <pre>
       
  1479      *          [   1   shx   0   ]
       
  1480      *          [  shy   1    0   ]
       
  1481      *          [   0    0    1   ]
       
  1482      * </pre>
       
  1483      * @param shx The factor by which coordinates are shifted towards the
       
  1484      * positive X axis direction according to their Y coordinate
       
  1485      * @param shy The factor by which coordinates are shifted towards the
       
  1486      * positive Y axis direction according to their X coordinate
       
  1487      */
       
  1488     public void shear(double shx, double shy) {
       
  1489         transform.shear(shx, shy);
       
  1490         invalidateTransform();
       
  1491     }
       
  1492 
       
  1493     /**
       
  1494      * Composes a Transform object with the transform in this
       
  1495      * Graphics2D according to the rule last-specified-first-applied.
       
  1496      * If the currrent transform is Cx, the result of composition
       
  1497      * with Tx is a new transform Cx'.  Cx' becomes the current
       
  1498      * transform for this Graphics2D.
       
  1499      * Transforming a point p by the updated transform Cx' is
       
  1500      * equivalent to first transforming p by Tx and then transforming
       
  1501      * the result by the original transform Cx.  In other words,
       
  1502      * Cx'(p) = Cx(Tx(p)).
       
  1503      * A copy of the Tx is made, if necessary, so further
       
  1504      * modifications to Tx do not affect rendering.
       
  1505      * @param Tx The Transform object to be composed with the current
       
  1506      * transform.
       
  1507      * @see #setTransform
       
  1508      * @see AffineTransform
       
  1509      */
       
  1510     public void transform(AffineTransform xform) {
       
  1511         this.transform.concatenate(xform);
       
  1512         invalidateTransform();
       
  1513     }
       
  1514 
       
  1515     /**
       
  1516      * Translate
       
  1517      */
       
  1518     public void translate(int x, int y) {
       
  1519         transform.translate(x, y);
       
  1520         if (transformState <= TRANSFORM_INT_TRANSLATE) {
       
  1521             transX += x;
       
  1522             transY += y;
       
  1523             transformState = (((transX | transY) == 0) ?
       
  1524                               TRANSFORM_ISIDENT : TRANSFORM_INT_TRANSLATE);
       
  1525         } else {
       
  1526             invalidateTransform();
       
  1527         }
       
  1528     }
       
  1529 
       
  1530     /**
       
  1531      * Sets the Transform in the current graphics state.
       
  1532      * @param Tx The Transform object to be used in the rendering process.
       
  1533      * @see #transform
       
  1534      * @see TransformChain
       
  1535      * @see AffineTransform
       
  1536      */
       
  1537     public void setTransform(AffineTransform Tx) {
       
  1538         if ((constrainX|constrainY) == 0) {
       
  1539             transform.setTransform(Tx);
       
  1540         } else {
       
  1541             transform.setToTranslation(constrainX, constrainY);
       
  1542             transform.concatenate(Tx);
       
  1543         }
       
  1544         invalidateTransform();
       
  1545     }
       
  1546 
       
  1547     protected void invalidateTransform() {
       
  1548         int type = transform.getType();
       
  1549         int origTransformState = transformState;
       
  1550         if (type == AffineTransform.TYPE_IDENTITY) {
       
  1551             transformState = TRANSFORM_ISIDENT;
       
  1552             transX = transY = 0;
       
  1553         } else if (type == AffineTransform.TYPE_TRANSLATION) {
       
  1554             double dtx = transform.getTranslateX();
       
  1555             double dty = transform.getTranslateY();
       
  1556             transX = (int) Math.floor(dtx + 0.5);
       
  1557             transY = (int) Math.floor(dty + 0.5);
       
  1558             if (dtx == transX && dty == transY) {
       
  1559                 transformState = TRANSFORM_INT_TRANSLATE;
       
  1560             } else {
       
  1561                 transformState = TRANSFORM_ANY_TRANSLATE;
       
  1562             }
       
  1563         } else if ((type & (AffineTransform.TYPE_FLIP |
       
  1564                             AffineTransform.TYPE_MASK_ROTATION |
       
  1565                             AffineTransform.TYPE_GENERAL_TRANSFORM)) == 0)
       
  1566         {
       
  1567             transformState = TRANSFORM_TRANSLATESCALE;
       
  1568             transX = transY = 0;
       
  1569         } else {
       
  1570             transformState = TRANSFORM_GENERIC;
       
  1571             transX = transY = 0;
       
  1572         }
       
  1573 
       
  1574         if (transformState >= TRANSFORM_TRANSLATESCALE ||
       
  1575             origTransformState >= TRANSFORM_TRANSLATESCALE)
       
  1576         {
       
  1577             /* Its only in this case that the previous or current transform
       
  1578              * was more than a translate that font info is invalidated
       
  1579              */
       
  1580             cachedFRC = null;
       
  1581             this.validFontInfo = false;
       
  1582             this.fontMetrics = null;
       
  1583             this.glyphVectorFontInfo = null;
       
  1584 
       
  1585             if (transformState != origTransformState) {
       
  1586                 invalidatePipe();
       
  1587             }
       
  1588         }
       
  1589         if (strokeState != STROKE_CUSTOM) {
       
  1590             validateBasicStroke((BasicStroke) stroke);
       
  1591         }
       
  1592     }
       
  1593 
       
  1594     /**
       
  1595      * Returns the current Transform in the Graphics2D state.
       
  1596      * @see #transform
       
  1597      * @see #setTransform
       
  1598      */
       
  1599     public AffineTransform getTransform() {
       
  1600         if ((constrainX|constrainY) == 0) {
       
  1601             return new AffineTransform(transform);
       
  1602         }
       
  1603         AffineTransform tx =
       
  1604             AffineTransform.getTranslateInstance(-constrainX, -constrainY);
       
  1605         tx.concatenate(transform);
       
  1606         return tx;
       
  1607     }
       
  1608 
       
  1609     /**
       
  1610      * Returns the current Transform ignoring the "constrain"
       
  1611      * rectangle.
       
  1612      */
       
  1613     public AffineTransform cloneTransform() {
       
  1614         return new AffineTransform(transform);
       
  1615     }
       
  1616 
       
  1617     /**
       
  1618      * Returns the current Paint in the Graphics2D state.
       
  1619      * @see #setPaint
       
  1620      * @see java.awt.Graphics#setColor
       
  1621      */
       
  1622     public Paint getPaint() {
       
  1623         return paint;
       
  1624     }
       
  1625 
       
  1626     /**
       
  1627      * Returns the current Composite in the Graphics2D state.
       
  1628      * @see #setComposite
       
  1629      */
       
  1630     public Composite getComposite() {
       
  1631         return composite;
       
  1632     }
       
  1633 
       
  1634     public Color getColor() {
       
  1635         return foregroundColor;
       
  1636     }
       
  1637 
       
  1638     /*
       
  1639      * Validate the eargb and pixel fields against the current color.
       
  1640      *
       
  1641      * The eargb field must take into account the extraAlpha
       
  1642      * value of an AlphaComposite.  It may also take into account
       
  1643      * the Fsrc Porter-Duff blending function if such a function is
       
  1644      * a constant (see handling of Clear mode below).  For instance,
       
  1645      * by factoring in the (Fsrc == 0) state of the Clear mode we can
       
  1646      * use a SrcNoEa loop just as easily as a general Alpha loop
       
  1647      * since the math will be the same in both cases.
       
  1648      *
       
  1649      * The pixel field will always be the best pixel data choice for
       
  1650      * the final result of all calculations applied to the eargb field.
       
  1651      *
       
  1652      * Note that this method is only necessary under the following
       
  1653      * conditions:
       
  1654      *     (paintState <= PAINT_ALPHA_COLOR &&
       
  1655      *      compositeState <= COMP_CUSTOM)
       
  1656      * though nothing bad will happen if it is run in other states.
       
  1657      */
       
  1658     final void validateColor() {
       
  1659         int eargb;
       
  1660         if (imageComp == CompositeType.Clear) {
       
  1661             eargb = 0;
       
  1662         } else {
       
  1663             eargb = foregroundColor.getRGB();
       
  1664             if (compositeState <= COMP_ALPHA &&
       
  1665                 imageComp != CompositeType.SrcNoEa &&
       
  1666                 imageComp != CompositeType.SrcOverNoEa)
       
  1667             {
       
  1668                 AlphaComposite alphacomp = (AlphaComposite) composite;
       
  1669                 int a = Math.round(alphacomp.getAlpha() * (eargb >>> 24));
       
  1670                 eargb = (eargb & 0x00ffffff) | (a << 24);
       
  1671             }
       
  1672         }
       
  1673         this.eargb = eargb;
       
  1674         this.pixel = surfaceData.pixelFor(eargb);
       
  1675     }
       
  1676 
       
  1677     public void setColor(Color color) {
       
  1678         if (color == null || color == paint) {
       
  1679             return;
       
  1680         }
       
  1681         this.paint = foregroundColor = color;
       
  1682         validateColor();
       
  1683         if ((eargb >> 24) == -1) {
       
  1684             if (paintState == PAINT_OPAQUECOLOR) {
       
  1685                 return;
       
  1686             }
       
  1687             paintState = PAINT_OPAQUECOLOR;
       
  1688             if (imageComp == CompositeType.SrcOverNoEa) {
       
  1689                 // special case where compState depends on opacity of paint
       
  1690                 compositeState = COMP_ISCOPY;
       
  1691             }
       
  1692         } else {
       
  1693             if (paintState == PAINT_ALPHACOLOR) {
       
  1694                 return;
       
  1695             }
       
  1696             paintState = PAINT_ALPHACOLOR;
       
  1697             if (imageComp == CompositeType.SrcOverNoEa) {
       
  1698                 // special case where compState depends on opacity of paint
       
  1699                 compositeState = COMP_ALPHA;
       
  1700             }
       
  1701         }
       
  1702         validFontInfo = false;
       
  1703         invalidatePipe();
       
  1704     }
       
  1705 
       
  1706     /**
       
  1707      * Sets the background color in this context used for clearing a region.
       
  1708      * When Graphics2D is constructed for a component, the backgroung color is
       
  1709      * inherited from the component. Setting the background color in the
       
  1710      * Graphics2D context only affects the subsequent clearRect() calls and
       
  1711      * not the background color of the component. To change the background
       
  1712      * of the component, use appropriate methods of the component.
       
  1713      * @param color The background color that should be used in
       
  1714      * subsequent calls to clearRect().
       
  1715      * @see getBackground
       
  1716      * @see Graphics.clearRect()
       
  1717      */
       
  1718     public void setBackground(Color color) {
       
  1719         backgroundColor = color;
       
  1720     }
       
  1721 
       
  1722     /**
       
  1723      * Returns the background color used for clearing a region.
       
  1724      * @see setBackground
       
  1725      */
       
  1726     public Color getBackground() {
       
  1727         return backgroundColor;
       
  1728     }
       
  1729 
       
  1730     /**
       
  1731      * Returns the current Stroke in the Graphics2D state.
       
  1732      * @see setStroke
       
  1733      */
       
  1734     public Stroke getStroke() {
       
  1735         return stroke;
       
  1736     }
       
  1737 
       
  1738     public Rectangle getClipBounds() {
       
  1739         Rectangle r;
       
  1740         if (clipState == CLIP_DEVICE) {
       
  1741             r = null;
       
  1742         } else if (transformState <= TRANSFORM_INT_TRANSLATE) {
       
  1743             if (usrClip instanceof Rectangle) {
       
  1744                 r = new Rectangle((Rectangle) usrClip);
       
  1745             } else {
       
  1746                 r = usrClip.getBounds();
       
  1747             }
       
  1748             r.translate(-transX, -transY);
       
  1749         } else {
       
  1750             r = getClip().getBounds();
       
  1751         }
       
  1752         return r;
       
  1753     }
       
  1754 
       
  1755     public Rectangle getClipBounds(Rectangle r) {
       
  1756         if (clipState != CLIP_DEVICE) {
       
  1757             if (transformState <= TRANSFORM_INT_TRANSLATE) {
       
  1758                 if (usrClip instanceof Rectangle) {
       
  1759                     r.setBounds((Rectangle) usrClip);
       
  1760                 } else {
       
  1761                     r.setBounds(usrClip.getBounds());
       
  1762                 }
       
  1763                 r.translate(-transX, -transY);
       
  1764             } else {
       
  1765                 r.setBounds(getClip().getBounds());
       
  1766             }
       
  1767         } else if (r == null) {
       
  1768             throw new NullPointerException("null rectangle parameter");
       
  1769         }
       
  1770         return r;
       
  1771     }
       
  1772 
       
  1773     public boolean hitClip(int x, int y, int width, int height) {
       
  1774         if (width <= 0 || height <= 0) {
       
  1775             return false;
       
  1776         }
       
  1777         if (transformState > TRANSFORM_INT_TRANSLATE) {
       
  1778             // Note: Technically the most accurate test would be to
       
  1779             // raster scan the parallelogram of the transformed rectangle
       
  1780             // and do a span for span hit test against the clip, but for
       
  1781             // speed we approximate the test with a bounding box of the
       
  1782             // transformed rectangle.  The cost of rasterizing the
       
  1783             // transformed rectangle is probably high enough that it is
       
  1784             // not worth doing so to save the caller from having to call
       
  1785             // a rendering method where we will end up discovering the
       
  1786             // same answer in about the same amount of time anyway.
       
  1787             // This logic breaks down if this hit test is being performed
       
  1788             // on the bounds of a group of shapes in which case it might
       
  1789             // be beneficial to be a little more accurate to avoid lots
       
  1790             // of subsequent rendering calls.  In either case, this relaxed
       
  1791             // test should not be significantly less accurate than the
       
  1792             // optimal test for most transforms and so the conservative
       
  1793             // answer should not cause too much extra work.
       
  1794 
       
  1795             double d[] = {
       
  1796                 x, y,
       
  1797                 x+width, y,
       
  1798                 x, y+height,
       
  1799                 x+width, y+height
       
  1800             };
       
  1801             transform.transform(d, 0, d, 0, 4);
       
  1802             x = (int) Math.floor(Math.min(Math.min(d[0], d[2]),
       
  1803                                           Math.min(d[4], d[6])));
       
  1804             y = (int) Math.floor(Math.min(Math.min(d[1], d[3]),
       
  1805                                           Math.min(d[5], d[7])));
       
  1806             width = (int) Math.ceil(Math.max(Math.max(d[0], d[2]),
       
  1807                                              Math.max(d[4], d[6])));
       
  1808             height = (int) Math.ceil(Math.max(Math.max(d[1], d[3]),
       
  1809                                               Math.max(d[5], d[7])));
       
  1810         } else {
       
  1811             x += transX;
       
  1812             y += transY;
       
  1813             width += x;
       
  1814             height += y;
       
  1815         }
       
  1816         if (!getCompClip().intersectsQuickCheckXYXY(x, y, width, height)) {
       
  1817             return false;
       
  1818         }
       
  1819         // REMIND: We could go one step further here and examine the
       
  1820         // non-rectangular clip shape more closely if there is one.
       
  1821         // Since the clip has already been rasterized, the performance
       
  1822         // penalty of doing the scan is probably still within the bounds
       
  1823         // of a good tradeoff between speed and quality of the answer.
       
  1824         return true;
       
  1825     }
       
  1826 
       
  1827     protected void validateCompClip() {
       
  1828         int origClipState = clipState;
       
  1829         if (usrClip == null) {
       
  1830             clipState = CLIP_DEVICE;
       
  1831             clipRegion = devClip;
       
  1832         } else if (usrClip instanceof Rectangle2D) {
       
  1833             clipState = CLIP_RECTANGULAR;
       
  1834             if (usrClip instanceof Rectangle) {
       
  1835                 clipRegion = devClip.getIntersection((Rectangle)usrClip);
       
  1836             } else {
       
  1837                 clipRegion = devClip.getIntersection(usrClip.getBounds());
       
  1838             }
       
  1839         } else {
       
  1840             PathIterator cpi = usrClip.getPathIterator(null);
       
  1841             int box[] = new int[4];
       
  1842             ShapeSpanIterator sr = LoopPipe.getFillSSI(this);
       
  1843             try {
       
  1844                 sr.setOutputArea(devClip);
       
  1845                 sr.appendPath(cpi);
       
  1846                 sr.getPathBox(box);
       
  1847                 Region r = Region.getInstance(box);
       
  1848                 r.appendSpans(sr);
       
  1849                 clipRegion = r;
       
  1850                 clipState =
       
  1851                     r.isRectangular() ? CLIP_RECTANGULAR : CLIP_SHAPE;
       
  1852             } finally {
       
  1853                 sr.dispose();
       
  1854             }
       
  1855         }
       
  1856         if (origClipState != clipState &&
       
  1857             (clipState == CLIP_SHAPE || origClipState == CLIP_SHAPE))
       
  1858         {
       
  1859             validFontInfo = false;
       
  1860             invalidatePipe();
       
  1861         }
       
  1862     }
       
  1863 
       
  1864     static final int NON_RECTILINEAR_TRANSFORM_MASK =
       
  1865         (AffineTransform.TYPE_GENERAL_TRANSFORM |
       
  1866          AffineTransform.TYPE_GENERAL_ROTATION);
       
  1867 
       
  1868     protected Shape transformShape(Shape s) {
       
  1869         if (s == null) {
       
  1870             return null;
       
  1871         }
       
  1872         if (transformState > TRANSFORM_INT_TRANSLATE) {
       
  1873             return transformShape(transform, s);
       
  1874         } else {
       
  1875             return transformShape(transX, transY, s);
       
  1876         }
       
  1877     }
       
  1878 
       
  1879     public Shape untransformShape(Shape s) {
       
  1880         if (s == null) {
       
  1881             return null;
       
  1882         }
       
  1883         if (transformState > TRANSFORM_INT_TRANSLATE) {
       
  1884             try {
       
  1885                 return transformShape(transform.createInverse(), s);
       
  1886             } catch (NoninvertibleTransformException e) {
       
  1887                 return null;
       
  1888             }
       
  1889         } else {
       
  1890             return transformShape(-transX, -transY, s);
       
  1891         }
       
  1892     }
       
  1893 
       
  1894     protected static Shape transformShape(int tx, int ty, Shape s) {
       
  1895         if (s == null) {
       
  1896             return null;
       
  1897         }
       
  1898 
       
  1899         if (s instanceof Rectangle) {
       
  1900             Rectangle r = s.getBounds();
       
  1901             r.translate(tx, ty);
       
  1902             return r;
       
  1903         }
       
  1904         if (s instanceof Rectangle2D) {
       
  1905             Rectangle2D rect = (Rectangle2D) s;
       
  1906             return new Rectangle2D.Double(rect.getX() + tx,
       
  1907                                           rect.getY() + ty,
       
  1908                                           rect.getWidth(),
       
  1909                                           rect.getHeight());
       
  1910         }
       
  1911 
       
  1912         if (tx == 0 && ty == 0) {
       
  1913             return cloneShape(s);
       
  1914         }
       
  1915 
       
  1916         AffineTransform mat = AffineTransform.getTranslateInstance(tx, ty);
       
  1917         return mat.createTransformedShape(s);
       
  1918     }
       
  1919 
       
  1920     protected static Shape transformShape(AffineTransform tx, Shape clip) {
       
  1921         if (clip == null) {
       
  1922             return null;
       
  1923         }
       
  1924 
       
  1925         if (clip instanceof Rectangle2D &&
       
  1926             (tx.getType() & NON_RECTILINEAR_TRANSFORM_MASK) == 0)
       
  1927         {
       
  1928             Rectangle2D rect = (Rectangle2D) clip;
       
  1929             double matrix[] = new double[4];
       
  1930             matrix[0] = rect.getX();
       
  1931             matrix[1] = rect.getY();
       
  1932             matrix[2] = matrix[0] + rect.getWidth();
       
  1933             matrix[3] = matrix[1] + rect.getHeight();
       
  1934             tx.transform(matrix, 0, matrix, 0, 2);
       
  1935             rect = new Rectangle2D.Float();
       
  1936             rect.setFrameFromDiagonal(matrix[0], matrix[1],
       
  1937                                       matrix[2], matrix[3]);
       
  1938             return rect;
       
  1939         }
       
  1940 
       
  1941         if (tx.isIdentity()) {
       
  1942             return cloneShape(clip);
       
  1943         }
       
  1944 
       
  1945         return tx.createTransformedShape(clip);
       
  1946     }
       
  1947 
       
  1948     public void clipRect(int x, int y, int w, int h) {
       
  1949         clip(new Rectangle(x, y, w, h));
       
  1950     }
       
  1951 
       
  1952     public void setClip(int x, int y, int w, int h) {
       
  1953         setClip(new Rectangle(x, y, w, h));
       
  1954     }
       
  1955 
       
  1956     public Shape getClip() {
       
  1957         return untransformShape(usrClip);
       
  1958     }
       
  1959 
       
  1960     public void setClip(Shape sh) {
       
  1961         usrClip = transformShape(sh);
       
  1962         validateCompClip();
       
  1963     }
       
  1964 
       
  1965     /**
       
  1966      * Intersects the current clip with the specified Path and sets the
       
  1967      * current clip to the resulting intersection. The clip is transformed
       
  1968      * with the current transform in the Graphics2D state before being
       
  1969      * intersected with the current clip. This method is used to make the
       
  1970      * current clip smaller. To make the clip larger, use any setClip method.
       
  1971      * @param p The Path to be intersected with the current clip.
       
  1972      */
       
  1973     public void clip(Shape s) {
       
  1974         s = transformShape(s);
       
  1975         if (usrClip != null) {
       
  1976             s = intersectShapes(usrClip, s, true, true);
       
  1977         }
       
  1978         usrClip = s;
       
  1979         validateCompClip();
       
  1980     }
       
  1981 
       
  1982     public void setPaintMode() {
       
  1983         setComposite(AlphaComposite.SrcOver);
       
  1984     }
       
  1985 
       
  1986     public void setXORMode(Color c) {
       
  1987         if (c == null) {
       
  1988             throw new IllegalArgumentException("null XORColor");
       
  1989         }
       
  1990         setComposite(new XORComposite(c, surfaceData));
       
  1991     }
       
  1992 
       
  1993     Blit lastCAblit;
       
  1994     Composite lastCAcomp;
       
  1995 
       
  1996     public void copyArea(int x, int y, int w, int h, int dx, int dy) {
       
  1997         try {
       
  1998             doCopyArea(x, y, w, h, dx, dy);
       
  1999         } catch (InvalidPipeException e) {
       
  2000             revalidateAll();
       
  2001             try {
       
  2002                 doCopyArea(x, y, w, h, dx, dy);
       
  2003             } catch (InvalidPipeException e2) {
       
  2004                 // Still catching the exception; we are not yet ready to
       
  2005                 // validate the surfaceData correctly.  Fail for now and
       
  2006                 // try again next time around.
       
  2007             }
       
  2008         } finally {
       
  2009             surfaceData.markDirty();
       
  2010         }
       
  2011     }
       
  2012 
       
  2013     private void doCopyArea(int x, int y, int w, int h, int dx, int dy) {
       
  2014         if (w <= 0 || h <= 0) {
       
  2015             return;
       
  2016         }
       
  2017         SurfaceData theData = surfaceData;
       
  2018         if (theData.copyArea(this, x, y, w, h, dx, dy)) {
       
  2019             return;
       
  2020         }
       
  2021         if (transformState >= TRANSFORM_TRANSLATESCALE) {
       
  2022             throw new InternalError("transformed copyArea not implemented yet");
       
  2023         }
       
  2024         // REMIND: This method does not deal with missing data from the
       
  2025         // source object (i.e. it does not send exposure events...)
       
  2026 
       
  2027         Region clip = getCompClip();
       
  2028 
       
  2029         Composite comp = composite;
       
  2030         if (lastCAcomp != comp) {
       
  2031             SurfaceType dsttype = theData.getSurfaceType();
       
  2032             CompositeType comptype = imageComp;
       
  2033             if (CompositeType.SrcOverNoEa.equals(comptype) &&
       
  2034                 theData.getTransparency() == Transparency.OPAQUE)
       
  2035             {
       
  2036                 comptype = CompositeType.SrcNoEa;
       
  2037             }
       
  2038             lastCAblit = Blit.locate(dsttype, comptype, dsttype);
       
  2039             lastCAcomp = comp;
       
  2040         }
       
  2041 
       
  2042         x += transX;
       
  2043         y += transY;
       
  2044 
       
  2045         Blit ob = lastCAblit;
       
  2046         if (dy == 0 && dx > 0 && dx < w) {
       
  2047             while (w > 0) {
       
  2048                 int partW = Math.min(w, dx);
       
  2049                 w -= partW;
       
  2050                 int sx = x + w;
       
  2051                 ob.Blit(theData, theData, comp, clip,
       
  2052                         sx, y, sx+dx, y+dy, partW, h);
       
  2053             }
       
  2054             return;
       
  2055         }
       
  2056         if (dy > 0 && dy < h && dx > -w && dx < w) {
       
  2057             while (h > 0) {
       
  2058                 int partH = Math.min(h, dy);
       
  2059                 h -= partH;
       
  2060                 int sy = y + h;
       
  2061                 ob.Blit(theData, theData, comp, clip,
       
  2062                         x, sy, x+dx, sy+dy, w, partH);
       
  2063             }
       
  2064             return;
       
  2065         }
       
  2066         ob.Blit(theData, theData, comp, clip, x, y, x+dx, y+dy, w, h);
       
  2067     }
       
  2068 
       
  2069     /*
       
  2070     public void XcopyArea(int x, int y, int w, int h, int dx, int dy) {
       
  2071         Rectangle rect = new Rectangle(x, y, w, h);
       
  2072         rect = transformBounds(rect, transform);
       
  2073         Point2D    point = new Point2D.Float(dx, dy);
       
  2074         Point2D    root  = new Point2D.Float(0, 0);
       
  2075         point = transform.transform(point, point);
       
  2076         root  = transform.transform(root, root);
       
  2077         int fdx = (int)(point.getX()-root.getX());
       
  2078         int fdy = (int)(point.getY()-root.getY());
       
  2079 
       
  2080         Rectangle r = getCompBounds().intersection(rect.getBounds());
       
  2081 
       
  2082         if (r.isEmpty()) {
       
  2083             return;
       
  2084         }
       
  2085 
       
  2086         // Begin Rasterizer for Clip Shape
       
  2087         boolean skipClip = true;
       
  2088         byte[] clipAlpha = null;
       
  2089 
       
  2090         if (clipState == CLIP_SHAPE) {
       
  2091 
       
  2092             int box[] = new int[4];
       
  2093 
       
  2094             clipRegion.getBounds(box);
       
  2095             Rectangle devR = new Rectangle(box[0], box[1],
       
  2096                                            box[2] - box[0],
       
  2097                                            box[3] - box[1]);
       
  2098             if (!devR.isEmpty()) {
       
  2099                 OutputManager mgr = getOutputManager();
       
  2100                 RegionIterator ri = clipRegion.getIterator();
       
  2101                 while (ri.nextYRange(box)) {
       
  2102                     int spany = box[1];
       
  2103                     int spanh = box[3] - spany;
       
  2104                     while (ri.nextXBand(box)) {
       
  2105                         int spanx = box[0];
       
  2106                         int spanw = box[2] - spanx;
       
  2107                         mgr.copyArea(this, null,
       
  2108                                      spanw, 0,
       
  2109                                      spanx, spany,
       
  2110                                      spanw, spanh,
       
  2111                                      fdx, fdy,
       
  2112                                      null);
       
  2113                     }
       
  2114                 }
       
  2115             }
       
  2116             return;
       
  2117         }
       
  2118         // End Rasterizer for Clip Shape
       
  2119 
       
  2120         getOutputManager().copyArea(this, null,
       
  2121                                     r.width, 0,
       
  2122                                     r.x, r.y, r.width,
       
  2123                                     r.height, fdx, fdy,
       
  2124                                     null);
       
  2125     }
       
  2126     */
       
  2127 
       
  2128     public void drawLine(int x1, int y1, int x2, int y2) {
       
  2129         try {
       
  2130             drawpipe.drawLine(this, x1, y1, x2, y2);
       
  2131         } catch (InvalidPipeException e) {
       
  2132             revalidateAll();
       
  2133             try {
       
  2134                 drawpipe.drawLine(this, x1, y1, x2, y2);
       
  2135             } catch (InvalidPipeException e2) {
       
  2136                 // Still catching the exception; we are not yet ready to
       
  2137                 // validate the surfaceData correctly.  Fail for now and
       
  2138                 // try again next time around.
       
  2139             }
       
  2140         } finally {
       
  2141             surfaceData.markDirty();
       
  2142         }
       
  2143     }
       
  2144 
       
  2145     public void drawRoundRect(int x, int y, int w, int h, int arcW, int arcH) {
       
  2146         try {
       
  2147             drawpipe.drawRoundRect(this, x, y, w, h, arcW, arcH);
       
  2148         } catch (InvalidPipeException e) {
       
  2149             revalidateAll();
       
  2150             try {
       
  2151                 drawpipe.drawRoundRect(this, x, y, w, h, arcW, arcH);
       
  2152             } catch (InvalidPipeException e2) {
       
  2153                 // Still catching the exception; we are not yet ready to
       
  2154                 // validate the surfaceData correctly.  Fail for now and
       
  2155                 // try again next time around.
       
  2156             }
       
  2157         } finally {
       
  2158             surfaceData.markDirty();
       
  2159         }
       
  2160     }
       
  2161 
       
  2162     public void fillRoundRect(int x, int y, int w, int h, int arcW, int arcH) {
       
  2163         try {
       
  2164             fillpipe.fillRoundRect(this, x, y, w, h, arcW, arcH);
       
  2165         } catch (InvalidPipeException e) {
       
  2166             revalidateAll();
       
  2167             try {
       
  2168                 fillpipe.fillRoundRect(this, x, y, w, h, arcW, arcH);
       
  2169             } catch (InvalidPipeException e2) {
       
  2170                 // Still catching the exception; we are not yet ready to
       
  2171                 // validate the surfaceData correctly.  Fail for now and
       
  2172                 // try again next time around.
       
  2173             }
       
  2174         } finally {
       
  2175             surfaceData.markDirty();
       
  2176         }
       
  2177     }
       
  2178 
       
  2179     public void drawOval(int x, int y, int w, int h) {
       
  2180         try {
       
  2181             drawpipe.drawOval(this, x, y, w, h);
       
  2182         } catch (InvalidPipeException e) {
       
  2183             revalidateAll();
       
  2184             try {
       
  2185                 drawpipe.drawOval(this, x, y, w, h);
       
  2186             } catch (InvalidPipeException e2) {
       
  2187                 // Still catching the exception; we are not yet ready to
       
  2188                 // validate the surfaceData correctly.  Fail for now and
       
  2189                 // try again next time around.
       
  2190             }
       
  2191         } finally {
       
  2192             surfaceData.markDirty();
       
  2193         }
       
  2194     }
       
  2195 
       
  2196     public void fillOval(int x, int y, int w, int h) {
       
  2197         try {
       
  2198             fillpipe.fillOval(this, x, y, w, h);
       
  2199         } catch (InvalidPipeException e) {
       
  2200             revalidateAll();
       
  2201             try {
       
  2202                 fillpipe.fillOval(this, x, y, w, h);
       
  2203             } catch (InvalidPipeException e2) {
       
  2204                 // Still catching the exception; we are not yet ready to
       
  2205                 // validate the surfaceData correctly.  Fail for now and
       
  2206                 // try again next time around.
       
  2207             }
       
  2208         } finally {
       
  2209             surfaceData.markDirty();
       
  2210         }
       
  2211     }
       
  2212 
       
  2213     public void drawArc(int x, int y, int w, int h,
       
  2214                         int startAngl, int arcAngl) {
       
  2215         try {
       
  2216             drawpipe.drawArc(this, x, y, w, h, startAngl, arcAngl);
       
  2217         } catch (InvalidPipeException e) {
       
  2218             revalidateAll();
       
  2219             try {
       
  2220                 drawpipe.drawArc(this, x, y, w, h, startAngl, arcAngl);
       
  2221             } catch (InvalidPipeException e2) {
       
  2222                 // Still catching the exception; we are not yet ready to
       
  2223                 // validate the surfaceData correctly.  Fail for now and
       
  2224                 // try again next time around.
       
  2225             }
       
  2226         } finally {
       
  2227             surfaceData.markDirty();
       
  2228         }
       
  2229     }
       
  2230 
       
  2231     public void fillArc(int x, int y, int w, int h,
       
  2232                         int startAngl, int arcAngl) {
       
  2233         try {
       
  2234             fillpipe.fillArc(this, x, y, w, h, startAngl, arcAngl);
       
  2235         } catch (InvalidPipeException e) {
       
  2236             revalidateAll();
       
  2237             try {
       
  2238                 fillpipe.fillArc(this, x, y, w, h, startAngl, arcAngl);
       
  2239             } catch (InvalidPipeException e2) {
       
  2240                 // Still catching the exception; we are not yet ready to
       
  2241                 // validate the surfaceData correctly.  Fail for now and
       
  2242                 // try again next time around.
       
  2243             }
       
  2244         } finally {
       
  2245             surfaceData.markDirty();
       
  2246         }
       
  2247     }
       
  2248 
       
  2249     public void drawPolyline(int xPoints[], int yPoints[], int nPoints) {
       
  2250         try {
       
  2251             drawpipe.drawPolyline(this, xPoints, yPoints, nPoints);
       
  2252         } catch (InvalidPipeException e) {
       
  2253             revalidateAll();
       
  2254             try {
       
  2255                 drawpipe.drawPolyline(this, xPoints, yPoints, nPoints);
       
  2256             } catch (InvalidPipeException e2) {
       
  2257                 // Still catching the exception; we are not yet ready to
       
  2258                 // validate the surfaceData correctly.  Fail for now and
       
  2259                 // try again next time around.
       
  2260             }
       
  2261         } finally {
       
  2262             surfaceData.markDirty();
       
  2263         }
       
  2264     }
       
  2265 
       
  2266     public void drawPolygon(int xPoints[], int yPoints[], int nPoints) {
       
  2267         try {
       
  2268             drawpipe.drawPolygon(this, xPoints, yPoints, nPoints);
       
  2269         } catch (InvalidPipeException e) {
       
  2270             revalidateAll();
       
  2271             try {
       
  2272                 drawpipe.drawPolygon(this, xPoints, yPoints, nPoints);
       
  2273             } catch (InvalidPipeException e2) {
       
  2274                 // Still catching the exception; we are not yet ready to
       
  2275                 // validate the surfaceData correctly.  Fail for now and
       
  2276                 // try again next time around.
       
  2277             }
       
  2278         } finally {
       
  2279             surfaceData.markDirty();
       
  2280         }
       
  2281     }
       
  2282 
       
  2283     public void fillPolygon(int xPoints[], int yPoints[], int nPoints) {
       
  2284         try {
       
  2285             fillpipe.fillPolygon(this, xPoints, yPoints, nPoints);
       
  2286         } catch (InvalidPipeException e) {
       
  2287             revalidateAll();
       
  2288             try {
       
  2289                 fillpipe.fillPolygon(this, xPoints, yPoints, nPoints);
       
  2290             } catch (InvalidPipeException e2) {
       
  2291                 // Still catching the exception; we are not yet ready to
       
  2292                 // validate the surfaceData correctly.  Fail for now and
       
  2293                 // try again next time around.
       
  2294             }
       
  2295         } finally {
       
  2296             surfaceData.markDirty();
       
  2297         }
       
  2298     }
       
  2299 
       
  2300     public void drawRect (int x, int y, int w, int h) {
       
  2301         try {
       
  2302             drawpipe.drawRect(this, x, y, w, h);
       
  2303         } catch (InvalidPipeException e) {
       
  2304             revalidateAll();
       
  2305             try {
       
  2306                 drawpipe.drawRect(this, x, y, w, h);
       
  2307             } catch (InvalidPipeException e2) {
       
  2308                 // Still catching the exception; we are not yet ready to
       
  2309                 // validate the surfaceData correctly.  Fail for now and
       
  2310                 // try again next time around.
       
  2311             }
       
  2312         } finally {
       
  2313             surfaceData.markDirty();
       
  2314         }
       
  2315     }
       
  2316 
       
  2317     public void fillRect (int x, int y, int w, int h) {
       
  2318         try {
       
  2319             fillpipe.fillRect(this, x, y, w, h);
       
  2320         } catch (InvalidPipeException e) {
       
  2321             revalidateAll();
       
  2322             try {
       
  2323                 fillpipe.fillRect(this, x, y, w, h);
       
  2324             } catch (InvalidPipeException e2) {
       
  2325                 // Still catching the exception; we are not yet ready to
       
  2326                 // validate the surfaceData correctly.  Fail for now and
       
  2327                 // try again next time around.
       
  2328             }
       
  2329         } finally {
       
  2330             surfaceData.markDirty();
       
  2331         }
       
  2332     }
       
  2333 
       
  2334     private void revalidateAll() {
       
  2335         try {
       
  2336             // REMIND: This locking needs to be done around the
       
  2337             // caller of this method so that the pipe stays valid
       
  2338             // long enough to call the new primitive.
       
  2339             // REMIND: No locking yet in screen SurfaceData objects!
       
  2340             // surfaceData.lock();
       
  2341             surfaceData = surfaceData.getReplacement();
       
  2342             if (surfaceData == null) {
       
  2343                 surfaceData = NullSurfaceData.theInstance;
       
  2344             }
       
  2345 
       
  2346             // this will recalculate the composite clip
       
  2347             setDevClip(surfaceData.getBounds());
       
  2348 
       
  2349             if (paintState <= PAINT_ALPHACOLOR) {
       
  2350                 validateColor();
       
  2351             }
       
  2352             if (composite instanceof XORComposite) {
       
  2353                 Color c = ((XORComposite) composite).getXorColor();
       
  2354                 setComposite(new XORComposite(c, surfaceData));
       
  2355             }
       
  2356             validatePipe();
       
  2357         } finally {
       
  2358             // REMIND: No locking yet in screen SurfaceData objects!
       
  2359             // surfaceData.unlock();
       
  2360         }
       
  2361     }
       
  2362 
       
  2363     public void clearRect(int x, int y, int w, int h) {
       
  2364         // REMIND: has some "interesting" consequences if threads are
       
  2365         // not synchronized
       
  2366         Composite c = composite;
       
  2367         Paint p = paint;
       
  2368         setComposite(AlphaComposite.Src);
       
  2369         setColor(getBackground());
       
  2370         validatePipe();
       
  2371         fillRect(x, y, w, h);
       
  2372         setPaint(p);
       
  2373         setComposite(c);
       
  2374     }
       
  2375 
       
  2376     /**
       
  2377      * Strokes the outline of a Path using the settings of the current
       
  2378      * graphics state.  The rendering attributes applied include the
       
  2379      * clip, transform, paint or color, composite and stroke attributes.
       
  2380      * @param p The path to be drawn.
       
  2381      * @see #setStroke
       
  2382      * @see #setPaint
       
  2383      * @see java.awt.Graphics#setColor
       
  2384      * @see #transform
       
  2385      * @see #setTransform
       
  2386      * @see #clip
       
  2387      * @see #setClip
       
  2388      * @see #setComposite
       
  2389      */
       
  2390     public void draw(Shape s) {
       
  2391         try {
       
  2392             shapepipe.draw(this, s);
       
  2393         } catch (InvalidPipeException e) {
       
  2394             revalidateAll();
       
  2395             try {
       
  2396                 shapepipe.draw(this, s);
       
  2397             } catch (InvalidPipeException e2) {
       
  2398                 // Still catching the exception; we are not yet ready to
       
  2399                 // validate the surfaceData correctly.  Fail for now and
       
  2400                 // try again next time around.
       
  2401             }
       
  2402         } finally {
       
  2403             surfaceData.markDirty();
       
  2404         }
       
  2405     }
       
  2406 
       
  2407 
       
  2408     /**
       
  2409      * Fills the interior of a Path using the settings of the current
       
  2410      * graphics state. The rendering attributes applied include the
       
  2411      * clip, transform, paint or color, and composite.
       
  2412      * @see #setPaint
       
  2413      * @see java.awt.Graphics#setColor
       
  2414      * @see #transform
       
  2415      * @see #setTransform
       
  2416      * @see #setComposite
       
  2417      * @see #clip
       
  2418      * @see #setClip
       
  2419      */
       
  2420     public void fill(Shape s) {
       
  2421         try {
       
  2422             shapepipe.fill(this, s);
       
  2423         } catch (InvalidPipeException e) {
       
  2424             revalidateAll();
       
  2425             try {
       
  2426                 shapepipe.fill(this, s);
       
  2427             } catch (InvalidPipeException e2) {
       
  2428                 // Still catching the exception; we are not yet ready to
       
  2429                 // validate the surfaceData correctly.  Fail for now and
       
  2430                 // try again next time around.
       
  2431             }
       
  2432         } finally {
       
  2433             surfaceData.markDirty();
       
  2434         }
       
  2435     }
       
  2436 
       
  2437     /**
       
  2438      * Returns true if the given AffineTransform is an integer
       
  2439      * translation.
       
  2440      */
       
  2441     private static boolean isIntegerTranslation(AffineTransform xform) {
       
  2442         if (xform.isIdentity()) {
       
  2443             return true;
       
  2444         }
       
  2445         if (xform.getType() == AffineTransform.TYPE_TRANSLATION) {
       
  2446             double tx = xform.getTranslateX();
       
  2447             double ty = xform.getTranslateY();
       
  2448             return (tx == (int)tx && ty == (int)ty);
       
  2449         }
       
  2450         return false;
       
  2451     }
       
  2452 
       
  2453     /**
       
  2454      * Returns the index of the tile corresponding to the supplied position
       
  2455      * given the tile grid offset and size along the same axis.
       
  2456      */
       
  2457     private static int getTileIndex(int p, int tileGridOffset, int tileSize) {
       
  2458         p -= tileGridOffset;
       
  2459         if (p < 0) {
       
  2460             p += 1 - tileSize;          // force round to -infinity (ceiling)
       
  2461         }
       
  2462         return p/tileSize;
       
  2463     }
       
  2464 
       
  2465     /**
       
  2466      * Returns a rectangle in image coordinates that may be required
       
  2467      * in order to draw the given image into the given clipping region
       
  2468      * through a pair of AffineTransforms.  In addition, horizontal and
       
  2469      * vertical padding factors for antialising and interpolation may
       
  2470      * be used.
       
  2471      */
       
  2472     private static Rectangle getImageRegion(RenderedImage img,
       
  2473                                             Region compClip,
       
  2474                                             AffineTransform transform,
       
  2475                                             AffineTransform xform,
       
  2476                                             int padX, int padY) {
       
  2477         Rectangle imageRect =
       
  2478             new Rectangle(img.getMinX(), img.getMinY(),
       
  2479                           img.getWidth(), img.getHeight());
       
  2480 
       
  2481         Rectangle result = null;
       
  2482         try {
       
  2483             double p[] = new double[8];
       
  2484             p[0] = p[2] = compClip.getLoX();
       
  2485             p[4] = p[6] = compClip.getHiX();
       
  2486             p[1] = p[5] = compClip.getLoY();
       
  2487             p[3] = p[7] = compClip.getHiY();
       
  2488 
       
  2489             // Inverse transform the output bounding rect
       
  2490             transform.inverseTransform(p, 0, p, 0, 4);
       
  2491             xform.inverseTransform(p, 0, p, 0, 4);
       
  2492 
       
  2493             // Determine a bounding box for the inverse transformed region
       
  2494             double x0,x1,y0,y1;
       
  2495             x0 = x1 = p[0];
       
  2496             y0 = y1 = p[1];
       
  2497 
       
  2498             for (int i = 2; i < 8; ) {
       
  2499                 double pt = p[i++];
       
  2500                 if (pt < x0)  {
       
  2501                     x0 = pt;
       
  2502                 } else if (pt > x1) {
       
  2503                     x1 = pt;
       
  2504                 }
       
  2505                 pt = p[i++];
       
  2506                 if (pt < y0)  {
       
  2507                     y0 = pt;
       
  2508                 } else if (pt > y1) {
       
  2509                     y1 = pt;
       
  2510                 }
       
  2511             }
       
  2512 
       
  2513             // This is padding for anti-aliasing and such.  It may
       
  2514             // be more than is needed.
       
  2515             int x = (int)x0 - padX;
       
  2516             int w = (int)(x1 - x0 + 2*padX);
       
  2517             int y = (int)y0 - padY;
       
  2518             int h = (int)(y1 - y0 + 2*padY);
       
  2519 
       
  2520             Rectangle clipRect = new Rectangle(x,y,w,h);
       
  2521             result = clipRect.intersection(imageRect);
       
  2522         } catch (NoninvertibleTransformException nte) {
       
  2523             // Worst case bounds are the bounds of the image.
       
  2524             result = imageRect;
       
  2525         }
       
  2526 
       
  2527         return result;
       
  2528     }
       
  2529 
       
  2530     /**
       
  2531      * Draws an image, applying a transform from image space into user space
       
  2532      * before drawing.
       
  2533      * The transformation from user space into device space is done with
       
  2534      * the current transform in the Graphics2D.
       
  2535      * The given transformation is applied to the image before the
       
  2536      * transform attribute in the Graphics2D state is applied.
       
  2537      * The rendering attributes applied include the clip, transform,
       
  2538      * and composite attributes. Note that the result is
       
  2539      * undefined, if the given transform is noninvertible.
       
  2540      * @param img The image to be drawn. Does nothing if img is null.
       
  2541      * @param xform The transformation from image space into user space.
       
  2542      * @see #transform
       
  2543      * @see #setTransform
       
  2544      * @see #setComposite
       
  2545      * @see #clip
       
  2546      * @see #setClip
       
  2547      */
       
  2548     public void drawRenderedImage(RenderedImage img,
       
  2549                                   AffineTransform xform) {
       
  2550 
       
  2551         if (img == null) {
       
  2552             return;
       
  2553         }
       
  2554 
       
  2555         // BufferedImage case: use a simple drawImage call
       
  2556         if (img instanceof BufferedImage) {
       
  2557             BufferedImage bufImg = (BufferedImage)img;
       
  2558             drawImage(bufImg,xform,null);
       
  2559             return;
       
  2560         }
       
  2561 
       
  2562         // transformState tracks the state of transform and
       
  2563         // transX, transY contain the integer casts of the
       
  2564         // translation factors
       
  2565         boolean isIntegerTranslate =
       
  2566             (transformState <= TRANSFORM_INT_TRANSLATE) &&
       
  2567             isIntegerTranslation(xform);
       
  2568 
       
  2569         // Include padding for interpolation/antialiasing if necessary
       
  2570         int pad = isIntegerTranslate ? 0 : 3;
       
  2571 
       
  2572         // Determine the region of the image that may contribute to
       
  2573         // the clipped drawing area
       
  2574         Rectangle region = getImageRegion(img,
       
  2575                                           getCompClip(),
       
  2576                                           transform,
       
  2577                                           xform,
       
  2578                                           pad, pad);
       
  2579         if (region.width <= 0 || region.height <= 0) {
       
  2580             return;
       
  2581         }
       
  2582 
       
  2583         // Attempt to optimize integer translation of tiled images.
       
  2584         // Although theoretically we are O.K. if the concatenation of
       
  2585         // the user transform and the device transform is an integer
       
  2586         // translation, we'll play it safe and only optimize the case
       
  2587         // where both are integer translations.
       
  2588         if (isIntegerTranslate) {
       
  2589             // Use optimized code
       
  2590             // Note that drawTranslatedRenderedImage calls copyImage
       
  2591             // which takes the user space to device space transform into
       
  2592             // account, but we need to provide the image space to user space
       
  2593             // translations.
       
  2594 
       
  2595             drawTranslatedRenderedImage(img, region,
       
  2596                                         (int) xform.getTranslateX(),
       
  2597                                         (int) xform.getTranslateY());
       
  2598             return;
       
  2599         }
       
  2600 
       
  2601         // General case: cobble the necessary region into a single Raster
       
  2602         Raster raster = img.getData(region);
       
  2603 
       
  2604         // Make a new Raster with the same contents as raster
       
  2605         // but starting at (0, 0).  This raster is thus in the same
       
  2606         // coordinate system as the SampleModel of the original raster.
       
  2607         WritableRaster wRaster =
       
  2608               Raster.createWritableRaster(raster.getSampleModel(),
       
  2609                                           raster.getDataBuffer(),
       
  2610                                           null);
       
  2611 
       
  2612         // If the original raster was in a different coordinate
       
  2613         // system than its SampleModel, we need to perform an
       
  2614         // additional translation in order to get the (minX, minY)
       
  2615         // pixel of raster to be pixel (0, 0) of wRaster.  We also
       
  2616         // have to have the correct width and height.
       
  2617         int minX = raster.getMinX();
       
  2618         int minY = raster.getMinY();
       
  2619         int width = raster.getWidth();
       
  2620         int height = raster.getHeight();
       
  2621         int px = minX - raster.getSampleModelTranslateX();
       
  2622         int py = minY - raster.getSampleModelTranslateY();
       
  2623         if (px != 0 || py != 0 || width != wRaster.getWidth() ||
       
  2624             height != wRaster.getHeight()) {
       
  2625             wRaster =
       
  2626                 wRaster.createWritableChild(px,
       
  2627                                             py,
       
  2628                                             width,
       
  2629                                             height,
       
  2630                                             0, 0,
       
  2631                                             null);
       
  2632         }
       
  2633 
       
  2634         // Now we have a BufferedImage starting at (0, 0)
       
  2635         // with the same contents that started at (minX, minY)
       
  2636         // in raster.  So we must draw the BufferedImage with a
       
  2637         // translation of (minX, minY).
       
  2638         AffineTransform transXform = (AffineTransform)xform.clone();
       
  2639         transXform.translate(minX, minY);
       
  2640 
       
  2641         ColorModel cm = img.getColorModel();
       
  2642         BufferedImage bufImg = new BufferedImage(cm,
       
  2643                                                  wRaster,
       
  2644                                                  cm.isAlphaPremultiplied(),
       
  2645                                                  null);
       
  2646         drawImage(bufImg, transXform, null);
       
  2647     }
       
  2648 
       
  2649     /**
       
  2650      * Intersects <code>destRect</code> with <code>clip</code> and
       
  2651      * overwrites <code>destRect</code> with the result.
       
  2652      * Returns false if the intersection was empty, true otherwise.
       
  2653      */
       
  2654     private boolean clipTo(Rectangle destRect, Rectangle clip) {
       
  2655         int x1 = Math.max(destRect.x, clip.x);
       
  2656         int x2 = Math.min(destRect.x + destRect.width, clip.x + clip.width);
       
  2657         int y1 = Math.max(destRect.y, clip.y);
       
  2658         int y2 = Math.min(destRect.y + destRect.height, clip.y + clip.height);
       
  2659         if (((x2 - x1) < 0) || ((y2 - y1) < 0)) {
       
  2660             destRect.width = -1; // Set both just to be safe
       
  2661             destRect.height = -1;
       
  2662             return false;
       
  2663         } else {
       
  2664             destRect.x = x1;
       
  2665             destRect.y = y1;
       
  2666             destRect.width = x2 - x1;
       
  2667             destRect.height = y2 - y1;
       
  2668             return true;
       
  2669         }
       
  2670     }
       
  2671 
       
  2672     /**
       
  2673      * Draw a portion of a RenderedImage tile-by-tile with a given
       
  2674      * integer image to user space translation.  The user to
       
  2675      * device transform must also be an integer translation.
       
  2676      */
       
  2677     private void drawTranslatedRenderedImage(RenderedImage img,
       
  2678                                              Rectangle region,
       
  2679                                              int i2uTransX,
       
  2680                                              int i2uTransY) {
       
  2681         // Cache tile grid info
       
  2682         int tileGridXOffset = img.getTileGridXOffset();
       
  2683         int tileGridYOffset = img.getTileGridYOffset();
       
  2684         int tileWidth = img.getTileWidth();
       
  2685         int tileHeight = img.getTileHeight();
       
  2686 
       
  2687         // Determine the tile index extrema in each direction
       
  2688         int minTileX =
       
  2689             getTileIndex(region.x, tileGridXOffset, tileWidth);
       
  2690         int minTileY =
       
  2691             getTileIndex(region.y, tileGridYOffset, tileHeight);
       
  2692         int maxTileX =
       
  2693             getTileIndex(region.x + region.width - 1,
       
  2694                          tileGridXOffset, tileWidth);
       
  2695         int maxTileY =
       
  2696             getTileIndex(region.y + region.height - 1,
       
  2697                          tileGridYOffset, tileHeight);
       
  2698 
       
  2699         // Create a single ColorModel to use for all BufferedImages
       
  2700         ColorModel colorModel = img.getColorModel();
       
  2701 
       
  2702         // Reuse the same Rectangle for each iteration
       
  2703         Rectangle tileRect = new Rectangle();
       
  2704 
       
  2705         for (int ty = minTileY; ty <= maxTileY; ty++) {
       
  2706             for (int tx = minTileX; tx <= maxTileX; tx++) {
       
  2707                 // Get the current tile.
       
  2708                 Raster raster = img.getTile(tx, ty);
       
  2709 
       
  2710                 // Fill in tileRect with the tile bounds
       
  2711                 tileRect.x = tx*tileWidth + tileGridXOffset;
       
  2712                 tileRect.y = ty*tileHeight + tileGridYOffset;
       
  2713                 tileRect.width = tileWidth;
       
  2714                 tileRect.height = tileHeight;
       
  2715 
       
  2716                 // Clip the tile against the image bounds and
       
  2717                 // backwards mapped clip region
       
  2718                 // The result can't be empty
       
  2719                 clipTo(tileRect, region);
       
  2720 
       
  2721                 // Create a WritableRaster containing the tile
       
  2722                 WritableRaster wRaster = null;
       
  2723                 if (raster instanceof WritableRaster) {
       
  2724                     wRaster = (WritableRaster)raster;
       
  2725                 } else {
       
  2726                     // Create a WritableRaster in the same coordinate system
       
  2727                     // as the original raster.
       
  2728                     wRaster =
       
  2729                         Raster.createWritableRaster(raster.getSampleModel(),
       
  2730                                                     raster.getDataBuffer(),
       
  2731                                                     null);
       
  2732                 }
       
  2733 
       
  2734                 // Translate wRaster to start at (0, 0) and to contain
       
  2735                 // only the relevent portion of the tile
       
  2736                 wRaster = wRaster.createWritableChild(tileRect.x, tileRect.y,
       
  2737                                                       tileRect.width,
       
  2738                                                       tileRect.height,
       
  2739                                                       0, 0,
       
  2740                                                       null);
       
  2741 
       
  2742                 // Wrap wRaster in a BufferedImage
       
  2743                 BufferedImage bufImg =
       
  2744                     new BufferedImage(colorModel,
       
  2745                                       wRaster,
       
  2746                                       colorModel.isAlphaPremultiplied(),
       
  2747                                       null);
       
  2748                 // Now we have a BufferedImage starting at (0, 0) that
       
  2749                 // represents data from a Raster starting at
       
  2750                 // (tileRect.x, tileRect.y).  Additionally, it needs
       
  2751                 // to be translated by (i2uTransX, i2uTransY).  We call
       
  2752                 // copyImage to draw just the region of interest
       
  2753                 // without needing to create a child image.
       
  2754                 copyImage(bufImg, tileRect.x + i2uTransX,
       
  2755                           tileRect.y + i2uTransY, 0, 0, tileRect.width,
       
  2756                           tileRect.height, null, null);
       
  2757             }
       
  2758         }
       
  2759     }
       
  2760 
       
  2761     public void drawRenderableImage(RenderableImage img,
       
  2762                                     AffineTransform xform) {
       
  2763 
       
  2764         if (img == null) {
       
  2765             return;
       
  2766         }
       
  2767 
       
  2768         AffineTransform pipeTransform = transform;
       
  2769         AffineTransform concatTransform = new AffineTransform(xform);
       
  2770         concatTransform.concatenate(pipeTransform);
       
  2771         AffineTransform reverseTransform;
       
  2772 
       
  2773         RenderContext rc = new RenderContext(concatTransform);
       
  2774 
       
  2775         try {
       
  2776             reverseTransform = pipeTransform.createInverse();
       
  2777         } catch (NoninvertibleTransformException nte) {
       
  2778             rc = new RenderContext(pipeTransform);
       
  2779             reverseTransform = new AffineTransform();
       
  2780         }
       
  2781 
       
  2782         RenderedImage rendering = img.createRendering(rc);
       
  2783         drawRenderedImage(rendering,reverseTransform);
       
  2784     }
       
  2785 
       
  2786 
       
  2787 
       
  2788     /*
       
  2789      * Transform the bounding box of the BufferedImage
       
  2790      */
       
  2791     protected Rectangle transformBounds(Rectangle rect,
       
  2792                                         AffineTransform tx) {
       
  2793         if (tx.isIdentity()) {
       
  2794             return rect;
       
  2795         }
       
  2796 
       
  2797         Shape s = transformShape(tx, rect);
       
  2798         return s.getBounds();
       
  2799     }
       
  2800 
       
  2801     // text rendering methods
       
  2802     public void drawString(String str, int x, int y) {
       
  2803         if (str == null) {
       
  2804             throw new NullPointerException("String is null");
       
  2805         }
       
  2806 
       
  2807         if (font.hasLayoutAttributes()) {
       
  2808             new TextLayout(str, font, getFontRenderContext()).draw(this, x, y);
       
  2809             return;
       
  2810         }
       
  2811 
       
  2812         try {
       
  2813             textpipe.drawString(this, str, x, y);
       
  2814         } catch (InvalidPipeException e) {
       
  2815             revalidateAll();
       
  2816             try {
       
  2817                 textpipe.drawString(this, str, x, y);
       
  2818             } catch (InvalidPipeException e2) {
       
  2819                 // Still catching the exception; we are not yet ready to
       
  2820                 // validate the surfaceData correctly.  Fail for now and
       
  2821                 // try again next time around.
       
  2822             }
       
  2823         } finally {
       
  2824             surfaceData.markDirty();
       
  2825         }
       
  2826     }
       
  2827 
       
  2828     public void drawString(String str, float x, float y) {
       
  2829         if (str == null) {
       
  2830             throw new NullPointerException("String is null");
       
  2831         }
       
  2832 
       
  2833         if (font.hasLayoutAttributes()) {
       
  2834             new TextLayout(str, font, getFontRenderContext()).draw(this, x, y);
       
  2835             return;
       
  2836         }
       
  2837 
       
  2838         try {
       
  2839             textpipe.drawString(this, str, x, y);
       
  2840         } catch (InvalidPipeException e) {
       
  2841             revalidateAll();
       
  2842             try {
       
  2843                 textpipe.drawString(this, str, x, y);
       
  2844             } catch (InvalidPipeException e2) {
       
  2845                 // Still catching the exception; we are not yet ready to
       
  2846                 // validate the surfaceData correctly.  Fail for now and
       
  2847                 // try again next time around.
       
  2848             }
       
  2849         } finally {
       
  2850             surfaceData.markDirty();
       
  2851         }
       
  2852     }
       
  2853 
       
  2854     public void drawString(AttributedCharacterIterator iterator,
       
  2855                            int x, int y) {
       
  2856         if (iterator == null) {
       
  2857             throw new NullPointerException("AttributedCharacterIterator is null");
       
  2858         }
       
  2859         TextLayout tl = new TextLayout(iterator, getFontRenderContext());
       
  2860         tl.draw(this, (float) x, (float) y);
       
  2861     }
       
  2862 
       
  2863     public void drawString(AttributedCharacterIterator iterator,
       
  2864                            float x, float y) {
       
  2865         if (iterator == null) {
       
  2866             throw new NullPointerException("AttributedCharacterIterator is null");
       
  2867         }
       
  2868         TextLayout tl = new TextLayout(iterator, getFontRenderContext());
       
  2869         tl.draw(this, x, y);
       
  2870     }
       
  2871 
       
  2872     public void drawGlyphVector(GlyphVector gv, float x, float y)
       
  2873     {
       
  2874         if (gv == null) {
       
  2875             throw new NullPointerException("GlyphVector is null");
       
  2876         }
       
  2877 
       
  2878         try {
       
  2879             textpipe.drawGlyphVector(this, gv, x, y);
       
  2880         } catch (InvalidPipeException e) {
       
  2881             revalidateAll();
       
  2882             try {
       
  2883                 textpipe.drawGlyphVector(this, gv, x, y);
       
  2884             } catch (InvalidPipeException e2) {
       
  2885                 // Still catching the exception; we are not yet ready to
       
  2886                 // validate the surfaceData correctly.  Fail for now and
       
  2887                 // try again next time around.
       
  2888             }
       
  2889         } finally {
       
  2890             surfaceData.markDirty();
       
  2891         }
       
  2892     }
       
  2893 
       
  2894     public void drawChars(char data[], int offset, int length, int x, int y) {
       
  2895 
       
  2896         if (data == null) {
       
  2897             throw new NullPointerException("char data is null");
       
  2898         }
       
  2899         if (offset < 0 || length < 0 || offset + length > data.length) {
       
  2900             throw new ArrayIndexOutOfBoundsException("bad offset/length");
       
  2901         }
       
  2902         if (font.hasLayoutAttributes()) {
       
  2903             new TextLayout(new String(data, offset, length),
       
  2904                            font, getFontRenderContext()).draw(this, x, y);
       
  2905             return;
       
  2906         }
       
  2907 
       
  2908         try {
       
  2909             textpipe.drawChars(this, data, offset, length, x, y);
       
  2910         } catch (InvalidPipeException e) {
       
  2911             revalidateAll();
       
  2912             try {
       
  2913                 textpipe.drawChars(this, data, offset, length, x, y);
       
  2914             } catch (InvalidPipeException e2) {
       
  2915                 // Still catching the exception; we are not yet ready to
       
  2916                 // validate the surfaceData correctly.  Fail for now and
       
  2917                 // try again next time around.
       
  2918             }
       
  2919         } finally {
       
  2920             surfaceData.markDirty();
       
  2921         }
       
  2922     }
       
  2923 
       
  2924     public void drawBytes(byte data[], int offset, int length, int x, int y) {
       
  2925         if (data == null) {
       
  2926             throw new NullPointerException("byte data is null");
       
  2927         }
       
  2928         if (offset < 0 || length < 0 || offset + length > data.length) {
       
  2929             throw new ArrayIndexOutOfBoundsException("bad offset/length");
       
  2930         }
       
  2931         /* Byte data is interpreted as 8-bit ASCII. Re-use drawChars loops */
       
  2932         char chData[] = new char[length];
       
  2933         for (int i = length; i-- > 0; ) {
       
  2934             chData[i] = (char)(data[i+offset] & 0xff);
       
  2935         }
       
  2936         if (font.hasLayoutAttributes()) {
       
  2937             new TextLayout(new String(chData),
       
  2938                            font, getFontRenderContext()).draw(this, x, y);
       
  2939             return;
       
  2940         }
       
  2941 
       
  2942         try {
       
  2943             textpipe.drawChars(this, chData, 0, length, x, y);
       
  2944         } catch (InvalidPipeException e) {
       
  2945             revalidateAll();
       
  2946             try {
       
  2947                 textpipe.drawChars(this, chData, 0, length, x, y);
       
  2948             } catch (InvalidPipeException e2) {
       
  2949                 // Still catching the exception; we are not yet ready to
       
  2950                 // validate the surfaceData correctly.  Fail for now and
       
  2951                 // try again next time around.
       
  2952             }
       
  2953         } finally {
       
  2954             surfaceData.markDirty();
       
  2955         }
       
  2956     }
       
  2957 // end of text rendering methods
       
  2958 
       
  2959     /**
       
  2960      * Draws an image scaled to x,y,w,h in nonblocking mode with a
       
  2961      * callback object.
       
  2962      */
       
  2963     public boolean drawImage(Image img, int x, int y, int width, int height,
       
  2964                              ImageObserver observer) {
       
  2965         return drawImage(img, x, y, width, height, null, observer);
       
  2966     }
       
  2967 
       
  2968     /**
       
  2969      * Not part of the advertised API but a useful utility method
       
  2970      * to call internally.  This is for the case where we are
       
  2971      * drawing to/from given coordinates using a given width/height,
       
  2972      * but we guarantee that the weidth/height of the src and dest
       
  2973      * areas are equal (no scale needed).
       
  2974      */
       
  2975     public boolean copyImage(Image img, int dx, int dy, int sx, int sy,
       
  2976                              int width, int height, Color bgcolor,
       
  2977                              ImageObserver observer) {
       
  2978         try {
       
  2979             return imagepipe.copyImage(this, img, dx, dy, sx, sy,
       
  2980                                        width, height, bgcolor, observer);
       
  2981         } catch (InvalidPipeException e) {
       
  2982             revalidateAll();
       
  2983             try {
       
  2984                 return imagepipe.copyImage(this, img, dx, dy, sx, sy,
       
  2985                                            width, height, bgcolor, observer);
       
  2986             } catch (InvalidPipeException e2) {
       
  2987                 // Still catching the exception; we are not yet ready to
       
  2988                 // validate the surfaceData correctly.  Fail for now and
       
  2989                 // try again next time around.
       
  2990                 return false;
       
  2991             }
       
  2992         } finally {
       
  2993             surfaceData.markDirty();
       
  2994         }
       
  2995     }
       
  2996 
       
  2997     /**
       
  2998      * Draws an image scaled to x,y,w,h in nonblocking mode with a
       
  2999      * solid background color and a callback object.
       
  3000      */
       
  3001     public boolean drawImage(Image img, int x, int y, int width, int height,
       
  3002                              Color bg, ImageObserver observer) {
       
  3003 
       
  3004         if (img == null) {
       
  3005             return true;
       
  3006         }
       
  3007 
       
  3008         if ((width == 0) || (height == 0)) {
       
  3009             return true;
       
  3010         }
       
  3011         if (width == img.getWidth(null) && height == img.getHeight(null)) {
       
  3012             return copyImage(img, x, y, 0, 0, width, height, bg, observer);
       
  3013         }
       
  3014 
       
  3015         try {
       
  3016             return imagepipe.scaleImage(this, img, x, y, width, height,
       
  3017                                         bg, observer);
       
  3018         } catch (InvalidPipeException e) {
       
  3019             revalidateAll();
       
  3020             try {
       
  3021                 return imagepipe.scaleImage(this, img, x, y, width, height,
       
  3022                                             bg, observer);
       
  3023             } catch (InvalidPipeException e2) {
       
  3024                 // Still catching the exception; we are not yet ready to
       
  3025                 // validate the surfaceData correctly.  Fail for now and
       
  3026                 // try again next time around.
       
  3027                 return false;
       
  3028             }
       
  3029         } finally {
       
  3030             surfaceData.markDirty();
       
  3031         }
       
  3032     }
       
  3033 
       
  3034     /**
       
  3035      * Draws an image at x,y in nonblocking mode.
       
  3036      */
       
  3037     public boolean drawImage(Image img, int x, int y, ImageObserver observer) {
       
  3038         return drawImage(img, x, y, null, observer);
       
  3039     }
       
  3040 
       
  3041     /**
       
  3042      * Draws an image at x,y in nonblocking mode with a solid background
       
  3043      * color and a callback object.
       
  3044      */
       
  3045     public boolean drawImage(Image img, int x, int y, Color bg,
       
  3046                              ImageObserver observer) {
       
  3047 
       
  3048         if (img == null) {
       
  3049             return true;
       
  3050         }
       
  3051 
       
  3052         try {
       
  3053             return imagepipe.copyImage(this, img, x, y, bg, observer);
       
  3054         } catch (InvalidPipeException e) {
       
  3055             revalidateAll();
       
  3056             try {
       
  3057                 return imagepipe.copyImage(this, img, x, y, bg, observer);
       
  3058             } catch (InvalidPipeException e2) {
       
  3059                 // Still catching the exception; we are not yet ready to
       
  3060                 // validate the surfaceData correctly.  Fail for now and
       
  3061                 // try again next time around.
       
  3062                 return false;
       
  3063             }
       
  3064         } finally {
       
  3065             surfaceData.markDirty();
       
  3066         }
       
  3067     }
       
  3068 
       
  3069     /**
       
  3070      * Draws a subrectangle of an image scaled to a destination rectangle
       
  3071      * in nonblocking mode with a callback object.
       
  3072      */
       
  3073     public boolean drawImage(Image img,
       
  3074                              int dx1, int dy1, int dx2, int dy2,
       
  3075                              int sx1, int sy1, int sx2, int sy2,
       
  3076                              ImageObserver observer) {
       
  3077         return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null,
       
  3078                          observer);
       
  3079     }
       
  3080 
       
  3081     /**
       
  3082      * Draws a subrectangle of an image scaled to a destination rectangle in
       
  3083      * nonblocking mode with a solid background color and a callback object.
       
  3084      */
       
  3085     public boolean drawImage(Image img,
       
  3086                              int dx1, int dy1, int dx2, int dy2,
       
  3087                              int sx1, int sy1, int sx2, int sy2,
       
  3088                              Color bgcolor, ImageObserver observer) {
       
  3089 
       
  3090         if (img == null) {
       
  3091             return true;
       
  3092         }
       
  3093 
       
  3094         if (dx1 == dx2 || dy1 == dy2 ||
       
  3095             sx1 == sx2 || sy1 == sy2)
       
  3096         {
       
  3097             return true;
       
  3098         }
       
  3099 
       
  3100         if (((sx2 - sx1) == (dx2 - dx1)) &&
       
  3101             ((sy2 - sy1) == (dy2 - dy1)))
       
  3102         {
       
  3103             // Not a scale - forward it to a copy routine
       
  3104             int srcX, srcY, dstX, dstY, width, height;
       
  3105             if (sx2 > sx1) {
       
  3106                 width = sx2 - sx1;
       
  3107                 srcX = sx1;
       
  3108                 dstX = dx1;
       
  3109             } else {
       
  3110                 width = sx1 - sx2;
       
  3111                 srcX = sx2;
       
  3112                 dstX = dx2;
       
  3113             }
       
  3114             if (sy2 > sy1) {
       
  3115                 height = sy2-sy1;
       
  3116                 srcY = sy1;
       
  3117                 dstY = dy1;
       
  3118             } else {
       
  3119                 height = sy1-sy2;
       
  3120                 srcY = sy2;
       
  3121                 dstY = dy2;
       
  3122             }
       
  3123             return copyImage(img, dstX, dstY, srcX, srcY,
       
  3124                              width, height, bgcolor, observer);
       
  3125         }
       
  3126 
       
  3127         try {
       
  3128             return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2,
       
  3129                                           sx1, sy1, sx2, sy2, bgcolor,
       
  3130                                           observer);
       
  3131         } catch (InvalidPipeException e) {
       
  3132             revalidateAll();
       
  3133             try {
       
  3134                 return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2,
       
  3135                                               sx1, sy1, sx2, sy2, bgcolor,
       
  3136                                               observer);
       
  3137             } catch (InvalidPipeException e2) {
       
  3138                 // Still catching the exception; we are not yet ready to
       
  3139                 // validate the surfaceData correctly.  Fail for now and
       
  3140                 // try again next time around.
       
  3141                 return false;
       
  3142             }
       
  3143         } finally {
       
  3144             surfaceData.markDirty();
       
  3145         }
       
  3146     }
       
  3147 
       
  3148     /**
       
  3149      * Draw an image, applying a transform from image space into user space
       
  3150      * before drawing.
       
  3151      * The transformation from user space into device space is done with
       
  3152      * the current transform in the Graphics2D.
       
  3153      * The given transformation is applied to the image before the
       
  3154      * transform attribute in the Graphics2D state is applied.
       
  3155      * The rendering attributes applied include the clip, transform,
       
  3156      * paint or color and composite attributes. Note that the result is
       
  3157      * undefined, if the given transform is non-invertible.
       
  3158      * @param img The image to be drawn.
       
  3159      * @param xform The transformation from image space into user space.
       
  3160      * @param observer The image observer to be notified on the image producing
       
  3161      * progress.
       
  3162      * @see #transform
       
  3163      * @see #setComposite
       
  3164      * @see #setClip
       
  3165      */
       
  3166     public boolean drawImage(Image img,
       
  3167                              AffineTransform xform,
       
  3168                              ImageObserver observer) {
       
  3169 
       
  3170         if (img == null) {
       
  3171             return true;
       
  3172         }
       
  3173 
       
  3174         if (xform == null || xform.isIdentity()) {
       
  3175             return drawImage(img, 0, 0, null, observer);
       
  3176         }
       
  3177 
       
  3178         try {
       
  3179             return imagepipe.transformImage(this, img, xform, observer);
       
  3180         } catch (InvalidPipeException e) {
       
  3181             revalidateAll();
       
  3182             try {
       
  3183                 return imagepipe.transformImage(this, img, xform, observer);
       
  3184             } catch (InvalidPipeException e2) {
       
  3185                 // Still catching the exception; we are not yet ready to
       
  3186                 // validate the surfaceData correctly.  Fail for now and
       
  3187                 // try again next time around.
       
  3188                 return false;
       
  3189             }
       
  3190         } finally {
       
  3191             surfaceData.markDirty();
       
  3192         }
       
  3193     }
       
  3194 
       
  3195     public void drawImage(BufferedImage bImg,
       
  3196                           BufferedImageOp op,
       
  3197                           int x,
       
  3198                           int y)  {
       
  3199 
       
  3200         if (bImg == null) {
       
  3201             return;
       
  3202         }
       
  3203 
       
  3204         try {
       
  3205             imagepipe.transformImage(this, bImg, op, x, y);
       
  3206         } catch (InvalidPipeException e) {
       
  3207             revalidateAll();
       
  3208             try {
       
  3209                 imagepipe.transformImage(this, bImg, op, x, y);
       
  3210             } catch (InvalidPipeException e2) {
       
  3211                 // Still catching the exception; we are not yet ready to
       
  3212                 // validate the surfaceData correctly.  Fail for now and
       
  3213                 // try again next time around.
       
  3214             }
       
  3215         } finally {
       
  3216             surfaceData.markDirty();
       
  3217         }
       
  3218     }
       
  3219 
       
  3220     /**
       
  3221     * Get the rendering context of the font
       
  3222     * within this Graphics2D context.
       
  3223     */
       
  3224     public FontRenderContext getFontRenderContext() {
       
  3225         if (cachedFRC == null) {
       
  3226             int aahint = textAntialiasHint;
       
  3227             if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT &&
       
  3228                 antialiasHint == SunHints.INTVAL_ANTIALIAS_ON) {
       
  3229                 aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON;
       
  3230             }
       
  3231             // Translation components should be excluded from the FRC transform
       
  3232             AffineTransform tx = null;
       
  3233             if (transformState >= TRANSFORM_TRANSLATESCALE) {
       
  3234                 if (transform.getTranslateX() == 0 &&
       
  3235                     transform.getTranslateY() == 0) {
       
  3236                     tx = transform;
       
  3237                 } else {
       
  3238                     tx = new AffineTransform(transform.getScaleX(),
       
  3239                                              transform.getShearY(),
       
  3240                                              transform.getShearX(),
       
  3241                                              transform.getScaleY(),
       
  3242                                              0, 0);
       
  3243                 }
       
  3244             }
       
  3245             cachedFRC = new FontRenderContext(tx,
       
  3246              SunHints.Value.get(SunHints.INTKEY_TEXT_ANTIALIASING, aahint),
       
  3247              SunHints.Value.get(SunHints.INTKEY_FRACTIONALMETRICS,
       
  3248                                 fractionalMetricsHint));
       
  3249         }
       
  3250         return cachedFRC;
       
  3251     }
       
  3252     private FontRenderContext cachedFRC;
       
  3253 
       
  3254     /**
       
  3255      * This object has no resources to dispose of per se, but the
       
  3256      * doc comments for the base method in java.awt.Graphics imply
       
  3257      * that this object will not be useable after it is disposed.
       
  3258      * So, we sabotage the object to prevent further use to prevent
       
  3259      * developers from relying on behavior that may not work on
       
  3260      * other, less forgiving, VMs that really need to dispose of
       
  3261      * resources.
       
  3262      */
       
  3263     public void dispose() {
       
  3264         surfaceData = NullSurfaceData.theInstance;
       
  3265         invalidatePipe();
       
  3266     }
       
  3267 
       
  3268     /**
       
  3269      * Graphics has a finalize method that automatically calls dispose()
       
  3270      * for subclasses.  For SunGraphics2D we do not need to be finalized
       
  3271      * so that method simply causes us to be enqueued on the Finalizer
       
  3272      * queues for no good reason.  Unfortunately, that method and
       
  3273      * implementation are now considered part of the public contract
       
  3274      * of that base class so we can not remove or gut the method.
       
  3275      * We override it here with an empty method and the VM is smart
       
  3276      * enough to know that if our override is empty then it should not
       
  3277      * mark us as finalizeable.
       
  3278      */
       
  3279     public void finalize() {
       
  3280         // DO NOT REMOVE THIS METHOD
       
  3281     }
       
  3282 
       
  3283     /**
       
  3284      * Returns destination that this Graphics renders to.  This could be
       
  3285      * either an Image or a Component; subclasses of SurfaceData are
       
  3286      * responsible for returning the appropriate object.
       
  3287      */
       
  3288     public Object getDestination() {
       
  3289         return surfaceData.getDestination();
       
  3290     }
       
  3291 }