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