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