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