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 
  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 (npoints > 1)
 442     {
 443         if (fill == YES)
 444         {
 445             renderType = SD_Fill;
 446         }
 447         else
 448         {
 449             renderType = SD_Stroke;
 450         }
 451 
 452         jint i;
 453 
 454         jint* xpoints = (jint*)(*env)->GetPrimitiveArrayCritical(env, xpointsarray, NULL);
 455         jint* ypoints = (jint*)(*env)->GetPrimitiveArrayCritical(env, ypointsarray, NULL);
 456 
 457         CGContextMoveToPoint(cgRef, xpoints[0]+offsetX, ypoints[0]+offsetY);
 458 
 459         for (i=1; i<npoints; i++)
 460         {
 461             CGContextAddLineToPoint(cgRef, xpoints[i]+offsetX, ypoints[i]+offsetY);
 462         }
 463 
 464         if (polygon == YES)
 465         {
 466             if ((xpoints[0] != xpoints[npoints-1]) || (ypoints[0] != ypoints[npoints-1])) // according to the specs (only applies to polygons, not polylines)
 467             {
 468                 CGContextAddLineToPoint(cgRef, xpoints[0]+offsetX, ypoints[0]+offsetY);
 469             }
 470         }
 471 
 472         (*env)->ReleasePrimitiveArrayCritical(env, ypointsarray, ypoints, 0);
 473         (*env)->ReleasePrimitiveArrayCritical(env, xpointsarray, xpoints, 0);
 474     }
 475 
 476     return renderType;
 477 }
 478 QUARTZ_RENDERER_INLINE SDRenderType doPoly(JNIEnv *env, QuartzSDOps *qsdo, jintArray xpointsarray, jintArray ypointsarray, jint npoints, BOOL polygon, BOOL fill)
 479 {
 480 PRINT(" doPoly")
 481     if (YES)
 482     {
 483         return doPolyUsingCG(env, qsdo->cgRef, xpointsarray, ypointsarray, npoints, polygon, fill,
 484             qsdo->graphicsStateInfo.offsetX, qsdo->graphicsStateInfo.offsetY);
 485     }
 486     // here we can add other implementations (ex. using QuickDraw, OpenGL, etc.)
 487 }
 488 
 489 SDRenderType doShape(QuartzSDOps *qsdo, jint *types, jfloat *coords, jint numtypes, BOOL fill, BOOL shouldApplyOffset)
 490 {
 491 PRINT(" doShape")
 492     if (YES)
 493     {
 494         CGFloat offsetX = 0.0f;
 495         CGFloat offsetY = 0.0f;
 496         if (shouldApplyOffset)
 497         {
 498             offsetX = qsdo->graphicsStateInfo.offsetX;
 499             offsetY = qsdo->graphicsStateInfo.offsetY;
 500         }
 501         return DoShapeUsingCG(qsdo->cgRef, types, coords, numtypes, fill, offsetX, offsetY); // defined in QuartzSurfaceData.m
 502     }
 503     // here we can add other implementations (ex. using QuickDraw, OpenGL, etc.)
 504 }
 505 
 506 
 507 
 508 QUARTZ_RENDERER_INLINE void doImageCG(JNIEnv *env, CGContextRef cgRef, jobject imageSurfaceData,
 509                                         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)
 510 {
 511 //fprintf(stderr, "doImageCG\n");
 512 //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);
 513     // gznote: need to handle interpolation
 514     ImageSDOps* isdo = LockImage(env, imageSurfaceData);
 515 
 516     CGFloat a = 1.0f;
 517     CGFloat b = 0.0f;
 518     CGFloat c = 0.0f;
 519     CGFloat d = -1.0f;
 520     CGFloat tx = dx;
 521     CGFloat ty = dy+dh;
 522 
 523     if (flipv == YES)
 524     {
 525         d = 1.0f;
 526         ty -= dh;
 527     }
 528     if (fliph == YES)
 529     {
 530         a = -1.0f;
 531         tx += dw;
 532     }
 533 
 534     makeSureImageIsCreated(isdo);
 535 
 536     CGContextSaveGState(cgRef);
 537     CGContextConcatCTM(cgRef, CGAffineTransformMake(a, b, c, d, tx, ty));
 538     jint alphaInfo = isdo->contextInfo.alphaInfo & kCGBitmapAlphaInfoMask;
 539 
 540     if ((sx == 0) && (sy == 0) && (sw == w) && (sh == h)) // no subimages allowed here
 541     {
 542         CGContextDrawImage(cgRef, CGRectMake(0, 0, dw, dh), isdo->imgRef);
 543     }
 544     else // handle subimages
 545     {
 546         CGImageRef subImg = CGImageCreateWithImageInRect(isdo->imgRef, CGRectMake(sx, sy, sw, sh));
 547         CGContextDrawImage(cgRef, CGRectMake(0.0f, 0.0f, dw, dh), subImg);
 548         CGImageRelease(subImg);
 549     }
 550 
 551     CGContextRestoreGState(cgRef);
 552     UnlockImage(env, isdo);
 553 }
 554 
 555 QUARTZ_RENDERER_INLINE void doImage(JNIEnv *env, QuartzSDOps *qsdo, jobject imageSurfaceData,
 556                                 jboolean fliph, jboolean flipv, jint w, jint h, jint sx, jint sy, jint sw, jint sh, jint dx, jint dy, jint dw, jint dh)
 557 {
 558     if ((w > 0) && (h > 0) && (sw > 0) && (sh > 0) && (dw > 0) && (dh > 0))
 559     {
 560        doImageCG(env, qsdo->cgRef, imageSurfaceData,
 561                             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);
 562     }
 563 }
 564 
 565 
 566 
 567 QUARTZ_RENDERER_INLINE void completePath(JNIEnv *env, QuartzSDOps *qsdo, CGContextRef cgRef, jint renderType)
 568 {
 569     switch (renderType)
 570     {
 571         case SD_Stroke:
 572             if (CGContextIsPathEmpty(cgRef) == 0)
 573             {
 574                 CGContextStrokePath(cgRef);
 575             }
 576             break;
 577         case SD_Fill:
 578             if (CGContextIsPathEmpty(cgRef) == 0)
 579             {
 580                 CGContextFillPath(cgRef);
 581             }
 582             break;
 583         case SD_Image:
 584             break;
 585         case SD_Nothing:
 586                 break;
 587         default:
 588 fprintf(stderr, "completePath unknown renderType=%d\n", (int)renderType);
 589             break;
 590     }
 591 }
 592 
 593 /*
 594  * Class:     sun_java2d_CRenderer
 595  * Method:    init
 596  * Signature: ()V
 597  */
 598 JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_init
 599 (JNIEnv *env, jobject jthis)
 600 {
 601 PRINT("Java_sun_java2d_CRenderer_init")
 602     CGFloat angle = PI / 4.0f;
 603     CGFloat a = 1.0f - cos(angle);
 604     CGFloat b = tan(angle);
 605     CGFloat c = sqrt(1.0f + b * b) - 1.0f + a;
 606     CGFloat cv = 4.0f / 3.0f * a * b / c;
 607     CGFloat acv = (1.0f - cv) / 2.0f;
 608 
 609     gRoundRectCtrlpts[2][3] = -acv;
 610     gRoundRectCtrlpts[2][5] = acv;
 611     gRoundRectCtrlpts[4][1] = -acv;
 612     gRoundRectCtrlpts[4][7] = -acv;
 613     gRoundRectCtrlpts[6][3] = acv;
 614     gRoundRectCtrlpts[6][5] = -acv;
 615     gRoundRectCtrlpts[8][1] = acv;
 616     gRoundRectCtrlpts[8][7] = acv;
 617 }
 618 
 619 /*
 620  * Class:     sun_java2d_CRenderer
 621  * Method:    doLine
 622  * Signature: (Lsun/java2d/SurfaceData;Ljava/nio/IntBuffer;Ljava/nio/FloatBuffer;[Ljava/lang/Object;FFFF)V
 623  */
 624 JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doLine
 625 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jfloat x1, jfloat y1, jfloat x2, jfloat y2)
 626 {
 627 PRINT("Java_sun_java2d_CRenderer_doLine")
 628     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 629 JNF_COCOA_ENTER(env);
 630     SDRenderType renderType = SD_Stroke;
 631     qsdo->BeginSurface(env, qsdo, renderType);
 632     if (qsdo->cgRef != NULL)
 633     {
 634         doLine(qsdo, x1, y1, x2, y2);
 635     }
 636     qsdo->FinishSurface(env, qsdo);
 637 JNF_COCOA_RENDERER_EXIT(env);
 638 }
 639 
 640 /*
 641  * Class:     sun_java2d_CRenderer
 642  * Method:    doRect
 643  * Signature: (Lsun/java2d/SurfaceData;Ljava/nio/IntBuffer;Ljava/nio/FloatBuffer;[Ljava/lang/Object;FFFF)V
 644  */
 645 JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doRect
 646 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jfloat x, jfloat y, jfloat w, jfloat h, jboolean isfill)
 647 {
 648 PRINT("Java_sun_java2d_CRenderer_doRect")
 649     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 650 JNF_COCOA_ENTER(env);
 651     SDRenderType renderType    = (isfill? SD_Fill : SD_Stroke);
 652     qsdo->BeginSurface(env, qsdo, renderType);
 653     if (qsdo->cgRef != NULL)
 654     {
 655         doRect(qsdo, x, y, w, h, isfill);
 656     }
 657     qsdo->FinishSurface(env, qsdo);
 658 JNF_COCOA_RENDERER_EXIT(env);
 659 }
 660 
 661 /*
 662  * Class:     sun_java2d_CRenderer
 663  * Method:    doRoundRect
 664  * Signature: (Lsun/java2d/SurfaceData;Ljava/nio/IntBuffer;Ljava/nio/FloatBuffer;[Ljava/lang/Object;IIIIII)V
 665  */
 666 JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doRoundRect
 667 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jfloat x, jfloat y, jfloat w, jfloat h, jfloat arcWidth, jfloat arcHeight, jboolean isfill)
 668 {
 669 PRINT("Java_sun_java2d_CRenderer_doRoundRect")
 670     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 671 JNF_COCOA_ENTER(env);
 672     SDRenderType renderType    = (isfill? SD_Fill : SD_Stroke);
 673     qsdo->BeginSurface(env, qsdo, renderType);
 674     if (qsdo->cgRef != NULL)
 675     {
 676         doRoundRect(qsdo, x, y, w, h, arcWidth, arcHeight, isfill);
 677     }
 678     qsdo->FinishSurface(env, qsdo);
 679 JNF_COCOA_RENDERER_EXIT(env);
 680 }
 681 
 682 /*
 683  * Class:     sun_java2d_CRenderer
 684  * Method:    doOval
 685  * Signature: (Lsun/java2d/SurfaceData;Ljava/nio/IntBuffer;Ljava/nio/FloatBuffer;[Ljava/lang/Object;IIII)V
 686  */
 687 JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doOval
 688 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jfloat x, jfloat y, jfloat w, jfloat h, jboolean isfill)
 689 {
 690 PRINT("Java_sun_java2d_CRenderer_doOval")
 691     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 692 JNF_COCOA_ENTER(env);
 693     SDRenderType renderType    = (isfill? SD_Fill : SD_Stroke);
 694     qsdo->BeginSurface(env, qsdo, renderType);
 695     if (qsdo->cgRef != NULL)
 696     {
 697         doOval(qsdo, x, y, w, h, isfill);
 698     }
 699     qsdo->FinishSurface(env, qsdo);
 700 JNF_COCOA_RENDERER_EXIT(env);
 701 }
 702 
 703 /*
 704  * Class:     sun_java2d_CRenderer
 705  * Method:    doArc
 706  * Signature: (Lsun/java2d/SurfaceData;Ljava/nio/IntBuffer;Ljava/nio/FloatBuffer;[Ljava/lang/Object;IIIIII)V
 707  */
 708 JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doArc
 709 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jfloat x, jfloat y, jfloat w, jfloat h, jfloat angleStart, jfloat angleExtent, jint arcType, jboolean isfill)
 710 {
 711 PRINT("Java_sun_java2d_CRenderer_doArc")
 712     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 713 JNF_COCOA_ENTER(env);
 714     SDRenderType renderType    = (isfill? SD_Fill : SD_Stroke);
 715     qsdo->BeginSurface(env, qsdo, renderType);
 716     if (qsdo->cgRef != NULL)
 717     {
 718         doArc(qsdo, x, y, w, h, angleStart, angleExtent, arcType, isfill);
 719     }
 720     qsdo->FinishSurface(env, qsdo);
 721 JNF_COCOA_RENDERER_EXIT(env);
 722 }
 723 
 724 /*
 725  * Class:     sun_java2d_CRenderer
 726  * Method:    doPoly
 727  * Signature:
 728  */
 729 JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doPoly
 730 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jintArray xpointsarray, jintArray ypointsarray, jint npoints, jboolean ispolygon, jboolean isfill)
 731 {
 732 PRINT("Java_sun_java2d_CRenderer_doPoly")
 733     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 734 JNF_COCOA_ENTER(env);
 735     BOOL eoFill = YES; // polys are WIND_EVEN_ODD by definition
 736     SDRenderType renderType    = (isfill? (eoFill ? SD_EOFill : SD_Fill) : SD_Stroke);
 737     qsdo->BeginSurface(env, qsdo, renderType);
 738     if (qsdo->cgRef != NULL)
 739     {
 740         doPoly(env, qsdo, xpointsarray, ypointsarray, npoints, ispolygon, isfill);
 741     }
 742     qsdo->FinishSurface(env, qsdo);
 743 JNF_COCOA_RENDERER_EXIT(env);
 744 }
 745 
 746 /*
 747  * Class:     sun_java2d_CRenderer
 748  * Method:    doShape
 749  * Signature: (Lsun/java2d/SurfaceData;Ljava/nio/IntBuffer;Ljava/nio/FloatBuffer;[Ljava/lang/Object;ILjava/nio/FloatBuffer;Ljava/nio/IntBuffer;IZ)V
 750  */
 751 JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doShape
 752 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jint length, jobject jFloatCoordinates, jobject jIntTypes, jint windingRule, jboolean isfill, jboolean shouldApplyOffset)
 753 {
 754 PRINT("Java_sun_java2d_CRenderer_doShape")
 755     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 756 JNF_COCOA_ENTER(env);
 757     BOOL eoFill = (windingRule == java_awt_geom_PathIterator_WIND_EVEN_ODD);
 758     SDRenderType renderType    = (isfill? (eoFill ? SD_EOFill : SD_Fill) : SD_Stroke);
 759     qsdo->BeginSurface(env, qsdo, renderType);
 760     if (qsdo->cgRef != NULL)
 761     {
 762         jfloat *coordinates = (jfloat*)((*env)->GetDirectBufferAddress(env, jFloatCoordinates));
 763         jint *types = (jint*)((*env)->GetDirectBufferAddress(env, jIntTypes));
 764         doShape(qsdo, types, coordinates, length, isfill, shouldApplyOffset);
 765     }
 766     qsdo->FinishSurface(env, qsdo);
 767 JNF_COCOA_RENDERER_EXIT(env);
 768 }
 769 
 770 #define invalidContext(c) \
 771     ((c) == NULL /* || (c)->identifer != CGContextIdentifier */)
 772 
 773 /*
 774  * Class:     sun_java2d_CRenderer
 775  * Method:    doImage
 776  * Signature: (Lsun/java2d/SurfaceData;Ljava/nio/IntBuffer;Ljava/nio/FloatBuffer;[Ljava/lang/Object;Lsun/java2d/SurfaceData;ZZIIIIIIII)V
 777  */
 778 JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doImage
 779 (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)
 780 {
 781 PRINT("Java_sun_java2d_CRenderer_doImage")
 782     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 783 JNF_COCOA_ENTER(env);
 784     qsdo->BeginSurface(env, qsdo, SD_Image);
 785     if (qsdo->cgRef != NULL)
 786     {
 787         doImage(env, qsdo, imageSurfaceData, fliph, flipv, w, h, sx, sy, sw, sh, dx, dy, dw, dh);
 788     }
 789     qsdo->FinishSurface(env, qsdo);
 790 JNF_COCOA_RENDERER_EXIT(env);
 791 }