1 /*
   2  * Copyright (c) 2007, 2019, 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 #include "jni.h"
  27 #include "jni_util.h"
  28 #include "jlong.h"
  29 #include "sunfontids.h"
  30 #include "sun_font_FreetypeFontScaler.h"
  31 
  32 #include<stdlib.h>
  33 #include <math.h>
  34 #include "ft2build.h"
  35 #include FT_FREETYPE_H
  36 #include FT_GLYPH_H
  37 #include FT_BBOX_H
  38 #include FT_SIZES_H
  39 #include FT_OUTLINE_H
  40 #include FT_SYNTHESIS_H
  41 #include FT_LCD_FILTER_H
  42 
  43 #include "fontscaler.h"
  44 
  45 #define  ftFixed1  (FT_Fixed) (1 << 16)
  46 #define  FloatToFTFixed(f) (FT_Fixed)((f) * (float)(ftFixed1))
  47 #define  FTFixedToFloat(x) ((x) / (float)(ftFixed1))
  48 #define  FT26Dot6ToFloat(x)  ((x) / ((float) (1<<6)))
  49 #define  FT26Dot6ToInt(x) (((int)(x)) >> 6)
  50 
  51 typedef struct {
  52     /* Important note:
  53          JNI forbids sharing same env between different threads.
  54          We are safe, because pointer is overwritten every time we get into
  55          JNI call (see setupFTContext).
  56 
  57          Pointer is used by font data reading callbacks
  58          such as ReadTTFontFileFunc.
  59 
  60          NB: We may consider switching to JNI_GetEnv. */
  61     JNIEnv* env;
  62     FT_Library library;
  63     FT_Face face;
  64     FT_Stream faceStream;
  65     jobject font2D;
  66     jobject directBuffer;
  67 
  68     unsigned char* fontData;
  69     unsigned fontDataOffset;
  70     unsigned fontDataLength;
  71     unsigned fileSize;
  72     TTLayoutTableCache* layoutTables;
  73 } FTScalerInfo;
  74 
  75 typedef struct FTScalerContext {
  76     FT_Matrix  transform;     /* glyph transform, including device transform */
  77     jboolean   useSbits;      /* sbit usage enabled? */
  78     jint       aaType;        /* antialiasing mode (off/on/grey/lcd) */
  79     jint       fmType;        /* fractional metrics - on/off */
  80     jboolean   doBold;        /* perform algorithmic bolding? */
  81     jboolean   doItalize;     /* perform algorithmic italicizing? */
  82     int        renderFlags;   /* configuration specific to particular engine */
  83     int        pathType;
  84     int        ptsz;          /* size in points */
  85 } FTScalerContext;
  86 
  87 #ifdef DEBUG
  88 /* These are referenced in the freetype sources if DEBUG macro is defined.
  89    To simplify work with debuging version of freetype we define
  90    them here. */
  91 int z_verbose;
  92 void z_error(char *s) {}
  93 #endif
  94 
  95 /**************** Error handling utilities *****************/
  96 
  97 static jmethodID invalidateScalerMID;
  98 
  99 JNIEXPORT void JNICALL
 100 Java_sun_font_FreetypeFontScaler_initIDs(
 101         JNIEnv *env, jobject scaler, jclass FFSClass) {
 102     invalidateScalerMID =
 103         (*env)->GetMethodID(env, FFSClass, "invalidateScaler", "()V");
 104 }
 105 
 106 static void freeNativeResources(JNIEnv *env, FTScalerInfo* scalerInfo) {
 107 
 108     if (scalerInfo == NULL)
 109         return;
 110 
 111     // FT_Done_Face always closes the stream, but only frees the memory
 112     // of the data structure if it was internally allocated by FT.
 113     // We hold on to a pointer to the stream structure if we provide it
 114     // ourselves, so that we can free it here.
 115     FT_Done_Face(scalerInfo->face);
 116     FT_Done_FreeType(scalerInfo->library);
 117 
 118     if (scalerInfo->directBuffer != NULL) {
 119         (*env)->DeleteGlobalRef(env, scalerInfo->directBuffer);
 120     }
 121 
 122     if (scalerInfo->fontData != NULL) {
 123         free(scalerInfo->fontData);
 124     }
 125 
 126     if (scalerInfo->faceStream != NULL) {
 127         free(scalerInfo->faceStream);
 128     }
 129     free(scalerInfo);
 130 }
 131 
 132 /* invalidates state of java scaler object */
 133 static void invalidateJavaScaler(JNIEnv *env,
 134                                  jobject scaler,
 135                                  FTScalerInfo* scalerInfo) {
 136     freeNativeResources(env, scalerInfo);
 137     (*env)->CallVoidMethod(env, scaler, invalidateScalerMID);
 138 }
 139 
 140 /******************* I/O handlers ***************************/
 141 
 142 #define FILEDATACACHESIZE 1024
 143 
 144 static unsigned long ReadTTFontFileFunc(FT_Stream stream,
 145                                         unsigned long offset,
 146                                         unsigned char* destBuffer,
 147                                         unsigned long numBytes)
 148 {
 149     FTScalerInfo *scalerInfo = (FTScalerInfo *) stream->pathname.pointer;
 150     JNIEnv* env = scalerInfo->env;
 151     jobject bBuffer;
 152     int bread = 0;
 153 
 154     if (numBytes == 0) return 0;
 155 
 156     /* Large reads will bypass the cache and data copying */
 157     if (numBytes > FILEDATACACHESIZE) {
 158         bBuffer = (*env)->NewDirectByteBuffer(env, destBuffer, numBytes);
 159         if (bBuffer != NULL) {
 160             bread = (*env)->CallIntMethod(env,
 161                                           scalerInfo->font2D,
 162                                           sunFontIDs.ttReadBlockMID,
 163                                           bBuffer, offset, numBytes);
 164             return bread;
 165         } else {
 166             /* We probably hit bug 4845371. For reasons that
 167              * are currently unclear, the call stacks after the initial
 168              * createScaler call that read large amounts of data seem to
 169              * be OK and can create the byte buffer above, but this code
 170              * is here just in case.
 171              * 4845371 is fixed now so I don't expect this code path to
 172              * ever get called but its harmless to leave it here on the
 173              * small chance its needed.
 174              */
 175             jbyteArray byteArray = (jbyteArray)
 176             (*env)->CallObjectMethod(env, scalerInfo->font2D,
 177                                      sunFontIDs.ttReadBytesMID,
 178                                      offset, numBytes);
 179             (*env)->GetByteArrayRegion(env, byteArray,
 180                                        0, numBytes, (jbyte*)destBuffer);
 181             return numBytes;
 182         }
 183     } /* Do we have a cache hit? */
 184       else if (scalerInfo->fontDataOffset <= offset &&
 185         scalerInfo->fontDataOffset + scalerInfo->fontDataLength >=
 186                                                          offset + numBytes)
 187     {
 188         unsigned cacheOffset = offset - scalerInfo->fontDataOffset;
 189 
 190         memcpy(destBuffer, scalerInfo->fontData+(size_t)cacheOffset, numBytes);
 191         return numBytes;
 192     } else {
 193         /* Must fill the cache */
 194         scalerInfo->fontDataOffset = offset;
 195         scalerInfo->fontDataLength =
 196                  (offset + FILEDATACACHESIZE > scalerInfo->fileSize) ?
 197                  scalerInfo->fileSize - offset : FILEDATACACHESIZE;
 198         bBuffer = scalerInfo->directBuffer;
 199         bread = (*env)->CallIntMethod(env, scalerInfo->font2D,
 200                                       sunFontIDs.ttReadBlockMID,
 201                                       bBuffer, offset,
 202                                       scalerInfo->fontDataLength);
 203         memcpy(destBuffer, scalerInfo->fontData, numBytes);
 204         return numBytes;
 205     }
 206 }
 207 
 208 /*
 209  * Class:     sun_font_FreetypeFontScaler
 210  * Method:    initNativeScaler
 211  * Signature: (Lsun/font/Font2D;IIZI)J
 212  */
 213 JNIEXPORT jlong JNICALL
 214 Java_sun_font_FreetypeFontScaler_initNativeScaler(
 215         JNIEnv *env, jobject scaler, jobject font2D, jint type,
 216         jint indexInCollection, jboolean supportsCJK, jint filesize) {
 217     FTScalerInfo* scalerInfo = NULL;
 218     FT_Open_Args ft_open_args;
 219     int error;
 220     jobject bBuffer;
 221     scalerInfo = (FTScalerInfo*) calloc(1, sizeof(FTScalerInfo));
 222 
 223     if (scalerInfo == NULL)
 224         return 0;
 225 
 226     scalerInfo->env = env;
 227     scalerInfo->font2D = font2D;
 228     scalerInfo->fontDataOffset = 0;
 229     scalerInfo->fontDataLength = 0;
 230     scalerInfo->fileSize = filesize;
 231 
 232     /*
 233        We can consider sharing freetype library between different
 234        scalers. However, Freetype docs suggest to use different libraries
 235        for different threads. Also, our architecture implies that single
 236        FontScaler object is shared for different sizes/transforms/styles
 237        of the same font.
 238 
 239        On other hand these methods can not be concurrently executed
 240        becaused they are "synchronized" in java.
 241     */
 242     error = FT_Init_FreeType(&scalerInfo->library);
 243     if (error) {
 244         free(scalerInfo);
 245         return 0;
 246     }
 247 
 248 #define TYPE1_FROM_JAVA        2
 249 
 250     error = 1; /* triggers memory freeing unless we clear it */
 251     if (type == TYPE1_FROM_JAVA) { /* TYPE1 */
 252         scalerInfo->fontData = (unsigned char*) malloc(filesize);
 253         scalerInfo->directBuffer = NULL;
 254         scalerInfo->layoutTables = NULL;
 255         scalerInfo->fontDataLength = filesize;
 256 
 257         if (scalerInfo->fontData != NULL) {
 258             bBuffer = (*env)->NewDirectByteBuffer(env,
 259                                               scalerInfo->fontData,
 260                                               scalerInfo->fontDataLength);
 261             if (bBuffer != NULL) {
 262                 (*env)->CallVoidMethod(env, font2D,
 263                                    sunFontIDs.readFileMID, bBuffer);
 264 
 265                 error = FT_New_Memory_Face(scalerInfo->library,
 266                                    scalerInfo->fontData,
 267                                    scalerInfo->fontDataLength,
 268                                    indexInCollection,
 269                                    &scalerInfo->face);
 270             }
 271         }
 272     } else { /* Truetype */
 273         scalerInfo->fontData = (unsigned char*) malloc(FILEDATACACHESIZE);
 274 
 275         if (scalerInfo->fontData != NULL) {
 276             FT_Stream ftstream = (FT_Stream) calloc(1, sizeof(FT_StreamRec));
 277             if (ftstream != NULL) {
 278                 scalerInfo->directBuffer = (*env)->NewDirectByteBuffer(env,
 279                                            scalerInfo->fontData,
 280                                            FILEDATACACHESIZE);
 281                 if (scalerInfo->directBuffer != NULL) {
 282                     scalerInfo->directBuffer = (*env)->NewGlobalRef(env,
 283                                                scalerInfo->directBuffer);
 284                     ftstream->base = NULL;
 285                     ftstream->size = filesize;
 286                     ftstream->pos = 0;
 287                     ftstream->read = (FT_Stream_IoFunc) ReadTTFontFileFunc;
 288                     ftstream->close = NULL;
 289                     ftstream->pathname.pointer = (void *) scalerInfo;
 290 
 291                     memset(&ft_open_args, 0, sizeof(FT_Open_Args));
 292                     ft_open_args.flags = FT_OPEN_STREAM;
 293                     ft_open_args.stream = ftstream;
 294 
 295                     error = FT_Open_Face(scalerInfo->library,
 296                                          &ft_open_args,
 297                                          indexInCollection,
 298                                          &scalerInfo->face);
 299                     if (!error) {
 300                         scalerInfo->faceStream = ftstream;
 301                     }
 302                 }
 303                 if (error || scalerInfo->directBuffer == NULL) {
 304                     free(ftstream);
 305                 }
 306             }
 307         }
 308     }
 309 
 310     if (error) {
 311         FT_Done_FreeType(scalerInfo->library);
 312         if (scalerInfo->directBuffer != NULL) {
 313             (*env)->DeleteGlobalRef(env, scalerInfo->directBuffer);
 314         }
 315         if (scalerInfo->fontData != NULL)
 316             free(scalerInfo->fontData);
 317         free(scalerInfo);
 318         return 0;
 319     }
 320 
 321     return ptr_to_jlong(scalerInfo);
 322 }
 323 
 324 static double euclidianDistance(double a, double b) {
 325     if (a < 0) a=-a;
 326     if (b < 0) b=-b;
 327 
 328     if (a == 0) return b;
 329     if (b == 0) return a;
 330 
 331     return sqrt(a*a+b*b);
 332 }
 333 
 334 JNIEXPORT jlong JNICALL
 335 Java_sun_font_FreetypeFontScaler_createScalerContextNative(
 336         JNIEnv *env, jobject scaler, jlong pScaler, jdoubleArray matrix,
 337         jint aa, jint fm, jfloat boldness, jfloat italic) {
 338     double dmat[4], ptsz;
 339     FTScalerContext *context =
 340             (FTScalerContext*) calloc(1, sizeof(FTScalerContext));
 341     FTScalerInfo *scalerInfo =
 342              (FTScalerInfo*) jlong_to_ptr(pScaler);
 343 
 344     if (context == NULL) {
 345         invalidateJavaScaler(env, scaler, NULL);
 346         return (jlong) 0;
 347     }
 348     (*env)->GetDoubleArrayRegion(env, matrix, 0, 4, dmat);
 349     ptsz = euclidianDistance(dmat[2], dmat[3]); //i.e. y-size
 350     if (ptsz < 1.0) {
 351         //text can not be smaller than 1 point
 352         ptsz = 1.0;
 353     }
 354     context->ptsz = (int)(ptsz * 64);
 355     context->transform.xx =  FloatToFTFixed((float)dmat[0]/ptsz);
 356     context->transform.yx = -FloatToFTFixed((float)dmat[1]/ptsz);
 357     context->transform.xy = -FloatToFTFixed((float)dmat[2]/ptsz);
 358     context->transform.yy =  FloatToFTFixed((float)dmat[3]/ptsz);
 359     context->aaType = aa;
 360     context->fmType = fm;
 361 
 362     /* If using algorithmic styling, the base values are
 363      * boldness = 1.0, italic = 0.0.
 364      */
 365     context->doBold = (boldness != 1.0);
 366     context->doItalize = (italic != 0);
 367 
 368     /* freetype is very keen to use embedded bitmaps, even if it knows
 369      * there is a rotation or you asked for antialiasing.
 370      * In the rendering path we will check useSBits and disable
 371      * bitmaps unless it is set. And here we set it only if none
 372      * of the conditions invalidate using it.
 373      * Note that we allow embedded bitmaps for the LCD case.
 374      */
 375     if ((aa != TEXT_AA_ON) && (fm != TEXT_FM_ON) &&
 376         !context->doBold && !context->doItalize &&
 377         (context->transform.yx == 0) && (context->transform.xy == 0))
 378     {
 379         context->useSbits = 1;
 380     }
 381     return ptr_to_jlong(context);
 382 }
 383 
 384 static int setupFTContext(JNIEnv *env,
 385                           jobject font2D,
 386                           FTScalerInfo *scalerInfo,
 387                           FTScalerContext *context) {
 388     int errCode = 0;
 389 
 390     scalerInfo->env = env;
 391     scalerInfo->font2D = font2D;
 392 
 393     if (context != NULL) {
 394         FT_Set_Transform(scalerInfo->face, &context->transform, NULL);
 395 
 396         errCode = FT_Set_Char_Size(scalerInfo->face, 0, context->ptsz, 72, 72);
 397 
 398         if (errCode == 0) {
 399             errCode = FT_Activate_Size(scalerInfo->face->size);
 400         }
 401 
 402         FT_Library_SetLcdFilter(scalerInfo->library, FT_LCD_FILTER_DEFAULT);
 403     }
 404 
 405     return errCode;
 406 }
 407 
 408 /* ftsynth.c uses (0x10000, 0x0366A, 0x0, 0x10000) matrix to get oblique
 409    outline.  Therefore x coordinate will change by 0x0366A*y.
 410    Note that y coordinate does not change. These values are based on
 411    libfreetype version 2.9.1. */
 412 #define OBLIQUE_MODIFIER(y)  (context->doItalize ? ((y)*0x366A/0x10000) : 0)
 413 
 414 /* FT_GlyphSlot_Embolden (ftsynth.c) uses FT_MulFix(units_per_EM, y_scale) / 24
 415  * strength value when glyph format is FT_GLYPH_FORMAT_OUTLINE. This value has
 416  * been taken from libfreetype version 2.6 and remain valid at least up to
 417  * 2.9.1. */
 418 #define BOLD_MODIFIER(units_per_EM, y_scale) \
 419     (context->doBold ? FT_MulFix(units_per_EM, y_scale) / 24 : 0)
 420 
 421 /*
 422  * Class:     sun_font_FreetypeFontScaler
 423  * Method:    getFontMetricsNative
 424  * Signature: (Lsun/font/Font2D;J)Lsun/font/StrikeMetrics;
 425  */
 426 JNIEXPORT jobject JNICALL
 427 Java_sun_font_FreetypeFontScaler_getFontMetricsNative(
 428         JNIEnv *env, jobject scaler, jobject font2D,
 429         jlong pScalerContext, jlong pScaler) {
 430 
 431     jobject metrics;
 432     jfloat ax, ay, dx, dy, bx, by, lx, ly, mx, my;
 433     jfloat f0 = 0.0;
 434     FTScalerContext *context =
 435         (FTScalerContext*) jlong_to_ptr(pScalerContext);
 436     FTScalerInfo *scalerInfo =
 437              (FTScalerInfo*) jlong_to_ptr(pScaler);
 438 
 439     int errCode;
 440 
 441     if (isNullScalerContext(context) || scalerInfo == NULL) {
 442         return (*env)->NewObject(env,
 443                                  sunFontIDs.strikeMetricsClass,
 444                                  sunFontIDs.strikeMetricsCtr,
 445                                  f0, f0, f0, f0, f0, f0, f0, f0, f0, f0);
 446     }
 447 
 448     errCode = setupFTContext(env, font2D, scalerInfo, context);
 449 
 450     if (errCode) {
 451         metrics = (*env)->NewObject(env,
 452                                  sunFontIDs.strikeMetricsClass,
 453                                  sunFontIDs.strikeMetricsCtr,
 454                                  f0, f0, f0, f0, f0, f0, f0, f0, f0, f0);
 455         invalidateJavaScaler(env, scaler, scalerInfo);
 456         return metrics;
 457     }
 458 
 459     /* This is ugly and has to be reworked.
 460        Freetype provide means to add style to glyph but
 461        it seems there is no way to adjust metrics accordingly.
 462 
 463        So, we have to do adust them explicitly and stay consistent with what
 464        freetype does to outlines. */
 465 
 466 
 467     /**** Note: only some metrics are affected by styling ***/
 468 
 469     /* See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=657854 */
 470 #define FT_MulFixFloatShift6(a, b) (((float) (a)) * ((float) (b)) / 65536.0 / 64.0)
 471 
 472 #define contextAwareMetricsX(x, y) \
 473     (FTFixedToFloat(context->transform.xx) * (x) - \
 474      FTFixedToFloat(context->transform.xy) * (y))
 475 
 476 #define contextAwareMetricsY(x, y) \
 477     (-FTFixedToFloat(context->transform.yx) * (x) + \
 478      FTFixedToFloat(context->transform.yy) * (y))
 479 
 480     /*
 481      * See FreeType source code: src/base/ftobjs.c ft_recompute_scaled_metrics()
 482      * http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=1659
 483      */
 484     /* ascent */
 485     ax = 0;
 486     ay = -(jfloat) (FT_MulFixFloatShift6(
 487                        ((jlong) scalerInfo->face->ascender),
 488                        (jlong) scalerInfo->face->size->metrics.y_scale));
 489     /* descent */
 490     dx = 0;
 491     dy = -(jfloat) (FT_MulFixFloatShift6(
 492                        ((jlong) scalerInfo->face->descender),
 493                        (jlong) scalerInfo->face->size->metrics.y_scale));
 494     /* baseline */
 495     bx = by = 0;
 496 
 497     /* leading */
 498     lx = 0;
 499     ly = (jfloat) (FT_MulFixFloatShift6(
 500                       (jlong) scalerInfo->face->height,
 501                       (jlong) scalerInfo->face->size->metrics.y_scale))
 502                   + ay - dy;
 503     /* max advance */
 504     mx = (jfloat) FT26Dot6ToFloat(
 505                      scalerInfo->face->size->metrics.max_advance +
 506                      OBLIQUE_MODIFIER(scalerInfo->face->size->metrics.height) +
 507                      BOLD_MODIFIER(scalerInfo->face->units_per_EM,
 508                              scalerInfo->face->size->metrics.y_scale));
 509     my = 0;
 510 
 511     metrics = (*env)->NewObject(env,
 512         sunFontIDs.strikeMetricsClass,
 513         sunFontIDs.strikeMetricsCtr,
 514         contextAwareMetricsX(ax, ay), contextAwareMetricsY(ax, ay),
 515         contextAwareMetricsX(dx, dy), contextAwareMetricsY(dx, dy),
 516         bx, by,
 517         contextAwareMetricsX(lx, ly), contextAwareMetricsY(lx, ly),
 518         contextAwareMetricsX(mx, my), contextAwareMetricsY(mx, my));
 519 
 520     return metrics;
 521 }
 522 
 523 /*
 524  * Class:     sun_font_FreetypeFontScaler
 525  * Method:    getGlyphAdvanceNative
 526  * Signature: (Lsun/font/Font2D;JI)F
 527  */
 528 JNIEXPORT jfloat JNICALL
 529 Java_sun_font_FreetypeFontScaler_getGlyphAdvanceNative(
 530         JNIEnv *env, jobject scaler, jobject font2D,
 531         jlong pScalerContext, jlong pScaler, jint glyphCode) {
 532 
 533    /* This method is rarely used because requests for metrics are usually
 534       coupled with request for bitmap and to large extend work can be reused
 535       (to find out metrics we need to hint glyph).
 536       So, we typically go through getGlyphImage code path.
 537 
 538       For initial freetype implementation we delegate
 539       all work to getGlyphImage but drop result image.
 540       This is waste of work related to scan conversion and conversion from
 541       freetype format to our format but for now this seems to be ok.
 542 
 543       NB: investigate performance benefits of refactoring code
 544       to avoid unnecesary work with bitmaps. */
 545 
 546     GlyphInfo *info;
 547     jfloat advance;
 548     jlong image;
 549 
 550     image = Java_sun_font_FreetypeFontScaler_getGlyphImageNative(
 551                  env, scaler, font2D, pScalerContext, pScaler, glyphCode);
 552     info = (GlyphInfo*) jlong_to_ptr(image);
 553 
 554     advance = info->advanceX;
 555 
 556     free(info);
 557 
 558     return advance;
 559 }
 560 
 561 /*
 562  * Class:     sun_font_FreetypeFontScaler
 563  * Method:    getGlyphMetricsNative
 564  * Signature: (Lsun/font/Font2D;JILjava/awt/geom/Point2D/Float;)V
 565  */
 566 JNIEXPORT void JNICALL
 567 Java_sun_font_FreetypeFontScaler_getGlyphMetricsNative(
 568         JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
 569         jlong pScaler, jint glyphCode, jobject metrics) {
 570 
 571      /* As initial implementation we delegate all work to getGlyphImage
 572         but drop result image. This is clearly waste of resorces.
 573 
 574         TODO: investigate performance benefits of refactoring code
 575               by avoiding bitmap generation and conversion from FT
 576               bitmap format. */
 577      GlyphInfo *info;
 578 
 579      jlong image = Java_sun_font_FreetypeFontScaler_getGlyphImageNative(
 580                                  env, scaler, font2D,
 581                                  pScalerContext, pScaler, glyphCode);
 582      info = (GlyphInfo*) jlong_to_ptr(image);
 583 
 584      (*env)->SetFloatField(env, metrics, sunFontIDs.xFID, info->advanceX);
 585      (*env)->SetFloatField(env, metrics, sunFontIDs.yFID, info->advanceY);
 586 
 587      free(info);
 588 }
 589 
 590 
 591 static GlyphInfo* getNullGlyphImage() {
 592     GlyphInfo *glyphInfo =  (GlyphInfo*) calloc(1, sizeof(GlyphInfo));
 593     return glyphInfo;
 594 }
 595 
 596 static void CopyBW2Grey8(const void* srcImage, int srcRowBytes,
 597                          void* dstImage, int dstRowBytes,
 598                          int width, int height) {
 599     const UInt8* srcRow = (UInt8*)srcImage;
 600     UInt8* dstRow = (UInt8*)dstImage;
 601     int wholeByteCount = width >> 3;
 602     int remainingBitsCount = width & 7;
 603     int i, j;
 604 
 605     while (height--) {
 606         const UInt8* src8 = srcRow;
 607         UInt8* dstByte = dstRow;
 608         unsigned srcValue;
 609 
 610         srcRow += srcRowBytes;
 611         dstRow += dstRowBytes;
 612 
 613         for (i = 0; i < wholeByteCount; i++) {
 614             srcValue = *src8++;
 615             for (j = 0; j < 8; j++) {
 616                 *dstByte++ = (srcValue & 0x80) ? 0xFF : 0;
 617                 srcValue <<= 1;
 618             }
 619         }
 620         if (remainingBitsCount) {
 621             srcValue = *src8;
 622             for (j = 0; j < remainingBitsCount; j++) {
 623                 *dstByte++ = (srcValue & 0x80) ? 0xFF : 0;
 624                 srcValue <<= 1;
 625             }
 626         }
 627     }
 628 }
 629 
 630 #define Grey4ToAlpha255(value) (((value) << 4) + ((value) >> 3))
 631 
 632 static void CopyGrey4ToGrey8(const void* srcImage, int srcRowBytes,
 633                 void* dstImage, int dstRowBytes, int width, int height) {
 634      const UInt8* srcRow = (UInt8*) srcImage;
 635      UInt8* dstRow = (UInt8*) dstImage;
 636      int i;
 637 
 638      while (height--) {
 639          const UInt8* src8 = srcRow;
 640          UInt8* dstByte = dstRow;
 641          unsigned srcValue;
 642 
 643          srcRow += srcRowBytes;
 644          dstRow += dstRowBytes;
 645 
 646          for (i = 0; i < width; i++) {
 647              srcValue = *src8++;
 648              *dstByte++ = Grey4ToAlpha255(srcValue & 0x0f);
 649              *dstByte++ = Grey4ToAlpha255(srcValue >> 4);
 650          }
 651      }
 652 }
 653 
 654 /* We need it because FT rows are often padded to 4 byte boundaries
 655     and our internal format is not padded */
 656 static void CopyFTSubpixelToSubpixel(const void* srcImage, int srcRowBytes,
 657                                      void* dstImage, int dstRowBytes,
 658                                      int width, int height) {
 659     unsigned char *srcRow = (unsigned char *) srcImage;
 660     unsigned char *dstRow = (unsigned char *) dstImage;
 661 
 662     while (height--) {
 663         memcpy(dstRow, srcRow, width);
 664         srcRow += srcRowBytes;
 665         dstRow += dstRowBytes;
 666     }
 667 }
 668 
 669 /* We need it because FT rows are often padded to 4 byte boundaries
 670    and our internal format is not padded */
 671 static void CopyFTSubpixelVToSubpixel(const void* srcImage, int srcRowBytes,
 672                                       void* dstImage, int dstRowBytes,
 673                                       int width, int height) {
 674     unsigned char *srcRow = (unsigned char *) srcImage, *srcByte;
 675     unsigned char *dstRow = (unsigned char *) dstImage, *dstByte;
 676     int i;
 677 
 678     while (height > 0) {
 679         srcByte = srcRow;
 680         dstByte = dstRow;
 681         for (i = 0; i < width; i++) {
 682             *dstByte++ = *srcByte;
 683             *dstByte++ = *(srcByte + srcRowBytes);
 684             *dstByte++ = *(srcByte + 2*srcRowBytes);
 685             srcByte++;
 686         }
 687         srcRow += 3*srcRowBytes;
 688         dstRow += dstRowBytes;
 689         height -= 3;
 690     }
 691 }
 692 
 693 
 694 /*
 695  * Class:     sun_font_FreetypeFontScaler
 696  * Method:    getGlyphImageNative
 697  * Signature: (Lsun/font/Font2D;JI)J
 698  */
 699 JNIEXPORT jlong JNICALL
 700 Java_sun_font_FreetypeFontScaler_getGlyphImageNative(
 701         JNIEnv *env, jobject scaler, jobject font2D,
 702         jlong pScalerContext, jlong pScaler, jint glyphCode) {
 703 
 704     int error, imageSize;
 705     UInt16 width, height;
 706     GlyphInfo *glyphInfo;
 707     int renderFlags = FT_LOAD_DEFAULT, target;
 708     FT_GlyphSlot ftglyph;
 709 
 710     FTScalerContext* context =
 711         (FTScalerContext*) jlong_to_ptr(pScalerContext);
 712     FTScalerInfo *scalerInfo =
 713              (FTScalerInfo*) jlong_to_ptr(pScaler);
 714 
 715     if (isNullScalerContext(context) || scalerInfo == NULL) {
 716         return ptr_to_jlong(getNullGlyphImage());
 717     }
 718 
 719     error = setupFTContext(env, font2D, scalerInfo, context);
 720     if (error) {
 721         invalidateJavaScaler(env, scaler, scalerInfo);
 722         return ptr_to_jlong(getNullGlyphImage());
 723     }
 724 
 725     if (!context->useSbits) {
 726         renderFlags |= FT_LOAD_NO_BITMAP;
 727     }
 728 
 729     /* NB: in case of non identity transform
 730      we might also prefer to disable transform before hinting,
 731      and apply it explicitly after hinting is performed.
 732      Or we can disable hinting. */
 733 
 734     /* select appropriate hinting mode */
 735     if (context->aaType == TEXT_AA_OFF) {
 736         target = FT_LOAD_TARGET_MONO;
 737     } else if (context->aaType == TEXT_AA_ON) {
 738         target = FT_LOAD_TARGET_NORMAL;
 739     } else if (context->aaType == TEXT_AA_LCD_HRGB ||
 740                context->aaType == TEXT_AA_LCD_HBGR) {
 741         target = FT_LOAD_TARGET_LCD;
 742     } else {
 743         target = FT_LOAD_TARGET_LCD_V;
 744     }
 745     renderFlags |= target;
 746 
 747     error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderFlags);
 748     if (error) {
 749         //do not destroy scaler yet.
 750         //this can be problem of particular context (e.g. with bad transform)
 751         return ptr_to_jlong(getNullGlyphImage());
 752     }
 753 
 754     ftglyph = scalerInfo->face->glyph;
 755 
 756     /* apply styles */
 757     if (context->doBold) { /* if bold style */
 758         FT_GlyphSlot_Embolden(ftglyph);
 759     }
 760     if (context->doItalize) { /* if oblique */
 761         FT_GlyphSlot_Oblique(ftglyph);
 762     }
 763 
 764     /* generate bitmap if it is not done yet
 765      e.g. if algorithmic styling is performed and style was added to outline */
 766     if (ftglyph->format == FT_GLYPH_FORMAT_OUTLINE) {
 767         error = FT_Render_Glyph(ftglyph, FT_LOAD_TARGET_MODE(target));
 768         if (error != 0) {
 769             return ptr_to_jlong(getNullGlyphImage());
 770         }
 771     }
 772 
 773     width  = (UInt16) ftglyph->bitmap.width;
 774     height = (UInt16) ftglyph->bitmap.rows;
 775 
 776     imageSize = width*height;
 777     glyphInfo = (GlyphInfo*) malloc(sizeof(GlyphInfo) + imageSize);
 778     if (glyphInfo == NULL) {
 779         glyphInfo = getNullGlyphImage();
 780         return ptr_to_jlong(glyphInfo);
 781     }
 782     glyphInfo->cellInfo  = NULL;
 783     glyphInfo->managed   = UNMANAGED_GLYPH;
 784     glyphInfo->rowBytes  = width;
 785     glyphInfo->width     = width;
 786     glyphInfo->height    = height;
 787     glyphInfo->topLeftX  = (float)  ftglyph->bitmap_left;
 788     glyphInfo->topLeftY  = (float) -ftglyph->bitmap_top;
 789 
 790     if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD) {
 791         glyphInfo->width = width/3;
 792     } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD_V) {
 793         glyphInfo->height = glyphInfo->height/3;
 794     }
 795 
 796     if (context->fmType == TEXT_FM_ON) {
 797         double advh = FTFixedToFloat(ftglyph->linearHoriAdvance);
 798         glyphInfo->advanceX =
 799             (float) (advh * FTFixedToFloat(context->transform.xx));
 800         glyphInfo->advanceY =
 801             (float) (advh * FTFixedToFloat(context->transform.xy));
 802     } else {
 803         if (!ftglyph->advance.y) {
 804             glyphInfo->advanceX =
 805                 (float) FT26Dot6ToInt(ftglyph->advance.x);
 806             glyphInfo->advanceY = 0;
 807         } else if (!ftglyph->advance.x) {
 808             glyphInfo->advanceX = 0;
 809             glyphInfo->advanceY =
 810                 (float) FT26Dot6ToInt(-ftglyph->advance.y);
 811         } else {
 812             glyphInfo->advanceX = FT26Dot6ToFloat(ftglyph->advance.x);
 813             glyphInfo->advanceY = FT26Dot6ToFloat(-ftglyph->advance.y);
 814         }
 815     }
 816 
 817     if (imageSize == 0) {
 818         glyphInfo->image = NULL;
 819     } else {
 820         glyphInfo->image = (unsigned char*) glyphInfo + sizeof(GlyphInfo);
 821         //convert result to output format
 822         //output format is either 3 bytes per pixel (for subpixel modes)
 823         // or 1 byte per pixel for AA and B&W
 824         if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_MONO) {
 825             /* convert from 8 pixels per byte to 1 byte per pixel */
 826             CopyBW2Grey8(ftglyph->bitmap.buffer,
 827                          ftglyph->bitmap.pitch,
 828                          (void *) glyphInfo->image,
 829                          width,
 830                          width,
 831                          height);
 832         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_GRAY) {
 833             /* byte per pixel to byte per pixel => just copy */
 834             memcpy(glyphInfo->image, ftglyph->bitmap.buffer, imageSize);
 835         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_GRAY4) {
 836             /* 4 bits per pixel to byte per pixel */
 837             CopyGrey4ToGrey8(ftglyph->bitmap.buffer,
 838                              ftglyph->bitmap.pitch,
 839                              (void *) glyphInfo->image,
 840                              width,
 841                              width,
 842                              height);
 843         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD) {
 844             /* 3 bytes per pixel to 3 bytes per pixel */
 845             CopyFTSubpixelToSubpixel(ftglyph->bitmap.buffer,
 846                                      ftglyph->bitmap.pitch,
 847                                      (void *) glyphInfo->image,
 848                                      width,
 849                                      width,
 850                                      height);
 851         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD_V) {
 852             /* 3 bytes per pixel to 3 bytes per pixel */
 853             CopyFTSubpixelVToSubpixel(ftglyph->bitmap.buffer,
 854                                       ftglyph->bitmap.pitch,
 855                                       (void *) glyphInfo->image,
 856                                       width*3,
 857                                       width,
 858                                       height);
 859             glyphInfo->rowBytes *=3;
 860         } else {
 861             free(glyphInfo);
 862             glyphInfo = getNullGlyphImage();
 863         }
 864     }
 865 
 866     return ptr_to_jlong(glyphInfo);
 867 }
 868 
 869 
 870 /*
 871  * Class:     sun_font_FreetypeFontScaler
 872  * Method:    getLayoutTableCacheNative
 873  * Signature: (J)J
 874  */
 875 JNIEXPORT jlong JNICALL
 876 Java_sun_font_FreetypeFontScaler_getLayoutTableCacheNative(
 877         JNIEnv *env, jobject scaler, jlong pScaler) {
 878     FTScalerInfo *scalerInfo = (FTScalerInfo*) jlong_to_ptr(pScaler);
 879 
 880     if (scalerInfo == NULL) {
 881         invalidateJavaScaler(env, scaler, scalerInfo);
 882         return 0L;
 883     }
 884 
 885     // init layout table cache in font
 886     // we're assuming the font is a file font and moreover it is Truetype font
 887     // otherwise we shouldn't be able to get here...
 888     if (scalerInfo->layoutTables == NULL) {
 889         scalerInfo->layoutTables = newLayoutTableCache();
 890     }
 891 
 892     return ptr_to_jlong(scalerInfo->layoutTables);
 893 }
 894 
 895 /*
 896  * Class:     sun_font_FreetypeFontScaler
 897  * Method:    disposeNativeScaler
 898  * Signature: (J)V
 899  */
 900 JNIEXPORT void JNICALL
 901 Java_sun_font_FreetypeFontScaler_disposeNativeScaler(
 902         JNIEnv *env, jobject scaler, jobject font2D, jlong pScaler) {
 903     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
 904 
 905     /* Freetype functions *may* cause callback to java
 906        that can use cached values. Make sure our cache is up to date.
 907        NB: scaler context is not important at this point, can use NULL. */
 908     int errCode = setupFTContext(env, font2D, scalerInfo, NULL);
 909     if (errCode) {
 910         return;
 911     }
 912 
 913     freeNativeResources(env, scalerInfo);
 914 }
 915 
 916 /*
 917  * Class:     sun_font_FreetypeFontScaler
 918  * Method:    getNumGlyphsNative
 919  * Signature: ()I
 920  */
 921 JNIEXPORT jint JNICALL
 922 Java_sun_font_FreetypeFontScaler_getNumGlyphsNative(
 923         JNIEnv *env, jobject scaler, jlong pScaler) {
 924     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
 925 
 926     if (scalerInfo == NULL || scalerInfo->face == NULL) { /* bad/null scaler */
 927         /* null scaler can render 1 glyph - "missing glyph" with code 0
 928            (all glyph codes requested by user are mapped to code 0 at
 929            validation step) */
 930         invalidateJavaScaler(env, scaler, scalerInfo);
 931         return (jint) 1;
 932     }
 933 
 934     return (jint) scalerInfo->face->num_glyphs;
 935 }
 936 
 937 /*
 938  * Class:     sun_font_FreetypeFontScaler
 939  * Method:    getMissingGlyphCodeNative
 940  * Signature: ()I
 941  */
 942 JNIEXPORT jint JNICALL
 943 Java_sun_font_FreetypeFontScaler_getMissingGlyphCodeNative(
 944         JNIEnv *env, jobject scaler, jlong pScaler) {
 945 
 946     /* Is it always 0 for freetype? */
 947     return 0;
 948 }
 949 
 950 /*
 951  * Class:     sun_font_FreetypeFontScaler
 952  * Method:    getGlyphCodeNative
 953  * Signature: (C)I
 954  */
 955 JNIEXPORT jint JNICALL
 956 Java_sun_font_FreetypeFontScaler_getGlyphCodeNative(
 957         JNIEnv *env, jobject scaler,
 958         jobject font2D, jlong pScaler, jchar charCode) {
 959 
 960     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
 961     int errCode;
 962 
 963     if (scaler == NULL || scalerInfo->face == NULL) { /* bad/null scaler */
 964         invalidateJavaScaler(env, scaler, scalerInfo);
 965         return 0;
 966     }
 967 
 968     /* Freetype functions *may* cause callback to java
 969        that can use cached values. Make sure our cache is up to date.
 970        Scaler context is not important here, can use NULL. */
 971     errCode = setupFTContext(env, font2D, scalerInfo, NULL);
 972     if (errCode) {
 973         return 0;
 974     }
 975 
 976     return FT_Get_Char_Index(scalerInfo->face, charCode);
 977 }
 978 
 979 
 980 #define FloatToF26Dot6(x) ((unsigned int) ((x)*64))
 981 
 982 static FT_Outline* getFTOutline(JNIEnv* env, jobject font2D,
 983         FTScalerContext *context, FTScalerInfo* scalerInfo,
 984         jint glyphCode, jfloat xpos, jfloat ypos) {
 985     int renderFlags;
 986     FT_Error error;
 987     FT_GlyphSlot ftglyph;
 988 
 989     if (glyphCode >= INVISIBLE_GLYPHS ||
 990             isNullScalerContext(context) || scalerInfo == NULL) {
 991         return NULL;
 992     }
 993 
 994     error = setupFTContext(env, font2D, scalerInfo, context);
 995     if (error) {
 996         return NULL;
 997     }
 998 
 999     renderFlags = FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP;
1000 
1001     error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderFlags);
1002     if (error) {
1003         return NULL;
1004     }
1005 
1006     ftglyph = scalerInfo->face->glyph;
1007 
1008     /* apply styles */
1009     if (context->doBold) { /* if bold style */
1010         FT_GlyphSlot_Embolden(ftglyph);
1011     }
1012     if (context->doItalize) { /* if oblique */
1013         FT_GlyphSlot_Oblique(ftglyph);
1014     }
1015 
1016     FT_Outline_Translate(&ftglyph->outline,
1017                          FloatToF26Dot6(xpos),
1018                          -FloatToF26Dot6(ypos));
1019 
1020     return &ftglyph->outline;
1021 }
1022 
1023 #define F26Dot6ToFloat(n) (((float)(n))/((float) 64))
1024 
1025 /* Types of GeneralPath segments.
1026    TODO: pull constants from other place? */
1027 
1028 #define SEG_UNKNOWN -1
1029 #define SEG_MOVETO   0
1030 #define SEG_LINETO   1
1031 #define SEG_QUADTO   2
1032 #define SEG_CUBICTO  3
1033 #define SEG_CLOSE    4
1034 
1035 #define WIND_NON_ZERO 0
1036 #define WIND_EVEN_ODD 1
1037 
1038 /* Placeholder to accumulate GeneralPath data */
1039 typedef struct {
1040     jint numTypes;
1041     jint numCoords;
1042     jint lenTypes;
1043     jint lenCoords;
1044     jint wr;
1045     jbyte* pointTypes;
1046     jfloat* pointCoords;
1047 } GPData;
1048 
1049 /* returns 0 on failure */
1050 static int allocateSpaceForGP(GPData* gpdata, int npoints, int ncontours) {
1051     int maxTypes, maxCoords;
1052 
1053     /* we may have up to N intermediate points per contour
1054        (and for each point can actually cause new curve to be generated)
1055        In addition we can also have 2 extra point per outline.
1056      */
1057     maxTypes  = 2*npoints  + 2*ncontours;
1058     maxCoords = 4*(npoints + 2*ncontours); //we may need to insert
1059                                            //up to n-1 intermediate points
1060 
1061     /* first usage - allocate space and intialize all fields */
1062     if (gpdata->pointTypes == NULL || gpdata->pointCoords == NULL) {
1063         gpdata->lenTypes  = maxTypes;
1064         gpdata->lenCoords = maxCoords;
1065         gpdata->pointTypes  = (jbyte*)
1066              malloc(gpdata->lenTypes*sizeof(jbyte));
1067         gpdata->pointCoords = (jfloat*)
1068              malloc(gpdata->lenCoords*sizeof(jfloat));
1069         gpdata->numTypes = 0;
1070         gpdata->numCoords = 0;
1071         gpdata->wr = WIND_NON_ZERO; /* By default, outlines are filled
1072                                        using the non-zero winding rule. */
1073     } else {
1074         /* do we have enough space? */
1075         if (gpdata->lenTypes - gpdata->numTypes < maxTypes) {
1076             gpdata->lenTypes  += maxTypes;
1077             gpdata->pointTypes  = (jbyte*)
1078               realloc(gpdata->pointTypes, gpdata->lenTypes*sizeof(jbyte));
1079         }
1080 
1081         if (gpdata->lenCoords - gpdata->numCoords < maxCoords) {
1082             gpdata->lenCoords += maxCoords;
1083             gpdata->pointCoords = (jfloat*)
1084               realloc(gpdata->pointCoords, gpdata->lenCoords*sizeof(jfloat));
1085         }
1086     }
1087 
1088     /* failure if any of mallocs failed */
1089     if (gpdata->pointTypes == NULL ||  gpdata->pointCoords == NULL)
1090         return 0;
1091     else
1092         return 1;
1093 }
1094 
1095 static void addSeg(GPData *gp, jbyte type) {
1096     gp->pointTypes[gp->numTypes++] = type;
1097 }
1098 
1099 static void addCoords(GPData *gp, FT_Vector *p) {
1100     gp->pointCoords[gp->numCoords++] =  F26Dot6ToFloat(p->x);
1101     gp->pointCoords[gp->numCoords++] = -F26Dot6ToFloat(p->y);
1102 }
1103 
1104 static int moveTo(FT_Vector *to, GPData *gp) {
1105     if (gp->numCoords)
1106         addSeg(gp, SEG_CLOSE);
1107     addCoords(gp, to);
1108     addSeg(gp, SEG_MOVETO);
1109     return FT_Err_Ok;
1110 }
1111 
1112 static int lineTo(FT_Vector *to, GPData *gp) {
1113     addCoords(gp, to);
1114     addSeg(gp, SEG_LINETO);
1115     return FT_Err_Ok;
1116 }
1117 
1118 static int conicTo(FT_Vector *control, FT_Vector *to, GPData *gp) {
1119     addCoords(gp, control);
1120     addCoords(gp, to);
1121     addSeg(gp, SEG_QUADTO);
1122     return FT_Err_Ok;
1123 }
1124 
1125 static int cubicTo(FT_Vector *control1,
1126                    FT_Vector *control2,
1127                    FT_Vector *to,
1128                    GPData    *gp) {
1129     addCoords(gp, control1);
1130     addCoords(gp, control2);
1131     addCoords(gp, to);
1132     addSeg(gp, SEG_CUBICTO);
1133     return FT_Err_Ok;
1134 }
1135 
1136 static void addToGP(GPData* gpdata, FT_Outline*outline) {
1137     static const FT_Outline_Funcs outline_funcs = {
1138         (FT_Outline_MoveToFunc) moveTo,
1139         (FT_Outline_LineToFunc) lineTo,
1140         (FT_Outline_ConicToFunc) conicTo,
1141         (FT_Outline_CubicToFunc) cubicTo,
1142         0, /* shift */
1143         0, /* delta */
1144     };
1145 
1146     FT_Outline_Decompose(outline, &outline_funcs, gpdata);
1147     if (gpdata->numCoords)
1148         addSeg(gpdata, SEG_CLOSE);
1149 
1150     /* If set to 1, the outline will be filled using the even-odd fill rule */
1151     if (outline->flags & FT_OUTLINE_EVEN_ODD_FILL) {
1152         gpdata->wr = WIND_EVEN_ODD;
1153     }
1154 }
1155 
1156 static void freeGP(GPData* gpdata) {
1157     if (gpdata->pointCoords != NULL) {
1158         free(gpdata->pointCoords);
1159         gpdata->pointCoords = NULL;
1160         gpdata->numCoords = 0;
1161         gpdata->lenCoords = 0;
1162     }
1163     if (gpdata->pointTypes != NULL) {
1164         free(gpdata->pointTypes);
1165         gpdata->pointTypes = NULL;
1166         gpdata->numTypes = 0;
1167         gpdata->lenTypes = 0;
1168     }
1169 }
1170 
1171 static jobject getGlyphGeneralPath(JNIEnv* env, jobject font2D,
1172         FTScalerContext *context, FTScalerInfo *scalerInfo,
1173         jint glyphCode, jfloat xpos, jfloat ypos) {
1174 
1175     FT_Outline* outline;
1176     jobject gp = NULL;
1177     jbyteArray types;
1178     jfloatArray coords;
1179     GPData gpdata;
1180 
1181     outline = getFTOutline(env, font2D, context, scalerInfo,
1182                            glyphCode, xpos, ypos);
1183 
1184     if (outline == NULL || outline->n_points == 0) {
1185         return gp;
1186     }
1187 
1188     gpdata.pointTypes  = NULL;
1189     gpdata.pointCoords = NULL;
1190     if (!allocateSpaceForGP(&gpdata, outline->n_points, outline->n_contours)) {
1191         return gp;
1192     }
1193 
1194     addToGP(&gpdata, outline);
1195 
1196     types  = (*env)->NewByteArray(env, gpdata.numTypes);
1197     coords = (*env)->NewFloatArray(env, gpdata.numCoords);
1198 
1199     if (types && coords) {
1200         (*env)->SetByteArrayRegion(env, types, 0,
1201                                    gpdata.numTypes,
1202                                    gpdata.pointTypes);
1203         (*env)->SetFloatArrayRegion(env, coords, 0,
1204                                     gpdata.numCoords,
1205                                     gpdata.pointCoords);
1206         gp = (*env)->NewObject(env,
1207                                sunFontIDs.gpClass,
1208                                sunFontIDs.gpCtr,
1209                                gpdata.wr,
1210                                types,
1211                                gpdata.numTypes,
1212                                coords,
1213                                gpdata.numCoords);
1214     }
1215 
1216     freeGP(&gpdata);
1217 
1218     return gp;
1219 }
1220 
1221 /*
1222  * Class:     sun_font_FreetypeFontScaler
1223  * Method:    getGlyphOutlineNative
1224  * Signature: (Lsun/font/Font2D;JIFF)Ljava/awt/geom/GeneralPath;
1225  */
1226 JNIEXPORT jobject JNICALL
1227 Java_sun_font_FreetypeFontScaler_getGlyphOutlineNative(
1228       JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
1229       jlong pScaler, jint glyphCode, jfloat xpos, jfloat ypos) {
1230 
1231     FTScalerContext *context =
1232          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1233     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
1234 
1235     jobject gp = getGlyphGeneralPath(env,
1236                                font2D,
1237                                context,
1238                                scalerInfo,
1239                                glyphCode,
1240                                xpos,
1241                                ypos);
1242     if (gp == NULL) { /* can be legal */
1243         gp = (*env)->NewObject(env,
1244                                sunFontIDs.gpClass,
1245                                sunFontIDs.gpCtrEmpty);
1246     }
1247     return gp;
1248 }
1249 
1250 /*
1251  * Class:     sun_font_FreetypeFontScaler
1252  * Method:    getGlyphOutlineBoundsNative
1253  * Signature: (Lsun/font/Font2D;JI)Ljava/awt/geom/Rectangle2D/Float;
1254  */
1255 JNIEXPORT jobject JNICALL
1256 Java_sun_font_FreetypeFontScaler_getGlyphOutlineBoundsNative(
1257         JNIEnv *env, jobject scaler, jobject font2D,
1258         jlong pScalerContext, jlong pScaler, jint glyphCode) {
1259 
1260     FT_Outline *outline;
1261     FT_BBox bbox;
1262     int error;
1263     jobject bounds;
1264 
1265     FTScalerContext *context =
1266          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1267     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
1268 
1269     outline = getFTOutline(env, font2D, context, scalerInfo, glyphCode, 0, 0);
1270     if (outline == NULL || outline->n_points == 0) {
1271         /* it is legal case, e.g. invisible glyph */
1272         bounds = (*env)->NewObject(env,
1273                                  sunFontIDs.rect2DFloatClass,
1274                                  sunFontIDs.rect2DFloatCtr);
1275         return bounds;
1276     }
1277 
1278     error = FT_Outline_Get_BBox(outline, &bbox);
1279 
1280     //convert bbox
1281     if (error || bbox.xMin >= bbox.xMax || bbox.yMin >= bbox.yMax) {
1282         bounds = (*env)->NewObject(env,
1283                                    sunFontIDs.rect2DFloatClass,
1284                                    sunFontIDs.rect2DFloatCtr);
1285     } else {
1286         bounds = (*env)->NewObject(env,
1287                                    sunFontIDs.rect2DFloatClass,
1288                                    sunFontIDs.rect2DFloatCtr4,
1289                                    F26Dot6ToFloat(bbox.xMin),
1290                                    F26Dot6ToFloat(-bbox.yMax),
1291                                    F26Dot6ToFloat(bbox.xMax-bbox.xMin),
1292                                    F26Dot6ToFloat(bbox.yMax-bbox.yMin));
1293     }
1294 
1295     return bounds;
1296 }
1297 
1298 /*
1299  * Class:     sun_font_FreetypeFontScaler
1300  * Method:    getGlyphVectorOutlineNative
1301  * Signature: (Lsun/font/Font2D;J[IIFF)Ljava/awt/geom/GeneralPath;
1302  */
1303 JNIEXPORT jobject
1304 JNICALL
1305 Java_sun_font_FreetypeFontScaler_getGlyphVectorOutlineNative(
1306         JNIEnv *env, jobject scaler, jobject font2D,
1307         jlong pScalerContext, jlong pScaler,
1308         jintArray glyphArray, jint numGlyphs, jfloat xpos, jfloat ypos) {
1309 
1310     FT_Outline* outline;
1311     jobject gp = NULL;
1312     jbyteArray types;
1313     jfloatArray coords;
1314     GPData gpdata;
1315     int i;
1316     jint *glyphs;
1317 
1318     FTScalerContext *context =
1319          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1320     FTScalerInfo *scalerInfo =
1321              (FTScalerInfo*) jlong_to_ptr(pScaler);
1322 
1323     glyphs = NULL;
1324     if (numGlyphs > 0 && 0xffffffffu / sizeof(jint) >= numGlyphs) {
1325         glyphs = (jint*) malloc(numGlyphs*sizeof(jint));
1326     }
1327     if (glyphs == NULL) {
1328         // We reach here if:
1329         // 1. numGlyphs <= 0,
1330         // 2. overflow check failed, or
1331         // 3. malloc failed.
1332         gp = (*env)->NewObject(env, sunFontIDs.gpClass, sunFontIDs.gpCtrEmpty);
1333         return gp;
1334     }
1335 
1336     (*env)->GetIntArrayRegion(env, glyphArray, 0, numGlyphs, glyphs);
1337 
1338     gpdata.numCoords = 0;
1339     for (i=0; i<numGlyphs;i++) {
1340         if (glyphs[i] >= INVISIBLE_GLYPHS) {
1341             continue;
1342         }
1343         outline = getFTOutline(env,
1344                                font2D,
1345                                context,
1346                                scalerInfo,
1347                                glyphs[i],
1348                                xpos, ypos);
1349 
1350         if (outline == NULL || outline->n_points == 0) {
1351             continue;
1352         }
1353 
1354         gpdata.pointTypes  = NULL;
1355         gpdata.pointCoords = NULL;
1356         if (!allocateSpaceForGP(&gpdata, outline->n_points,
1357                                 outline->n_contours)) {
1358             break;
1359         }
1360 
1361         addToGP(&gpdata, outline);
1362     }
1363     free(glyphs);
1364 
1365     if (gpdata.numCoords != 0) {
1366       types = (*env)->NewByteArray(env, gpdata.numTypes);
1367       coords = (*env)->NewFloatArray(env, gpdata.numCoords);
1368 
1369       if (types && coords) {
1370         (*env)->SetByteArrayRegion(env, types, 0,
1371                                    gpdata.numTypes, gpdata.pointTypes);
1372         (*env)->SetFloatArrayRegion(env, coords, 0,
1373                                     gpdata.numCoords, gpdata.pointCoords);
1374 
1375         gp=(*env)->NewObject(env,
1376                              sunFontIDs.gpClass,
1377                              sunFontIDs.gpCtr,
1378                              gpdata.wr,
1379                              types,
1380                              gpdata.numTypes,
1381                              coords,
1382                              gpdata.numCoords);
1383         return gp;
1384       }
1385     }
1386     return (*env)->NewObject(env, sunFontIDs.gpClass, sunFontIDs.gpCtrEmpty);
1387 }
1388 
1389 JNIEXPORT jlong JNICALL
1390 Java_sun_font_FreetypeFontScaler_getUnitsPerEMNative(
1391         JNIEnv *env, jobject scaler, jlong pScaler) {
1392 
1393     FTScalerInfo *s = (FTScalerInfo* ) jlong_to_ptr(pScaler);
1394 
1395     /* Freetype doc says:
1396      The number of font units per EM square for this face.
1397      This is typically 2048 for TrueType fonts, and 1000 for Type 1 fonts.
1398      Only relevant for scalable formats.
1399      However, layout engine might be not tested with anything but 2048.
1400 
1401      NB: test it! */
1402     if (s != NULL) {
1403         return s->face->units_per_EM;
1404     }
1405     return 2048;
1406 }
1407 
1408 /* This native method is called by the OpenType layout engine. */
1409 JNIEXPORT jobject JNICALL
1410 Java_sun_font_FreetypeFontScaler_getGlyphPointNative(
1411         JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
1412         jlong pScaler, jint glyphCode, jint pointNumber) {
1413 
1414     FT_Outline* outline;
1415     jobject point = NULL;
1416     jfloat x=0, y=0;
1417     FTScalerContext *context =
1418          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1419     FTScalerInfo *scalerInfo = (FTScalerInfo*) jlong_to_ptr(pScaler);
1420 
1421     outline = getFTOutline(env, font2D, context, scalerInfo, glyphCode, 0, 0);
1422 
1423     if (outline != NULL && outline->n_points > pointNumber) {
1424         x =  F26Dot6ToFloat(outline->points[pointNumber].x);
1425         y = -F26Dot6ToFloat(outline->points[pointNumber].y);
1426     }
1427 
1428     return (*env)->NewObject(env, sunFontIDs.pt2DFloatClass,
1429                              sunFontIDs.pt2DFloatCtr, x, y);
1430 }