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      (*env)->SetFloatField(env, metrics, sunFontIDs.xFID, info->advanceX);
 677      (*env)->SetFloatField(env, metrics, sunFontIDs.yFID, info->advanceY);
 678 
 679      free(info);
 680 }
 681 
 682 
 683 static GlyphInfo* getNullGlyphImage() {
 684     GlyphInfo *glyphInfo =  (GlyphInfo*) calloc(1, sizeof(GlyphInfo));
 685     return glyphInfo;
 686 }
 687 
 688 static void CopyBW2Grey8(const void* srcImage, int srcRowBytes,
 689                          void* dstImage, int dstRowBytes,
 690                          int width, int height) {
 691     const UInt8* srcRow = (UInt8*)srcImage;
 692     UInt8* dstRow = (UInt8*)dstImage;
 693     int wholeByteCount = width >> 3;
 694     int remainingBitsCount = width & 7;
 695     int i, j;
 696 
 697     while (height--) {
 698         const UInt8* src8 = srcRow;
 699         UInt8* dstByte = dstRow;
 700         unsigned srcValue;
 701 
 702         srcRow += srcRowBytes;
 703         dstRow += dstRowBytes;
 704 
 705         for (i = 0; i < wholeByteCount; i++) {
 706             srcValue = *src8++;
 707             for (j = 0; j < 8; j++) {
 708                 *dstByte++ = (srcValue & 0x80) ? 0xFF : 0;
 709                 srcValue <<= 1;
 710             }
 711         }
 712         if (remainingBitsCount) {
 713             srcValue = *src8;
 714             for (j = 0; j < remainingBitsCount; j++) {
 715                 *dstByte++ = (srcValue & 0x80) ? 0xFF : 0;
 716                 srcValue <<= 1;
 717             }
 718         }
 719     }
 720 }
 721 
 722 #define Grey4ToAlpha255(value) (((value) << 4) + ((value) >> 3))
 723 
 724 static void CopyGrey4ToGrey8(const void* srcImage, int srcRowBytes,
 725                 void* dstImage, int dstRowBytes, int width, int height) {
 726      const UInt8* srcRow = (UInt8*) srcImage;
 727      UInt8* dstRow = (UInt8*) dstImage;
 728      int i;
 729 
 730      while (height--) {
 731          const UInt8* src8 = srcRow;
 732          UInt8* dstByte = dstRow;
 733          unsigned srcValue;
 734 
 735          srcRow += srcRowBytes;
 736          dstRow += dstRowBytes;
 737 
 738          for (i = 0; i < width; i++) {
 739              srcValue = *src8++;
 740              *dstByte++ = Grey4ToAlpha255(srcValue & 0x0f);
 741              *dstByte++ = Grey4ToAlpha255(srcValue >> 4);
 742          }
 743      }
 744 }
 745 
 746 /* We need it because FT rows are often padded to 4 byte boundaries
 747     and our internal format is not padded */
 748 static void CopyFTSubpixelToSubpixel(const void* srcImage, int srcRowBytes,
 749                                      void* dstImage, int dstRowBytes,
 750                                      int width, int height) {
 751     unsigned char *srcRow = (unsigned char *) srcImage;
 752     unsigned char *dstRow = (unsigned char *) dstImage;
 753 
 754     while (height--) {
 755         memcpy(dstRow, srcRow, width);
 756         srcRow += srcRowBytes;
 757         dstRow += dstRowBytes;
 758     }
 759 }
 760 
 761 /* We need it because FT rows are often padded to 4 byte boundaries
 762    and our internal format is not padded */
 763 static void CopyFTSubpixelVToSubpixel(const void* srcImage, int srcRowBytes,
 764                                       void* dstImage, int dstRowBytes,
 765                                       int width, int height) {
 766     unsigned char *srcRow = (unsigned char *) srcImage, *srcByte;
 767     unsigned char *dstRow = (unsigned char *) dstImage, *dstByte;
 768     int i;
 769 
 770     while (height > 0) {
 771         srcByte = srcRow;
 772         dstByte = dstRow;
 773         for (i = 0; i < width; i++) {
 774             *dstByte++ = *srcByte;
 775             *dstByte++ = *(srcByte + srcRowBytes);
 776             *dstByte++ = *(srcByte + 2*srcRowBytes);
 777             srcByte++;
 778         }
 779         srcRow += 3*srcRowBytes;
 780         dstRow += dstRowBytes;
 781         height -= 3;
 782     }
 783 }
 784 
 785 
 786 /* JDK does not use glyph images for fonts with a
 787  * pixel size > 100 (see THRESHOLD in OutlineTextRenderer.java)
 788  * so if the glyph bitmap image dimension is > 1024 pixels,
 789  * something is up.
 790  */
 791 #define MAX_GLYPH_DIM 1024
 792 
 793 /*
 794  * Class:     sun_font_FreetypeFontScaler
 795  * Method:    getGlyphImageNative
 796  * Signature: (Lsun/font/Font2D;JI)J
 797  */
 798 JNIEXPORT jlong JNICALL
 799 Java_sun_font_FreetypeFontScaler_getGlyphImageNative(
 800         JNIEnv *env, jobject scaler, jobject font2D,
 801         jlong pScalerContext, jlong pScaler, jint glyphCode) {
 802 
 803     int error, imageSize;
 804     UInt16 width, height;
 805     GlyphInfo *glyphInfo;
 806     int renderFlags = FT_LOAD_DEFAULT, target;
 807     FT_GlyphSlot ftglyph;
 808 
 809     FTScalerContext* context =
 810         (FTScalerContext*) jlong_to_ptr(pScalerContext);
 811     FTScalerInfo *scalerInfo =
 812              (FTScalerInfo*) jlong_to_ptr(pScaler);
 813 
 814     if (isNullScalerContext(context) || scalerInfo == NULL) {
 815         return ptr_to_jlong(getNullGlyphImage());
 816     }
 817 
 818     error = setupFTContext(env, font2D, scalerInfo, context);
 819     if (error) {
 820         invalidateJavaScaler(env, scaler, scalerInfo);
 821         return ptr_to_jlong(getNullGlyphImage());
 822     }
 823 
 824     if (!context->useSbits) {
 825         renderFlags |= FT_LOAD_NO_BITMAP;
 826     }
 827 
 828     /* NB: in case of non identity transform
 829      we might also prefer to disable transform before hinting,
 830      and apply it explicitly after hinting is performed.
 831      Or we can disable hinting. */
 832 
 833     /* select appropriate hinting mode */
 834     if (context->aaType == TEXT_AA_OFF) {
 835         target = FT_LOAD_TARGET_MONO;
 836     } else if (context->aaType == TEXT_AA_ON) {
 837         target = FT_LOAD_TARGET_NORMAL;
 838     } else if (context->aaType == TEXT_AA_LCD_HRGB ||
 839                context->aaType == TEXT_AA_LCD_HBGR) {
 840         target = FT_LOAD_TARGET_LCD;
 841     } else {
 842         target = FT_LOAD_TARGET_LCD_V;
 843     }
 844     renderFlags |= target;
 845 
 846     error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderFlags);
 847     if (error) {
 848         //do not destroy scaler yet.
 849         //this can be problem of particular context (e.g. with bad transform)
 850         return ptr_to_jlong(getNullGlyphImage());
 851     }
 852 
 853     ftglyph = scalerInfo->face->glyph;
 854 
 855     /* apply styles */
 856     if (context->doBold) { /* if bold style */
 857         FT_GlyphSlot_Embolden(ftglyph);
 858     }
 859     if (context->doItalize) { /* if oblique */
 860         FT_GlyphSlot_Oblique(ftglyph);
 861     }
 862 
 863     /* generate bitmap if it is not done yet
 864      e.g. if algorithmic styling is performed and style was added to outline */
 865     if (ftglyph->format == FT_GLYPH_FORMAT_OUTLINE) {
 866         FT_BBox bbox;
 867         FT_Outline_Get_CBox(&(ftglyph->outline), &bbox);
 868         int w = (int)((bbox.xMax>>6)-(bbox.xMin>>6));
 869         int h = (int)((bbox.yMax>>6)-(bbox.yMin>>6));
 870         if (w > MAX_GLYPH_DIM || h > MAX_GLYPH_DIM) {
 871             glyphInfo = getNullGlyphImage();
 872             return ptr_to_jlong(glyphInfo);
 873         }
 874         error = FT_Render_Glyph(ftglyph, FT_LOAD_TARGET_MODE(target));
 875         if (error != 0) {
 876             return ptr_to_jlong(getNullGlyphImage());
 877         }
 878     }
 879 
 880     width  = (UInt16) ftglyph->bitmap.width;
 881     height = (UInt16) ftglyph->bitmap.rows;
 882     if (width > MAX_GLYPH_DIM || height > MAX_GLYPH_DIM) {
 883         glyphInfo = getNullGlyphImage();
 884         return ptr_to_jlong(glyphInfo);
 885     }
 886 
 887 
 888     imageSize = width*height;
 889     glyphInfo = (GlyphInfo*) malloc(sizeof(GlyphInfo) + imageSize);
 890     if (glyphInfo == NULL) {
 891         glyphInfo = getNullGlyphImage();
 892         return ptr_to_jlong(glyphInfo);
 893     }
 894     glyphInfo->cellInfo  = NULL;
 895     glyphInfo->managed   = UNMANAGED_GLYPH;
 896     glyphInfo->rowBytes  = width;
 897     glyphInfo->width     = width;
 898     glyphInfo->height    = height;
 899     glyphInfo->topLeftX  = (float)  ftglyph->bitmap_left;
 900     glyphInfo->topLeftY  = (float) -ftglyph->bitmap_top;
 901 
 902     if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD) {
 903         glyphInfo->width = width/3;
 904     } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD_V) {
 905         glyphInfo->height = glyphInfo->height/3;
 906     }
 907 
 908     if (context->fmType == TEXT_FM_ON) {
 909         double advh = FTFixedToFloat(ftglyph->linearHoriAdvance);
 910         glyphInfo->advanceX =
 911             (float) (advh * FTFixedToFloat(context->transform.xx));
 912         glyphInfo->advanceY =
 913             (float) (advh * FTFixedToFloat(context->transform.xy));
 914     } else {
 915         if (!ftglyph->advance.y) {
 916             glyphInfo->advanceX =
 917                 (float) FT26Dot6ToInt(ftglyph->advance.x);
 918             glyphInfo->advanceY = 0;
 919         } else if (!ftglyph->advance.x) {
 920             glyphInfo->advanceX = 0;
 921             glyphInfo->advanceY =
 922                 (float) FT26Dot6ToInt(-ftglyph->advance.y);
 923         } else {
 924             glyphInfo->advanceX = FT26Dot6ToFloat(ftglyph->advance.x);
 925             glyphInfo->advanceY = FT26Dot6ToFloat(-ftglyph->advance.y);
 926         }
 927     }
 928 
 929     if (imageSize == 0) {
 930         glyphInfo->image = NULL;
 931     } else {
 932         glyphInfo->image = (unsigned char*) glyphInfo + sizeof(GlyphInfo);
 933         //convert result to output format
 934         //output format is either 3 bytes per pixel (for subpixel modes)
 935         // or 1 byte per pixel for AA and B&W
 936         if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_MONO) {
 937             /* convert from 8 pixels per byte to 1 byte per pixel */
 938             CopyBW2Grey8(ftglyph->bitmap.buffer,
 939                          ftglyph->bitmap.pitch,
 940                          (void *) glyphInfo->image,
 941                          width,
 942                          width,
 943                          height);
 944         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_GRAY) {
 945             /* byte per pixel to byte per pixel => just copy */
 946             memcpy(glyphInfo->image, ftglyph->bitmap.buffer, imageSize);
 947         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_GRAY4) {
 948             /* 4 bits per pixel to byte per pixel */
 949             CopyGrey4ToGrey8(ftglyph->bitmap.buffer,
 950                              ftglyph->bitmap.pitch,
 951                              (void *) glyphInfo->image,
 952                              width,
 953                              width,
 954                              height);
 955         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD) {
 956             /* 3 bytes per pixel to 3 bytes per pixel */
 957             CopyFTSubpixelToSubpixel(ftglyph->bitmap.buffer,
 958                                      ftglyph->bitmap.pitch,
 959                                      (void *) glyphInfo->image,
 960                                      width,
 961                                      width,
 962                                      height);
 963         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD_V) {
 964             /* 3 bytes per pixel to 3 bytes per pixel */
 965             CopyFTSubpixelVToSubpixel(ftglyph->bitmap.buffer,
 966                                       ftglyph->bitmap.pitch,
 967                                       (void *) glyphInfo->image,
 968                                       width*3,
 969                                       width,
 970                                       height);
 971             glyphInfo->rowBytes *=3;
 972         } else {
 973             free(glyphInfo);
 974             glyphInfo = getNullGlyphImage();
 975         }
 976     }
 977 
 978     return ptr_to_jlong(glyphInfo);
 979 }
 980 
 981 /*
 982  * Class:     sun_font_FreetypeFontScaler
 983  * Method:    disposeNativeScaler
 984  * Signature: (J)V
 985  */
 986 JNIEXPORT void JNICALL
 987 Java_sun_font_FreetypeFontScaler_disposeNativeScaler(
 988         JNIEnv *env, jobject scaler, jobject font2D, jlong pScaler) {
 989     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
 990 
 991     /* Freetype functions *may* cause callback to java
 992        that can use cached values. Make sure our cache is up to date.
 993        NB: scaler context is not important at this point, can use NULL. */
 994     int errCode = setupFTContext(env, font2D, scalerInfo, NULL);
 995     if (errCode) {
 996         return;
 997     }
 998 
 999     freeNativeResources(env, scalerInfo);
1000 }
1001 
1002 /*
1003  * Class:     sun_font_FreetypeFontScaler
1004  * Method:    getNumGlyphsNative
1005  * Signature: ()I
1006  */
1007 JNIEXPORT jint JNICALL
1008 Java_sun_font_FreetypeFontScaler_getNumGlyphsNative(
1009         JNIEnv *env, jobject scaler, jlong pScaler) {
1010     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
1011 
1012     if (scalerInfo == NULL || scalerInfo->face == NULL) { /* bad/null scaler */
1013         /* null scaler can render 1 glyph - "missing glyph" with code 0
1014            (all glyph codes requested by user are mapped to code 0 at
1015            validation step) */
1016         invalidateJavaScaler(env, scaler, scalerInfo);
1017         return (jint) 1;
1018     }
1019 
1020     return (jint) scalerInfo->face->num_glyphs;
1021 }
1022 
1023 /*
1024  * Class:     sun_font_FreetypeFontScaler
1025  * Method:    getMissingGlyphCodeNative
1026  * Signature: ()I
1027  */
1028 JNIEXPORT jint JNICALL
1029 Java_sun_font_FreetypeFontScaler_getMissingGlyphCodeNative(
1030         JNIEnv *env, jobject scaler, jlong pScaler) {
1031 
1032     /* Is it always 0 for freetype? */
1033     return 0;
1034 }
1035 
1036 /*
1037  * Class:     sun_font_FreetypeFontScaler
1038  * Method:    getGlyphCodeNative
1039  * Signature: (C)I
1040  */
1041 JNIEXPORT jint JNICALL
1042 Java_sun_font_FreetypeFontScaler_getGlyphCodeNative(
1043         JNIEnv *env, jobject scaler,
1044         jobject font2D, jlong pScaler, jchar charCode) {
1045 
1046     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
1047     int errCode;
1048 
1049     if (scaler == NULL || scalerInfo->face == NULL) { /* bad/null scaler */
1050         invalidateJavaScaler(env, scaler, scalerInfo);
1051         return 0;
1052     }
1053 
1054     /* Freetype functions *may* cause callback to java
1055        that can use cached values. Make sure our cache is up to date.
1056        Scaler context is not important here, can use NULL. */
1057     errCode = setupFTContext(env, font2D, scalerInfo, NULL);
1058     if (errCode) {
1059         return 0;
1060     }
1061 
1062     return FT_Get_Char_Index(scalerInfo->face, charCode);
1063 }
1064 
1065 
1066 #define FloatToF26Dot6(x) ((unsigned int) ((x)*64))
1067 
1068 static FT_Outline* getFTOutline(JNIEnv* env, jobject font2D,
1069         FTScalerContext *context, FTScalerInfo* scalerInfo,
1070         jint glyphCode, jfloat xpos, jfloat ypos) {
1071     int renderFlags;
1072     FT_Error error;
1073     FT_GlyphSlot ftglyph;
1074 
1075     if (glyphCode >= INVISIBLE_GLYPHS ||
1076             isNullScalerContext(context) || scalerInfo == NULL) {
1077         return NULL;
1078     }
1079 
1080     error = setupFTContext(env, font2D, scalerInfo, context);
1081     if (error) {
1082         return NULL;
1083     }
1084 
1085     renderFlags = FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP;
1086 
1087     error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderFlags);
1088     if (error) {
1089         return NULL;
1090     }
1091 
1092     ftglyph = scalerInfo->face->glyph;
1093 
1094     /* apply styles */
1095     if (context->doBold) { /* if bold style */
1096         FT_GlyphSlot_Embolden(ftglyph);
1097     }
1098     if (context->doItalize) { /* if oblique */
1099         FT_GlyphSlot_Oblique(ftglyph);
1100     }
1101 
1102     FT_Outline_Translate(&ftglyph->outline,
1103                          FloatToF26Dot6(xpos),
1104                          -FloatToF26Dot6(ypos));
1105 
1106     return &ftglyph->outline;
1107 }
1108 
1109 #define F26Dot6ToFloat(n) (((float)(n))/((float) 64))
1110 
1111 /* Types of GeneralPath segments.
1112    TODO: pull constants from other place? */
1113 
1114 #define SEG_UNKNOWN -1
1115 #define SEG_MOVETO   0
1116 #define SEG_LINETO   1
1117 #define SEG_QUADTO   2
1118 #define SEG_CUBICTO  3
1119 #define SEG_CLOSE    4
1120 
1121 #define WIND_NON_ZERO 0
1122 #define WIND_EVEN_ODD 1
1123 
1124 /* Placeholder to accumulate GeneralPath data */
1125 typedef struct {
1126     jint numTypes;
1127     jint numCoords;
1128     jint lenTypes;
1129     jint lenCoords;
1130     jint wr;
1131     jbyte* pointTypes;
1132     jfloat* pointCoords;
1133 } GPData;
1134 
1135 /* returns 0 on failure */
1136 static int allocateSpaceForGP(GPData* gpdata, int npoints, int ncontours) {
1137     int maxTypes, maxCoords;
1138 
1139     /* we may have up to N intermediate points per contour
1140        (and for each point can actually cause new curve to be generated)
1141        In addition we can also have 2 extra point per outline.
1142      */
1143     maxTypes  = 2*npoints  + 2*ncontours;
1144     maxCoords = 4*(npoints + 2*ncontours); //we may need to insert
1145                                            //up to n-1 intermediate points
1146 
1147     /* first usage - allocate space and intialize all fields */
1148     if (gpdata->pointTypes == NULL || gpdata->pointCoords == NULL) {
1149         gpdata->lenTypes  = maxTypes;
1150         gpdata->lenCoords = maxCoords;
1151         gpdata->pointTypes  = (jbyte*)
1152              malloc(gpdata->lenTypes*sizeof(jbyte));
1153         gpdata->pointCoords = (jfloat*)
1154              malloc(gpdata->lenCoords*sizeof(jfloat));
1155         gpdata->numTypes = 0;
1156         gpdata->numCoords = 0;
1157         gpdata->wr = WIND_NON_ZERO; /* By default, outlines are filled
1158                                        using the non-zero winding rule. */
1159     } else {
1160         /* do we have enough space? */
1161         if (gpdata->lenTypes - gpdata->numTypes < maxTypes) {
1162             gpdata->lenTypes  += maxTypes;
1163             gpdata->pointTypes  = (jbyte*)
1164               realloc(gpdata->pointTypes, gpdata->lenTypes*sizeof(jbyte));
1165         }
1166 
1167         if (gpdata->lenCoords - gpdata->numCoords < maxCoords) {
1168             gpdata->lenCoords += maxCoords;
1169             gpdata->pointCoords = (jfloat*)
1170               realloc(gpdata->pointCoords, gpdata->lenCoords*sizeof(jfloat));
1171         }
1172     }
1173 
1174     /* failure if any of mallocs failed */
1175     if (gpdata->pointTypes == NULL ||  gpdata->pointCoords == NULL)
1176         return 0;
1177     else
1178         return 1;
1179 }
1180 
1181 static void addSeg(GPData *gp, jbyte type) {
1182     gp->pointTypes[gp->numTypes++] = type;
1183 }
1184 
1185 static void addCoords(GPData *gp, FT_Vector *p) {
1186     gp->pointCoords[gp->numCoords++] =  F26Dot6ToFloat(p->x);
1187     gp->pointCoords[gp->numCoords++] = -F26Dot6ToFloat(p->y);
1188 }
1189 
1190 static int moveTo(FT_Vector *to, GPData *gp) {
1191     if (gp->numCoords)
1192         addSeg(gp, SEG_CLOSE);
1193     addCoords(gp, to);
1194     addSeg(gp, SEG_MOVETO);
1195     return FT_Err_Ok;
1196 }
1197 
1198 static int lineTo(FT_Vector *to, GPData *gp) {
1199     addCoords(gp, to);
1200     addSeg(gp, SEG_LINETO);
1201     return FT_Err_Ok;
1202 }
1203 
1204 static int conicTo(FT_Vector *control, FT_Vector *to, GPData *gp) {
1205     addCoords(gp, control);
1206     addCoords(gp, to);
1207     addSeg(gp, SEG_QUADTO);
1208     return FT_Err_Ok;
1209 }
1210 
1211 static int cubicTo(FT_Vector *control1,
1212                    FT_Vector *control2,
1213                    FT_Vector *to,
1214                    GPData    *gp) {
1215     addCoords(gp, control1);
1216     addCoords(gp, control2);
1217     addCoords(gp, to);
1218     addSeg(gp, SEG_CUBICTO);
1219     return FT_Err_Ok;
1220 }
1221 
1222 static void addToGP(GPData* gpdata, FT_Outline*outline) {
1223     static const FT_Outline_Funcs outline_funcs = {
1224         (FT_Outline_MoveToFunc) moveTo,
1225         (FT_Outline_LineToFunc) lineTo,
1226         (FT_Outline_ConicToFunc) conicTo,
1227         (FT_Outline_CubicToFunc) cubicTo,
1228         0, /* shift */
1229         0, /* delta */
1230     };
1231 
1232     FT_Outline_Decompose(outline, &outline_funcs, gpdata);
1233     if (gpdata->numCoords)
1234         addSeg(gpdata, SEG_CLOSE);
1235 
1236     /* If set to 1, the outline will be filled using the even-odd fill rule */
1237     if (outline->flags & FT_OUTLINE_EVEN_ODD_FILL) {
1238         gpdata->wr = WIND_EVEN_ODD;
1239     }
1240 }
1241 
1242 static void freeGP(GPData* gpdata) {
1243     if (gpdata->pointCoords != NULL) {
1244         free(gpdata->pointCoords);
1245         gpdata->pointCoords = NULL;
1246         gpdata->numCoords = 0;
1247         gpdata->lenCoords = 0;
1248     }
1249     if (gpdata->pointTypes != NULL) {
1250         free(gpdata->pointTypes);
1251         gpdata->pointTypes = NULL;
1252         gpdata->numTypes = 0;
1253         gpdata->lenTypes = 0;
1254     }
1255 }
1256 
1257 static jobject getGlyphGeneralPath(JNIEnv* env, jobject font2D,
1258         FTScalerContext *context, FTScalerInfo *scalerInfo,
1259         jint glyphCode, jfloat xpos, jfloat ypos) {
1260 
1261     FT_Outline* outline;
1262     jobject gp = NULL;
1263     jbyteArray types;
1264     jfloatArray coords;
1265     GPData gpdata;
1266 
1267     outline = getFTOutline(env, font2D, context, scalerInfo,
1268                            glyphCode, xpos, ypos);
1269 
1270     if (outline == NULL || outline->n_points == 0) {
1271         return gp;
1272     }
1273 
1274     gpdata.pointTypes  = NULL;
1275     gpdata.pointCoords = NULL;
1276     if (!allocateSpaceForGP(&gpdata, outline->n_points, outline->n_contours)) {
1277         return gp;
1278     }
1279 
1280     addToGP(&gpdata, outline);
1281 
1282     types  = (*env)->NewByteArray(env, gpdata.numTypes);
1283     coords = (*env)->NewFloatArray(env, gpdata.numCoords);
1284 
1285     if (types && coords) {
1286         (*env)->SetByteArrayRegion(env, types, 0,
1287                                    gpdata.numTypes,
1288                                    gpdata.pointTypes);
1289         (*env)->SetFloatArrayRegion(env, coords, 0,
1290                                     gpdata.numCoords,
1291                                     gpdata.pointCoords);
1292         gp = (*env)->NewObject(env,
1293                                sunFontIDs.gpClass,
1294                                sunFontIDs.gpCtr,
1295                                gpdata.wr,
1296                                types,
1297                                gpdata.numTypes,
1298                                coords,
1299                                gpdata.numCoords);
1300     }
1301 
1302     freeGP(&gpdata);
1303 
1304     return gp;
1305 }
1306 
1307 /*
1308  * Class:     sun_font_FreetypeFontScaler
1309  * Method:    getGlyphOutlineNative
1310  * Signature: (Lsun/font/Font2D;JIFF)Ljava/awt/geom/GeneralPath;
1311  */
1312 JNIEXPORT jobject JNICALL
1313 Java_sun_font_FreetypeFontScaler_getGlyphOutlineNative(
1314       JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
1315       jlong pScaler, jint glyphCode, jfloat xpos, jfloat ypos) {
1316 
1317     FTScalerContext *context =
1318          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1319     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
1320 
1321     jobject gp = getGlyphGeneralPath(env,
1322                                font2D,
1323                                context,
1324                                scalerInfo,
1325                                glyphCode,
1326                                xpos,
1327                                ypos);
1328     if (gp == NULL) { /* can be legal */
1329         gp = (*env)->NewObject(env,
1330                                sunFontIDs.gpClass,
1331                                sunFontIDs.gpCtrEmpty);
1332     }
1333     return gp;
1334 }
1335 
1336 /*
1337  * Class:     sun_font_FreetypeFontScaler
1338  * Method:    getGlyphOutlineBoundsNative
1339  * Signature: (Lsun/font/Font2D;JI)Ljava/awt/geom/Rectangle2D/Float;
1340  */
1341 JNIEXPORT jobject JNICALL
1342 Java_sun_font_FreetypeFontScaler_getGlyphOutlineBoundsNative(
1343         JNIEnv *env, jobject scaler, jobject font2D,
1344         jlong pScalerContext, jlong pScaler, jint glyphCode) {
1345 
1346     FT_Outline *outline;
1347     FT_BBox bbox;
1348     int error;
1349     jobject bounds;
1350 
1351     FTScalerContext *context =
1352          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1353     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
1354 
1355     outline = getFTOutline(env, font2D, context, scalerInfo, glyphCode, 0, 0);
1356     if (outline == NULL || outline->n_points == 0) {
1357         /* it is legal case, e.g. invisible glyph */
1358         bounds = (*env)->NewObject(env,
1359                                  sunFontIDs.rect2DFloatClass,
1360                                  sunFontIDs.rect2DFloatCtr);
1361         return bounds;
1362     }
1363 
1364     error = FT_Outline_Get_BBox(outline, &bbox);
1365 
1366     //convert bbox
1367     if (error || bbox.xMin >= bbox.xMax || bbox.yMin >= bbox.yMax) {
1368         bounds = (*env)->NewObject(env,
1369                                    sunFontIDs.rect2DFloatClass,
1370                                    sunFontIDs.rect2DFloatCtr);
1371     } else {
1372         bounds = (*env)->NewObject(env,
1373                                    sunFontIDs.rect2DFloatClass,
1374                                    sunFontIDs.rect2DFloatCtr4,
1375                                    F26Dot6ToFloat(bbox.xMin),
1376                                    F26Dot6ToFloat(-bbox.yMax),
1377                                    F26Dot6ToFloat(bbox.xMax-bbox.xMin),
1378                                    F26Dot6ToFloat(bbox.yMax-bbox.yMin));
1379     }
1380 
1381     return bounds;
1382 }
1383 
1384 /*
1385  * Class:     sun_font_FreetypeFontScaler
1386  * Method:    getGlyphVectorOutlineNative
1387  * Signature: (Lsun/font/Font2D;J[IIFF)Ljava/awt/geom/GeneralPath;
1388  */
1389 JNIEXPORT jobject
1390 JNICALL
1391 Java_sun_font_FreetypeFontScaler_getGlyphVectorOutlineNative(
1392         JNIEnv *env, jobject scaler, jobject font2D,
1393         jlong pScalerContext, jlong pScaler,
1394         jintArray glyphArray, jint numGlyphs, jfloat xpos, jfloat ypos) {
1395 
1396     FT_Outline* outline;
1397     jobject gp = NULL;
1398     jbyteArray types;
1399     jfloatArray coords;
1400     GPData gpdata;
1401     int i;
1402     jint *glyphs;
1403 
1404     FTScalerContext *context =
1405          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1406     FTScalerInfo *scalerInfo =
1407              (FTScalerInfo*) jlong_to_ptr(pScaler);
1408 
1409     glyphs = NULL;
1410     if (numGlyphs > 0 && 0xffffffffu / sizeof(jint) >= numGlyphs) {
1411         glyphs = (jint*) malloc(numGlyphs*sizeof(jint));
1412     }
1413     if (glyphs == NULL) {
1414         // We reach here if:
1415         // 1. numGlyphs <= 0,
1416         // 2. overflow check failed, or
1417         // 3. malloc failed.
1418         gp = (*env)->NewObject(env, sunFontIDs.gpClass, sunFontIDs.gpCtrEmpty);
1419         return gp;
1420     }
1421 
1422     (*env)->GetIntArrayRegion(env, glyphArray, 0, numGlyphs, glyphs);
1423 
1424     gpdata.numCoords = 0;
1425     for (i=0; i<numGlyphs;i++) {
1426         if (glyphs[i] >= INVISIBLE_GLYPHS) {
1427             continue;
1428         }
1429         outline = getFTOutline(env,
1430                                font2D,
1431                                context,
1432                                scalerInfo,
1433                                glyphs[i],
1434                                xpos, ypos);
1435 
1436         if (outline == NULL || outline->n_points == 0) {
1437             continue;
1438         }
1439 
1440         gpdata.pointTypes  = NULL;
1441         gpdata.pointCoords = NULL;
1442         if (!allocateSpaceForGP(&gpdata, outline->n_points,
1443                                 outline->n_contours)) {
1444             break;
1445         }
1446 
1447         addToGP(&gpdata, outline);
1448     }
1449     free(glyphs);
1450 
1451     if (gpdata.numCoords != 0) {
1452       types = (*env)->NewByteArray(env, gpdata.numTypes);
1453       coords = (*env)->NewFloatArray(env, gpdata.numCoords);
1454 
1455       if (types && coords) {
1456         (*env)->SetByteArrayRegion(env, types, 0,
1457                                    gpdata.numTypes, gpdata.pointTypes);
1458         (*env)->SetFloatArrayRegion(env, coords, 0,
1459                                     gpdata.numCoords, gpdata.pointCoords);
1460 
1461         gp=(*env)->NewObject(env,
1462                              sunFontIDs.gpClass,
1463                              sunFontIDs.gpCtr,
1464                              gpdata.wr,
1465                              types,
1466                              gpdata.numTypes,
1467                              coords,
1468                              gpdata.numCoords);
1469         return gp;
1470       }
1471     }
1472     return (*env)->NewObject(env, sunFontIDs.gpClass, sunFontIDs.gpCtrEmpty);
1473 }
1474 
1475 JNIEXPORT jlong JNICALL
1476 Java_sun_font_FreetypeFontScaler_getUnitsPerEMNative(
1477         JNIEnv *env, jobject scaler, jlong pScaler) {
1478 
1479     FTScalerInfo *s = (FTScalerInfo* ) jlong_to_ptr(pScaler);
1480 
1481     /* Freetype doc says:
1482      The number of font units per EM square for this face.
1483      This is typically 2048 for TrueType fonts, and 1000 for Type 1 fonts.
1484      Only relevant for scalable formats.
1485      However, layout engine might be not tested with anything but 2048.
1486 
1487      NB: test it! */
1488     if (s != NULL) {
1489         return s->face->units_per_EM;
1490     }
1491     return 2048;
1492 }
1493 
1494 /* This native method is called by the OpenType layout engine. */
1495 JNIEXPORT jobject JNICALL
1496 Java_sun_font_FreetypeFontScaler_getGlyphPointNative(
1497         JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
1498         jlong pScaler, jint glyphCode, jint pointNumber) {
1499 
1500     FT_Outline* outline;
1501     jobject point = NULL;
1502     jfloat x=0, y=0;
1503     FTScalerContext *context =
1504          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1505     FTScalerInfo *scalerInfo = (FTScalerInfo*) jlong_to_ptr(pScaler);
1506 
1507     outline = getFTOutline(env, font2D, context, scalerInfo, glyphCode, 0, 0);
1508 
1509     if (outline != NULL && outline->n_points > pointNumber) {
1510         x =  F26Dot6ToFloat(outline->points[pointNumber].x);
1511         y = -F26Dot6ToFloat(outline->points[pointNumber].y);
1512     }
1513 
1514     return (*env)->NewObject(env, sunFontIDs.pt2DFloatClass,
1515                              sunFontIDs.pt2DFloatCtr, x, y);
1516 }