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] = OSXOffScreenSurfaceData.createNewSurface(texturePaintImage);
 604 
 605 
 606                 this.fChangeFlag = (this.fChangeFlag | kColorChangedBit);
 607             } else {
 608                 this.fChangeFlag = (this.fChangeFlag & kColorNotChangedBit);
 609             }
 610         }
 611         lastPaint = sg2d.paint;
 612     }
 613 
 614     Composite lastComposite;
 615     int lastCompositeAlphaRule = 0;
 616     float lastCompositeAlphaValue = 0;
 617 
 618     void setupComposite(SunGraphics2D sg2d) {
 619         Composite composite = sg2d.composite;
 620 
 621         if (lastComposite != composite) {
 622             lastComposite = composite;
 623 
 624             // For composite state COMP_ISCOPY, COMP_XOR or COMP_CUSTOM set alpha compositor to COPY:
 625             int alphaRule = AlphaComposite.SRC_OVER;
 626             float alphaValue = 1.0f;
 627 
 628             // For composite state COMP_ISCOPY composite could be null. If it's not (or composite state == COMP_ALPHA)
 629             // get alpha compositor's values:
 630             if ((sg2d.compositeState <= SunGraphics2D.COMP_ALPHA) && (composite != null)) {
 631                 AlphaComposite alphaComposite = (AlphaComposite) composite;
 632                 alphaRule = alphaComposite.getRule();
 633                 alphaValue = alphaComposite.getAlpha();
 634             }
 635 
 636             // 2-17-03 VL: [Radar 3174922]
 637             // For COMP_XOR and COMP_CUSTOM compositing modes we should be setting alphaRule = AlphaComposite.SRC
 638             // which should map to kCGCompositeCopy.
 639 
 640             if ((lastCompositeAlphaRule != alphaRule) || (lastCompositeAlphaValue != alphaValue)) {
 641                 this.fGraphicsStatesInt.put(kCompositeRuleIndex, alphaRule);
 642                 this.fGraphicsStatesFloat.put(kCompositeValueIndex, alphaValue);
 643 
 644                 lastCompositeAlphaRule = alphaRule;
 645                 lastCompositeAlphaValue = alphaValue;
 646 
 647                 this.fChangeFlag = (this.fChangeFlag | kCompositeChangedBit);
 648             } else {
 649                 this.fChangeFlag = (this.fChangeFlag & kCompositeNotChangedBit);
 650             }
 651         } else {
 652             this.fChangeFlag = (this.fChangeFlag & kCompositeNotChangedBit);
 653         }
 654     }
 655 
 656     BasicStroke lastStroke = null;
 657     static BasicStroke defaultBasicStroke = new BasicStroke();
 658 
 659     void setupStroke(SunGraphics2D sg2d) {
 660         BasicStroke stroke = defaultBasicStroke;
 661 
 662         if (sg2d.stroke instanceof BasicStroke) {
 663             stroke = (BasicStroke) sg2d.stroke;
 664         }
 665 
 666         if (lastStroke != stroke) {
 667             this.fGraphicsStatesObject[kStrokeDashArrayIndex] = stroke.getDashArray();
 668             this.fGraphicsStatesFloat.put(kStrokeDashPhaseIndex, stroke.getDashPhase());
 669             this.fGraphicsStatesInt.put(kStrokeCapIndex, stroke.getEndCap());
 670             this.fGraphicsStatesInt.put(kStrokeJoinIndex, stroke.getLineJoin());
 671             this.fGraphicsStatesFloat.put(kStrokeWidthIndex, stroke.getLineWidth());
 672             this.fGraphicsStatesFloat.put(kStrokeLimitIndex, stroke.getMiterLimit());
 673 
 674             this.fChangeFlag = (this.fChangeFlag | kStrokeChangedBit);
 675 
 676             lastStroke = stroke;
 677         } else {
 678             this.fChangeFlag = (this.fChangeFlag & kStrokeNotChangedBit);
 679         }
 680     }
 681 
 682     Font lastFont;
 683 
 684     void setupFont(Font font, Paint paint) {
 685         if (font == null) { return; }
 686 
 687         // We have to setup the kFontPaintIndex if we have changed the color so we added the last
 688         // test to see if the color has changed - needed for complex strings
 689         // see Radar 3368674
 690         if ((font != lastFont) || ((this.fChangeFlag & kColorChangedBit) != 0)) {
 691             this.fGraphicsStatesObject[kFontIndex] = font;
 692             this.fGraphicsStatesObject[kFontPaintIndex] = paint;
 693 
 694             this.fChangeFlag = (this.fChangeFlag | kFontChangedBit);
 695 
 696             lastFont = font;
 697         } else {
 698             this.fChangeFlag = (this.fChangeFlag & kFontNotChangedBit);
 699         }
 700     }
 701 
 702     void setupRenderingHints(SunGraphics2D sg2d) {
 703         boolean hintsChanged = false;
 704 
 705         // Significant for draw, fill, text, and image ops:
 706         int antialiasHint = sg2d.antialiasHint;
 707         if (this.fGraphicsStatesInt.get(kHintsAntialiasIndex) != antialiasHint) {
 708             this.fGraphicsStatesInt.put(kHintsAntialiasIndex, antialiasHint);
 709             hintsChanged = true;
 710         }
 711 
 712         // Significant only for text ops:
 713         int textAntialiasHint = sg2d.textAntialiasHint;
 714         if (this.fGraphicsStatesInt.get(kHintsTextAntialiasIndex) != textAntialiasHint) {
 715             this.fGraphicsStatesInt.put(kHintsTextAntialiasIndex, textAntialiasHint);
 716             hintsChanged = true;
 717         }
 718 
 719         // Significant only for text ops:
 720         int fractionalMetricsHint = sg2d.fractionalMetricsHint;
 721         if (this.fGraphicsStatesInt.get(kHintsFractionalMetricsIndex) != fractionalMetricsHint) {
 722             this.fGraphicsStatesInt.put(kHintsFractionalMetricsIndex, fractionalMetricsHint);
 723             hintsChanged = true;
 724         }
 725 
 726         // Significant only for image ops:
 727         int renderHint = sg2d.renderHint;
 728         if (this.fGraphicsStatesInt.get(kHintsRenderingIndex) != renderHint) {
 729             this.fGraphicsStatesInt.put(kHintsRenderingIndex, renderHint);
 730             hintsChanged = true;
 731         }
 732 
 733         // Significant only for image ops:
 734         Object hintValue = sg2d.getRenderingHint(RenderingHints.KEY_INTERPOLATION);
 735         int interpolationHint = (hintValue != null ? ((SunHints.Value) hintValue).getIndex() : -1);
 736         if (this.fGraphicsStatesInt.get(kHintsInterpolationIndex) != interpolationHint) {
 737             this.fGraphicsStatesInt.put(kHintsInterpolationIndex, interpolationHint);
 738             hintsChanged = true;
 739         }
 740 
 741         if (hintsChanged) {
 742             this.fChangeFlag = (this.fChangeFlag | kHintsChangedBit);
 743         } else {
 744             this.fChangeFlag = (this.fChangeFlag & kHintsNotChangedBit);
 745         }
 746     }
 747 
 748     SunGraphics2D sg2dCurrent = null;
 749     Thread threadCurrent = null;
 750 
 751     void setupGraphicsState(SunGraphics2D sg2d, int primitiveType) {
 752         setupGraphicsState(sg2d, primitiveType, sg2d.font, 0, 0, fBounds.width, fBounds.height); // deviceBounds into userBounds
 753     }
 754 
 755     void setupGraphicsState(SunGraphics2D sg2d, int primitiveType, int x, int y, int w, int h) {
 756         setupGraphicsState(sg2d, primitiveType, sg2d.font, x, y, w, h);
 757     }
 758 
 759     // the method below is overriden by CPeerSurface to check the last peer used to draw
 760     // if the peer changed we finish lazy drawing
 761     void setupGraphicsState(SunGraphics2D sg2d, int primitiveType, Font font, int x, int y, int w, int h) {
 762         this.fChangeFlag = 0;
 763 
 764         setUserBounds(sg2d, x, y, w, h);
 765 
 766         Thread thread = Thread.currentThread();
 767         if ((this.sg2dCurrent != sg2d) || (this.threadCurrent != thread)) {
 768             this.sg2dCurrent = sg2d;
 769             this.threadCurrent = thread;
 770 
 771             setupClip(sg2d);
 772             setupTransform(sg2d);
 773             setupPaint(sg2d, x, y, w, h);
 774             setupComposite(sg2d);
 775             setupStroke(sg2d);
 776             setupFont(font, sg2d.paint);
 777             setupRenderingHints(sg2d);
 778 
 779             this.fChangeFlag = kEverythingChangedFlag;
 780         } else {
 781             int rendererType = getRendererTypeForPrimitive(primitiveType);
 782 
 783             setupClip(sg2d);
 784             setupTransform(sg2d);
 785 
 786             if (rendererType != kCopyArea) {
 787                 setupComposite(sg2d);
 788                 setupRenderingHints(sg2d);
 789 
 790                 if ((rendererType != kImage)) {
 791                     setupPaint(sg2d, x, y, w, h);
 792                     setupStroke(sg2d);
 793                 }
 794                 if (rendererType != kPrimitive) {
 795                     setupFont(font, sg2d.paint);
 796                 }
 797 
 798             }
 799         }
 800 
 801         this.fGraphicsStatesInt.put(kChangeFlagIndex, this.fChangeFlag);
 802     }
 803 
 804     boolean isCustomPaint(SunGraphics2D sg2d) {
 805         if ((sg2d.paint instanceof Color) || (sg2d.paint instanceof SystemColor) || (sg2d.paint instanceof GradientPaint) || (sg2d.paint instanceof TexturePaint)) { return false; }
 806 
 807         return true;
 808     }
 809 
 810     final float[] segmentCoordinatesArray = new float[6];
 811 
 812     int getPathLength(GeneralPath gp) {
 813         int length = 0;
 814 
 815         PathIterator pi = gp.getPathIterator(null);
 816         while (pi.isDone() == false) {
 817             pi.next();
 818             length++;
 819         }
 820 
 821         return length;
 822     }
 823 
 824     int getPathCoordinates(GeneralPath gp, FloatBuffer coordinates, IntBuffer types) {
 825         // System.err.println("getPathCoordinates");
 826         boolean skip = false;
 827 
 828         coordinates.clear();
 829         types.clear();
 830 
 831         int type;
 832 
 833         PathIterator pi = gp.getPathIterator(null);
 834         while (pi.isDone() == false) {
 835             skip = false;
 836             type = pi.currentSegment(segmentCoordinatesArray);
 837 
 838             switch (type) {
 839                 case PathIterator.SEG_MOVETO:
 840                     // System.err.println(" SEG_MOVETO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+")");
 841                     if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND &&
 842                             segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND) {
 843                         coordinates.put(segmentCoordinatesArray[0]);
 844                         coordinates.put(segmentCoordinatesArray[1]);
 845                     } else {
 846                         skip = true;
 847                     }
 848                     break;
 849                 case PathIterator.SEG_LINETO:
 850                     // System.err.println(" SEG_LINETO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+")");
 851                     if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND &&
 852                             segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND) {
 853                         coordinates.put(segmentCoordinatesArray[0]);
 854                         coordinates.put(segmentCoordinatesArray[1]);
 855                     } else {
 856                         skip = true;
 857                     }
 858                     break;
 859                 case PathIterator.SEG_QUADTO:
 860                     // System.err.println(" SEG_QUADTO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+"), ("+segmentCoordinatesArray[2]+", "+segmentCoordinatesArray[3]+")");
 861                     if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND &&
 862                             segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND &&
 863                             segmentCoordinatesArray[2] < UPPER_BND && segmentCoordinatesArray[2] > LOWER_BND &&
 864                             segmentCoordinatesArray[3] < UPPER_BND && segmentCoordinatesArray[3] > LOWER_BND) {
 865                         coordinates.put(segmentCoordinatesArray[0]);
 866                         coordinates.put(segmentCoordinatesArray[1]);
 867                         coordinates.put(segmentCoordinatesArray[2]);
 868                         coordinates.put(segmentCoordinatesArray[3]);
 869                     } else {
 870                         skip = true;
 871                     }
 872                     break;
 873                 case PathIterator.SEG_CUBICTO:
 874                     // System.err.println(" SEG_QUADTO ("+segmentCoordinatesArray[0]+", "+segmentCoordinatesArray[1]+"), ("+segmentCoordinatesArray[2]+", "+segmentCoordinatesArray[3]+"), ("+segmentCoordinatesArray[4]+", "+segmentCoordinatesArray[5]+")");
 875                     if (segmentCoordinatesArray[0] < UPPER_BND && segmentCoordinatesArray[0] > LOWER_BND &&
 876                             segmentCoordinatesArray[1] < UPPER_BND && segmentCoordinatesArray[1] > LOWER_BND &&
 877                             segmentCoordinatesArray[2] < UPPER_BND && segmentCoordinatesArray[2] > LOWER_BND &&
 878                             segmentCoordinatesArray[3] < UPPER_BND && segmentCoordinatesArray[3] > LOWER_BND &&
 879                             segmentCoordinatesArray[4] < UPPER_BND && segmentCoordinatesArray[4] > LOWER_BND &&
 880                             segmentCoordinatesArray[5] < UPPER_BND && segmentCoordinatesArray[5] > LOWER_BND) {
 881                         coordinates.put(segmentCoordinatesArray[0]);
 882                         coordinates.put(segmentCoordinatesArray[1]);
 883                         coordinates.put(segmentCoordinatesArray[2]);
 884                         coordinates.put(segmentCoordinatesArray[3]);
 885                         coordinates.put(segmentCoordinatesArray[4]);
 886                         coordinates.put(segmentCoordinatesArray[5]);
 887                     } else {
 888                         skip = true;
 889                     }
 890                     break;
 891                 case PathIterator.SEG_CLOSE:
 892                     // System.err.println(" SEG_CLOSE");
 893                     break;
 894             }
 895 
 896             if (!skip) {
 897                 types.put(type);
 898             }
 899 
 900             pi.next();
 901         }
 902 
 903         return pi.getWindingRule();
 904     }
 905 
 906     public void doLine(CRenderer renderer, SunGraphics2D sg2d, float x1, float y1, float x2, float y2) {
 907         // System.err.println("-- doLine x1="+x1+" y1="+y1+" x2="+x2+" y2="+y2+" paint="+sg2d.paint);
 908         setupGraphicsState(sg2d, kLine, sg2d.font, 0, 0, fBounds.width, fBounds.height);
 909         renderer.doLine(this, x1, y1, x2, y2);
 910     }
 911 
 912     public void doRect(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, boolean isfill) {
 913         // System.err.println("-- doRect x="+x+" y="+y+" w="+width+" h="+height+" isfill="+isfill+" paint="+sg2d.paint);
 914         if ((isfill) && (isCustomPaint(sg2d))) {
 915             setupGraphicsState(sg2d, kRect, (int) x, (int) y, (int) width, (int) height);
 916         } else {
 917             setupGraphicsState(sg2d, kRect, sg2d.font, 0, 0, fBounds.width, fBounds.height);
 918         }
 919         renderer.doRect(this, x, y, width, height, isfill);
 920     }
 921 
 922     public void doRoundRect(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, float arcW, float arcH, boolean isfill) {
 923         // System.err.println("--- doRoundRect");
 924         if ((isfill) && (isCustomPaint(sg2d))) {
 925             setupGraphicsState(sg2d, kRoundRect, (int) x, (int) y, (int) width, (int) height);
 926         } else {
 927             setupGraphicsState(sg2d, kRoundRect, sg2d.font, 0, 0, fBounds.width, fBounds.height);
 928         }
 929         renderer.doRoundRect(this, x, y, width, height, arcW, arcH, isfill);
 930     }
 931 
 932     public void doOval(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, boolean isfill) {
 933         // System.err.println("--- doOval");
 934         if ((isfill) && (isCustomPaint(sg2d))) {
 935             setupGraphicsState(sg2d, kOval, (int) x, (int) y, (int) width, (int) height);
 936         } else {
 937             setupGraphicsState(sg2d, kOval, sg2d.font, 0, 0, fBounds.width, fBounds.height);
 938         }
 939         renderer.doOval(this, x, y, width, height, isfill);
 940     }
 941 
 942     public void doArc(CRenderer renderer, SunGraphics2D sg2d, float x, float y, float width, float height, float startAngle, float arcAngle, int type, boolean isfill) {
 943         // System.err.println("--- doArc");
 944         if ((isfill) && (isCustomPaint(sg2d))) {
 945             setupGraphicsState(sg2d, kArc, (int) x, (int) y, (int) width, (int) height);
 946         } else {
 947             setupGraphicsState(sg2d, kArc, sg2d.font, 0, 0, fBounds.width, fBounds.height);
 948         }
 949 
 950         renderer.doArc(this, x, y, width, height, startAngle, arcAngle, type, isfill);
 951     }
 952 
 953     public void doPolygon(CRenderer renderer, SunGraphics2D sg2d, int xpoints[], int ypoints[], int npoints, boolean ispolygon, boolean isfill) {
 954         // System.err.println("--- doPolygon");
 955 
 956         if ((isfill) && (isCustomPaint(sg2d))) {
 957             int minx = xpoints[0];
 958             int miny = ypoints[0];
 959             int maxx = minx;
 960             int maxy = miny;
 961             for (int i = 1; i < npoints; i++) {
 962                 int x = xpoints[i];
 963                 if (x < minx) {
 964                     minx = x;
 965                 } else if (x > maxx) {
 966                     maxx = x;
 967                 }
 968 
 969                 int y = ypoints[i];
 970                 if (y < miny) {
 971                     miny = y;
 972                 } else if (y > maxy) {
 973                     maxy = y;
 974                 }
 975             }
 976             setupGraphicsState(sg2d, kPolygon, minx, miny, maxx - minx, maxy - miny);
 977         } else {
 978             setupGraphicsState(sg2d, kPolygon, sg2d.font, 0, 0, fBounds.width, fBounds.height);
 979         }
 980         renderer.doPoly(this, xpoints, ypoints, npoints, ispolygon, isfill);
 981     }
 982 
 983     FloatBuffer shapeCoordinatesArray = null;
 984     IntBuffer shapeTypesArray = null;
 985 
 986     public void drawfillShape(CRenderer renderer, SunGraphics2D sg2d, GeneralPath gp, boolean isfill, boolean shouldApplyOffset) {
 987         // System.err.println("--- drawfillShape");
 988 
 989         if ((isfill) && (isCustomPaint(sg2d))) {
 990             Rectangle bounds = gp.getBounds();
 991             setupGraphicsState(sg2d, kShape, bounds.x, bounds.y, bounds.width, bounds.height);
 992         } else {
 993             setupGraphicsState(sg2d, kShape, sg2d.font, 0, 0, fBounds.width, fBounds.height);
 994         }
 995 
 996         int shapeLength = getPathLength(gp);
 997 
 998         if ((shapeCoordinatesArray == null) || (shapeCoordinatesArray.capacity() < (shapeLength * 6))) {
 999             shapeCoordinatesArray = getBufferOfSize(shapeLength * 6).asFloatBuffer(); // segment can have a max of 6
1000                                                                                       // coordinates
1001         }
1002         if ((shapeTypesArray == null) || (shapeTypesArray.capacity() < shapeLength)) {
1003             shapeTypesArray = getBufferOfSize(shapeLength).asIntBuffer();
1004         }
1005 
1006         int windingRule = getPathCoordinates(gp, shapeCoordinatesArray, shapeTypesArray);
1007 
1008         renderer.doShape(this, shapeLength, shapeCoordinatesArray, shapeTypesArray, windingRule, isfill, shouldApplyOffset);
1009     }
1010 
1011     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) {
1012         // System.err.println("--- blitImage sx="+sx+", sy="+sy+", sw="+sw+", sh="+sh+", img="+img);
1013         OSXOffScreenSurfaceData osxsd = (OSXOffScreenSurfaceData) img;
1014         synchronized (osxsd.getLockObject()) {
1015             int w = osxsd.bim.getWidth();
1016             int h = osxsd.bim.getHeight();
1017 
1018             // the image itself can have outstanding graphics primitives that might need to be flushed
1019             setupGraphicsState(sg2d, kImage, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1020 
1021             // 04/06/04 cmc: radr://3612381 Graphics.drawImage ignores bgcolor parameter
1022             if (bgColor != null) {
1023                 img = osxsd.getCopyWithBgColor(bgColor);
1024             }
1025 
1026             renderer.doImage(this, img, fliph, flipv, w, h, sx, sy, sw, sh, dx, dy, dw, dh);
1027         }
1028     }
1029 
1030     public interface CGContextDrawable {
1031         public void drawIntoCGContext(final long cgContext);
1032     }
1033 
1034     public void drawString(CTextPipe renderer, SunGraphics2D sg2d, long nativeStrikePtr, String str, double x, double y) {
1035         // System.err.println("--- drawString str=\""+str+"\"");
1036         // see <rdar://problem/3825795>. We don't want to call anything if the string is empty!
1037         if (str.length() == 0) { return; }
1038 
1039         setupGraphicsState(sg2d, kString, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1040         renderer.doDrawString(this, nativeStrikePtr, str, x, y);
1041     }
1042 
1043     public void drawGlyphs(CTextPipe renderer, SunGraphics2D sg2d, long nativeStrikePtr, GlyphVector gv, float x, float y) {
1044         // System.err.println("--- drawGlyphs");
1045         setupGraphicsState(sg2d, kGlyphs, gv.getFont(), 0, 0, fBounds.width, fBounds.height);
1046         renderer.doDrawGlyphs(this, nativeStrikePtr, gv, x, y);
1047     }
1048 
1049     public void drawUnicodes(CTextPipe renderer, SunGraphics2D sg2d, long nativeStrikePtr, char unicodes[], int offset, int length, float x, float y) {
1050         // System.err.println("--- drawUnicodes "+(new String(unicodes, offset, length)));
1051         setupGraphicsState(sg2d, kUnicodes, sg2d.font, 0, 0, fBounds.width, fBounds.height);
1052         if (length == 1) {
1053             renderer.doOneUnicode(this, nativeStrikePtr, unicodes[offset], x, y);
1054         } else {
1055             renderer.doUnicodes(this, nativeStrikePtr, unicodes, offset, length, x, y);
1056         }
1057     }
1058 
1059     // used by copyArea:
1060 
1061     Rectangle srcCopyAreaRect = new Rectangle();
1062     Rectangle dstCopyAreaRect = new Rectangle();
1063     Rectangle finalCopyAreaRect = new Rectangle();
1064     Rectangle copyAreaBounds = new Rectangle();
1065 
1066     void intersection(Rectangle r1, Rectangle r2, Rectangle r3) {
1067         // this code is taken from Rectangle.java (modified to put results in r3)
1068         int tx1 = r1.x;
1069         int ty1 = r1.y;
1070         long tx2 = tx1 + r1.width;
1071         long ty2 = ty1 + r1.height;
1072 
1073         int rx1 = r2.x;
1074         int ry1 = r2.y;
1075         long rx2 = rx1 + r2.width;
1076         long ry2 = ry1 + r2.height;
1077 
1078         if (tx1 < rx1) tx1 = rx1;
1079         if (ty1 < ry1) ty1 = ry1;
1080         if (tx2 > rx2) tx2 = rx2;
1081         if (ty2 > ry2) ty2 = ry2;
1082 
1083         tx2 -= tx1;
1084         ty2 -= ty1;
1085 
1086         // tx2,ty2 will never overflow (they will never be
1087         // larger than the smallest of the two source w,h)
1088         // they might underflow, though...
1089         if (tx2 < Integer.MIN_VALUE) tx2 = Integer.MIN_VALUE;
1090         if (ty2 < Integer.MIN_VALUE) ty2 = Integer.MIN_VALUE;
1091 
1092         r3.setBounds(tx1, ty1, (int) tx2, (int) ty2);
1093     }
1094 
1095     /**
1096      * Clips the copy area to the heavyweight bounds and returns the clipped rectangle.
1097      * The returned clipped rectangle is in the coordinate space of the surface.
1098      */
1099     protected Rectangle clipCopyArea(SunGraphics2D sg2d, int x, int y, int w, int h, int dx, int dy) {
1100         // we need to clip against the heavyweight bounds
1101         copyAreaBounds.setBounds(sg2d.devClip.getLoX(), sg2d.devClip.getLoY(), sg2d.devClip.getWidth(), sg2d.devClip.getHeight());
1102 
1103         // clip src rect
1104         srcCopyAreaRect.setBounds(x, y, w, h);
1105         intersection(srcCopyAreaRect, copyAreaBounds, srcCopyAreaRect);
1106         if ((srcCopyAreaRect.width <= 0) || (srcCopyAreaRect.height <= 0)) {
1107             // src rect outside bounds
1108             return null;
1109         }
1110 
1111         // clip dst rect
1112         dstCopyAreaRect.setBounds(srcCopyAreaRect.x + dx, srcCopyAreaRect.y + dy, srcCopyAreaRect.width, srcCopyAreaRect.height);
1113         intersection(dstCopyAreaRect, copyAreaBounds, dstCopyAreaRect);
1114         if ((dstCopyAreaRect.width <= 0) || (dstCopyAreaRect.height <= 0)) {
1115             // dst rect outside clip
1116             return null;
1117         }
1118 
1119         x = dstCopyAreaRect.x - dx;
1120         y = dstCopyAreaRect.y - dy;
1121         w = dstCopyAreaRect.width;
1122         h = dstCopyAreaRect.height;
1123 
1124         finalCopyAreaRect.setBounds(x, y, w, h);
1125 
1126         return finalCopyAreaRect;
1127     }
1128 
1129     // <rdar://3785539> We only need to mark dirty on screen surfaces. This method is
1130     // marked as protected and it is intended for subclasses to override if they need to
1131     // be notified when the surface is dirtied. See CPeerSurfaceData.markDirty() for implementation.
1132     // We don't do anything for buffered images.
1133     protected void markDirty(boolean markAsDirty) {
1134         // do nothing by default
1135     }
1136 
1137     // LazyDrawing optimization implementation:
1138 
1139     @Override
1140     public boolean canRenderLCDText(SunGraphics2D sg2d) {
1141         if (sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY &&
1142                 sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR &&
1143                 sg2d.clipState <= SunGraphics2D.CLIP_RECTANGULAR &&
1144                 // sg2d.surfaceData.getTransparency() == Transparency.OPAQUE &&
1145                 // This last test is a workaround until we fix loop selection
1146                 // in the pipe validation
1147                 sg2d.antialiasHint != SunHints.INTVAL_ANTIALIAS_ON) { return true; }
1148         return false; /* for now - in the future we may want to search */
1149     }
1150 
1151     public static boolean IsSimpleColor(Object c) {
1152         return ((c instanceof Color) || (c instanceof SystemColor) || (c instanceof javax.swing.plaf.ColorUIResource));
1153     }
1154 
1155     static {
1156         if ((kColorPointerIndex % 2) != 0) {
1157             System.err.println("kColorPointerIndex=" + kColorPointerIndex + " is NOT aligned for 64 bit");
1158             System.exit(0);
1159         }
1160     }
1161 }