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     CGAffineTransform ctm = CGContextGetCTM(cgRef);
 537     CGAffineTransform newTm = CGAffineTransformMake(a, b, c, d, tx, ty);
 538     CGContextConcatCTM(cgRef, newTm);
 539     jint alphaInfo = isdo->contextInfo.alphaInfo & kCGBitmapAlphaInfoMask;
 540 
 541     if ((sx == 0) && (sy == 0) && (sw == w) && (sh == h)) // no subimages allowed here
 542     {
 543         CGContextDrawImage(cgRef, CGRectMake(0, 0, dw, dh), isdo->imgRef);
 544     }
 545     else // handle subimages
 546     {
 547         CGImageRef subImg = CGImageCreateWithImageInRect(isdo->imgRef, CGRectMake(sx, sy, sw, sh));
 548         CGContextDrawImage(cgRef, CGRectMake(0.0f, 0.0f, dw, dh), subImg);
 549         CGImageRelease(subImg);
 550     }
 551 
 552     CGAffineTransform inverse = CGAffineTransformInvert(newTm);
 553     CGContextConcatCTM(cgRef, inverse);
 554     UnlockImage(env, isdo);
 555 }
 556 
 557 QUARTZ_RENDERER_INLINE void doImage(JNIEnv *env, QuartzSDOps *qsdo, jobject imageSurfaceData,
 558                                 jboolean fliph, jboolean flipv, jint w, jint h, jint sx, jint sy, jint sw, jint sh, jint dx, jint dy, jint dw, jint dh)
 559 {
 560     if ((w > 0) && (h > 0) && (sw > 0) && (sh > 0) && (dw > 0) && (dh > 0))
 561     {
 562        doImageCG(env, qsdo->cgRef, imageSurfaceData,
 563                             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);
 564     }
 565 }
 566 
 567 
 568 
 569 QUARTZ_RENDERER_INLINE void completePath(JNIEnv *env, QuartzSDOps *qsdo, CGContextRef cgRef, jint renderType)
 570 {
 571     switch (renderType)
 572     {
 573         case SD_Stroke:
 574             if (CGContextIsPathEmpty(cgRef) == 0)
 575             {
 576                 CGContextStrokePath(cgRef);
 577             }
 578             break;
 579         case SD_Fill:
 580             if (CGContextIsPathEmpty(cgRef) == 0)
 581             {
 582                 CGContextFillPath(cgRef);
 583             }
 584             break;
 585         case SD_Image:
 586             break;
 587         case SD_Nothing:
 588                 break;
 589         default:
 590 fprintf(stderr, "completePath unknown renderType=%d\n", (int)renderType);
 591             break;
 592     }
 593 }
 594 
 595 /*
 596  * Class:     sun_java2d_CRenderer
 597  * Method:    init
 598  * Signature: ()V
 599  */
 600 JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_init
 601 (JNIEnv *env, jobject jthis)
 602 {
 603 PRINT("Java_sun_java2d_CRenderer_init")
 604     CGFloat angle = PI / 4.0f;
 605     CGFloat a = 1.0f - cos(angle);
 606     CGFloat b = tan(angle);
 607     CGFloat c = sqrt(1.0f + b * b) - 1.0f + a;
 608     CGFloat cv = 4.0f / 3.0f * a * b / c;
 609     CGFloat acv = (1.0f - cv) / 2.0f;
 610 
 611     gRoundRectCtrlpts[2][3] = -acv;
 612     gRoundRectCtrlpts[2][5] = acv;
 613     gRoundRectCtrlpts[4][1] = -acv;
 614     gRoundRectCtrlpts[4][7] = -acv;
 615     gRoundRectCtrlpts[6][3] = acv;
 616     gRoundRectCtrlpts[6][5] = -acv;
 617     gRoundRectCtrlpts[8][1] = acv;
 618     gRoundRectCtrlpts[8][7] = acv;
 619 }
 620 
 621 /*
 622  * Class:     sun_java2d_CRenderer
 623  * Method:    doLine
 624  * Signature: (Lsun/java2d/SurfaceData;Ljava/nio/IntBuffer;Ljava/nio/FloatBuffer;[Ljava/lang/Object;FFFF)V
 625  */
 626 JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doLine
 627 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jfloat x1, jfloat y1, jfloat x2, jfloat y2)
 628 {
 629 PRINT("Java_sun_java2d_CRenderer_doLine")
 630     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 631 JNF_COCOA_ENTER(env);
 632     SDRenderType renderType = SD_Stroke;
 633     qsdo->BeginSurface(env, qsdo, renderType);
 634     if (qsdo->cgRef != NULL)
 635     {
 636         doLine(qsdo, x1, y1, x2, y2);
 637     }
 638     qsdo->FinishSurface(env, qsdo);
 639 JNF_COCOA_RENDERER_EXIT(env);
 640 }
 641 
 642 /*
 643  * Class:     sun_java2d_CRenderer
 644  * Method:    doRect
 645  * Signature: (Lsun/java2d/SurfaceData;Ljava/nio/IntBuffer;Ljava/nio/FloatBuffer;[Ljava/lang/Object;FFFF)V
 646  */
 647 JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doRect
 648 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jfloat x, jfloat y, jfloat w, jfloat h, jboolean isfill)
 649 {
 650 PRINT("Java_sun_java2d_CRenderer_doRect")
 651     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 652 JNF_COCOA_ENTER(env);
 653     SDRenderType renderType    = (isfill? SD_Fill : SD_Stroke);
 654     qsdo->BeginSurface(env, qsdo, renderType);
 655     if (qsdo->cgRef != NULL)
 656     {
 657         doRect(qsdo, x, y, w, h, isfill);
 658     }
 659     qsdo->FinishSurface(env, qsdo);
 660 JNF_COCOA_RENDERER_EXIT(env);
 661 }
 662 
 663 /*
 664  * Class:     sun_java2d_CRenderer
 665  * Method:    doRoundRect
 666  * Signature: (Lsun/java2d/SurfaceData;Ljava/nio/IntBuffer;Ljava/nio/FloatBuffer;[Ljava/lang/Object;IIIIII)V
 667  */
 668 JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doRoundRect
 669 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jfloat x, jfloat y, jfloat w, jfloat h, jfloat arcWidth, jfloat arcHeight, jboolean isfill)
 670 {
 671 PRINT("Java_sun_java2d_CRenderer_doRoundRect")
 672     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 673 JNF_COCOA_ENTER(env);
 674     SDRenderType renderType    = (isfill? SD_Fill : SD_Stroke);
 675     qsdo->BeginSurface(env, qsdo, renderType);
 676     if (qsdo->cgRef != NULL)
 677     {
 678         doRoundRect(qsdo, x, y, w, h, arcWidth, arcHeight, isfill);
 679     }
 680     qsdo->FinishSurface(env, qsdo);
 681 JNF_COCOA_RENDERER_EXIT(env);
 682 }
 683 
 684 /*
 685  * Class:     sun_java2d_CRenderer
 686  * Method:    doOval
 687  * Signature: (Lsun/java2d/SurfaceData;Ljava/nio/IntBuffer;Ljava/nio/FloatBuffer;[Ljava/lang/Object;IIII)V
 688  */
 689 JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doOval
 690 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jfloat x, jfloat y, jfloat w, jfloat h, jboolean isfill)
 691 {
 692 PRINT("Java_sun_java2d_CRenderer_doOval")
 693     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 694 JNF_COCOA_ENTER(env);
 695     SDRenderType renderType    = (isfill? SD_Fill : SD_Stroke);
 696     qsdo->BeginSurface(env, qsdo, renderType);
 697     if (qsdo->cgRef != NULL)
 698     {
 699         doOval(qsdo, x, y, w, h, isfill);
 700     }
 701     qsdo->FinishSurface(env, qsdo);
 702 JNF_COCOA_RENDERER_EXIT(env);
 703 }
 704 
 705 /*
 706  * Class:     sun_java2d_CRenderer
 707  * Method:    doArc
 708  * Signature: (Lsun/java2d/SurfaceData;Ljava/nio/IntBuffer;Ljava/nio/FloatBuffer;[Ljava/lang/Object;IIIIII)V
 709  */
 710 JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doArc
 711 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jfloat x, jfloat y, jfloat w, jfloat h, jfloat angleStart, jfloat angleExtent, jint arcType, jboolean isfill)
 712 {
 713 PRINT("Java_sun_java2d_CRenderer_doArc")
 714     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 715 JNF_COCOA_ENTER(env);
 716     SDRenderType renderType    = (isfill? SD_Fill : SD_Stroke);
 717     qsdo->BeginSurface(env, qsdo, renderType);
 718     if (qsdo->cgRef != NULL)
 719     {
 720         doArc(qsdo, x, y, w, h, angleStart, angleExtent, arcType, isfill);
 721     }
 722     qsdo->FinishSurface(env, qsdo);
 723 JNF_COCOA_RENDERER_EXIT(env);
 724 }
 725 
 726 /*
 727  * Class:     sun_java2d_CRenderer
 728  * Method:    doPoly
 729  * Signature:
 730  */
 731 JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doPoly
 732 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jintArray xpointsarray, jintArray ypointsarray, jint npoints, jboolean ispolygon, jboolean isfill)
 733 {
 734 PRINT("Java_sun_java2d_CRenderer_doPoly")
 735     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 736 JNF_COCOA_ENTER(env);
 737     BOOL eoFill = YES; // polys are WIND_EVEN_ODD by definition
 738     SDRenderType renderType    = (isfill? (eoFill ? SD_EOFill : SD_Fill) : SD_Stroke);
 739     qsdo->BeginSurface(env, qsdo, renderType);
 740     if (qsdo->cgRef != NULL)
 741     {
 742         doPoly(env, qsdo, xpointsarray, ypointsarray, npoints, ispolygon, isfill);
 743     }
 744     qsdo->FinishSurface(env, qsdo);
 745 JNF_COCOA_RENDERER_EXIT(env);
 746 }
 747 
 748 /*
 749  * Class:     sun_java2d_CRenderer
 750  * Method:    doShape
 751  * Signature: (Lsun/java2d/SurfaceData;Ljava/nio/IntBuffer;Ljava/nio/FloatBuffer;[Ljava/lang/Object;ILjava/nio/FloatBuffer;Ljava/nio/IntBuffer;IZ)V
 752  */
 753 JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doShape
 754 (JNIEnv *env, jobject jthis, jobject jsurfacedata, jint length, jobject jFloatCoordinates, jobject jIntTypes, jint windingRule, jboolean isfill, jboolean shouldApplyOffset)
 755 {
 756 PRINT("Java_sun_java2d_CRenderer_doShape")
 757     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 758 JNF_COCOA_ENTER(env);
 759     BOOL eoFill = (windingRule == java_awt_geom_PathIterator_WIND_EVEN_ODD);
 760     SDRenderType renderType    = (isfill? (eoFill ? SD_EOFill : SD_Fill) : SD_Stroke);
 761     qsdo->BeginSurface(env, qsdo, renderType);
 762     if (qsdo->cgRef != NULL)
 763     {
 764         jfloat *coordinates = (jfloat*)((*env)->GetDirectBufferAddress(env, jFloatCoordinates));
 765         jint *types = (jint*)((*env)->GetDirectBufferAddress(env, jIntTypes));
 766         doShape(qsdo, types, coordinates, length, isfill, shouldApplyOffset);
 767     }
 768     qsdo->FinishSurface(env, qsdo);
 769 JNF_COCOA_RENDERER_EXIT(env);
 770 }
 771 
 772 #define invalidContext(c) \
 773     ((c) == NULL /* || (c)->identifer != CGContextIdentifier */)
 774 
 775 /*
 776  * Class:     sun_java2d_CRenderer
 777  * Method:    doImage
 778  * Signature: (Lsun/java2d/SurfaceData;Ljava/nio/IntBuffer;Ljava/nio/FloatBuffer;[Ljava/lang/Object;Lsun/java2d/SurfaceData;ZZIIIIIIII)V
 779  */
 780 JNIEXPORT void JNICALL Java_sun_java2d_CRenderer_doImage
 781 (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)
 782 {
 783 PRINT("Java_sun_java2d_CRenderer_doImage")
 784     QuartzSDOps *qsdo = (QuartzSDOps*)SurfaceData_GetOps(env, jsurfacedata);
 785 JNF_COCOA_ENTER(env);
 786     qsdo->BeginSurface(env, qsdo, SD_Image);
 787     if (qsdo->cgRef != NULL)
 788     {
 789         doImage(env, qsdo, imageSurfaceData, fliph, flipv, w, h, sx, sy, sw, sh, dx, dy, dw, dh);
 790     }
 791     qsdo->FinishSurface(env, qsdo);
 792 JNF_COCOA_RENDERER_EXIT(env);
 793 }