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