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