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 "java_awt_image_BufferedImage.h"
27#import "java_awt_geom_PathIterator.h"
28#import "sun_java2d_OSXSurfaceData.h"
29
30#import <stdio.h>
31#import <JavaNativeFoundation/JavaNativeFoundation.h>
32
33#import "ImageSurfaceData.h"
34
35
36//#define DEBUG 1
37#if defined DEBUG
38    #define QUARTZ_RENDERER_INLINE
39    #define PRINT(msg) {fprintf(stderr, "%s\n", msg);fflush(stderr);}
40#else
41    #define QUARTZ_RENDERER_INLINE static inline
42    #define PRINT(msg) {}
43#endif
44
45// Copied the following from Math.java
46#define PI 3.14159265358979323846f
47
48#define BATCHED_POINTS_SIZE 1024
49
50// same value as defined in Sun's own code
51#define XOR_ALPHA_CUTOFF 128
52
53
54static CGFloat gRoundRectCtrlpts[10][12] =
55{
56    {0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
57    {0.0f, 0.0f, 1.0f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
58    {0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, 1.0f, 0.0f},
59    {1.0f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
60    {1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, -0.5f},
61    {1.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
62    {1.0f, 0.0f, 0.0f, 0.0f,  1.0f, 0.0f, 0.0f, 0.0f, 1.0f, -0.5f, 0.0f, 0.0f},
63    {0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
64    {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f},
65    {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
66};
67
68CG_EXTERN CGRect CGRectApplyAffineTransform(CGRect rect, CGAffineTransform t);
69
70
71CGRect sanitizedRect(CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2) {
72    CGFloat temp;
73    if (x1 > x2) {
74        temp = x2;
75        x2 = x1;
76        x1 = temp;
77    }
78    if (y1 > y2) {
79        temp = y2;
80        y2 = y1;
81        y1 = temp;
82    }
83    return CGRectMake(x1, y1, x2-x1, y2-y1);
84}
85
86QUARTZ_RENDERER_INLINE SDRenderType doLineUsingCG(CGContextRef cgRef, CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2, BOOL simple, CGFloat offsetX, CGFloat offsetY)
87{
88//fprintf(stderr, "doLine start=(%f, %f), end=(%f, %f), linewidth:%f, offsetX:%f, offsetY:%f\n", x1, y1, x2, y2, CGContextGetLineWidth(cgRef), offsetX, offsetY);
89    SDRenderType renderType = SD_Nothing;
90
91    if (simple == YES)
92    {
93        struct CGPoint oneLinePoints[2];
94
95        oneLinePoints[0] = CGPointMake(x1+offsetX, y1+offsetY);
96        oneLinePoints[1] = CGPointMake(x2+offsetX, y2+offsetY);
97
98        CGContextStrokeLineSegments(cgRef, oneLinePoints, 2);
99        renderType = SD_Nothing;
100    }
101    else
102    {
103        CGContextMoveToPoint(cgRef, x1+offsetX, y1+offsetY);
104        CGContextAddLineToPoint(cgRef, x2+offsetX, y2+offsetY);
105        renderType = SD_Stroke;
106    }
107
108    return renderType;
109}
110QUARTZ_RENDERER_INLINE SDRenderType doLine(QuartzSDOps *qsdo, CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2)
111{
112PRINT(" doLine")
113    if (YES)
114    {
115        return doLineUsingCG(qsdo->cgRef, x1, y1, x2, y2,
116                                qsdo->graphicsStateInfo.simpleStroke, qsdo->graphicsStateInfo.offsetX, qsdo->graphicsStateInfo.offsetY);
117    }
118    // here we can add other implementations (ex. using QuickDraw, OpenGL, etc.)
119}
120
121
122QUARTZ_RENDERER_INLINE SDRenderType doRectUsingCG(CGContextRef cgRef, CGFloat x, CGFloat y, CGFloat w, CGFloat h, BOOL fill, BOOL simple, CGFloat offsetX, CGFloat offsetY)
123{
124//fprintf(stderr, "doRect point=(%f, %f), size=(%f, %f), offsets=(%f, %f) fill=%d simple=%d\n", x, y, w, h, offsetX, offsetY, fill, simple);
125//CGRect clip = CGContextGetClipBoundingBox(cgRef);
126//fprintf(stderr, "    clip: ((%f, %f), (%f, %f))\n", clip.origin.x, clip.origin.y, clip.size.width, clip.size.height);
127//CGAffineTransform ctm = CGContextGetCTM(cgRef);
128//fprintf(stderr, "    ctm: (%f, %f, %f, %f, %f, %f)\n", ctm.a, ctm.b, ctm.c, ctm.d, ctm.tx, ctm.ty);
129    SDRenderType renderType = SD_Nothing;
130
131    if (fill == YES)
132    {
133        if (simple == YES)
134        {
135            CGContextFillRect(cgRef, CGRectMake(x, y, w, h));
136            renderType = SD_Nothing;
137        }
138        else
139        {
140            CGContextAddRect(cgRef, CGRectMake(x, y, w, h));
141            renderType = SD_Fill;
142        }
143    }
144    else
145    {
146        if (simple == YES)
147        {
148            CGContextStrokeRect(cgRef, CGRectMake(x+offsetX, y+offsetY, w, h));
149            renderType = SD_Nothing;
150        }
151        else
152        {
153            CGContextAddRect(cgRef, CGRectMake(x+offsetX, y+offsetY, w, h));
154            renderType = SD_Stroke;
155        }
156    }
157
158    return renderType;
159}
160QUARTZ_RENDERER_INLINE SDRenderType doRect(QuartzSDOps *qsdo, CGFloat x, CGFloat y, CGFloat w, CGFloat h, BOOL fill)
161{
162PRINT(" doRect")
163    if (YES)
164    {
165        return doRectUsingCG(qsdo->cgRef, x, y, w, h, fill,
166                                qsdo->graphicsStateInfo.simpleStroke, qsdo->graphicsStateInfo.offsetX, qsdo->graphicsStateInfo.offsetY);
167    }
168    // here we can add other implementations (ex. using QuickDraw, OpenGL, etc.)
169}
170
171// from RoundRectIterator.java
172QUARTZ_RENDERER_INLINE SDRenderType doRoundRectUsingCG(CGContextRef cgRef, CGFloat x, CGFloat y, CGFloat w, CGFloat h, CGFloat arcWidth, CGFloat arcHeight, BOOL fill, CGFloat offsetX, CGFloat offsetY)
173{
174    SDRenderType renderType = SD_Nothing;
175
176    if (fill == YES)
177    {
178        renderType = SD_Fill;
179    }
180    else
181    {
182        renderType = SD_Stroke;
183    }
184
185    // radr://3593731 RoundRects with corner width/height of 0 don't draw
186    arcWidth = (arcWidth > 0.0f) ? arcWidth : 0.0f;
187    arcHeight = (arcHeight > 0.0f) ? arcHeight : 0.0f;
188
189    CGFloat aw = (w < arcWidth) ? w : arcWidth;
190    CGFloat ah = (h < arcHeight) ? h : arcHeight;
191
192    CGFloat *ctrls, p1, q1, p2, q2, p3, q3;
193    ctrls = gRoundRectCtrlpts[0];
194    p1 = (x + ctrls[0] * w + ctrls[1] * aw);
195    q1 = (y + ctrls[2] * h + ctrls[3] * ah);
196    CGContextMoveToPoint(cgRef, p1+offsetX, q1+offsetY);
197
198    ctrls = gRoundRectCtrlpts[1];
199    p1 = (x + ctrls[0] * w + ctrls[1] * aw);
200    q1 = (y + ctrls[2] * h + ctrls[3] * ah);
201    CGContextAddLineToPoint(cgRef, p1+offsetX, q1+offsetY);
202
203    ctrls = gRoundRectCtrlpts[2];
204    p1 = (x + ctrls[0] * w + ctrls[1] * aw);
205    q1 = (y + ctrls[2] * h + ctrls[3] * ah);
206    p2 = (x + ctrls[4] * w + ctrls[5] * aw);
207    q2 = (y + ctrls[6] * h + ctrls[7] * ah);
208    p3 = (x + ctrls[8] * w + ctrls[9] * aw);
209    q3 = (y + ctrls[10] * h + ctrls[11] * ah);
210    CGContextAddCurveToPoint(cgRef, p1+offsetX, q1+offsetY, p2+offsetX, q2+offsetY, p3+offsetX, q3+offsetY);
211
212    ctrls = gRoundRectCtrlpts[3];
213    p1 = (x + ctrls[0] * w + ctrls[1] * aw);
214    q1 = (y + ctrls[2] * h + ctrls[3] * ah);
215    CGContextAddLineToPoint(cgRef, p1+offsetX, q1+offsetY);
216
217    ctrls = gRoundRectCtrlpts[4];
218    p1 = (x + ctrls[0] * w + ctrls[1] * aw);
219    q1 = (y + ctrls[2] * h + ctrls[3] * ah);
220    p2 = (x + ctrls[4] * w + ctrls[5] * aw);
221    q2 = (y + ctrls[6] * h + ctrls[7] * ah);
222    p3 = (x + ctrls[8] * w + ctrls[9] * aw);
223    q3 = (y + ctrls[10] * h + ctrls[11] * ah);
224    CGContextAddCurveToPoint(cgRef, p1+offsetX, q1+offsetY, p2+offsetX, q2+offsetY, p3+offsetX, q3+offsetY);
225
226    ctrls = gRoundRectCtrlpts[5];
227    p1 = (x + ctrls[0] * w + ctrls[1] * aw);
228    q1 = (y + ctrls[2] * h + ctrls[3] * ah);
229    CGContextAddLineToPoint(cgRef, p1+offsetX, q1+offsetY);
230
231    ctrls = gRoundRectCtrlpts[6];
232    p1 = (x + ctrls[0] * w + ctrls[1] * aw);
233    q1 = (y + ctrls[2] * h + ctrls[3] * ah);
234    p2 = (x + ctrls[4] * w + ctrls[5] * aw);
235    q2 = (y + ctrls[6] * h + ctrls[7] * ah);
236    p3 = (x + ctrls[8] * w + ctrls[9] * aw);
237    q3 = (y + ctrls[10] * h + ctrls[11] * ah);
238    CGContextAddCurveToPoint(cgRef, p1+offsetX, q1+offsetY, p2+offsetX, q2+offsetY, p3+offsetX, q3+offsetY);
239
240    ctrls = gRoundRectCtrlpts[7];
241    p1 = (x + ctrls[0] * w + ctrls[1] * aw);
242    q1 = (y + ctrls[2] * h + ctrls[3] * ah);
243    CGContextAddLineToPoint(cgRef, p1+offsetX, q1+offsetY);
244
245    ctrls = gRoundRectCtrlpts[8];
246    p1 = (x + ctrls[0] * w + ctrls[1] * aw);
247    q1 = (y + ctrls[2] * h + ctrls[3] * ah);
248    p2 = (x + ctrls[4] * w + ctrls[5] * aw);
249    q2 = (y + ctrls[6] * h + ctrls[7] * ah);
250    p3 = (x + ctrls[8] * w + ctrls[9] * aw);
251    q3 = (y + ctrls[10] * h + ctrls[11] * ah);
252    CGContextAddCurveToPoint(cgRef, p1+offsetX, q1+offsetY, p2+offsetX, q2+offsetY, p3+offsetX, q3+offsetY);
253
254    CGContextClosePath(cgRef);
255
256    return renderType;
257}
258
259QUARTZ_RENDERER_INLINE SDRenderType doRoundRect(QuartzSDOps *qsdo, CGFloat x, CGFloat y, CGFloat w, CGFloat h, CGFloat arcWidth, CGFloat arcHeight, BOOL fill)
260{
261PRINT(" doRoundRect")
262    if (YES)
263    {
264        return doRoundRectUsingCG(qsdo->cgRef, x, y, w, h, arcWidth, arcHeight, fill,
265                                    qsdo->graphicsStateInfo.offsetX, qsdo->graphicsStateInfo.offsetY);
266    }
267    // here we can add other implementations (ex. using QuickDraw, OpenGL, etc.)
268}
269
270// from EllipseIterator.java
271QUARTZ_RENDERER_INLINE SDRenderType doOvalUsingCG(CGContextRef cgRef, CGFloat x, CGFloat y, CGFloat w, CGFloat h, BOOL fill, BOOL simple, CGFloat offsetX, CGFloat offsetY)
272{
273    SDRenderType renderType = SD_Nothing;
274
275    if (simple == YES)
276    {
277        if (fill == YES)
278        {
279            CGContextFillEllipseInRect(cgRef, CGRectMake(x+offsetX, y+offsetY, w, h));
280        }
281        else
282        {
283            CGContextStrokeEllipseInRect(cgRef, CGRectMake(x+offsetX, y+offsetY, w, h));
284        }
285    }
286    else
287    {
288        if (fill == YES)
289        {
290            renderType = SD_Fill;
291        }
292        else
293        {
294            renderType = SD_Stroke;
295        }
296
297        CGContextAddEllipseInRect(cgRef, CGRectMake(x+offsetX, y+offsetY, w, h));
298    }
299
300    return renderType;
301}
302QUARTZ_RENDERER_INLINE SDRenderType doOval(QuartzSDOps *qsdo, CGFloat x, CGFloat y, CGFloat w, CGFloat h, BOOL fill)
303{
304PRINT(" doOval")
305    if (YES)
306    {
307        return doOvalUsingCG(qsdo->cgRef, x, y, w, h, fill,
308                                qsdo->graphicsStateInfo.simpleStroke, qsdo->graphicsStateInfo.offsetX, qsdo->graphicsStateInfo.offsetY);
309    }
310    // here we can add other implementations (ex. using QuickDraw, OpenGL, etc.)
311}
312
313// from ArcIterator.java
314QUARTZ_RENDERER_INLINE CGFloat btan(CGFloat increment)
315{
316    increment /= 2.0f;
317    CGFloat a = 1.0f - cos(increment);
318    CGFloat b = tan(increment);
319    CGFloat c = sqrt(1.0f + b * b) - 1.0f + a;
320
321    return 4.0f / 3.0f * a * b / c;
322}
323QUARTZ_RENDERER_INLINE SDRenderType doArcUsingCG(CGContextRef cgRef, CGFloat x, CGFloat y, CGFloat w, CGFloat h, CGFloat angleStart, CGFloat angleExtent, jint arcType, BOOL fill, CGFloat offsetX, CGFloat offsetY)
324{
325//fprintf(stderr, "doArc\n");
326    SDRenderType renderType = SD_Nothing;
327
328    if (fill == YES)
329    {
330        renderType = SD_Fill;
331    }
332    else
333    {
334        renderType = SD_Stroke;
335    }
336
337    CGFloat angStRad, angExtDeg;
338    jint arcSegs;
339    jint lineSegs;
340    jint index = 1;
341
342    w = w / 2.0f;
343    h = h / 2.0f;
344    x = x + w;
345    y = y + h;
346    angStRad = -(angleStart / 180.0f * PI);
347    angExtDeg = -angleExtent;
348    CGFloat ext = (angExtDeg>0) ? angExtDeg : -angExtDeg;
349    if (ext >= 360.0f)
350    {
351        arcSegs = 4;
352    }
353    else
354    {
355        arcSegs = (jint)ceil(ext/90.0f);
356    }
357    switch (arcType)
358    {
359        case 0:
360            lineSegs = 0;
361            break;
362        case 1:
363            lineSegs = 1;
364            break;
365        case 2:
366            lineSegs = 2;
367            break;
368    }
369    if (w < 0 || h < 0)
370    {
371        arcSegs = lineSegs = -1;
372    }
373
374    CGFloat angle = angStRad;
375    CGContextMoveToPoint(cgRef, (x + cos(angle) * w)+offsetX, (y + sin(angle) * h)+offsetY);
376
377    CGFloat increment = angExtDeg;
378    if (increment > 360.0f)
379    {
380        increment = 360.0f;
381    }
382    else if (increment < -360.0f)
383    {
384        increment = -360.0f;
385    }
386    increment /= arcSegs;
387    increment = (increment / 180.0f * PI);
388    CGFloat z = btan(increment);
389    CGFloat angleBase = angle;
390    CGFloat p1, q1, p2, q2, p3, q3;
391    while (index <= arcSegs)
392    {
393        angle = angleBase + increment * (index - 1);
394        CGFloat relx = cos(angle);
395        CGFloat rely = sin(angle);
396        p1 = (x + (relx - z * rely) * w);
397        q1 = (y + (rely + z * relx) * h);
398        angle += increment;
399        relx = cos(angle);
400        rely = sin(angle);
401        p2 = (x + (relx + z * rely) * w);
402        q2 = (y + (rely - z * relx) * h);
403        p3 = (x + relx * w);
404        q3 = (y + rely * h);
405
406        CGContextAddCurveToPoint(cgRef, p1+offsetX, q1+offsetY, p2+offsetX, q2+offsetY, p3+offsetX, q3+offsetY);
407
408        index++;
409    }
410
411    switch (arcType)
412    {
413        case 1:
414            CGContextClosePath(cgRef);
415            break;
416        case 2:
417            CGContextAddLineToPoint(cgRef, x+offsetX, y+offsetY);
418            CGContextClosePath(cgRef);
419            break;
420        default:
421            break;
422    }
423
424    return renderType;
425}
426QUARTZ_RENDERER_INLINE SDRenderType doArc(QuartzSDOps *qsdo, CGFloat x, CGFloat y, CGFloat w, CGFloat h, CGFloat angleStart, CGFloat angleExtent, jint arcType, BOOL fill)
427{
428PRINT(" doArc")
429    if (YES)
430    {
431        return doArcUsingCG(qsdo->cgRef, x, y, w, h, angleStart, angleExtent, arcType, fill,
432                                qsdo->graphicsStateInfo.offsetX, qsdo->graphicsStateInfo.offsetY);
433    }
434    // here we can add other implementations (ex. using QuickDraw, OpenGL, etc.)
435}
436
437QUARTZ_RENDERER_INLINE SDRenderType doPolyUsingCG(JNIEnv *env, CGContextRef cgRef, jintArray xpointsarray, jintArray ypointsarray, jint npoints, BOOL polygon, BOOL fill, CGFloat offsetX, CGFloat offsetY)
438{
439    SDRenderType renderType = SD_Nothing;
440
441    if (xpointsarray == NULL || ypointsarray == NULL) {
442        return SD_Nothing;
443    }
444    if (npoints > 1)
445    {
446        if (fill == YES)
447        {
448            renderType = SD_Fill;
449        }
450        else
451        {
452            renderType = SD_Stroke;
453        }
454
455        jint i;
456
457        jint* xpoints = (jint*)(*env)->GetPrimitiveArrayCritical(env, xpointsarray, NULL);
458        if (xpoints == NULL) {
459            return SD_Nothing;
460        }
461        jint* ypoints = (jint*)(*env)->GetPrimitiveArrayCritical(env, ypointsarray, NULL);
462        if (ypoints == NULL) {
463            (*env)->ReleasePrimitiveArrayCritical(env, xpointsarray, xpoints, 0);
464            return SD_Nothing;
465        }
466
467        CGContextMoveToPoint(cgRef, xpoints[0]+offsetX, ypoints[0]+offsetY);
468
469        for (i=1; i<npoints; i++)
470        {
471            CGContextAddLineToPoint(cgRef, xpoints[i]+offsetX, ypoints[i]+offsetY);
472        }
473
474        if (polygon == YES)
475        {
476            if ((xpoints[0] != xpoints[npoints-1]) || (ypoints[0] != ypoints[npoints-1])) // according to the specs (only applies to polygons, not polylines)
477            {
478                CGContextAddLineToPoint(cgRef, xpoints[0]+offsetX, ypoints[0]+offsetY);
479            }
480        }
481
482        (*env)->ReleasePrimitiveArrayCritical(env, ypointsarray, ypoints, 0);
483        (*env)->ReleasePrimitiveArrayCritical(env, xpointsarray, xpoints, 0);
484    }
485
486    return renderType;
487}
488QUARTZ_RENDERER_INLINE SDRenderType doPoly(JNIEnv *env, QuartzSDOps *qsdo, jintArray xpointsarray, jintArray ypointsarray, jint npoints, BOOL polygon, BOOL fill)
489{
490PRINT(" doPoly")
491    if (YES)
492    {
493        return doPolyUsingCG(env, qsdo->cgRef, xpointsarray, ypointsarray, npoints, polygon, fill,
494            qsdo->graphicsStateInfo.offsetX, qsdo->graphicsStateInfo.offsetY);
495    }
496    // here we can add other implementations (ex. using QuickDraw, OpenGL, etc.)
497}
498
499SDRenderType doShape(QuartzSDOps *qsdo, jint *types, jfloat *coords, jint numtypes, BOOL fill, BOOL shouldApplyOffset)
500{
501PRINT(" doShape")
502    if (YES)
503    {
504        CGFloat offsetX = 0.0f;
505        CGFloat offsetY = 0.0f;
506        if (shouldApplyOffset)
507        {
508            offsetX = qsdo->graphicsStateInfo.offsetX;
509            offsetY = qsdo->graphicsStateInfo.offsetY;
510        }
511        return DoShapeUsingCG(qsdo->cgRef, types, coords, numtypes, fill, offsetX, offsetY); // defined in QuartzSurfaceData.m
512    }
513    // here we can add other implementations (ex. using QuickDraw, OpenGL, etc.)
514}
515
516
517
518QUARTZ_RENDERER_INLINE void doImageCG(JNIEnv *env, CGContextRef cgRef, jobject imageSurfaceData,
519                                        jint interpolation, BOOL fliph, BOOL flipv, jint w, jint h, jint sx, jint sy, jint sw, jint sh, jint dx, jint dy, jint dw, jint dh)
520{
521//fprintf(stderr, "doImageCG\n");
522//fprintf(stderr, "    flip:(%d, %d), size:(%d, %d), src:(%d, %d, %d, %d), dst:(%d, %d, %d, %d)\n", (jint)fliph, (jint)flipv, w, h, sx, sy, sw, sh, dx, dy, dw, dh);
523    // gznote: need to handle interpolation
524    ImageSDOps* isdo = LockImage(env, imageSurfaceData);
525
526    CGFloat a = 1.0f;
527    CGFloat b = 0.0f;
528    CGFloat c = 0.0f;
529    CGFloat d = -1.0f;
530    CGFloat tx = dx;
531    CGFloat ty = dy+dh;
532
533    if (flipv == YES)
534    {
535        d = 1.0f;
536        ty -= dh;
537    }
538    if (fliph == YES)
539    {
540        a = -1.0f;
541        tx += dw;
542    }
543
544    makeSureImageIsCreated(isdo);
545
546    CGContextSaveGState(cgRef);
547    CGContextConcatCTM(cgRef, CGAffineTransformMake(a, b, c, d, tx, ty));
548    jint alphaInfo = isdo->contextInfo.alphaInfo & kCGBitmapAlphaInfoMask;
549
550    if ((sx == 0) && (sy == 0) && (sw == w) && (sh == h)) // no subimages allowed here
551    {
552        CGContextDrawImage(cgRef, CGRectMake(0, 0, dw, dh), isdo->imgRef);
553    }
554    else // handle subimages
555    {
556        CGImageRef subImg = CGImageCreateWithImageInRect(isdo->imgRef, CGRectMake(sx, sy, sw, sh));
557        CGContextDrawImage(cgRef, CGRectMake(0.0f, 0.0f, dw, dh), subImg);
558        CGImageRelease(subImg);
559    }
560
561    CGContextRestoreGState(cgRef);
562    UnlockImage(env, isdo);
563}
564
565QUARTZ_RENDERER_INLINE void doImage(JNIEnv *env, QuartzSDOps *qsdo, jobject imageSurfaceData,
566                                jboolean fliph, jboolean flipv, jint w, jint h, jint sx, jint sy, jint sw, jint sh, jint dx, jint dy, jint dw, jint dh)
567{
568    if ((w > 0) && (h > 0) && (sw > 0) && (sh > 0) && (dw > 0) && (dh > 0))
569    {
570       doImageCG(env, qsdo->cgRef, imageSurfaceData,
571                            qsdo->graphicsStateInfo.interpolation, (BOOL)fliph, (BOOL)flipv, (jint)w, (jint)h, (jint)sx, (jint)sy, (jint)sw, (jint)sh, (jint)dx, (jint)dy, (jint)dw, (jint)dh);
572    }
573}
574
575
576
577QUARTZ_RENDERER_INLINE void completePath(JNIEnv *env, QuartzSDOps *qsdo, CGContextRef cgRef, jint renderType)
578{
579    switch (renderType)
580    {
581        case SD_Stroke:
582            if (CGContextIsPathEmpty(cgRef) == 0)
583            {
584                CGContextStrokePath(cgRef);
585            }
586            break;
587        case SD_Fill:
588            if (CGContextIsPathEmpty(cgRef) == 0)
589            {
590                CGContextFillPath(cgRef);
591            }
592            break;
593        case SD_Image:
594            break;
595        case SD_Nothing:
596                break;
597        default:
598fprintf(stderr, "completePath unknown renderType=%d\n", (int)renderType);
599            break;
600    }
601}
602
603/*
604 * Class:     sun_java2d_CRenderer
605 * Method:    init
606 * Signature: ()V
607 */
608JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_init
609(JNIEnv *env, jobject jthis)
610{
611PRINT("Java_sun_java2d_CRenderer_init")
612    CGFloat angle = PI / 4.0f;
613    CGFloat a = 1.0f - cos(angle);
614    CGFloat b = tan(angle);
615    CGFloat c = sqrt(1.0f + b * b) - 1.0f + a;
616    CGFloat cv = 4.0f / 3.0f * a * b / c;
617    CGFloat acv = (1.0f - cv) / 2.0f;
618
619    gRoundRectCtrlpts[2][3] = -acv;
620    gRoundRectCtrlpts[2][5] = acv;
621    gRoundRectCtrlpts[4][1] = -acv;
622    gRoundRectCtrlpts[4][7] = -acv;
623    gRoundRectCtrlpts[6][3] = acv;
624    gRoundRectCtrlpts[6][5] = -acv;
625    gRoundRectCtrlpts[8][1] = acv;
626    gRoundRectCtrlpts[8][7] = acv;
627}
628
629/*
630 * Class:     sun_java2d_CRenderer
631 * Method:    doLine
632 * Signature: (Lsun/java2d/SurfaceData;Ljava/nio/IntBuffer;Ljava/nio/FloatBuffer;[Ljava/lang/Object;FFFF)V
633 */
634JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doLine
635(JNIEnv *env, jobject jthis, jobject jsurfacedata, jfloat x1, jfloat y1, jfloat x2, jfloat y2)
636{
637PRINT("Java_sun_java2d_CRenderer_doLine")
638    QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
639JNF_COCOA_ENTER(env);
640    SDRenderType renderType = SD_Stroke;
641    qsdo->BeginSurface(env, qsdo, renderType);
642    if (qsdo->cgRef != NULL)
643    {
644        doLine(qsdo, x1, y1, x2, y2);
645    }
646    qsdo->FinishSurface(env, qsdo);
647JNF_COCOA_RENDERER_EXIT(env);
648}
649
650/*
651 * Class:     sun_java2d_CRenderer
652 * Method:    doRect
653 * Signature: (Lsun/java2d/SurfaceData;Ljava/nio/IntBuffer;Ljava/nio/FloatBuffer;[Ljava/lang/Object;FFFF)V
654 */
655JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doRect
656(JNIEnv *env, jobject jthis, jobject jsurfacedata, jfloat x, jfloat y, jfloat w, jfloat h, jboolean isfill)
657{
658PRINT("Java_sun_java2d_CRenderer_doRect")
659    QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
660JNF_COCOA_ENTER(env);
661    SDRenderType renderType    = (isfill? SD_Fill : SD_Stroke);
662    qsdo->BeginSurface(env, qsdo, renderType);
663    if (qsdo->cgRef != NULL)
664    {
665        doRect(qsdo, x, y, w, h, isfill);
666    }
667    qsdo->FinishSurface(env, qsdo);
668JNF_COCOA_RENDERER_EXIT(env);
669}
670
671/*
672 * Class:     sun_java2d_CRenderer
673 * Method:    doRoundRect
674 * Signature: (Lsun/java2d/SurfaceData;Ljava/nio/IntBuffer;Ljava/nio/FloatBuffer;[Ljava/lang/Object;IIIIII)V
675 */
676JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doRoundRect
677(JNIEnv *env, jobject jthis, jobject jsurfacedata, jfloat x, jfloat y, jfloat w, jfloat h, jfloat arcWidth, jfloat arcHeight, jboolean isfill)
678{
679PRINT("Java_sun_java2d_CRenderer_doRoundRect")
680    QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
681JNF_COCOA_ENTER(env);
682    SDRenderType renderType    = (isfill? SD_Fill : SD_Stroke);
683    qsdo->BeginSurface(env, qsdo, renderType);
684    if (qsdo->cgRef != NULL)
685    {
686        doRoundRect(qsdo, x, y, w, h, arcWidth, arcHeight, isfill);
687    }
688    qsdo->FinishSurface(env, qsdo);
689JNF_COCOA_RENDERER_EXIT(env);
690}
691
692/*
693 * Class:     sun_java2d_CRenderer
694 * Method:    doOval
695 * Signature: (Lsun/java2d/SurfaceData;Ljava/nio/IntBuffer;Ljava/nio/FloatBuffer;[Ljava/lang/Object;IIII)V
696 */
697JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doOval
698(JNIEnv *env, jobject jthis, jobject jsurfacedata, jfloat x, jfloat y, jfloat w, jfloat h, jboolean isfill)
699{
700PRINT("Java_sun_java2d_CRenderer_doOval")
701    QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
702JNF_COCOA_ENTER(env);
703    SDRenderType renderType    = (isfill? SD_Fill : SD_Stroke);
704    qsdo->BeginSurface(env, qsdo, renderType);
705    if (qsdo->cgRef != NULL)
706    {
707        doOval(qsdo, x, y, w, h, isfill);
708    }
709    qsdo->FinishSurface(env, qsdo);
710JNF_COCOA_RENDERER_EXIT(env);
711}
712
713/*
714 * Class:     sun_java2d_CRenderer
715 * Method:    doArc
716 * Signature: (Lsun/java2d/SurfaceData;Ljava/nio/IntBuffer;Ljava/nio/FloatBuffer;[Ljava/lang/Object;IIIIII)V
717 */
718JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doArc
719(JNIEnv *env, jobject jthis, jobject jsurfacedata, jfloat x, jfloat y, jfloat w, jfloat h, jfloat angleStart, jfloat angleExtent, jint arcType, jboolean isfill)
720{
721PRINT("Java_sun_java2d_CRenderer_doArc")
722    QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
723JNF_COCOA_ENTER(env);
724    SDRenderType renderType    = (isfill? SD_Fill : SD_Stroke);
725    qsdo->BeginSurface(env, qsdo, renderType);
726    if (qsdo->cgRef != NULL)
727    {
728        doArc(qsdo, x, y, w, h, angleStart, angleExtent, arcType, isfill);
729    }
730    qsdo->FinishSurface(env, qsdo);
731JNF_COCOA_RENDERER_EXIT(env);
732}
733
734/*
735 * Class:     sun_java2d_CRenderer
736 * Method:    doPoly
737 * Signature:
738 */
739JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doPoly
740(JNIEnv *env, jobject jthis, jobject jsurfacedata, jintArray xpointsarray, jintArray ypointsarray, jint npoints, jboolean ispolygon, jboolean isfill)
741{
742PRINT("Java_sun_java2d_CRenderer_doPoly")
743    QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
744JNF_COCOA_ENTER(env);
745    BOOL eoFill = YES; // polys are WIND_EVEN_ODD by definition
746    SDRenderType renderType    = (isfill? (eoFill ? SD_EOFill : SD_Fill) : SD_Stroke);
747    qsdo->BeginSurface(env, qsdo, renderType);
748    if (qsdo->cgRef != NULL)
749    {
750        doPoly(env, qsdo, xpointsarray, ypointsarray, npoints, ispolygon, isfill);
751    }
752    qsdo->FinishSurface(env, qsdo);
753JNF_COCOA_RENDERER_EXIT(env);
754}
755
756/*
757 * Class:     sun_java2d_CRenderer
758 * Method:    doShape
759 * Signature: (Lsun/java2d/SurfaceData;Ljava/nio/IntBuffer;Ljava/nio/FloatBuffer;[Ljava/lang/Object;ILjava/nio/FloatBuffer;Ljava/nio/IntBuffer;IZ)V
760 */
761JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doShape
762(JNIEnv *env, jobject jthis, jobject jsurfacedata, jint length, jobject jFloatCoordinates, jobject jIntTypes, jint windingRule, jboolean isfill, jboolean shouldApplyOffset)
763{
764PRINT("Java_sun_java2d_CRenderer_doShape")
765    QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
766JNF_COCOA_ENTER(env);
767    BOOL eoFill = (windingRule == java_awt_geom_PathIterator_WIND_EVEN_ODD);
768    SDRenderType renderType    = (isfill? (eoFill ? SD_EOFill : SD_Fill) : SD_Stroke);
769    qsdo->BeginSurface(env, qsdo, renderType);
770    if (qsdo->cgRef != NULL)
771    {
772        jfloat *coordinates = (jfloat*)((*env)->GetDirectBufferAddress(env, jFloatCoordinates));
773        jint *types = (jint*)((*env)->GetDirectBufferAddress(env, jIntTypes));
774        doShape(qsdo, types, coordinates, length, isfill, shouldApplyOffset);
775    }
776    qsdo->FinishSurface(env, qsdo);
777JNF_COCOA_RENDERER_EXIT(env);
778}
779
780#define invalidContext(c) \
781    ((c) == NULL /* || (c)->identifer != CGContextIdentifier */)
782
783/*
784 * Class:     sun_java2d_CRenderer
785 * Method:    doImage
786 * Signature: (Lsun/java2d/SurfaceData;Ljava/nio/IntBuffer;Ljava/nio/FloatBuffer;[Ljava/lang/Object;Lsun/java2d/SurfaceData;ZZIIIIIIII)V
787 */
788JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doImage
789(JNIEnv *env, jobject jthis, jobject jsurfacedata, jobject imageSurfaceData, jboolean fliph, jboolean flipv, jint w, jint h, jint sx, jint sy, jint sw, jint sh, jint dx, jint dy, jint dw, jint dh)
790{
791PRINT("Java_sun_java2d_CRenderer_doImage")
792    QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
793JNF_COCOA_ENTER(env);
794    qsdo->BeginSurface(env, qsdo, SD_Image);
795    if (qsdo->cgRef != NULL)
796    {
797        doImage(env, qsdo, imageSurfaceData, fliph, flipv, w, h, sx, sy, sw, sh, dx, dy, dw, dh);
798    }
799    qsdo->FinishSurface(env, qsdo);
800JNF_COCOA_RENDERER_EXIT(env);
801}
802