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