1 /*
   2  * Copyright (c) 2011, 2015, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.java2d;
  27 
  28 import java.awt.*;
  29 import java.awt.font.*;
  30 import java.awt.geom.*;
  31 import java.awt.image.*;
  32 import java.nio.*;
  33 
  34 import sun.awt.*;
  35 import sun.awt.image.*;
  36 import sun.java2d.loops.*;
  37 import sun.java2d.pipe.*;
  38 import sun.lwawt.macosx.*;
  39 
  40 import java.lang.annotation.Native;
  41 
  42 /*
  43  * This is the SurfaceData for a CGContextRef.
  44  */
  45 public abstract class OSXSurfaceData extends BufImgSurfaceData {
  46     static final float UPPER_BND = Float.MAX_VALUE / 2.0f;
  47     static final float LOWER_BND = -UPPER_BND;
  48 
  49     protected static CRenderer sQuartzPipe = null;
  50     protected static CTextPipe sCocoaTextPipe = null;
  51     protected static CompositeCRenderer sQuartzCompositePipe = null;
  52 
  53     private GraphicsConfiguration fConfig;
  54     private Rectangle fBounds; // bounds in user coordinates
  55 
  56     static {
  57         sQuartzPipe = new CRenderer(); // Creates the singleton quartz pipe.
  58     }
  59 
  60     // NOTE: Any subclasses must eventually call QuartzSurfaceData_InitOps in OSXSurfaceData.h
  61     // This sets up the native side for the SurfaceData, and is required.
  62     public OSXSurfaceData(SurfaceType sType, ColorModel cm) {
  63         this(sType, cm, null, new Rectangle());
  64     }
  65 
  66     public OSXSurfaceData(SurfaceType sType, ColorModel cm, GraphicsConfiguration config, Rectangle bounds) {
  67         super(sType, cm);
  68 
  69         this.fConfig = config;
  70 
  71         this.fBounds = new Rectangle(bounds.x, bounds.y, bounds.width, bounds.y + bounds.height);
  72 
  73         this.fGraphicsStates = getBufferOfSize(kSizeOfParameters);
  74         this.fGraphicsStatesInt = this.fGraphicsStates.asIntBuffer();
  75         this.fGraphicsStatesFloat = this.fGraphicsStates.asFloatBuffer();
  76         this.fGraphicsStatesLong = this.fGraphicsStates.asLongBuffer();
  77         this.fGraphicsStatesObject = new Object[8]; // clip coordinates +
  78                                                     // clip types +
  79                                                     // texture paint image +
  80                                                     // stroke dash array +
  81                                                     // font + font paint +
  82                                                     // linear/radial gradient color +
  83                                                     // linear/radial gradient fractions
  84 
  85         // NOTE: All access to the DrawingQueue comes through this OSXSurfaceData instance. Therefore
  86         // every instance method of OSXSurfaceData that accesses the fDrawingQueue is synchronized.
  87 
  88         // Thread.dumpStack();
  89     }
  90 
  91     public void validatePipe(SunGraphics2D sg2d) {
  92 
  93         if (sg2d.compositeState <= SunGraphics2D.COMP_ALPHA) {
  94             if (sCocoaTextPipe == null) {
  95                 sCocoaTextPipe = new CTextPipe();
  96             }
  97 
  98             sg2d.imagepipe = sQuartzPipe;
  99             sg2d.drawpipe = sQuartzPipe;
 100             sg2d.fillpipe = sQuartzPipe;
 101             sg2d.shapepipe = sQuartzPipe;
 102             sg2d.textpipe = sCocoaTextPipe;
 103         } else {
 104             setPipesToQuartzComposite(sg2d);
 105         }
 106     }
 107 
 108     protected void setPipesToQuartzComposite(SunGraphics2D sg2d) {
 109         if (sQuartzCompositePipe == null) {
 110             sQuartzCompositePipe = new CompositeCRenderer();
 111         }
 112 
 113         if (sCocoaTextPipe == null) {
 114             sCocoaTextPipe = new CTextPipe();
 115         }
 116 
 117         sg2d.imagepipe = sQuartzCompositePipe;
 118         sg2d.drawpipe = sQuartzCompositePipe;
 119         sg2d.fillpipe = sQuartzCompositePipe;
 120         sg2d.shapepipe = sQuartzCompositePipe;
 121         sg2d.textpipe = sCocoaTextPipe;
 122     }
 123 
 124     public Rectangle getBounds() {
 125         // gznote: always return a copy, not the rect itself and translate into device space
 126         return new Rectangle(fBounds.x, fBounds.y, fBounds.width, fBounds.height - fBounds.y);
 127     }
 128 
 129     public GraphicsConfiguration getDeviceConfiguration() {
 130         return fConfig;
 131     }
 132 
 133     protected void setBounds(int x, int y, int w, int h) {
 134         fBounds.setBounds(x, y, w, y + h);
 135     }
 136 
 137     // START compositing support API
 138     public abstract BufferedImage copyArea(SunGraphics2D sg2d, int x, int y, int w, int h, BufferedImage image);
 139 
 140     public abstract boolean xorSurfacePixels(SunGraphics2D sg2d, BufferedImage srcPixels, int x, int y, int w, int h, int colorXOR);
 141 
 142     GraphicsConfiguration sDefaultGraphicsConfiguration = null;
 143 
 144     protected BufferedImage getCompositingImage(int w, int h) {
 145         if (sDefaultGraphicsConfiguration == null) {
 146             sDefaultGraphicsConfiguration = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
 147         }
 148 
 149         BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB_PRE);
 150         // clear the image.
 151         clearRect(img, w, h);
 152         return img;
 153     }
 154 
 155     protected BufferedImage getCompositingImageSame(BufferedImage img, int w, int h) {
 156         if ((img == null) || (img.getWidth() != w) || (img.getHeight() != h)) {
 157             img = getCompositingImage(w, h);
 158         }
 159         return img;
 160     }
 161 
 162     BufferedImage sSrcComposite = null;
 163 
 164     public BufferedImage getCompositingSrcImage(int w, int h) {
 165         // <rdar://problem/3720263>. Changed from getCompositingImageBiggerOrSame() to
 166         // getCompositingImageSame(). (vm)
 167         BufferedImage bim = getCompositingImageSame(sSrcComposite, w, h);
 168         sSrcComposite = bim;
 169         return bim;
 170     }
 171 
 172     BufferedImage sDstInComposite = null;
 173 
 174     public BufferedImage getCompositingDstInImage(int w, int h) {
 175         BufferedImage bim = getCompositingImageSame(sDstInComposite, w, h);
 176         sDstInComposite = bim;
 177         return bim;
 178     }
 179 
 180     BufferedImage sDstOutComposite = null;
 181 
 182     public BufferedImage getCompositingDstOutImage(int w, int h) {
 183         BufferedImage bim = getCompositingImageSame(sDstOutComposite, w, h);
 184         sDstOutComposite = bim;
 185         return bim;
 186     }
 187 
 188     public void clearRect(BufferedImage bim, int w, int h) {
 189         Graphics2D g = bim.createGraphics();
 190         g.setComposite(AlphaComposite.Clear);
 191         g.fillRect(0, 0, w, h);
 192         g.dispose();
 193     }
 194 
 195     // END compositing support API
 196 
 197     public void invalidate() {
 198         // always valid
 199     }
 200 
 201      // graphics primitives drawing implementation:
 202 
 203     // certain primitives don't care about all the states (ex. drawing an image needs not involve setting current paint)
 204     @Native static final int kPrimitive = 0;
 205     @Native static final int kImage = 1;
 206     @Native static final int kText = 2;
 207     @Native static final int kCopyArea = 3;
 208     @Native static final int kExternal = 4;
 209 
 210     @Native static final int kLine = 5; // belongs to kPrimitive
 211     @Native static final int kRect = 6; // belongs to kPrimitive
 212     @Native static final int kRoundRect = 7; // belongs to kPrimitive
 213     @Native static final int kOval = 8; // belongs to kPrimitive
 214     @Native static final int kArc = 9; // belongs to kPrimitive
 215     @Native static final int kPolygon = 10; // belongs to kPrimitive
 216     @Native static final int kShape = 11; // belongs to kPrimitive
 217     // static final int kImage = 12; // belongs to kImage
 218     @Native static final int kString = 13; // belongs to kText
 219     @Native static final int kGlyphs = 14; // belongs to kText
 220     @Native static final int kUnicodes = 15; // belongs to kText
 221     // static final int kCopyArea = 16; // belongs to kCopyArea
 222     // static final int kExternal = 17; // belongs to kExternal
 223 
 224     @Native static final int kCommonParameterCount = 1 + 1 + 4 + 4; // type + change flags + color info (type(1) align(1) and
 225                                                             // value(2)) + parameters ((x1, y1, x2, y2) OR (x, y, w, h))
 226     @Native static final int kLineParametersCount = kCommonParameterCount; // kCommonParameterCount
 227     @Native static final int kRectParametersCount = kCommonParameterCount + 1; // kCommonParameterCount + isfill
 228     @Native static final int kRoundRectParametersCount = kCommonParameterCount + 2 + 1; // kCommonParameterCount + arcW + arcH +
 229                                                                                 // isfill
 230     @Native static final int kOvalParametersCount = kCommonParameterCount + 1; // kCommonParameterCount + isfill
 231     @Native static final int kArcParametersCount = kCommonParameterCount + 2 + 1 + 1;// kCommonParameterCount + startAngle +
 232                                                                              // arcAngle + isfill + type
 233     @Native static final int kPolygonParametersCount = 0; // not supported
 234     @Native static final int kShapeParametersCount = 0; // not supported
 235     @Native static final int kImageParametersCount = kCommonParameterCount + 2 + 2 + 4 + 4; // flip horz vert + w&h + src + dst
 236     @Native static final int kStringParametersCount = 0; // not supported
 237     @Native static final int kGlyphsParametersCount = 0; // not supported
 238     @Native static final int kUnicodesParametersCount = 0; // not supported
 239     @Native static final int kPixelParametersCount = 0; // not supported
 240     @Native static final int kExternalParametersCount = 0; // not supported
 241 
 242     // for intParameters
 243     // states info
 244     @Native static final int kChangeFlagIndex = 0; // kBoundsChangedBit | .. | kFontChangedBit
 245     // bounds info
 246     @Native static final int kBoundsXIndex = 1;
 247     @Native static final int kBoundsYIndex = 2;
 248     @Native static final int kBoundsWidthIndex = 3;
 249     @Native static final int kBoundsHeightIndex = 4;
 250     // clip info
 251     @Native static final int kClipStateIndex = 5;
 252     @Native static final int kClipNumTypesIndex = 6;
 253     @Native static final int kClipNumCoordsIndex = 7;
 254     @Native static final int kClipWindingRuleIndex = 8;
 255     @Native static final int kClipXIndex = 9;
 256     @Native static final int kClipYIndex = 10;
 257     @Native static final int kClipWidthIndex = 11;
 258     @Native static final int kClipHeightIndex = 12;
 259     // ctm info
 260     @Native static final int kCTMaIndex = 13;
 261     @Native static final int kCTMbIndex = 14;
 262     @Native static final int kCTMcIndex = 15;
 263     @Native static final int kCTMdIndex = 16;
 264     @Native static final int kCTMtxIndex = 17;
 265     @Native static final int kCTMtyIndex = 18;
 266     // color info
 267     @Native static final int kColorStateIndex = 19; // kColorSimple or kColorGradient or kColorTexture
 268     @Native static final int kColorRGBValueIndex = 20; // if kColorSimple
 269     @Native static final int kColorIndexValueIndex = 21; // if kColorSystem
 270     @Native static final int kColorPointerIndex = 22; //
 271     @Native static final int kColorPointerIndex2 = 23; //
 272     @Native static final int kColorRGBValue1Index = 24; // if kColorGradient
 273     @Native static final int kColorWidthIndex = 25; // if kColorTexture
 274     @Native static final int kColorRGBValue2Index = 26; // if kColorGradient
 275     @Native static final int kColorHeightIndex = 27; // if kColorTexture
 276     @Native static final int kColorIsCyclicIndex = 28; // if kColorGradient (kColorNonCyclic or kColorCyclic)
 277     @Native static final int kColorx1Index = 29;
 278     @Native static final int kColortxIndex = 30;
 279     @Native static final int kColory1Index = 31;
 280     @Native static final int kColortyIndex = 32;
 281     @Native static final int kColorx2Index = 33;
 282     @Native static final int kColorsxIndex = 34;
 283     @Native static final int kColory2Index = 35;
 284     @Native static final int kColorsyIndex = 36;
 285     // composite info
 286     @Native static final int kCompositeRuleIndex = 37; // kCGCompositeClear or ... or kCGCompositeXor
 287     @Native static final int kCompositeValueIndex = 38;
 288     // stroke info
 289     @Native static final int kStrokeJoinIndex = 39; // see BasicStroke.java
 290     @Native static final int kStrokeCapIndex = 40; // see BasicStroke.java
 291     @Native static final int kStrokeWidthIndex = 41;
 292     @Native static final int kStrokeDashPhaseIndex = 42;
 293     @Native static final int kStrokeLimitIndex = 43;
 294     // hints info
 295     @Native static final int kHintsAntialiasIndex = 44;
 296     @Native static final int kHintsTextAntialiasIndex = 45;
 297     @Native static final int kHintsFractionalMetricsIndex = 46;
 298     @Native static final int kHintsRenderingIndex = 47;
 299     @Native static final int kHintsInterpolationIndex = 48;
 300     //gradient info
 301     @Native static final int kRadiusIndex = 49;
 302 
 303     @Native static final int kSizeOfParameters = kRadiusIndex + 1;
 304 
 305     // for objectParameters
 306     @Native static final int kClipCoordinatesIndex = 0;
 307     @Native static final int kClipTypesIndex = 1;
 308     @Native static final int kTextureImageIndex = 2;
 309     @Native static final int kStrokeDashArrayIndex = 3;
 310     @Native static final int kFontIndex = 4;
 311     @Native static final int kFontPaintIndex = 5;
 312     @Native static final int kColorArrayIndex = 6;
 313     @Native static final int kFractionsArrayIndex = 7;
 314 
 315     // possible state changes
 316     @Native static final int kBoundsChangedBit = 1 << 0;
 317     @Native static final int kBoundsNotChangedBit = ~kBoundsChangedBit;
 318     @Native static final int kClipChangedBit = 1 << 1;
 319     @Native static final int kClipNotChangedBit = ~kClipChangedBit;
 320     @Native static final int kCTMChangedBit = 1 << 2;
 321     @Native static final int kCTMNotChangedBit = ~kCTMChangedBit;
 322     @Native static final int kColorChangedBit = 1 << 3;
 323     @Native static final int kColorNotChangedBit = ~kColorChangedBit;
 324     @Native static final int kCompositeChangedBit = 1 << 4;
 325     @Native static final int kCompositeNotChangedBit = ~kCompositeChangedBit;
 326     @Native static final int kStrokeChangedBit = 1 << 5;
 327     @Native static final int kStrokeNotChangedBit = ~kStrokeChangedBit;
 328     @Native static final int kHintsChangedBit = 1 << 6;
 329     @Native static final int kHintsNotChangedBit = ~kHintsChangedBit;
 330     @Native static final int kFontChangedBit = 1 << 7;
 331     @Native static final int kFontNotChangedBit = ~kFontChangedBit;
 332     @Native static final int kEverythingChangedFlag = 0xffffffff;
 333 
 334     // possible color states
 335     @Native static final int kColorSimple = 0;
 336     @Native static final int kColorSystem = 1;
 337     @Native static final int kColorGradient = 2;
 338     @Native static final int kColorTexture = 3;
 339     @Native static final int kColorLinearGradient = 4;
 340     @Native static final int kColorRadialGradient = 5;
 341 
 342     // possible gradient color states
 343     @Native static final int kColorNonCyclic = 0;
 344     @Native static final int kColorCyclic = 1;
 345 
 346     // possible clip states
 347     @Native static final int kClipRect = 0;
 348     @Native static final int kClipShape = 1;
 349 
 350     static int getRendererTypeForPrimitive(int primitiveType) {
 351         switch (primitiveType) {
 352             case kImage:
 353                 return kImage;
 354             case kCopyArea:
 355                 return kCopyArea;
 356             case kExternal:
 357                 return kExternal;
 358             case kString:
 359             case kGlyphs:
 360             case kUnicodes:
 361                 return kText;
 362             default:
 363                 return kPrimitive;
 364         }
 365     }
 366 
 367     int fChangeFlag;
 368     protected ByteBuffer fGraphicsStates = null;
 369     IntBuffer fGraphicsStatesInt = null;
 370     FloatBuffer fGraphicsStatesFloat = null;
 371     LongBuffer fGraphicsStatesLong = null;
 372     protected Object[] fGraphicsStatesObject = null;
 373 
 374     Rectangle userBounds = new Rectangle();
 375     float lastUserX = 0;
 376     float lastUserY = 0;
 377     float lastUserW = 0;
 378     float lastUserH = 0;
 379 
 380     void setUserBounds(SunGraphics2D sg2d, int x, int y, int width, int height) {
 381         if ((lastUserX != x) || (lastUserY != y) || (lastUserW != width) || (lastUserH != height)) {
 382             lastUserX = x;
 383             lastUserY = y;
 384             lastUserW = width;
 385             lastUserH = height;
 386 
 387             this.fGraphicsStatesInt.put(kBoundsXIndex, x);
 388             this.fGraphicsStatesInt.put(kBoundsYIndex, y);
 389             this.fGraphicsStatesInt.put(kBoundsWidthIndex, width);
 390             this.fGraphicsStatesInt.put(kBoundsHeightIndex, height);
 391 
 392             userBounds.setBounds(x, y, width, height);
 393 
 394             this.fChangeFlag = (this.fChangeFlag | kBoundsChangedBit);
 395         } else {
 396             this.fChangeFlag = (this.fChangeFlag & kBoundsNotChangedBit);
 397         }
 398     }
 399 
 400     static ByteBuffer getBufferOfSize(int size) {
 401         ByteBuffer buffer = ByteBuffer.allocateDirect(size * 4);
 402         buffer.order(ByteOrder.nativeOrder());
 403         return buffer;
 404     }
 405 
 406     FloatBuffer clipCoordinatesArray = null;
 407     IntBuffer clipTypesArray = null;
 408     Shape lastClipShape = null;
 409     float lastClipX = 0;
 410     float lastClipY = 0;
 411     float lastClipW = 0;
 412     float lastClipH = 0;
 413 
 414     void setupClip(SunGraphics2D sg2d) {
 415         switch (sg2d.clipState) {
 416             case SunGraphics2D.CLIP_DEVICE:
 417             case SunGraphics2D.CLIP_RECTANGULAR: {
 418                 Region clip = sg2d.getCompClip();
 419                 float x = clip.getLoX();
 420                 float y = clip.getLoY();
 421                 float w = clip.getWidth();
 422                 float h = clip.getHeight();
 423                 if ((this.fGraphicsStatesInt.get(kClipStateIndex) != kClipRect) ||
 424                         (x != lastClipX) ||
 425                             (y != lastClipY) ||
 426                                 (w != lastClipW) ||
 427                                     (h != lastClipH)) {
 428                     this.fGraphicsStatesFloat.put(kClipXIndex, x);
 429                     this.fGraphicsStatesFloat.put(kClipYIndex, y);
 430                     this.fGraphicsStatesFloat.put(kClipWidthIndex, w);
 431                     this.fGraphicsStatesFloat.put(kClipHeightIndex, h);
 432 
 433                     lastClipX = x;
 434                     lastClipY = y;
 435                     lastClipW = w;
 436                     lastClipH = h;
 437 
 438                     this.fChangeFlag = (this.fChangeFlag | kClipChangedBit);
 439                 } else {
 440                     this.fChangeFlag = (this.fChangeFlag & kClipNotChangedBit);
 441                 }
 442                 this.fGraphicsStatesInt.put(kClipStateIndex, kClipRect);
 443                 break;
 444             }
 445             case SunGraphics2D.CLIP_SHAPE: {
 446                 // if (lastClipShape != sg2d.usrClip) shapes are mutable!, and doing "equals" traverses all
 447                 // the coordinates, so we might as well do all of it anyhow
 448                 lastClipShape = sg2d.usrClip;
 449 
 450                 GeneralPath gp = null;
 451 
 452                 if (sg2d.usrClip instanceof GeneralPath) {
 453                     gp = (GeneralPath) sg2d.usrClip;
 454                 } else {
 455                     gp = new GeneralPath(sg2d.usrClip);
 456                 }
 457 
 458                 int shapeLength = getPathLength(gp);
 459 
 460                 if ((clipCoordinatesArray == null) || (clipCoordinatesArray.capacity() < (shapeLength * 6))) {
 461                     clipCoordinatesArray = getBufferOfSize(shapeLength * 6).asFloatBuffer(); // segment can have a
 462                                                                                              // max of 6 coordinates
 463                 }
 464                 if ((clipTypesArray == null) || (clipTypesArray.capacity() < shapeLength)) {
 465                     clipTypesArray = getBufferOfSize(shapeLength).asIntBuffer();
 466                 }
 467 
 468                 int windingRule = getPathCoordinates(gp, clipCoordinatesArray, clipTypesArray);
 469 
 470                 this.fGraphicsStatesInt.put(kClipNumTypesIndex, clipTypesArray.position());
 471                 this.fGraphicsStatesInt.put(kClipNumCoordsIndex, clipCoordinatesArray.position());
 472                 this.fGraphicsStatesInt.put(kClipWindingRuleIndex, windingRule);
 473                 this.fGraphicsStatesObject[kClipTypesIndex] = clipTypesArray;
 474                 this.fGraphicsStatesObject[kClipCoordinatesIndex] = clipCoordinatesArray;
 475 
 476                 this.fChangeFlag = (this.fChangeFlag | kClipChangedBit);
 477                 this.fGraphicsStatesInt.put(kClipStateIndex, kClipShape);
 478                 break;
 479             }
 480         }
 481 
 482     }
 483 
 484     final double[] lastCTM = new double[6];
 485     float lastCTMa = 0;
 486     float lastCTMb = 0;
 487     float lastCTMc = 0;
 488     float lastCTMd = 0;
 489     float lastCTMtx = 0;
 490     float lastCTMty = 0;
 491 
 492     void setupTransform(SunGraphics2D sg2d) {
 493         sg2d.transform.getMatrix(lastCTM);
 494 
 495         float a = (float) lastCTM[0];
 496         float b = (float) lastCTM[1];
 497         float c = (float) lastCTM[2];
 498         float d = (float) lastCTM[3];
 499         float tx = (float) lastCTM[4];
 500         float ty = (float) lastCTM[5];
 501         if (tx != lastCTMtx ||
 502                 ty != lastCTMty ||
 503                     a != lastCTMa ||
 504                         b != lastCTMb ||
 505                             c != lastCTMc ||
 506                                 d != lastCTMd) {
 507             this.fGraphicsStatesFloat.put(kCTMaIndex, a);
 508             this.fGraphicsStatesFloat.put(kCTMbIndex, b);
 509             this.fGraphicsStatesFloat.put(kCTMcIndex, c);
 510             this.fGraphicsStatesFloat.put(kCTMdIndex, d);
 511             this.fGraphicsStatesFloat.put(kCTMtxIndex, tx);
 512             this.fGraphicsStatesFloat.put(kCTMtyIndex, ty);
 513 
 514             lastCTMa = a;
 515             lastCTMb = b;
 516             lastCTMc = c;
 517             lastCTMd = d;
 518             lastCTMtx = tx;
 519             lastCTMty = ty;
 520 
 521             this.fChangeFlag = (this.fChangeFlag | kCTMChangedBit);
 522         } else {
 523             this.fChangeFlag = (this.fChangeFlag & kCTMNotChangedBit);
 524         }
 525     }
 526 
 527     static AffineTransform sIdentityMatrix = new AffineTransform();
 528     Paint lastPaint = null;
 529     long lastPaintPtr = 0;
 530     int lastPaintRGB = 0;
 531     int lastPaintIndex = 0;
 532     BufferedImage texturePaintImage = null;
 533 
 534     void setGradientViaRasterPath(SunGraphics2D sg2d) {
 535         if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorTexture) || (lastPaint != sg2d.paint) || ((this.fChangeFlag & kBoundsChangedBit) != 0)) {
 536             PaintContext context = sg2d.paint.createContext(sg2d.getDeviceColorModel(), userBounds, userBounds, sIdentityMatrix, sg2d.getRenderingHints());
 537             WritableRaster raster = (WritableRaster) (context.getRaster(userBounds.x, userBounds.y, userBounds.width, userBounds.height));
 538             ColorModel cm = context.getColorModel();
 539             texturePaintImage = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
 540 
 541             this.fGraphicsStatesInt.put(kColorStateIndex, kColorTexture);
 542             this.fGraphicsStatesInt.put(kColorWidthIndex, texturePaintImage.getWidth());
 543             this.fGraphicsStatesInt.put(kColorHeightIndex, texturePaintImage.getHeight());
 544             this.fGraphicsStatesFloat.put(kColortxIndex, (float) userBounds.getX());
 545             this.fGraphicsStatesFloat.put(kColortyIndex, (float) userBounds.getY());
 546             this.fGraphicsStatesFloat.put(kColorsxIndex, 1.0f);
 547             this.fGraphicsStatesFloat.put(kColorsyIndex, 1.0f);
 548             this.fGraphicsStatesObject[kTextureImageIndex] = OSXOffScreenSurfaceData.createNewSurface(texturePaintImage);
 549 
 550             this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
 551         } else {
 552             this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
 553         }
 554     }
 555 
 556     void setupPaint(SunGraphics2D sg2d, int x, int y, int w, int h) {
 557         if (sg2d.paint instanceof SystemColor) {
 558             SystemColor color = (SystemColor) sg2d.paint;
 559             int index = color.hashCode(); // depends on Color.java hashCode implementation! (returns "value" of color)
 560             if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorSystem) || (index != this.lastPaintIndex)) {
 561                 this.lastPaintIndex = index;
 562 
 563                 this.fGraphicsStatesInt.put(kColorStateIndex, kColorSystem);
 564                 this.fGraphicsStatesInt.put(kColorIndexValueIndex, index);
 565 
 566                 this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
 567             } else {
 568                 this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
 569             }
 570         } else if (sg2d.paint instanceof Color) {
 571             Color color = (Color) sg2d.paint;
 572             int rgb = color.getRGB();
 573             if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorSimple) || (rgb != this.lastPaintRGB)) {
 574                 this.lastPaintRGB = rgb;
 575 
 576                 this.fGraphicsStatesInt.put(kColorStateIndex, kColorSimple);
 577                 this.fGraphicsStatesInt.put(kColorRGBValueIndex, rgb);
 578 
 579                 this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
 580             } else {
 581                 this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
 582             }
 583         } else if (sg2d.paint instanceof GradientPaint) {
 584             if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorGradient) || (lastPaint != sg2d.paint)) {
 585                 GradientPaint color = (GradientPaint) sg2d.paint;
 586                 this.fGraphicsStatesInt.put(kColorStateIndex, kColorGradient);
 587                 this.fGraphicsStatesInt.put(kColorRGBValue1Index, color.getColor1().getRGB());
 588                 this.fGraphicsStatesInt.put(kColorRGBValue2Index, color.getColor2().getRGB());
 589                 this.fGraphicsStatesInt.put(kColorIsCyclicIndex, (color.isCyclic()) ? kColorCyclic : kColorNonCyclic);
 590                 Point2D p = color.getPoint1();
 591                 this.fGraphicsStatesFloat.put(kColorx1Index, (float) p.getX());
 592                 this.fGraphicsStatesFloat.put(kColory1Index, (float) p.getY());
 593                 p = color.getPoint2();
 594                 this.fGraphicsStatesFloat.put(kColorx2Index, (float) p.getX());
 595                 this.fGraphicsStatesFloat.put(kColory2Index, (float) p.getY());
 596 
 597                 this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
 598             } else {
 599                 this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
 600             }
 601         } else if (sg2d.paint instanceof LinearGradientPaint) {
 602             LinearGradientPaint color = (LinearGradientPaint) sg2d.paint;
 603             if (color.getCycleMethod() == LinearGradientPaint.CycleMethod.NO_CYCLE) {
 604                 if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorLinearGradient) || (lastPaint != sg2d.paint)) {
 605 
 606                     this.fGraphicsStatesInt.put(kColorStateIndex, kColorLinearGradient);
 607                     int numColor = color.getColors().length;
 608                     int colorArray[] = new int[numColor];
 609                     for (int i = 0; i < numColor; i++) {
 610                         colorArray[i] = color.getColors()[i].getRGB();
 611                     }
 612                     this.fGraphicsStatesObject[kColorArrayIndex] = colorArray;
 613 
 614                     int numFractions = color.getFractions().length;
 615                     float fractionArray[] = new float[numFractions];
 616                     for (int i = 0; i < numFractions; i++) {
 617                         fractionArray[i] = color.getFractions()[i];
 618                     }
 619                     this.fGraphicsStatesObject[kFractionsArrayIndex] = color.getFractions();
 620 
 621                     Point2D p = color.getStartPoint();
 622                     this.fGraphicsStatesFloat.put(kColorx1Index, (float) p.getX());
 623                     this.fGraphicsStatesFloat.put(kColory1Index, (float) p.getY());
 624                     p = color.getEndPoint();
 625                     this.fGraphicsStatesFloat.put(kColorx2Index, (float) p.getX());
 626                     this.fGraphicsStatesFloat.put(kColory2Index, (float) p.getY());
 627 
 628                     this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
 629                 } else {
 630                     this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
 631                 }
 632             } else {
 633                 setGradientViaRasterPath(sg2d);
 634             }
 635         } else if (sg2d.paint instanceof RadialGradientPaint) {
 636             RadialGradientPaint color = (RadialGradientPaint) sg2d.paint;
 637             if (color.getCycleMethod() == RadialGradientPaint.CycleMethod.NO_CYCLE) {
 638                 if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorRadialGradient) || (lastPaint != sg2d.paint)) {
 639 
 640                     this.fGraphicsStatesInt.put(kColorStateIndex, kColorRadialGradient);
 641                     int numColor = color.getColors().length;
 642                     int colorArray[] = new int[numColor];
 643                     for (int i = 0; i < numColor; i++) {
 644                         colorArray[i] = color.getColors()[i].getRGB();
 645                     }
 646                     this.fGraphicsStatesObject[kColorArrayIndex] = colorArray;
 647 
 648                     int numStops = color.getFractions().length;
 649                     float stopsArray[] = new float[numStops];
 650                     for (int i = 0; i < numStops; i++) {
 651                         stopsArray[i] = color.getFractions()[i];
 652                     }
 653                     this.fGraphicsStatesObject[kFractionsArrayIndex] = color.getFractions();
 654 
 655                     Point2D p = color.getFocusPoint();
 656                     this.fGraphicsStatesFloat.put(kColorx1Index, (float) p.getX());
 657                     this.fGraphicsStatesFloat.put(kColory1Index, (float) p.getY());
 658                     p = color.getCenterPoint();
 659                     this.fGraphicsStatesFloat.put(kColorx2Index, (float) p.getX());
 660                     this.fGraphicsStatesFloat.put(kColory2Index, (float) p.getY());
 661                     this.fGraphicsStatesFloat.put(kRadiusIndex,     color.getRadius());
 662 
 663                     this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
 664                 } else {
 665                     this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
 666                 }
 667             } else {
 668                 setGradientViaRasterPath(sg2d);
 669             }
 670         } else if (sg2d.paint instanceof TexturePaint) {
 671             if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorTexture) || (lastPaint != sg2d.paint)) {
 672                 TexturePaint color = (TexturePaint) sg2d.paint;
 673                 this.fGraphicsStatesInt.put(kColorStateIndex, kColorTexture);
 674                 texturePaintImage = color.getImage();
 675                 SurfaceData textureSurfaceData = OSXOffScreenSurfaceData.createNewSurface(texturePaintImage);
 676                 this.fGraphicsStatesInt.put(kColorWidthIndex, texturePaintImage.getWidth());
 677                 this.fGraphicsStatesInt.put(kColorHeightIndex, texturePaintImage.getHeight());
 678                 Rectangle2D anchor = color.getAnchorRect();
 679                 this.fGraphicsStatesFloat.put(kColortxIndex, (float) anchor.getX());
 680                 this.fGraphicsStatesFloat.put(kColortyIndex, (float) anchor.getY());
 681                 this.fGraphicsStatesFloat.put(kColorsxIndex, (float) (anchor.getWidth() / texturePaintImage.getWidth()));
 682                 this.fGraphicsStatesFloat.put(kColorsyIndex, (float) (anchor.getHeight() / texturePaintImage.getHeight()));
 683                 this.fGraphicsStatesObject[kTextureImageIndex] = textureSurfaceData;
 684 
 685                 this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
 686             } else {
 687                 this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
 688             }
 689         } else {
 690             setGradientViaRasterPath(sg2d);
 691         }
 692         lastPaint = sg2d.paint;
 693     }
 694 
 695     Composite lastComposite;
 696     int lastCompositeAlphaRule = 0;
 697     float lastCompositeAlphaValue = 0;
 698 
 699     void setupComposite(SunGraphics2D sg2d) {
 700         Composite composite = sg2d.composite;
 701 
 702         if (lastComposite != composite) {
 703             lastComposite = composite;
 704 
 705             // For composite state COMP_ISCOPY, COMP_XOR or COMP_CUSTOM set alpha compositor to COPY:
 706             int alphaRule = AlphaComposite.SRC_OVER;
 707             float alphaValue = 1.0f;
 708 
 709             // For composite state COMP_ISCOPY composite could be null. If it's not (or composite state == COMP_ALPHA)
 710             // get alpha compositor's values:
 711             if ((sg2d.compositeState <= SunGraphics2D.COMP_ALPHA) && (composite != null)) {
 712                 AlphaComposite alphaComposite = (AlphaComposite) composite;
 713                 alphaRule = alphaComposite.getRule();
 714                 alphaValue = alphaComposite.getAlpha();
 715             }
 716 
 717             // 2-17-03 VL: [Radar 3174922]
 718             // For COMP_XOR and COMP_CUSTOM compositing modes we should be setting alphaRule = AlphaComposite.SRC
 719             // which should map to kCGCompositeCopy.
 720 
 721             if ((lastCompositeAlphaRule != alphaRule) || (lastCompositeAlphaValue != alphaValue)) {
 722                 this.fGraphicsStatesInt.put(kCompositeRuleIndex, alphaRule);
 723                 this.fGraphicsStatesFloat.put(kCompositeValueIndex, alphaValue);
 724 
 725                 lastCompositeAlphaRule = alphaRule;
 726                 lastCompositeAlphaValue = alphaValue;
 727 
 728                 this.fChangeFlag = (this.fChangeFlag | kCompositeChangedBit);
 729             } else {
 730                 this.fChangeFlag = (this.fChangeFlag & kCompositeNotChangedBit);
 731             }
 732         } else {
 733             this.fChangeFlag = (this.fChangeFlag & kCompositeNotChangedBit);
 734         }
 735     }
 736 
 737     BasicStroke lastStroke = null;
 738     static BasicStroke defaultBasicStroke = new BasicStroke();
 739 
 740     void setupStroke(SunGraphics2D sg2d) {
 741         BasicStroke stroke = defaultBasicStroke;
 742 
 743         if (sg2d.stroke instanceof BasicStroke) {
 744             stroke = (BasicStroke) sg2d.stroke;
 745         }
 746 
 747         if (lastStroke != stroke) {
 748             this.fGraphicsStatesObject[kStrokeDashArrayIndex] = stroke.getDashArray();
 749             this.fGraphicsStatesFloat.put(kStrokeDashPhaseIndex, stroke.getDashPhase());
 750             this.fGraphicsStatesInt.put(kStrokeCapIndex, stroke.getEndCap());
 751             this.fGraphicsStatesInt.put(kStrokeJoinIndex, stroke.getLineJoin());
 752             this.fGraphicsStatesFloat.put(kStrokeWidthIndex, stroke.getLineWidth());
 753             this.fGraphicsStatesFloat.put(kStrokeLimitIndex, stroke.getMiterLimit());
 754 
 755             this.fChangeFlag = (this.fChangeFlag | kStrokeChangedBit);
 756 
 757             lastStroke = stroke;
 758         } else {
 759             this.fChangeFlag = (this.fChangeFlag & kStrokeNotChangedBit);
 760         }
 761     }
 762 
 763     Font lastFont;
 764 
 765     void setupFont(Font font, Paint paint) {
 766         if (font == null) { return; }
 767 
 768         // We have to setup the kFontPaintIndex if we have changed the color so we added the last
 769         // test to see if the color has changed - needed for complex strings
 770         // see Radar 3368674
 771         if ((font != lastFont) || ((this.fChangeFlag & kColorChangedBit) != 0)) {
 772             this.fGraphicsStatesObject[kFontIndex] = font;
 773             this.fGraphicsStatesObject[kFontPaintIndex] = paint;
 774 
 775             this.fChangeFlag = (this.fChangeFlag | kFontChangedBit);
 776 
 777             lastFont = font;
 778         } else {
 779             this.fChangeFlag = (this.fChangeFlag & kFontNotChangedBit);
 780         }
 781     }
 782 
 783     void setupRenderingHints(SunGraphics2D sg2d) {
 784         boolean hintsChanged = false;
 785 
 786         // Significant for draw, fill, text, and image ops:
 787         int antialiasHint = sg2d.antialiasHint;
 788         if (this.fGraphicsStatesInt.get(kHintsAntialiasIndex) != antialiasHint) {
 789             this.fGraphicsStatesInt.put(kHintsAntialiasIndex, antialiasHint);
 790             hintsChanged = true;
 791         }
 792 
 793         // Significant only for text ops:
 794         int textAntialiasHint = sg2d.textAntialiasHint;
 795         if (this.fGraphicsStatesInt.get(kHintsTextAntialiasIndex) != textAntialiasHint) {
 796             this.fGraphicsStatesInt.put(kHintsTextAntialiasIndex, textAntialiasHint);
 797             hintsChanged = true;
 798         }
 799 
 800         // Significant only for text ops:
 801         int fractionalMetricsHint = sg2d.fractionalMetricsHint;
 802         if (this.fGraphicsStatesInt.get(kHintsFractionalMetricsIndex) != fractionalMetricsHint) {
 803             this.fGraphicsStatesInt.put(kHintsFractionalMetricsIndex, fractionalMetricsHint);
 804             hintsChanged = true;
 805         }
 806 
 807         // Significant only for image ops:
 808         int renderHint = sg2d.renderHint;
 809         if (this.fGraphicsStatesInt.get(kHintsRenderingIndex) != renderHint) {
 810             this.fGraphicsStatesInt.put(kHintsRenderingIndex, renderHint);
 811             hintsChanged = true;
 812         }
 813 
 814         // Significant only for image ops:
 815         Object hintValue = sg2d.getRenderingHint(RenderingHints.KEY_INTERPOLATION);
 816         int interpolationHint = (hintValue != null ? ((SunHints.Value) hintValue).getIndex() : -1);
 817         if (this.fGraphicsStatesInt.get(kHintsInterpolationIndex) != interpolationHint) {
 818             this.fGraphicsStatesInt.put(kHintsInterpolationIndex, interpolationHint);
 819             hintsChanged = true;
 820         }
 821 
 822         if (hintsChanged) {
 823             this.fChangeFlag = (this.fChangeFlag | kHintsChangedBit);
 824         } else {
 825             this.fChangeFlag = (this.fChangeFlag & kHintsNotChangedBit);
 826         }
 827     }
 828 
 829     SunGraphics2D sg2dCurrent = null;
 830     Thread threadCurrent = null;
 831 
 832     void setupGraphicsState(SunGraphics2D sg2d, int primitiveType) {
 833         setupGraphicsState(sg2d, primitiveType, sg2d.font, 0, 0, fBounds.width, fBounds.height); // deviceBounds into userBounds
 834     }
 835 
 836     void setupGraphicsState(SunGraphics2D sg2d, int primitiveType, int x, int y, int w, int h) {
 837         setupGraphicsState(sg2d, primitiveType, sg2d.font, x, y, w, h);
 838     }
 839 
 840     // the method below is overriden by CPeerSurface to check the last peer used to draw
 841     // if the peer changed we finish lazy drawing
 842     void setupGraphicsState(SunGraphics2D sg2d, int primitiveType, Font font, int x, int y, int w, int h) {
 843         this.fChangeFlag = 0;
 844 
 845         setUserBounds(sg2d, x, y, w, h);
 846 
 847         Thread thread = Thread.currentThread();
 848         if ((this.sg2dCurrent != sg2d) || (this.threadCurrent != thread)) {
 849             this.sg2dCurrent = sg2d;
 850             this.threadCurrent = thread;
 851 
 852             setupClip(sg2d);
 853             setupTransform(sg2d);
 854             setupPaint(sg2d, x, y, w, h);
 855             setupComposite(sg2d);
 856             setupStroke(sg2d);
 857             setupFont(font, sg2d.paint);
 858             setupRenderingHints(sg2d);
 859 
 860             this.fChangeFlag = kEverythingChangedFlag;
 861         } else {
 862             int rendererType = getRendererTypeForPrimitive(primitiveType);
 863 
 864             setupClip(sg2d);
 865             setupTransform(sg2d);
 866 
 867             if (rendererType != kCopyArea) {
 868                 setupComposite(sg2d);
 869                 setupRenderingHints(sg2d);
 870 
 871                 if ((rendererType != kImage)) {
 872                     setupPaint(sg2d, x, y, w, h);
 873                     setupStroke(sg2d);
 874                 }
 875                 if (rendererType != kPrimitive) {
 876                     setupFont(font, sg2d.paint);
 877                 }
 878 
 879             }
 880         }
 881 
 882         this.fGraphicsStatesInt.put(kChangeFlagIndex, this.fChangeFlag);
 883     }
 884 
 885     boolean isCustomPaint(SunGraphics2D sg2d) {
 886         if ((sg2d.paint instanceof Color) || (sg2d.paint instanceof SystemColor) || (sg2d.paint instanceof GradientPaint) || (sg2d.paint instanceof TexturePaint)) { return false; }
 887 
 888         return true;
 889     }
 890 
 891     final float[] segmentCoordinatesArray = new float[6];
 892 
 893     int getPathLength(GeneralPath gp) {
 894         int length = 0;
 895 
 896         PathIterator pi = gp.getPathIterator(null);
 897         while (pi.isDone() == false) {
 898             pi.next();
 899             length++;
 900         }
 901 
 902         return length;
 903     }
 904 
 905     int getPathCoordinates(GeneralPath gp, FloatBuffer coordinates, IntBuffer types) {
 906         // System.err.println("getPathCoordinates");
 907         boolean skip = false;
 908 
 909         coordinates.clear();
 910         types.clear();
 911 
 912         int type;
 913 
 914         PathIterator pi = gp.getPathIterator(null);
 915         while (pi.isDone() == false) {
 916             skip = false;
 917             type = pi.currentSegment(segmentCoordinatesArray);
 918 
 919             switch (type) {
 920                 case PathIterator.SEG_MOVETO:
 921                     // System.err.println(" SEG_MOVETO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+")");
 922                     if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND &&
 923                             segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND) {
 924                         coordinates.put(segmentCoordinatesArray[0]);
 925                         coordinates.put(segmentCoordinatesArray[1]);
 926                     } else {
 927                         skip = true;
 928                     }
 929                     break;
 930                 case PathIterator.SEG_LINETO:
 931                     // System.err.println(" SEG_LINETO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+")");
 932                     if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND &&
 933                             segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND) {
 934                         coordinates.put(segmentCoordinatesArray[0]);
 935                         coordinates.put(segmentCoordinatesArray[1]);
 936                     } else {
 937                         skip = true;
 938                     }
 939                     break;
 940                 case PathIterator.SEG_QUADTO:
 941                     // System.err.println(" SEG_QUADTO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+"), ("+segmentCoordinatesArray[2]+", "+segmentCoordinatesArray[3]+")");
 942                     if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND &&
 943                             segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND &&
 944                             segmentCoordinatesArray[2] < UPPER_BND && segmentCoordinatesArray[2] > LOWER_BND &&
 945                             segmentCoordinatesArray[3] < UPPER_BND && segmentCoordinatesArray[3] > LOWER_BND) {
 946                         coordinates.put(segmentCoordinatesArray[0]);
 947                         coordinates.put(segmentCoordinatesArray[1]);
 948                         coordinates.put(segmentCoordinatesArray[2]);
 949                         coordinates.put(segmentCoordinatesArray[3]);
 950                     } else {
 951                         skip = true;
 952                     }
 953                     break;
 954                 case PathIterator.SEG_CUBICTO:
 955                     // System.err.println(" SEG_QUADTO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+"), ("+segmentCoordinatesArray[2]+", "+segmentCoordinatesArray[3]+"), ("+segmentCoordinatesArray[4]+", "+segmentCoordinatesArray[5]+")");
 956                     if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND &&
 957                             segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND &&
 958                             segmentCoordinatesArray[2] < UPPER_BND && segmentCoordinatesArray[2] > LOWER_BND &&
 959                             segmentCoordinatesArray[3] < UPPER_BND && segmentCoordinatesArray[3] > LOWER_BND &&
 960                             segmentCoordinatesArray[4] < UPPER_BND && segmentCoordinatesArray[4] > LOWER_BND &&
 961                             segmentCoordinatesArray[5] < UPPER_BND && segmentCoordinatesArray[5] > LOWER_BND) {
 962                         coordinates.put(segmentCoordinatesArray[0]);
 963                         coordinates.put(segmentCoordinatesArray[1]);
 964                         coordinates.put(segmentCoordinatesArray[2]);
 965                         coordinates.put(segmentCoordinatesArray[3]);
 966                         coordinates.put(segmentCoordinatesArray[4]);
 967                         coordinates.put(segmentCoordinatesArray[5]);
 968                     } else {
 969                         skip = true;
 970                     }
 971                     break;
 972                 case PathIterator.SEG_CLOSE:
 973                     // System.err.println(" SEG_CLOSE");
 974                     break;
 975             }
 976 
 977             if (!skip) {
 978                 types.put(type);
 979             }
 980 
 981             pi.next();
 982         }
 983 
 984         return pi.getWindingRule();
 985     }
 986 
 987     public void doLine(CRenderer renderer, SunGraphics2D sg2d, float x1, float y1, float x2, float y2) {
 988         // System.err.println("-- doLine x1="+x1+" y1="+y1+" x2="+x2+" y2="+y2+" paint="+sg2d.paint);
 989         setupGraphicsState(sg2d, kLine, sg2d.font, 0, 0, fBounds.width, fBounds.height);
 990         renderer.doLine(this, x1, y1, x2, y2);
 991     }
 992 
 993     public void doRect(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, boolean isfill) {
 994         // System.err.println("-- doRect x="+x+" y="+y+" w="+width+" h="+height+" isfill="+isfill+" paint="+sg2d.paint);
 995         if ((isfill) && (isCustomPaint(sg2d))) {
 996             setupGraphicsState(sg2d, kRect, (int) x, (int) y, (int) width, (int) height);
 997         } else {
 998             setupGraphicsState(sg2d, kRect, sg2d.font, 0, 0, fBounds.width, fBounds.height);
 999         }
1000         renderer.doRect(this, x, y, width, height, isfill);
1001     }
1002 
1003     public void doRoundRect(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, float arcW, float arcH, boolean isfill) {
1004         // System.err.println("--- doRoundRect");
1005         if ((isfill) && (isCustomPaint(sg2d))) {
1006             setupGraphicsState(sg2d, kRoundRect, (int) x, (int) y, (int) width, (int) height);
1007         } else {
1008             setupGraphicsState(sg2d, kRoundRect, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1009         }
1010         renderer.doRoundRect(this, x, y, width, height, arcW, arcH, isfill);
1011     }
1012 
1013     public void doOval(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, boolean isfill) {
1014         // System.err.println("--- doOval");
1015         if ((isfill) && (isCustomPaint(sg2d))) {
1016             setupGraphicsState(sg2d, kOval, (int) x, (int) y, (int) width, (int) height);
1017         } else {
1018             setupGraphicsState(sg2d, kOval, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1019         }
1020         renderer.doOval(this, x, y, width, height, isfill);
1021     }
1022 
1023     public void doArc(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, float startAngle, float arcAngle, int type, boolean isfill) {
1024         // System.err.println("--- doArc");
1025         if ((isfill) && (isCustomPaint(sg2d))) {
1026             setupGraphicsState(sg2d, kArc, (int) x, (int) y, (int) width, (int) height);
1027         } else {
1028             setupGraphicsState(sg2d, kArc, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1029         }
1030 
1031         renderer.doArc(this, x, y, width, height, startAngle, arcAngle, type, isfill);
1032     }
1033 
1034     public void doPolygon(CRenderer renderer, SunGraphics2D sg2d, int xpoints[], int ypoints[], int npoints, boolean ispolygon, boolean isfill) {
1035         // System.err.println("--- doPolygon");
1036 
1037         if ((isfill) && (isCustomPaint(sg2d))) {
1038             int minx = xpoints[0];
1039             int miny = ypoints[0];
1040             int maxx = minx;
1041             int maxy = miny;
1042             for (int i = 1; i < npoints; i++) {
1043                 int x = xpoints[i];
1044                 if (x < minx) {
1045                     minx = x;
1046                 } else if (x > maxx) {
1047                     maxx = x;
1048                 }
1049 
1050                 int y = ypoints[i];
1051                 if (y < miny) {
1052                     miny = y;
1053                 } else if (y > maxy) {
1054                     maxy = y;
1055                 }
1056             }
1057             setupGraphicsState(sg2d, kPolygon, minx, miny, maxx - minx, maxy - miny);
1058         } else {
1059             setupGraphicsState(sg2d, kPolygon, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1060         }
1061         renderer.doPoly(this, xpoints, ypoints, npoints, ispolygon, isfill);
1062     }
1063 
1064     FloatBuffer shapeCoordinatesArray = null;
1065     IntBuffer shapeTypesArray = null;
1066 
1067     public void drawfillShape(CRenderer renderer, SunGraphics2D sg2d, GeneralPath gp, boolean isfill, boolean shouldApplyOffset) {
1068         // System.err.println("--- drawfillShape");
1069 
1070         if ((isfill) && (isCustomPaint(sg2d))) {
1071             Rectangle bounds = gp.getBounds();
1072             setupGraphicsState(sg2d, kShape, bounds.x, bounds.y, bounds.width, bounds.height);
1073         } else {
1074             setupGraphicsState(sg2d, kShape, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1075         }
1076 
1077         int shapeLength = getPathLength(gp);
1078 
1079         if ((shapeCoordinatesArray == null) || (shapeCoordinatesArray.capacity() < (shapeLength * 6))) {
1080             shapeCoordinatesArray = getBufferOfSize(shapeLength * 6).asFloatBuffer(); // segment can have a max of 6
1081                                                                                       // coordinates
1082         }
1083         if ((shapeTypesArray == null) || (shapeTypesArray.capacity() < shapeLength)) {
1084             shapeTypesArray = getBufferOfSize(shapeLength).asIntBuffer();
1085         }
1086 
1087         int windingRule = getPathCoordinates(gp, shapeCoordinatesArray, shapeTypesArray);
1088 
1089         renderer.doShape(this, shapeLength, shapeCoordinatesArray, shapeTypesArray, windingRule, isfill, shouldApplyOffset);
1090     }
1091 
1092     public void blitImage(CRenderer renderer, SunGraphics2D sg2d, SurfaceData img, boolean fliph, boolean flipv, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, Color bgColor) {
1093         // System.err.println("--- blitImage sx="+sx+", sy="+sy+", sw="+sw+", sh="+sh+", img="+img);
1094         OSXOffScreenSurfaceData osxsd = (OSXOffScreenSurfaceData) img;
1095         synchronized (osxsd.getLockObject()) {
1096             int w = osxsd.bim.getWidth();
1097             int h = osxsd.bim.getHeight();
1098 
1099             // the image itself can have outstanding graphics primitives that might need to be flushed
1100             setupGraphicsState(sg2d, kImage, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1101 
1102             // 04/06/04 cmc: radr://3612381 Graphics.drawImage ignores bgcolor parameter
1103             if (bgColor != null) {
1104                 img = osxsd.getCopyWithBgColor(bgColor);
1105             }
1106 
1107             renderer.doImage(this, img, fliph, flipv, w, h, sx, sy, sw, sh, dx, dy, dw, dh);
1108         }
1109     }
1110 
1111     public interface CGContextDrawable {
1112         public void drawIntoCGContext(final long cgContext);
1113     }
1114 
1115     public void drawString(CTextPipe renderer, SunGraphics2D sg2d, long nativeStrikePtr, String str, double x, double y) {
1116         // System.err.println("--- drawString str=\""+str+"\"");
1117         // see <rdar://problem/3825795>. We don't want to call anything if the string is empty!
1118         if (str.length() == 0) { return; }
1119 
1120         setupGraphicsState(sg2d, kString, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1121         renderer.doDrawString(this, nativeStrikePtr, str, x, y);
1122     }
1123 
1124     public void drawGlyphs(CTextPipe renderer, SunGraphics2D sg2d, long nativeStrikePtr, GlyphVector gv, float x, float y) {
1125         // System.err.println("--- drawGlyphs");
1126         setupGraphicsState(sg2d, kGlyphs, gv.getFont(), 0, 0, fBounds.width, fBounds.height);
1127         renderer.doDrawGlyphs(this, nativeStrikePtr, gv, x, y);
1128     }
1129 
1130     public void drawUnicodes(CTextPipe renderer, SunGraphics2D sg2d, long nativeStrikePtr, char unicodes[], int offset, int length, float x, float y) {
1131         // System.err.println("--- drawUnicodes "+(new String(unicodes, offset, length)));
1132         setupGraphicsState(sg2d, kUnicodes, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1133         if (length == 1) {
1134             renderer.doOneUnicode(this, nativeStrikePtr, unicodes[offset], x, y);
1135         } else {
1136             renderer.doUnicodes(this, nativeStrikePtr, unicodes, offset, length, x, y);
1137         }
1138     }
1139 
1140     // used by copyArea:
1141 
1142     Rectangle srcCopyAreaRect = new Rectangle();
1143     Rectangle dstCopyAreaRect = new Rectangle();
1144     Rectangle finalCopyAreaRect = new Rectangle();
1145     Rectangle copyAreaBounds = new Rectangle();
1146 
1147     void intersection(Rectangle r1, Rectangle r2, Rectangle r3) {
1148         // this code is taken from Rectangle.java (modified to put results in r3)
1149         int tx1 = r1.x;
1150         int ty1 = r1.y;
1151         long tx2 = tx1 + r1.width;
1152         long ty2 = ty1 + r1.height;
1153 
1154         int rx1 = r2.x;
1155         int ry1 = r2.y;
1156         long rx2 = rx1 + r2.width;
1157         long ry2 = ry1 + r2.height;
1158 
1159         if (tx1 < rx1) tx1 = rx1;
1160         if (ty1 < ry1) ty1 = ry1;
1161         if (tx2 > rx2) tx2 = rx2;
1162         if (ty2 > ry2) ty2 = ry2;
1163 
1164         tx2 -= tx1;
1165         ty2 -= ty1;
1166 
1167         // tx2,ty2 will never overflow (they will never be
1168         // larger than the smallest of the two source w,h)
1169         // they might underflow, though...
1170         if (tx2 < Integer.MIN_VALUE) tx2 = Integer.MIN_VALUE;
1171         if (ty2 < Integer.MIN_VALUE) ty2 = Integer.MIN_VALUE;
1172 
1173         r3.setBounds(tx1, ty1, (int) tx2, (int) ty2);
1174     }
1175 
1176     /**
1177      * Clips the copy area to the heavyweight bounds and returns the clipped rectangle.
1178      * The returned clipped rectangle is in the coordinate space of the surface.
1179      */
1180     protected Rectangle clipCopyArea(SunGraphics2D sg2d, int x, int y, int w, int h, int dx, int dy) {
1181         // we need to clip against the heavyweight bounds
1182         copyAreaBounds.setBounds(sg2d.devClip.getLoX(), sg2d.devClip.getLoY(), sg2d.devClip.getWidth(), sg2d.devClip.getHeight());
1183 
1184         // clip src rect
1185         srcCopyAreaRect.setBounds(x, y, w, h);
1186         intersection(srcCopyAreaRect, copyAreaBounds, srcCopyAreaRect);
1187         if ((srcCopyAreaRect.width <= 0) || (srcCopyAreaRect.height <= 0)) {
1188             // src rect outside bounds
1189             return null;
1190         }
1191 
1192         // clip dst rect
1193         dstCopyAreaRect.setBounds(srcCopyAreaRect.x + dx, srcCopyAreaRect.y + dy, srcCopyAreaRect.width, srcCopyAreaRect.height);
1194         intersection(dstCopyAreaRect, copyAreaBounds, dstCopyAreaRect);
1195         if ((dstCopyAreaRect.width <= 0) || (dstCopyAreaRect.height <= 0)) {
1196             // dst rect outside clip
1197             return null;
1198         }
1199 
1200         x = dstCopyAreaRect.x - dx;
1201         y = dstCopyAreaRect.y - dy;
1202         w = dstCopyAreaRect.width;
1203         h = dstCopyAreaRect.height;
1204 
1205         finalCopyAreaRect.setBounds(x, y, w, h);
1206 
1207         return finalCopyAreaRect;
1208     }
1209 
1210     // <rdar://3785539> We only need to mark dirty on screen surfaces. This method is
1211     // marked as protected and it is intended for subclasses to override if they need to
1212     // be notified when the surface is dirtied. See CPeerSurfaceData.markDirty() for implementation.
1213     // We don't do anything for buffered images.
1214     protected void markDirty(boolean markAsDirty) {
1215         // do nothing by default
1216     }
1217 
1218     // LazyDrawing optimization implementation:
1219 
1220     @Override
1221     public boolean canRenderLCDText(SunGraphics2D sg2d) {
1222         if (sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY &&
1223                 sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR &&
1224                 sg2d.clipState <= SunGraphics2D.CLIP_RECTANGULAR &&
1225                 // sg2d.surfaceData.getTransparency() == Transparency.OPAQUE &&
1226                 // This last test is a workaround until we fix loop selection
1227                 // in the pipe validation
1228                 sg2d.antialiasHint != SunHints.INTVAL_ANTIALIAS_ON) { return true; }
1229         return false; /* for now - in the future we may want to search */
1230     }
1231 
1232     public static boolean IsSimpleColor(Object c) {
1233         return ((c instanceof Color) || (c instanceof SystemColor) || (c instanceof javax.swing.plaf.ColorUIResource));
1234     }
1235 
1236     static {
1237         if ((kColorPointerIndex % 2) != 0) {
1238             System.err.println("kColorPointerIndex=" + kColorPointerIndex + " is NOT aligned for 64 bit");
1239             System.exit(0);
1240         }
1241     }
1242 }