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