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