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