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 gradient color +
  83                                                     // linear 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     // live resizing info
 301     @Native static final int kCanDrawDuringLiveResizeIndex = 49;
 302 
 303     @Native static final int kSizeOfParameters = kCanDrawDuringLiveResizeIndex + 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 kColorLinearOrRadialGradient = 4;
 340 
 341     // possible gradient color states
 342     @Native static final int kColorNonCyclic = 0;
 343     @Native static final int kColorCyclic = 1;
 344 
 345     // possible clip states
 346     @Native static final int kClipRect = 0;
 347     @Native static final int kClipShape = 1;
 348 
 349     static int getRendererTypeForPrimitive(int primitiveType) {
 350         switch (primitiveType) {
 351             case kImage:
 352                 return kImage;
 353             case kCopyArea:
 354                 return kCopyArea;
 355             case kExternal:
 356                 return kExternal;
 357             case kString:
 358             case kGlyphs:
 359             case kUnicodes:
 360                 return kText;
 361             default:
 362                 return kPrimitive;
 363         }
 364     }
 365 
 366     int fChangeFlag;
 367     protected ByteBuffer fGraphicsStates = null;
 368     IntBuffer fGraphicsStatesInt = null;
 369     FloatBuffer fGraphicsStatesFloat = null;
 370     LongBuffer fGraphicsStatesLong = null;
 371     protected Object[] fGraphicsStatesObject = null;
 372 
 373     Rectangle userBounds = new Rectangle();
 374     float lastUserX = 0;
 375     float lastUserY = 0;
 376     float lastUserW = 0;
 377     float lastUserH = 0;
 378 
 379     void setUserBounds(SunGraphics2D sg2d, int x, int y, int width, int height) {
 380         if ((lastUserX != x) || (lastUserY != y) || (lastUserW != width) || (lastUserH != height)) {
 381             lastUserX = x;
 382             lastUserY = y;
 383             lastUserW = width;
 384             lastUserH = height;
 385 
 386             this.fGraphicsStatesInt.put(kBoundsXIndex, x);
 387             this.fGraphicsStatesInt.put(kBoundsYIndex, y);
 388             this.fGraphicsStatesInt.put(kBoundsWidthIndex, width);
 389             this.fGraphicsStatesInt.put(kBoundsHeightIndex, height);
 390 
 391             userBounds.setBounds(x, y, width, height);
 392 
 393             this.fChangeFlag = (this.fChangeFlag | kBoundsChangedBit);
 394         } else {
 395             this.fChangeFlag = (this.fChangeFlag & kBoundsNotChangedBit);
 396         }
 397     }
 398 
 399     static ByteBuffer getBufferOfSize(int size) {
 400         ByteBuffer buffer = ByteBuffer.allocateDirect(size * 4);
 401         buffer.order(ByteOrder.nativeOrder());
 402         return buffer;
 403     }
 404 
 405     FloatBuffer clipCoordinatesArray = null;
 406     IntBuffer clipTypesArray = null;
 407     Shape lastClipShape = null;
 408     float lastClipX = 0;
 409     float lastClipY = 0;
 410     float lastClipW = 0;
 411     float lastClipH = 0;
 412 
 413     void setupClip(SunGraphics2D sg2d) {
 414         switch (sg2d.clipState) {
 415             case SunGraphics2D.CLIP_DEVICE:
 416             case SunGraphics2D.CLIP_RECTANGULAR: {
 417                 Region clip = sg2d.getCompClip();
 418                 float x = clip.getLoX();
 419                 float y = clip.getLoY();
 420                 float w = clip.getWidth();
 421                 float h = clip.getHeight();
 422                 if ((this.fGraphicsStatesInt.get(kClipStateIndex) != kClipRect) ||
 423                         (x != lastClipX) ||
 424                             (y != lastClipY) ||
 425                                 (w != lastClipW) ||
 426                                     (h != lastClipH)) {
 427                     this.fGraphicsStatesFloat.put(kClipXIndex, x);
 428                     this.fGraphicsStatesFloat.put(kClipYIndex, y);
 429                     this.fGraphicsStatesFloat.put(kClipWidthIndex, w);
 430                     this.fGraphicsStatesFloat.put(kClipHeightIndex, h);
 431 
 432                     lastClipX = x;
 433                     lastClipY = y;
 434                     lastClipW = w;
 435                     lastClipH = h;
 436 
 437                     this.fChangeFlag = (this.fChangeFlag | kClipChangedBit);
 438                 } else {
 439                     this.fChangeFlag = (this.fChangeFlag & kClipNotChangedBit);
 440                 }
 441                 this.fGraphicsStatesInt.put(kClipStateIndex, kClipRect);
 442                 break;
 443             }
 444             case SunGraphics2D.CLIP_SHAPE: {
 445                 // if (lastClipShape != sg2d.usrClip) shapes are mutable!, and doing "equals" traverses all
 446                 // the coordinates, so we might as well do all of it anyhow
 447                 lastClipShape = sg2d.usrClip;
 448 
 449                 GeneralPath gp = null;
 450 
 451                 if (sg2d.usrClip instanceof GeneralPath) {
 452                     gp = (GeneralPath) sg2d.usrClip;
 453                 } else {
 454                     gp = new GeneralPath(sg2d.usrClip);
 455                 }
 456 
 457                 int shapeLength = getPathLength(gp);
 458 
 459                 if ((clipCoordinatesArray == null) || (clipCoordinatesArray.capacity() < (shapeLength * 6))) {
 460                     clipCoordinatesArray = getBufferOfSize(shapeLength * 6).asFloatBuffer(); // segment can have a
 461                                                                                              // max of 6 coordinates
 462                 }
 463                 if ((clipTypesArray == null) || (clipTypesArray.capacity() < shapeLength)) {
 464                     clipTypesArray = getBufferOfSize(shapeLength).asIntBuffer();
 465                 }
 466 
 467                 int windingRule = getPathCoordinates(gp, clipCoordinatesArray, clipTypesArray);
 468 
 469                 this.fGraphicsStatesInt.put(kClipNumTypesIndex, clipTypesArray.position());
 470                 this.fGraphicsStatesInt.put(kClipNumCoordsIndex, clipCoordinatesArray.position());
 471                 this.fGraphicsStatesInt.put(kClipWindingRuleIndex, windingRule);
 472                 this.fGraphicsStatesObject[kClipTypesIndex] = clipTypesArray;
 473                 this.fGraphicsStatesObject[kClipCoordinatesIndex] = clipCoordinatesArray;
 474 
 475                 this.fChangeFlag = (this.fChangeFlag | kClipChangedBit);
 476                 this.fGraphicsStatesInt.put(kClipStateIndex, kClipShape);
 477                 break;
 478             }
 479         }
 480 
 481     }
 482 
 483     final double[] lastCTM = new double[6];
 484     float lastCTMa = 0;
 485     float lastCTMb = 0;
 486     float lastCTMc = 0;
 487     float lastCTMd = 0;
 488     float lastCTMtx = 0;
 489     float lastCTMty = 0;
 490 
 491     void setupTransform(SunGraphics2D sg2d) {
 492         sg2d.transform.getMatrix(lastCTM);
 493 
 494         float a = (float) lastCTM[0];
 495         float b = (float) lastCTM[1];
 496         float c = (float) lastCTM[2];
 497         float d = (float) lastCTM[3];
 498         float tx = (float) lastCTM[4];
 499         float ty = (float) lastCTM[5];
 500         if (tx != lastCTMtx ||
 501                 ty != lastCTMty ||
 502                     a != lastCTMa ||
 503                         b != lastCTMb ||
 504                             c != lastCTMc ||
 505                                 d != lastCTMd) {
 506             this.fGraphicsStatesFloat.put(kCTMaIndex, a);
 507             this.fGraphicsStatesFloat.put(kCTMbIndex, b);
 508             this.fGraphicsStatesFloat.put(kCTMcIndex, c);
 509             this.fGraphicsStatesFloat.put(kCTMdIndex, d);
 510             this.fGraphicsStatesFloat.put(kCTMtxIndex, tx);
 511             this.fGraphicsStatesFloat.put(kCTMtyIndex, ty);
 512 
 513             lastCTMa = a;
 514             lastCTMb = b;
 515             lastCTMc = c;
 516             lastCTMd = d;
 517             lastCTMtx = tx;
 518             lastCTMty = ty;
 519 
 520             this.fChangeFlag = (this.fChangeFlag | kCTMChangedBit);
 521         } else {
 522             this.fChangeFlag = (this.fChangeFlag & kCTMNotChangedBit);
 523         }
 524     }
 525 
 526     static AffineTransform sIdentityMatrix = new AffineTransform();
 527     Paint lastPaint = null;
 528     long lastPaintPtr = 0;
 529     int lastPaintRGB = 0;
 530     int lastPaintIndex = 0;
 531     BufferedImage texturePaintImage = null;
 532 
 533     void setupPaint(SunGraphics2D sg2d, int x, int y, int w, int h) {
 534         if (sg2d.paint instanceof SystemColor) {
 535             SystemColor color = (SystemColor) sg2d.paint;
 536             int index = color.hashCode(); // depends on Color.java hashCode implementation! (returns "value" of color)
 537             if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorSystem) || (index != this.lastPaintIndex)) {
 538                 this.lastPaintIndex = index;
 539 
 540                 this.fGraphicsStatesInt.put(kColorStateIndex, kColorSystem);
 541                 this.fGraphicsStatesInt.put(kColorIndexValueIndex, index);
 542 
 543                 this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
 544             } else {
 545                 this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
 546             }
 547         } else if (sg2d.paint instanceof Color) {
 548             Color color = (Color) sg2d.paint;
 549             int rgb = color.getRGB();
 550             if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorSimple) || (rgb != this.lastPaintRGB)) {
 551                 this.lastPaintRGB = rgb;
 552 
 553                 this.fGraphicsStatesInt.put(kColorStateIndex, kColorSimple);
 554                 this.fGraphicsStatesInt.put(kColorRGBValueIndex, rgb);
 555 
 556                 this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
 557             } else {
 558                 this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
 559             }
 560         } else if (sg2d.paint instanceof GradientPaint) {
 561             if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorGradient) || (lastPaint != sg2d.paint)) {
 562                 GradientPaint color = (GradientPaint) sg2d.paint;
 563                 this.fGraphicsStatesInt.put(kColorStateIndex, kColorGradient);
 564                 this.fGraphicsStatesInt.put(kColorRGBValue1Index, color.getColor1().getRGB());
 565                 this.fGraphicsStatesInt.put(kColorRGBValue2Index, color.getColor2().getRGB());
 566                 this.fGraphicsStatesInt.put(kColorIsCyclicIndex, (color.isCyclic()) ? kColorCyclic : kColorNonCyclic);
 567                 Point2D p = color.getPoint1();
 568                 this.fGraphicsStatesFloat.put(kColorx1Index, (float) p.getX());
 569                 this.fGraphicsStatesFloat.put(kColory1Index, (float) p.getY());
 570                 p = color.getPoint2();
 571                 this.fGraphicsStatesFloat.put(kColorx2Index, (float) p.getX());
 572                 this.fGraphicsStatesFloat.put(kColory2Index, (float) p.getY());
 573 
 574                 this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
 575             } else {
 576                 this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
 577             }
 578         } else if (sg2d.paint instanceof LinearGradientPaint) {
 579             if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorGradient) || (lastPaint != sg2d.paint)) {
 580                 LinearGradientPaint color = (LinearGradientPaint) sg2d.paint;
 581                 this.fGraphicsStatesInt.put(kColorStateIndex, kColorLinearOrRadialGradient);
 582                 int numColor = color.getColors().length;
 583                 int colorArray[] = new int[numColor];
 584                 for (int i = 0; i < numColor; i++) {
 585                     colorArray[i] = color.getColors()[i].getRGB();
 586                 }
 587                 this.fGraphicsStatesObject[kColorArrayIndex] = colorArray;
 588                 
 589                 int numFractions = color.getFractions().length;
 590                 float fractionArray[] = new float[numFractions];
 591                 for (int i = 0; i < numFractions; i++) {
 592                     fractionArray[i] = color.getFractions()[i];
 593                 }
 594                 this.fGraphicsStatesObject[kFractionsArrayIndex] = color.getFractions();
 595                 
 596                 this.fGraphicsStatesInt.put(kColorIsCyclicIndex, (color.getCycleMethod() == LinearGradientPaint.CycleMethod.NO_CYCLE) ? kColorNonCyclic  : kColorCyclic);
 597                 Point2D p = color.getStartPoint();
 598                 this.fGraphicsStatesFloat.put(kColorx1Index, (float) p.getX());
 599                 this.fGraphicsStatesFloat.put(kColory1Index, (float) p.getY());
 600                 p = color.getEndPoint();
 601                 this.fGraphicsStatesFloat.put(kColorx2Index, (float) p.getX());
 602                 this.fGraphicsStatesFloat.put(kColory2Index, (float) p.getY()); 
 603 
 604                 this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
 605             } else {
 606                 this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
 607             }
 608         } else if (sg2d.paint instanceof TexturePaint) {
 609             if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorTexture) || (lastPaint != sg2d.paint)) {
 610                 TexturePaint color = (TexturePaint) sg2d.paint;
 611                 this.fGraphicsStatesInt.put(kColorStateIndex, kColorTexture);
 612                 texturePaintImage = color.getImage();
 613                 SurfaceData textureSurfaceData = BufImgSurfaceData.createData(texturePaintImage);
 614                 this.fGraphicsStatesInt.put(kColorWidthIndex, texturePaintImage.getWidth());
 615                 this.fGraphicsStatesInt.put(kColorHeightIndex, texturePaintImage.getHeight());
 616                 Rectangle2D anchor = color.getAnchorRect();
 617                 this.fGraphicsStatesFloat.put(kColortxIndex, (float) anchor.getX());
 618                 this.fGraphicsStatesFloat.put(kColortyIndex, (float) anchor.getY());
 619                 this.fGraphicsStatesFloat.put(kColorsxIndex, (float) (anchor.getWidth() / texturePaintImage.getWidth()));
 620                 this.fGraphicsStatesFloat.put(kColorsyIndex, (float) (anchor.getHeight() / texturePaintImage.getHeight()));
 621                 this.fGraphicsStatesObject[kTextureImageIndex] = textureSurfaceData;
 622 
 623                 this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
 624             } else {
 625                 this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
 626             }
 627         } else {
 628             if ((this.fGraphicsStatesInt.get(kColorStateIndex) != kColorTexture) || (lastPaint != sg2d.paint) || ((this.fChangeFlag & kBoundsChangedBit) != 0)) {
 629                 PaintContext context = sg2d.paint.createContext(sg2d.getDeviceColorModel(), userBounds, userBounds, sIdentityMatrix, sg2d.getRenderingHints());
 630                 WritableRaster raster = (WritableRaster) (context.getRaster(userBounds.x, userBounds.y, userBounds.width, userBounds.height));
 631                 ColorModel cm = context.getColorModel();
 632                 texturePaintImage = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
 633 
 634                 this.fGraphicsStatesInt.put(kColorStateIndex, kColorTexture);
 635                 this.fGraphicsStatesInt.put(kColorWidthIndex, texturePaintImage.getWidth());
 636                 this.fGraphicsStatesInt.put(kColorHeightIndex, texturePaintImage.getHeight());
 637                 this.fGraphicsStatesFloat.put(kColortxIndex, (float) userBounds.getX());
 638                 this.fGraphicsStatesFloat.put(kColortyIndex, (float) userBounds.getY());
 639                 this.fGraphicsStatesFloat.put(kColorsxIndex, 1.0f);
 640                 this.fGraphicsStatesFloat.put(kColorsyIndex, 1.0f);
 641                 this.fGraphicsStatesObject[kTextureImageIndex] = sun.awt.image.BufImgSurfaceData.createData(texturePaintImage);
 642 
 643                 context.dispose();
 644 
 645                 this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
 646             } else {
 647                 this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
 648             }
 649         }
 650         lastPaint = sg2d.paint;
 651     }
 652 
 653     Composite lastComposite;
 654     int lastCompositeAlphaRule = 0;
 655     float lastCompositeAlphaValue = 0;
 656 
 657     void setupComposite(SunGraphics2D sg2d) {
 658         Composite composite = sg2d.composite;
 659 
 660         if (lastComposite != composite) {
 661             lastComposite = composite;
 662 
 663             // For composite state COMP_ISCOPY, COMP_XOR or COMP_CUSTOM set alpha compositor to COPY:
 664             int alphaRule = AlphaComposite.SRC_OVER;
 665             float alphaValue = 1.0f;
 666 
 667             // For composite state COMP_ISCOPY composite could be null. If it's not (or composite state == COMP_ALPHA)
 668             // get alpha compositor's values:
 669             if ((sg2d.compositeState <= SunGraphics2D.COMP_ALPHA) && (composite != null)) {
 670                 AlphaComposite alphaComposite = (AlphaComposite) composite;
 671                 alphaRule = alphaComposite.getRule();
 672                 alphaValue = alphaComposite.getAlpha();
 673             }
 674 
 675             // 2-17-03 VL: [Radar 3174922]
 676             // For COMP_XOR and COMP_CUSTOM compositing modes we should be setting alphaRule = AlphaComposite.SRC
 677             // which should map to kCGCompositeCopy.
 678 
 679             if ((lastCompositeAlphaRule != alphaRule) || (lastCompositeAlphaValue != alphaValue)) {
 680                 this.fGraphicsStatesInt.put(kCompositeRuleIndex, alphaRule);
 681                 this.fGraphicsStatesFloat.put(kCompositeValueIndex, alphaValue);
 682 
 683                 lastCompositeAlphaRule = alphaRule;
 684                 lastCompositeAlphaValue = alphaValue;
 685 
 686                 this.fChangeFlag = (this.fChangeFlag | kCompositeChangedBit);
 687             } else {
 688                 this.fChangeFlag = (this.fChangeFlag & kCompositeNotChangedBit);
 689             }
 690         } else {
 691             this.fChangeFlag = (this.fChangeFlag & kCompositeNotChangedBit);
 692         }
 693     }
 694 
 695     BasicStroke lastStroke = null;
 696     static BasicStroke defaultBasicStroke = new BasicStroke();
 697 
 698     void setupStroke(SunGraphics2D sg2d) {
 699         BasicStroke stroke = defaultBasicStroke;
 700 
 701         if (sg2d.stroke instanceof BasicStroke) {
 702             stroke = (BasicStroke) sg2d.stroke;
 703         }
 704 
 705         if (lastStroke != stroke) {
 706             this.fGraphicsStatesObject[kStrokeDashArrayIndex] = stroke.getDashArray();
 707             this.fGraphicsStatesFloat.put(kStrokeDashPhaseIndex, stroke.getDashPhase());
 708             this.fGraphicsStatesInt.put(kStrokeCapIndex, stroke.getEndCap());
 709             this.fGraphicsStatesInt.put(kStrokeJoinIndex, stroke.getLineJoin());
 710             this.fGraphicsStatesFloat.put(kStrokeWidthIndex, stroke.getLineWidth());
 711             this.fGraphicsStatesFloat.put(kStrokeLimitIndex, stroke.getMiterLimit());
 712 
 713             this.fChangeFlag = (this.fChangeFlag | kStrokeChangedBit);
 714 
 715             lastStroke = stroke;
 716         } else {
 717             this.fChangeFlag = (this.fChangeFlag & kStrokeNotChangedBit);
 718         }
 719     }
 720 
 721     Font lastFont;
 722 
 723     void setupFont(Font font, Paint paint) {
 724         if (font == null) { return; }
 725 
 726         // We have to setup the kFontPaintIndex if we have changed the color so we added the last
 727         // test to see if the color has changed - needed for complex strings
 728         // see Radar 3368674
 729         if ((font != lastFont) || ((this.fChangeFlag & kColorChangedBit) != 0)) {
 730             this.fGraphicsStatesObject[kFontIndex] = font;
 731             this.fGraphicsStatesObject[kFontPaintIndex] = paint;
 732 
 733             this.fChangeFlag = (this.fChangeFlag | kFontChangedBit);
 734 
 735             lastFont = font;
 736         } else {
 737             this.fChangeFlag = (this.fChangeFlag & kFontNotChangedBit);
 738         }
 739     }
 740 
 741     void setupRenderingHints(SunGraphics2D sg2d) {
 742         boolean hintsChanged = false;
 743 
 744         // Significant for draw, fill, text, and image ops:
 745         int antialiasHint = sg2d.antialiasHint;
 746         if (this.fGraphicsStatesInt.get(kHintsAntialiasIndex) != antialiasHint) {
 747             this.fGraphicsStatesInt.put(kHintsAntialiasIndex, antialiasHint);
 748             hintsChanged = true;
 749         }
 750 
 751         // Significant only for text ops:
 752         int textAntialiasHint = sg2d.textAntialiasHint;
 753         if (this.fGraphicsStatesInt.get(kHintsTextAntialiasIndex) != textAntialiasHint) {
 754             this.fGraphicsStatesInt.put(kHintsTextAntialiasIndex, textAntialiasHint);
 755             hintsChanged = true;
 756         }
 757 
 758         // Significant only for text ops:
 759         int fractionalMetricsHint = sg2d.fractionalMetricsHint;
 760         if (this.fGraphicsStatesInt.get(kHintsFractionalMetricsIndex) != fractionalMetricsHint) {
 761             this.fGraphicsStatesInt.put(kHintsFractionalMetricsIndex, fractionalMetricsHint);
 762             hintsChanged = true;
 763         }
 764 
 765         // Significant only for image ops:
 766         int renderHint = sg2d.renderHint;
 767         if (this.fGraphicsStatesInt.get(kHintsRenderingIndex) != renderHint) {
 768             this.fGraphicsStatesInt.put(kHintsRenderingIndex, renderHint);
 769             hintsChanged = true;
 770         }
 771 
 772         // Significant only for image ops:
 773         Object hintValue = sg2d.getRenderingHint(RenderingHints.KEY_INTERPOLATION);
 774         int interpolationHint = (hintValue != null ? ((SunHints.Value) hintValue).getIndex() : -1);
 775         if (this.fGraphicsStatesInt.get(kHintsInterpolationIndex) != interpolationHint) {
 776             this.fGraphicsStatesInt.put(kHintsInterpolationIndex, interpolationHint);
 777             hintsChanged = true;
 778         }
 779 
 780         if (hintsChanged) {
 781             this.fChangeFlag = (this.fChangeFlag | kHintsChangedBit);
 782         } else {
 783             this.fChangeFlag = (this.fChangeFlag & kHintsNotChangedBit);
 784         }
 785     }
 786 
 787     SunGraphics2D sg2dCurrent = null;
 788     Thread threadCurrent = null;
 789 
 790     void setupGraphicsState(SunGraphics2D sg2d, int primitiveType) {
 791         setupGraphicsState(sg2d, primitiveType, sg2d.font, 0, 0, fBounds.width, fBounds.height); // deviceBounds into userBounds
 792     }
 793 
 794     void setupGraphicsState(SunGraphics2D sg2d, int primitiveType, int x, int y, int w, int h) {
 795         setupGraphicsState(sg2d, primitiveType, sg2d.font, x, y, w, h);
 796     }
 797 
 798     // the method below is overriden by CPeerSurface to check the last peer used to draw
 799     // if the peer changed we finish lazy drawing
 800     void setupGraphicsState(SunGraphics2D sg2d, int primitiveType, Font font, int x, int y, int w, int h) {
 801         this.fChangeFlag = 0;
 802 
 803         setUserBounds(sg2d, x, y, w, h);
 804 
 805         Thread thread = Thread.currentThread();
 806         if ((this.sg2dCurrent != sg2d) || (this.threadCurrent != thread)) {
 807             this.sg2dCurrent = sg2d;
 808             this.threadCurrent = thread;
 809 
 810             setupClip(sg2d);
 811             setupTransform(sg2d);
 812             setupPaint(sg2d, x, y, w, h);
 813             setupComposite(sg2d);
 814             setupStroke(sg2d);
 815             setupFont(font, sg2d.paint);
 816             setupRenderingHints(sg2d);
 817 
 818             this.fChangeFlag = kEverythingChangedFlag;
 819         } else {
 820             int rendererType = getRendererTypeForPrimitive(primitiveType);
 821 
 822             setupClip(sg2d);
 823             setupTransform(sg2d);
 824 
 825             if (rendererType != kCopyArea) {
 826                 setupComposite(sg2d);
 827                 setupRenderingHints(sg2d);
 828 
 829                 if ((rendererType != kImage)) {
 830                     setupPaint(sg2d, x, y, w, h);
 831                     setupStroke(sg2d);
 832                 }
 833                 if (rendererType != kPrimitive) {
 834                     setupFont(font, sg2d.paint);
 835                 }
 836 
 837             }
 838         }
 839 
 840         this.fGraphicsStatesInt.put(kChangeFlagIndex, this.fChangeFlag);
 841     }
 842 
 843     boolean isCustomPaint(SunGraphics2D sg2d) {
 844         if ((sg2d.paint instanceof Color) || (sg2d.paint instanceof SystemColor) || (sg2d.paint instanceof GradientPaint) || (sg2d.paint instanceof TexturePaint)) { return false; }
 845 
 846         return true;
 847     }
 848 
 849     final float[] segmentCoordinatesArray = new float[6];
 850 
 851     int getPathLength(GeneralPath gp) {
 852         int length = 0;
 853 
 854         PathIterator pi = gp.getPathIterator(null);
 855         while (pi.isDone() == false) {
 856             pi.next();
 857             length++;
 858         }
 859 
 860         return length;
 861     }
 862 
 863     int getPathCoordinates(GeneralPath gp, FloatBuffer coordinates, IntBuffer types) {
 864         // System.err.println("getPathCoordinates");
 865         boolean skip = false;
 866 
 867         coordinates.clear();
 868         types.clear();
 869 
 870         int type;
 871 
 872         PathIterator pi = gp.getPathIterator(null);
 873         while (pi.isDone() == false) {
 874             skip = false;
 875             type = pi.currentSegment(segmentCoordinatesArray);
 876 
 877             switch (type) {
 878                 case PathIterator.SEG_MOVETO:
 879                     // System.err.println(" SEG_MOVETO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+")");
 880                     if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND &&
 881                             segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND) {
 882                         coordinates.put(segmentCoordinatesArray[0]);
 883                         coordinates.put(segmentCoordinatesArray[1]);
 884                     } else {
 885                         skip = true;
 886                     }
 887                     break;
 888                 case PathIterator.SEG_LINETO:
 889                     // System.err.println(" SEG_LINETO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+")");
 890                     if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND &&
 891                             segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND) {
 892                         coordinates.put(segmentCoordinatesArray[0]);
 893                         coordinates.put(segmentCoordinatesArray[1]);
 894                     } else {
 895                         skip = true;
 896                     }
 897                     break;
 898                 case PathIterator.SEG_QUADTO:
 899                     // System.err.println(" SEG_QUADTO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+"), ("+segmentCoordinatesArray[2]+", "+segmentCoordinatesArray[3]+")");
 900                     if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND &&
 901                             segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND &&
 902                             segmentCoordinatesArray[2] < UPPER_BND && segmentCoordinatesArray[2] > LOWER_BND &&
 903                             segmentCoordinatesArray[3] < UPPER_BND && segmentCoordinatesArray[3] > LOWER_BND) {
 904                         coordinates.put(segmentCoordinatesArray[0]);
 905                         coordinates.put(segmentCoordinatesArray[1]);
 906                         coordinates.put(segmentCoordinatesArray[2]);
 907                         coordinates.put(segmentCoordinatesArray[3]);
 908                     } else {
 909                         skip = true;
 910                     }
 911                     break;
 912                 case PathIterator.SEG_CUBICTO:
 913                     // System.err.println(" SEG_QUADTO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+"), ("+segmentCoordinatesArray[2]+", "+segmentCoordinatesArray[3]+"), ("+segmentCoordinatesArray[4]+", "+segmentCoordinatesArray[5]+")");
 914                     if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND &&
 915                             segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND &&
 916                             segmentCoordinatesArray[2] < UPPER_BND && segmentCoordinatesArray[2] > LOWER_BND &&
 917                             segmentCoordinatesArray[3] < UPPER_BND && segmentCoordinatesArray[3] > LOWER_BND &&
 918                             segmentCoordinatesArray[4] < UPPER_BND && segmentCoordinatesArray[4] > LOWER_BND &&
 919                             segmentCoordinatesArray[5] < UPPER_BND && segmentCoordinatesArray[5] > LOWER_BND) {
 920                         coordinates.put(segmentCoordinatesArray[0]);
 921                         coordinates.put(segmentCoordinatesArray[1]);
 922                         coordinates.put(segmentCoordinatesArray[2]);
 923                         coordinates.put(segmentCoordinatesArray[3]);
 924                         coordinates.put(segmentCoordinatesArray[4]);
 925                         coordinates.put(segmentCoordinatesArray[5]);
 926                     } else {
 927                         skip = true;
 928                     }
 929                     break;
 930                 case PathIterator.SEG_CLOSE:
 931                     // System.err.println(" SEG_CLOSE");
 932                     break;
 933             }
 934 
 935             if (!skip) {
 936                 types.put(type);
 937             }
 938 
 939             pi.next();
 940         }
 941 
 942         return pi.getWindingRule();
 943     }
 944 
 945     public void doLine(CRenderer renderer, SunGraphics2D sg2d, float x1, float y1, float x2, float y2) {
 946         // System.err.println("-- doLine x1="+x1+" y1="+y1+" x2="+x2+" y2="+y2+" paint="+sg2d.paint);
 947         setupGraphicsState(sg2d, kLine, sg2d.font, 0, 0, fBounds.width, fBounds.height);
 948         renderer.doLine(this, x1, y1, x2, y2);
 949     }
 950 
 951     public void doRect(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, boolean isfill) {
 952         // System.err.println("-- doRect x="+x+" y="+y+" w="+width+" h="+height+" isfill="+isfill+" paint="+sg2d.paint);
 953         if ((isfill) && (isCustomPaint(sg2d))) {
 954             setupGraphicsState(sg2d, kRect, (int) x, (int) y, (int) width, (int) height);
 955         } else {
 956             setupGraphicsState(sg2d, kRect, sg2d.font, 0, 0, fBounds.width, fBounds.height);
 957         }
 958         renderer.doRect(this, x, y, width, height, isfill);
 959     }
 960 
 961     public void doRoundRect(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, float arcW, float arcH, boolean isfill) {
 962         // System.err.println("--- doRoundRect");
 963         if ((isfill) && (isCustomPaint(sg2d))) {
 964             setupGraphicsState(sg2d, kRoundRect, (int) x, (int) y, (int) width, (int) height);
 965         } else {
 966             setupGraphicsState(sg2d, kRoundRect, sg2d.font, 0, 0, fBounds.width, fBounds.height);
 967         }
 968         renderer.doRoundRect(this, x, y, width, height, arcW, arcH, isfill);
 969     }
 970 
 971     public void doOval(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, boolean isfill) {
 972         // System.err.println("--- doOval");
 973         if ((isfill) && (isCustomPaint(sg2d))) {
 974             setupGraphicsState(sg2d, kOval, (int) x, (int) y, (int) width, (int) height);
 975         } else {
 976             setupGraphicsState(sg2d, kOval, sg2d.font, 0, 0, fBounds.width, fBounds.height);
 977         }
 978         renderer.doOval(this, x, y, width, height, isfill);
 979     }
 980 
 981     public void doArc(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, float startAngle, float arcAngle, int type, boolean isfill) {
 982         // System.err.println("--- doArc");
 983         if ((isfill) && (isCustomPaint(sg2d))) {
 984             setupGraphicsState(sg2d, kArc, (int) x, (int) y, (int) width, (int) height);
 985         } else {
 986             setupGraphicsState(sg2d, kArc, sg2d.font, 0, 0, fBounds.width, fBounds.height);
 987         }
 988 
 989         renderer.doArc(this, x, y, width, height, startAngle, arcAngle, type, isfill);
 990     }
 991 
 992     public void doPolygon(CRenderer renderer, SunGraphics2D sg2d, int xpoints[], int ypoints[], int npoints, boolean ispolygon, boolean isfill) {
 993         // System.err.println("--- doPolygon");
 994 
 995         if ((isfill) && (isCustomPaint(sg2d))) {
 996             int minx = xpoints[0];
 997             int miny = ypoints[0];
 998             int maxx = minx;
 999             int maxy = miny;
1000             for (int i = 1; i < npoints; i++) {
1001                 int x = xpoints[i];
1002                 if (x < minx) {
1003                     minx = x;
1004                 } else if (x > maxx) {
1005                     maxx = x;
1006                 }
1007 
1008                 int y = ypoints[i];
1009                 if (y < miny) {
1010                     miny = y;
1011                 } else if (y > maxy) {
1012                     maxy = y;
1013                 }
1014             }
1015             setupGraphicsState(sg2d, kPolygon, minx, miny, maxx - minx, maxy - miny);
1016         } else {
1017             setupGraphicsState(sg2d, kPolygon, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1018         }
1019         renderer.doPoly(this, xpoints, ypoints, npoints, ispolygon, isfill);
1020     }
1021 
1022     FloatBuffer shapeCoordinatesArray = null;
1023     IntBuffer shapeTypesArray = null;
1024 
1025     public void drawfillShape(CRenderer renderer, SunGraphics2D sg2d, GeneralPath gp, boolean isfill, boolean shouldApplyOffset) {
1026         // System.err.println("--- drawfillShape");
1027 
1028         if ((isfill) && (isCustomPaint(sg2d))) {
1029             Rectangle bounds = gp.getBounds();
1030             setupGraphicsState(sg2d, kShape, bounds.x, bounds.y, bounds.width, bounds.height);
1031         } else {
1032             setupGraphicsState(sg2d, kShape, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1033         }
1034 
1035         int shapeLength = getPathLength(gp);
1036 
1037         if ((shapeCoordinatesArray == null) || (shapeCoordinatesArray.capacity() < (shapeLength * 6))) {
1038             shapeCoordinatesArray = getBufferOfSize(shapeLength * 6).asFloatBuffer(); // segment can have a max of 6
1039                                                                                       // coordinates
1040         }
1041         if ((shapeTypesArray == null) || (shapeTypesArray.capacity() < shapeLength)) {
1042             shapeTypesArray = getBufferOfSize(shapeLength).asIntBuffer();
1043         }
1044 
1045         int windingRule = getPathCoordinates(gp, shapeCoordinatesArray, shapeTypesArray);
1046 
1047         renderer.doShape(this, shapeLength, shapeCoordinatesArray, shapeTypesArray, windingRule, isfill, shouldApplyOffset);
1048     }
1049 
1050     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) {
1051         // System.err.println("--- blitImage sx="+sx+", sy="+sy+", sw="+sw+", sh="+sh+", img="+img);
1052         OSXOffScreenSurfaceData osxsd = (OSXOffScreenSurfaceData) img;
1053         synchronized (osxsd.getLockObject()) {
1054             int w = osxsd.bim.getWidth();
1055             int h = osxsd.bim.getHeight();
1056 
1057             // the image itself can have outstanding graphics primitives that might need to be flushed
1058             setupGraphicsState(sg2d, kImage, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1059 
1060             // 04/06/04 cmc: radr://3612381 Graphics.drawImage ignores bgcolor parameter
1061             if (bgColor != null) {
1062                 img = osxsd.getCopyWithBgColor(bgColor);
1063             }
1064 
1065             renderer.doImage(this, img, fliph, flipv, w, h, sx, sy, sw, sh, dx, dy, dw, dh);
1066         }
1067     }
1068 
1069     public interface CGContextDrawable {
1070         public void drawIntoCGContext(final long cgContext);
1071     }
1072 
1073     public void drawString(CTextPipe renderer, SunGraphics2D sg2d, long nativeStrikePtr, String str, double x, double y) {
1074         // System.err.println("--- drawString str=\""+str+"\"");
1075         // see <rdar://problem/3825795>. We don't want to call anything if the string is empty!
1076         if (str.length() == 0) { return; }
1077 
1078         setupGraphicsState(sg2d, kString, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1079         renderer.doDrawString(this, nativeStrikePtr, str, x, y);
1080     }
1081 
1082     public void drawGlyphs(CTextPipe renderer, SunGraphics2D sg2d, long nativeStrikePtr, GlyphVector gv, float x, float y) {
1083         // System.err.println("--- drawGlyphs");
1084         setupGraphicsState(sg2d, kGlyphs, gv.getFont(), 0, 0, fBounds.width, fBounds.height);
1085         renderer.doDrawGlyphs(this, nativeStrikePtr, gv, x, y);
1086     }
1087 
1088     public void drawUnicodes(CTextPipe renderer, SunGraphics2D sg2d, long nativeStrikePtr, char unicodes[], int offset, int length, float x, float y) {
1089         // System.err.println("--- drawUnicodes "+(new String(unicodes, offset, length)));
1090         setupGraphicsState(sg2d, kUnicodes, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1091         if (length == 1) {
1092             renderer.doOneUnicode(this, nativeStrikePtr, unicodes[offset], x, y);
1093         } else {
1094             renderer.doUnicodes(this, nativeStrikePtr, unicodes, offset, length, x, y);
1095         }
1096     }
1097 
1098     // used by copyArea:
1099 
1100     Rectangle srcCopyAreaRect = new Rectangle();
1101     Rectangle dstCopyAreaRect = new Rectangle();
1102     Rectangle finalCopyAreaRect = new Rectangle();
1103     Rectangle copyAreaBounds = new Rectangle();
1104 
1105     void intersection(Rectangle r1, Rectangle r2, Rectangle r3) {
1106         // this code is taken from Rectangle.java (modified to put results in r3)
1107         int tx1 = r1.x;
1108         int ty1 = r1.y;
1109         long tx2 = tx1 + r1.width;
1110         long ty2 = ty1 + r1.height;
1111 
1112         int rx1 = r2.x;
1113         int ry1 = r2.y;
1114         long rx2 = rx1 + r2.width;
1115         long ry2 = ry1 + r2.height;
1116 
1117         if (tx1 < rx1) tx1 = rx1;
1118         if (ty1 < ry1) ty1 = ry1;
1119         if (tx2 > rx2) tx2 = rx2;
1120         if (ty2 > ry2) ty2 = ry2;
1121 
1122         tx2 -= tx1;
1123         ty2 -= ty1;
1124 
1125         // tx2,ty2 will never overflow (they will never be
1126         // larger than the smallest of the two source w,h)
1127         // they might underflow, though...
1128         if (tx2 < Integer.MIN_VALUE) tx2 = Integer.MIN_VALUE;
1129         if (ty2 < Integer.MIN_VALUE) ty2 = Integer.MIN_VALUE;
1130 
1131         r3.setBounds(tx1, ty1, (int) tx2, (int) ty2);
1132     }
1133 
1134     /**
1135      * Clips the copy area to the heavyweight bounds and returns the clipped rectangle.
1136      * The returned clipped rectangle is in the coordinate space of the surface.
1137      */
1138     protected Rectangle clipCopyArea(SunGraphics2D sg2d, int x, int y, int w, int h, int dx, int dy) {
1139         // we need to clip against the heavyweight bounds
1140         copyAreaBounds.setBounds(sg2d.devClip.getLoX(), sg2d.devClip.getLoY(), sg2d.devClip.getWidth(), sg2d.devClip.getHeight());
1141 
1142         // clip src rect
1143         srcCopyAreaRect.setBounds(x, y, w, h);
1144         intersection(srcCopyAreaRect, copyAreaBounds, srcCopyAreaRect);
1145         if ((srcCopyAreaRect.width <= 0) || (srcCopyAreaRect.height <= 0)) {
1146             // src rect outside bounds
1147             return null;
1148         }
1149 
1150         // clip dst rect
1151         dstCopyAreaRect.setBounds(srcCopyAreaRect.x + dx, srcCopyAreaRect.y + dy, srcCopyAreaRect.width, srcCopyAreaRect.height);
1152         intersection(dstCopyAreaRect, copyAreaBounds, dstCopyAreaRect);
1153         if ((dstCopyAreaRect.width <= 0) || (dstCopyAreaRect.height <= 0)) {
1154             // dst rect outside clip
1155             return null;
1156         }
1157 
1158         x = dstCopyAreaRect.x - dx;
1159         y = dstCopyAreaRect.y - dy;
1160         w = dstCopyAreaRect.width;
1161         h = dstCopyAreaRect.height;
1162 
1163         finalCopyAreaRect.setBounds(x, y, w, h);
1164 
1165         return finalCopyAreaRect;
1166     }
1167 
1168     // <rdar://3785539> We only need to mark dirty on screen surfaces. This method is
1169     // marked as protected and it is intended for subclasses to override if they need to
1170     // be notified when the surface is dirtied. See CPeerSurfaceData.markDirty() for implementation.
1171     // We don't do anything for buffered images.
1172     protected void markDirty(boolean markAsDirty) {
1173         // do nothing by default
1174     }
1175 
1176     // LazyDrawing optimization implementation:
1177 
1178     @Override
1179     public boolean canRenderLCDText(SunGraphics2D sg2d) {
1180         if (sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY &&
1181                 sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR &&
1182                 sg2d.clipState <= SunGraphics2D.CLIP_RECTANGULAR &&
1183                 // sg2d.surfaceData.getTransparency() == Transparency.OPAQUE &&
1184                 // This last test is a workaround until we fix loop selection
1185                 // in the pipe validation
1186                 sg2d.antialiasHint != SunHints.INTVAL_ANTIALIAS_ON) { return true; }
1187         return false; /* for now - in the future we may want to search */
1188     }
1189 
1190     public static boolean IsSimpleColor(Object c) {
1191         return ((c instanceof Color) || (c instanceof SystemColor) || (c instanceof javax.swing.plaf.ColorUIResource));
1192     }
1193 
1194     static {
1195         if ((kColorPointerIndex % 2) != 0) {
1196             System.err.println("kColorPointerIndex=" + kColorPointerIndex + " is NOT aligned for 64 bit");
1197             System.exit(0);
1198         }
1199     }
1200 }