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