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