1 /*
   2  * Copyright (c) 2013, 2018, 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 #ifdef __APPLE__
  27 #include <TargetConditionals.h>
  28 
  29 #if TARGET_OS_MAC
  30 
  31 #include <jni.h>
  32 #include <com_sun_javafx_font_coretext_OS.h>
  33 
  34 #import <CoreFoundation/CoreFoundation.h>
  35 
  36 #if TARGET_OS_IPHONE
  37 #import <CoreGraphics/CoreGraphics.h>
  38 #import <CoreText/CoreText.h>
  39 #else
  40 #import <ApplicationServices/ApplicationServices.h>
  41 #endif
  42 
  43 
  44 #define OS_NATIVE(func) Java_com_sun_javafx_font_coretext_OS_##func
  45 
  46 extern jboolean checkAndClearException(JNIEnv *env);
  47 
  48 jboolean checkAndClearException(JNIEnv *env)
  49 {
  50     jthrowable t = (*env)->ExceptionOccurred(env);
  51     if (!t) {
  52         return JNI_FALSE;
  53     }
  54     (*env)->ExceptionClear(env);
  55     return JNI_TRUE;
  56 }
  57 
  58 /**************************************************************************/
  59 /*                                                                        */
  60 /*                            Structs                                     */
  61 /*                                                                        */
  62 /**************************************************************************/
  63 typedef struct CGAffineTransform_FID_CACHE {
  64     int cached;
  65     jclass clazz;
  66     jfieldID a, b, c, d, tx, ty;
  67     jmethodID init;
  68 } CGAffineTransform_FID_CACHE;
  69 
  70 CGAffineTransform_FID_CACHE CGAffineTransformFc;
  71 
  72 void cacheCGAffineTransformFields(JNIEnv *env)
  73 {
  74     if (CGAffineTransformFc.cached) return;
  75     jclass tmpClass = (*env)->FindClass(env, "com/sun/javafx/font/coretext/CGAffineTransform");
  76     if (checkAndClearException(env) || !tmpClass) {
  77         fprintf(stderr, "cacheCGAffineTransformFields error: JNI exception or tmpClass == NULL");
  78         return;
  79     }
  80     CGAffineTransformFc.clazz =  (jclass)(*env)->NewGlobalRef(env, tmpClass);
  81     CGAffineTransformFc.a = (*env)->GetFieldID(env, CGAffineTransformFc.clazz, "a", "D");
  82     if (checkAndClearException(env) || !CGAffineTransformFc.a) {
  83         fprintf(stderr, "cacheCGAffineTransformFields error: JNI exception or a == NULL");
  84         return;
  85     }
  86     CGAffineTransformFc.b = (*env)->GetFieldID(env, CGAffineTransformFc.clazz, "b", "D");
  87     if (checkAndClearException(env) || !CGAffineTransformFc.b) {
  88         fprintf(stderr, "cacheCGAffineTransformFields error: JNI exception or b == NULL");
  89         return;
  90     }
  91     CGAffineTransformFc.c = (*env)->GetFieldID(env, CGAffineTransformFc.clazz, "c", "D");
  92     if (checkAndClearException(env) || !CGAffineTransformFc.c) {
  93         fprintf(stderr, "cacheCGAffineTransformFields error: JNI exception or c == NULL");
  94         return;
  95     }
  96     CGAffineTransformFc.d = (*env)->GetFieldID(env, CGAffineTransformFc.clazz, "d", "D");
  97     if (checkAndClearException(env) || !CGAffineTransformFc.d) {
  98         fprintf(stderr, "cacheCGAffineTransformFields error: JNI exception or d == NULL");
  99         return;
 100     }
 101     CGAffineTransformFc.tx = (*env)->GetFieldID(env, CGAffineTransformFc.clazz, "tx", "D");
 102     if (checkAndClearException(env) || !CGAffineTransformFc.tx) {
 103         fprintf(stderr, "cacheCGAffineTransformFields error: JNI exception or tx == NULL");
 104         return;
 105     }
 106     CGAffineTransformFc.ty = (*env)->GetFieldID(env, CGAffineTransformFc.clazz, "ty", "D");
 107     if (checkAndClearException(env) || !CGAffineTransformFc.ty) {
 108         fprintf(stderr, "cacheCGAffineTransformFields error: JNI exception or ty == NULL");
 109         return;
 110     }
 111     CGAffineTransformFc.init = (*env)->GetMethodID(env, CGAffineTransformFc.clazz, "<init>", "()V");
 112     if (checkAndClearException(env) || !CGAffineTransformFc.init) {
 113         fprintf(stderr, "cacheCGAffineTransformFields error: JNI exception or init == NULL");
 114         return;
 115     }
 116     CGAffineTransformFc.cached = 1;
 117 }
 118 
 119 CGAffineTransform *getCGAffineTransformFields(JNIEnv *env, jobject lpObject, CGAffineTransform *lpStruct)
 120 {
 121     if (!CGAffineTransformFc.cached) cacheCGAffineTransformFields(env);
 122     lpStruct->a = (*env)->GetDoubleField(env, lpObject, CGAffineTransformFc.a);
 123     lpStruct->b = (*env)->GetDoubleField(env, lpObject, CGAffineTransformFc.b);
 124     lpStruct->c = (*env)->GetDoubleField(env, lpObject, CGAffineTransformFc.c);
 125     lpStruct->d = (*env)->GetDoubleField(env, lpObject, CGAffineTransformFc.d);
 126     lpStruct->tx = (*env)->GetDoubleField(env, lpObject, CGAffineTransformFc.tx);
 127     lpStruct->ty = (*env)->GetDoubleField(env, lpObject, CGAffineTransformFc.ty);
 128     return lpStruct;
 129 }
 130 
 131 void setCGAffineTransformFields(JNIEnv *env, jobject lpObject, CGAffineTransform *lpStruct)
 132 {
 133     if (!CGAffineTransformFc.cached) cacheCGAffineTransformFields(env);
 134     (*env)->SetDoubleField(env, lpObject, CGAffineTransformFc.a, (jdouble)lpStruct->a);
 135     (*env)->SetDoubleField(env, lpObject, CGAffineTransformFc.b, (jdouble)lpStruct->b);
 136     (*env)->SetDoubleField(env, lpObject, CGAffineTransformFc.c, (jdouble)lpStruct->c);
 137     (*env)->SetDoubleField(env, lpObject, CGAffineTransformFc.d, (jdouble)lpStruct->d);
 138     (*env)->SetDoubleField(env, lpObject, CGAffineTransformFc.tx, (jdouble)lpStruct->tx);
 139     (*env)->SetDoubleField(env, lpObject, CGAffineTransformFc.ty, (jdouble)lpStruct->ty);
 140 }
 141 
 142 jobject newCGAffineTransform(JNIEnv *env, CGAffineTransform *lpStruct)
 143 {
 144     jobject lpObject = NULL;
 145     if (!CGAffineTransformFc.cached) cacheCGAffineTransformFields(env);
 146     lpObject = (*env)->NewObject(env, CGAffineTransformFc.clazz, CGAffineTransformFc.init);
 147     if (lpObject && lpStruct) setCGAffineTransformFields(env, lpObject, lpStruct);
 148     return lpObject;
 149 }
 150 
 151 typedef struct CGPoint_FID_CACHE {
 152     int cached;
 153     jclass clazz;
 154     jfieldID x, y;
 155     jmethodID init;
 156 } CGPoint_FID_CACHE;
 157 
 158 CGPoint_FID_CACHE CGPointFc;
 159 
 160 void cacheCGPointFields(JNIEnv *env)
 161 {
 162     if (CGPointFc.cached) return;
 163     jclass tmpClass = (*env)->FindClass(env, "com/sun/javafx/font/coretext/CGPoint");
 164     if (checkAndClearException(env) || !tmpClass) {
 165         fprintf(stderr, "cacheCGPointFields error: JNI exception or tmpClass == NULL");
 166         return;
 167     }
 168     CGPointFc.clazz =  (jclass)(*env)->NewGlobalRef(env, tmpClass);
 169     CGPointFc.x = (*env)->GetFieldID(env, CGPointFc.clazz, "x", "D");
 170     if (checkAndClearException(env) || !CGPointFc.x) {
 171         fprintf(stderr, "cacheCGPointFields error: JNI exception or x == NULL");
 172         return;
 173     }
 174     CGPointFc.y = (*env)->GetFieldID(env, CGPointFc.clazz, "y", "D");
 175     if (checkAndClearException(env) || !CGPointFc.y) {
 176         fprintf(stderr, "cacheCGPointFields error: JNI exception or y == NULL");
 177         return;
 178     }
 179     CGPointFc.init = (*env)->GetMethodID(env, CGPointFc.clazz, "<init>", "()V");
 180     if (checkAndClearException(env) || !CGPointFc.init) {
 181         fprintf(stderr, "cacheCGPointFields error: JNI exception or init == NULL");
 182         return;
 183     }
 184     CGPointFc.cached = 1;
 185 }
 186 
 187 CGPoint *getCGPointFields(JNIEnv *env, jobject lpObject, CGPoint *lpStruct)
 188 {
 189     if (!CGPointFc.cached) cacheCGPointFields(env);
 190     lpStruct->x = (*env)->GetDoubleField(env, lpObject, CGPointFc.x);
 191     lpStruct->y = (*env)->GetDoubleField(env, lpObject, CGPointFc.y);
 192     return lpStruct;
 193 }
 194 
 195 void setCGPointFields(JNIEnv *env, jobject lpObject, CGPoint *lpStruct)
 196 {
 197     if (!CGPointFc.cached) cacheCGPointFields(env);
 198     (*env)->SetDoubleField(env, lpObject, CGPointFc.x, (jdouble)lpStruct->x);
 199     (*env)->SetDoubleField(env, lpObject, CGPointFc.y, (jdouble)lpStruct->y);
 200 }
 201 
 202 jobject newCGPoint(JNIEnv *env, CGPoint *lpStruct)
 203 {
 204     jobject lpObject = NULL;
 205     if (!CGPointFc.cached) cacheCGPointFields(env);
 206     lpObject = (*env)->NewObject(env, CGPointFc.clazz, CGPointFc.init);
 207     if (lpObject && lpStruct) setCGPointFields(env, lpObject, lpStruct);
 208     return lpObject;
 209 }
 210 
 211 typedef struct CGSize_FID_CACHE {
 212     int cached;
 213     jclass clazz;
 214     jfieldID width, height;
 215     jmethodID init;
 216 } CGSize_FID_CACHE;
 217 
 218 CGSize_FID_CACHE CGSizeFc;
 219 
 220 void cacheCGSizeFields(JNIEnv *env)
 221 {
 222     if (CGSizeFc.cached) return;
 223     jclass tmpClass = (*env)->FindClass(env, "com/sun/javafx/font/coretext/CGSize");
 224     if (checkAndClearException(env) || !tmpClass) {
 225         fprintf(stderr, "cacheCGSizeFields error: JNI exception or tmpClass == NULL");
 226         return;
 227     }
 228     CGSizeFc.clazz =  (jclass)(*env)->NewGlobalRef(env, tmpClass);
 229     CGSizeFc.width = (*env)->GetFieldID(env, CGSizeFc.clazz, "width", "D");
 230     if (checkAndClearException(env) || !CGSizeFc.width) {
 231         fprintf(stderr, "cacheCGSizeFields error: JNI exception or width == NULL");
 232         return;
 233     }
 234     CGSizeFc.height = (*env)->GetFieldID(env, CGSizeFc.clazz, "height", "D");
 235     if (checkAndClearException(env) || !CGSizeFc.height) {
 236         fprintf(stderr, "cacheCGSizeFields error: JNI exception or height == NULL");
 237         return;
 238     }
 239     CGSizeFc.init = (*env)->GetMethodID(env, CGSizeFc.clazz, "<init>", "()V");
 240     if (checkAndClearException(env) || !CGSizeFc.init) {
 241         fprintf(stderr, "cacheCGSizeFields error: JNI exception or init == NULL");
 242         return;
 243     }
 244     CGSizeFc.cached = 1;
 245 }
 246 
 247 CGSize *getCGSizeFields(JNIEnv *env, jobject lpObject, CGSize *lpStruct)
 248 {
 249     if (!CGSizeFc.cached) cacheCGSizeFields(env);
 250     lpStruct->width = (*env)->GetDoubleField(env, lpObject, CGSizeFc.width);
 251     lpStruct->height = (*env)->GetDoubleField(env, lpObject, CGSizeFc.height);
 252     return lpStruct;
 253 }
 254 
 255 void setCGSizeFields(JNIEnv *env, jobject lpObject, CGSize *lpStruct)
 256 {
 257     if (!CGSizeFc.cached) cacheCGSizeFields(env);
 258     (*env)->SetDoubleField(env, lpObject, CGSizeFc.width, (jdouble)lpStruct->width);
 259     (*env)->SetDoubleField(env, lpObject, CGSizeFc.height, (jdouble)lpStruct->height);
 260 }
 261 
 262 jobject newCGSize(JNIEnv *env, CGSize *lpStruct)
 263 {
 264     jobject lpObject = NULL;
 265     if (!CGSizeFc.cached) cacheCGSizeFields(env);
 266     lpObject = (*env)->NewObject(env, CGSizeFc.clazz, CGSizeFc.init);
 267     if (lpObject && lpStruct) setCGSizeFields(env, lpObject, lpStruct);
 268     return lpObject;
 269 }
 270 
 271 typedef struct CGRect_FID_CACHE {
 272     int cached;
 273     jclass clazz;
 274     jfieldID origin, size;
 275     jmethodID init;
 276 } CGRect_FID_CACHE;
 277 
 278 CGRect_FID_CACHE CGRectFc;
 279 
 280 void cacheCGRectFields(JNIEnv *env)
 281 {
 282     if (CGRectFc.cached) return;
 283     jclass tmpClass = (*env)->FindClass(env, "com/sun/javafx/font/coretext/CGRect");
 284     if (checkAndClearException(env) || !tmpClass) {
 285         fprintf(stderr, "cacheCGRectFields error: JNI exception or tmpClass == NULL");
 286         return;
 287     }
 288     CGRectFc.clazz =  (jclass)(*env)->NewGlobalRef(env, tmpClass);
 289     CGRectFc.origin = (*env)->GetFieldID(env, CGRectFc.clazz, "origin", "Lcom/sun/javafx/font/coretext/CGPoint;");
 290     if (checkAndClearException(env) || !CGRectFc.origin) {
 291         fprintf(stderr, "cacheCGRectFields error: JNI exception or origin == NULL");
 292         return;
 293     }
 294     CGRectFc.size = (*env)->GetFieldID(env, CGRectFc.clazz, "size", "Lcom/sun/javafx/font/coretext/CGSize;");
 295     if (checkAndClearException(env) || !CGRectFc.size) {
 296         fprintf(stderr, "cacheCGRectFields error: JNI exception or size == NULL");
 297         return;
 298     }
 299     CGRectFc.init = (*env)->GetMethodID(env, CGRectFc.clazz, "<init>", "()V");
 300     if (checkAndClearException(env) || !CGRectFc.init) {
 301         fprintf(stderr, "cacheCGRectFields error: JNI exception or init == NULL");
 302         return;
 303     }
 304     CGRectFc.cached = 1;
 305 }
 306 
 307 CGRect *getCGRectFields(JNIEnv *env, jobject lpObject, CGRect *lpStruct)
 308 {
 309     if (!CGRectFc.cached) cacheCGRectFields(env);
 310     {
 311     jobject lpObject1 = (*env)->GetObjectField(env, lpObject, CGRectFc.origin);
 312     if (lpObject1 != NULL) getCGPointFields(env, lpObject1, &lpStruct->origin);
 313     }
 314     {
 315     jobject lpObject1 = (*env)->GetObjectField(env, lpObject, CGRectFc.size);
 316     if (lpObject1 != NULL) getCGSizeFields(env, lpObject1, &lpStruct->size);
 317     }
 318     return lpStruct;
 319 }
 320 
 321 void setCGRectFields(JNIEnv *env, jobject lpObject, CGRect *lpStruct)
 322 {
 323     if (!CGRectFc.cached) cacheCGRectFields(env);
 324     {
 325     jobject lpObject1 = (*env)->GetObjectField(env, lpObject, CGRectFc.origin);
 326     if (lpObject1 != NULL) setCGPointFields(env, lpObject1, &lpStruct->origin);
 327     }
 328     {
 329     jobject lpObject1 = (*env)->GetObjectField(env, lpObject, CGRectFc.size);
 330     if (lpObject1 != NULL) setCGSizeFields(env, lpObject1, &lpStruct->size);
 331     }
 332 }
 333 
 334 jobject newCGRect(JNIEnv *env, CGRect *lpStruct)
 335 {
 336     jobject lpObject = NULL;
 337     if (!CGRectFc.cached) cacheCGRectFields(env);
 338     lpObject = (*env)->NewObject(env, CGRectFc.clazz, CGRectFc.init);
 339     if (lpObject && lpStruct) setCGRectFields(env, lpObject, lpStruct);
 340     return lpObject;
 341 }
 342 
 343 /**************************************************************************/
 344 /*                                                                        */
 345 /*                            Functions                                   */
 346 /*                                                                        */
 347 /**************************************************************************/
 348 
 349 JNIEXPORT jlong JNICALL OS_NATIVE(kCFAllocatorDefault)
 350     (JNIEnv *env, jclass that)
 351 {
 352     return (jlong)kCFAllocatorDefault;
 353 }
 354 
 355 JNIEXPORT jlong JNICALL OS_NATIVE(CFStringCreateWithCharacters__J_3CJ)
 356     (JNIEnv *env, jclass that, jlong arg0, jcharArray arg1, jlong arg2)
 357 {
 358     jchar *lparg1=NULL;
 359     jlong rc = 0;
 360     if (arg1) if ((lparg1 = (*env)->GetCharArrayElements(env, arg1, NULL)) == NULL) goto fail;
 361     rc = (jlong)CFStringCreateWithCharacters((CFAllocatorRef)arg0, (UniChar*)lparg1, (CFIndex)arg2);
 362 fail:
 363     if (arg1 && lparg1) (*env)->ReleaseCharArrayElements(env, arg1, lparg1, 0);
 364     return rc;
 365 }
 366 
 367 JNIEXPORT void JNICALL OS_NATIVE(CFRelease)
 368     (JNIEnv *env, jclass that, jlong arg0)
 369 {
 370     CFRelease((CFTypeRef)arg0);
 371 }
 372 
 373 JNIEXPORT jlong JNICALL OS_NATIVE(CTFontCreateWithGraphicsFont)
 374     (JNIEnv *env, jclass that, jlong cgFont, jdouble size, jobject matrix, jlong attributes)
 375 {
 376     CGAffineTransform transform;
 377     if (matrix) {
 378         getCGAffineTransformFields(env, matrix, &transform);
 379     } else {
 380         transform = CGAffineTransformIdentity;
 381     }
 382     return (jlong)CTFontCreateWithGraphicsFont((CGFontRef)cgFont, (CGFloat)size, &transform, (CTFontDescriptorRef)attributes);
 383 }
 384 
 385 JNIEXPORT jlong JNICALL OS_NATIVE(CTFontCreateWithName)
 386     (JNIEnv *env, jclass that, jlong arg0, jdouble arg1, jobject arg2)
 387 {
 388     CGAffineTransform _arg2, *lparg2=NULL;
 389     jlong rc = 0;
 390     if (arg2) if ((lparg2 = getCGAffineTransformFields(env, arg2, &_arg2)) == NULL) goto fail;
 391     rc = (jlong)CTFontCreateWithName((CFStringRef)arg0, (CGFloat)arg1, (CGAffineTransform*)lparg2);
 392 fail:
 393     /* In only */
 394 //    if (arg2 && lparg2) setCGAffineTransformFields(env, arg2, lparg2);
 395     return rc;
 396 }
 397 
 398 
 399 JNIEXPORT jlong JNICALL OS_NATIVE(CFURLCreateWithFileSystemPath)
 400     (JNIEnv *env, jclass that, jlong arg0, jlong arg1, jlong arg2, jboolean arg3)
 401 {
 402     return (jlong)CFURLCreateWithFileSystemPath((CFAllocatorRef)arg0, (CFStringRef)arg1, (CFURLPathStyle)arg2, (Boolean)arg3);
 403 }
 404 
 405 JNIEXPORT jboolean JNICALL OS_NATIVE(CTFontManagerRegisterFontsForURL)
 406     (JNIEnv *env, jclass that, jlong arg0, jint arg1, jlong arg2)
 407 {
 408     return (jboolean)CTFontManagerRegisterFontsForURL((CFURLRef)arg0, (CTFontManagerScope)arg1, (CFErrorRef*)arg2);
 409 }
 410 
 411 JNIEXPORT jlong JNICALL OS_NATIVE(CTFontCreatePathForGlyph)
 412     (JNIEnv *env, jclass that, jlong arg0, jshort arg1, jobject arg2)
 413 {
 414     CGAffineTransform _arg2, *lparg2=NULL;
 415     jlong rc = 0;
 416     if (arg2) if ((lparg2 = getCGAffineTransformFields(env, arg2, &_arg2)) == NULL) goto fail;
 417     rc = (jlong)CTFontCreatePathForGlyph((CTFontRef)arg0, (CGGlyph)arg1, (CGAffineTransform*)lparg2);
 418 fail:
 419     /* In Only */
 420 //    if (arg2 && lparg2) setCGAffineTransformFields(env, arg2, lparg2);
 421     return rc;
 422 }
 423 
 424 JNIEXPORT jlong JNICALL OS_NATIVE(CGFontCreateWithDataProvider)
 425     (JNIEnv *env, jclass that, jlong dataProvider)
 426 {
 427     return (jlong)CGFontCreateWithDataProvider((CGDataProviderRef)dataProvider);
 428 }
 429 
 430 JNIEXPORT void JNICALL OS_NATIVE(CGPathRelease)
 431     (JNIEnv *env, jclass that, jlong arg0)
 432 {
 433     CGPathRelease((CGPathRef)arg0);
 434 }
 435 
 436 JNIEXPORT jlong JNICALL OS_NATIVE(CGColorSpaceCreateDeviceRGB)
 437     (JNIEnv *env, jclass that)
 438 {
 439     return (jlong)CGColorSpaceCreateDeviceRGB();
 440 }
 441 
 442 JNIEXPORT jlong JNICALL OS_NATIVE(CGColorSpaceCreateDeviceGray)
 443     (JNIEnv *env, jclass that)
 444 {
 445     return (jlong)CGColorSpaceCreateDeviceGray();
 446 }
 447 
 448 JNIEXPORT jlong JNICALL OS_NATIVE(CGBitmapContextCreate)
 449     (JNIEnv *env, jclass that, jlong arg0, jlong arg1, jlong arg2, jlong arg3, jlong arg4, jlong arg5, jint arg6)
 450 {
 451     return (jlong)CGBitmapContextCreate((void*)arg0, (size_t)arg1, (size_t)arg2, (size_t)arg3, (size_t)arg4, (CGColorSpaceRef)arg5, (CGBitmapInfo)arg6);
 452 }
 453 
 454 JNIEXPORT void JNICALL OS_NATIVE(CGContextSetAllowsFontSmoothing)
 455     (JNIEnv *env, jclass that, jlong arg0, jboolean arg1)
 456 {
 457     CGContextSetAllowsFontSmoothing((CGContextRef)arg0, (_Bool)arg1);
 458 }
 459 
 460 JNIEXPORT void JNICALL OS_NATIVE(CGContextSetAllowsAntialiasing)
 461     (JNIEnv *env, jclass that, jlong arg0, jboolean arg1)
 462 {
 463     CGContextSetAllowsAntialiasing((CGContextRef)arg0, (_Bool)arg1);
 464 }
 465 
 466 JNIEXPORT void JNICALL OS_NATIVE(CGContextSetAllowsFontSubpixelPositioning)
 467     (JNIEnv *env, jclass that, jlong arg0, jboolean arg1)
 468 {
 469     CGContextSetAllowsFontSubpixelPositioning((CGContextRef)arg0, (_Bool)arg1);
 470 }
 471 
 472 JNIEXPORT void JNICALL OS_NATIVE(CGContextSetAllowsFontSubpixelQuantization)
 473     (JNIEnv *env, jclass that, jlong arg0, jboolean arg1)
 474 {
 475     CGContextSetAllowsFontSubpixelQuantization((CGContextRef)arg0, (_Bool)arg1);
 476 }
 477 
 478 JNIEXPORT void JNICALL OS_NATIVE(CGContextSetRGBFillColor)
 479     (JNIEnv *env, jclass that, jlong arg0, jdouble arg1, jdouble arg2, jdouble arg3, jdouble arg4)
 480 {
 481     CGContextSetRGBFillColor((CGContextRef)arg0, (CGFloat)arg1, (CGFloat)arg2, (CGFloat)arg3, (CGFloat)arg4);
 482 }
 483 
 484 JNIEXPORT void JNICALL OS_NATIVE(CGContextFillRect)
 485     (JNIEnv *env, jclass that, jlong arg0, jobject arg1)
 486 {
 487     CGRect _arg1, *lparg1=NULL;
 488     /* In Only */
 489     if (arg1) if ((lparg1 = getCGRectFields(env, arg1, &_arg1)) == NULL) return;
 490     CGContextFillRect((CGContextRef)arg0, *lparg1);
 491 }
 492 
 493 JNIEXPORT void JNICALL OS_NATIVE(CGContextTranslateCTM)
 494     (JNIEnv *env, jclass that, jlong arg0, jdouble arg1, jdouble arg2)
 495 {
 496     CGContextTranslateCTM((CGContextRef)arg0, (CGFloat)arg1, (CGFloat)arg2);
 497 }
 498 
 499 JNIEXPORT void JNICALL OS_NATIVE(CGContextRelease)
 500     (JNIEnv *env, jclass that, jlong arg0)
 501 {
 502     CGContextRelease((CGContextRef)arg0);
 503 }
 504 
 505 JNIEXPORT void JNICALL OS_NATIVE(CGColorSpaceRelease)
 506     (JNIEnv *env, jclass that, jlong arg0)
 507 {
 508     CGColorSpaceRelease((CGColorSpaceRef)arg0);
 509 }
 510 
 511 JNIEXPORT jlong JNICALL OS_NATIVE(CGDataProviderCreateWithURL)
 512     (JNIEnv *env, jclass that, jlong cfURL) {
 513       return (jlong)CGDataProviderCreateWithURL((CFURLRef)cfURL);
 514 }
 515 
 516 JNIEXPORT jlong JNICALL OS_NATIVE(kCFTypeDictionaryKeyCallBacks)
 517     (JNIEnv *env, jclass that)
 518 {
 519     return (jlong)&kCFTypeDictionaryKeyCallBacks;
 520 }
 521 
 522 JNIEXPORT jlong JNICALL OS_NATIVE(kCFTypeDictionaryValueCallBacks)
 523     (JNIEnv *env, jclass that)
 524 {
 525     return (jlong)&kCFTypeDictionaryValueCallBacks;
 526 }
 527 
 528 JNIEXPORT jlong JNICALL OS_NATIVE(CFDictionaryCreateMutable)
 529     (JNIEnv *env, jclass that, jlong arg0, jlong arg1, jlong arg2, jlong arg3)
 530 {
 531     return (jlong)CFDictionaryCreateMutable((CFAllocatorRef)arg0, (CFIndex)arg1, (CFDictionaryKeyCallBacks*)arg2, (CFDictionaryValueCallBacks*)arg3);
 532 }
 533 
 534 JNIEXPORT void JNICALL OS_NATIVE(CFDictionaryAddValue)
 535     (JNIEnv *env, jclass that, jlong arg0, jlong arg1, jlong arg2)
 536 {
 537     CFDictionaryAddValue((CFMutableDictionaryRef)arg0, (void*)arg1, (void*)arg2);
 538 }
 539 
 540 JNIEXPORT jlong JNICALL OS_NATIVE(CFDictionaryGetValue)
 541     (JNIEnv *env, jclass that, jlong arg0, jlong arg1)
 542 {
 543     return (jlong)CFDictionaryGetValue((CFDictionaryRef)arg0, (void*)arg1);
 544 }
 545 
 546 JNIEXPORT jlong JNICALL OS_NATIVE(kCTFontAttributeName)
 547     (JNIEnv *env, jclass that)
 548 {
 549     return (jlong)kCTFontAttributeName;
 550 }
 551 
 552 JNIEXPORT jlong JNICALL OS_NATIVE(kCTParagraphStyleAttributeName)
 553     (JNIEnv *env, jclass that)
 554 {
 555     return (jlong)kCTParagraphStyleAttributeName;
 556 }
 557 
 558 JNIEXPORT jlong JNICALL OS_NATIVE(CFAttributedStringCreate)
 559     (JNIEnv *env, jclass that, jlong arg0, jlong arg1, jlong arg2)
 560 {
 561     return (jlong)CFAttributedStringCreate((CFAllocatorRef)arg0, (CFStringRef)arg1, (CFDictionaryRef)arg2);
 562 }
 563 
 564 JNIEXPORT jlong JNICALL OS_NATIVE(CTLineCreateWithAttributedString)
 565     (JNIEnv *env, jclass that, jlong arg0)
 566 {
 567     return (jlong)CTLineCreateWithAttributedString((CFAttributedStringRef)arg0);
 568 }
 569 
 570 JNIEXPORT jlong JNICALL OS_NATIVE(CTLineGetGlyphRuns)
 571     (JNIEnv *env, jclass that, jlong arg0)
 572 {
 573     return (jlong)CTLineGetGlyphRuns((CTLineRef)arg0);
 574 }
 575 
 576 JNIEXPORT jdouble JNICALL OS_NATIVE(CTLineGetTypographicBounds)
 577     (JNIEnv *env, jclass that, jlong arg0)
 578 {
 579     return (jdouble)CTLineGetTypographicBounds((CTLineRef)arg0, NULL, NULL, NULL);
 580 }
 581 
 582 JNIEXPORT jlong JNICALL OS_NATIVE(CTLineGetGlyphCount)
 583     (JNIEnv *env, jclass that, jlong arg0)
 584 {
 585     return (jlong)CTLineGetGlyphCount((CTLineRef)arg0);
 586 }
 587 
 588 JNIEXPORT jlong JNICALL OS_NATIVE(CFArrayGetCount)
 589     (JNIEnv *env, jclass that, jlong arg0)
 590 {
 591     return (jlong)CFArrayGetCount((CFArrayRef)arg0);
 592 }
 593 
 594 JNIEXPORT jlong JNICALL OS_NATIVE(CFArrayGetValueAtIndex)
 595     (JNIEnv *env, jclass that, jlong arg0, jlong arg1)
 596 {
 597     return (jlong)CFArrayGetValueAtIndex((CFArrayRef)arg0, (CFIndex)arg1);
 598 }
 599 
 600 JNIEXPORT jlong JNICALL OS_NATIVE(CTRunGetGlyphCount)
 601     (JNIEnv *env, jclass that, jlong arg0)
 602 {
 603     return (jlong)CTRunGetGlyphCount((CTRunRef)arg0);
 604 }
 605 
 606 JNIEXPORT jlong JNICALL OS_NATIVE(CTRunGetAttributes)
 607     (JNIEnv *env, jclass that, jlong arg0)
 608 {
 609     return (jlong)CTRunGetAttributes((CTRunRef)arg0);
 610 }
 611 
 612 /**************************************************************************/
 613 /*                                                                        */
 614 /*                           Custom Functions                             */
 615 /*                                                                        */
 616 /**************************************************************************/
 617 
 618 
 619 JNIEXPORT jlong JNICALL OS_NATIVE(CFStringCreateWithCharacters__J_3CJJ)
 620     (JNIEnv *env, jclass that, jlong arg0, jcharArray arg1, jlong arg2, jlong arg3)
 621 {
 622     jchar *lparg1=NULL;
 623     jlong rc = 0;
 624     if (arg1) if ((lparg1 = (*env)->GetPrimitiveArrayCritical(env, arg1, NULL)) == NULL) goto fail;
 625     UniChar* str = lparg1 + arg2;
 626     rc = (jlong)CFStringCreateWithCharacters((CFAllocatorRef)arg0, str, (CFIndex)arg3);
 627 fail:
 628     if (arg1 && lparg1) (*env)->ReleasePrimitiveArrayCritical(env, arg1, lparg1, 0);
 629     return rc;
 630 }
 631 
 632 JNIEXPORT jint JNICALL OS_NATIVE(CTRunGetGlyphs)
 633     (JNIEnv *env, jclass that, jlong runRef, jint slotMask, jint start, jintArray bufferRef)
 634 {
 635     CTRunRef run = (CTRunRef)runRef;
 636     const CGGlyph * glyphs = CTRunGetGlyphsPtr(run);
 637     CFIndex count = CTRunGetGlyphCount(run);
 638     if (count == 0) {
 639         return 0;
 640     }
 641 
 642     CGGlyph* tempGlyphs = NULL;
 643     if (!glyphs) {
 644         tempGlyphs = (CGGlyph*) malloc(count * sizeof(CGGlyph));
 645         if (!tempGlyphs) {
 646             return 0;
 647         }
 648 
 649         CTRunGetGlyphs(run, CFRangeMake(0, 0), tempGlyphs);
 650         glyphs = tempGlyphs;
 651     }
 652 
 653     int i = 0;
 654     if (glyphs) {
 655         jint* buffer = (*env)->GetPrimitiveArrayCritical(env, bufferRef, NULL);
 656         if (buffer) {
 657             while(i < count) {
 658                 buffer[start + i] = slotMask | (glyphs[i] & 0xFFFF);
 659                 i++;
 660             }
 661             (*env)->ReleasePrimitiveArrayCritical(env, bufferRef, buffer, 0);
 662         }
 663     }
 664 
 665     if (tempGlyphs) {
 666         free(tempGlyphs);
 667     }
 668     return i;
 669 }
 670 
 671 JNIEXPORT jint JNICALL OS_NATIVE(CTRunGetPositions)
 672     (JNIEnv *env, jclass that, jlong runRef, jint start, jfloatArray bufferRef)
 673 {
 674     CTRunRef run = (CTRunRef)runRef;
 675     const CGPoint* positions = CTRunGetPositionsPtr(run);
 676     CFIndex count = CTRunGetGlyphCount(run);
 677     if (count == 0) {
 678         return 0;
 679     }
 680 
 681     CGPoint* tempPositions = NULL;
 682     if (!positions) {
 683         tempPositions = (CGPoint*) malloc(count * sizeof(CGPoint));
 684         if (!tempPositions) {
 685             return 0;
 686         }
 687 
 688         CTRunGetPositions(run, CFRangeMake(0, 0), tempPositions);
 689         positions = tempPositions;
 690     }
 691 
 692     int j = 0;
 693     if (positions) {
 694         jfloat* buffer = (*env)->GetPrimitiveArrayCritical(env, bufferRef, NULL);
 695         if (buffer) {
 696             int i = 0;
 697             while (i < count) {
 698                 CGPoint pos = positions[i++];
 699                 buffer[start + j++] = pos.x;
 700                 buffer[start + j++] = pos.y;
 701             }
 702             (*env)->ReleasePrimitiveArrayCritical(env, bufferRef, buffer, 0);
 703         }
 704     }
 705 
 706     if (tempPositions) {
 707         free(tempPositions);
 708     }
 709     return j;
 710 }
 711 
 712 JNIEXPORT jint JNICALL OS_NATIVE(CTRunGetStringIndices)
 713     (JNIEnv *env, jclass that, jlong runRef, jint start, jintArray bufferRef)
 714 {
 715     CTRunRef run = (CTRunRef)runRef;
 716     const CFIndex* indices = CTRunGetStringIndicesPtr(run);
 717     CFIndex count = CTRunGetGlyphCount(run);
 718     if (count == 0) {
 719         return 0;
 720     }
 721 
 722     CFIndex* tempIndices = NULL;
 723     if (!indices) {
 724         tempIndices = (CFIndex*) malloc(count * sizeof(CFIndex));
 725         if (!tempIndices) {
 726             return 0;
 727         }
 728 
 729         CTRunGetStringIndices(run, CFRangeMake(0, 0), tempIndices);
 730         indices = tempIndices;
 731     }
 732 
 733     int i = 0;
 734     if (indices) {
 735         jint* buffer = (*env)->GetPrimitiveArrayCritical(env, bufferRef, NULL);
 736         if (buffer) {
 737             while(i < count) {
 738                 buffer[start + i] = indices[i];
 739                 i++;
 740             }
 741             (*env)->ReleasePrimitiveArrayCritical(env, bufferRef, buffer, 0);
 742         }
 743 
 744         if (tempIndices) {
 745             free(tempIndices);
 746         }
 747     }
 748     return i;
 749 }
 750 
 751 JNIEXPORT jstring JNICALL OS_NATIVE(CTFontCopyAttributeDisplayName)
 752     (JNIEnv *env, jclass that, jlong arg0)
 753 {
 754     CFStringRef stringRef = CTFontCopyAttribute((CTFontRef)arg0, kCTFontDisplayNameAttribute);
 755     if (stringRef == NULL) return NULL;
 756     CFIndex length = CFStringGetLength(stringRef);
 757     UniChar buffer[length];
 758     CFStringGetCharacters(stringRef, CFRangeMake(0, length), buffer);
 759     CFRelease(stringRef);
 760     return (*env)->NewString(env, (jchar *)buffer, length);
 761 }
 762 
 763 JNIEXPORT jbyteArray JNICALL OS_NATIVE(CGBitmapContextGetData)
 764     (JNIEnv *env, jclass that, jlong arg0, jint dstWidth, jint dstHeight, jint bpp)
 765 {
 766     jbyteArray result = NULL;
 767     if (dstWidth < 0) return NULL;
 768     if (dstHeight < 0) return NULL;
 769     if (bpp != 8 && bpp != 24) return NULL;
 770     CGContextRef context = (CGContextRef)arg0;
 771     if (context == NULL) return NULL;
 772     jbyte *srcData = (jbyte*)CGBitmapContextGetData(context);
 773 
 774     if (srcData) {
 775         /* Use one byte per pixel for grayscale */
 776         size_t srcWidth = CGBitmapContextGetWidth(context);
 777         if (srcWidth < dstWidth) return NULL;
 778         size_t srcHeight =  CGBitmapContextGetHeight(context);
 779         if (srcHeight < dstHeight) return NULL;
 780         size_t srcBytesPerRow = CGBitmapContextGetBytesPerRow(context);
 781         size_t srcStep = CGBitmapContextGetBitsPerPixel(context) / 8;
 782         int srcOffset = (srcHeight - dstHeight) * srcBytesPerRow;
 783 
 784 
 785         //bits per pixel, either 8 for gray or 24 for LCD.
 786         int dstStep = bpp / 8;
 787         size_t size = dstWidth * dstHeight * dstStep;
 788         jbyte* data = (jbyte*)calloc(size, sizeof(jbyte));
 789         if (data == NULL) return NULL;
 790 
 791         int x, y, sx;
 792         int dstOffset = 0;
 793         for (y = 0; y < dstHeight; y++) {
 794             for (x = 0, sx = 0; x < dstWidth; x++, dstOffset += dstStep, sx += srcStep) {
 795                 if (dstStep == 1) {
 796                     /* BGRA or Gray to Gray */
 797                     data[dstOffset] = 0xFF - srcData[srcOffset + sx];
 798                 } else {
 799                     /* BGRA to RGB */
 800                     data[dstOffset]     = 0xFF - srcData[srcOffset + sx + 2];
 801                     data[dstOffset + 1] = 0xFF - srcData[srcOffset + sx + 1];
 802                     data[dstOffset + 2] = 0xFF - srcData[srcOffset + sx];
 803                 }
 804             }
 805             srcOffset += srcBytesPerRow;
 806         }
 807 
 808         result = (*env)->NewByteArray(env, size);
 809         if (result) {
 810             (*env)->SetByteArrayRegion(env, result, 0, size, data);
 811         }
 812         free(data);
 813     }
 814     return result;
 815 }
 816 
 817 JNIEXPORT void JNICALL OS_NATIVE(CGRectApplyAffineTransform)
 818     (JNIEnv *env, jclass that, jobject arg0, jobject arg1)
 819 {
 820     CGRect _arg0, *lparg0=NULL;
 821     CGAffineTransform _arg1, *lparg1=NULL;
 822     if (arg0) if ((lparg0 = getCGRectFields(env, arg0, &_arg0)) == NULL) goto fail;
 823     if (arg1) if ((lparg1 = getCGAffineTransformFields(env, arg1, &_arg1)) == NULL) goto fail;
 824     _arg0 = CGRectApplyAffineTransform(*lparg0, *lparg1);
 825 fail:
 826     /* In Only */
 827 //    if (arg1 && lparg1) setCGAffineTransformFields(env, arg1, lparg1);
 828     if (arg0 && lparg0) setCGRectFields(env, arg0, lparg0);
 829 }
 830 
 831 JNIEXPORT void JNICALL OS_NATIVE(CTFontDrawGlyphs)
 832     (JNIEnv *env, jclass that, jlong arg0, jshort arg1, jdouble arg2, jdouble arg3, jlong contextRef)
 833 {
 834     /* Custom: only takes one glyph at the time */
 835     CGGlyph glyphs[] = {arg1};
 836     CGPoint pos[] = {CGPointMake(arg2, arg3)};
 837     CTFontDrawGlyphs((CTFontRef)arg0, glyphs, pos, 1, (CGContextRef)contextRef);
 838 }
 839 
 840 JNIEXPORT jboolean JNICALL OS_NATIVE(CTFontGetBoundingRectForGlyphUsingTables)
 841     (JNIEnv *env, jclass that, jlong arg1, jshort arg2, jshort arg3, jintArray arg4)
 842 {
 843     /* The following code is based on scalerMethods.c#getGlyphBoundingBoxNative */
 844     CTFontRef fontRef = (CTFontRef)arg1;
 845     CTFontTableOptions options = kCTFontTableOptionNoOptions;
 846     CFDataRef tableData;
 847     CFIndex length;
 848 
 849     /* indexToLocFormat is stored in Java for performance */
 850 //    tableData = CTFontCopyTable(fontRef, kCTFontTableHead, options);
 851 //    const UInt8 * head = CFDataGetBytePtr(tableData);
 852 //    UInt16 indexToLocFormat = CFSwapInt16BigToHost(*((SInt16*)(head + 50)));
 853 //    printf("here0 indexToLocFormat=%u \n", indexToLocFormat); fflush(stdout);
 854 //    CFRelease(tableData);
 855     UInt16 indexToLocFormat = arg3;
 856 
 857     tableData = CTFontCopyTable(fontRef, kCTFontTableLoca, options);
 858     if (tableData == NULL) return FALSE;
 859     length = CFDataGetLength(tableData);
 860     UInt32 offset1 = 0, offset2 = 0;
 861     UInt32 index = arg2 & 0xFFFF;
 862     if (indexToLocFormat) {
 863         const UInt32 * loca = (const UInt32 *)CFDataGetBytePtr(tableData);
 864         if (loca != NULL && (index + 1) < (length / 4)) {
 865             offset1 = CFSwapInt32BigToHost(loca[index]);
 866             offset2 = CFSwapInt32BigToHost(loca[index + 1]);
 867         }
 868     } else {
 869         const UInt16 * loca = (const UInt16 *)CFDataGetBytePtr(tableData);
 870         if (loca != NULL && (index + 1) < (length / 2)) {
 871             offset1 = CFSwapInt16BigToHost(loca[index]) << 1;
 872             offset2 = CFSwapInt16BigToHost(loca[index + 1]) << 1;
 873         }
 874     }
 875     CFRelease(tableData);
 876     jboolean result = FALSE;
 877     if (offset2 > offset1 && (offset2 - offset1) >= 10) {
 878         tableData = CTFontCopyTable(fontRef, kCTFontTableGlyf, options);
 879         if (tableData == NULL) return FALSE;
 880         length = CFDataGetLength(tableData);
 881         const UInt8 * ptr = CFDataGetBytePtr(tableData);
 882         if (ptr != NULL && (offset1 + 10) < length) {
 883             const SInt16 * glyf = (const SInt16 *)(ptr + offset1);
 884             /*
 885              * CFSwapInt16BigToHost returns an unsigned short, need
 886              * to cast back to signed short before assigning to jint.
 887              */
 888             jint data[] = {
 889                 (SInt16)CFSwapInt16BigToHost(glyf[1]),
 890                 (SInt16)CFSwapInt16BigToHost(glyf[2]),
 891                 (SInt16)CFSwapInt16BigToHost(glyf[3]),
 892                 (SInt16)CFSwapInt16BigToHost(glyf[4]),
 893             };
 894             (*env)->SetIntArrayRegion(env, arg4, 0, 4, data);
 895             result = TRUE;
 896         }
 897         CFRelease(tableData);
 898     }
 899     return result;
 900 }
 901 
 902 JNIEXPORT jdouble JNICALL OS_NATIVE(CTFontGetAdvancesForGlyphs)
 903     (JNIEnv *env, jclass that, jlong arg0, jint arg1, jshort arg2, jobject arg3)
 904 {
 905     /* Custom: only takes one glyph at the time */
 906     jdouble rc = 0;
 907     CGGlyph glyphs[] = {arg2};
 908     CGSize _arg3, *lparg3=NULL;
 909     if (arg3) if ((lparg3 = getCGSizeFields(env, arg3, &_arg3)) == NULL) goto fail;
 910     rc = (jdouble)CTFontGetAdvancesForGlyphs((CTFontRef)arg0, (CTFontOrientation)arg1, glyphs, lparg3, 1);
 911 fail:
 912     if (arg3 && lparg3) setCGSizeFields(env, arg3, &_arg3);
 913     return rc;
 914 }
 915 
 916 JNIEXPORT jobject JNICALL OS_NATIVE(CGPathGetPathBoundingBox)
 917     (JNIEnv *env, jclass that, jlong arg0)
 918 {
 919     CGRect result = CGPathGetPathBoundingBox((CGPathRef)arg0);
 920     return newCGRect(env, &result);
 921 }
 922 
 923 JNIEXPORT jlong JNICALL OS_NATIVE(CTParagraphStyleCreate)
 924     (JNIEnv *env, jclass that, jint arg0)
 925 {
 926     CTWritingDirection dir = (CTWritingDirection)arg0;
 927     CTParagraphStyleSetting settings[] = {
 928         {kCTParagraphStyleSpecifierBaseWritingDirection, sizeof(dir), &dir}
 929     };
 930     return (jlong)CTParagraphStyleCreate(settings, sizeof(settings) / sizeof(settings[0]));
 931 }
 932 
 933 /***********************************************/
 934 /*                Glyph Outline                */
 935 /***********************************************/
 936 
 937 static const int DEFAULT_LEN_TYPES = 10;
 938 static const int DEFAULT_LEN_COORDS = 50;
 939 typedef struct _PathData {
 940     jbyte* pointTypes;
 941     int numTypes;
 942     int lenTypes;
 943     jfloat* pointCoords;
 944     int numCoords;
 945     int lenCoords;
 946 } PathData;
 947 
 948 void pathApplierFunctionFast(void *i, const CGPathElement *e) {
 949     PathData *info = (PathData *)i;
 950     if (info->numTypes == info->lenTypes) {
 951         info->lenTypes += DEFAULT_LEN_TYPES;
 952         info->pointTypes = (jbyte*)realloc(info->pointTypes, info->lenTypes * sizeof(jbyte));
 953     }
 954     jint type;
 955     int coordCount = 0;
 956     switch (e->type) {
 957     case kCGPathElementMoveToPoint:
 958         type = 0;
 959         coordCount = 1;
 960         break;
 961     case kCGPathElementAddLineToPoint:
 962         type = 1;
 963         coordCount = 1;
 964         break;
 965     case kCGPathElementAddQuadCurveToPoint:
 966         type = 2;
 967         coordCount = 2;
 968         break;
 969     case kCGPathElementAddCurveToPoint:
 970         type = 3;
 971         coordCount = 3;
 972         break;
 973     case kCGPathElementCloseSubpath:
 974         type = 4;
 975         coordCount = 0;
 976         break;
 977     }
 978     info->pointTypes[info->numTypes++] = type;
 979 
 980     if (info->numCoords + (coordCount * 2) > info->lenCoords) {
 981         info->lenCoords += DEFAULT_LEN_COORDS;
 982         info->pointCoords = (jfloat*)realloc(info->pointCoords, info->lenCoords * sizeof(jfloat));
 983     }
 984     int j;
 985     for (j = 0; j < coordCount; j++) {
 986         CGPoint pt = e->points[j];
 987         info->pointCoords[info->numCoords++] = pt.x;
 988         info->pointCoords[info->numCoords++] = pt.y;
 989     }
 990 }
 991 
 992 JNIEXPORT jobject JNICALL OS_NATIVE(CGPathApply)
 993     (JNIEnv *env, jclass that, jlong arg0)
 994 {
 995     jobject path2D = NULL;
 996     PathData data;
 997     data.pointTypes = (jbyte*)malloc(sizeof(jbyte) * DEFAULT_LEN_TYPES);
 998     data.numTypes = 0;
 999     data.lenTypes = DEFAULT_LEN_TYPES;
1000     data.pointCoords = (jfloat*)malloc(sizeof(jfloat) * DEFAULT_LEN_COORDS);
1001     data.numCoords = 0;
1002     data.lenCoords = DEFAULT_LEN_COORDS;
1003 
1004     CGPathApply((CGPathRef)arg0, &data, pathApplierFunctionFast);
1005 
1006     static jclass path2DClass = NULL;
1007     static jmethodID path2DCtr = NULL;
1008     if (path2DClass == NULL) {
1009         jclass tmpClass = (*env)->FindClass(env, "com/sun/javafx/geom/Path2D");
1010         if ((*env)->ExceptionOccurred(env) || !tmpClass) {
1011             fprintf(stderr, "OS_NATIVE error: JNI exception or tmpClass == NULL");
1012             goto fail;
1013         }
1014         path2DClass = (jclass)(*env)->NewGlobalRef(env, tmpClass);
1015         path2DCtr = (*env)->GetMethodID(env, path2DClass, "<init>", "(I[BI[FI)V");
1016         if ((*env)->ExceptionOccurred(env) || !path2DCtr) {
1017             fprintf(stderr, "OS_NATIVE error: JNI exception or path2DCtr == NULL");
1018             goto fail;
1019         }
1020     }
1021 
1022     jbyteArray types = (*env)->NewByteArray(env, data.numTypes);
1023     jfloatArray coords = (*env)->NewFloatArray(env, data.numCoords);
1024     if (types && coords) {
1025         (*env)->SetByteArrayRegion(env, types, 0, data.numTypes, data.pointTypes);
1026         if ((*env)->ExceptionOccurred(env)) {
1027             fprintf(stderr, "OS_NATIVE error: JNI exception");
1028             goto fail;
1029         }
1030         (*env)->SetFloatArrayRegion(env, coords, 0, data.numCoords, data.pointCoords);
1031         if ((*env)->ExceptionOccurred(env)) {
1032             fprintf(stderr, "OS_NATIVE error: JNI exception");
1033             goto fail;
1034         }
1035         path2D = (*env)->NewObject(env, path2DClass, path2DCtr,
1036                                    0 /*winding rule*/,
1037                                    types, data.numTypes,
1038                                    coords, data.numCoords);
1039         if ((*env)->ExceptionOccurred(env) || !path2D) {
1040             goto fail;
1041         }
1042     }
1043 fail:
1044     free(data.pointTypes);
1045     free(data.pointCoords);
1046     return path2D;
1047 }
1048 
1049 #endif /* TARGET_OS_MAC */
1050 #endif /* __APPLE__ */
1051