1 /*
2  * Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 #include "jni.h"
27 #include "jni_util.h"
28 #include "jlong.h"
29 #include "sunfontids.h"
30 #include "sun_font_FreetypeFontScaler.h"
31 
32 #include <stdlib.h>
33 #if !defined(_WIN32) && !defined(__APPLE_)
34 #include <dlfcn.h>
35 #endif
36 #include <math.h>
37 #include "ft2build.h"
38 #include FT_FREETYPE_H
39 #include FT_GLYPH_H
40 #include FT_BBOX_H
41 #include FT_SIZES_H
42 #include FT_OUTLINE_H
43 #include FT_SYNTHESIS_H
44 #include FT_LCD_FILTER_H
45 #include FT_MODULE_H
46 #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(property, props)) {
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     {
598         context->useSbits = 1;
599     }
600     return ptr_to_jlong(context);
601 }
602 
603 // values used by FreeType (as of version 2.10.1) for italics transformation matrix in FT_GlyphSlot_Oblique
604 #define FT_MATRIX_ONE 0x10000
605 #define FT_MATRIX_OBLIQUE_XY 0x0366A
606 
setupTransform(FT_Matrix * target,FTScalerContext * context)607 static void setupTransform(FT_Matrix* target, FTScalerContext *context) {
608     FT_Matrix* transform = &context->transform;
609     if (context->doItalize) {
610         // we cannot use FT_GlyphSlot_Oblique as it doesn't work well with arbitrary transforms,
611         // so we add corresponding shear transform to the requested glyph transformation
612         target->xx = FT_MATRIX_ONE;
613         target->xy = FT_MATRIX_OBLIQUE_XY;
614         target->yx = 0;
615         target->yy = FT_MATRIX_ONE;
616         FT_Matrix_Multiply(transform, target);
617     } else {
618         target->xx = transform->xx;
619         target->xy = transform->xy;
620         target->yx = transform->yx;
621         target->yy = transform->yy;
622     }
623 }
624 
setupFTContext(JNIEnv * env,jobject font2D,FTScalerInfo * scalerInfo,FTScalerContext * context)625 static int setupFTContext(JNIEnv *env,
626                           jobject font2D,
627                           FTScalerInfo *scalerInfo,
628                           FTScalerContext *context) {
629     FT_Matrix matrix;
630     int errCode = 0;
631 
632     scalerInfo->env = env;
633     scalerInfo->font2D = font2D;
634 
635     if (context != NULL) {
636         setupTransform(&matrix, context);
637         FT_Set_Transform(scalerInfo->face, &matrix, NULL);
638 
639         errCode = FT_Set_Char_Size(scalerInfo->face, 0, context->ptsz, 72, 72);
640 
641         if (errCode == 0) {
642             errCode = FT_Activate_Size(scalerInfo->face->size);
643         }
644 
645         FT_Library_SetLcdFilter(scalerInfo->library, FT_LCD_FILTER_DEFAULT);
646     }
647 
648     return errCode;
649 }
650 
651 // using same values as for the transformation matrix
652 #define OBLIQUE_MODIFIER(y)  (context->doItalize ? ((y)*FT_MATRIX_OBLIQUE_XY/FT_MATRIX_ONE) : 0)
653 
654 /* FT_GlyphSlot_Embolden (ftsynth.c) uses FT_MulFix(units_per_EM, y_scale) / 24
655  * strength value when glyph format is FT_GLYPH_FORMAT_OUTLINE. This value has
656  * been taken from libfreetype version 2.6 and remain valid at least up to
657  * 2.9.1. */
658 #define BOLD_MODIFIER(units_per_EM, y_scale) \
659     (context->doBold ? FT_MulFix(units_per_EM, y_scale) / 24 : 0)
660 
661 /*
662  * Class:     sun_font_FreetypeFontScaler
663  * Method:    getFontMetricsNative
664  * Signature: (Lsun/font/Font2D;J)Lsun/font/StrikeMetrics;
665  */
666 JNIEXPORT jobject JNICALL
Java_sun_font_FreetypeFontScaler_getFontMetricsNative(JNIEnv * env,jobject scaler,jobject font2D,jlong pScalerContext,jlong pScaler)667 Java_sun_font_FreetypeFontScaler_getFontMetricsNative(
668         JNIEnv *env, jobject scaler, jobject font2D,
669         jlong pScalerContext, jlong pScaler) {
670 
671     jobject metrics;
672     jfloat ax, ay, dx, dy, bx, by, lx, ly, mx, my;
673     jfloat f0 = 0.0;
674     FTScalerContext *context =
675         (FTScalerContext*) jlong_to_ptr(pScalerContext);
676     FTScalerInfo *scalerInfo =
677              (FTScalerInfo*) jlong_to_ptr(pScaler);
678 
679     int errCode;
680 
681     if (isNullScalerContext(context) || scalerInfo == NULL) {
682         return (*env)->NewObject(env,
683                                  sunFontIDs.strikeMetricsClass,
684                                  sunFontIDs.strikeMetricsCtr,
685                                  f0, f0, f0, f0, f0, f0, f0, f0, f0, f0);
686     }
687 
688     errCode = setupFTContext(env, font2D, scalerInfo, context);
689 
690     if (errCode) {
691         metrics = (*env)->NewObject(env,
692                                  sunFontIDs.strikeMetricsClass,
693                                  sunFontIDs.strikeMetricsCtr,
694                                  f0, f0, f0, f0, f0, f0, f0, f0, f0, f0);
695         invalidateJavaScaler(env, scaler, scalerInfo);
696         return metrics;
697     }
698 
699     /* This is ugly and has to be reworked.
700        Freetype provide means to add style to glyph but
701        it seems there is no way to adjust metrics accordingly.
702 
703        So, we have to do adust them explicitly and stay consistent with what
704        freetype does to outlines. */
705 
706 
707     /**** Note: only some metrics are affected by styling ***/
708 
709     /* See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=657854 */
710 #define FT_MulFixFloatShift6(a, b) (((float) (a)) * ((float) (b)) / 65536.0 / 64.0)
711 
712 #define contextAwareMetricsX(x, y) \
713     (FTFixedToFloat(context->transform.xx) * (x) - \
714      FTFixedToFloat(context->transform.xy) * (y))
715 
716 #define contextAwareMetricsY(x, y) \
717     (-FTFixedToFloat(context->transform.yx) * (x) + \
718      FTFixedToFloat(context->transform.yy) * (y))
719 
720     /*
721      * See FreeType source code: src/base/ftobjs.c ft_recompute_scaled_metrics()
722      * http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=1659
723      */
724     /* ascent */
725     ax = 0;
726     ay = -(jfloat) (FT_MulFixFloatShift6(
727                        ((jlong) scalerInfo->face->ascender),
728                        (jlong) scalerInfo->face->size->metrics.y_scale));
729     /* descent */
730     dx = 0;
731     dy = -(jfloat) (FT_MulFixFloatShift6(
732                        ((jlong) scalerInfo->face->descender),
733                        (jlong) scalerInfo->face->size->metrics.y_scale));
734     /* baseline */
735     bx = by = 0;
736 
737     /* leading */
738     lx = 0;
739     ly = (jfloat) (FT_MulFixFloatShift6(
740                       (jlong) scalerInfo->face->height,
741                       (jlong) scalerInfo->face->size->metrics.y_scale))
742                   + ay - dy;
743     /* max advance */
744     mx = (jfloat) FT26Dot6ToFloat(
745                      scalerInfo->face->size->metrics.max_advance +
746                      OBLIQUE_MODIFIER(scalerInfo->face->size->metrics.height) +
747                      BOLD_MODIFIER(scalerInfo->face->units_per_EM,
748                              scalerInfo->face->size->metrics.y_scale));
749     my = 0;
750 
751     metrics = (*env)->NewObject(env,
752         sunFontIDs.strikeMetricsClass,
753         sunFontIDs.strikeMetricsCtr,
754         contextAwareMetricsX(ax, ay), contextAwareMetricsY(ax, ay),
755         contextAwareMetricsX(dx, dy), contextAwareMetricsY(dx, dy),
756         bx, by,
757         contextAwareMetricsX(lx, ly), contextAwareMetricsY(lx, ly),
758         contextAwareMetricsX(mx, my), contextAwareMetricsY(mx, my));
759 
760     return metrics;
761 }
762 
763 static jlong
764     getGlyphImageNativeInternal(
765         JNIEnv *env, jobject scaler, jobject font2D,
766         jlong pScalerContext, jlong pScaler, jint glyphCode,
767         jboolean renderImage);
768 
769 /*
770  * Class:     sun_font_FreetypeFontScaler
771  * Method:    getGlyphAdvanceNative
772  * Signature: (Lsun/font/Font2D;JI)F
773  */
774 JNIEXPORT jfloat JNICALL
Java_sun_font_FreetypeFontScaler_getGlyphAdvanceNative(JNIEnv * env,jobject scaler,jobject font2D,jlong pScalerContext,jlong pScaler,jint glyphCode)775 Java_sun_font_FreetypeFontScaler_getGlyphAdvanceNative(
776         JNIEnv *env, jobject scaler, jobject font2D,
777         jlong pScalerContext, jlong pScaler, jint glyphCode) {
778 
779    /* This method is rarely used because requests for metrics are usually
780     * coupled with a request for the bitmap and to a large extent the
781     * work can be reused (to find out metrics we may need to hint the glyph).
782     * So, we typically go through the getGlyphImage code path.
783     * When we do get here, we need to pass a parameter which indicates
784     * that we don't need freetype to render the bitmap, and consequently
785     * don't need to allocate our own storage either.
786     * This is also important when enter here requesting metrics for sizes
787     * of text which a large size would be rejected for a bitmap but we
788     * still need the metrics.
789     */
790 
791     GlyphInfo *info;
792     jfloat advance = 0.0f;
793     jlong image;
794 
795     image = getGlyphImageNativeInternal(
796           env, scaler, font2D, pScalerContext, pScaler, glyphCode, JNI_FALSE);
797     info = (GlyphInfo*) jlong_to_ptr(image);
798 
799     if (info != NULL) {
800         advance = info->advanceX;
801         free(info);
802     }
803 
804     return advance;
805 }
806 
807 /*
808  * Class:     sun_font_FreetypeFontScaler
809  * Method:    getGlyphMetricsNative
810  * Signature: (Lsun/font/Font2D;JILjava/awt/geom/Point2D/Float;)V
811  */
812 JNIEXPORT void JNICALL
Java_sun_font_FreetypeFontScaler_getGlyphMetricsNative(JNIEnv * env,jobject scaler,jobject font2D,jlong pScalerContext,jlong pScaler,jint glyphCode,jobject metrics)813 Java_sun_font_FreetypeFontScaler_getGlyphMetricsNative(
814         JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
815         jlong pScaler, jint glyphCode, jobject metrics) {
816 
817      /* See the comments in getGlyphMetricsNative. They apply here too. */
818      GlyphInfo *info;
819 
820      jlong image = getGlyphImageNativeInternal(
821                                  env, scaler, font2D,
822                                  pScalerContext, pScaler, glyphCode, JNI_FALSE);
823      info = (GlyphInfo*) jlong_to_ptr(image);
824 
825      if (info != NULL) {
826          (*env)->SetFloatField(env, metrics, sunFontIDs.xFID, info->advanceX);
827          (*env)->SetFloatField(env, metrics, sunFontIDs.yFID, info->advanceY);
828          free(info);
829      } else {
830          (*env)->SetFloatField(env, metrics, sunFontIDs.xFID, 0.0f);
831          (*env)->SetFloatField(env, metrics, sunFontIDs.yFID, 0.0f);
832      }
833 }
834 
835 
getNullGlyphImage()836 static GlyphInfo* getNullGlyphImage() {
837     GlyphInfo *glyphInfo =  (GlyphInfo*) calloc(1, sizeof(GlyphInfo));
838     return glyphInfo;
839 }
840 
CopyBW2Grey8(const void * srcImage,int srcRowBytes,void * dstImage,int dstRowBytes,int width,int height)841 static void CopyBW2Grey8(const void* srcImage, int srcRowBytes,
842                          void* dstImage, int dstRowBytes,
843                          int width, int height) {
844     const UInt8* srcRow = (UInt8*)srcImage;
845     UInt8* dstRow = (UInt8*)dstImage;
846     int wholeByteCount = width >> 3;
847     int remainingBitsCount = width & 7;
848     int i, j;
849 
850     while (height--) {
851         const UInt8* src8 = srcRow;
852         UInt8* dstByte = dstRow;
853         unsigned srcValue;
854 
855         srcRow += srcRowBytes;
856         dstRow += dstRowBytes;
857 
858         for (i = 0; i < wholeByteCount; i++) {
859             srcValue = *src8++;
860             for (j = 0; j < 8; j++) {
861                 *dstByte++ = (srcValue & 0x80) ? 0xFF : 0;
862                 srcValue <<= 1;
863             }
864         }
865         if (remainingBitsCount) {
866             srcValue = *src8;
867             for (j = 0; j < remainingBitsCount; j++) {
868                 *dstByte++ = (srcValue & 0x80) ? 0xFF : 0;
869                 srcValue <<= 1;
870             }
871         }
872     }
873 }
874 
875 #define Grey4ToAlpha255(value) (((value) << 4) + ((value) >> 3))
876 
CopyGrey4ToGrey8(const void * srcImage,int srcRowBytes,void * dstImage,int dstRowBytes,int width,int height)877 static void CopyGrey4ToGrey8(const void* srcImage, int srcRowBytes,
878                 void* dstImage, int dstRowBytes, int width, int height) {
879      const UInt8* srcRow = (UInt8*) srcImage;
880      UInt8* dstRow = (UInt8*) dstImage;
881      int i;
882 
883      while (height--) {
884          const UInt8* src8 = srcRow;
885          UInt8* dstByte = dstRow;
886          unsigned srcValue;
887 
888          srcRow += srcRowBytes;
889          dstRow += dstRowBytes;
890 
891          for (i = 0; i < width; i++) {
892              srcValue = *src8++;
893              *dstByte++ = Grey4ToAlpha255(srcValue & 0x0f);
894              *dstByte++ = Grey4ToAlpha255(srcValue >> 4);
895          }
896      }
897 }
898 
899 /* We need it because FT rows are often padded to 4 byte boundaries
900     and our internal format is not padded */
CopyFTSubpixelToSubpixel(const void * srcImage,int srcRowBytes,void * dstImage,int dstRowBytes,int width,int height)901 static void CopyFTSubpixelToSubpixel(const void* srcImage, int srcRowBytes,
902                                      void* dstImage, int dstRowBytes,
903                                      int width, int height) {
904     unsigned char *srcRow = (unsigned char *) srcImage;
905     unsigned char *dstRow = (unsigned char *) dstImage;
906 
907     while (height--) {
908         memcpy(dstRow, srcRow, width);
909         srcRow += srcRowBytes;
910         dstRow += dstRowBytes;
911     }
912 }
913 
914 /* We need it because FT rows are often padded to 4 byte boundaries
915    and our internal format is not padded */
CopyFTSubpixelVToSubpixel(const void * srcImage,int srcRowBytes,void * dstImage,int dstRowBytes,int width,int height)916 static void CopyFTSubpixelVToSubpixel(const void* srcImage, int srcRowBytes,
917                                       void* dstImage, int dstRowBytes,
918                                       int width, int height) {
919     unsigned char *srcRow = (unsigned char *) srcImage, *srcByte;
920     unsigned char *dstRow = (unsigned char *) dstImage, *dstByte;
921     int i;
922 
923     while (height > 0) {
924         srcByte = srcRow;
925         dstByte = dstRow;
926         for (i = 0; i < width; i++) {
927             *dstByte++ = *srcByte;
928             *dstByte++ = *(srcByte + srcRowBytes);
929             *dstByte++ = *(srcByte + 2*srcRowBytes);
930             srcByte++;
931         }
932         srcRow += 3*srcRowBytes;
933         dstRow += dstRowBytes;
934         height -= 3;
935     }
936 }
937 
938 
939 /* JDK does not use glyph images for fonts with a
940  * pixel size > 100 (see THRESHOLD in OutlineTextRenderer.java)
941  * so if the glyph bitmap image dimension is > 1024 pixels,
942  * something is up.
943  */
944 #define MAX_GLYPH_DIM 1024
945 
946 /*
947  * Class:     sun_font_FreetypeFontScaler
948  * Method:    getGlyphImageNative
949  * Signature: (Lsun/font/Font2D;JI)J
950  */
951 JNIEXPORT jlong JNICALL
Java_sun_font_FreetypeFontScaler_getGlyphImageNative(JNIEnv * env,jobject scaler,jobject font2D,jlong pScalerContext,jlong pScaler,jint glyphCode)952 Java_sun_font_FreetypeFontScaler_getGlyphImageNative(
953         JNIEnv *env, jobject scaler, jobject font2D,
954         jlong pScalerContext, jlong pScaler, jint glyphCode) {
955 
956     return getGlyphImageNativeInternal(
957         env, scaler, font2D,
958         pScalerContext, pScaler, glyphCode, JNI_TRUE);
959 }
960 
961 static jlong
getGlyphImageNativeInternal(JNIEnv * env,jobject scaler,jobject font2D,jlong pScalerContext,jlong pScaler,jint glyphCode,jboolean renderImage)962      getGlyphImageNativeInternal(
963         JNIEnv *env, jobject scaler, jobject font2D,
964         jlong pScalerContext, jlong pScaler, jint glyphCode,
965         jboolean renderImage) {
966 
967     int error, imageSize;
968     UInt16 width, height;
969     GlyphInfo *glyphInfo;
970     int renderFlags = FT_LOAD_DEFAULT, target;
971     FT_GlyphSlot ftglyph;
972 
973     FTScalerContext* context =
974         (FTScalerContext*) jlong_to_ptr(pScalerContext);
975     FTScalerInfo *scalerInfo =
976              (FTScalerInfo*) jlong_to_ptr(pScaler);
977 
978     if (isNullScalerContext(context) || scalerInfo == NULL) {
979         return ptr_to_jlong(getNullGlyphImage());
980     }
981 
982     error = setupFTContext(env, font2D, scalerInfo, context);
983     if (error) {
984         invalidateJavaScaler(env, scaler, scalerInfo);
985         return ptr_to_jlong(getNullGlyphImage());
986     }
987 
988     /*
989      * When using Fractional metrics (linearly scaling advances) and
990      * greyscale antialiasing, disable hinting so that the glyph shapes
991      * are constant as size increases. This is good for animation as well
992      * as being compatible with what happened in earlier JDK versions
993      * which did not use freetype.
994      */
995     if (context->aaType == TEXT_AA_ON && context->fmType == TEXT_FM_ON) {
996          renderFlags |= FT_LOAD_NO_HINTING;
997      }
998 
999     RenderingProperties renderingProperties;
1000     readFontconfig((const FcChar8 *) scalerInfo->face->family_name,
1001                    context->ptsz, context->aaType, &renderingProperties);
1002 
1003     FT_Library_SetLcdFilter(scalerInfo->library, renderingProperties.ftLcdFilter);
1004     error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderingProperties.ftLoadFlags);
1005     if (error) {
1006         //do not destroy scaler yet.
1007         //this can be problem of particular context (e.g. with bad transform)
1008         return ptr_to_jlong(getNullGlyphImage());
1009     }
1010 
1011     ftglyph = scalerInfo->face->glyph;
1012 
1013     /* apply styles */
1014     if (context->doBold) { /* if bold style */
1015         FT_GlyphSlot_Embolden(ftglyph);
1016     }
1017 
1018     /* generate bitmap if it is not done yet
1019      e.g. if algorithmic styling is performed and style was added to outline */
1020     if (renderImage && (ftglyph->format == FT_GLYPH_FORMAT_OUTLINE)) {
1021         FT_BBox bbox;
1022         FT_Outline_Get_CBox(&(ftglyph->outline), &bbox);
1023         int w = (int)((bbox.xMax>>6)-(bbox.xMin>>6));
1024         int h = (int)((bbox.yMax>>6)-(bbox.yMin>>6));
1025         if (w > MAX_GLYPH_DIM || h > MAX_GLYPH_DIM) {
1026             glyphInfo = getNullGlyphImage();
1027             return ptr_to_jlong(glyphInfo);
1028         }
1029     }
1030     error = FT_Render_Glyph(ftglyph, renderingProperties.ftRenderMode);
1031     if (error != 0) {
1032         return ptr_to_jlong(getNullGlyphImage());
1033     }
1034 
1035     if (renderImage) {
1036         width  = (UInt16) ftglyph->bitmap.width;
1037         height = (UInt16) ftglyph->bitmap.rows;
1038             if (width > MAX_GLYPH_DIM || height > MAX_GLYPH_DIM) {
1039               glyphInfo = getNullGlyphImage();
1040               return ptr_to_jlong(glyphInfo);
1041             }
1042      } else {
1043         width = 0;
1044         height = 0;
1045      }
1046 
1047 
1048     imageSize = width*height;
1049     glyphInfo = (GlyphInfo*) malloc(sizeof(GlyphInfo) + imageSize);
1050     if (glyphInfo == NULL) {
1051         glyphInfo = getNullGlyphImage();
1052         return ptr_to_jlong(glyphInfo);
1053     }
1054     glyphInfo->cellInfo  = NULL;
1055     glyphInfo->managed   = UNMANAGED_GLYPH;
1056     glyphInfo->rowBytes  = width;
1057     glyphInfo->width     = width;
1058     glyphInfo->height    = height;
1059 
1060     if (renderImage) {
1061         glyphInfo->topLeftX  = (float)  ftglyph->bitmap_left;
1062         glyphInfo->topLeftY  = (float) -ftglyph->bitmap_top;
1063 
1064         if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD) {
1065             glyphInfo->width = width/3;
1066         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD_V) {
1067             glyphInfo->height = glyphInfo->height/3;
1068         }
1069     }
1070 
1071     if (context->fmType == TEXT_FM_ON) {
1072         double advh = FTFixedToFloat(ftglyph->linearHoriAdvance);
1073         glyphInfo->advanceX =
1074             (float) (advh * FTFixedToFloat(context->transform.xx));
1075         glyphInfo->advanceY =
1076             (float) (advh * FTFixedToFloat(context->transform.xy));
1077     } else {
1078         if (!ftglyph->advance.y) {
1079             glyphInfo->advanceX =
1080                 (float) FT26Dot6ToInt(ftglyph->advance.x);
1081             glyphInfo->advanceY = 0;
1082         } else if (!ftglyph->advance.x) {
1083             glyphInfo->advanceX = 0;
1084             glyphInfo->advanceY =
1085                 (float) FT26Dot6ToInt(-ftglyph->advance.y);
1086         } else {
1087             glyphInfo->advanceX = FT26Dot6ToFloat(ftglyph->advance.x);
1088             glyphInfo->advanceY = FT26Dot6ToFloat(-ftglyph->advance.y);
1089         }
1090     }
1091 
1092     if (imageSize == 0) {
1093         glyphInfo->image = NULL;
1094     } else {
1095         glyphInfo->image = (unsigned char*) glyphInfo + sizeof(GlyphInfo);
1096         //convert result to output format
1097         //output format is either 3 bytes per pixel (for subpixel modes)
1098         // or 1 byte per pixel for AA and B&W
1099         if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_MONO) {
1100             /* convert from 8 pixels per byte to 1 byte per pixel */
1101             CopyBW2Grey8(ftglyph->bitmap.buffer,
1102                          ftglyph->bitmap.pitch,
1103                          (void *) glyphInfo->image,
1104                          width,
1105                          width,
1106                          height);
1107         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_GRAY) {
1108             /* byte per pixel to byte per pixel => just copy */
1109             memcpy(glyphInfo->image, ftglyph->bitmap.buffer, imageSize);
1110         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_GRAY4) {
1111             /* 4 bits per pixel to byte per pixel */
1112             CopyGrey4ToGrey8(ftglyph->bitmap.buffer,
1113                              ftglyph->bitmap.pitch,
1114                              (void *) glyphInfo->image,
1115                              width,
1116                              width,
1117                              height);
1118         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD) {
1119             /* 3 bytes per pixel to 3 bytes per pixel */
1120             CopyFTSubpixelToSubpixel(ftglyph->bitmap.buffer,
1121                                      ftglyph->bitmap.pitch,
1122                                      (void *) glyphInfo->image,
1123                                      width,
1124                                      width,
1125                                      height);
1126         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD_V) {
1127             /* 3 bytes per pixel to 3 bytes per pixel */
1128             CopyFTSubpixelVToSubpixel(ftglyph->bitmap.buffer,
1129                                       ftglyph->bitmap.pitch,
1130                                       (void *) glyphInfo->image,
1131                                       width*3,
1132                                       width,
1133                                       height);
1134             glyphInfo->rowBytes *=3;
1135         } else {
1136             free(glyphInfo);
1137             glyphInfo = getNullGlyphImage();
1138         }
1139     }
1140 
1141     return ptr_to_jlong(glyphInfo);
1142 }
1143 
1144 /*
1145  * Class:     sun_font_FreetypeFontScaler
1146  * Method:    disposeNativeScaler
1147  * Signature: (J)V
1148  */
1149 JNIEXPORT void JNICALL
Java_sun_font_FreetypeFontScaler_disposeNativeScaler(JNIEnv * env,jobject scaler,jobject font2D,jlong pScaler)1150 Java_sun_font_FreetypeFontScaler_disposeNativeScaler(
1151         JNIEnv *env, jobject scaler, jobject font2D, jlong pScaler) {
1152     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
1153 
1154     /* Freetype functions *may* cause callback to java
1155        that can use cached values. Make sure our cache is up to date.
1156        NB: scaler context is not important at this point, can use NULL. */
1157     int errCode = setupFTContext(env, font2D, scalerInfo, NULL);
1158     if (errCode) {
1159         return;
1160     }
1161 
1162     freeNativeResources(env, scalerInfo);
1163 }
1164 
1165 /*
1166  * Class:     sun_font_FreetypeFontScaler
1167  * Method:    getNumGlyphsNative
1168  * Signature: ()I
1169  */
1170 JNIEXPORT jint JNICALL
Java_sun_font_FreetypeFontScaler_getNumGlyphsNative(JNIEnv * env,jobject scaler,jlong pScaler)1171 Java_sun_font_FreetypeFontScaler_getNumGlyphsNative(
1172         JNIEnv *env, jobject scaler, jlong pScaler) {
1173     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
1174 
1175     if (scalerInfo == NULL || scalerInfo->face == NULL) { /* bad/null scaler */
1176         /* null scaler can render 1 glyph - "missing glyph" with code 0
1177            (all glyph codes requested by user are mapped to code 0 at
1178            validation step) */
1179         invalidateJavaScaler(env, scaler, scalerInfo);
1180         return (jint) 1;
1181     }
1182 
1183     return (jint) scalerInfo->face->num_glyphs;
1184 }
1185 
1186 /*
1187  * Class:     sun_font_FreetypeFontScaler
1188  * Method:    getMissingGlyphCodeNative
1189  * Signature: ()I
1190  */
1191 JNIEXPORT jint JNICALL
Java_sun_font_FreetypeFontScaler_getMissingGlyphCodeNative(JNIEnv * env,jobject scaler,jlong pScaler)1192 Java_sun_font_FreetypeFontScaler_getMissingGlyphCodeNative(
1193         JNIEnv *env, jobject scaler, jlong pScaler) {
1194 
1195     /* Is it always 0 for freetype? */
1196     return 0;
1197 }
1198 
1199 /*
1200  * Class:     sun_font_FreetypeFontScaler
1201  * Method:    getGlyphCodeNative
1202  * Signature: (C)I
1203  */
1204 JNIEXPORT jint JNICALL
Java_sun_font_FreetypeFontScaler_getGlyphCodeNative(JNIEnv * env,jobject scaler,jobject font2D,jlong pScaler,jchar charCode)1205 Java_sun_font_FreetypeFontScaler_getGlyphCodeNative(
1206         JNIEnv *env, jobject scaler,
1207         jobject font2D, jlong pScaler, jchar charCode) {
1208 
1209     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
1210     int errCode;
1211 
1212     if (scaler == NULL || scalerInfo->face == NULL) { /* bad/null scaler */
1213         invalidateJavaScaler(env, scaler, scalerInfo);
1214         return 0;
1215     }
1216 
1217     /* Freetype functions *may* cause callback to java
1218        that can use cached values. Make sure our cache is up to date.
1219        Scaler context is not important here, can use NULL. */
1220     errCode = setupFTContext(env, font2D, scalerInfo, NULL);
1221     if (errCode) {
1222         return 0;
1223     }
1224 
1225     return FT_Get_Char_Index(scalerInfo->face, charCode);
1226 }
1227 
1228 
1229 #define FloatToF26Dot6(x) ((unsigned int) ((x)*64))
1230 
getFTOutline(JNIEnv * env,jobject font2D,FTScalerContext * context,FTScalerInfo * scalerInfo,jint glyphCode,jfloat xpos,jfloat ypos)1231 static FT_Outline* getFTOutline(JNIEnv* env, jobject font2D,
1232         FTScalerContext *context, FTScalerInfo* scalerInfo,
1233         jint glyphCode, jfloat xpos, jfloat ypos) {
1234     int renderFlags;
1235     FT_Error error;
1236     FT_GlyphSlot ftglyph;
1237 
1238     if (glyphCode >= INVISIBLE_GLYPHS ||
1239             isNullScalerContext(context) || scalerInfo == NULL) {
1240         return NULL;
1241     }
1242 
1243     error = setupFTContext(env, font2D, scalerInfo, context);
1244     if (error) {
1245         return NULL;
1246     }
1247 
1248     RenderingProperties renderingProperties;
1249     readFontconfig((const FcChar8 *) scalerInfo->face->family_name,
1250                    context->ptsz, context->aaType, &renderingProperties);
1251 
1252     error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderingProperties.ftLoadFlags);
1253     if (error) {
1254         return NULL;
1255     }
1256 
1257     ftglyph = scalerInfo->face->glyph;
1258 
1259     /* apply styles */
1260     if (context->doBold) { /* if bold style */
1261         FT_GlyphSlot_Embolden(ftglyph);
1262     }
1263 
1264     FT_Outline_Translate(&ftglyph->outline,
1265                          FloatToF26Dot6(xpos),
1266                          -FloatToF26Dot6(ypos));
1267 
1268     return &ftglyph->outline;
1269 }
1270 
1271 #define F26Dot6ToFloat(n) (((float)(n))/((float) 64))
1272 
1273 /* Types of GeneralPath segments.
1274    TODO: pull constants from other place? */
1275 
1276 #define SEG_UNKNOWN -1
1277 #define SEG_MOVETO   0
1278 #define SEG_LINETO   1
1279 #define SEG_QUADTO   2
1280 #define SEG_CUBICTO  3
1281 #define SEG_CLOSE    4
1282 
1283 #define WIND_NON_ZERO 0
1284 #define WIND_EVEN_ODD 1
1285 
1286 /* Placeholder to accumulate GeneralPath data */
1287 typedef struct {
1288     jint numTypes;
1289     jint numCoords;
1290     jint lenTypes;
1291     jint lenCoords;
1292     jint wr;
1293     jbyte* pointTypes;
1294     jfloat* pointCoords;
1295 } GPData;
1296 
1297 /* returns 0 on failure */
allocateSpaceForGP(GPData * gpdata,int npoints,int ncontours)1298 static int allocateSpaceForGP(GPData* gpdata, int npoints, int ncontours) {
1299     int maxTypes, maxCoords;
1300 
1301     /* we may have up to N intermediate points per contour
1302        (and for each point can actually cause new curve to be generated)
1303        In addition we can also have 2 extra point per outline.
1304      */
1305     maxTypes  = 2*npoints  + 2*ncontours;
1306     maxCoords = 4*(npoints + 2*ncontours); //we may need to insert
1307                                            //up to n-1 intermediate points
1308 
1309     /* first usage - allocate space and intialize all fields */
1310     if (gpdata->pointTypes == NULL || gpdata->pointCoords == NULL) {
1311         gpdata->lenTypes  = maxTypes;
1312         gpdata->lenCoords = maxCoords;
1313         gpdata->pointTypes  = (jbyte*)
1314              malloc(gpdata->lenTypes*sizeof(jbyte));
1315         gpdata->pointCoords = (jfloat*)
1316              malloc(gpdata->lenCoords*sizeof(jfloat));
1317         gpdata->numTypes = 0;
1318         gpdata->numCoords = 0;
1319         gpdata->wr = WIND_NON_ZERO; /* By default, outlines are filled
1320                                        using the non-zero winding rule. */
1321     } else {
1322         /* do we have enough space? */
1323         if (gpdata->lenTypes - gpdata->numTypes < maxTypes) {
1324             gpdata->lenTypes  += maxTypes;
1325             gpdata->pointTypes  = (jbyte*)
1326               realloc(gpdata->pointTypes, gpdata->lenTypes*sizeof(jbyte));
1327         }
1328 
1329         if (gpdata->lenCoords - gpdata->numCoords < maxCoords) {
1330             gpdata->lenCoords += maxCoords;
1331             gpdata->pointCoords = (jfloat*)
1332               realloc(gpdata->pointCoords, gpdata->lenCoords*sizeof(jfloat));
1333         }
1334     }
1335 
1336     /* failure if any of mallocs failed */
1337     if (gpdata->pointTypes == NULL ||  gpdata->pointCoords == NULL)
1338         return 0;
1339     else
1340         return 1;
1341 }
1342 
addSeg(GPData * gp,jbyte type)1343 static void addSeg(GPData *gp, jbyte type) {
1344     gp->pointTypes[gp->numTypes++] = type;
1345 }
1346 
addCoords(GPData * gp,FT_Vector * p)1347 static void addCoords(GPData *gp, FT_Vector *p) {
1348     gp->pointCoords[gp->numCoords++] =  F26Dot6ToFloat(p->x);
1349     gp->pointCoords[gp->numCoords++] = -F26Dot6ToFloat(p->y);
1350 }
1351 
moveTo(FT_Vector * to,GPData * gp)1352 static int moveTo(FT_Vector *to, GPData *gp) {
1353     if (gp->numCoords)
1354         addSeg(gp, SEG_CLOSE);
1355     addCoords(gp, to);
1356     addSeg(gp, SEG_MOVETO);
1357     return FT_Err_Ok;
1358 }
1359 
lineTo(FT_Vector * to,GPData * gp)1360 static int lineTo(FT_Vector *to, GPData *gp) {
1361     addCoords(gp, to);
1362     addSeg(gp, SEG_LINETO);
1363     return FT_Err_Ok;
1364 }
1365 
conicTo(FT_Vector * control,FT_Vector * to,GPData * gp)1366 static int conicTo(FT_Vector *control, FT_Vector *to, GPData *gp) {
1367     addCoords(gp, control);
1368     addCoords(gp, to);
1369     addSeg(gp, SEG_QUADTO);
1370     return FT_Err_Ok;
1371 }
1372 
cubicTo(FT_Vector * control1,FT_Vector * control2,FT_Vector * to,GPData * gp)1373 static int cubicTo(FT_Vector *control1,
1374                    FT_Vector *control2,
1375                    FT_Vector *to,
1376                    GPData    *gp) {
1377     addCoords(gp, control1);
1378     addCoords(gp, control2);
1379     addCoords(gp, to);
1380     addSeg(gp, SEG_CUBICTO);
1381     return FT_Err_Ok;
1382 }
1383 
addToGP(GPData * gpdata,FT_Outline * outline)1384 static void addToGP(GPData* gpdata, FT_Outline*outline) {
1385     static const FT_Outline_Funcs outline_funcs = {
1386         (FT_Outline_MoveToFunc) moveTo,
1387         (FT_Outline_LineToFunc) lineTo,
1388         (FT_Outline_ConicToFunc) conicTo,
1389         (FT_Outline_CubicToFunc) cubicTo,
1390         0, /* shift */
1391         0, /* delta */
1392     };
1393 
1394     FT_Outline_Decompose(outline, &outline_funcs, gpdata);
1395     if (gpdata->numCoords)
1396         addSeg(gpdata, SEG_CLOSE);
1397 
1398     /* If set to 1, the outline will be filled using the even-odd fill rule */
1399     if (outline->flags & FT_OUTLINE_EVEN_ODD_FILL) {
1400         gpdata->wr = WIND_EVEN_ODD;
1401     }
1402 }
1403 
freeGP(GPData * gpdata)1404 static void freeGP(GPData* gpdata) {
1405     if (gpdata->pointCoords != NULL) {
1406         free(gpdata->pointCoords);
1407         gpdata->pointCoords = NULL;
1408         gpdata->numCoords = 0;
1409         gpdata->lenCoords = 0;
1410     }
1411     if (gpdata->pointTypes != NULL) {
1412         free(gpdata->pointTypes);
1413         gpdata->pointTypes = NULL;
1414         gpdata->numTypes = 0;
1415         gpdata->lenTypes = 0;
1416     }
1417 }
1418 
getGlyphGeneralPath(JNIEnv * env,jobject font2D,FTScalerContext * context,FTScalerInfo * scalerInfo,jint glyphCode,jfloat xpos,jfloat ypos)1419 static jobject getGlyphGeneralPath(JNIEnv* env, jobject font2D,
1420         FTScalerContext *context, FTScalerInfo *scalerInfo,
1421         jint glyphCode, jfloat xpos, jfloat ypos) {
1422 
1423     FT_Outline* outline;
1424     jobject gp = NULL;
1425     jbyteArray types;
1426     jfloatArray coords;
1427     GPData gpdata;
1428 
1429     outline = getFTOutline(env, font2D, context, scalerInfo,
1430                            glyphCode, xpos, ypos);
1431 
1432     if (outline == NULL || outline->n_points == 0) {
1433         return gp;
1434     }
1435 
1436     gpdata.pointTypes  = NULL;
1437     gpdata.pointCoords = NULL;
1438     if (!allocateSpaceForGP(&gpdata, outline->n_points, outline->n_contours)) {
1439         return gp;
1440     }
1441 
1442     addToGP(&gpdata, outline);
1443 
1444     types  = (*env)->NewByteArray(env, gpdata.numTypes);
1445     coords = (*env)->NewFloatArray(env, gpdata.numCoords);
1446 
1447     if (types && coords) {
1448         (*env)->SetByteArrayRegion(env, types, 0,
1449                                    gpdata.numTypes,
1450                                    gpdata.pointTypes);
1451         (*env)->SetFloatArrayRegion(env, coords, 0,
1452                                     gpdata.numCoords,
1453                                     gpdata.pointCoords);
1454         gp = (*env)->NewObject(env,
1455                                sunFontIDs.gpClass,
1456                                sunFontIDs.gpCtr,
1457                                gpdata.wr,
1458                                types,
1459                                gpdata.numTypes,
1460                                coords,
1461                                gpdata.numCoords);
1462     }
1463 
1464     freeGP(&gpdata);
1465 
1466     return gp;
1467 }
1468 
1469 /*
1470  * Class:     sun_font_FreetypeFontScaler
1471  * Method:    getGlyphOutlineNative
1472  * Signature: (Lsun/font/Font2D;JIFF)Ljava/awt/geom/GeneralPath;
1473  */
1474 JNIEXPORT jobject JNICALL
Java_sun_font_FreetypeFontScaler_getGlyphOutlineNative(JNIEnv * env,jobject scaler,jobject font2D,jlong pScalerContext,jlong pScaler,jint glyphCode,jfloat xpos,jfloat ypos)1475 Java_sun_font_FreetypeFontScaler_getGlyphOutlineNative(
1476       JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
1477       jlong pScaler, jint glyphCode, jfloat xpos, jfloat ypos) {
1478 
1479     FTScalerContext *context =
1480          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1481     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
1482 
1483     jobject gp = getGlyphGeneralPath(env,
1484                                font2D,
1485                                context,
1486                                scalerInfo,
1487                                glyphCode,
1488                                xpos,
1489                                ypos);
1490     if (gp == NULL) { /* can be legal */
1491         gp = (*env)->NewObject(env,
1492                                sunFontIDs.gpClass,
1493                                sunFontIDs.gpCtrEmpty);
1494     }
1495     return gp;
1496 }
1497 
1498 /*
1499  * Class:     sun_font_FreetypeFontScaler
1500  * Method:    getGlyphOutlineBoundsNative
1501  * Signature: (Lsun/font/Font2D;JI)Ljava/awt/geom/Rectangle2D/Float;
1502  */
1503 JNIEXPORT jobject JNICALL
Java_sun_font_FreetypeFontScaler_getGlyphOutlineBoundsNative(JNIEnv * env,jobject scaler,jobject font2D,jlong pScalerContext,jlong pScaler,jint glyphCode)1504 Java_sun_font_FreetypeFontScaler_getGlyphOutlineBoundsNative(
1505         JNIEnv *env, jobject scaler, jobject font2D,
1506         jlong pScalerContext, jlong pScaler, jint glyphCode) {
1507 
1508     FT_Outline *outline;
1509     FT_BBox bbox;
1510     int error;
1511     jobject bounds;
1512 
1513     FTScalerContext *context =
1514          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1515     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
1516 
1517     outline = getFTOutline(env, font2D, context, scalerInfo, glyphCode, 0, 0);
1518     if (outline == NULL || outline->n_points == 0) {
1519         /* it is legal case, e.g. invisible glyph */
1520         bounds = (*env)->NewObject(env,
1521                                  sunFontIDs.rect2DFloatClass,
1522                                  sunFontIDs.rect2DFloatCtr);
1523         return bounds;
1524     }
1525 
1526     error = FT_Outline_Get_BBox(outline, &bbox);
1527 
1528     //convert bbox
1529     if (error || bbox.xMin >= bbox.xMax || bbox.yMin >= bbox.yMax) {
1530         bounds = (*env)->NewObject(env,
1531                                    sunFontIDs.rect2DFloatClass,
1532                                    sunFontIDs.rect2DFloatCtr);
1533     } else {
1534         bounds = (*env)->NewObject(env,
1535                                    sunFontIDs.rect2DFloatClass,
1536                                    sunFontIDs.rect2DFloatCtr4,
1537                                    F26Dot6ToFloat(bbox.xMin),
1538                                    F26Dot6ToFloat(-bbox.yMax),
1539                                    F26Dot6ToFloat(bbox.xMax-bbox.xMin),
1540                                    F26Dot6ToFloat(bbox.yMax-bbox.yMin));
1541     }
1542 
1543     return bounds;
1544 }
1545 
1546 /*
1547  * Class:     sun_font_FreetypeFontScaler
1548  * Method:    getGlyphVectorOutlineNative
1549  * Signature: (Lsun/font/Font2D;J[IIFF)Ljava/awt/geom/GeneralPath;
1550  */
1551 JNIEXPORT jobject
1552 JNICALL
Java_sun_font_FreetypeFontScaler_getGlyphVectorOutlineNative(JNIEnv * env,jobject scaler,jobject font2D,jlong pScalerContext,jlong pScaler,jintArray glyphArray,jint numGlyphs,jfloat xpos,jfloat ypos)1553 Java_sun_font_FreetypeFontScaler_getGlyphVectorOutlineNative(
1554         JNIEnv *env, jobject scaler, jobject font2D,
1555         jlong pScalerContext, jlong pScaler,
1556         jintArray glyphArray, jint numGlyphs, jfloat xpos, jfloat ypos) {
1557 
1558     FT_Outline* outline;
1559     jobject gp = NULL;
1560     jbyteArray types;
1561     jfloatArray coords;
1562     GPData gpdata;
1563     int i;
1564     jint *glyphs;
1565 
1566     FTScalerContext *context =
1567          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1568     FTScalerInfo *scalerInfo =
1569              (FTScalerInfo*) jlong_to_ptr(pScaler);
1570 
1571     glyphs = NULL;
1572     if (numGlyphs > 0 && 0xffffffffu / sizeof(jint) >= numGlyphs) {
1573         glyphs = (jint*) malloc(numGlyphs*sizeof(jint));
1574     }
1575     if (glyphs == NULL) {
1576         // We reach here if:
1577         // 1. numGlyphs <= 0,
1578         // 2. overflow check failed, or
1579         // 3. malloc failed.
1580         gp = (*env)->NewObject(env, sunFontIDs.gpClass, sunFontIDs.gpCtrEmpty);
1581         return gp;
1582     }
1583 
1584     (*env)->GetIntArrayRegion(env, glyphArray, 0, numGlyphs, glyphs);
1585 
1586     gpdata.numCoords = 0;
1587     for (i=0; i<numGlyphs;i++) {
1588         if (glyphs[i] >= INVISIBLE_GLYPHS) {
1589             continue;
1590         }
1591         outline = getFTOutline(env,
1592                                font2D,
1593                                context,
1594                                scalerInfo,
1595                                glyphs[i],
1596                                xpos, ypos);
1597 
1598         if (outline == NULL || outline->n_points == 0) {
1599             continue;
1600         }
1601 
1602         gpdata.pointTypes  = NULL;
1603         gpdata.pointCoords = NULL;
1604         if (!allocateSpaceForGP(&gpdata, outline->n_points,
1605                                 outline->n_contours)) {
1606             break;
1607         }
1608 
1609         addToGP(&gpdata, outline);
1610     }
1611     free(glyphs);
1612 
1613     if (gpdata.numCoords != 0) {
1614       types = (*env)->NewByteArray(env, gpdata.numTypes);
1615       coords = (*env)->NewFloatArray(env, gpdata.numCoords);
1616 
1617       if (types && coords) {
1618         (*env)->SetByteArrayRegion(env, types, 0,
1619                                    gpdata.numTypes, gpdata.pointTypes);
1620         (*env)->SetFloatArrayRegion(env, coords, 0,
1621                                     gpdata.numCoords, gpdata.pointCoords);
1622 
1623         gp=(*env)->NewObject(env,
1624                              sunFontIDs.gpClass,
1625                              sunFontIDs.gpCtr,
1626                              gpdata.wr,
1627                              types,
1628                              gpdata.numTypes,
1629                              coords,
1630                              gpdata.numCoords);
1631         return gp;
1632       }
1633     }
1634     return (*env)->NewObject(env, sunFontIDs.gpClass, sunFontIDs.gpCtrEmpty);
1635 }
1636 
1637 JNIEXPORT jlong JNICALL
Java_sun_font_FreetypeFontScaler_getUnitsPerEMNative(JNIEnv * env,jobject scaler,jlong pScaler)1638 Java_sun_font_FreetypeFontScaler_getUnitsPerEMNative(
1639         JNIEnv *env, jobject scaler, jlong pScaler) {
1640 
1641     FTScalerInfo *s = (FTScalerInfo* ) jlong_to_ptr(pScaler);
1642 
1643     /* Freetype doc says:
1644      The number of font units per EM square for this face.
1645      This is typically 2048 for TrueType fonts, and 1000 for Type 1 fonts.
1646      Only relevant for scalable formats.
1647      However, layout engine might be not tested with anything but 2048.
1648 
1649      NB: test it! */
1650     if (s != NULL) {
1651         return s->face->units_per_EM;
1652     }
1653     return 2048;
1654 }
1655 
1656 /* This native method is called by the OpenType layout engine. */
1657 JNIEXPORT jobject JNICALL
Java_sun_font_FreetypeFontScaler_getGlyphPointNative(JNIEnv * env,jobject scaler,jobject font2D,jlong pScalerContext,jlong pScaler,jint glyphCode,jint pointNumber)1658 Java_sun_font_FreetypeFontScaler_getGlyphPointNative(
1659         JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
1660         jlong pScaler, jint glyphCode, jint pointNumber) {
1661 
1662     FT_Outline* outline;
1663     jobject point = NULL;
1664     jfloat x=0, y=0;
1665     FTScalerContext *context =
1666          (FTScalerContext*) jlong_to_ptr(pScalerContext);
1667     FTScalerInfo *scalerInfo = (FTScalerInfo*) jlong_to_ptr(pScaler);
1668 
1669     outline = getFTOutline(env, font2D, context, scalerInfo, glyphCode, 0, 0);
1670 
1671     if (outline != NULL && outline->n_points > pointNumber) {
1672         x =  F26Dot6ToFloat(outline->points[pointNumber].x);
1673         y = -F26Dot6ToFloat(outline->points[pointNumber].y);
1674     }
1675 
1676     return (*env)->NewObject(env, sunFontIDs.pt2DFloatClass,
1677                              sunFontIDs.pt2DFloatCtr, x, y);
1678 }
1679