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