1 /*
   2  * Copyright (c) 2007, 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  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(FT_MulFix(
 494                        ((jlong) scalerInfo->face->ascender + bmodifier/2),
 495                        (jlong) scalerInfo->face->size->metrics.y_scale));
 496     /* descent */
 497     dx = 0;
 498     dy = -(jfloat) FT26Dot6ToFloat(FT_MulFix(
 499                        ((jlong) scalerInfo->face->descender + bmodifier/2),
 500                        (jlong) scalerInfo->face->size->metrics.y_scale));
 501     /* baseline */
 502     bx = by = 0;
 503 
 504     /* leading */
 505     lx = 0;
 506     ly = (jfloat) FT26Dot6ToFloat(FT_MulFix(
 507                       (jlong) scalerInfo->face->height + bmodifier,
 508                       (jlong) scalerInfo->face->size->metrics.y_scale))
 509                   + ay - dy;
 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->managed   = UNMANAGED_GLYPH;
 787     glyphInfo->rowBytes  = width;
 788     glyphInfo->width     = width;
 789     glyphInfo->height    = height;
 790     glyphInfo->topLeftX  = (float)  ftglyph->bitmap_left;
 791     glyphInfo->topLeftY  = (float) -ftglyph->bitmap_top;
 792 
 793     if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD) {
 794         glyphInfo->width = width/3;
 795     } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD_V) {
 796         glyphInfo->height = glyphInfo->height/3;
 797     }
 798 
 799     if (context->fmType == TEXT_FM_ON) {
 800         double advh = FTFixedToFloat(ftglyph->linearHoriAdvance);
 801         glyphInfo->advanceX =
 802             (float) (advh * FTFixedToFloat(context->transform.xx));
 803         glyphInfo->advanceY =
 804             (float) (advh * FTFixedToFloat(context->transform.xy));
 805     } else {
 806         if (!ftglyph->advance.y) {
 807             glyphInfo->advanceX =
 808                 (float) ROUND(FT26Dot6ToFloat(ftglyph->advance.x));
 809             glyphInfo->advanceY = 0;
 810         } else if (!ftglyph->advance.x) {
 811             glyphInfo->advanceX = 0;
 812             glyphInfo->advanceY =
 813                 (float) ROUND(FT26Dot6ToFloat(-ftglyph->advance.y));
 814         } else {
 815             glyphInfo->advanceX = FT26Dot6ToFloat(ftglyph->advance.x);
 816             glyphInfo->advanceY = FT26Dot6ToFloat(-ftglyph->advance.y);
 817         }
 818     }
 819 
 820     if (imageSize == 0) {
 821         glyphInfo->image = NULL;
 822     } else {
 823         glyphInfo->image = (unsigned char*) glyphInfo + sizeof(GlyphInfo);
 824         //convert result to output format
 825         //output format is either 3 bytes per pixel (for subpixel modes)
 826         // or 1 byte per pixel for AA and B&W
 827         if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_MONO) {
 828             /* convert from 8 pixels per byte to 1 byte per pixel */
 829             CopyBW2Grey8(ftglyph->bitmap.buffer,
 830                          ftglyph->bitmap.pitch,
 831                          (void *) glyphInfo->image,
 832                          width,
 833                          width,
 834                          height);
 835         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_GRAY) {
 836             /* byte per pixel to byte per pixel => just copy */
 837             memcpy(glyphInfo->image, ftglyph->bitmap.buffer, imageSize);
 838         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_GRAY4) {
 839             /* 4 bits per pixel to byte per pixel */
 840             CopyGrey4ToGrey8(ftglyph->bitmap.buffer,
 841                              ftglyph->bitmap.pitch,
 842                              (void *) glyphInfo->image,
 843                              width,
 844                              width,
 845                              height);
 846         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD) {
 847             /* 3 bytes per pixel to 3 bytes per pixel */
 848             CopyFTSubpixelToSubpixel(ftglyph->bitmap.buffer,
 849                                      ftglyph->bitmap.pitch,
 850                                      (void *) glyphInfo->image,
 851                                      width,
 852                                      width,
 853                                      height);
 854         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD_V) {
 855             /* 3 bytes per pixel to 3 bytes per pixel */
 856             CopyFTSubpixelVToSubpixel(ftglyph->bitmap.buffer,
 857                                       ftglyph->bitmap.pitch,
 858                                       (void *) glyphInfo->image,
 859                                       width*3,
 860                                       width,
 861                                       height);
 862             glyphInfo->rowBytes *=3;
 863         } else {
 864             free(glyphInfo);
 865             glyphInfo = getNullGlyphImage();
 866         }
 867     }
 868 
 869     return ptr_to_jlong(glyphInfo);
 870 }
 871 
 872 
 873 /*
 874  * Class:     sun_font_FreetypeFontScaler
 875  * Method:    getLayoutTableCacheNative
 876  * Signature: (J)J
 877  */
 878 JNIEXPORT jlong JNICALL
 879 Java_sun_font_FreetypeFontScaler_getLayoutTableCacheNative(
 880         JNIEnv *env, jobject scaler, jlong pScaler) {
 881     FTScalerInfo *scalerInfo = (FTScalerInfo*) jlong_to_ptr(pScaler);
 882 
 883     if (scalerInfo == NULL) {
 884         invalidateJavaScaler(env, scaler, scalerInfo);
 885         return 0L;
 886     }
 887 
 888     // init layout table cache in font
 889     // we're assuming the font is a file font and moreover it is Truetype font
 890     // otherwise we shouldn't be able to get here...
 891     if (scalerInfo->layoutTables == NULL) {
 892         scalerInfo->layoutTables = newLayoutTableCache();
 893     }
 894 
 895     return ptr_to_jlong(scalerInfo->layoutTables);
 896 }
 897 
 898 /*
 899  * Class:     sun_font_FreetypeFontScaler
 900  * Method:    disposeNativeScaler
 901  * Signature: (J)V
 902  */
 903 JNIEXPORT void JNICALL
 904 Java_sun_font_FreetypeFontScaler_disposeNativeScaler(
 905         JNIEnv *env, jobject scaler, jlong pScaler) {
 906     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
 907 
 908     /* Freetype functions *may* cause callback to java
 909        that can use cached values. Make sure our cache is up to date.
 910        NB: scaler context is not important at this point, can use NULL. */
 911     int errCode = setupFTContext(env, scaler, scalerInfo, NULL);
 912     if (errCode) {
 913         return;
 914     }
 915 
 916     freeNativeResources(env, scalerInfo);
 917 }
 918 
 919 /*
 920  * Class:     sun_font_FreetypeFontScaler
 921  * Method:    getNumGlyphsNative
 922  * Signature: ()I
 923  */
 924 JNIEXPORT jint JNICALL
 925 Java_sun_font_FreetypeFontScaler_getNumGlyphsNative(
 926         JNIEnv *env, jobject scaler, jlong pScaler) {
 927     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
 928 
 929     if (scalerInfo == NULL || scalerInfo->face == NULL) { /* bad/null scaler */
 930         /* null scaler can render 1 glyph - "missing glyph" with code 0
 931            (all glyph codes requested by user are mapped to code 0 at
 932            validation step) */
 933         invalidateJavaScaler(env, scaler, scalerInfo);
 934         return (jint) 1;
 935     }
 936 
 937     return (jint) scalerInfo->face->num_glyphs;
 938 }
 939 
 940 /*
 941  * Class:     sun_font_FreetypeFontScaler
 942  * Method:    getMissingGlyphCodeNative
 943  * Signature: ()I
 944  */
 945 JNIEXPORT jint JNICALL
 946 Java_sun_font_FreetypeFontScaler_getMissingGlyphCodeNative(
 947         JNIEnv *env, jobject scaler, jlong pScaler) {
 948 
 949     /* Is it always 0 for freetype? */
 950     return 0;
 951 }
 952 
 953 /*
 954  * Class:     sun_font_FreetypeFontScaler
 955  * Method:    getGlyphCodeNative
 956  * Signature: (C)I
 957  */
 958 JNIEXPORT jint JNICALL
 959 Java_sun_font_FreetypeFontScaler_getGlyphCodeNative(
 960         JNIEnv *env, jobject scaler, jlong pScaler, jchar charCode) {
 961 
 962     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
 963     int errCode;
 964 
 965     if (scaler == NULL || scalerInfo->face == NULL) { /* bad/null scaler */
 966         invalidateJavaScaler(env, scaler, scalerInfo);
 967         return 0;
 968     }
 969 
 970     /* Freetype functions *may* cause callback to java
 971        that can use cached values. Make sure our cache is up to date.
 972        Scaler context is not important here, can use NULL. */
 973     errCode = setupFTContext(env, scaler, scalerInfo, NULL);
 974     if (errCode) {
 975         return 0;
 976     }
 977 
 978     return FT_Get_Char_Index(scalerInfo->face, charCode);
 979 }
 980 
 981 
 982 #define FloatToF26Dot6(x) ((unsigned int) ((x)*64))
 983 
 984 static FT_Outline* getFTOutline(JNIEnv* env, jobject font2D,
 985         FTScalerContext *context, FTScalerInfo* scalerInfo,
 986         jint glyphCode, jfloat xpos, jfloat ypos) {
 987     int renderFlags;
 988     int glyph_index;
 989     FT_Error error;
 990     FT_GlyphSlot ftglyph;
 991 
 992     if (glyphCode >= INVISIBLE_GLYPHS ||
 993             isNullScalerContext(context) || scalerInfo == NULL) {
 994         return NULL;
 995     }
 996 
 997     error = setupFTContext(env, font2D, scalerInfo, context);
 998     if (error) {
 999         return NULL;
1000     }
1001 
1002     renderFlags = FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP;
1003 
1004     glyph_index = FT_Get_Char_Index(scalerInfo->face, glyphCode);
1005 
1006     error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderFlags);
1007     if (error) {
1008         return NULL;
1009     }
1010 
1011     ftglyph = scalerInfo->face->glyph;
1012 
1013     /* apply styles */
1014     if (context->doBold) { /* if bold style */
1015         FT_GlyphSlot_Embolden(ftglyph);
1016     }
1017     if (context->doItalize) { /* if oblique */
1018         FT_GlyphSlot_Oblique(ftglyph);
1019     }
1020 
1021     FT_Outline_Translate(&ftglyph->outline,
1022                          FloatToF26Dot6(xpos),
1023                          -FloatToF26Dot6(ypos));
1024 
1025     return &ftglyph->outline;
1026 }
1027 
1028 #define F26Dot6ToFloat(n) (((float)(n))/((float) 64))
1029 
1030 /* Types of GeneralPath segments.
1031    TODO: pull constants from other place? */
1032 
1033 #define SEG_UNKNOWN -1
1034 #define SEG_MOVETO   0
1035 #define SEG_LINETO   1
1036 #define SEG_QUADTO   2
1037 #define SEG_CUBICTO  3
1038 #define SEG_CLOSE    4
1039 
1040 #define WIND_NON_ZERO 0
1041 #define WIND_EVEN_ODD 1
1042 
1043 /* Placeholder to accumulate GeneralPath data */
1044 typedef struct {
1045     jint numTypes;
1046     jint numCoords;
1047     jint lenTypes;
1048     jint lenCoords;
1049     jint wr;
1050     jbyte* pointTypes;
1051     jfloat* pointCoords;
1052 } GPData;
1053 
1054 /* returns 0 on failure */
1055 static int allocateSpaceForGP(GPData* gpdata, int npoints, int ncontours) {
1056     int maxTypes, maxCoords;
1057 
1058     /* we may have up to N intermediate points per contour
1059        (and for each point can actually cause new curve to be generated)
1060        In addition we can also have 2 extra point per outline.
1061      */
1062     maxTypes  = 2*npoints  + 2*ncontours;
1063     maxCoords = 4*(npoints + 2*ncontours); //we may need to insert
1064                                            //up to n-1 intermediate points
1065 
1066     /* first usage - allocate space and intialize all fields */
1067     if (gpdata->pointTypes == NULL || gpdata->pointCoords == NULL) {
1068         gpdata->lenTypes  = maxTypes;
1069         gpdata->lenCoords = maxCoords;
1070         gpdata->pointTypes  = (jbyte*)
1071              malloc(gpdata->lenTypes*sizeof(jbyte));
1072         gpdata->pointCoords = (jfloat*)
1073              malloc(gpdata->lenCoords*sizeof(jfloat));
1074         gpdata->numTypes = 0;
1075         gpdata->numCoords = 0;
1076         gpdata->wr = WIND_NON_ZERO; /* By default, outlines are filled
1077                                        using the non-zero winding rule. */
1078     } else {
1079         /* do we have enough space? */
1080         if (gpdata->lenTypes - gpdata->numTypes < maxTypes) {
1081             gpdata->lenTypes  += maxTypes;
1082             gpdata->pointTypes  = (jbyte*)
1083               realloc(gpdata->pointTypes, gpdata->lenTypes*sizeof(jbyte));
1084         }
1085 
1086         if (gpdata->lenCoords - gpdata->numCoords < maxCoords) {
1087             gpdata->lenCoords += maxCoords;
1088             gpdata->pointCoords = (jfloat*)
1089               realloc(gpdata->pointCoords, gpdata->lenCoords*sizeof(jfloat));
1090         }
1091     }
1092 
1093     /* failure if any of mallocs failed */
1094     if (gpdata->pointTypes == NULL ||  gpdata->pointCoords == NULL)
1095         return 0;
1096     else
1097         return 1;
1098 }
1099 
1100 static void addToGP(GPData* gpdata, FT_Outline*outline) {
1101     jbyte current_type=SEG_UNKNOWN;
1102     int i, j;
1103     jfloat x, y;
1104 
1105     j = 0;
1106     for(i=0; i<outline->n_points; i++) {
1107         x =  F26Dot6ToFloat(outline->points[i].x);
1108         y = -F26Dot6ToFloat(outline->points[i].y);
1109 
1110         if (FT_CURVE_TAG(outline->tags[i]) == FT_CURVE_TAG_ON) {
1111             /* If bit 0 is unset, the point is "off" the curve,
1112              i.e., a Bezier control point, while it is "on" when set. */
1113             if (current_type == SEG_UNKNOWN) { /* special case:
1114                                                   very first point */
1115                 /* add segment */
1116                 gpdata->pointTypes[gpdata->numTypes++] = SEG_MOVETO;
1117                 current_type = SEG_LINETO;
1118             } else {
1119                 gpdata->pointTypes[gpdata->numTypes++] = current_type;
1120                 current_type = SEG_LINETO;
1121             }
1122         } else {
1123             if (current_type == SEG_UNKNOWN) { /* special case:
1124                                                    very first point */
1125                 if (FT_CURVE_TAG(outline->tags[i+1]) == FT_CURVE_TAG_ON) {
1126                     /* just skip first point. Adhoc heuristic? */
1127                     continue;
1128                 } else {
1129                     x = (x + F26Dot6ToFloat(outline->points[i+1].x))/2;
1130                     y = (y - F26Dot6ToFloat(outline->points[i+1].y))/2;
1131                     gpdata->pointTypes[gpdata->numTypes++] = SEG_MOVETO;
1132                     current_type = SEG_LINETO;
1133                 }
1134             } else if (FT_CURVE_TAG(outline->tags[i]) == FT_CURVE_TAG_CUBIC) {
1135                 /* Bit 1 is meaningful for 'off' points only.
1136                    If set, it indicates a third-order Bezier arc control
1137                    point; and a second-order control point if unset.  */
1138                 current_type = SEG_CUBICTO;
1139             } else {
1140                 /* two successive conic "off" points forces the rasterizer
1141                    to create (during the scan-line conversion process
1142                    exclusively) a virtual "on" point amidst them, at their
1143                    exact middle. This greatly facilitates the definition of
1144                    successive conic Bezier arcs.  Moreover, it is the way
1145                    outlines are described in the TrueType specification. */
1146                 if (current_type == SEG_QUADTO) {
1147                     gpdata->pointCoords[gpdata->numCoords++] =
1148                         F26Dot6ToFloat(outline->points[i].x +
1149                         outline->points[i-1].x)/2;
1150                     gpdata->pointCoords[gpdata->numCoords++] =
1151                         - F26Dot6ToFloat(outline->points[i].y +
1152                         outline->points[i-1].y)/2;
1153                     gpdata->pointTypes[gpdata->numTypes++] = SEG_QUADTO;
1154                 }
1155                 current_type = SEG_QUADTO;
1156             }
1157         }
1158         gpdata->pointCoords[gpdata->numCoords++] = x;
1159         gpdata->pointCoords[gpdata->numCoords++] = y;
1160         if (outline->contours[j] == i) { //end of contour
1161             int start = j > 0 ? outline->contours[j-1]+1 : 0;
1162             gpdata->pointTypes[gpdata->numTypes++] = current_type;
1163             if (current_type == SEG_QUADTO &&
1164             FT_CURVE_TAG(outline->tags[start]) != FT_CURVE_TAG_ON) {
1165                 gpdata->pointCoords[gpdata->numCoords++] =
1166                             (F26Dot6ToFloat(outline->points[start].x) + x)/2;
1167                 gpdata->pointCoords[gpdata->numCoords++] =
1168                             (-F26Dot6ToFloat(outline->points[start].y) + y)/2;
1169             } else {
1170                 gpdata->pointCoords[gpdata->numCoords++] =
1171                             F26Dot6ToFloat(outline->points[start].x);
1172                 gpdata->pointCoords[gpdata->numCoords++] =
1173                             -F26Dot6ToFloat(outline->points[start].y);
1174             }
1175             gpdata->pointTypes[gpdata->numTypes++] = SEG_CLOSE;
1176             current_type = SEG_UNKNOWN;
1177             j++;
1178         }
1179     }
1180 
1181     /* If set to 1, the outline will be filled using the even-odd fill rule */
1182     if (outline->flags & FT_OUTLINE_EVEN_ODD_FILL) {
1183         gpdata->wr = WIND_EVEN_ODD;
1184     }
1185 }
1186 
1187 static void freeGP(GPData* gpdata) {
1188     if (gpdata->pointCoords != NULL) {
1189         free(gpdata->pointCoords);
1190         gpdata->pointCoords = NULL;
1191         gpdata->numCoords = 0;
1192         gpdata->lenCoords = 0;
1193     }
1194     if (gpdata->pointTypes != NULL) {
1195         free(gpdata->pointTypes);
1196         gpdata->pointTypes = NULL;
1197         gpdata->numTypes = 0;
1198         gpdata->lenTypes = 0;
1199     }
1200 }
1201 
1202 static jobject getGlyphGeneralPath(JNIEnv* env, jobject font2D,
1203         FTScalerContext *context, FTScalerInfo *scalerInfo,
1204         jint glyphCode, jfloat xpos, jfloat ypos) {
1205 
1206     FT_Outline* outline;
1207     jobject gp = NULL;
1208     jbyteArray types;
1209     jfloatArray coords;
1210     GPData gpdata;
1211 
1212     outline = getFTOutline(env, font2D, context, scalerInfo,
1213                            glyphCode, xpos, ypos);
1214 
1215     if (outline == NULL || outline->n_points == 0) {
1216         return gp;
1217     }
1218 
1219     gpdata.pointTypes  = NULL;
1220     gpdata.pointCoords = NULL;
1221     if (!allocateSpaceForGP(&gpdata, outline->n_points, outline->n_contours)) {
1222         return gp;
1223     }
1224 
1225     addToGP(&gpdata, outline);
1226 
1227     types  = (*env)->NewByteArray(env, gpdata.numTypes);
1228     coords = (*env)->NewFloatArray(env, gpdata.numCoords);
1229 
1230     if (types && coords) {
1231         (*env)->SetByteArrayRegion(env, types, 0,
1232                                    gpdata.numTypes,
1233                                    gpdata.pointTypes);
1234         (*env)->SetFloatArrayRegion(env, coords, 0,
1235                                     gpdata.numCoords,
1236                                     gpdata.pointCoords);
1237         gp = (*env)->NewObject(env,
1238                                sunFontIDs.gpClass,
1239                                sunFontIDs.gpCtr,
1240                                gpdata.wr,
1241                                types,
1242                                gpdata.numTypes,
1243                                coords,
1244                                gpdata.numCoords);
1245     }
1246 
1247     freeGP(&gpdata);
1248 
1249     return gp;
1250 }
1251 
1252 /*
1253  * Class:     sun_font_FreetypeFontScaler
1254  * Method:    getGlyphOutlineNative
1255  * Signature: (Lsun/font/Font2D;JIFF)Ljava/awt/geom/GeneralPath;
1256  */
1257 JNIEXPORT jobject JNICALL
1258 Java_sun_font_FreetypeFontScaler_getGlyphOutlineNative(
1259       JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
1260       jlong pScaler, jint glyphCode, jfloat xpos, jfloat ypos) {
1261 
1262     FTScalerContext *context =
1263          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1264     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
1265 
1266     jobject gp = getGlyphGeneralPath(env,
1267                                font2D,
1268                                context,
1269                                scalerInfo,
1270                                glyphCode,
1271                                xpos,
1272                                ypos);
1273     if (gp == NULL) { /* can be legal */
1274         gp = (*env)->NewObject(env,
1275                                sunFontIDs.gpClass,
1276                                sunFontIDs.gpCtrEmpty);
1277     }
1278     return gp;
1279 }
1280 
1281 /*
1282  * Class:     sun_font_FreetypeFontScaler
1283  * Method:    getGlyphOutlineBoundsNative
1284  * Signature: (Lsun/font/Font2D;JI)Ljava/awt/geom/Rectangle2D/Float;
1285  */
1286 JNIEXPORT jobject JNICALL
1287 Java_sun_font_FreetypeFontScaler_getGlyphOutlineBoundsNative(
1288         JNIEnv *env, jobject scaler, jobject font2D,
1289         jlong pScalerContext, jlong pScaler, jint glyphCode) {
1290 
1291     FT_Outline *outline;
1292     FT_BBox bbox;
1293     int error;
1294     jobject bounds;
1295 
1296     FTScalerContext *context =
1297          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1298     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
1299 
1300     outline = getFTOutline(env, font2D, context, scalerInfo, glyphCode, 0, 0);
1301     if (outline == NULL || outline->n_points == 0) {
1302         /* it is legal case, e.g. invisible glyph */
1303         bounds = (*env)->NewObject(env,
1304                                  sunFontIDs.rect2DFloatClass,
1305                                  sunFontIDs.rect2DFloatCtr);
1306         return bounds;
1307     }
1308 
1309     error = FT_Outline_Get_BBox(outline, &bbox);
1310 
1311     //convert bbox
1312     if (error || bbox.xMin >= bbox.xMax || bbox.yMin >= bbox.yMax) {
1313         bounds = (*env)->NewObject(env,
1314                                    sunFontIDs.rect2DFloatClass,
1315                                    sunFontIDs.rect2DFloatCtr);
1316     } else {
1317         bounds = (*env)->NewObject(env,
1318                                    sunFontIDs.rect2DFloatClass,
1319                                    sunFontIDs.rect2DFloatCtr4,
1320                                    F26Dot6ToFloat(bbox.xMin),
1321                                    F26Dot6ToFloat(-bbox.yMax),
1322                                    F26Dot6ToFloat(bbox.xMax-bbox.xMin),
1323                                    F26Dot6ToFloat(bbox.yMax-bbox.yMin));
1324     }
1325 
1326     return bounds;
1327 }
1328 
1329 /*
1330  * Class:     sun_font_FreetypeFontScaler
1331  * Method:    getGlyphVectorOutlineNative
1332  * Signature: (Lsun/font/Font2D;J[IIFF)Ljava/awt/geom/GeneralPath;
1333  */
1334 JNIEXPORT jobject
1335 JNICALL
1336 Java_sun_font_FreetypeFontScaler_getGlyphVectorOutlineNative(
1337         JNIEnv *env, jobject scaler, jobject font2D,
1338         jlong pScalerContext, jlong pScaler,
1339         jintArray glyphArray, jint numGlyphs, jfloat xpos, jfloat ypos) {
1340 
1341     FT_Outline* outline;
1342     jobject gp = NULL;
1343     jbyteArray types;
1344     jfloatArray coords;
1345     GPData gpdata;
1346     int i;
1347     jint *glyphs;
1348 
1349     FTScalerContext *context =
1350          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1351     FTScalerInfo *scalerInfo =
1352              (FTScalerInfo*) jlong_to_ptr(pScaler);
1353 
1354     glyphs = (jint*) malloc(numGlyphs*sizeof(jint));
1355     if (glyphs == NULL) {
1356         gp = (*env)->NewObject(env, sunFontIDs.gpClass, sunFontIDs.gpCtrEmpty);
1357         if (!isNullScalerContext(context) && scalerInfo != NULL) {
1358             invalidateJavaScaler(env, scaler, scalerInfo);
1359         }
1360         return gp;
1361     }
1362 
1363     (*env)->GetIntArrayRegion(env, glyphArray, 0, numGlyphs, glyphs);
1364 
1365     for (i=0; i<numGlyphs;i++) {
1366         if (glyphs[i] >= INVISIBLE_GLYPHS) {
1367             continue;
1368         }
1369         outline = getFTOutline(env,
1370                                font2D,
1371                                context,
1372                                scalerInfo,
1373                                glyphs[i],
1374                                xpos, ypos);
1375 
1376         if (outline == NULL || outline->n_points == 0) {
1377             continue;
1378         }
1379 
1380         gpdata.pointTypes  = NULL;
1381         gpdata.pointCoords = NULL;
1382         if (!allocateSpaceForGP(&gpdata, outline->n_points,
1383                                 outline->n_contours)) {
1384             break;
1385         }
1386 
1387         addToGP(&gpdata, outline);
1388     }
1389     free(glyphs);
1390 
1391     if (gpdata.numCoords != 0) {
1392       types = (*env)->NewByteArray(env, gpdata.numTypes);
1393       coords = (*env)->NewFloatArray(env, gpdata.numCoords);
1394 
1395       if (types && coords) {
1396         (*env)->SetByteArrayRegion(env, types, 0,
1397                                    gpdata.numTypes, gpdata.pointTypes);
1398         (*env)->SetFloatArrayRegion(env, coords, 0,
1399                                     gpdata.numCoords, gpdata.pointCoords);
1400 
1401         gp=(*env)->NewObject(env,
1402                              sunFontIDs.gpClass,
1403                              sunFontIDs.gpCtr,
1404                              gpdata.wr,
1405                              types,
1406                              gpdata.numTypes,
1407                              coords,
1408                              gpdata.numCoords);
1409         return gp;
1410       }
1411     }
1412     return (*env)->NewObject(env, sunFontIDs.gpClass, sunFontIDs.gpCtrEmpty);
1413 }
1414 
1415 JNIEXPORT jlong JNICALL
1416 Java_sun_font_FreetypeFontScaler_getUnitsPerEMNative(
1417         JNIEnv *env, jobject scaler, jlong pScaler) {
1418 
1419     FTScalerInfo *s = (FTScalerInfo* ) jlong_to_ptr(pScaler);
1420 
1421     /* Freetype doc says:
1422      The number of font units per EM square for this face.
1423      This is typically 2048 for TrueType fonts, and 1000 for Type 1 fonts.
1424      Only relevant for scalable formats.
1425      However, layout engine might be not tested with anything but 2048.
1426 
1427      NB: test it! */
1428     if (s != NULL) {
1429         return s->face->units_per_EM;
1430     }
1431     return 2048;
1432 }
1433 
1434 /* This native method is called by the OpenType layout engine. */
1435 JNIEXPORT jobject JNICALL
1436 Java_sun_font_FreetypeFontScaler_getGlyphPointNative(
1437         JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
1438         jlong pScaler, jint glyphCode, jint pointNumber) {
1439 
1440     FT_Outline* outline;
1441     jobject point = NULL;
1442     jfloat x=0, y=0;
1443     FTScalerContext *context =
1444          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1445     FTScalerInfo *scalerInfo = (FTScalerInfo*) jlong_to_ptr(pScaler);
1446 
1447     outline = getFTOutline(env, font2D, context, scalerInfo, glyphCode, 0, 0);
1448 
1449     if (outline != NULL && outline->n_points > pointNumber) {
1450         x =  F26Dot6ToFloat(outline->points[pointNumber].x);
1451         y = -F26Dot6ToFloat(outline->points[pointNumber].y);
1452     }
1453 
1454     return (*env)->NewObject(env, sunFontIDs.pt2DFloatClass,
1455                              sunFontIDs.pt2DFloatCtr, x, y);
1456 }