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 
  54 static 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 
  68 CG_EXTERN CGRect CGRectApplyAffineTransform(CGRect rect, CGAffineTransform t);
  69 
  70 
  71 CGRect 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 
  86 QUARTZ_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 }
 110 QUARTZ_RENDERER_INLINE SDRenderType doLine(QuartzSDOps *qsdo, CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2)
 111 {
 112 PRINT(" 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 
 122 QUARTZ_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 }
 160 QUARTZ_RENDERER_INLINE SDRenderType doRect(QuartzSDOps *qsdo, CGFloat x, CGFloat y, CGFloat w, CGFloat h, BOOL fill)
 161 {
 162 PRINT(" 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
 172 QUARTZ_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 
 259 QUARTZ_RENDERER_INLINE SDRenderType doRoundRect(QuartzSDOps *qsdo, CGFloat x, CGFloat y, CGFloat w, CGFloat h, CGFloat arcWidth, CGFloat arcHeight, BOOL fill)
 260 {
 261 PRINT(" 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
 271 QUARTZ_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 }
 302 QUARTZ_RENDERER_INLINE SDRenderType doOval(QuartzSDOps *qsdo, CGFloat x, CGFloat y, CGFloat w, CGFloat h, BOOL fill)
 303 {
 304 PRINT(" 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
 314 QUARTZ_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 }
 323 QUARTZ_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 }
 426 QUARTZ_RENDERER_INLINE SDRenderType doArc(QuartzSDOps *qsdo, CGFloat x, CGFloat y, CGFloat w, CGFloat h, CGFloat angleStart, CGFloat angleExtent, jint arcType, BOOL fill)
 427 {
 428 PRINT(" 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 
 437 QUARTZ_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 }
 488 QUARTZ_RENDERER_INLINE SDRenderType doPoly(JNIEnv *env, QuartzSDOps *qsdo, jintArray xpointsarray, jintArray ypointsarray, jint npoints, BOOL polygon, BOOL fill)
 489 {
 490 PRINT(" 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 
 499 SDRenderType doShape(QuartzSDOps *qsdo, jint *types, jfloat *coords, jint numtypes, BOOL fill, BOOL shouldApplyOffset)
 500 {
 501 PRINT(" 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 
 518 QUARTZ_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 
 565 QUARTZ_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 
 577 QUARTZ_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:
 598 fprintf(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  */
 608 JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_init
 609 (JNIEnv *env, jobject jthis)
 610 {
 611 PRINT("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  */
 634 JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doLine
 635 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jfloat x1, jfloat y1, jfloat x2, jfloat y2)
 636 {
 637 PRINT("Java_sun_java2d_CRenderer_doLine")
 638     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 639 JNF_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);
 647 JNF_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  */
 655 JNIEXPORT 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 {
 658 PRINT("Java_sun_java2d_CRenderer_doRect")
 659     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 660 JNF_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);
 668 JNF_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  */
 676 JNIEXPORT 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 {
 679 PRINT("Java_sun_java2d_CRenderer_doRoundRect")
 680     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 681 JNF_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);
 689 JNF_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  */
 697 JNIEXPORT 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 {
 700 PRINT("Java_sun_java2d_CRenderer_doOval")
 701     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 702 JNF_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);
 710 JNF_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  */
 718 JNIEXPORT 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 {
 721 PRINT("Java_sun_java2d_CRenderer_doArc")
 722     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 723 JNF_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);
 731 JNF_COCOA_RENDERER_EXIT(env);
 732 }
 733 
 734 /*
 735  * Class:     sun_java2d_CRenderer
 736  * Method:    doPoly
 737  * Signature:
 738  */
 739 JNIEXPORT 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 {
 742 PRINT("Java_sun_java2d_CRenderer_doPoly")
 743     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 744 JNF_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);
 753 JNF_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  */
 761 JNIEXPORT 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 {
 764 PRINT("Java_sun_java2d_CRenderer_doShape")
 765     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 766 JNF_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);
 777 JNF_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  */
 788 JNIEXPORT 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 {
 791 PRINT("Java_sun_java2d_CRenderer_doImage")
 792     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 793 JNF_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);
 800 JNF_COCOA_RENDERER_EXIT(env);
 801 }