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