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 }