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