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 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 }