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  ROUND(x) ((int) (x+0.5))
  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     jobject font2D;
  64     jobject directBuffer;
  65 
  66     unsigned char* fontData;
  67     unsigned fontDataOffset;
  68     unsigned fontDataLength;
  69     unsigned fileSize;
  70     TTLayoutTableCache* layoutTables;
  71 } FTScalerInfo;
  72 
  73 typedef struct FTScalerContext {
  74     FT_Matrix  transform;     /* glyph transform, including device transform */
  75     jboolean   useSbits;      /* sbit usage enabled? */
  76     jint       aaType;        /* antialiasing mode (off/on/grey/lcd) */
  77     jint       fmType;        /* fractional metrics - on/off */
  78     jboolean   doBold;        /* perform algorithmic bolding? */
  79     jboolean   doItalize;     /* perform algorithmic italicizing? */
  80     int        renderFlags;   /* configuration specific to particular engine */
  81     int        pathType;
  82     int        ptsz;          /* size in points */
  83 } FTScalerContext;
  84 
  85 #ifdef DEBUG
  86 /* These are referenced in the freetype sources if DEBUG macro is defined.
  87    To simplify work with debuging version of freetype we define
  88    them here. */
  89 int z_verbose;
  90 void z_error(char *s) {}
  91 #endif
  92 
  93 /**************** Error handling utilities *****************/
  94 
  95 static jmethodID invalidateScalerMID;
  96 
  97 JNIEXPORT void JNICALL
  98 Java_sun_font_FreetypeFontScaler_initIDs(
  99         JNIEnv *env, jobject scaler, jclass FFSClass) {
 100     invalidateScalerMID =
 101         (*env)->GetMethodID(env, FFSClass, "invalidateScaler", "()V");
 102 }
 103 
 104 static void freeNativeResources(JNIEnv *env, FTScalerInfo* scalerInfo) {
 105     void *stream;
 106 
 107     if (scalerInfo == NULL)
 108         return;
 109 
 110     //apparently Done_Face will only close the stream
 111     // but will not relase the memory of stream structure.
 112     // We need to free it explicitly to avoid leak.
 113     //Direct access to the stream field might be not ideal solution as
 114     // it is considred to be "private".
 115     //Alternatively we could have stored pointer to the structure
 116     // in the scalerInfo but this will increase size of the structure
 117     // for no good reason
 118     stream = scalerInfo->face->stream;
 119 
 120     FT_Done_Face(scalerInfo->face);
 121     FT_Done_FreeType(scalerInfo->library);
 122 
 123     if (scalerInfo->directBuffer != NULL) {
 124         (*env)->DeleteGlobalRef(env, scalerInfo->directBuffer);
 125     }
 126 
 127     if (scalerInfo->fontData != NULL) {
 128         free(scalerInfo->fontData);
 129     }
 130 
 131    if (stream != NULL) {
 132         free(stream);
 133    }
 134 
 135     free(scalerInfo);
 136 }
 137 
 138 /* invalidates state of java scaler object */
 139 static void invalidateJavaScaler(JNIEnv *env,
 140                                  jobject scaler,
 141                                  FTScalerInfo* scalerInfo) {
 142     freeNativeResources(env, scalerInfo);
 143     (*env)->CallVoidMethod(env, scaler, invalidateScalerMID);
 144 }
 145 
 146 /******************* I/O handlers ***************************/
 147 
 148 #define FILEDATACACHESIZE 1024
 149 
 150 /* NB: is it ever called? */
 151 static void CloseTTFontFileFunc(FT_Stream stream) {
 152     FTScalerInfo *scalerInfo = (FTScalerInfo *) stream->pathname.pointer;
 153     JNIEnv* env = scalerInfo->env;
 154     jclass tmpClass = (*env)->FindClass(env, "sun/font/TrueTypeFont");
 155     jfieldID platNameField =
 156          (*env)->GetFieldID(env, tmpClass, "platName", "Ljava/lang/String;");
 157     jstring platName = (*env)->GetObjectField(env,
 158                                               scalerInfo->font2D,
 159                                               platNameField);
 160     const char *name = JNU_GetStringPlatformChars(env, platName, NULL);
 161     JNU_ReleaseStringPlatformChars(env, platName, name);
 162 }
 163 
 164 static unsigned long ReadTTFontFileFunc(FT_Stream stream,
 165                                         unsigned long offset,
 166                                         unsigned char* destBuffer,
 167                                         unsigned long numBytes)
 168 {
 169     FTScalerInfo *scalerInfo = (FTScalerInfo *) stream->pathname.pointer;
 170     JNIEnv* env = scalerInfo->env;
 171     jobject bBuffer;
 172     int bread = 0;
 173 
 174     if (numBytes == 0) return 0;
 175 
 176     /* Large reads will bypass the cache and data copying */
 177     if (numBytes > FILEDATACACHESIZE) {
 178         bBuffer = (*env)->NewDirectByteBuffer(env, destBuffer, numBytes);
 179         if (bBuffer != NULL) {
 180             /* Loop until the read succeeds (or EOF).
 181              * This should improve robustness in the event of a problem in
 182              * the I/O system. If we find that we ever end up spinning here
 183              * we are going to have to do some serious work to recover.
 184              * Just returning without reading the data will cause a crash.
 185              */
 186             while (bread == 0) {
 187                 bread = (*env)->CallIntMethod(env,
 188                                               scalerInfo->font2D,
 189                                               sunFontIDs.ttReadBlockMID,
 190                                               bBuffer, offset, numBytes);
 191             }
 192             return bread;
 193         } else {
 194             /* We probably hit bug bug 4845371. For reasons that
 195              * are currently unclear, the call stacks after the initial
 196              * createScaler call that read large amounts of data seem to
 197              * be OK and can create the byte buffer above, but this code
 198              * is here just in case.
 199              * 4845371 is fixed now so I don't expect this code path to
 200              * ever get called but its harmless to leave it here on the
 201              * small chance its needed.
 202              */
 203             jbyteArray byteArray = (jbyteArray)
 204             (*env)->CallObjectMethod(env, scalerInfo->font2D,
 205                                      sunFontIDs.ttReadBytesMID,
 206                                      offset, numBytes);
 207             (*env)->GetByteArrayRegion(env, byteArray,
 208                                        0, numBytes, (jbyte*)destBuffer);
 209             return numBytes;
 210         }
 211     } /* Do we have a cache hit? */
 212       else if (scalerInfo->fontDataOffset <= offset &&
 213         scalerInfo->fontDataOffset + scalerInfo->fontDataLength >=
 214                                                          offset + numBytes)
 215     {
 216         unsigned cacheOffset = offset - scalerInfo->fontDataOffset;
 217 
 218         memcpy(destBuffer, scalerInfo->fontData+(size_t)cacheOffset, numBytes);
 219         return numBytes;
 220     } else {
 221         /* Must fill the cache */
 222         scalerInfo->fontDataOffset = offset;
 223         scalerInfo->fontDataLength =
 224                  (offset + FILEDATACACHESIZE > scalerInfo->fileSize) ?
 225                  scalerInfo->fileSize - offset : FILEDATACACHESIZE;
 226         bBuffer = scalerInfo->directBuffer;
 227         /* Loop until all the read succeeds (or EOF).
 228          * This should improve robustness in the event of a problem in
 229          * the I/O system. If we find that we ever end up spinning here
 230          * we are going to have to do some serious work to recover.
 231          * Just returning without reading the data will cause a crash.
 232          */
 233         while (bread == 0) {
 234             bread = (*env)->CallIntMethod(env, scalerInfo->font2D,
 235                                           sunFontIDs.ttReadBlockMID,
 236                                           bBuffer, offset,
 237                                           scalerInfo->fontDataLength);
 238         }
 239 
 240         memcpy(destBuffer, scalerInfo->fontData, numBytes);
 241         return numBytes;
 242     }
 243 }
 244 
 245 /*
 246  * Class:     sun_font_FreetypeFontScaler
 247  * Method:    initNativeScaler
 248  * Signature: (Lsun/font/Font2D;IIZI)J
 249  */
 250 JNIEXPORT jlong JNICALL
 251 Java_sun_font_FreetypeFontScaler_initNativeScaler(
 252         JNIEnv *env, jobject scaler, jobject font2D, jint type,
 253         jint indexInCollection, jboolean supportsCJK, jint filesize) {
 254     FTScalerInfo* scalerInfo = NULL;
 255     FT_Open_Args ft_open_args;
 256     int error;
 257     jobject bBuffer;
 258     scalerInfo = (FTScalerInfo*) calloc(1, sizeof(FTScalerInfo));
 259 
 260     if (scalerInfo == NULL)
 261         return 0;
 262 
 263     scalerInfo->env = env;
 264     scalerInfo->font2D = font2D;
 265     scalerInfo->fontDataOffset = 0;
 266     scalerInfo->fontDataLength = 0;
 267     scalerInfo->fileSize = filesize;
 268 
 269     /*
 270        We can consider sharing freetype library between different
 271        scalers. However, Freetype docs suggest to use different libraries
 272        for different threads. Also, our architecture implies that single
 273        FontScaler object is shared for for different sizes/transforms/styles
 274        of the same font.
 275 
 276        On other hand these methods can not be concurrently executed
 277        becaused they are "synchronized" in java.
 278     */
 279     error = FT_Init_FreeType(&scalerInfo->library);
 280     if (error) {
 281         free(scalerInfo);
 282         return 0;
 283     }
 284 
 285 #define TYPE1_FROM_JAVA        2
 286 
 287     error = 1; /* triggers memory freeing unless we clear it */
 288     if (type == TYPE1_FROM_JAVA) { /* TYPE1 */
 289         scalerInfo->fontData = (unsigned char*) malloc(filesize);
 290         scalerInfo->directBuffer = NULL;
 291         scalerInfo->layoutTables = NULL;
 292         scalerInfo->fontDataLength = filesize;
 293 
 294         if (scalerInfo->fontData != NULL) {
 295             bBuffer = (*env)->NewDirectByteBuffer(env,
 296                                               scalerInfo->fontData,
 297                                               scalerInfo->fontDataLength);
 298             if (bBuffer != NULL) {
 299                 (*env)->CallObjectMethod(env, font2D,
 300                                    sunFontIDs.readFileMID, bBuffer);
 301 
 302                 error = FT_New_Memory_Face(scalerInfo->library,
 303                                    scalerInfo->fontData,
 304                                    scalerInfo->fontDataLength,
 305                                    indexInCollection,
 306                                    &scalerInfo->face);
 307             }
 308         }
 309     } else { /* Truetype */
 310         scalerInfo->fontData = (unsigned char*) malloc(FILEDATACACHESIZE);
 311 
 312         if (scalerInfo->fontData != NULL) {
 313             FT_Stream ftstream = (FT_Stream) calloc(1, sizeof(FT_StreamRec));
 314             if (ftstream != NULL) {
 315                 scalerInfo->directBuffer = (*env)->NewDirectByteBuffer(env,
 316                                            scalerInfo->fontData,
 317                                            FILEDATACACHESIZE);
 318                 if (scalerInfo->directBuffer != NULL) {
 319                     scalerInfo->directBuffer = (*env)->NewGlobalRef(env,
 320                                                scalerInfo->directBuffer);
 321                     ftstream->base = NULL;
 322                     ftstream->size = filesize;
 323                     ftstream->pos = 0;
 324                     ftstream->read = (FT_Stream_IoFunc) ReadTTFontFileFunc;
 325                     ftstream->close = (FT_Stream_CloseFunc) CloseTTFontFileFunc;
 326                     ftstream->pathname.pointer = (void *) scalerInfo;
 327 
 328                     memset(&ft_open_args, 0, sizeof(FT_Open_Args));
 329                     ft_open_args.flags = FT_OPEN_STREAM;
 330                     ft_open_args.stream = ftstream;
 331 
 332                     error = FT_Open_Face(scalerInfo->library,
 333                                          &ft_open_args,
 334                                          indexInCollection,
 335                                          &scalerInfo->face);
 336                 }
 337                 if (error || scalerInfo->directBuffer == NULL) {
 338                     free(ftstream);
 339                 }
 340             }
 341         }
 342     }
 343 
 344     if (error) {
 345         FT_Done_FreeType(scalerInfo->library);
 346         if (scalerInfo->directBuffer != NULL) {
 347             (*env)->DeleteGlobalRef(env, scalerInfo->directBuffer);
 348         }
 349         if (scalerInfo->fontData != NULL)
 350             free(scalerInfo->fontData);
 351         free(scalerInfo);
 352         return 0;
 353     }
 354 
 355     return ptr_to_jlong(scalerInfo);
 356 }
 357 
 358 static double euclidianDistance(double a, double b) {
 359     if (a < 0) a=-a;
 360     if (b < 0) b=-b;
 361 
 362     if (a == 0) return b;
 363     if (b == 0) return a;
 364 
 365     return sqrt(a*a+b*b);
 366 }
 367 
 368 JNIEXPORT jlong JNICALL
 369 Java_sun_font_FreetypeFontScaler_createScalerContextNative(
 370         JNIEnv *env, jobject scaler, jlong pScaler, jdoubleArray matrix,
 371         jint aa, jint fm, jfloat boldness, jfloat italic) {
 372     double dmat[4], ptsz;
 373     FTScalerContext *context =
 374             (FTScalerContext*) calloc(1, sizeof(FTScalerContext));
 375     FTScalerInfo *scalerInfo =
 376              (FTScalerInfo*) jlong_to_ptr(pScaler);
 377 
 378     if (context == NULL) {
 379         invalidateJavaScaler(env, scaler, NULL);
 380         return (jlong) 0;
 381     }
 382     (*env)->GetDoubleArrayRegion(env, matrix, 0, 4, dmat);
 383     ptsz = euclidianDistance(dmat[2], dmat[3]); //i.e. y-size
 384     if (ptsz < 1.0) {
 385         //text can not be smaller than 1 point
 386         ptsz = 1.0;
 387     }
 388     context->ptsz = (int)(ptsz * 64);
 389     context->transform.xx =  FloatToFTFixed((float)dmat[0]/ptsz);
 390     context->transform.yx = -FloatToFTFixed((float)dmat[1]/ptsz);
 391     context->transform.xy = -FloatToFTFixed((float)dmat[2]/ptsz);
 392     context->transform.yy =  FloatToFTFixed((float)dmat[3]/ptsz);
 393     context->aaType = aa;
 394     context->fmType = fm;
 395 
 396     /* If using algorithmic styling, the base values are
 397      * boldness = 1.0, italic = 0.0.
 398      */
 399     context->doBold = (boldness != 1.0);
 400     context->doItalize = (italic != 0);
 401 
 402     return ptr_to_jlong(context);
 403 }
 404 
 405 static int setupFTContext(JNIEnv *env,
 406                           jobject font2D,
 407                           FTScalerInfo *scalerInfo,
 408                           FTScalerContext *context) {
 409     int errCode = 0;
 410 
 411     scalerInfo->env = env;
 412     scalerInfo->font2D = font2D;
 413 
 414     if (context != NULL) {
 415         FT_Set_Transform(scalerInfo->face, &context->transform, NULL);
 416 
 417         errCode = FT_Set_Char_Size(scalerInfo->face, 0, context->ptsz, 72, 72);
 418 
 419         if (errCode == 0) {
 420             errCode = FT_Activate_Size(scalerInfo->face->size);
 421         }
 422     }
 423 
 424     return errCode;
 425 }
 426 
 427 /* ftsynth.c uses (0x10000, 0x06000, 0x0, 0x10000) matrix to get oblique
 428    outline.  Therefore x coordinate will change by 0x06000*y.
 429    Note that y coordinate does not change. */
 430 #define OBLIQUE_MODIFIER(y)  (context->doItalize ? ((y)*6/16) : 0)
 431 
 432 /*
 433  * Class:     sun_font_FreetypeFontScaler
 434  * Method:    getFontMetricsNative
 435  * Signature: (Lsun/font/Font2D;J)Lsun/font/StrikeMetrics;
 436  */
 437 JNIEXPORT jobject JNICALL
 438 Java_sun_font_FreetypeFontScaler_getFontMetricsNative(
 439         JNIEnv *env, jobject scaler, jobject font2D,
 440         jlong pScalerContext, jlong pScaler) {
 441 
 442     jobject metrics;
 443     jfloat ax, ay, dx, dy, bx, by, lx, ly, mx, my;
 444     jfloat f0 = 0.0;

 445     FTScalerContext *context =
 446         (FTScalerContext*) jlong_to_ptr(pScalerContext);
 447     FTScalerInfo *scalerInfo =
 448              (FTScalerInfo*) jlong_to_ptr(pScaler);
 449 
 450     int errCode;
 451 
 452     if (isNullScalerContext(context) || scalerInfo == NULL) {
 453         return (*env)->NewObject(env,
 454                                  sunFontIDs.strikeMetricsClass,
 455                                  sunFontIDs.strikeMetricsCtr,
 456                                  f0, f0, f0, f0, f0, f0, f0, f0, f0, f0);
 457     }
 458 
 459     errCode = setupFTContext(env, font2D, scalerInfo, context);
 460 
 461     if (errCode) {
 462         metrics = (*env)->NewObject(env,
 463                                  sunFontIDs.strikeMetricsClass,
 464                                  sunFontIDs.strikeMetricsCtr,
 465                                  f0, f0, f0, f0, f0, f0, f0, f0, f0, f0);
 466         invalidateJavaScaler(env, scaler, scalerInfo);
 467         return metrics;
 468     }
 469 
 470     /* This is ugly and has to be reworked.
 471        Freetype provide means to add style to glyph but
 472        it seems there is no way to adjust metrics accordingly.
 473 
 474        So, we have to do adust them explicitly and stay consistent with what
 475        freetype does to outlines. */
 476 











 477 
 478     /**** Note: only some metrics are affected by styling ***/
 479 
 480     /* See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=657854 */
 481 #define FT_MulFixFloatShift6(a, b) (((float) (a)) * ((float) (b)) / 65536.0 / 64.0)
 482 
 483     /*
 484      * See FreeType source code: src/base/ftobjs.c ft_recompute_scaled_metrics()
 485      * http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=1659
 486      */
 487     /* ascent */
 488     ax = 0;
 489     ay = -(jfloat) (FT_MulFixFloatShift6(
 490                        ((jlong) scalerInfo->face->ascender),
 491                        (jlong) scalerInfo->face->size->metrics.y_scale));
 492     /* descent */
 493     dx = 0;
 494     dy = -(jfloat) (FT_MulFixFloatShift6(
 495                        ((jlong) scalerInfo->face->descender),
 496                        (jlong) scalerInfo->face->size->metrics.y_scale));
 497     /* baseline */
 498     bx = by = 0;
 499 
 500     /* leading */
 501     lx = 0;
 502     ly = (jfloat) (FT_MulFixFloatShift6(
 503                       (jlong) scalerInfo->face->height,
 504                       (jlong) scalerInfo->face->size->metrics.y_scale))
 505                   + ay - dy;
 506     /* max advance */
 507     mx = (jfloat) FT26Dot6ToFloat(
 508                      scalerInfo->face->size->metrics.max_advance +

 509                      OBLIQUE_MODIFIER(scalerInfo->face->size->metrics.height));
 510     my = 0;
 511 
 512     metrics = (*env)->NewObject(env,
 513                                 sunFontIDs.strikeMetricsClass,
 514                                 sunFontIDs.strikeMetricsCtr,
 515                                 ax, ay, dx, dy, bx, by, lx, ly, mx, my);
 516 
 517     return metrics;
 518 }
 519 
 520 /*
 521  * Class:     sun_font_FreetypeFontScaler
 522  * Method:    getGlyphAdvanceNative
 523  * Signature: (Lsun/font/Font2D;JI)F
 524  */
 525 JNIEXPORT jfloat JNICALL
 526 Java_sun_font_FreetypeFontScaler_getGlyphAdvanceNative(
 527         JNIEnv *env, jobject scaler, jobject font2D,
 528         jlong pScalerContext, jlong pScaler, jint glyphCode) {
 529 
 530    /* This method is rarely used because requests for metrics are usually
 531       coupled with request for bitmap and to large extend work can be reused
 532       (to find out metrics we need to hint glyph).
 533       So, we typically go through getGlyphImage code path.
 534 
 535       For initial freetype implementation we delegate
 536       all work to getGlyphImage but drop result image.
 537       This is waste of work related to scan conversion and conversion from
 538       freetype format to our format but for now this seems to be ok.
 539 
 540       NB: investigate performance benefits of refactoring code
 541       to avoid unnecesary work with bitmaps. */
 542 
 543     GlyphInfo *info;
 544     jfloat advance;
 545     jlong image;
 546 
 547     image = Java_sun_font_FreetypeFontScaler_getGlyphImageNative(
 548                  env, scaler, font2D, pScalerContext, pScaler, glyphCode);
 549     info = (GlyphInfo*) jlong_to_ptr(image);
 550 
 551     advance = info->advanceX;
 552 
 553     free(info);
 554 
 555     return advance;
 556 }
 557 
 558 /*
 559  * Class:     sun_font_FreetypeFontScaler
 560  * Method:    getGlyphMetricsNative
 561  * Signature: (Lsun/font/Font2D;JILjava/awt/geom/Point2D/Float;)V
 562  */
 563 JNIEXPORT void JNICALL
 564 Java_sun_font_FreetypeFontScaler_getGlyphMetricsNative(
 565         JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
 566         jlong pScaler, jint glyphCode, jobject metrics) {
 567 
 568      /* As initial implementation we delegate all work to getGlyphImage
 569         but drop result image. This is clearly waste of resorces.
 570 
 571         TODO: investigate performance benefits of refactoring code
 572               by avoiding bitmap generation and conversion from FT
 573               bitmap format. */
 574      GlyphInfo *info;
 575 
 576      jlong image = Java_sun_font_FreetypeFontScaler_getGlyphImageNative(
 577                                  env, scaler, font2D,
 578                                  pScalerContext, pScaler, glyphCode);
 579      info = (GlyphInfo*) jlong_to_ptr(image);
 580 
 581      (*env)->SetFloatField(env, metrics, sunFontIDs.xFID, info->advanceX);
 582      (*env)->SetFloatField(env, metrics, sunFontIDs.yFID, info->advanceY);
 583 
 584      free(info);
 585 }
 586 
 587 
 588 static GlyphInfo* getNullGlyphImage() {
 589     GlyphInfo *glyphInfo =  (GlyphInfo*) calloc(1, sizeof(GlyphInfo));
 590     return glyphInfo;
 591 }
 592 
 593 static void CopyBW2Grey8(const void* srcImage, int srcRowBytes,
 594                          void* dstImage, int dstRowBytes,
 595                          int width, int height) {
 596     const UInt8* srcRow = (UInt8*)srcImage;
 597     UInt8* dstRow = (UInt8*)dstImage;
 598     int wholeByteCount = width >> 3;
 599     int remainingBitsCount = width & 7;
 600     int i, j;
 601 
 602     while (height--) {
 603         const UInt8* src8 = srcRow;
 604         UInt8* dstByte = dstRow;
 605         unsigned srcValue;
 606 
 607         srcRow += srcRowBytes;
 608         dstRow += dstRowBytes;
 609 
 610         for (i = 0; i < wholeByteCount; i++) {
 611             srcValue = *src8++;
 612             for (j = 0; j < 8; j++) {
 613                 *dstByte++ = (srcValue & 0x80) ? 0xFF : 0;
 614                 srcValue <<= 1;
 615             }
 616         }
 617         if (remainingBitsCount) {
 618             srcValue = *src8;
 619             for (j = 0; j < remainingBitsCount; j++) {
 620                 *dstByte++ = (srcValue & 0x80) ? 0xFF : 0;
 621                 srcValue <<= 1;
 622             }
 623         }
 624     }
 625 }
 626 
 627 #define Grey4ToAlpha255(value) (((value) << 4) + ((value) >> 3))
 628 
 629 static void CopyGrey4ToGrey8(const void* srcImage, int srcRowBytes,
 630                 void* dstImage, int dstRowBytes, int width, int height) {
 631      const UInt8* srcRow = (UInt8*) srcImage;
 632      UInt8* dstRow = (UInt8*) dstImage;
 633      int i;
 634 
 635      while (height--) {
 636          const UInt8* src8 = srcRow;
 637          UInt8* dstByte = dstRow;
 638          unsigned srcValue;
 639 
 640          srcRow += srcRowBytes;
 641          dstRow += dstRowBytes;
 642 
 643          for (i = 0; i < width; i++) {
 644              srcValue = *src8++;
 645              *dstByte++ = Grey4ToAlpha255(srcValue & 0x0f);
 646              *dstByte++ = Grey4ToAlpha255(srcValue >> 4);
 647          }
 648      }
 649 }
 650 
 651 /* We need it because FT rows are often padded to 4 byte boundaries
 652     and our internal format is not padded */
 653 static void CopyFTSubpixelToSubpixel(const void* srcImage, int srcRowBytes,
 654                                      void* dstImage, int dstRowBytes,
 655                                      int width, int height) {
 656     unsigned char *srcRow = (unsigned char *) srcImage;
 657     unsigned char *dstRow = (unsigned char *) dstImage;
 658 
 659     while (height--) {
 660         memcpy(dstRow, srcRow, width);
 661         srcRow += srcRowBytes;
 662         dstRow += dstRowBytes;
 663     }
 664 }
 665 
 666 /* We need it because FT rows are often padded to 4 byte boundaries
 667    and our internal format is not padded */
 668 static void CopyFTSubpixelVToSubpixel(const void* srcImage, int srcRowBytes,
 669                                       void* dstImage, int dstRowBytes,
 670                                       int width, int height) {
 671     unsigned char *srcRow = (unsigned char *) srcImage, *srcByte;
 672     unsigned char *dstRow = (unsigned char *) dstImage, *dstByte;
 673     int i;
 674 
 675     while (height > 0) {
 676         srcByte = srcRow;
 677         dstByte = dstRow;
 678         for (i = 0; i < width; i++) {
 679             *dstByte++ = *srcByte;
 680             *dstByte++ = *(srcByte + srcRowBytes);
 681             *dstByte++ = *(srcByte + 2*srcRowBytes);
 682             srcByte++;
 683         }
 684         srcRow += 3*srcRowBytes;
 685         dstRow += dstRowBytes;
 686         height -= 3;
 687     }
 688 }
 689 
 690 
 691 /*
 692  * Class:     sun_font_FreetypeFontScaler
 693  * Method:    getGlyphImageNative
 694  * Signature: (Lsun/font/Font2D;JI)J
 695  */
 696 JNIEXPORT jlong JNICALL
 697 Java_sun_font_FreetypeFontScaler_getGlyphImageNative(
 698         JNIEnv *env, jobject scaler, jobject font2D,
 699         jlong pScalerContext, jlong pScaler, jint glyphCode) {
 700 
 701     int error, imageSize;
 702     UInt16 width, height;
 703     GlyphInfo *glyphInfo;
 704     int glyph_index;
 705     int renderFlags = FT_LOAD_RENDER, target;
 706     FT_GlyphSlot ftglyph;
 707 
 708     FTScalerContext* context =
 709         (FTScalerContext*) jlong_to_ptr(pScalerContext);
 710     FTScalerInfo *scalerInfo =
 711              (FTScalerInfo*) jlong_to_ptr(pScaler);
 712 
 713     if (isNullScalerContext(context) || scalerInfo == NULL) {
 714         return ptr_to_jlong(getNullGlyphImage());
 715     }
 716 
 717     error = setupFTContext(env, font2D, scalerInfo, context);
 718     if (error) {
 719         invalidateJavaScaler(env, scaler, scalerInfo);
 720         return ptr_to_jlong(getNullGlyphImage());
 721     }
 722 
 723     /* if algorithmic styling is required then we do not request bitmap */
 724     if (context->doBold || context->doItalize) {
 725         renderFlags =  FT_LOAD_DEFAULT;
 726     }
 727 
 728     /* NB: in case of non identity transform
 729      we might also prefer to disable transform before hinting,
 730      and apply it explicitly after hinting is performed.
 731      Or we can disable hinting. */
 732 
 733     /* select appropriate hinting mode */
 734     if (context->aaType == TEXT_AA_OFF) {
 735         target = FT_LOAD_TARGET_MONO;
 736     } else if (context->aaType == TEXT_AA_ON) {
 737         target = FT_LOAD_TARGET_NORMAL;
 738     } else if (context->aaType == TEXT_AA_LCD_HRGB ||
 739                context->aaType == TEXT_AA_LCD_HBGR) {
 740         target = FT_LOAD_TARGET_LCD;
 741     } else {
 742         target = FT_LOAD_TARGET_LCD_V;
 743     }
 744     renderFlags |= target;
 745 
 746     glyph_index = FT_Get_Char_Index(scalerInfo->face, glyphCode);
 747 
 748     error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderFlags);
 749     if (error) {
 750         //do not destroy scaler yet.
 751         //this can be problem of particular context (e.g. with bad transform)
 752         return ptr_to_jlong(getNullGlyphImage());
 753     }
 754 
 755     ftglyph = scalerInfo->face->glyph;
 756 
 757     /* apply styles */
 758     if (context->doBold) { /* if bold style */
 759         FT_GlyphSlot_Embolden(ftglyph);
 760     }
 761     if (context->doItalize) { /* if oblique */
 762         FT_GlyphSlot_Oblique(ftglyph);
 763     }
 764 
 765     /* generate bitmap if it is not done yet
 766      e.g. if algorithmic styling is performed and style was added to outline */
 767     if (ftglyph->format == FT_GLYPH_FORMAT_OUTLINE) {
 768         FT_Render_Glyph(ftglyph, FT_LOAD_TARGET_MODE(target));
 769     }
 770 
 771     width  = (UInt16) ftglyph->bitmap.width;
 772     height = (UInt16) ftglyph->bitmap.rows;
 773 
 774     imageSize = width*height;
 775     glyphInfo = (GlyphInfo*) malloc(sizeof(GlyphInfo) + imageSize);
 776     if (glyphInfo == NULL) {
 777         glyphInfo = getNullGlyphImage();
 778         return ptr_to_jlong(glyphInfo);
 779     }
 780     glyphInfo->cellInfo  = NULL;
 781     glyphInfo->managed   = UNMANAGED_GLYPH;
 782     glyphInfo->rowBytes  = width;
 783     glyphInfo->width     = width;
 784     glyphInfo->height    = height;
 785     glyphInfo->topLeftX  = (float)  ftglyph->bitmap_left;
 786     glyphInfo->topLeftY  = (float) -ftglyph->bitmap_top;
 787 
 788     if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD) {
 789         glyphInfo->width = width/3;
 790     } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD_V) {
 791         glyphInfo->height = glyphInfo->height/3;
 792     }
 793 
 794     if (context->fmType == TEXT_FM_ON) {
 795         double advh = FTFixedToFloat(ftglyph->linearHoriAdvance);
 796         glyphInfo->advanceX =
 797             (float) (advh * FTFixedToFloat(context->transform.xx));
 798         glyphInfo->advanceY =
 799             (float) (advh * FTFixedToFloat(context->transform.xy));
 800     } else {
 801         if (!ftglyph->advance.y) {
 802             glyphInfo->advanceX =
 803                 (float) ROUND(FT26Dot6ToFloat(ftglyph->advance.x));
 804             glyphInfo->advanceY = 0;
 805         } else if (!ftglyph->advance.x) {
 806             glyphInfo->advanceX = 0;
 807             glyphInfo->advanceY =
 808                 (float) ROUND(FT26Dot6ToFloat(-ftglyph->advance.y));
 809         } else {
 810             glyphInfo->advanceX = FT26Dot6ToFloat(ftglyph->advance.x);
 811             glyphInfo->advanceY = FT26Dot6ToFloat(-ftglyph->advance.y);
 812         }
 813     }
 814 
 815     if (imageSize == 0) {
 816         glyphInfo->image = NULL;
 817     } else {
 818         glyphInfo->image = (unsigned char*) glyphInfo + sizeof(GlyphInfo);
 819         //convert result to output format
 820         //output format is either 3 bytes per pixel (for subpixel modes)
 821         // or 1 byte per pixel for AA and B&W
 822         if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_MONO) {
 823             /* convert from 8 pixels per byte to 1 byte per pixel */
 824             CopyBW2Grey8(ftglyph->bitmap.buffer,
 825                          ftglyph->bitmap.pitch,
 826                          (void *) glyphInfo->image,
 827                          width,
 828                          width,
 829                          height);
 830         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_GRAY) {
 831             /* byte per pixel to byte per pixel => just copy */
 832             memcpy(glyphInfo->image, ftglyph->bitmap.buffer, imageSize);
 833         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_GRAY4) {
 834             /* 4 bits per pixel to byte per pixel */
 835             CopyGrey4ToGrey8(ftglyph->bitmap.buffer,
 836                              ftglyph->bitmap.pitch,
 837                              (void *) glyphInfo->image,
 838                              width,
 839                              width,
 840                              height);
 841         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD) {
 842             /* 3 bytes per pixel to 3 bytes per pixel */
 843             CopyFTSubpixelToSubpixel(ftglyph->bitmap.buffer,
 844                                      ftglyph->bitmap.pitch,
 845                                      (void *) glyphInfo->image,
 846                                      width,
 847                                      width,
 848                                      height);
 849         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD_V) {
 850             /* 3 bytes per pixel to 3 bytes per pixel */
 851             CopyFTSubpixelVToSubpixel(ftglyph->bitmap.buffer,
 852                                       ftglyph->bitmap.pitch,
 853                                       (void *) glyphInfo->image,
 854                                       width*3,
 855                                       width,
 856                                       height);
 857             glyphInfo->rowBytes *=3;
 858         } else {
 859             free(glyphInfo);
 860             glyphInfo = getNullGlyphImage();
 861         }
 862     }
 863 
 864     return ptr_to_jlong(glyphInfo);
 865 }
 866 
 867 
 868 /*
 869  * Class:     sun_font_FreetypeFontScaler
 870  * Method:    getLayoutTableCacheNative
 871  * Signature: (J)J
 872  */
 873 JNIEXPORT jlong JNICALL
 874 Java_sun_font_FreetypeFontScaler_getLayoutTableCacheNative(
 875         JNIEnv *env, jobject scaler, jlong pScaler) {
 876     FTScalerInfo *scalerInfo = (FTScalerInfo*) jlong_to_ptr(pScaler);
 877 
 878     if (scalerInfo == NULL) {
 879         invalidateJavaScaler(env, scaler, scalerInfo);
 880         return 0L;
 881     }
 882 
 883     // init layout table cache in font
 884     // we're assuming the font is a file font and moreover it is Truetype font
 885     // otherwise we shouldn't be able to get here...
 886     if (scalerInfo->layoutTables == NULL) {
 887         scalerInfo->layoutTables = newLayoutTableCache();
 888     }
 889 
 890     return ptr_to_jlong(scalerInfo->layoutTables);
 891 }
 892 
 893 /*
 894  * Class:     sun_font_FreetypeFontScaler
 895  * Method:    disposeNativeScaler
 896  * Signature: (J)V
 897  */
 898 JNIEXPORT void JNICALL
 899 Java_sun_font_FreetypeFontScaler_disposeNativeScaler(
 900         JNIEnv *env, jobject scaler, jobject font2D, jlong pScaler) {
 901     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
 902 
 903     /* Freetype functions *may* cause callback to java
 904        that can use cached values. Make sure our cache is up to date.
 905        NB: scaler context is not important at this point, can use NULL. */
 906     int errCode = setupFTContext(env, font2D, scalerInfo, NULL);
 907     if (errCode) {
 908         return;
 909     }
 910 
 911     freeNativeResources(env, scalerInfo);
 912 }
 913 
 914 /*
 915  * Class:     sun_font_FreetypeFontScaler
 916  * Method:    getNumGlyphsNative
 917  * Signature: ()I
 918  */
 919 JNIEXPORT jint JNICALL
 920 Java_sun_font_FreetypeFontScaler_getNumGlyphsNative(
 921         JNIEnv *env, jobject scaler, jlong pScaler) {
 922     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
 923 
 924     if (scalerInfo == NULL || scalerInfo->face == NULL) { /* bad/null scaler */
 925         /* null scaler can render 1 glyph - "missing glyph" with code 0
 926            (all glyph codes requested by user are mapped to code 0 at
 927            validation step) */
 928         invalidateJavaScaler(env, scaler, scalerInfo);
 929         return (jint) 1;
 930     }
 931 
 932     return (jint) scalerInfo->face->num_glyphs;
 933 }
 934 
 935 /*
 936  * Class:     sun_font_FreetypeFontScaler
 937  * Method:    getMissingGlyphCodeNative
 938  * Signature: ()I
 939  */
 940 JNIEXPORT jint JNICALL
 941 Java_sun_font_FreetypeFontScaler_getMissingGlyphCodeNative(
 942         JNIEnv *env, jobject scaler, jlong pScaler) {
 943 
 944     /* Is it always 0 for freetype? */
 945     return 0;
 946 }
 947 
 948 /*
 949  * Class:     sun_font_FreetypeFontScaler
 950  * Method:    getGlyphCodeNative
 951  * Signature: (C)I
 952  */
 953 JNIEXPORT jint JNICALL
 954 Java_sun_font_FreetypeFontScaler_getGlyphCodeNative(
 955         JNIEnv *env, jobject scaler,
 956         jobject font2D, jlong pScaler, jchar charCode) {
 957 
 958     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
 959     int errCode;
 960 
 961     if (scaler == NULL || scalerInfo->face == NULL) { /* bad/null scaler */
 962         invalidateJavaScaler(env, scaler, scalerInfo);
 963         return 0;
 964     }
 965 
 966     /* Freetype functions *may* cause callback to java
 967        that can use cached values. Make sure our cache is up to date.
 968        Scaler context is not important here, can use NULL. */
 969     errCode = setupFTContext(env, font2D, scalerInfo, NULL);
 970     if (errCode) {
 971         return 0;
 972     }
 973 
 974     return FT_Get_Char_Index(scalerInfo->face, charCode);
 975 }
 976 
 977 
 978 #define FloatToF26Dot6(x) ((unsigned int) ((x)*64))
 979 
 980 static FT_Outline* getFTOutline(JNIEnv* env, jobject font2D,
 981         FTScalerContext *context, FTScalerInfo* scalerInfo,
 982         jint glyphCode, jfloat xpos, jfloat ypos) {
 983     int renderFlags;
 984     int glyph_index;
 985     FT_Error error;
 986     FT_GlyphSlot ftglyph;
 987 
 988     if (glyphCode >= INVISIBLE_GLYPHS ||
 989             isNullScalerContext(context) || scalerInfo == NULL) {
 990         return NULL;
 991     }
 992 
 993     error = setupFTContext(env, font2D, scalerInfo, context);
 994     if (error) {
 995         return NULL;
 996     }
 997 
 998     renderFlags = FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP;
 999 
1000     glyph_index = FT_Get_Char_Index(scalerInfo->face, glyphCode);
1001 
1002     error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderFlags);
1003     if (error) {
1004         return NULL;
1005     }
1006 
1007     ftglyph = scalerInfo->face->glyph;
1008 
1009     /* apply styles */
1010     if (context->doBold) { /* if bold style */
1011         FT_GlyphSlot_Embolden(ftglyph);
1012     }
1013     if (context->doItalize) { /* if oblique */
1014         FT_GlyphSlot_Oblique(ftglyph);
1015     }
1016 
1017     FT_Outline_Translate(&ftglyph->outline,
1018                          FloatToF26Dot6(xpos),
1019                          -FloatToF26Dot6(ypos));
1020 
1021     return &ftglyph->outline;
1022 }
1023 
1024 #define F26Dot6ToFloat(n) (((float)(n))/((float) 64))
1025 
1026 /* Types of GeneralPath segments.
1027    TODO: pull constants from other place? */
1028 
1029 #define SEG_UNKNOWN -1
1030 #define SEG_MOVETO   0
1031 #define SEG_LINETO   1
1032 #define SEG_QUADTO   2
1033 #define SEG_CUBICTO  3
1034 #define SEG_CLOSE    4
1035 
1036 #define WIND_NON_ZERO 0
1037 #define WIND_EVEN_ODD 1
1038 
1039 /* Placeholder to accumulate GeneralPath data */
1040 typedef struct {
1041     jint numTypes;
1042     jint numCoords;
1043     jint lenTypes;
1044     jint lenCoords;
1045     jint wr;
1046     jbyte* pointTypes;
1047     jfloat* pointCoords;
1048 } GPData;
1049 
1050 /* returns 0 on failure */
1051 static int allocateSpaceForGP(GPData* gpdata, int npoints, int ncontours) {
1052     int maxTypes, maxCoords;
1053 
1054     /* we may have up to N intermediate points per contour
1055        (and for each point can actually cause new curve to be generated)
1056        In addition we can also have 2 extra point per outline.
1057      */
1058     maxTypes  = 2*npoints  + 2*ncontours;
1059     maxCoords = 4*(npoints + 2*ncontours); //we may need to insert
1060                                            //up to n-1 intermediate points
1061 
1062     /* first usage - allocate space and intialize all fields */
1063     if (gpdata->pointTypes == NULL || gpdata->pointCoords == NULL) {
1064         gpdata->lenTypes  = maxTypes;
1065         gpdata->lenCoords = maxCoords;
1066         gpdata->pointTypes  = (jbyte*)
1067              malloc(gpdata->lenTypes*sizeof(jbyte));
1068         gpdata->pointCoords = (jfloat*)
1069              malloc(gpdata->lenCoords*sizeof(jfloat));
1070         gpdata->numTypes = 0;
1071         gpdata->numCoords = 0;
1072         gpdata->wr = WIND_NON_ZERO; /* By default, outlines are filled
1073                                        using the non-zero winding rule. */
1074     } else {
1075         /* do we have enough space? */
1076         if (gpdata->lenTypes - gpdata->numTypes < maxTypes) {
1077             gpdata->lenTypes  += maxTypes;
1078             gpdata->pointTypes  = (jbyte*)
1079               realloc(gpdata->pointTypes, gpdata->lenTypes*sizeof(jbyte));
1080         }
1081 
1082         if (gpdata->lenCoords - gpdata->numCoords < maxCoords) {
1083             gpdata->lenCoords += maxCoords;
1084             gpdata->pointCoords = (jfloat*)
1085               realloc(gpdata->pointCoords, gpdata->lenCoords*sizeof(jfloat));
1086         }
1087     }
1088 
1089     /* failure if any of mallocs failed */
1090     if (gpdata->pointTypes == NULL ||  gpdata->pointCoords == NULL)
1091         return 0;
1092     else
1093         return 1;
1094 }
1095 
1096 static void addToGP(GPData* gpdata, FT_Outline*outline) {
1097     jbyte current_type=SEG_UNKNOWN;
1098     int i, j;
1099     jfloat x, y;
1100 
1101     j = 0;
1102     for(i=0; i<outline->n_points; i++) {
1103         x =  F26Dot6ToFloat(outline->points[i].x);
1104         y = -F26Dot6ToFloat(outline->points[i].y);
1105 
1106         if (FT_CURVE_TAG(outline->tags[i]) == FT_CURVE_TAG_ON) {
1107             /* If bit 0 is unset, the point is "off" the curve,
1108              i.e., a Bezier control point, while it is "on" when set. */
1109             if (current_type == SEG_UNKNOWN) { /* special case:
1110                                                   very first point */
1111                 /* add segment */
1112                 gpdata->pointTypes[gpdata->numTypes++] = SEG_MOVETO;
1113                 current_type = SEG_LINETO;
1114             } else {
1115                 gpdata->pointTypes[gpdata->numTypes++] = current_type;
1116                 current_type = SEG_LINETO;
1117             }
1118         } else {
1119             if (current_type == SEG_UNKNOWN) { /* special case:
1120                                                    very first point */
1121                 if (FT_CURVE_TAG(outline->tags[i+1]) == FT_CURVE_TAG_ON) {
1122                     /* just skip first point. Adhoc heuristic? */
1123                     continue;
1124                 } else {
1125                     x = (x + F26Dot6ToFloat(outline->points[i+1].x))/2;
1126                     y = (y - F26Dot6ToFloat(outline->points[i+1].y))/2;
1127                     gpdata->pointTypes[gpdata->numTypes++] = SEG_MOVETO;
1128                     current_type = SEG_LINETO;
1129                 }
1130             } else if (FT_CURVE_TAG(outline->tags[i]) == FT_CURVE_TAG_CUBIC) {
1131                 /* Bit 1 is meaningful for 'off' points only.
1132                    If set, it indicates a third-order Bezier arc control
1133                    point; and a second-order control point if unset.  */
1134                 current_type = SEG_CUBICTO;
1135             } else {
1136                 /* two successive conic "off" points forces the rasterizer
1137                    to create (during the scan-line conversion process
1138                    exclusively) a virtual "on" point amidst them, at their
1139                    exact middle. This greatly facilitates the definition of
1140                    successive conic Bezier arcs.  Moreover, it is the way
1141                    outlines are described in the TrueType specification. */
1142                 if (current_type == SEG_QUADTO) {
1143                     gpdata->pointCoords[gpdata->numCoords++] =
1144                         F26Dot6ToFloat(outline->points[i].x +
1145                         outline->points[i-1].x)/2;
1146                     gpdata->pointCoords[gpdata->numCoords++] =
1147                         - F26Dot6ToFloat(outline->points[i].y +
1148                         outline->points[i-1].y)/2;
1149                     gpdata->pointTypes[gpdata->numTypes++] = SEG_QUADTO;
1150                 }
1151                 current_type = SEG_QUADTO;
1152             }
1153         }
1154         gpdata->pointCoords[gpdata->numCoords++] = x;
1155         gpdata->pointCoords[gpdata->numCoords++] = y;
1156         if (outline->contours[j] == i) { //end of contour
1157             int start = j > 0 ? outline->contours[j-1]+1 : 0;
1158             gpdata->pointTypes[gpdata->numTypes++] = current_type;
1159             if (current_type == SEG_QUADTO &&
1160             FT_CURVE_TAG(outline->tags[start]) != FT_CURVE_TAG_ON) {
1161                 gpdata->pointCoords[gpdata->numCoords++] =
1162                             (F26Dot6ToFloat(outline->points[start].x) + x)/2;
1163                 gpdata->pointCoords[gpdata->numCoords++] =
1164                             (-F26Dot6ToFloat(outline->points[start].y) + y)/2;
1165             } else {
1166                 gpdata->pointCoords[gpdata->numCoords++] =
1167                             F26Dot6ToFloat(outline->points[start].x);
1168                 gpdata->pointCoords[gpdata->numCoords++] =
1169                             -F26Dot6ToFloat(outline->points[start].y);
1170             }
1171             gpdata->pointTypes[gpdata->numTypes++] = SEG_CLOSE;
1172             current_type = SEG_UNKNOWN;
1173             j++;
1174         }
1175     }
1176 
1177     /* If set to 1, the outline will be filled using the even-odd fill rule */
1178     if (outline->flags & FT_OUTLINE_EVEN_ODD_FILL) {
1179         gpdata->wr = WIND_EVEN_ODD;
1180     }
1181 }
1182 
1183 static void freeGP(GPData* gpdata) {
1184     if (gpdata->pointCoords != NULL) {
1185         free(gpdata->pointCoords);
1186         gpdata->pointCoords = NULL;
1187         gpdata->numCoords = 0;
1188         gpdata->lenCoords = 0;
1189     }
1190     if (gpdata->pointTypes != NULL) {
1191         free(gpdata->pointTypes);
1192         gpdata->pointTypes = NULL;
1193         gpdata->numTypes = 0;
1194         gpdata->lenTypes = 0;
1195     }
1196 }
1197 
1198 static jobject getGlyphGeneralPath(JNIEnv* env, jobject font2D,
1199         FTScalerContext *context, FTScalerInfo *scalerInfo,
1200         jint glyphCode, jfloat xpos, jfloat ypos) {
1201 
1202     FT_Outline* outline;
1203     jobject gp = NULL;
1204     jbyteArray types;
1205     jfloatArray coords;
1206     GPData gpdata;
1207 
1208     outline = getFTOutline(env, font2D, context, scalerInfo,
1209                            glyphCode, xpos, ypos);
1210 
1211     if (outline == NULL || outline->n_points == 0) {
1212         return gp;
1213     }
1214 
1215     gpdata.pointTypes  = NULL;
1216     gpdata.pointCoords = NULL;
1217     if (!allocateSpaceForGP(&gpdata, outline->n_points, outline->n_contours)) {
1218         return gp;
1219     }
1220 
1221     addToGP(&gpdata, outline);
1222 
1223     types  = (*env)->NewByteArray(env, gpdata.numTypes);
1224     coords = (*env)->NewFloatArray(env, gpdata.numCoords);
1225 
1226     if (types && coords) {
1227         (*env)->SetByteArrayRegion(env, types, 0,
1228                                    gpdata.numTypes,
1229                                    gpdata.pointTypes);
1230         (*env)->SetFloatArrayRegion(env, coords, 0,
1231                                     gpdata.numCoords,
1232                                     gpdata.pointCoords);
1233         gp = (*env)->NewObject(env,
1234                                sunFontIDs.gpClass,
1235                                sunFontIDs.gpCtr,
1236                                gpdata.wr,
1237                                types,
1238                                gpdata.numTypes,
1239                                coords,
1240                                gpdata.numCoords);
1241     }
1242 
1243     freeGP(&gpdata);
1244 
1245     return gp;
1246 }
1247 
1248 /*
1249  * Class:     sun_font_FreetypeFontScaler
1250  * Method:    getGlyphOutlineNative
1251  * Signature: (Lsun/font/Font2D;JIFF)Ljava/awt/geom/GeneralPath;
1252  */
1253 JNIEXPORT jobject JNICALL
1254 Java_sun_font_FreetypeFontScaler_getGlyphOutlineNative(
1255       JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
1256       jlong pScaler, jint glyphCode, jfloat xpos, jfloat ypos) {
1257 
1258     FTScalerContext *context =
1259          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1260     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
1261 
1262     jobject gp = getGlyphGeneralPath(env,
1263                                font2D,
1264                                context,
1265                                scalerInfo,
1266                                glyphCode,
1267                                xpos,
1268                                ypos);
1269     if (gp == NULL) { /* can be legal */
1270         gp = (*env)->NewObject(env,
1271                                sunFontIDs.gpClass,
1272                                sunFontIDs.gpCtrEmpty);
1273     }
1274     return gp;
1275 }
1276 
1277 /*
1278  * Class:     sun_font_FreetypeFontScaler
1279  * Method:    getGlyphOutlineBoundsNative
1280  * Signature: (Lsun/font/Font2D;JI)Ljava/awt/geom/Rectangle2D/Float;
1281  */
1282 JNIEXPORT jobject JNICALL
1283 Java_sun_font_FreetypeFontScaler_getGlyphOutlineBoundsNative(
1284         JNIEnv *env, jobject scaler, jobject font2D,
1285         jlong pScalerContext, jlong pScaler, jint glyphCode) {
1286 
1287     FT_Outline *outline;
1288     FT_BBox bbox;
1289     int error;
1290     jobject bounds;
1291 
1292     FTScalerContext *context =
1293          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1294     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
1295 
1296     outline = getFTOutline(env, font2D, context, scalerInfo, glyphCode, 0, 0);
1297     if (outline == NULL || outline->n_points == 0) {
1298         /* it is legal case, e.g. invisible glyph */
1299         bounds = (*env)->NewObject(env,
1300                                  sunFontIDs.rect2DFloatClass,
1301                                  sunFontIDs.rect2DFloatCtr);
1302         return bounds;
1303     }
1304 
1305     error = FT_Outline_Get_BBox(outline, &bbox);
1306 
1307     //convert bbox
1308     if (error || bbox.xMin >= bbox.xMax || bbox.yMin >= bbox.yMax) {
1309         bounds = (*env)->NewObject(env,
1310                                    sunFontIDs.rect2DFloatClass,
1311                                    sunFontIDs.rect2DFloatCtr);
1312     } else {
1313         bounds = (*env)->NewObject(env,
1314                                    sunFontIDs.rect2DFloatClass,
1315                                    sunFontIDs.rect2DFloatCtr4,
1316                                    F26Dot6ToFloat(bbox.xMin),
1317                                    F26Dot6ToFloat(-bbox.yMax),
1318                                    F26Dot6ToFloat(bbox.xMax-bbox.xMin),
1319                                    F26Dot6ToFloat(bbox.yMax-bbox.yMin));
1320     }
1321 
1322     return bounds;
1323 }
1324 
1325 /*
1326  * Class:     sun_font_FreetypeFontScaler
1327  * Method:    getGlyphVectorOutlineNative
1328  * Signature: (Lsun/font/Font2D;J[IIFF)Ljava/awt/geom/GeneralPath;
1329  */
1330 JNIEXPORT jobject
1331 JNICALL
1332 Java_sun_font_FreetypeFontScaler_getGlyphVectorOutlineNative(
1333         JNIEnv *env, jobject scaler, jobject font2D,
1334         jlong pScalerContext, jlong pScaler,
1335         jintArray glyphArray, jint numGlyphs, jfloat xpos, jfloat ypos) {
1336 
1337     FT_Outline* outline;
1338     jobject gp = NULL;
1339     jbyteArray types;
1340     jfloatArray coords;
1341     GPData gpdata;
1342     int i;
1343     jint *glyphs;
1344 
1345     FTScalerContext *context =
1346          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1347     FTScalerInfo *scalerInfo =
1348              (FTScalerInfo*) jlong_to_ptr(pScaler);
1349 
1350     glyphs = NULL;
1351     if (numGlyphs > 0 && 0xffffffffu / sizeof(jint) >= numGlyphs) {
1352         glyphs = (jint*) malloc(numGlyphs*sizeof(jint));
1353     }
1354     if (glyphs == NULL) {
1355         // We reach here if:
1356         // 1. numGlyphs <= 0,
1357         // 2. overflow check failed, or
1358         // 3. malloc failed.
1359         gp = (*env)->NewObject(env, sunFontIDs.gpClass, sunFontIDs.gpCtrEmpty);
1360         return gp;
1361     }
1362 
1363     (*env)->GetIntArrayRegion(env, glyphArray, 0, numGlyphs, glyphs);
1364 
1365     gpdata.numCoords = 0;
1366     for (i=0; i<numGlyphs;i++) {
1367         if (glyphs[i] >= INVISIBLE_GLYPHS) {
1368             continue;
1369         }
1370         outline = getFTOutline(env,
1371                                font2D,
1372                                context,
1373                                scalerInfo,
1374                                glyphs[i],
1375                                xpos, ypos);
1376 
1377         if (outline == NULL || outline->n_points == 0) {
1378             continue;
1379         }
1380 
1381         gpdata.pointTypes  = NULL;
1382         gpdata.pointCoords = NULL;
1383         if (!allocateSpaceForGP(&gpdata, outline->n_points,
1384                                 outline->n_contours)) {
1385             break;
1386         }
1387 
1388         addToGP(&gpdata, outline);
1389     }
1390     free(glyphs);
1391 
1392     if (gpdata.numCoords != 0) {
1393       types = (*env)->NewByteArray(env, gpdata.numTypes);
1394       coords = (*env)->NewFloatArray(env, gpdata.numCoords);
1395 
1396       if (types && coords) {
1397         (*env)->SetByteArrayRegion(env, types, 0,
1398                                    gpdata.numTypes, gpdata.pointTypes);
1399         (*env)->SetFloatArrayRegion(env, coords, 0,
1400                                     gpdata.numCoords, gpdata.pointCoords);
1401 
1402         gp=(*env)->NewObject(env,
1403                              sunFontIDs.gpClass,
1404                              sunFontIDs.gpCtr,
1405                              gpdata.wr,
1406                              types,
1407                              gpdata.numTypes,
1408                              coords,
1409                              gpdata.numCoords);
1410         return gp;
1411       }
1412     }
1413     return (*env)->NewObject(env, sunFontIDs.gpClass, sunFontIDs.gpCtrEmpty);
1414 }
1415 
1416 JNIEXPORT jlong JNICALL
1417 Java_sun_font_FreetypeFontScaler_getUnitsPerEMNative(
1418         JNIEnv *env, jobject scaler, jlong pScaler) {
1419 
1420     FTScalerInfo *s = (FTScalerInfo* ) jlong_to_ptr(pScaler);
1421 
1422     /* Freetype doc says:
1423      The number of font units per EM square for this face.
1424      This is typically 2048 for TrueType fonts, and 1000 for Type 1 fonts.
1425      Only relevant for scalable formats.
1426      However, layout engine might be not tested with anything but 2048.
1427 
1428      NB: test it! */
1429     if (s != NULL) {
1430         return s->face->units_per_EM;
1431     }
1432     return 2048;
1433 }
1434 
1435 /* This native method is called by the OpenType layout engine. */
1436 JNIEXPORT jobject JNICALL
1437 Java_sun_font_FreetypeFontScaler_getGlyphPointNative(
1438         JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
1439         jlong pScaler, jint glyphCode, jint pointNumber) {
1440 
1441     FT_Outline* outline;
1442     jobject point = NULL;
1443     jfloat x=0, y=0;
1444     FTScalerContext *context =
1445          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1446     FTScalerInfo *scalerInfo = (FTScalerInfo*) jlong_to_ptr(pScaler);
1447 
1448     outline = getFTOutline(env, font2D, context, scalerInfo, glyphCode, 0, 0);
1449 
1450     if (outline != NULL && outline->n_points > pointNumber) {
1451         x =  F26Dot6ToFloat(outline->points[pointNumber].x);
1452         y = -F26Dot6ToFloat(outline->points[pointNumber].y);
1453     }
1454 
1455     return (*env)->NewObject(env, sunFontIDs.pt2DFloatClass,
1456                              sunFontIDs.pt2DFloatCtr, x, y);
1457 }
--- EOF ---