1 /*
   2  * Copyright (c) 2007, 2013, 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, 0x06000, 0x0, 0x10000) matrix to get oblique
 409    outline.  Therefore x coordinate will change by 0x06000*y.
 410    Note that y coordinate does not change. */
 411 #define OBLIQUE_MODIFIER(y)  (context->doItalize ? ((y)*6/16) : 0)
 412 
 413 /*
 414  * Class:     sun_font_FreetypeFontScaler
 415  * Method:    getFontMetricsNative
 416  * Signature: (Lsun/font/Font2D;J)Lsun/font/StrikeMetrics;
 417  */
 418 JNIEXPORT jobject JNICALL
 419 Java_sun_font_FreetypeFontScaler_getFontMetricsNative(
 420         JNIEnv *env, jobject scaler, jobject font2D,
 421         jlong pScalerContext, jlong pScaler) {
 422 
 423     jobject metrics;
 424     jfloat ax, ay, dx, dy, bx, by, lx, ly, mx, my;
 425     jfloat f0 = 0.0;
 426     FTScalerContext *context =
 427         (FTScalerContext*) jlong_to_ptr(pScalerContext);
 428     FTScalerInfo *scalerInfo =
 429              (FTScalerInfo*) jlong_to_ptr(pScaler);
 430 
 431     int errCode;
 432 
 433     if (isNullScalerContext(context) || scalerInfo == NULL) {
 434         return (*env)->NewObject(env,
 435                                  sunFontIDs.strikeMetricsClass,
 436                                  sunFontIDs.strikeMetricsCtr,
 437                                  f0, f0, f0, f0, f0, f0, f0, f0, f0, f0);
 438     }
 439 
 440     errCode = setupFTContext(env, font2D, scalerInfo, context);
 441 
 442     if (errCode) {
 443         metrics = (*env)->NewObject(env,
 444                                  sunFontIDs.strikeMetricsClass,
 445                                  sunFontIDs.strikeMetricsCtr,
 446                                  f0, f0, f0, f0, f0, f0, f0, f0, f0, f0);
 447         invalidateJavaScaler(env, scaler, scalerInfo);
 448         return metrics;
 449     }
 450 
 451     /* This is ugly and has to be reworked.
 452        Freetype provide means to add style to glyph but
 453        it seems there is no way to adjust metrics accordingly.
 454 
 455        So, we have to do adust them explicitly and stay consistent with what
 456        freetype does to outlines. */
 457 
 458 
 459     /**** Note: only some metrics are affected by styling ***/
 460 
 461     /* See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=657854 */
 462 #define FT_MulFixFloatShift6(a, b) (((float) (a)) * ((float) (b)) / 65536.0 / 64.0)
 463 
 464 #define contextAwareMetricsX(vx, vy) (FTFixedToFloat(context->transform.xx) * (vx) - FTFixedToFloat(context->transform.xy) * (vy))
 465 #define contextAwareMetricsY(vx, vy) (-FTFixedToFloat(context->transform.yx) * (vx) + FTFixedToFloat(context->transform.yy) * (vy))
 466     /*
 467      * See FreeType source code: src/base/ftobjs.c ft_recompute_scaled_metrics()
 468      * http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=1659
 469      */
 470     /* ascent */
 471     ax = 0;
 472     ay = -(jfloat) (FT_MulFixFloatShift6(
 473                        ((jlong) scalerInfo->face->ascender),
 474                        (jlong) scalerInfo->face->size->metrics.y_scale));
 475     /* descent */
 476     dx = 0;
 477     dy = -(jfloat) (FT_MulFixFloatShift6(
 478                        ((jlong) scalerInfo->face->descender),
 479                        (jlong) scalerInfo->face->size->metrics.y_scale));
 480     /* baseline */
 481     bx = by = 0;
 482 
 483     /* leading */
 484     lx = 0;
 485     ly = (jfloat) (FT_MulFixFloatShift6(
 486                       (jlong) scalerInfo->face->height,
 487                       (jlong) scalerInfo->face->size->metrics.y_scale))
 488                   + ay - dy;
 489     /* max advance */
 490     mx = (jfloat) FT26Dot6ToFloat(
 491                      scalerInfo->face->size->metrics.max_advance +
 492                      OBLIQUE_MODIFIER(scalerInfo->face->size->metrics.height));
 493     my = 0;
 494 
 495     metrics = (*env)->NewObject(env,
 496         sunFontIDs.strikeMetricsClass,
 497         sunFontIDs.strikeMetricsCtr,
 498         contextAwareMetricsX(ax, ay), contextAwareMetricsY(ax, ay),
 499         contextAwareMetricsX(dx, dy), contextAwareMetricsY(dx, dy),
 500         bx, by,
 501         contextAwareMetricsX(lx, ly), contextAwareMetricsY(lx, ly),
 502         contextAwareMetricsX(mx, my), contextAwareMetricsY(mx, my));
 503 
 504     return metrics;
 505 }
 506 
 507 /*
 508  * Class:     sun_font_FreetypeFontScaler
 509  * Method:    getGlyphAdvanceNative
 510  * Signature: (Lsun/font/Font2D;JI)F
 511  */
 512 JNIEXPORT jfloat JNICALL
 513 Java_sun_font_FreetypeFontScaler_getGlyphAdvanceNative(
 514         JNIEnv *env, jobject scaler, jobject font2D,
 515         jlong pScalerContext, jlong pScaler, jint glyphCode) {
 516 
 517    /* This method is rarely used because requests for metrics are usually
 518       coupled with request for bitmap and to large extend work can be reused
 519       (to find out metrics we need to hint glyph).
 520       So, we typically go through getGlyphImage code path.
 521 
 522       For initial freetype implementation we delegate
 523       all work to getGlyphImage but drop result image.
 524       This is waste of work related to scan conversion and conversion from
 525       freetype format to our format but for now this seems to be ok.
 526 
 527       NB: investigate performance benefits of refactoring code
 528       to avoid unnecesary work with bitmaps. */
 529 
 530     GlyphInfo *info;
 531     jfloat advance;
 532     jlong image;
 533 
 534     image = Java_sun_font_FreetypeFontScaler_getGlyphImageNative(
 535                  env, scaler, font2D, pScalerContext, pScaler, glyphCode);
 536     info = (GlyphInfo*) jlong_to_ptr(image);
 537 
 538     advance = info->advanceX;
 539 
 540     free(info);
 541 
 542     return advance;
 543 }
 544 
 545 /*
 546  * Class:     sun_font_FreetypeFontScaler
 547  * Method:    getGlyphMetricsNative
 548  * Signature: (Lsun/font/Font2D;JILjava/awt/geom/Point2D/Float;)V
 549  */
 550 JNIEXPORT void JNICALL
 551 Java_sun_font_FreetypeFontScaler_getGlyphMetricsNative(
 552         JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
 553         jlong pScaler, jint glyphCode, jobject metrics) {
 554 
 555      /* As initial implementation we delegate all work to getGlyphImage
 556         but drop result image. This is clearly waste of resorces.
 557 
 558         TODO: investigate performance benefits of refactoring code
 559               by avoiding bitmap generation and conversion from FT
 560               bitmap format. */
 561      GlyphInfo *info;
 562 
 563      jlong image = Java_sun_font_FreetypeFontScaler_getGlyphImageNative(
 564                                  env, scaler, font2D,
 565                                  pScalerContext, pScaler, glyphCode);
 566      info = (GlyphInfo*) jlong_to_ptr(image);
 567 
 568      (*env)->SetFloatField(env, metrics, sunFontIDs.xFID, info->advanceX);
 569      (*env)->SetFloatField(env, metrics, sunFontIDs.yFID, info->advanceY);
 570 
 571      free(info);
 572 }
 573 
 574 
 575 static GlyphInfo* getNullGlyphImage() {
 576     GlyphInfo *glyphInfo =  (GlyphInfo*) calloc(1, sizeof(GlyphInfo));
 577     return glyphInfo;
 578 }
 579 
 580 static void CopyBW2Grey8(const void* srcImage, int srcRowBytes,
 581                          void* dstImage, int dstRowBytes,
 582                          int width, int height) {
 583     const UInt8* srcRow = (UInt8*)srcImage;
 584     UInt8* dstRow = (UInt8*)dstImage;
 585     int wholeByteCount = width >> 3;
 586     int remainingBitsCount = width & 7;
 587     int i, j;
 588 
 589     while (height--) {
 590         const UInt8* src8 = srcRow;
 591         UInt8* dstByte = dstRow;
 592         unsigned srcValue;
 593 
 594         srcRow += srcRowBytes;
 595         dstRow += dstRowBytes;
 596 
 597         for (i = 0; i < wholeByteCount; i++) {
 598             srcValue = *src8++;
 599             for (j = 0; j < 8; j++) {
 600                 *dstByte++ = (srcValue & 0x80) ? 0xFF : 0;
 601                 srcValue <<= 1;
 602             }
 603         }
 604         if (remainingBitsCount) {
 605             srcValue = *src8;
 606             for (j = 0; j < remainingBitsCount; j++) {
 607                 *dstByte++ = (srcValue & 0x80) ? 0xFF : 0;
 608                 srcValue <<= 1;
 609             }
 610         }
 611     }
 612 }
 613 
 614 #define Grey4ToAlpha255(value) (((value) << 4) + ((value) >> 3))
 615 
 616 static void CopyGrey4ToGrey8(const void* srcImage, int srcRowBytes,
 617                 void* dstImage, int dstRowBytes, int width, int height) {
 618      const UInt8* srcRow = (UInt8*) srcImage;
 619      UInt8* dstRow = (UInt8*) dstImage;
 620      int i;
 621 
 622      while (height--) {
 623          const UInt8* src8 = srcRow;
 624          UInt8* dstByte = dstRow;
 625          unsigned srcValue;
 626 
 627          srcRow += srcRowBytes;
 628          dstRow += dstRowBytes;
 629 
 630          for (i = 0; i < width; i++) {
 631              srcValue = *src8++;
 632              *dstByte++ = Grey4ToAlpha255(srcValue & 0x0f);
 633              *dstByte++ = Grey4ToAlpha255(srcValue >> 4);
 634          }
 635      }
 636 }
 637 
 638 /* We need it because FT rows are often padded to 4 byte boundaries
 639     and our internal format is not padded */
 640 static void CopyFTSubpixelToSubpixel(const void* srcImage, int srcRowBytes,
 641                                      void* dstImage, int dstRowBytes,
 642                                      int width, int height) {
 643     unsigned char *srcRow = (unsigned char *) srcImage;
 644     unsigned char *dstRow = (unsigned char *) dstImage;
 645 
 646     while (height--) {
 647         memcpy(dstRow, srcRow, width);
 648         srcRow += srcRowBytes;
 649         dstRow += dstRowBytes;
 650     }
 651 }
 652 
 653 /* We need it because FT rows are often padded to 4 byte boundaries
 654    and our internal format is not padded */
 655 static void CopyFTSubpixelVToSubpixel(const void* srcImage, int srcRowBytes,
 656                                       void* dstImage, int dstRowBytes,
 657                                       int width, int height) {
 658     unsigned char *srcRow = (unsigned char *) srcImage, *srcByte;
 659     unsigned char *dstRow = (unsigned char *) dstImage, *dstByte;
 660     int i;
 661 
 662     while (height > 0) {
 663         srcByte = srcRow;
 664         dstByte = dstRow;
 665         for (i = 0; i < width; i++) {
 666             *dstByte++ = *srcByte;
 667             *dstByte++ = *(srcByte + srcRowBytes);
 668             *dstByte++ = *(srcByte + 2*srcRowBytes);
 669             srcByte++;
 670         }
 671         srcRow += 3*srcRowBytes;
 672         dstRow += dstRowBytes;
 673         height -= 3;
 674     }
 675 }
 676 
 677 
 678 /*
 679  * Class:     sun_font_FreetypeFontScaler
 680  * Method:    getGlyphImageNative
 681  * Signature: (Lsun/font/Font2D;JI)J
 682  */
 683 JNIEXPORT jlong JNICALL
 684 Java_sun_font_FreetypeFontScaler_getGlyphImageNative(
 685         JNIEnv *env, jobject scaler, jobject font2D,
 686         jlong pScalerContext, jlong pScaler, jint glyphCode) {
 687 
 688     int error, imageSize;
 689     UInt16 width, height;
 690     GlyphInfo *glyphInfo;
 691     int renderFlags = FT_LOAD_RENDER, target;
 692     FT_GlyphSlot ftglyph;
 693 
 694     FTScalerContext* context =
 695         (FTScalerContext*) jlong_to_ptr(pScalerContext);
 696     FTScalerInfo *scalerInfo =
 697              (FTScalerInfo*) jlong_to_ptr(pScaler);
 698 
 699     if (isNullScalerContext(context) || scalerInfo == NULL) {
 700         return ptr_to_jlong(getNullGlyphImage());
 701     }
 702 
 703     error = setupFTContext(env, font2D, scalerInfo, context);
 704     if (error) {
 705         invalidateJavaScaler(env, scaler, scalerInfo);
 706         return ptr_to_jlong(getNullGlyphImage());
 707     }
 708 
 709     if (!context->useSbits) {
 710         renderFlags |= FT_LOAD_NO_BITMAP;
 711     }
 712 
 713     /* NB: in case of non identity transform
 714      we might also prefer to disable transform before hinting,
 715      and apply it explicitly after hinting is performed.
 716      Or we can disable hinting. */
 717 
 718     /* select appropriate hinting mode */
 719     if (context->aaType == TEXT_AA_OFF) {
 720         target = FT_LOAD_TARGET_MONO;
 721     } else if (context->aaType == TEXT_AA_ON) {
 722         target = FT_LOAD_TARGET_NORMAL;
 723     } else if (context->aaType == TEXT_AA_LCD_HRGB ||
 724                context->aaType == TEXT_AA_LCD_HBGR) {
 725         target = FT_LOAD_TARGET_LCD;
 726     } else {
 727         target = FT_LOAD_TARGET_LCD_V;
 728     }
 729     renderFlags |= target;
 730 
 731     error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderFlags);
 732     if (error) {
 733         //do not destroy scaler yet.
 734         //this can be problem of particular context (e.g. with bad transform)
 735         return ptr_to_jlong(getNullGlyphImage());
 736     }
 737 
 738     ftglyph = scalerInfo->face->glyph;
 739 
 740     /* apply styles */
 741     if (context->doBold) { /* if bold style */
 742         FT_GlyphSlot_Embolden(ftglyph);
 743     }
 744     if (context->doItalize) { /* if oblique */
 745         FT_GlyphSlot_Oblique(ftglyph);
 746     }
 747 
 748     /* generate bitmap if it is not done yet
 749      e.g. if algorithmic styling is performed and style was added to outline */
 750     if (ftglyph->format == FT_GLYPH_FORMAT_OUTLINE) {
 751         FT_Render_Glyph(ftglyph, FT_LOAD_TARGET_MODE(target));
 752     }
 753 
 754     width  = (UInt16) ftglyph->bitmap.width;
 755     height = (UInt16) ftglyph->bitmap.rows;
 756 
 757     imageSize = width*height;
 758     glyphInfo = (GlyphInfo*) malloc(sizeof(GlyphInfo) + imageSize);
 759     if (glyphInfo == NULL) {
 760         glyphInfo = getNullGlyphImage();
 761         return ptr_to_jlong(glyphInfo);
 762     }
 763     glyphInfo->cellInfo  = NULL;
 764     glyphInfo->managed   = UNMANAGED_GLYPH;
 765     glyphInfo->rowBytes  = width;
 766     glyphInfo->width     = width;
 767     glyphInfo->height    = height;
 768     glyphInfo->topLeftX  = (float)  ftglyph->bitmap_left;
 769     glyphInfo->topLeftY  = (float) -ftglyph->bitmap_top;
 770 
 771     if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD) {
 772         glyphInfo->width = width/3;
 773     } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD_V) {
 774         glyphInfo->height = glyphInfo->height/3;
 775     }
 776 
 777     if (context->fmType == TEXT_FM_ON) {
 778         double advh = FTFixedToFloat(ftglyph->linearHoriAdvance);
 779         glyphInfo->advanceX =
 780             (float) (advh * FTFixedToFloat(context->transform.xx));
 781         glyphInfo->advanceY =
 782             (float) (advh * FTFixedToFloat(context->transform.xy));
 783     } else {
 784         if (!ftglyph->advance.y) {
 785             glyphInfo->advanceX =
 786                 (float) FT26Dot6ToInt(ftglyph->advance.x);
 787             glyphInfo->advanceY = 0;
 788         } else if (!ftglyph->advance.x) {
 789             glyphInfo->advanceX = 0;
 790             glyphInfo->advanceY =
 791                 (float) FT26Dot6ToInt(-ftglyph->advance.y);
 792         } else {
 793             glyphInfo->advanceX = FT26Dot6ToFloat(ftglyph->advance.x);
 794             glyphInfo->advanceY = FT26Dot6ToFloat(-ftglyph->advance.y);
 795         }
 796     }
 797 
 798     if (imageSize == 0) {
 799         glyphInfo->image = NULL;
 800     } else {
 801         glyphInfo->image = (unsigned char*) glyphInfo + sizeof(GlyphInfo);
 802         //convert result to output format
 803         //output format is either 3 bytes per pixel (for subpixel modes)
 804         // or 1 byte per pixel for AA and B&W
 805         if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_MONO) {
 806             /* convert from 8 pixels per byte to 1 byte per pixel */
 807             CopyBW2Grey8(ftglyph->bitmap.buffer,
 808                          ftglyph->bitmap.pitch,
 809                          (void *) glyphInfo->image,
 810                          width,
 811                          width,
 812                          height);
 813         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_GRAY) {
 814             /* byte per pixel to byte per pixel => just copy */
 815             memcpy(glyphInfo->image, ftglyph->bitmap.buffer, imageSize);
 816         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_GRAY4) {
 817             /* 4 bits per pixel to byte per pixel */
 818             CopyGrey4ToGrey8(ftglyph->bitmap.buffer,
 819                              ftglyph->bitmap.pitch,
 820                              (void *) glyphInfo->image,
 821                              width,
 822                              width,
 823                              height);
 824         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD) {
 825             /* 3 bytes per pixel to 3 bytes per pixel */
 826             CopyFTSubpixelToSubpixel(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_LCD_V) {
 833             /* 3 bytes per pixel to 3 bytes per pixel */
 834             CopyFTSubpixelVToSubpixel(ftglyph->bitmap.buffer,
 835                                       ftglyph->bitmap.pitch,
 836                                       (void *) glyphInfo->image,
 837                                       width*3,
 838                                       width,
 839                                       height);
 840             glyphInfo->rowBytes *=3;
 841         } else {
 842             free(glyphInfo);
 843             glyphInfo = getNullGlyphImage();
 844         }
 845     }
 846 
 847     return ptr_to_jlong(glyphInfo);
 848 }
 849 
 850 
 851 /*
 852  * Class:     sun_font_FreetypeFontScaler
 853  * Method:    getLayoutTableCacheNative
 854  * Signature: (J)J
 855  */
 856 JNIEXPORT jlong JNICALL
 857 Java_sun_font_FreetypeFontScaler_getLayoutTableCacheNative(
 858         JNIEnv *env, jobject scaler, jlong pScaler) {
 859     FTScalerInfo *scalerInfo = (FTScalerInfo*) jlong_to_ptr(pScaler);
 860 
 861     if (scalerInfo == NULL) {
 862         invalidateJavaScaler(env, scaler, scalerInfo);
 863         return 0L;
 864     }
 865 
 866     // init layout table cache in font
 867     // we're assuming the font is a file font and moreover it is Truetype font
 868     // otherwise we shouldn't be able to get here...
 869     if (scalerInfo->layoutTables == NULL) {
 870         scalerInfo->layoutTables = newLayoutTableCache();
 871     }
 872 
 873     return ptr_to_jlong(scalerInfo->layoutTables);
 874 }
 875 
 876 /*
 877  * Class:     sun_font_FreetypeFontScaler
 878  * Method:    disposeNativeScaler
 879  * Signature: (J)V
 880  */
 881 JNIEXPORT void JNICALL
 882 Java_sun_font_FreetypeFontScaler_disposeNativeScaler(
 883         JNIEnv *env, jobject scaler, jobject font2D, jlong pScaler) {
 884     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
 885 
 886     /* Freetype functions *may* cause callback to java
 887        that can use cached values. Make sure our cache is up to date.
 888        NB: scaler context is not important at this point, can use NULL. */
 889     int errCode = setupFTContext(env, font2D, scalerInfo, NULL);
 890     if (errCode) {
 891         return;
 892     }
 893 
 894     freeNativeResources(env, scalerInfo);
 895 }
 896 
 897 /*
 898  * Class:     sun_font_FreetypeFontScaler
 899  * Method:    getNumGlyphsNative
 900  * Signature: ()I
 901  */
 902 JNIEXPORT jint JNICALL
 903 Java_sun_font_FreetypeFontScaler_getNumGlyphsNative(
 904         JNIEnv *env, jobject scaler, jlong pScaler) {
 905     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
 906 
 907     if (scalerInfo == NULL || scalerInfo->face == NULL) { /* bad/null scaler */
 908         /* null scaler can render 1 glyph - "missing glyph" with code 0
 909            (all glyph codes requested by user are mapped to code 0 at
 910            validation step) */
 911         invalidateJavaScaler(env, scaler, scalerInfo);
 912         return (jint) 1;
 913     }
 914 
 915     return (jint) scalerInfo->face->num_glyphs;
 916 }
 917 
 918 /*
 919  * Class:     sun_font_FreetypeFontScaler
 920  * Method:    getMissingGlyphCodeNative
 921  * Signature: ()I
 922  */
 923 JNIEXPORT jint JNICALL
 924 Java_sun_font_FreetypeFontScaler_getMissingGlyphCodeNative(
 925         JNIEnv *env, jobject scaler, jlong pScaler) {
 926 
 927     /* Is it always 0 for freetype? */
 928     return 0;
 929 }
 930 
 931 /*
 932  * Class:     sun_font_FreetypeFontScaler
 933  * Method:    getGlyphCodeNative
 934  * Signature: (C)I
 935  */
 936 JNIEXPORT jint JNICALL
 937 Java_sun_font_FreetypeFontScaler_getGlyphCodeNative(
 938         JNIEnv *env, jobject scaler,
 939         jobject font2D, jlong pScaler, jchar charCode) {
 940 
 941     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
 942     int errCode;
 943 
 944     if (scaler == NULL || scalerInfo->face == NULL) { /* bad/null scaler */
 945         invalidateJavaScaler(env, scaler, scalerInfo);
 946         return 0;
 947     }
 948 
 949     /* Freetype functions *may* cause callback to java
 950        that can use cached values. Make sure our cache is up to date.
 951        Scaler context is not important here, can use NULL. */
 952     errCode = setupFTContext(env, font2D, scalerInfo, NULL);
 953     if (errCode) {
 954         return 0;
 955     }
 956 
 957     return FT_Get_Char_Index(scalerInfo->face, charCode);
 958 }
 959 
 960 
 961 #define FloatToF26Dot6(x) ((unsigned int) ((x)*64))
 962 
 963 static FT_Outline* getFTOutline(JNIEnv* env, jobject font2D,
 964         FTScalerContext *context, FTScalerInfo* scalerInfo,
 965         jint glyphCode, jfloat xpos, jfloat ypos) {
 966     int renderFlags;
 967     FT_Error error;
 968     FT_GlyphSlot ftglyph;
 969 
 970     if (glyphCode >= INVISIBLE_GLYPHS ||
 971             isNullScalerContext(context) || scalerInfo == NULL) {
 972         return NULL;
 973     }
 974 
 975     error = setupFTContext(env, font2D, scalerInfo, context);
 976     if (error) {
 977         return NULL;
 978     }
 979 
 980     renderFlags = FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP;
 981 
 982     error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderFlags);
 983     if (error) {
 984         return NULL;
 985     }
 986 
 987     ftglyph = scalerInfo->face->glyph;
 988 
 989     /* apply styles */
 990     if (context->doBold) { /* if bold style */
 991         FT_GlyphSlot_Embolden(ftglyph);
 992     }
 993     if (context->doItalize) { /* if oblique */
 994         FT_GlyphSlot_Oblique(ftglyph);
 995     }
 996 
 997     FT_Outline_Translate(&ftglyph->outline,
 998                          FloatToF26Dot6(xpos),
 999                          -FloatToF26Dot6(ypos));
1000 
1001     return &ftglyph->outline;
1002 }
1003 
1004 #define F26Dot6ToFloat(n) (((float)(n))/((float) 64))
1005 
1006 /* Types of GeneralPath segments.
1007    TODO: pull constants from other place? */
1008 
1009 #define SEG_UNKNOWN -1
1010 #define SEG_MOVETO   0
1011 #define SEG_LINETO   1
1012 #define SEG_QUADTO   2
1013 #define SEG_CUBICTO  3
1014 #define SEG_CLOSE    4
1015 
1016 #define WIND_NON_ZERO 0
1017 #define WIND_EVEN_ODD 1
1018 
1019 /* Placeholder to accumulate GeneralPath data */
1020 typedef struct {
1021     jint numTypes;
1022     jint numCoords;
1023     jint lenTypes;
1024     jint lenCoords;
1025     jint wr;
1026     jbyte* pointTypes;
1027     jfloat* pointCoords;
1028 } GPData;
1029 
1030 /* returns 0 on failure */
1031 static int allocateSpaceForGP(GPData* gpdata, int npoints, int ncontours) {
1032     int maxTypes, maxCoords;
1033 
1034     /* we may have up to N intermediate points per contour
1035        (and for each point can actually cause new curve to be generated)
1036        In addition we can also have 2 extra point per outline.
1037      */
1038     maxTypes  = 2*npoints  + 2*ncontours;
1039     maxCoords = 4*(npoints + 2*ncontours); //we may need to insert
1040                                            //up to n-1 intermediate points
1041 
1042     /* first usage - allocate space and intialize all fields */
1043     if (gpdata->pointTypes == NULL || gpdata->pointCoords == NULL) {
1044         gpdata->lenTypes  = maxTypes;
1045         gpdata->lenCoords = maxCoords;
1046         gpdata->pointTypes  = (jbyte*)
1047              malloc(gpdata->lenTypes*sizeof(jbyte));
1048         gpdata->pointCoords = (jfloat*)
1049              malloc(gpdata->lenCoords*sizeof(jfloat));
1050         gpdata->numTypes = 0;
1051         gpdata->numCoords = 0;
1052         gpdata->wr = WIND_NON_ZERO; /* By default, outlines are filled
1053                                        using the non-zero winding rule. */
1054     } else {
1055         /* do we have enough space? */
1056         if (gpdata->lenTypes - gpdata->numTypes < maxTypes) {
1057             gpdata->lenTypes  += maxTypes;
1058             gpdata->pointTypes  = (jbyte*)
1059               realloc(gpdata->pointTypes, gpdata->lenTypes*sizeof(jbyte));
1060         }
1061 
1062         if (gpdata->lenCoords - gpdata->numCoords < maxCoords) {
1063             gpdata->lenCoords += maxCoords;
1064             gpdata->pointCoords = (jfloat*)
1065               realloc(gpdata->pointCoords, gpdata->lenCoords*sizeof(jfloat));
1066         }
1067     }
1068 
1069     /* failure if any of mallocs failed */
1070     if (gpdata->pointTypes == NULL ||  gpdata->pointCoords == NULL)
1071         return 0;
1072     else
1073         return 1;
1074 }
1075 
1076 static void addSeg(GPData *gp, jbyte type) {
1077     gp->pointTypes[gp->numTypes++] = type;
1078 }
1079 
1080 static void addCoords(GPData *gp, FT_Vector *p) {
1081     gp->pointCoords[gp->numCoords++] =  F26Dot6ToFloat(p->x);
1082     gp->pointCoords[gp->numCoords++] = -F26Dot6ToFloat(p->y);
1083 }
1084 
1085 static int moveTo(FT_Vector *to, GPData *gp) {
1086     if (gp->numCoords)
1087         addSeg(gp, SEG_CLOSE);
1088     addCoords(gp, to);
1089     addSeg(gp, SEG_MOVETO);
1090     return FT_Err_Ok;
1091 }
1092 
1093 static int lineTo(FT_Vector *to, GPData *gp) {
1094     addCoords(gp, to);
1095     addSeg(gp, SEG_LINETO);
1096     return FT_Err_Ok;
1097 }
1098 
1099 static int conicTo(FT_Vector *control, FT_Vector *to, GPData *gp) {
1100     addCoords(gp, control);
1101     addCoords(gp, to);
1102     addSeg(gp, SEG_QUADTO);
1103     return FT_Err_Ok;
1104 }
1105 
1106 static int cubicTo(FT_Vector *control1,
1107                    FT_Vector *control2,
1108                    FT_Vector *to,
1109                    GPData    *gp) {
1110     addCoords(gp, control1);
1111     addCoords(gp, control2);
1112     addCoords(gp, to);
1113     addSeg(gp, SEG_CUBICTO);
1114     return FT_Err_Ok;
1115 }
1116 
1117 static void addToGP(GPData* gpdata, FT_Outline*outline) {
1118     static const FT_Outline_Funcs outline_funcs = {
1119         (FT_Outline_MoveToFunc) moveTo,
1120         (FT_Outline_LineToFunc) lineTo,
1121         (FT_Outline_ConicToFunc) conicTo,
1122         (FT_Outline_CubicToFunc) cubicTo,
1123         0, /* shift */
1124         0, /* delta */
1125     };
1126 
1127     FT_Outline_Decompose(outline, &outline_funcs, gpdata);
1128     if (gpdata->numCoords)
1129         addSeg(gpdata, SEG_CLOSE);
1130 
1131     /* If set to 1, the outline will be filled using the even-odd fill rule */
1132     if (outline->flags & FT_OUTLINE_EVEN_ODD_FILL) {
1133         gpdata->wr = WIND_EVEN_ODD;
1134     }
1135 }
1136 
1137 static void freeGP(GPData* gpdata) {
1138     if (gpdata->pointCoords != NULL) {
1139         free(gpdata->pointCoords);
1140         gpdata->pointCoords = NULL;
1141         gpdata->numCoords = 0;
1142         gpdata->lenCoords = 0;
1143     }
1144     if (gpdata->pointTypes != NULL) {
1145         free(gpdata->pointTypes);
1146         gpdata->pointTypes = NULL;
1147         gpdata->numTypes = 0;
1148         gpdata->lenTypes = 0;
1149     }
1150 }
1151 
1152 static jobject getGlyphGeneralPath(JNIEnv* env, jobject font2D,
1153         FTScalerContext *context, FTScalerInfo *scalerInfo,
1154         jint glyphCode, jfloat xpos, jfloat ypos) {
1155 
1156     FT_Outline* outline;
1157     jobject gp = NULL;
1158     jbyteArray types;
1159     jfloatArray coords;
1160     GPData gpdata;
1161 
1162     outline = getFTOutline(env, font2D, context, scalerInfo,
1163                            glyphCode, xpos, ypos);
1164 
1165     if (outline == NULL || outline->n_points == 0) {
1166         return gp;
1167     }
1168 
1169     gpdata.pointTypes  = NULL;
1170     gpdata.pointCoords = NULL;
1171     if (!allocateSpaceForGP(&gpdata, outline->n_points, outline->n_contours)) {
1172         return gp;
1173     }
1174 
1175     addToGP(&gpdata, outline);
1176 
1177     types  = (*env)->NewByteArray(env, gpdata.numTypes);
1178     coords = (*env)->NewFloatArray(env, gpdata.numCoords);
1179 
1180     if (types && coords) {
1181         (*env)->SetByteArrayRegion(env, types, 0,
1182                                    gpdata.numTypes,
1183                                    gpdata.pointTypes);
1184         (*env)->SetFloatArrayRegion(env, coords, 0,
1185                                     gpdata.numCoords,
1186                                     gpdata.pointCoords);
1187         gp = (*env)->NewObject(env,
1188                                sunFontIDs.gpClass,
1189                                sunFontIDs.gpCtr,
1190                                gpdata.wr,
1191                                types,
1192                                gpdata.numTypes,
1193                                coords,
1194                                gpdata.numCoords);
1195     }
1196 
1197     freeGP(&gpdata);
1198 
1199     return gp;
1200 }
1201 
1202 /*
1203  * Class:     sun_font_FreetypeFontScaler
1204  * Method:    getGlyphOutlineNative
1205  * Signature: (Lsun/font/Font2D;JIFF)Ljava/awt/geom/GeneralPath;
1206  */
1207 JNIEXPORT jobject JNICALL
1208 Java_sun_font_FreetypeFontScaler_getGlyphOutlineNative(
1209       JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
1210       jlong pScaler, jint glyphCode, jfloat xpos, jfloat ypos) {
1211 
1212     FTScalerContext *context =
1213          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1214     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
1215 
1216     jobject gp = getGlyphGeneralPath(env,
1217                                font2D,
1218                                context,
1219                                scalerInfo,
1220                                glyphCode,
1221                                xpos,
1222                                ypos);
1223     if (gp == NULL) { /* can be legal */
1224         gp = (*env)->NewObject(env,
1225                                sunFontIDs.gpClass,
1226                                sunFontIDs.gpCtrEmpty);
1227     }
1228     return gp;
1229 }
1230 
1231 /*
1232  * Class:     sun_font_FreetypeFontScaler
1233  * Method:    getGlyphOutlineBoundsNative
1234  * Signature: (Lsun/font/Font2D;JI)Ljava/awt/geom/Rectangle2D/Float;
1235  */
1236 JNIEXPORT jobject JNICALL
1237 Java_sun_font_FreetypeFontScaler_getGlyphOutlineBoundsNative(
1238         JNIEnv *env, jobject scaler, jobject font2D,
1239         jlong pScalerContext, jlong pScaler, jint glyphCode) {
1240 
1241     FT_Outline *outline;
1242     FT_BBox bbox;
1243     int error;
1244     jobject bounds;
1245 
1246     FTScalerContext *context =
1247          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1248     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
1249 
1250     outline = getFTOutline(env, font2D, context, scalerInfo, glyphCode, 0, 0);
1251     if (outline == NULL || outline->n_points == 0) {
1252         /* it is legal case, e.g. invisible glyph */
1253         bounds = (*env)->NewObject(env,
1254                                  sunFontIDs.rect2DFloatClass,
1255                                  sunFontIDs.rect2DFloatCtr);
1256         return bounds;
1257     }
1258 
1259     error = FT_Outline_Get_BBox(outline, &bbox);
1260 
1261     //convert bbox
1262     if (error || bbox.xMin >= bbox.xMax || bbox.yMin >= bbox.yMax) {
1263         bounds = (*env)->NewObject(env,
1264                                    sunFontIDs.rect2DFloatClass,
1265                                    sunFontIDs.rect2DFloatCtr);
1266     } else {
1267         bounds = (*env)->NewObject(env,
1268                                    sunFontIDs.rect2DFloatClass,
1269                                    sunFontIDs.rect2DFloatCtr4,
1270                                    F26Dot6ToFloat(bbox.xMin),
1271                                    F26Dot6ToFloat(-bbox.yMax),
1272                                    F26Dot6ToFloat(bbox.xMax-bbox.xMin),
1273                                    F26Dot6ToFloat(bbox.yMax-bbox.yMin));
1274     }
1275 
1276     return bounds;
1277 }
1278 
1279 /*
1280  * Class:     sun_font_FreetypeFontScaler
1281  * Method:    getGlyphVectorOutlineNative
1282  * Signature: (Lsun/font/Font2D;J[IIFF)Ljava/awt/geom/GeneralPath;
1283  */
1284 JNIEXPORT jobject
1285 JNICALL
1286 Java_sun_font_FreetypeFontScaler_getGlyphVectorOutlineNative(
1287         JNIEnv *env, jobject scaler, jobject font2D,
1288         jlong pScalerContext, jlong pScaler,
1289         jintArray glyphArray, jint numGlyphs, jfloat xpos, jfloat ypos) {
1290 
1291     FT_Outline* outline;
1292     jobject gp = NULL;
1293     jbyteArray types;
1294     jfloatArray coords;
1295     GPData gpdata;
1296     int i;
1297     jint *glyphs;
1298 
1299     FTScalerContext *context =
1300          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1301     FTScalerInfo *scalerInfo =
1302              (FTScalerInfo*) jlong_to_ptr(pScaler);
1303 
1304     glyphs = NULL;
1305     if (numGlyphs > 0 && 0xffffffffu / sizeof(jint) >= numGlyphs) {
1306         glyphs = (jint*) malloc(numGlyphs*sizeof(jint));
1307     }
1308     if (glyphs == NULL) {
1309         // We reach here if:
1310         // 1. numGlyphs <= 0,
1311         // 2. overflow check failed, or
1312         // 3. malloc failed.
1313         gp = (*env)->NewObject(env, sunFontIDs.gpClass, sunFontIDs.gpCtrEmpty);
1314         return gp;
1315     }
1316 
1317     (*env)->GetIntArrayRegion(env, glyphArray, 0, numGlyphs, glyphs);
1318 
1319     gpdata.numCoords = 0;
1320     for (i=0; i<numGlyphs;i++) {
1321         if (glyphs[i] >= INVISIBLE_GLYPHS) {
1322             continue;
1323         }
1324         outline = getFTOutline(env,
1325                                font2D,
1326                                context,
1327                                scalerInfo,
1328                                glyphs[i],
1329                                xpos, ypos);
1330 
1331         if (outline == NULL || outline->n_points == 0) {
1332             continue;
1333         }
1334 
1335         gpdata.pointTypes  = NULL;
1336         gpdata.pointCoords = NULL;
1337         if (!allocateSpaceForGP(&gpdata, outline->n_points,
1338                                 outline->n_contours)) {
1339             break;
1340         }
1341 
1342         addToGP(&gpdata, outline);
1343     }
1344     free(glyphs);
1345 
1346     if (gpdata.numCoords != 0) {
1347       types = (*env)->NewByteArray(env, gpdata.numTypes);
1348       coords = (*env)->NewFloatArray(env, gpdata.numCoords);
1349 
1350       if (types && coords) {
1351         (*env)->SetByteArrayRegion(env, types, 0,
1352                                    gpdata.numTypes, gpdata.pointTypes);
1353         (*env)->SetFloatArrayRegion(env, coords, 0,
1354                                     gpdata.numCoords, gpdata.pointCoords);
1355 
1356         gp=(*env)->NewObject(env,
1357                              sunFontIDs.gpClass,
1358                              sunFontIDs.gpCtr,
1359                              gpdata.wr,
1360                              types,
1361                              gpdata.numTypes,
1362                              coords,
1363                              gpdata.numCoords);
1364         return gp;
1365       }
1366     }
1367     return (*env)->NewObject(env, sunFontIDs.gpClass, sunFontIDs.gpCtrEmpty);
1368 }
1369 
1370 JNIEXPORT jlong JNICALL
1371 Java_sun_font_FreetypeFontScaler_getUnitsPerEMNative(
1372         JNIEnv *env, jobject scaler, jlong pScaler) {
1373 
1374     FTScalerInfo *s = (FTScalerInfo* ) jlong_to_ptr(pScaler);
1375 
1376     /* Freetype doc says:
1377      The number of font units per EM square for this face.
1378      This is typically 2048 for TrueType fonts, and 1000 for Type 1 fonts.
1379      Only relevant for scalable formats.
1380      However, layout engine might be not tested with anything but 2048.
1381 
1382      NB: test it! */
1383     if (s != NULL) {
1384         return s->face->units_per_EM;
1385     }
1386     return 2048;
1387 }
1388 
1389 /* This native method is called by the OpenType layout engine. */
1390 JNIEXPORT jobject JNICALL
1391 Java_sun_font_FreetypeFontScaler_getGlyphPointNative(
1392         JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
1393         jlong pScaler, jint glyphCode, jint pointNumber) {
1394 
1395     FT_Outline* outline;
1396     jobject point = NULL;
1397     jfloat x=0, y=0;
1398     FTScalerContext *context =
1399          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1400     FTScalerInfo *scalerInfo = (FTScalerInfo*) jlong_to_ptr(pScaler);
1401 
1402     outline = getFTOutline(env, font2D, context, scalerInfo, glyphCode, 0, 0);
1403 
1404     if (outline != NULL && outline->n_points > pointNumber) {
1405         x =  F26Dot6ToFloat(outline->points[pointNumber].x);
1406         y = -F26Dot6ToFloat(outline->points[pointNumber].y);
1407     }
1408 
1409     return (*env)->NewObject(env, sunFontIDs.pt2DFloatClass,
1410                              sunFontIDs.pt2DFloatCtr, x, y);
1411 }