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 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 contextGradientPath(QuartzSDOps* qsdo)
 272 {
 273 PRINT("    ContextGradientPath")
 274     CGContextRef cgRef = qsdo->cgRef;
 275     StateShadingInfo* shadingInfo = qsdo->shadingInfo;
 276 
 277     CGRect bounds = CGContextGetClipBoundingBox(cgRef);
 278 
 279     static const CGFloat domain[2] = {0.0f, 1.0f};
 280     static const CGFloat range[8] = {0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f};
 281     CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
 282     CGFunctionRef shadingFunc = NULL;
 283     CGShadingRef shading = NULL;
 284     if (shadingInfo->cyclic == NO)
 285     {
 286         static const CGFunctionCallbacks callbacks = {0, &gradientLinearPaintEvaluateFunction, &gradientPaintReleaseFunction};
 287         shadingFunc = CGFunctionCreate((void *)shadingInfo, 1, domain, 4, range, &callbacks);
 288         shading = CGShadingCreateAxial(colorspace, shadingInfo->start, shadingInfo->end, shadingFunc, 1, 1);
 289     }
 290     else
 291     {
 292 //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);
 293         // need to extend the line start-end
 294 
 295         CGFloat x1 = shadingInfo->start.x;
 296         CGFloat y1 = shadingInfo->start.y;
 297         CGFloat x2 = shadingInfo->end.x;
 298         CGFloat y2 = shadingInfo->end.y;
 299 //fprintf(stderr, "GIVEN x1=%f, y1=%f      x2=%f, y2=%f\n", x1, y1, x2, y2);
 300 
 301         if (x1 == x2)
 302         {
 303             y1 = bounds.origin.y;
 304             y2 = y1 + bounds.size.height;
 305         }
 306         else if (y1 == y2)
 307         {
 308             x1 = bounds.origin.x;
 309             x2 = x1 + bounds.size.width;
 310         }
 311         else
 312         {
 313             // find the original line function y = mx + c
 314             CGFloat m1 = (y2-y1)/(x2-x1);
 315             CGFloat c1 = y1 - m1*x1;
 316 //fprintf(stderr, "         m1=%f, c1=%f\n", m1, c1);
 317 
 318             // a line perpendicular to the original one will have the slope
 319             CGFloat m2 = -(1/m1);
 320 //fprintf(stderr, "         m2=%f\n", m2);
 321 
 322             // find the only 2 possible lines perpendicular to the original line, passing the two top corners of the bounding box
 323             CGFloat x1A = bounds.origin.x;
 324             CGFloat y1A = bounds.origin.y;
 325             CGFloat c1A = y1A - m2*x1A;
 326 //fprintf(stderr, "         x1A=%f, y1A=%f, c1A=%f\n", x1A, y1A, c1A);
 327             CGFloat x1B = bounds.origin.x+bounds.size.width;
 328             CGFloat y1B = bounds.origin.y;
 329             CGFloat c1B = y1B - m2*x1B;
 330 //fprintf(stderr, "         x1B=%f, y1B=%f, c1B=%f\n", x1B, y1B, c1B);
 331 
 332             // find the crossing points of the original line and the two lines we computed above to find the new possible starting points
 333             CGFloat x1Anew = (c1A-c1)/(m1-m2);
 334             CGFloat y1Anew = m2*x1Anew + c1A;
 335             CGFloat x1Bnew = (c1B-c1)/(m1-m2);
 336             CGFloat y1Bnew = m2*x1Bnew + c1B;
 337 //fprintf(stderr, "NEW x1Anew=%f, y1Anew=%f      x1Bnew=%f, y1Bnew=%f\n", x1Anew, y1Anew, x1Bnew, y1Bnew);
 338 
 339             // select the new starting point
 340             if (y1Anew <= y1Bnew)
 341             {
 342                 x1 = x1Anew;
 343                 y1 = y1Anew;
 344             }
 345             else
 346             {
 347                 x1 = x1Bnew;
 348                 y1 = y1Bnew;
 349             }
 350 //fprintf(stderr, "--- NEW x1=%f, y1=%f\n", x1, y1);
 351 
 352             // find the only 2 possible lines perpendicular to the original line, passing the two bottom corners of the bounding box
 353             CGFloat x2A = bounds.origin.x;
 354             CGFloat y2A = bounds.origin.y+bounds.size.height;
 355             CGFloat c2A = y2A - m2*x2A;
 356 //fprintf(stderr, "         x2A=%f, y2A=%f, c2A=%f\n", x2A, y2A, c2A);
 357             CGFloat x2B = bounds.origin.x+bounds.size.width;
 358             CGFloat y2B = bounds.origin.y+bounds.size.height;
 359             CGFloat c2B = y2B - m2*x2B;
 360 //fprintf(stderr, "         x2B=%f, y2B=%f, c2B=%f\n", x2B, y2B, c2B);
 361 
 362             // find the crossing points of the original line and the two lines we computed above to find the new possible ending points
 363             CGFloat x2Anew = (c2A-c1)/(m1-m2);
 364             CGFloat y2Anew = m2*x2Anew + c2A;
 365             CGFloat x2Bnew = (c2B-c1)/(m1-m2);
 366             CGFloat y2Bnew = m2*x2Bnew + c2B;
 367 //fprintf(stderr, "NEW x2Anew=%f, y2Anew=%f      x2Bnew=%f, y2Bnew=%f\n", x2Anew, y2Anew, x2Bnew, y2Bnew);
 368 
 369             // select the new ending point
 370             if (y2Anew >= y2Bnew)
 371             {
 372                 x2 = x2Anew;
 373                 y2 = y2Anew;
 374             }
 375             else
 376             {
 377                 x2 = x2Bnew;
 378                 y2 = y2Bnew;
 379             }
 380 //fprintf(stderr, "--- NEW x2=%f, y2=%f\n", x2, y2);
 381         }
 382 
 383         qsdo->shadingInfo->period = sqrt(pow(shadingInfo->end.x-shadingInfo->start.x, 2.0) + pow(shadingInfo->end.y-shadingInfo->start.y, 2.0));
 384         if ((qsdo->shadingInfo->period != 0))
 385         {
 386             // compute segment lengths that we will need for the gradient function
 387             qsdo->shadingInfo->length = sqrt(pow(x2-x1, 2.0) + pow(y2-y1, 2.0));
 388             qsdo->shadingInfo->offset = sqrt(pow(shadingInfo->start.x-x1, 2.0) + pow(shadingInfo->start.y-y1, 2.0));
 389 //fprintf(stderr, "length=%f, period=%f, offset=%f\n", qsdo->shadingInfo->length, qsdo->shadingInfo->period, qsdo->shadingInfo->offset);
 390 
 391             CGPoint newStart = {x1, y1};
 392             CGPoint newEnd = {x2, y2};
 393 
 394             static const CGFunctionCallbacks callbacks = {0, &gradientCyclicPaintEvaluateFunction, &gradientPaintReleaseFunction};
 395             shadingFunc = CGFunctionCreate((void *)shadingInfo, 1, domain, 4, range, &callbacks);
 396             shading = CGShadingCreateAxial(colorspace, newStart, newEnd, shadingFunc, 0, 0);
 397         }
 398     }
 399     CGColorSpaceRelease(colorspace);
 400 
 401     if (shadingFunc != NULL)
 402     {
 403         CGContextSaveGState(cgRef);
 404 
 405         // rdar://problem/5214320
 406         // Gradient fills of Java GeneralPath don't respect the even odd winding rule (quartz pipeline).
 407         if (qsdo->isEvenOddFill) {
 408             CGContextEOClip(cgRef);
 409         } else {
 410             CGContextClip(cgRef);
 411         }
 412         CGContextDrawShading(cgRef, shading);
 413 
 414         CGContextRestoreGState(cgRef);
 415         CGShadingRelease(shading);
 416         CGFunctionRelease(shadingFunc);
 417         qsdo->shadingInfo = NULL;
 418     }
 419 }
 420 
 421 #pragma mark
 422 #pragma mark --- Texture ---
 423 
 424 // this function MUST NOT be inlined!
 425 void texturePaintEvaluateFunction(void *info, CGContextRef cgRef)
 426 {
 427     JNIEnv* env = [ThreadUtilities getJNIEnvUncached];
 428 
 429     StatePatternInfo* patternInfo = (StatePatternInfo*)info;
 430     ImageSDOps* isdo = LockImage(env, patternInfo->sdata);
 431 
 432     makeSureImageIsCreated(isdo);
 433     CGContextDrawImage(cgRef, CGRectMake(0.0f, 0.0f, patternInfo->width, patternInfo->height), isdo->imgRef);
 434 
 435     UnlockImage(env, isdo);
 436 }
 437 
 438 // this function MUST NOT be inlined!
 439 void texturePaintReleaseFunction(void *info)
 440 {
 441     PRINT("    texturePaintReleaseFunction")
 442     JNIEnv* env = [ThreadUtilities getJNIEnvUncached];
 443 
 444     StatePatternInfo* patternInfo = (StatePatternInfo*)info;
 445     (*env)->DeleteGlobalRef(env, patternInfo->sdata);
 446 
 447     free(info);
 448 }
 449 
 450 static inline void contextTexturePath(JNIEnv* env, QuartzSDOps* qsdo)
 451 {
 452     PRINT("    ContextTexturePath")
 453     CGContextRef cgRef = qsdo->cgRef;
 454     StatePatternInfo* patternInfo = qsdo->patternInfo;
 455 
 456     CGAffineTransform ctm = CGContextGetCTM(cgRef);
 457     CGAffineTransform ptm = {patternInfo->sx, 0.0f, 0.0f, -patternInfo->sy, patternInfo->tx, patternInfo->ty};
 458     CGAffineTransform tm = CGAffineTransformConcat(ptm, ctm);
 459     CGFloat xStep = (CGFloat)qsdo->patternInfo->width;
 460     CGFloat yStep = (CGFloat)qsdo->patternInfo->height;
 461     CGPatternTiling tiling = kCGPatternTilingNoDistortion;
 462     BOOL isColored = YES;
 463     static const CGPatternCallbacks callbacks = {0, &texturePaintEvaluateFunction, &texturePaintReleaseFunction};
 464     CGPatternRef pattern = CGPatternCreate((void*)patternInfo, CGRectMake(0.0f, 0.0f, xStep, yStep), tm, xStep, yStep, tiling, isColored, &callbacks);
 465 
 466     CGColorSpaceRef colorspace = CGColorSpaceCreatePattern(NULL);
 467     static const CGFloat alpha = 1.0f;
 468 
 469     CGContextSaveGState(cgRef);
 470 
 471     CGContextSetFillColorSpace(cgRef, colorspace);
 472     CGContextSetFillPattern(cgRef, pattern, &alpha);
 473     CGContextSetRGBStrokeColor(cgRef, 0.0f, 0.0f, 0.0f, 1.0f);
 474     CGContextSetPatternPhase(cgRef, CGSizeMake(0.0f, 0.0f));
 475     // rdar://problem/5214320
 476     // Gradient fills of Java GeneralPath don't respect the even odd winding rule (quartz pipeline).
 477     if (qsdo->isEvenOddFill) {
 478         CGContextEOFillPath(cgRef);
 479     } else {
 480         CGContextFillPath(cgRef);
 481     }
 482 
 483     CGContextRestoreGState(cgRef);
 484 
 485     CGColorSpaceRelease(colorspace);
 486     CGPatternRelease(pattern);
 487 
 488     qsdo->patternInfo = NULL;
 489 }
 490 
 491 #pragma mark
 492 #pragma mark --- Context Setup ---
 493 
 494 static inline void setDefaultColorSpace(CGContextRef cgRef)
 495 {
 496     static CGColorSpaceRef colorspace = NULL;
 497     if (colorspace == NULL)
 498     {
 499         colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
 500     }
 501     CGContextSetStrokeColorSpace(cgRef, colorspace);
 502     CGContextSetFillColorSpace(cgRef, colorspace);
 503 }
 504 
 505 void SetUpCGContext(JNIEnv *env, QuartzSDOps *qsdo, SDRenderType renderType)
 506 {
 507 PRINT(" SetUpCGContext")
 508     CGContextRef cgRef = qsdo->cgRef;
 509 //fprintf(stderr, "%p ", cgRef);
 510     jint *javaGraphicsStates = qsdo->javaGraphicsStates;
 511     jfloat *javaFloatGraphicsStates = (jfloat*)(qsdo->javaGraphicsStates);
 512 
 513     jint changeFlags            = javaGraphicsStates[sun_java2d_OSXSurfaceData_kChangeFlagIndex];
 514     BOOL everyThingChanged        = qsdo->newContext || (changeFlags == sun_java2d_OSXSurfaceData_kEverythingChangedFlag);
 515     BOOL clipChanged            = everyThingChanged || ((changeFlags&sun_java2d_OSXSurfaceData_kClipChangedBit) != 0);
 516     BOOL transformChanged        = everyThingChanged || ((changeFlags&sun_java2d_OSXSurfaceData_kCTMChangedBit) != 0);
 517     BOOL paintChanged            = everyThingChanged || ((changeFlags&sun_java2d_OSXSurfaceData_kColorChangedBit) != 0);
 518     BOOL compositeChanged        = everyThingChanged || ((changeFlags&sun_java2d_OSXSurfaceData_kCompositeChangedBit) != 0);
 519     BOOL strokeChanged            = everyThingChanged || ((changeFlags&sun_java2d_OSXSurfaceData_kStrokeChangedBit) != 0);
 520 //    BOOL fontChanged            = everyThingChanged || ((changeFlags&sun_java2d_OSXSurfaceData_kFontChangedBit) != 0);
 521     BOOL renderingHintsChanged  = everyThingChanged || ((changeFlags&sun_java2d_OSXSurfaceData_kHintsChangedBit) != 0);
 522 
 523 //fprintf(stderr, "SetUpCGContext cgRef=%p new=%d changeFlags=%d, everyThingChanged=%d clipChanged=%d transformChanged=%d\n",
 524 //                    cgRef, qsdo->newContext, changeFlags, everyThingChanged, clipChanged, transformChanged);
 525 
 526     if ((everyThingChanged == YES) || (clipChanged == YES) || (transformChanged == YES))
 527     {
 528         everyThingChanged = YES; // in case clipChanged or transformChanged
 529 
 530         CGContextRestoreGState(cgRef);  // restore to the original state
 531 
 532         CGContextSaveGState(cgRef);        // make our local copy of the state
 533 
 534         setDefaultColorSpace(cgRef);
 535     }
 536 
 537     if ((everyThingChanged == YES) || (clipChanged == YES))
 538     {
 539         if (javaGraphicsStates[sun_java2d_OSXSurfaceData_kClipStateIndex] == sun_java2d_OSXSurfaceData_kClipRect)
 540         {
 541             CGFloat x = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kClipXIndex];
 542             CGFloat y = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kClipYIndex];
 543             CGFloat w = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kClipWidthIndex];
 544             CGFloat h = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kClipHeightIndex];
 545             CGContextClipToRect(cgRef, CGRectMake(x, y, w, h));
 546         }
 547         else
 548         {
 549             BOOL eoFill = (javaGraphicsStates[sun_java2d_OSXSurfaceData_kClipWindingRuleIndex] == java_awt_geom_PathIterator_WIND_EVEN_ODD);
 550             jint numtypes = javaGraphicsStates[sun_java2d_OSXSurfaceData_kClipNumTypesIndex];
 551 
 552             jobject coordsarray = (jobject)((*env)->GetObjectArrayElement(env, qsdo->javaGraphicsStatesObjects, sun_java2d_OSXSurfaceData_kClipCoordinatesIndex));
 553             jobject typesarray = (jobject)((*env)->GetObjectArrayElement(env, qsdo->javaGraphicsStatesObjects, sun_java2d_OSXSurfaceData_kClipTypesIndex));
 554 
 555             jfloat* coords = (jfloat*)(*env)->GetDirectBufferAddress(env, coordsarray);
 556             jint* types = (jint*)(*env)->GetDirectBufferAddress(env, typesarray);
 557 
 558             DoShapeUsingCG(cgRef, types, coords, numtypes, NO, qsdo->graphicsStateInfo.offsetX, qsdo->graphicsStateInfo.offsetY);
 559 
 560             if (CGContextIsPathEmpty(cgRef) == 0)
 561             {
 562                 if (eoFill)
 563                 {
 564                     CGContextEOClip(cgRef);
 565                 }
 566                 else
 567                 {
 568                     CGContextClip(cgRef);
 569                 }
 570             }
 571             else
 572             {
 573                 CGContextClipToRect(cgRef, CGRectZero);
 574             }
 575         }
 576     }
 577 // for debugging
 578 //CGContextResetClip(cgRef);
 579 
 580     if ((everyThingChanged == YES) || (transformChanged == YES))
 581     {
 582         CGFloat a = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCTMaIndex];
 583         CGFloat b = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCTMbIndex];
 584         CGFloat c = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCTMcIndex];
 585         CGFloat d = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCTMdIndex];
 586         CGFloat tx = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCTMtxIndex];
 587         CGFloat ty = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCTMtyIndex];
 588 
 589         CGContextConcatCTM(cgRef, CGAffineTransformMake(a, b, c, d, tx, ty));
 590 
 591         if (gAdjustForJavaDrawing == YES)
 592         {
 593             // find the offsets in the device corrdinate system
 594             CGAffineTransform ctm = CGContextGetCTM(cgRef);
 595             if ((qsdo->graphicsStateInfo.ctm.a != ctm.a) ||
 596                     (qsdo->graphicsStateInfo.ctm.b != ctm.b) ||
 597                         (qsdo->graphicsStateInfo.ctm.c != ctm.c) ||
 598                             (qsdo->graphicsStateInfo.ctm.d != ctm.d))
 599             {
 600                 qsdo->graphicsStateInfo.ctm = ctm;
 601                 // In CG affine xforms y' = bx+dy+ty
 602                 // We need to flip both y coefficeints to flip the offset point into the java coordinate system.
 603                 ctm.b = -ctm.b; ctm.d = -ctm.d; ctm.tx = 0.0f; ctm.ty = 0.0f;
 604                 CGPoint offsets = {kOffset, kOffset};
 605                 CGAffineTransform inverse = CGAffineTransformInvert(ctm);
 606                 offsets = CGPointApplyAffineTransform(offsets, inverse);
 607                 qsdo->graphicsStateInfo.offsetX = offsets.x;
 608                 qsdo->graphicsStateInfo.offsetY = offsets.y;
 609             }
 610         }
 611         else
 612         {
 613             qsdo->graphicsStateInfo.offsetX = 0.0f;
 614             qsdo->graphicsStateInfo.offsetY = 0.0f;
 615         }
 616     }
 617 
 618 // for debugging
 619 //CGContextResetCTM(cgRef);
 620 
 621     if ((everyThingChanged == YES) || (compositeChanged == YES))
 622     {
 623         jint alphaCompositeRule = javaGraphicsStates[sun_java2d_OSXSurfaceData_kCompositeRuleIndex];
 624         CGFloat alphaCompositeValue = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCompositeValueIndex];
 625 
 626         NSCompositingOperation op;
 627         switch (alphaCompositeRule)
 628         {
 629                 case java_awt_AlphaComposite_CLEAR:
 630                 op = NSCompositeClear;
 631                 break;
 632             case java_awt_AlphaComposite_SRC:
 633                 op = NSCompositeCopy;
 634                 break;
 635             case java_awt_AlphaComposite_SRC_OVER:
 636                 op = NSCompositeSourceOver;
 637                 break;
 638             case java_awt_AlphaComposite_DST_OVER:
 639                 op = NSCompositeDestinationOver;
 640                 break;
 641             case java_awt_AlphaComposite_SRC_IN:
 642                 op = NSCompositeSourceIn;
 643                 break;
 644             case java_awt_AlphaComposite_DST_IN:
 645                 op = NSCompositeDestinationIn;
 646                 break;
 647             case java_awt_AlphaComposite_SRC_OUT:
 648                 op = NSCompositeSourceOut;
 649                 break;
 650             case java_awt_AlphaComposite_DST_OUT:
 651                 op = NSCompositeDestinationOut;
 652                 break;
 653             case java_awt_AlphaComposite_DST:
 654                 // Alpha must be set to 0 because we're using the kCGCompositeSover rule
 655                 op = NSCompositeSourceOver;
 656                 alphaCompositeValue = 0.0f;
 657                 break;
 658             case java_awt_AlphaComposite_SRC_ATOP:
 659                 op = NSCompositeSourceAtop;
 660                 break;
 661             case java_awt_AlphaComposite_DST_ATOP:
 662                 op = NSCompositeDestinationAtop;
 663                 break;
 664             case java_awt_AlphaComposite_XOR:
 665                 op = NSCompositeXOR;
 666                 break;
 667             default:
 668                 op = NSCompositeSourceOver;
 669                 alphaCompositeValue = 1.0f;
 670                 break;
 671         }
 672 
 673         NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithGraphicsPort:cgRef flipped:NO];
 674         //CGContextSetCompositeOperation(cgRef, op);
 675         [context setCompositingOperation:op];
 676         CGContextSetAlpha(cgRef, alphaCompositeValue);
 677     }
 678 
 679     if ((everyThingChanged == YES) || (renderingHintsChanged == YES))
 680     {
 681         jint antialiasHint = javaGraphicsStates[sun_java2d_OSXSurfaceData_kHintsAntialiasIndex];
 682 //        jint textAntialiasHint = javaGraphicsStates[sun_java2d_OSXSurfaceData_kHintsTextAntialiasIndex];
 683         jint renderingHint = javaGraphicsStates[sun_java2d_OSXSurfaceData_kHintsRenderingIndex];
 684         jint interpolationHint = javaGraphicsStates[sun_java2d_OSXSurfaceData_kHintsInterpolationIndex];
 685 //        jint textFractionalMetricsHint = javaGraphicsStates[sun_java2d_OSXSurfaceData_kHintsFractionalMetricsIndex];
 686 
 687         // 10-10-02 VL: since CoreGraphics supports only an interpolation quality attribute we have to map
 688         // both interpolationHint and renderingHint to an attribute value that best represents their combination.
 689         // (See Radar 3071704.) We'll go for the best quality. CG maps interpolation quality values as follows:
 690         // kCGInterpolationNone - nearest_neighbor
 691         // kCGInterpolationLow - bilinear
 692         // kCGInterpolationHigh - Lanczos (better than bicubic)
 693         CGInterpolationQuality interpolationQuality = kCGInterpolationDefault;
 694         // First check if the interpolation hint is suggesting to turn off interpolation:
 695         if (interpolationHint == sun_awt_SunHints_INTVAL_INTERPOLATION_NEAREST_NEIGHBOR)
 696         {
 697             interpolationQuality = kCGInterpolationNone;
 698         }
 699         else if ((interpolationHint >= sun_awt_SunHints_INTVAL_INTERPOLATION_BICUBIC) || (renderingHint >= sun_awt_SunHints_INTVAL_RENDER_QUALITY))
 700         {
 701             // Use >= just in case Sun adds some hint values in the future - this check wouldn't fall apart then:
 702             interpolationQuality = kCGInterpolationHigh;
 703         }
 704         else if (interpolationHint == sun_awt_SunHints_INTVAL_INTERPOLATION_BILINEAR)
 705         {
 706             interpolationQuality = kCGInterpolationLow;
 707         }
 708         else if (renderingHint == sun_awt_SunHints_INTVAL_RENDER_SPEED)
 709         {
 710             interpolationQuality = kCGInterpolationNone;
 711         }
 712         // else interpolationHint == -1 || renderingHint == sun_awt_SunHints_INTVAL_CSURFACE_DEFAULT --> kCGInterpolationDefault
 713         CGContextSetInterpolationQuality(cgRef, interpolationQuality);
 714         qsdo->graphicsStateInfo.interpolation = interpolationQuality;
 715 
 716         // antialiasing
 717         BOOL antialiased = (antialiasHint == sun_awt_SunHints_INTVAL_ANTIALIAS_ON);
 718         CGContextSetShouldAntialias(cgRef, antialiased);
 719         qsdo->graphicsStateInfo.antialiased = antialiased;
 720     }
 721 
 722     if ((everyThingChanged == YES) || (strokeChanged == YES))
 723     {
 724         qsdo->graphicsStateInfo.simpleStroke = YES;
 725 
 726         CGFloat linewidth = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kStrokeWidthIndex];
 727         jint linejoin = javaGraphicsStates[sun_java2d_OSXSurfaceData_kStrokeJoinIndex];
 728         jint linecap = javaGraphicsStates[sun_java2d_OSXSurfaceData_kStrokeCapIndex];
 729         CGFloat miterlimit = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kStrokeLimitIndex];
 730         jobject dasharray = ((*env)->GetObjectArrayElement(env, qsdo->javaGraphicsStatesObjects, sun_java2d_OSXSurfaceData_kStrokeDashArrayIndex));
 731         CGFloat dashphase = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kStrokeDashPhaseIndex];
 732 
 733         if (linewidth == 0.0f)
 734         {
 735             linewidth = (CGFloat)-109.05473e+14; // Don't ask !
 736         }
 737         CGContextSetLineWidth(cgRef, linewidth);
 738 
 739         CGLineCap cap;
 740         switch (linecap)
 741         {
 742             case java_awt_BasicStroke_CAP_BUTT:
 743                 qsdo->graphicsStateInfo.simpleStroke = NO;
 744                 cap = kCGLineCapButt;
 745                 break;
 746             case java_awt_BasicStroke_CAP_ROUND:
 747                 qsdo->graphicsStateInfo.simpleStroke = NO;
 748                 cap = kCGLineCapRound;
 749                 break;
 750             case java_awt_BasicStroke_CAP_SQUARE:
 751             default:
 752                 cap = kCGLineCapSquare;
 753                 break;
 754         }
 755         CGContextSetLineCap(cgRef, cap);
 756 
 757         CGLineJoin join;
 758         switch (linejoin)
 759         {
 760             case java_awt_BasicStroke_JOIN_ROUND:
 761                 qsdo->graphicsStateInfo.simpleStroke = NO;
 762                 join = kCGLineJoinRound;
 763                 break;
 764             case java_awt_BasicStroke_JOIN_BEVEL:
 765                 qsdo->graphicsStateInfo.simpleStroke = NO;
 766                 join = kCGLineJoinBevel;
 767                 break;
 768             case java_awt_BasicStroke_JOIN_MITER:
 769             default:
 770                 join = kCGLineJoinMiter;
 771                 break;
 772         }
 773         CGContextSetLineJoin(cgRef, join);
 774         CGContextSetMiterLimit(cgRef, miterlimit);
 775 
 776         if (dasharray != NULL)
 777         {
 778             qsdo->graphicsStateInfo.simpleStroke = NO;
 779             jint length = (*env)->GetArrayLength(env, dasharray);
 780             jfloat* jdashes = (jfloat*)(*env)->GetPrimitiveArrayCritical(env, dasharray, NULL);
 781             if (jdashes == NULL) {
 782                 CGContextSetLineDash(cgRef, 0, NULL, 0);
 783                 return;
 784             }
 785             CGFloat* dashes = (CGFloat*)malloc(sizeof(CGFloat)*length);
 786             if (dashes != NULL)
 787             {
 788                 jint i;
 789                 for (i=0; i<length; i++)
 790                 {
 791                     dashes[i] = (CGFloat)jdashes[i];
 792                 }
 793             }
 794             else
 795             {
 796                 dashphase = 0;
 797                 length = 0;
 798             }
 799             CGContextSetLineDash(cgRef, dashphase, dashes, length);
 800             if (dashes != NULL)
 801             {
 802                 free(dashes);
 803             }
 804             (*env)->ReleasePrimitiveArrayCritical(env, dasharray, jdashes, 0);
 805         }
 806         else
 807         {
 808             CGContextSetLineDash(cgRef, 0, NULL, 0);
 809         }
 810     }
 811 
 812     BOOL cocoaPaint = (javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorStateIndex] == sun_java2d_OSXSurfaceData_kColorSystem);
 813     BOOL complexPaint = (javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorStateIndex] == sun_java2d_OSXSurfaceData_kColorGradient) ||
 814                         (javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorStateIndex] == sun_java2d_OSXSurfaceData_kColorTexture);
 815     if ((everyThingChanged == YES) || (paintChanged == YES) || (cocoaPaint == YES) || (complexPaint == YES))
 816     {
 817         // rdar://problem/5214320
 818         // Gradient fills of Java GeneralPath don't respect the even odd winding rule (quartz pipeline).
 819         // Notice the side effect of the stmt after this if-block.
 820         if (renderType == SD_EOFill) {
 821             qsdo->isEvenOddFill = YES;
 822         }
 823 
 824         renderType = SetUpPaint(env, qsdo, renderType);
 825     }
 826 
 827     qsdo->renderType = renderType;
 828 }
 829 
 830 SDRenderType SetUpPaint(JNIEnv *env, QuartzSDOps *qsdo, SDRenderType renderType)
 831 {
 832     CGContextRef cgRef = qsdo->cgRef;
 833 
 834     jint *javaGraphicsStates = qsdo->javaGraphicsStates;
 835     jfloat *javaFloatGraphicsStates = (jfloat*)(qsdo->javaGraphicsStates);
 836 
 837     static const CGFloat kColorConversionMultiplier = 1.0f/255.0f;
 838     jint colorState = javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorStateIndex];
 839 
 840     switch (colorState)
 841     {
 842         case sun_java2d_OSXSurfaceData_kColorSimple:
 843         {
 844             if (qsdo->graphicsStateInfo.simpleColor == NO)
 845             {
 846                 setDefaultColorSpace(cgRef);
 847             }
 848             qsdo->graphicsStateInfo.simpleColor = YES;
 849 
 850             // sets the color on the CGContextRef (CGContextSetStrokeColorWithColor/CGContextSetFillColorWithColor)
 851             setCachedColor(qsdo, javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorRGBValueIndex]);
 852 
 853             break;
 854         }
 855         case sun_java2d_OSXSurfaceData_kColorSystem:
 856         {
 857             qsdo->graphicsStateInfo.simpleStroke = NO;
 858             // All our custom Colors are NSPatternColorSpace so we are complex colors!
 859             qsdo->graphicsStateInfo.simpleColor = NO;
 860 
 861             NSColor *color = nil;
 862             /* TODO:BG
 863             {
 864                 color = getColor(javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorIndexValueIndex]);
 865             }
 866             */
 867             [color set];
 868             break;
 869         }
 870         case sun_java2d_OSXSurfaceData_kColorGradient:
 871         {
 872             qsdo->shadingInfo = (StateShadingInfo*)malloc(sizeof(StateShadingInfo));
 873             if (qsdo->shadingInfo == NULL)
 874             {
 875                 [JNFException raise:env as:kOutOfMemoryError reason:"Failed to malloc memory for gradient paint"];
 876             }
 877 
 878             qsdo->graphicsStateInfo.simpleStroke = NO;
 879             qsdo->graphicsStateInfo.simpleColor = NO;
 880 
 881             renderType = SD_Shade;
 882 
 883             qsdo->shadingInfo->start.x    = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColorx1Index];
 884             qsdo->shadingInfo->start.y    = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColory1Index];
 885             qsdo->shadingInfo->end.x    = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColorx2Index];
 886             qsdo->shadingInfo->end.y    = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColory2Index];
 887             jint c1 = javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorRGBValue1Index];
 888             qsdo->shadingInfo->colors[0] = ((c1>>16)&0xff)*kColorConversionMultiplier;
 889             qsdo->shadingInfo->colors[1] = ((c1>>8)&0xff)*kColorConversionMultiplier;
 890             qsdo->shadingInfo->colors[2] = ((c1>>0)&0xff)*kColorConversionMultiplier;
 891             qsdo->shadingInfo->colors[3] = ((c1>>24)&0xff)*kColorConversionMultiplier;
 892             jint c2 = javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorRGBValue2Index];
 893             qsdo->shadingInfo->colors[4] = ((c2>>16)&0xff)*kColorConversionMultiplier;
 894             qsdo->shadingInfo->colors[5] = ((c2>>8)&0xff)*kColorConversionMultiplier;
 895             qsdo->shadingInfo->colors[6] = ((c2>>0)&0xff)*kColorConversionMultiplier;
 896             qsdo->shadingInfo->colors[7] = ((c2>>24)&0xff)*kColorConversionMultiplier;
 897             qsdo->shadingInfo->cyclic    = (javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorIsCyclicIndex] == sun_java2d_OSXSurfaceData_kColorCyclic);
 898 
 899             break;
 900         }
 901         case sun_java2d_OSXSurfaceData_kColorTexture:
 902         {
 903             qsdo->patternInfo = (StatePatternInfo*)malloc(sizeof(StatePatternInfo));
 904             if (qsdo->patternInfo == NULL)
 905             {
 906                 [JNFException raise:env as:kOutOfMemoryError reason:"Failed to malloc memory for texture paint"];
 907             }
 908 
 909             qsdo->graphicsStateInfo.simpleStroke = NO;
 910             qsdo->graphicsStateInfo.simpleColor = NO;
 911 
 912             renderType = SD_Pattern;
 913 
 914             qsdo->patternInfo->tx        = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColortxIndex];
 915             qsdo->patternInfo->ty        = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColortyIndex];
 916             qsdo->patternInfo->sx        = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColorsxIndex];
 917             if (qsdo->patternInfo->sx == 0.0f)
 918             {
 919                 return SD_Fill; // 0 is an invalid value, fill argb rect
 920             }
 921             qsdo->patternInfo->sy        = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColorsyIndex];
 922             if (qsdo->patternInfo->sy == 0.0f)
 923             {
 924                 return SD_Fill; // 0 is an invalid value, fill argb rect
 925             }
 926             qsdo->patternInfo->width    = javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorWidthIndex];
 927             qsdo->patternInfo->height    = javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorHeightIndex];
 928 
 929             jobject sData = ((*env)->GetObjectArrayElement(env, qsdo->javaGraphicsStatesObjects, sun_java2d_OSXSurfaceData_kTextureImageIndex)); //deleted next time through SetUpPaint and not before ( radr://3913190 )
 930             if (sData != NULL)
 931             {
 932                 qsdo->patternInfo->sdata = (*env)->NewGlobalRef(env, sData);
 933                 if (qsdo->patternInfo->sdata == NULL)
 934                 {
 935                     renderType = SD_Fill;
 936                 }
 937             }
 938             else
 939             {
 940                 renderType = SD_Fill;
 941             }
 942 
 943             break;
 944         }
 945     }
 946 
 947     return renderType;
 948 }
 949 
 950 #pragma mark
 951 #pragma mark --- Shape Drawing Code ---
 952 
 953 SDRenderType DoShapeUsingCG(CGContextRef cgRef, jint *types, jfloat *coords, jint numtypes, BOOL fill, CGFloat offsetX, CGFloat offsetY)
 954 {
 955 //fprintf(stderr, "DoShapeUsingCG fill=%d\n", (jint)fill);
 956     SDRenderType renderType = SD_Nothing;
 957 
 958     if (gAdjustForJavaDrawing != YES)
 959     {
 960         offsetX = 0.0f;
 961         offsetY = 0.0f;
 962     }
 963 
 964     if (fill == YES)
 965     {
 966         renderType = SD_Fill;
 967     }
 968     else
 969     {
 970         renderType = SD_Stroke;
 971     }
 972 
 973     if (numtypes > 0)
 974     {
 975         BOOL needNewSubpath = NO;
 976 
 977         CGContextBeginPath(cgRef); // create new path
 978 //fprintf(stderr, "    CGContextBeginPath\n");
 979 
 980         jint index = 0;
 981         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;
 982         jint i;
 983 
 984         mx = (CGFloat)coords[index++] + offsetX;
 985         my = (CGFloat)coords[index++] + offsetY;
 986         CGContextMoveToPoint(cgRef, mx, my);
 987 
 988         for (i=1; i<numtypes; i++)
 989         {
 990             jint pathType = types[i];
 991 
 992             if (needNewSubpath == YES)
 993             {
 994                 needNewSubpath = NO;
 995                 switch (pathType)
 996                 {
 997                     case java_awt_geom_PathIterator_SEG_LINETO:
 998                     case java_awt_geom_PathIterator_SEG_QUADTO:
 999                     case java_awt_geom_PathIterator_SEG_CUBICTO:
1000 //fprintf(stderr, "    forced CGContextMoveToPoint (%f, %f)\n", mx, my);
1001                         CGContextMoveToPoint(cgRef, mx, my); // force new subpath
1002                         break;
1003                 }
1004             }
1005 
1006             switch (pathType)
1007             {
1008                 case java_awt_geom_PathIterator_SEG_MOVETO:
1009                     mx = x1 = (CGFloat)coords[index++] + offsetX;
1010                     my = y1 = (CGFloat)coords[index++] + offsetY;
1011                     CGContextMoveToPoint(cgRef, x1, y1); // start new subpath
1012 //fprintf(stderr, "    SEG_MOVETO CGContextMoveToPoint (%f, %f)\n", x1, y1);
1013                     break;
1014                 case java_awt_geom_PathIterator_SEG_LINETO:
1015                     x1 = (CGFloat)coords[index++] + offsetX;
1016                     y1 = (CGFloat)coords[index++] + offsetY;
1017                     CGContextAddLineToPoint(cgRef, x1, y1);
1018 //fprintf(stderr, "    SEG_LINETO CGContextAddLineToPoint (%f, %f)\n", x1, y1);
1019                     break;
1020                 case java_awt_geom_PathIterator_SEG_QUADTO:
1021                     cpx1 = (CGFloat)coords[index++] + offsetX;
1022                     cpy1 = (CGFloat)coords[index++] + offsetY;
1023                     x1 = (CGFloat)coords[index++] + offsetX;
1024                     y1 = (CGFloat)coords[index++]+ offsetY;
1025                     CGContextAddQuadCurveToPoint(cgRef, cpx1, cpy1, x1, y1);
1026 //fprintf(stderr, "    SEG_QUADTO CGContextAddQuadCurveToPoint (%f, %f), (%f, %f)\n", cpx1, cpy1, x1, y1);
1027                     break;
1028                 case java_awt_geom_PathIterator_SEG_CUBICTO:
1029                     cpx1 = (CGFloat)coords[index++] + offsetX;
1030                     cpy1 = (CGFloat)coords[index++] + offsetY;
1031                     cpx2 = (CGFloat)coords[index++] + offsetX;
1032                     cpy2 = (CGFloat)coords[index++] + offsetY;
1033                     x1 = (CGFloat)coords[index++] + offsetX;
1034                     y1 = (CGFloat)coords[index++] + offsetY;
1035                     CGContextAddCurveToPoint(cgRef, cpx1, cpy1, cpx2, cpy2, x1, y1);
1036 //fprintf(stderr, "    SEG_CUBICTO CGContextAddCurveToPoint (%f, %f), (%f, %f), (%f, %f)\n", cpx1, cpy1, cpx2, cpy2, x1, y1);
1037                     break;
1038                 case java_awt_geom_PathIterator_SEG_CLOSE:
1039                     CGContextClosePath(cgRef); // close subpath
1040                     needNewSubpath = YES;
1041 //fprintf(stderr, "    SEG_CLOSE CGContextClosePath\n");
1042                     break;
1043             }
1044         }
1045     }
1046 
1047     return renderType;
1048 }
1049 
1050 void CompleteCGContext(JNIEnv *env, QuartzSDOps *qsdo)
1051 {
1052 PRINT(" CompleteCGContext")
1053     switch (qsdo->renderType)
1054     {
1055         case SD_Nothing:
1056             break;
1057 
1058         case SD_Stroke:
1059             if (CGContextIsPathEmpty(qsdo->cgRef) == 0)
1060             {
1061                 CGContextStrokePath(qsdo->cgRef);
1062             }
1063             break;
1064 
1065         case SD_Fill:
1066             if (CGContextIsPathEmpty(qsdo->cgRef) == 0)
1067             {
1068                 CGContextFillPath(qsdo->cgRef);
1069             }
1070             break;
1071 
1072         case SD_Shade:
1073             if (CGContextIsPathEmpty(qsdo->cgRef) == 0)
1074             {
1075                 contextGradientPath(qsdo);
1076             }
1077             break;
1078 
1079         case SD_Pattern:
1080             if (CGContextIsPathEmpty(qsdo->cgRef) == 0)
1081             {
1082                 //TODO:BG
1083                 //contextTexturePath(env, qsdo);
1084             }
1085             break;
1086 
1087         case SD_EOFill:
1088             if (CGContextIsPathEmpty(qsdo->cgRef) == 0)
1089             {
1090                 CGContextEOFillPath(qsdo->cgRef);
1091             }
1092             break;
1093 
1094         case SD_Image:
1095             break;
1096 
1097         case SD_Text:
1098             break;
1099 
1100         case SD_CopyArea:
1101             break;
1102 
1103         case SD_Queue:
1104             break;
1105 
1106         case SD_External:
1107             break;
1108     }
1109 
1110     if (qsdo->shadingInfo != NULL) {
1111         gradientPaintReleaseFunction(qsdo->shadingInfo);
1112         qsdo->shadingInfo = NULL;
1113     }
1114 }