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