1 /*
   2  * Copyright (c) 2011, 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 #import "QuartzSurfaceData.h"
  27 
  28 #import "java_awt_BasicStroke.h"
  29 #import "java_awt_AlphaComposite.h"
  30 #import "java_awt_geom_PathIterator.h"
  31 #import "java_awt_image_BufferedImage.h"
  32 #import "sun_awt_SunHints.h"
  33 #import "sun_java2d_CRenderer.h"
  34 #import "sun_java2d_OSXSurfaceData.h"
  35 #import "sun_lwawt_macosx_CPrinterSurfaceData.h"
  36 #import "ImageSurfaceData.h"
  37 
  38 #import <JavaNativeFoundation/JavaNativeFoundation.h>
  39 
  40 #import <AppKit/AppKit.h>
  41 #import "ThreadUtilities.h"
  42 
  43 // private Quartz routines needed here
  44 CG_EXTERN void CGContextSetCTM(CGContextRef ref, CGAffineTransform tx);
  45 
  46 //#define DEBUG
  47 #if defined DEBUG
  48     #define PRINT(msg) {fprintf(stderr, "%s\n", msg);}
  49 #else
  50     #define PRINT(msg) {}
  51 #endif
  52 
  53 // from CGAffineTransformPrivate.h
  54 extern CGPoint CGPointApplyInverseAffineTransform(CGPoint point, CGAffineTransform t);
  55 
  56 #define kOffset (0.5f)
  57 
  58 BOOL gAdjustForJavaDrawing;
  59 
  60 #pragma mark
  61 #pragma mark --- Color Cache ---
  62 
  63 // Creating and deleting CGColorRefs can be expensive, therefore we have a color cache.
  64 // The color cache was first introduced with <rdar://problem/3923927>
  65 // With <rdar://problem/4280514>, the hashing function was improved
  66 // With <rdar://problem/4012223>, the color cache became global (per process) instead of per surface.
  67 
  68 // Must be power of 2. 1024 is the least power of 2 number that makes SwingSet2 run without any non-empty cache misses
  69 #define gColorCacheSize 1024
  70 struct _ColorCacheInfo
  71 {
  72     UInt32        keys[gColorCacheSize];
  73     CGColorRef    values[gColorCacheSize];
  74 };
  75 static struct _ColorCacheInfo colorCacheInfo;
  76 
  77 static pthread_mutex_t gColorCacheLock = PTHREAD_MUTEX_INITIALIZER;
  78 
  79 // given a UInt32 color, it tries to find that find the corresponding CGColorRef in the hash cache. If the CGColorRef
  80 // doesn't exist or there is a collision, it creates a new one CGColorRef and put's in the cache. Then,
  81 // it sets with current fill/stroke color for the the CGContext passed in (qsdo->cgRef).
  82 void setCachedColor(QuartzSDOps *qsdo, UInt32 color)
  83 {
  84     static const CGFloat kColorConversionMultiplier = 1.0f/255.0f;
  85 
  86     pthread_mutex_lock(&gColorCacheLock);
  87 
  88     static CGColorSpaceRef colorspace = NULL;
  89     if (colorspace == NULL)
  90     {
  91         colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
  92     }
  93 
  94     CGColorRef cgColor = NULL;
  95 
  96     // The colors passed have low randomness. That means we need to scramble the bits of the color
  97     // to produce a good hash key. After some analysis, it looks like Thomas's Wang integer hasing algorithm
  98     // seems a nice trade off between performance and effectivness.
  99     UInt32 index = color;
 100     index += ~(index << 15);
 101     index ^=  (index >> 10);
 102     index +=  (index << 3);
 103     index ^=  (index >> 6);
 104     index += ~(index << 11);
 105     index ^=  (index >> 16);
 106     index = index & (gColorCacheSize - 1);   // The bits are scrambled, we just need to make sure it fits inside our table
 107 
 108     UInt32 key = colorCacheInfo.keys[index];
 109     CGColorRef value = colorCacheInfo.values[index];
 110     if ((key == color) && (value != NULL))
 111     {
 112         //fprintf(stderr, "+");fflush(stderr);//hit
 113         cgColor = value;
 114     }
 115     else
 116     {
 117         if (value != NULL)
 118         {
 119             //fprintf(stderr, "!");fflush(stderr);//miss and replace - double ouch
 120             CGColorRelease(value);
 121         }
 122         //fprintf(stderr, "-");fflush(stderr);// miss
 123 
 124         CGFloat alpha = ((color>>24)&0xff)*kColorConversionMultiplier;
 125         CGFloat red = ((color>>16)&0xff)*kColorConversionMultiplier;
 126         CGFloat green = ((color>>8)&0xff)*kColorConversionMultiplier;
 127         CGFloat blue = ((color>>0)&0xff)*kColorConversionMultiplier;
 128         const CGFloat components[] = {red, green, blue, alpha, 1.0f};
 129         value = CGColorCreate(colorspace, components);
 130 
 131         colorCacheInfo.keys[index] = color;
 132         colorCacheInfo.values[index] = value;
 133 
 134         cgColor = value;
 135     }
 136 
 137     CGContextSetStrokeColorWithColor(qsdo->cgRef, cgColor);
 138     CGContextSetFillColorWithColor(qsdo->cgRef, cgColor);
 139 
 140     pthread_mutex_unlock(&gColorCacheLock);
 141 }
 142 
 143 #pragma mark
 144 #pragma mark --- Gradient ---
 145 
 146 // this function MUST NOT be inlined!
 147 void gradientLinearPaintEvaluateFunction(void *info, const CGFloat *in, CGFloat *out)
 148 {
 149     StateShadingInfo *shadingInfo = (StateShadingInfo *)info;
 150     CGFloat *colors = shadingInfo->colors;
 151     CGFloat range = *in;
 152     CGFloat c1, c2;
 153     jint k;
 154 
 155 //fprintf(stderr, "range=%f\n", range);
 156     for (k=0; k<4; k++)
 157     {
 158         c1 = colors[k];
 159 //fprintf(stderr, "    c1=%f", c1);
 160         c2 = colors[k+4];
 161 //fprintf(stderr, ", c2=%f", c2);
 162         if (c1 == c2)
 163         {
 164             *out++ = c2;
 165 //fprintf(stderr, ", %f", *(out-1));
 166         }
 167         else if (c1 > c2)
 168         {
 169             *out++ = c1 - ((c1-c2)*range);
 170 //fprintf(stderr, ", %f", *(out-1));
 171         }
 172         else// if (c1 < c2)
 173         {
 174             *out++ = c1 + ((c2-c1)*range);
 175 //fprintf(stderr, ", %f", *(out-1));
 176         }
 177 //fprintf(stderr, "\n");
 178     }
 179 }
 180 
 181 // this function MUST NOT be inlined!
 182 void gradientCyclicPaintEvaluateFunction(void *info, const CGFloat *in, CGFloat *out)
 183 {
 184     StateShadingInfo *shadingInfo = (StateShadingInfo *)info;
 185     CGFloat length = shadingInfo->length ;
 186     CGFloat period = shadingInfo->period;
 187     CGFloat offset = shadingInfo->offset;
 188     CGFloat periodLeft = offset;
 189     CGFloat periodRight = periodLeft+period;
 190     CGFloat *colors = shadingInfo->colors;
 191     CGFloat range = *in;
 192     CGFloat c1, c2;
 193     jint k;
 194     jint count = 0;
 195 
 196     range *= length;
 197 
 198     // put the range within the period
 199     if (range < periodLeft)
 200     {
 201         while (range < periodLeft)
 202         {
 203             range += period;
 204             count++;
 205         }
 206 
 207         range = range-periodLeft;
 208     }
 209     else if (range > periodRight)
 210     {
 211         count = 1;
 212 
 213         while (range > periodRight)
 214         {
 215             range -= period;
 216             count++;
 217         }
 218 
 219         range = periodRight-range;
 220     }
 221     else
 222     {
 223         range = range - offset;
 224     }
 225     range = range/period;
 226 
 227     // cycle up or down
 228     if (count%2 == 0)
 229     {
 230         for (k=0; k<4; k++)
 231         {
 232             c1 = colors[k];
 233             c2 = colors[k+4];
 234             if (c1 == c2)
 235             {
 236                 *out++ = c2;
 237             }
 238             else if (c1 > c2)
 239             {
 240                 *out++ = c1 - ((c1-c2)*range);
 241             }
 242             else// if (c1 < c2)
 243             {
 244                 *out++ = c1 + ((c2-c1)*range);
 245             }
 246         }
 247     }
 248     else
 249     {
 250         for (k=0; k<4; k++)
 251         {
 252             c1 = colors[k+4];
 253             c2 = colors[k];
 254             if (c1 == c2)
 255             {
 256                 *out++ = c2;
 257             }
 258             else if (c1 > c2)
 259             {
 260                 *out++ = c1 - ((c1-c2)*range);
 261             }
 262             else// if (c1 < c2)
 263             {
 264                 *out++ = c1 + ((c2-c1)*range);
 265             }
 266         }
 267     }
 268  }
 269 
 270 // this function MUST NOT be inlined!
 271 void gradientPaintReleaseFunction(void *info)
 272 {
 273 PRINT("    gradientPaintReleaseFunction")
 274     free(info);
 275 }
 276 
 277 static inline void contextGradientPath(QuartzSDOps* qsdo)
 278 {
 279 PRINT("    ContextGradientPath")
 280     CGContextRef cgRef = qsdo->cgRef;
 281     StateShadingInfo* shadingInfo = qsdo->shadingInfo;
 282 
 283     CGRect bounds = CGContextGetClipBoundingBox(cgRef);
 284 
 285     static const CGFloat domain[2] = {0.0f, 1.0f};
 286     static const CGFloat range[8] = {0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f};
 287     CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
 288     CGFunctionRef shadingFunc = NULL;
 289     CGShadingRef shading = NULL;
 290     if (shadingInfo->cyclic == NO)
 291     {
 292         static const CGFunctionCallbacks callbacks = {0, &gradientLinearPaintEvaluateFunction, &gradientPaintReleaseFunction};
 293         shadingFunc = CGFunctionCreate((void *)shadingInfo, 1, domain, 4, range, &callbacks);
 294         shading = CGShadingCreateAxial(colorspace, shadingInfo->start, shadingInfo->end, shadingFunc, 1, 1);
 295     }
 296     else
 297     {
 298 //fprintf(stderr, "BOUNDING BOX x1=%f, y1=%f x2=%f, y2=%f\n", bounds.origin.x, bounds.origin.y, bounds.origin.x+bounds.size.width, bounds.origin.y+bounds.size.height);
 299         // need to extend the line start-end
 300 
 301         CGFloat x1 = shadingInfo->start.x;
 302         CGFloat y1 = shadingInfo->start.y;
 303         CGFloat x2 = shadingInfo->end.x;
 304         CGFloat y2 = shadingInfo->end.y;
 305 //fprintf(stderr, "GIVEN x1=%f, y1=%f      x2=%f, y2=%f\n", x1, y1, x2, y2);
 306 
 307         if (x1 == x2)
 308         {
 309             y1 = bounds.origin.y;
 310             y2 = y1 + bounds.size.height;
 311         }
 312         else if (y1 == y2)
 313         {
 314             x1 = bounds.origin.x;
 315             x2 = x1 + bounds.size.width;
 316         }
 317         else
 318         {
 319             // find the original line function y = mx + c
 320             CGFloat m1 = (y2-y1)/(x2-x1);
 321             CGFloat c1 = y1 - m1*x1;
 322 //fprintf(stderr, "         m1=%f, c1=%f\n", m1, c1);
 323 
 324             // a line perpendicular to the original one will have the slope
 325             CGFloat m2 = -(1/m1);
 326 //fprintf(stderr, "         m2=%f\n", m2);
 327 
 328             // find the only 2 possible lines perpendicular to the original line, passing the two top corners of the bounding box
 329             CGFloat x1A = bounds.origin.x;
 330             CGFloat y1A = bounds.origin.y;
 331             CGFloat c1A = y1A - m2*x1A;
 332 //fprintf(stderr, "         x1A=%f, y1A=%f, c1A=%f\n", x1A, y1A, c1A);
 333             CGFloat x1B = bounds.origin.x+bounds.size.width;
 334             CGFloat y1B = bounds.origin.y;
 335             CGFloat c1B = y1B - m2*x1B;
 336 //fprintf(stderr, "         x1B=%f, y1B=%f, c1B=%f\n", x1B, y1B, c1B);
 337 
 338             // find the crossing points of the original line and the two lines we computed above to find the new possible starting points
 339             CGFloat x1Anew = (c1A-c1)/(m1-m2);
 340             CGFloat y1Anew = m2*x1Anew + c1A;
 341             CGFloat x1Bnew = (c1B-c1)/(m1-m2);
 342             CGFloat y1Bnew = m2*x1Bnew + c1B;
 343 //fprintf(stderr, "NEW x1Anew=%f, y1Anew=%f      x1Bnew=%f, y1Bnew=%f\n", x1Anew, y1Anew, x1Bnew, y1Bnew);
 344 
 345             // select the new starting point
 346             if (y1Anew <= y1Bnew)
 347             {
 348                 x1 = x1Anew;
 349                 y1 = y1Anew;
 350             }
 351             else
 352             {
 353                 x1 = x1Bnew;
 354                 y1 = y1Bnew;
 355             }
 356 //fprintf(stderr, "--- NEW x1=%f, y1=%f\n", x1, y1);
 357 
 358             // find the only 2 possible lines perpendicular to the original line, passing the two bottom corners of the bounding box
 359             CGFloat x2A = bounds.origin.x;
 360             CGFloat y2A = bounds.origin.y+bounds.size.height;
 361             CGFloat c2A = y2A - m2*x2A;
 362 //fprintf(stderr, "         x2A=%f, y2A=%f, c2A=%f\n", x2A, y2A, c2A);
 363             CGFloat x2B = bounds.origin.x+bounds.size.width;
 364             CGFloat y2B = bounds.origin.y+bounds.size.height;
 365             CGFloat c2B = y2B - m2*x2B;
 366 //fprintf(stderr, "         x2B=%f, y2B=%f, c2B=%f\n", x2B, y2B, c2B);
 367 
 368             // find the crossing points of the original line and the two lines we computed above to find the new possible ending points
 369             CGFloat x2Anew = (c2A-c1)/(m1-m2);
 370             CGFloat y2Anew = m2*x2Anew + c2A;
 371             CGFloat x2Bnew = (c2B-c1)/(m1-m2);
 372             CGFloat y2Bnew = m2*x2Bnew + c2B;
 373 //fprintf(stderr, "NEW x2Anew=%f, y2Anew=%f      x2Bnew=%f, y2Bnew=%f\n", x2Anew, y2Anew, x2Bnew, y2Bnew);
 374 
 375             // select the new ending point
 376             if (y2Anew >= y2Bnew)
 377             {
 378                 x2 = x2Anew;
 379                 y2 = y2Anew;
 380             }
 381             else
 382             {
 383                 x2 = x2Bnew;
 384                 y2 = y2Bnew;
 385             }
 386 //fprintf(stderr, "--- NEW x2=%f, y2=%f\n", x2, y2);
 387         }
 388 
 389         qsdo->shadingInfo->period = sqrt(pow(shadingInfo->end.x-shadingInfo->start.x, 2.0) + pow(shadingInfo->end.y-shadingInfo->start.y, 2.0));
 390         if ((qsdo->shadingInfo->period != 0))
 391         {
 392             // compute segment lengths that we will need for the gradient function
 393             qsdo->shadingInfo->length = sqrt(pow(x2-x1, 2.0) + pow(y2-y1, 2.0));
 394             qsdo->shadingInfo->offset = sqrt(pow(shadingInfo->start.x-x1, 2.0) + pow(shadingInfo->start.y-y1, 2.0));
 395 //fprintf(stderr, "length=%f, period=%f, offset=%f\n", qsdo->shadingInfo->length, qsdo->shadingInfo->period, qsdo->shadingInfo->offset);
 396 
 397             CGPoint newStart = {x1, y1};
 398             CGPoint newEnd = {x2, y2};
 399 
 400             static const CGFunctionCallbacks callbacks = {0, &gradientCyclicPaintEvaluateFunction, &gradientPaintReleaseFunction};
 401             shadingFunc = CGFunctionCreate((void *)shadingInfo, 1, domain, 4, range, &callbacks);
 402             shading = CGShadingCreateAxial(colorspace, newStart, newEnd, shadingFunc, 0, 0);
 403         }
 404     }
 405     CGColorSpaceRelease(colorspace);
 406 
 407     if (shadingFunc != NULL)
 408     {
 409         CGContextSaveGState(cgRef);
 410 
 411         // rdar://problem/5214320
 412         // Gradient fills of Java GeneralPath don't respect the even odd winding rule (quartz pipeline).
 413         if (qsdo->isEvenOddFill) {
 414             CGContextEOClip(cgRef);
 415         } else {
 416             CGContextClip(cgRef);
 417         }
 418         CGContextDrawShading(cgRef, shading);
 419 
 420         CGContextRestoreGState(cgRef);
 421         CGShadingRelease(shading);
 422         CGFunctionRelease(shadingFunc);
 423         qsdo->shadingInfo = NULL;
 424     }
 425 }
 426 
 427 #pragma mark
 428 #pragma mark --- Texture ---
 429 
 430 // this function MUST NOT be inlined!
 431 void texturePaintEvaluateFunction(void *info, CGContextRef cgRef)
 432 {
 433     JNIEnv* env = [ThreadUtilities getJNIEnvUncached];
 434 
 435     StatePatternInfo* patternInfo = (StatePatternInfo*)info;
 436     ImageSDOps* isdo = LockImage(env, patternInfo->sdata);
 437 
 438     makeSureImageIsCreated(isdo);
 439     CGContextDrawImage(cgRef, CGRectMake(0.0f, 0.0f, patternInfo->width, patternInfo->height), isdo->imgRef);
 440 
 441     UnlockImage(env, isdo);
 442 }
 443 
 444 // this function MUST NOT be inlined!
 445 void texturePaintReleaseFunction(void *info)
 446 {
 447     PRINT("    texturePaintReleaseFunction")
 448     JNIEnv* env = [ThreadUtilities getJNIEnvUncached];
 449 
 450     StatePatternInfo* patternInfo = (StatePatternInfo*)info;
 451     (*env)->DeleteGlobalRef(env, patternInfo->sdata);
 452 
 453     free(info);
 454 }
 455 
 456 static inline void contextTexturePath(JNIEnv* env, QuartzSDOps* qsdo)
 457 {
 458     PRINT("    ContextTexturePath")
 459     CGContextRef cgRef = qsdo->cgRef;
 460     StatePatternInfo* patternInfo = qsdo->patternInfo;
 461 
 462     CGAffineTransform ctm = CGContextGetCTM(cgRef);
 463     CGAffineTransform ptm = {patternInfo->sx, 0.0f, 0.0f, -patternInfo->sy, patternInfo->tx, patternInfo->ty};
 464     CGAffineTransform tm = CGAffineTransformConcat(ptm, ctm);
 465     CGFloat xStep = (CGFloat)qsdo->patternInfo->width;
 466     CGFloat yStep = (CGFloat)qsdo->patternInfo->height;
 467     CGPatternTiling tiling = kCGPatternTilingNoDistortion;
 468     BOOL isColored = YES;
 469     static const CGPatternCallbacks callbacks = {0, &texturePaintEvaluateFunction, &texturePaintReleaseFunction};
 470     CGPatternRef pattern = CGPatternCreate((void*)patternInfo, CGRectMake(0.0f, 0.0f, xStep, yStep), tm, xStep, yStep, tiling, isColored, &callbacks);
 471 
 472     CGColorSpaceRef colorspace = CGColorSpaceCreatePattern(NULL);
 473     static const CGFloat alpha = 1.0f;
 474 
 475     CGContextSaveGState(cgRef);
 476 
 477     CGContextSetFillColorSpace(cgRef, colorspace);
 478     CGContextSetFillPattern(cgRef, pattern, &alpha);
 479     CGContextSetRGBStrokeColor(cgRef, 0.0f, 0.0f, 0.0f, 1.0f);
 480     CGContextSetPatternPhase(cgRef, CGSizeMake(0.0f, 0.0f));
 481     // rdar://problem/5214320
 482     // Gradient fills of Java GeneralPath don't respect the even odd winding rule (quartz pipeline).
 483     if (qsdo->isEvenOddFill) {
 484         CGContextEOFillPath(cgRef);
 485     } else {
 486         CGContextFillPath(cgRef);
 487     }
 488 
 489     CGContextRestoreGState(cgRef);
 490 
 491     CGColorSpaceRelease(colorspace);
 492     CGPatternRelease(pattern);
 493 
 494     qsdo->patternInfo = NULL;
 495 }
 496 
 497 #pragma mark
 498 #pragma mark --- Context Setup ---
 499 
 500 static inline void setDefaultColorSpace(CGContextRef cgRef)
 501 {
 502     static CGColorSpaceRef colorspace = NULL;
 503     if (colorspace == NULL)
 504     {
 505         colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
 506     }
 507     CGContextSetStrokeColorSpace(cgRef, colorspace);
 508     CGContextSetFillColorSpace(cgRef, colorspace);
 509 }
 510 
 511 void SetUpCGContext(JNIEnv *env, QuartzSDOps *qsdo, SDRenderType renderType)
 512 {
 513 PRINT(" SetUpCGContext")
 514     CGContextRef cgRef = qsdo->cgRef;
 515 //fprintf(stderr, "%p ", cgRef);
 516     jint *javaGraphicsStates = qsdo->javaGraphicsStates;
 517     jfloat *javaFloatGraphicsStates = (jfloat*)(qsdo->javaGraphicsStates);
 518 
 519     jint changeFlags            = javaGraphicsStates[sun_java2d_OSXSurfaceData_kChangeFlagIndex];
 520     BOOL everyThingChanged        = qsdo->newContext || (changeFlags == sun_java2d_OSXSurfaceData_kEverythingChangedFlag);
 521     BOOL clipChanged            = everyThingChanged || ((changeFlags&sun_java2d_OSXSurfaceData_kClipChangedBit) != 0);
 522     BOOL transformChanged        = everyThingChanged || ((changeFlags&sun_java2d_OSXSurfaceData_kCTMChangedBit) != 0);
 523     BOOL paintChanged            = everyThingChanged || ((changeFlags&sun_java2d_OSXSurfaceData_kColorChangedBit) != 0);
 524     BOOL compositeChanged        = everyThingChanged || ((changeFlags&sun_java2d_OSXSurfaceData_kCompositeChangedBit) != 0);
 525     BOOL strokeChanged            = everyThingChanged || ((changeFlags&sun_java2d_OSXSurfaceData_kStrokeChangedBit) != 0);
 526 //    BOOL fontChanged            = everyThingChanged || ((changeFlags&sun_java2d_OSXSurfaceData_kFontChangedBit) != 0);
 527     BOOL renderingHintsChanged  = everyThingChanged || ((changeFlags&sun_java2d_OSXSurfaceData_kHintsChangedBit) != 0);
 528 
 529 //fprintf(stderr, "SetUpCGContext cgRef=%p new=%d changeFlags=%d, everyThingChanged=%d clipChanged=%d transformChanged=%d\n",
 530 //                    cgRef, qsdo->newContext, changeFlags, everyThingChanged, clipChanged, transformChanged);
 531 
 532     if ((everyThingChanged == YES) || (clipChanged == YES) || (transformChanged == YES))
 533     {
 534         everyThingChanged = YES; // in case clipChanged or transformChanged
 535 
 536         CGContextRestoreGState(cgRef);  // restore to the original state
 537 
 538         CGContextSaveGState(cgRef);        // make our local copy of the state
 539 
 540         setDefaultColorSpace(cgRef);
 541     }
 542 
 543     if ((everyThingChanged == YES) || (clipChanged == YES))
 544     {
 545         if (javaGraphicsStates[sun_java2d_OSXSurfaceData_kClipStateIndex] == sun_java2d_OSXSurfaceData_kClipRect)
 546         {
 547             CGFloat x = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kClipXIndex];
 548             CGFloat y = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kClipYIndex];
 549             CGFloat w = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kClipWidthIndex];
 550             CGFloat h = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kClipHeightIndex];
 551             CGContextClipToRect(cgRef, CGRectMake(x, y, w, h));
 552         }
 553         else
 554         {
 555             BOOL eoFill = (javaGraphicsStates[sun_java2d_OSXSurfaceData_kClipWindingRuleIndex] == java_awt_geom_PathIterator_WIND_EVEN_ODD);
 556             jint numtypes = javaGraphicsStates[sun_java2d_OSXSurfaceData_kClipNumTypesIndex];
 557 
 558             jobject coordsarray = (jobject)((*env)->GetObjectArrayElement(env, qsdo->javaGraphicsStatesObjects, sun_java2d_OSXSurfaceData_kClipCoordinatesIndex));
 559             jobject typesarray = (jobject)((*env)->GetObjectArrayElement(env, qsdo->javaGraphicsStatesObjects, sun_java2d_OSXSurfaceData_kClipTypesIndex));
 560 
 561             jfloat* coords = (jfloat*)(*env)->GetDirectBufferAddress(env, coordsarray);
 562             jint* types = (jint*)(*env)->GetDirectBufferAddress(env, typesarray);
 563 
 564             DoShapeUsingCG(cgRef, types, coords, numtypes, NO, qsdo->graphicsStateInfo.offsetX, qsdo->graphicsStateInfo.offsetY);
 565 
 566             if (CGContextIsPathEmpty(cgRef) == 0)
 567             {
 568                 if (eoFill)
 569                 {
 570                     CGContextEOClip(cgRef);
 571                 }
 572                 else
 573                 {
 574                     CGContextClip(cgRef);
 575                 }
 576             }
 577             else
 578             {
 579                 CGContextClipToRect(cgRef, CGRectZero);
 580             }
 581         }
 582     }
 583 // for debugging
 584 //CGContextResetClip(cgRef);
 585 
 586     if ((everyThingChanged == YES) || (transformChanged == YES))
 587     {
 588         CGFloat a = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCTMaIndex];
 589         CGFloat b = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCTMbIndex];
 590         CGFloat c = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCTMcIndex];
 591         CGFloat d = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCTMdIndex];
 592         CGFloat tx = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCTMtxIndex];
 593         CGFloat ty = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCTMtyIndex];
 594 
 595         CGContextConcatCTM(cgRef, CGAffineTransformMake(a, b, c, d, tx, ty));
 596 
 597         if (gAdjustForJavaDrawing == YES)
 598         {
 599             // find the offsets in the device corrdinate system
 600             CGAffineTransform ctm = CGContextGetCTM(cgRef);
 601             if ((qsdo->graphicsStateInfo.ctm.a != ctm.a) ||
 602                     (qsdo->graphicsStateInfo.ctm.b != ctm.b) ||
 603                         (qsdo->graphicsStateInfo.ctm.c != ctm.c) ||
 604                             (qsdo->graphicsStateInfo.ctm.d != ctm.d))
 605             {
 606                 qsdo->graphicsStateInfo.ctm = ctm;
 607                 // In CG affine xforms y' = bx+dy+ty
 608                 // We need to flip both y coefficeints to flip the offset point into the java coordinate system.
 609                 ctm.b = -ctm.b; ctm.d = -ctm.d; ctm.tx = 0.0f; ctm.ty = 0.0f;
 610                 CGPoint offsets = {kOffset, kOffset};
 611                 offsets = CGPointApplyInverseAffineTransform(offsets, ctm);
 612                 qsdo->graphicsStateInfo.offsetX = offsets.x;
 613                 qsdo->graphicsStateInfo.offsetY = offsets.y;
 614             }
 615         }
 616         else
 617         {
 618             qsdo->graphicsStateInfo.offsetX = 0.0f;
 619             qsdo->graphicsStateInfo.offsetY = 0.0f;
 620         }
 621     }
 622 
 623 // for debugging
 624 //CGContextResetCTM(cgRef);
 625 
 626     if ((everyThingChanged == YES) || (compositeChanged == YES))
 627     {
 628         jint alphaCompositeRule = javaGraphicsStates[sun_java2d_OSXSurfaceData_kCompositeRuleIndex];
 629         CGFloat alphaCompositeValue = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCompositeValueIndex];
 630 
 631         NSCompositingOperation op;
 632         switch (alphaCompositeRule)
 633         {
 634                 case java_awt_AlphaComposite_CLEAR:
 635                 op = NSCompositeClear;
 636                 break;
 637             case java_awt_AlphaComposite_SRC:
 638                 op = NSCompositeCopy;
 639                 break;
 640             case java_awt_AlphaComposite_SRC_OVER:
 641                 op = NSCompositeSourceOver;
 642                 break;
 643             case java_awt_AlphaComposite_DST_OVER:
 644                 op = NSCompositeDestinationOver;
 645                 break;
 646             case java_awt_AlphaComposite_SRC_IN:
 647                 op = NSCompositeSourceIn;
 648                 break;
 649             case java_awt_AlphaComposite_DST_IN:
 650                 op = NSCompositeDestinationIn;
 651                 break;
 652             case java_awt_AlphaComposite_SRC_OUT:
 653                 op = NSCompositeSourceOut;
 654                 break;
 655             case java_awt_AlphaComposite_DST_OUT:
 656                 op = NSCompositeDestinationOut;
 657                 break;
 658             case java_awt_AlphaComposite_DST:
 659                 // Alpha must be set to 0 because we're using the kCGCompositeSover rule
 660                 op = NSCompositeSourceOver;
 661                 alphaCompositeValue = 0.0f;
 662                 break;
 663             case java_awt_AlphaComposite_SRC_ATOP:
 664                 op = NSCompositeSourceAtop;
 665                 break;
 666             case java_awt_AlphaComposite_DST_ATOP:
 667                 op = NSCompositeDestinationAtop;
 668                 break;
 669             case java_awt_AlphaComposite_XOR:
 670                 op = NSCompositeXOR;
 671                 break;
 672             default:
 673                 op = NSCompositeSourceOver;
 674                 alphaCompositeValue = 1.0f;
 675                 break;
 676         }
 677 
 678         NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithGraphicsPort:cgRef flipped:NO];
 679         //CGContextSetCompositeOperation(cgRef, op);
 680         [context setCompositingOperation:op];
 681         CGContextSetAlpha(cgRef, alphaCompositeValue);
 682     }
 683 
 684     if ((everyThingChanged == YES) || (renderingHintsChanged == YES))
 685     {
 686         jint antialiasHint = javaGraphicsStates[sun_java2d_OSXSurfaceData_kHintsAntialiasIndex];
 687 //        jint textAntialiasHint = javaGraphicsStates[sun_java2d_OSXSurfaceData_kHintsTextAntialiasIndex];
 688         jint renderingHint = javaGraphicsStates[sun_java2d_OSXSurfaceData_kHintsRenderingIndex];
 689         jint interpolationHint = javaGraphicsStates[sun_java2d_OSXSurfaceData_kHintsInterpolationIndex];
 690 //        jint textFractionalMetricsHint = javaGraphicsStates[sun_java2d_OSXSurfaceData_kHintsFractionalMetricsIndex];
 691 
 692         // 10-10-02 VL: since CoreGraphics supports only an interpolation quality attribute we have to map
 693         // both interpolationHint and renderingHint to an attribute value that best represents their combination.
 694         // (See Radar 3071704.) We'll go for the best quality. CG maps interpolation quality values as follows:
 695         // kCGInterpolationNone - nearest_neighbor
 696         // kCGInterpolationLow - bilinear
 697         // kCGInterpolationHigh - Lanczos (better than bicubic)
 698         CGInterpolationQuality interpolationQuality = kCGInterpolationDefault;
 699         // First check if the interpolation hint is suggesting to turn off interpolation:
 700         if (interpolationHint == sun_awt_SunHints_INTVAL_INTERPOLATION_NEAREST_NEIGHBOR)
 701         {
 702             interpolationQuality = kCGInterpolationNone;
 703         }
 704         else if ((interpolationHint >= sun_awt_SunHints_INTVAL_INTERPOLATION_BICUBIC) || (renderingHint >= sun_awt_SunHints_INTVAL_RENDER_QUALITY))
 705         {
 706             // Use >= just in case Sun adds some hint values in the future - this check wouldn't fall apart then:
 707             interpolationQuality = kCGInterpolationHigh;
 708         }
 709         else if (interpolationHint == sun_awt_SunHints_INTVAL_INTERPOLATION_BILINEAR)
 710         {
 711             interpolationQuality = kCGInterpolationLow;
 712         }
 713         else if (renderingHint == sun_awt_SunHints_INTVAL_RENDER_SPEED)
 714         {
 715             interpolationQuality = kCGInterpolationNone;
 716         }
 717         // else interpolationHint == -1 || renderingHint == sun_awt_SunHints_INTVAL_CSURFACE_DEFAULT --> kCGInterpolationDefault
 718         CGContextSetInterpolationQuality(cgRef, interpolationQuality);
 719         qsdo->graphicsStateInfo.interpolation = interpolationQuality;
 720 
 721         // antialiasing
 722         BOOL antialiased = (antialiasHint == sun_awt_SunHints_INTVAL_ANTIALIAS_ON);
 723         CGContextSetShouldAntialias(cgRef, antialiased);
 724         qsdo->graphicsStateInfo.antialiased = antialiased;
 725     }
 726 
 727     if ((everyThingChanged == YES) || (strokeChanged == YES))
 728     {
 729         qsdo->graphicsStateInfo.simpleStroke = YES;
 730 
 731         CGFloat linewidth = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kStrokeWidthIndex];
 732         jint linejoin = javaGraphicsStates[sun_java2d_OSXSurfaceData_kStrokeJoinIndex];
 733         jint linecap = javaGraphicsStates[sun_java2d_OSXSurfaceData_kStrokeCapIndex];
 734         CGFloat miterlimit = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kStrokeLimitIndex];
 735         jobject dasharray = ((*env)->GetObjectArrayElement(env, qsdo->javaGraphicsStatesObjects, sun_java2d_OSXSurfaceData_kStrokeDashArrayIndex));
 736         CGFloat dashphase = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kStrokeDashPhaseIndex];
 737 
 738         if (linewidth == 0.0f)
 739         {
 740             linewidth = (CGFloat)-109.05473e+14; // Don't ask !
 741         }
 742         CGContextSetLineWidth(cgRef, linewidth);
 743 
 744         CGLineCap cap;
 745         switch (linecap)
 746         {
 747             case java_awt_BasicStroke_CAP_BUTT:
 748                 qsdo->graphicsStateInfo.simpleStroke = NO;
 749                 cap = kCGLineCapButt;
 750                 break;
 751             case java_awt_BasicStroke_CAP_ROUND:
 752                 qsdo->graphicsStateInfo.simpleStroke = NO;
 753                 cap = kCGLineCapRound;
 754                 break;
 755             case java_awt_BasicStroke_CAP_SQUARE:
 756             default:
 757                 cap = kCGLineCapSquare;
 758                 break;
 759         }
 760         CGContextSetLineCap(cgRef, cap);
 761 
 762         CGLineJoin join;
 763         switch (linejoin)
 764         {
 765             case java_awt_BasicStroke_JOIN_ROUND:
 766                 qsdo->graphicsStateInfo.simpleStroke = NO;
 767                 join = kCGLineJoinRound;
 768                 break;
 769             case java_awt_BasicStroke_JOIN_BEVEL:
 770                 qsdo->graphicsStateInfo.simpleStroke = NO;
 771                 join = kCGLineJoinBevel;
 772                 break;
 773             case java_awt_BasicStroke_JOIN_MITER:
 774             default:
 775                 join = kCGLineJoinMiter;
 776                 break;
 777         }
 778         CGContextSetLineJoin(cgRef, join);
 779         CGContextSetMiterLimit(cgRef, miterlimit);
 780 
 781         if (dasharray != NULL)
 782         {
 783             qsdo->graphicsStateInfo.simpleStroke = NO;
 784             jint length = (*env)->GetArrayLength(env, dasharray);
 785             jfloat* jdashes = (jfloat*)(*env)->GetPrimitiveArrayCritical(env, dasharray, NULL);
 786             CGFloat* dashes = (CGFloat*)malloc(sizeof(CGFloat)*length);
 787             if (dashes != NULL)
 788             {
 789                 jint i;
 790                 for (i=0; i<length; i++)
 791                 {
 792                     dashes[i] = (CGFloat)jdashes[i];
 793                 }
 794             }
 795             else
 796             {
 797                 dashphase = 0;
 798                 length = 0;
 799             }
 800             CGContextSetLineDash(cgRef, dashphase, dashes, length);
 801             if (dashes != NULL)
 802             {
 803                 free(dashes);
 804             }
 805             (*env)->ReleasePrimitiveArrayCritical(env, dasharray, jdashes, 0);
 806         }
 807         else
 808         {
 809             CGContextSetLineDash(cgRef, 0, NULL, 0);
 810         }
 811     }
 812 
 813     BOOL cocoaPaint = (javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorStateIndex] == sun_java2d_OSXSurfaceData_kColorSystem);
 814     BOOL complexPaint = (javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorStateIndex] == sun_java2d_OSXSurfaceData_kColorGradient) ||
 815                         (javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorStateIndex] == sun_java2d_OSXSurfaceData_kColorTexture);
 816     if ((everyThingChanged == YES) || (paintChanged == YES) || (cocoaPaint == YES) || (complexPaint == YES))
 817     {
 818         // rdar://problem/5214320
 819         // Gradient fills of Java GeneralPath don't respect the even odd winding rule (quartz pipeline).
 820         // Notice the side effect of the stmt after this if-block.
 821         if (renderType == SD_EOFill) {
 822             qsdo->isEvenOddFill = YES;
 823         }
 824 
 825         renderType = SetUpPaint(env, qsdo, renderType);
 826     }
 827 
 828     qsdo->renderType = renderType;
 829 }
 830 
 831 SDRenderType SetUpPaint(JNIEnv *env, QuartzSDOps *qsdo, SDRenderType renderType)
 832 {
 833     CGContextRef cgRef = qsdo->cgRef;
 834 
 835     jint *javaGraphicsStates = qsdo->javaGraphicsStates;
 836     jfloat *javaFloatGraphicsStates = (jfloat*)(qsdo->javaGraphicsStates);
 837 
 838     static const CGFloat kColorConversionMultiplier = 1.0f/255.0f;
 839     jint colorState = javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorStateIndex];
 840 
 841     switch (colorState)
 842     {
 843         case sun_java2d_OSXSurfaceData_kColorSimple:
 844         {
 845             if (qsdo->graphicsStateInfo.simpleColor == NO)
 846             {
 847                 setDefaultColorSpace(cgRef);
 848             }
 849             qsdo->graphicsStateInfo.simpleColor = YES;
 850 
 851             // sets the color on the CGContextRef (CGContextSetStrokeColorWithColor/CGContextSetFillColorWithColor)
 852             setCachedColor(qsdo, javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorRGBValueIndex]);
 853 
 854             break;
 855         }
 856         case sun_java2d_OSXSurfaceData_kColorSystem:
 857         {
 858             qsdo->graphicsStateInfo.simpleStroke = NO;
 859             // All our custom Colors are NSPatternColorSpace so we are complex colors!
 860             qsdo->graphicsStateInfo.simpleColor = NO;
 861 
 862             NSColor *color = nil;
 863             /* TODO:BG
 864             {
 865                 color = getColor(javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorIndexValueIndex]);
 866             }
 867             */
 868             [color set];
 869             break;
 870         }
 871         case sun_java2d_OSXSurfaceData_kColorGradient:
 872         {
 873             qsdo->shadingInfo = (StateShadingInfo*)malloc(sizeof(StateShadingInfo));
 874             if (qsdo->shadingInfo == NULL)
 875             {
 876                 [JNFException raise:env as:kOutOfMemoryError reason:"Failed to malloc memory for gradient paint"];
 877             }
 878 
 879             qsdo->graphicsStateInfo.simpleStroke = NO;
 880             qsdo->graphicsStateInfo.simpleColor = NO;
 881 
 882             renderType = SD_Shade;
 883 
 884             qsdo->shadingInfo->start.x    = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColorx1Index];
 885             qsdo->shadingInfo->start.y    = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColory1Index];
 886             qsdo->shadingInfo->end.x    = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColorx2Index];
 887             qsdo->shadingInfo->end.y    = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColory2Index];
 888             jint c1 = javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorRGBValue1Index];
 889             qsdo->shadingInfo->colors[0] = ((c1>>16)&0xff)*kColorConversionMultiplier;
 890             qsdo->shadingInfo->colors[1] = ((c1>>8)&0xff)*kColorConversionMultiplier;
 891             qsdo->shadingInfo->colors[2] = ((c1>>0)&0xff)*kColorConversionMultiplier;
 892             qsdo->shadingInfo->colors[3] = ((c1>>24)&0xff)*kColorConversionMultiplier;
 893             jint c2 = javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorRGBValue2Index];
 894             qsdo->shadingInfo->colors[4] = ((c2>>16)&0xff)*kColorConversionMultiplier;
 895             qsdo->shadingInfo->colors[5] = ((c2>>8)&0xff)*kColorConversionMultiplier;
 896             qsdo->shadingInfo->colors[6] = ((c2>>0)&0xff)*kColorConversionMultiplier;
 897             qsdo->shadingInfo->colors[7] = ((c2>>24)&0xff)*kColorConversionMultiplier;
 898             qsdo->shadingInfo->cyclic    = (javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorIsCyclicIndex] == sun_java2d_OSXSurfaceData_kColorCyclic);
 899 
 900             break;
 901         }
 902         case sun_java2d_OSXSurfaceData_kColorTexture:
 903         {
 904             qsdo->patternInfo = (StatePatternInfo*)malloc(sizeof(StatePatternInfo));
 905             if (qsdo->patternInfo == NULL)
 906             {
 907                 [JNFException raise:env as:kOutOfMemoryError reason:"Failed to malloc memory for texture paint"];
 908             }
 909 
 910             qsdo->graphicsStateInfo.simpleStroke = NO;
 911             qsdo->graphicsStateInfo.simpleColor = NO;
 912 
 913             renderType = SD_Pattern;
 914 
 915             qsdo->patternInfo->tx        = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColortxIndex];
 916             qsdo->patternInfo->ty        = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColortyIndex];
 917             qsdo->patternInfo->sx        = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColorsxIndex];
 918             if (qsdo->patternInfo->sx == 0.0f)
 919             {
 920                 return SD_Fill; // 0 is an invalid value, fill argb rect
 921             }
 922             qsdo->patternInfo->sy        = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColorsyIndex];
 923             if (qsdo->patternInfo->sy == 0.0f)
 924             {
 925                 return SD_Fill; // 0 is an invalid value, fill argb rect
 926             }
 927             qsdo->patternInfo->width    = javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorWidthIndex];
 928             qsdo->patternInfo->height    = javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorHeightIndex];
 929 
 930             jobject sData = ((*env)->GetObjectArrayElement(env, qsdo->javaGraphicsStatesObjects, sun_java2d_OSXSurfaceData_kTextureImageIndex)); //deleted next time through SetUpPaint and not before ( radr://3913190 )
 931             if (sData != NULL)
 932             {
 933                 qsdo->patternInfo->sdata = (*env)->NewGlobalRef(env, sData);
 934                 if (qsdo->patternInfo->sdata == NULL)
 935                 {
 936                     renderType = SD_Fill;
 937                 }
 938             }
 939             else
 940             {
 941                 renderType = SD_Fill;
 942             }
 943 
 944             break;
 945         }
 946     }
 947 
 948     return renderType;
 949 }
 950 
 951 #pragma mark
 952 #pragma mark --- Shape Drawing Code ---
 953 
 954 SDRenderType DoShapeUsingCG(CGContextRef cgRef, jint *types, jfloat *coords, jint numtypes, BOOL fill, CGFloat offsetX, CGFloat offsetY)
 955 {
 956 //fprintf(stderr, "DoShapeUsingCG fill=%d\n", (jint)fill);
 957     SDRenderType renderType = SD_Nothing;
 958 
 959     if (gAdjustForJavaDrawing != YES)
 960     {
 961         offsetX = 0.0f;
 962         offsetY = 0.0f;
 963     }
 964 
 965     if (fill == YES)
 966     {
 967         renderType = SD_Fill;
 968     }
 969     else
 970     {
 971         renderType = SD_Stroke;
 972     }
 973 
 974     if (numtypes > 0)
 975     {
 976         BOOL needNewSubpath = NO;
 977 
 978         CGContextBeginPath(cgRef); // create new path
 979 //fprintf(stderr, "    CGContextBeginPath\n");
 980 
 981         jint index = 0;
 982         CGFloat mx = 0.0f, my = 0.0f, x1 = 0.0f, y1 = 0.0f, cpx1 = 0.0f, cpy1 = 0.0f, cpx2 = 0.0f, cpy2 = 0.0f;
 983         jint i;
 984 
 985         mx = (CGFloat)coords[index++] + offsetX;
 986         my = (CGFloat)coords[index++] + offsetY;
 987         CGContextMoveToPoint(cgRef, mx, my);
 988 
 989         for (i=1; i<numtypes; i++)
 990         {
 991             jint pathType = types[i];
 992 
 993             if (needNewSubpath == YES)
 994             {
 995                 needNewSubpath = NO;
 996                 switch (pathType)
 997                 {
 998                     case java_awt_geom_PathIterator_SEG_LINETO:
 999                     case java_awt_geom_PathIterator_SEG_QUADTO:
1000                     case java_awt_geom_PathIterator_SEG_CUBICTO:
1001 //fprintf(stderr, "    forced CGContextMoveToPoint (%f, %f)\n", mx, my);
1002                         CGContextMoveToPoint(cgRef, mx, my); // force new subpath
1003                         break;
1004                 }
1005             }
1006 
1007             switch (pathType)
1008             {
1009                 case java_awt_geom_PathIterator_SEG_MOVETO:
1010                     mx = x1 = (CGFloat)coords[index++] + offsetX;
1011                     my = y1 = (CGFloat)coords[index++] + offsetY;
1012                     CGContextMoveToPoint(cgRef, x1, y1); // start new subpath
1013 //fprintf(stderr, "    SEG_MOVETO CGContextMoveToPoint (%f, %f)\n", x1, y1);
1014                     break;
1015                 case java_awt_geom_PathIterator_SEG_LINETO:
1016                     x1 = (CGFloat)coords[index++] + offsetX;
1017                     y1 = (CGFloat)coords[index++] + offsetY;
1018                     CGContextAddLineToPoint(cgRef, x1, y1);
1019 //fprintf(stderr, "    SEG_LINETO CGContextAddLineToPoint (%f, %f)\n", x1, y1);
1020                     break;
1021                 case java_awt_geom_PathIterator_SEG_QUADTO:
1022                     cpx1 = (CGFloat)coords[index++] + offsetX;
1023                     cpy1 = (CGFloat)coords[index++] + offsetY;
1024                     x1 = (CGFloat)coords[index++] + offsetX;
1025                     y1 = (CGFloat)coords[index++]+ offsetY;
1026                     CGContextAddQuadCurveToPoint(cgRef, cpx1, cpy1, x1, y1);
1027 //fprintf(stderr, "    SEG_QUADTO CGContextAddQuadCurveToPoint (%f, %f), (%f, %f)\n", cpx1, cpy1, x1, y1);
1028                     break;
1029                 case java_awt_geom_PathIterator_SEG_CUBICTO:
1030                     cpx1 = (CGFloat)coords[index++] + offsetX;
1031                     cpy1 = (CGFloat)coords[index++] + offsetY;
1032                     cpx2 = (CGFloat)coords[index++] + offsetX;
1033                     cpy2 = (CGFloat)coords[index++] + offsetY;
1034                     x1 = (CGFloat)coords[index++] + offsetX;
1035                     y1 = (CGFloat)coords[index++] + offsetY;
1036                     CGContextAddCurveToPoint(cgRef, cpx1, cpy1, cpx2, cpy2, x1, y1);
1037 //fprintf(stderr, "    SEG_CUBICTO CGContextAddCurveToPoint (%f, %f), (%f, %f), (%f, %f)\n", cpx1, cpy1, cpx2, cpy2, x1, y1);
1038                     break;
1039                 case java_awt_geom_PathIterator_SEG_CLOSE:
1040                     CGContextClosePath(cgRef); // close subpath
1041                     needNewSubpath = YES;
1042 //fprintf(stderr, "    SEG_CLOSE CGContextClosePath\n");
1043                     break;
1044             }
1045         }
1046     }
1047 
1048     return renderType;
1049 }
1050 
1051 void CompleteCGContext(JNIEnv *env, QuartzSDOps *qsdo)
1052 {
1053 PRINT(" CompleteCGContext")
1054     switch (qsdo->renderType)
1055     {
1056         case SD_Nothing:
1057             break;
1058 
1059         case SD_Stroke:
1060             if (CGContextIsPathEmpty(qsdo->cgRef) == 0)
1061             {
1062                 CGContextStrokePath(qsdo->cgRef);
1063             }
1064             break;
1065 
1066         case SD_Fill:
1067             if (CGContextIsPathEmpty(qsdo->cgRef) == 0)
1068             {
1069                 CGContextFillPath(qsdo->cgRef);
1070             }
1071             break;
1072 
1073         case SD_Shade:
1074             if (CGContextIsPathEmpty(qsdo->cgRef) == 0)
1075             {
1076                 contextGradientPath(qsdo);
1077             }
1078             break;
1079 
1080         case SD_Pattern:
1081             if (CGContextIsPathEmpty(qsdo->cgRef) == 0)
1082             {
1083                 //TODO:BG
1084                 //contextTexturePath(env, qsdo);
1085             }
1086             break;
1087 
1088         case SD_EOFill:
1089             if (CGContextIsPathEmpty(qsdo->cgRef) == 0)
1090             {
1091                 CGContextEOFillPath(qsdo->cgRef);
1092             }
1093             break;
1094 
1095         case SD_Image:
1096             break;
1097 
1098         case SD_Text:
1099             break;
1100 
1101         case SD_CopyArea:
1102             break;
1103 
1104         case SD_Queue:
1105             break;
1106 
1107         case SD_External:
1108             break;
1109     }
1110 
1111     if (qsdo->shadingInfo != NULL) {
1112         gradientPaintReleaseFunction(qsdo->shadingInfo);
1113         qsdo->shadingInfo = NULL;
1114     }
1115 }