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