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  FT26Dot6ToInt(x) (((int)(x)) >> 6)
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 } FTScalerInfo;
77 
78 typedef struct FTScalerContext {
79     FT_Matrix  transform;     /* glyph transform, including device transform */
80     jboolean   useSbits;      /* sbit usage enabled? */
81     jint       aaType;        /* antialiasing mode (off/on/grey/lcd) */
82     jint       fmType;        /* fractional metrics - on/off */
83     jboolean   doBold;        /* perform algorithmic bolding? */
84     jboolean   doItalize;     /* perform algorithmic italicizing? */
85     int        renderFlags;   /* configuration specific to particular engine */
86     int        pathType;
87     int        ptsz;          /* size in points */
88 } FTScalerContext;
89 
90 #ifdef DEBUG
91 /* These are referenced in the freetype sources if DEBUG macro is defined.
92    To simplify work with debuging version of freetype we define
93    them here. */
94 int z_verbose;
z_error(char * s)95 void z_error(char *s) {}
96 #endif
97 
98 /**************** Error handling utilities *****************/
99 
100 static jmethodID invalidateScalerMID;
101 
102 JNIEXPORT void JNICALL
Java_sun_font_FreetypeFontScaler_initIDs(JNIEnv * env,jobject scaler,jclass FFSClass)103 Java_sun_font_FreetypeFontScaler_initIDs(
104         JNIEnv *env, jobject scaler, jclass FFSClass) {
105     invalidateScalerMID =
106         (*env)->GetMethodID(env, FFSClass, "invalidateScaler", "()V");
107 }
108 
freeNativeResources(JNIEnv * env,FTScalerInfo * scalerInfo)109 static void freeNativeResources(JNIEnv *env, FTScalerInfo* scalerInfo) {
110 
111     if (scalerInfo == NULL)
112         return;
113 
114     // FT_Done_Face always closes the stream, but only frees the memory
115     // of the data structure if it was internally allocated by FT.
116     // We hold on to a pointer to the stream structure if we provide it
117     // ourselves, so that we can free it here.
118     FT_Done_Face(scalerInfo->face);
119     FT_Done_FreeType(scalerInfo->library);
120 
121     if (scalerInfo->directBuffer != NULL) {
122         (*env)->DeleteGlobalRef(env, scalerInfo->directBuffer);
123     }
124 
125     if (scalerInfo->fontData != NULL) {
126         free(scalerInfo->fontData);
127     }
128 
129     if (scalerInfo->faceStream != NULL) {
130         free(scalerInfo->faceStream);
131     }
132     free(scalerInfo);
133 }
134 
135 /* invalidates state of java scaler object */
invalidateJavaScaler(JNIEnv * env,jobject scaler,FTScalerInfo * scalerInfo)136 static void invalidateJavaScaler(JNIEnv *env,
137                                  jobject scaler,
138                                  FTScalerInfo* scalerInfo) {
139     freeNativeResources(env, scalerInfo);
140     (*env)->CallVoidMethod(env, scaler, invalidateScalerMID);
141 }
142 
143 /******************* I/O handlers ***************************/
144 
145 #define FILEDATACACHESIZE 1024
146 
ReadTTFontFileFunc(FT_Stream stream,unsigned long offset,unsigned char * destBuffer,unsigned long numBytes)147 static unsigned long ReadTTFontFileFunc(FT_Stream stream,
148                                         unsigned long offset,
149                                         unsigned char* destBuffer,
150                                         unsigned long numBytes)
151 {
152     FTScalerInfo *scalerInfo = (FTScalerInfo *) stream->pathname.pointer;
153     JNIEnv* env = scalerInfo->env;
154     jobject bBuffer;
155     int bread = 0;
156 
157     /* A call with numBytes == 0 is a seek. It should return 0 if the
158      * seek position is within the file and non-zero otherwise.
159      * For all other cases, ie numBytes !=0, return the number of bytes
160      * actually read. This applies to truncated reads and also failed reads.
161      */
162 
163     if (numBytes == 0) {
164         if (offset > scalerInfo->fileSize) {
165             return -1;
166         } else {
167             return 0;
168        }
169     }
170 
171     if (offset + numBytes < offset) {
172         return 0; // ft should not do this, but just in case.
173     }
174 
175     if (offset >= scalerInfo->fileSize) {
176         return 0;
177     }
178 
179     if (offset + numBytes > scalerInfo->fileSize) {
180         numBytes = scalerInfo->fileSize - offset;
181     }
182 
183     /* Large reads will bypass the cache and data copying */
184     if (numBytes > FILEDATACACHESIZE) {
185         bBuffer = (*env)->NewDirectByteBuffer(env, destBuffer, numBytes);
186         if (bBuffer != NULL) {
187             bread = (*env)->CallIntMethod(env,
188                                           scalerInfo->font2D,
189                                           sunFontIDs.ttReadBlockMID,
190                                           bBuffer, offset, numBytes);
191             if (bread < 0) {
192                 return 0;
193             } else {
194                return bread;
195             }
196         } else {
197             /* We probably hit bug 4845371. For reasons that
198              * are currently unclear, the call stacks after the initial
199              * createScaler call that read large amounts of data seem to
200              * be OK and can create the byte buffer above, but this code
201              * is here just in case.
202              * 4845371 is fixed now so I don't expect this code path to
203              * ever get called but its harmless to leave it here on the
204              * small chance its needed.
205              */
206             jbyteArray byteArray = (jbyteArray)
207             (*env)->CallObjectMethod(env, scalerInfo->font2D,
208                                      sunFontIDs.ttReadBytesMID,
209                                      offset, numBytes);
210             /* If there's an OutofMemoryError then byteArray will be null */
211             if (byteArray == NULL) {
212                 return 0;
213             } else {
214                 jsize len = (*env)->GetArrayLength(env, byteArray);
215                 if (len < numBytes) {
216                     numBytes = len; // don't get more bytes than there are ..
217                 }
218                 (*env)->GetByteArrayRegion(env, byteArray,
219                                            0, numBytes, (jbyte*)destBuffer);
220                 return numBytes;
221             }
222         }
223     } /* Do we have a cache hit? */
224       else if (scalerInfo->fontDataOffset <= offset &&
225         scalerInfo->fontDataOffset + scalerInfo->fontDataLength >=
226                                                          offset + numBytes)
227     {
228         unsigned cacheOffset = offset - scalerInfo->fontDataOffset;
229 
230         memcpy(destBuffer, scalerInfo->fontData+(size_t)cacheOffset, numBytes);
231         return numBytes;
232     } else {
233         /* Must fill the cache */
234         scalerInfo->fontDataOffset = offset;
235         scalerInfo->fontDataLength =
236                  (offset + FILEDATACACHESIZE > scalerInfo->fileSize) ?
237                  scalerInfo->fileSize - offset : FILEDATACACHESIZE;
238         bBuffer = scalerInfo->directBuffer;
239         bread = (*env)->CallIntMethod(env, scalerInfo->font2D,
240                                       sunFontIDs.ttReadBlockMID,
241                                       bBuffer, offset,
242                                       scalerInfo->fontDataLength);
243         if (bread <= 0) {
244             return 0;
245         } else if (bread < numBytes) {
246            numBytes = bread;
247         }
248         memcpy(destBuffer, scalerInfo->fontData, numBytes);
249         return numBytes;
250     }
251 }
252 
253 typedef FT_Error (*FT_Prop_Set_Func)(FT_Library library,
254                                      const FT_String*  module_name,
255                                      const FT_String*  property_name,
256                                      const void*       value );
257 
258 /**
259  * Prefer the older v35 freetype byte code interpreter.
260  */
setInterpreterVersion(FT_Library library)261 static void setInterpreterVersion(FT_Library library) {
262 
263     char* props = getenv("FREETYPE_PROPERTIES");
264     int version = 35;
265     const char* module = "truetype";
266     const char* property = "interpreter-version";
267 
268     /* If some one is setting this, don't override it */
269     if (props != NULL && strstr(props, property)) {
270         return;
271     }
272     /*
273      * FT_Property_Set was introduced in 2.4.11.
274      * Some older supported Linux OSes may not include it so look
275      * this up dynamically.
276      * And if its not available it doesn't matter, since the reason
277      * we need it dates from 2.7.
278      * On Windows & Mac the library is always bundled so it is safe
279      * to use directly in those cases.
280      */
281 #if defined(_WIN32) || defined(__APPLE__)
282     FT_Property_Set(library, module, property, (void*)(&version));
283 #else
284     void *lib = dlopen("libfreetype.so", RTLD_LOCAL|RTLD_LAZY);
285     if (lib == NULL) {
286         lib = dlopen("libfreetype.so.6", RTLD_LOCAL|RTLD_LAZY);
287         if (lib == NULL) {
288             return;
289         }
290     }
291     FT_Prop_Set_Func func = (FT_Prop_Set_Func)dlsym(lib, "FT_Property_Set");
292     if (func != NULL) {
293         func(library, module, property, (void*)(&version));
294     }
295     dlclose(lib);
296 #endif
297 }
298 
299 /*
300  * Class:     sun_font_FreetypeFontScaler
301  * Method:    initNativeScaler
302  * Signature: (Lsun/font/Font2D;IIZI)J
303  */
304 JNIEXPORT jlong JNICALL
Java_sun_font_FreetypeFontScaler_initNativeScaler(JNIEnv * env,jobject scaler,jobject font2D,jint type,jint indexInCollection,jboolean supportsCJK,jint filesize)305 Java_sun_font_FreetypeFontScaler_initNativeScaler(
306         JNIEnv *env, jobject scaler, jobject font2D, jint type,
307         jint indexInCollection, jboolean supportsCJK, jint filesize) {
308     FTScalerInfo* scalerInfo = NULL;
309     FT_Open_Args ft_open_args;
310     int error;
311     jobject bBuffer;
312     scalerInfo = (FTScalerInfo*) calloc(1, sizeof(FTScalerInfo));
313 
314     if (scalerInfo == NULL)
315         return 0;
316 
317     scalerInfo->env = env;
318     scalerInfo->font2D = font2D;
319     scalerInfo->fontDataOffset = 0;
320     scalerInfo->fontDataLength = 0;
321     scalerInfo->fileSize = filesize;
322 
323     /*
324        We can consider sharing freetype library between different
325        scalers. However, Freetype docs suggest to use different libraries
326        for different threads. Also, our architecture implies that single
327        FontScaler object is shared for different sizes/transforms/styles
328        of the same font.
329 
330        On other hand these methods can not be concurrently executed
331        becaused they are "synchronized" in java.
332     */
333     error = FT_Init_FreeType(&scalerInfo->library);
334     if (error) {
335         free(scalerInfo);
336         return 0;
337     }
338     setInterpreterVersion(scalerInfo->library);
339 
340 #define TYPE1_FROM_JAVA        2
341 
342     error = 1; /* triggers memory freeing unless we clear it */
343     if (type == TYPE1_FROM_JAVA) { /* TYPE1 */
344         scalerInfo->fontData = (unsigned char*) malloc(filesize);
345         scalerInfo->directBuffer = NULL;
346         scalerInfo->fontDataLength = filesize;
347 
348         if (scalerInfo->fontData != NULL) {
349             bBuffer = (*env)->NewDirectByteBuffer(env,
350                                               scalerInfo->fontData,
351                                               scalerInfo->fontDataLength);
352             if (bBuffer != NULL) {
353                 (*env)->CallVoidMethod(env, font2D,
354                                    sunFontIDs.readFileMID, bBuffer);
355 
356                 error = FT_New_Memory_Face(scalerInfo->library,
357                                    scalerInfo->fontData,
358                                    scalerInfo->fontDataLength,
359                                    indexInCollection,
360                                    &scalerInfo->face);
361             }
362         }
363     } else { /* Truetype */
364         scalerInfo->fontData = (unsigned char*) malloc(FILEDATACACHESIZE);
365 
366         if (scalerInfo->fontData != NULL) {
367             FT_Stream ftstream = (FT_Stream) calloc(1, sizeof(FT_StreamRec));
368             if (ftstream != NULL) {
369                 scalerInfo->directBuffer = (*env)->NewDirectByteBuffer(env,
370                                            scalerInfo->fontData,
371                                            FILEDATACACHESIZE);
372                 if (scalerInfo->directBuffer != NULL) {
373                     scalerInfo->directBuffer = (*env)->NewGlobalRef(env,
374                                                scalerInfo->directBuffer);
375                     ftstream->base = NULL;
376                     ftstream->size = filesize;
377                     ftstream->pos = 0;
378                     ftstream->read = (FT_Stream_IoFunc) ReadTTFontFileFunc;
379                     ftstream->close = NULL;
380                     ftstream->pathname.pointer = (void *) scalerInfo;
381 
382                     memset(&ft_open_args, 0, sizeof(FT_Open_Args));
383                     ft_open_args.flags = FT_OPEN_STREAM;
384                     ft_open_args.stream = ftstream;
385 
386                     error = FT_Open_Face(scalerInfo->library,
387                                          &ft_open_args,
388                                          indexInCollection,
389                                          &scalerInfo->face);
390                     if (!error) {
391                         scalerInfo->faceStream = ftstream;
392                     }
393                 }
394                 if (error || scalerInfo->directBuffer == NULL) {
395                     free(ftstream);
396                 }
397             }
398         }
399     }
400 
401     if (error) {
402         FT_Done_FreeType(scalerInfo->library);
403         if (scalerInfo->directBuffer != NULL) {
404             (*env)->DeleteGlobalRef(env, scalerInfo->directBuffer);
405         }
406         if (scalerInfo->fontData != NULL)
407             free(scalerInfo->fontData);
408         free(scalerInfo);
409         return 0;
410     }
411 
412     return ptr_to_jlong(scalerInfo);
413 }
414 
euclidianDistance(double a,double b)415 static double euclidianDistance(double a, double b) {
416     if (a < 0) a=-a;
417     if (b < 0) b=-b;
418 
419     if (a == 0) return b;
420     if (b == 0) return a;
421 
422     return sqrt(a*a+b*b);
423 }
424 
425 JNIEXPORT jlong JNICALL
Java_sun_font_FreetypeFontScaler_createScalerContextNative(JNIEnv * env,jobject scaler,jlong pScaler,jdoubleArray matrix,jint aa,jint fm,jfloat boldness,jfloat italic)426 Java_sun_font_FreetypeFontScaler_createScalerContextNative(
427         JNIEnv *env, jobject scaler, jlong pScaler, jdoubleArray matrix,
428         jint aa, jint fm, jfloat boldness, jfloat italic) {
429     double dmat[4], ptsz;
430     FTScalerContext *context =
431             (FTScalerContext*) calloc(1, sizeof(FTScalerContext));
432     FTScalerInfo *scalerInfo =
433              (FTScalerInfo*) jlong_to_ptr(pScaler);
434 
435     if (context == NULL) {
436         invalidateJavaScaler(env, scaler, NULL);
437         return (jlong) 0;
438     }
439     (*env)->GetDoubleArrayRegion(env, matrix, 0, 4, dmat);
440     ptsz = euclidianDistance(dmat[2], dmat[3]); //i.e. y-size
441     if (ptsz < 1.0) {
442         //text can not be smaller than 1 point
443         ptsz = 1.0;
444     }
445     context->ptsz = (int)(ptsz * 64);
446     context->transform.xx =  FloatToFTFixed((float)dmat[0]/ptsz);
447     context->transform.yx = -FloatToFTFixed((float)dmat[1]/ptsz);
448     context->transform.xy = -FloatToFTFixed((float)dmat[2]/ptsz);
449     context->transform.yy =  FloatToFTFixed((float)dmat[3]/ptsz);
450     context->aaType = aa;
451     context->fmType = fm;
452 
453     /* If using algorithmic styling, the base values are
454      * boldness = 1.0, italic = 0.0.
455      */
456     context->doBold = (boldness != 1.0);
457     context->doItalize = (italic != 0);
458 
459     /* freetype is very keen to use embedded bitmaps, even if it knows
460      * there is a rotation or you asked for antialiasing.
461      * In the rendering path we will check useSBits and disable
462      * bitmaps unless it is set. And here we set it only if none
463      * of the conditions invalidate using it.
464      * Note that we allow embedded bitmaps for the LCD case.
465      */
466     if ((aa != TEXT_AA_ON) && (fm != TEXT_FM_ON) &&
467         !context->doBold && !context->doItalize &&
468         (context->transform.yx == 0) && (context->transform.xy == 0))
469     {
470         context->useSbits = 1;
471     }
472     return ptr_to_jlong(context);
473 }
474 
475 // values used by FreeType (as of version 2.10.1) for italics transformation matrix in FT_GlyphSlot_Oblique
476 #define FT_MATRIX_ONE 0x10000
477 #define FT_MATRIX_OBLIQUE_XY 0x0366A
478 
setupTransform(FT_Matrix * target,FTScalerContext * context)479 static void setupTransform(FT_Matrix* target, FTScalerContext *context) {
480     FT_Matrix* transform = &context->transform;
481     if (context->doItalize) {
482         // we cannot use FT_GlyphSlot_Oblique as it doesn't work well with arbitrary transforms,
483         // so we add corresponding shear transform to the requested glyph transformation
484         target->xx = FT_MATRIX_ONE;
485         target->xy = FT_MATRIX_OBLIQUE_XY;
486         target->yx = 0;
487         target->yy = FT_MATRIX_ONE;
488         FT_Matrix_Multiply(transform, target);
489     } else {
490         target->xx = transform->xx;
491         target->xy = transform->xy;
492         target->yx = transform->yx;
493         target->yy = transform->yy;
494     }
495 }
496 
setupFTContext(JNIEnv * env,jobject font2D,FTScalerInfo * scalerInfo,FTScalerContext * context)497 static int setupFTContext(JNIEnv *env,
498                           jobject font2D,
499                           FTScalerInfo *scalerInfo,
500                           FTScalerContext *context) {
501     FT_Matrix matrix;
502     int errCode = 0;
503 
504     scalerInfo->env = env;
505     scalerInfo->font2D = font2D;
506 
507     if (context != NULL) {
508         setupTransform(&matrix, context);
509         FT_Set_Transform(scalerInfo->face, &matrix, NULL);
510 
511         errCode = FT_Set_Char_Size(scalerInfo->face, 0, context->ptsz, 72, 72);
512 
513         if (errCode == 0) {
514             errCode = FT_Activate_Size(scalerInfo->face->size);
515         }
516 
517         FT_Library_SetLcdFilter(scalerInfo->library, FT_LCD_FILTER_DEFAULT);
518     }
519 
520     return errCode;
521 }
522 
523 // using same values as for the transformation matrix
524 #define OBLIQUE_MODIFIER(y)  (context->doItalize ? ((y)*FT_MATRIX_OBLIQUE_XY/FT_MATRIX_ONE) : 0)
525 
526 /* FT_GlyphSlot_Embolden (ftsynth.c) uses FT_MulFix(units_per_EM, y_scale) / 24
527  * strength value when glyph format is FT_GLYPH_FORMAT_OUTLINE. This value has
528  * been taken from libfreetype version 2.6 and remain valid at least up to
529  * 2.9.1. */
530 #define BOLD_MODIFIER(units_per_EM, y_scale) \
531     (context->doBold ? FT_MulFix(units_per_EM, y_scale) / 24 : 0)
532 
533 /*
534  * Class:     sun_font_FreetypeFontScaler
535  * Method:    getFontMetricsNative
536  * Signature: (Lsun/font/Font2D;J)Lsun/font/StrikeMetrics;
537  */
538 JNIEXPORT jobject JNICALL
Java_sun_font_FreetypeFontScaler_getFontMetricsNative(JNIEnv * env,jobject scaler,jobject font2D,jlong pScalerContext,jlong pScaler)539 Java_sun_font_FreetypeFontScaler_getFontMetricsNative(
540         JNIEnv *env, jobject scaler, jobject font2D,
541         jlong pScalerContext, jlong pScaler) {
542 
543     jobject metrics;
544     jfloat ax, ay, dx, dy, bx, by, lx, ly, mx, my;
545     jfloat f0 = 0.0;
546     FTScalerContext *context =
547         (FTScalerContext*) jlong_to_ptr(pScalerContext);
548     FTScalerInfo *scalerInfo =
549              (FTScalerInfo*) jlong_to_ptr(pScaler);
550 
551     int errCode;
552 
553     if (isNullScalerContext(context) || scalerInfo == NULL) {
554         return (*env)->NewObject(env,
555                                  sunFontIDs.strikeMetricsClass,
556                                  sunFontIDs.strikeMetricsCtr,
557                                  f0, f0, f0, f0, f0, f0, f0, f0, f0, f0);
558     }
559 
560     errCode = setupFTContext(env, font2D, scalerInfo, context);
561 
562     if (errCode) {
563         metrics = (*env)->NewObject(env,
564                                  sunFontIDs.strikeMetricsClass,
565                                  sunFontIDs.strikeMetricsCtr,
566                                  f0, f0, f0, f0, f0, f0, f0, f0, f0, f0);
567         invalidateJavaScaler(env, scaler, scalerInfo);
568         return metrics;
569     }
570 
571     /* This is ugly and has to be reworked.
572        Freetype provide means to add style to glyph but
573        it seems there is no way to adjust metrics accordingly.
574 
575        So, we have to do adust them explicitly and stay consistent with what
576        freetype does to outlines. */
577 
578 
579     /**** Note: only some metrics are affected by styling ***/
580 
581     /* See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=657854 */
582 #define FT_MulFixFloatShift6(a, b) (((float) (a)) * ((float) (b)) / 65536.0 / 64.0)
583 
584 #define contextAwareMetricsX(x, y) \
585     (FTFixedToFloat(context->transform.xx) * (x) - \
586      FTFixedToFloat(context->transform.xy) * (y))
587 
588 #define contextAwareMetricsY(x, y) \
589     (-FTFixedToFloat(context->transform.yx) * (x) + \
590      FTFixedToFloat(context->transform.yy) * (y))
591 
592     /*
593      * See FreeType source code: src/base/ftobjs.c ft_recompute_scaled_metrics()
594      * http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=1659
595      */
596     /* ascent */
597     ax = 0;
598     ay = -(jfloat) (FT_MulFixFloatShift6(
599                        ((jlong) scalerInfo->face->ascender),
600                        (jlong) scalerInfo->face->size->metrics.y_scale));
601     /* descent */
602     dx = 0;
603     dy = -(jfloat) (FT_MulFixFloatShift6(
604                        ((jlong) scalerInfo->face->descender),
605                        (jlong) scalerInfo->face->size->metrics.y_scale));
606     /* baseline */
607     bx = by = 0;
608 
609     /* leading */
610     lx = 0;
611     ly = (jfloat) (FT_MulFixFloatShift6(
612                       (jlong) scalerInfo->face->height,
613                       (jlong) scalerInfo->face->size->metrics.y_scale))
614                   + ay - dy;
615     /* max advance */
616     mx = (jfloat) FT26Dot6ToFloat(
617                      scalerInfo->face->size->metrics.max_advance +
618                      OBLIQUE_MODIFIER(scalerInfo->face->size->metrics.height) +
619                      BOLD_MODIFIER(scalerInfo->face->units_per_EM,
620                              scalerInfo->face->size->metrics.y_scale));
621     my = 0;
622 
623     metrics = (*env)->NewObject(env,
624         sunFontIDs.strikeMetricsClass,
625         sunFontIDs.strikeMetricsCtr,
626         contextAwareMetricsX(ax, ay), contextAwareMetricsY(ax, ay),
627         contextAwareMetricsX(dx, dy), contextAwareMetricsY(dx, dy),
628         bx, by,
629         contextAwareMetricsX(lx, ly), contextAwareMetricsY(lx, ly),
630         contextAwareMetricsX(mx, my), contextAwareMetricsY(mx, my));
631 
632     return metrics;
633 }
634 
635 static jlong
636     getGlyphImageNativeInternal(
637         JNIEnv *env, jobject scaler, jobject font2D,
638         jlong pScalerContext, jlong pScaler, jint glyphCode,
639         jboolean renderImage);
640 
641 /*
642  * Class:     sun_font_FreetypeFontScaler
643  * Method:    getGlyphAdvanceNative
644  * Signature: (Lsun/font/Font2D;JI)F
645  */
646 JNIEXPORT jfloat JNICALL
Java_sun_font_FreetypeFontScaler_getGlyphAdvanceNative(JNIEnv * env,jobject scaler,jobject font2D,jlong pScalerContext,jlong pScaler,jint glyphCode)647 Java_sun_font_FreetypeFontScaler_getGlyphAdvanceNative(
648         JNIEnv *env, jobject scaler, jobject font2D,
649         jlong pScalerContext, jlong pScaler, jint glyphCode) {
650 
651    /* This method is rarely used because requests for metrics are usually
652     * coupled with a request for the bitmap and to a large extent the
653     * work can be reused (to find out metrics we may need to hint the glyph).
654     * So, we typically go through the getGlyphImage code path.
655     * When we do get here, we need to pass a parameter which indicates
656     * that we don't need freetype to render the bitmap, and consequently
657     * don't need to allocate our own storage either.
658     * This is also important when enter here requesting metrics for sizes
659     * of text which a large size would be rejected for a bitmap but we
660     * still need the metrics.
661     */
662 
663     GlyphInfo *info;
664     jfloat advance = 0.0f;
665     jlong image;
666 
667     image = getGlyphImageNativeInternal(
668           env, scaler, font2D, pScalerContext, pScaler, glyphCode, JNI_FALSE);
669     info = (GlyphInfo*) jlong_to_ptr(image);
670 
671     if (info != NULL) {
672         advance = info->advanceX;
673         free(info);
674     }
675 
676     return advance;
677 }
678 
679 /*
680  * Class:     sun_font_FreetypeFontScaler
681  * Method:    getGlyphMetricsNative
682  * Signature: (Lsun/font/Font2D;JILjava/awt/geom/Point2D/Float;)V
683  */
684 JNIEXPORT void JNICALL
Java_sun_font_FreetypeFontScaler_getGlyphMetricsNative(JNIEnv * env,jobject scaler,jobject font2D,jlong pScalerContext,jlong pScaler,jint glyphCode,jobject metrics)685 Java_sun_font_FreetypeFontScaler_getGlyphMetricsNative(
686         JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
687         jlong pScaler, jint glyphCode, jobject metrics) {
688 
689      /* See the comments in getGlyphMetricsNative. They apply here too. */
690      GlyphInfo *info;
691 
692      jlong image = getGlyphImageNativeInternal(
693                                  env, scaler, font2D,
694                                  pScalerContext, pScaler, glyphCode, JNI_FALSE);
695      info = (GlyphInfo*) jlong_to_ptr(image);
696 
697      if (info != NULL) {
698          (*env)->SetFloatField(env, metrics, sunFontIDs.xFID, info->advanceX);
699          (*env)->SetFloatField(env, metrics, sunFontIDs.yFID, info->advanceY);
700 
701          free(info);
702      } else {
703          (*env)->SetFloatField(env, metrics, sunFontIDs.xFID, 0.0f);
704          (*env)->SetFloatField(env, metrics, sunFontIDs.yFID, 0.0f);
705      }
706 }
707 
708 
getNullGlyphImage()709 static GlyphInfo* getNullGlyphImage() {
710     GlyphInfo *glyphInfo =  (GlyphInfo*) calloc(1, sizeof(GlyphInfo));
711     return glyphInfo;
712 }
713 
CopyBW2Grey8(const void * srcImage,int srcRowBytes,void * dstImage,int dstRowBytes,int width,int height)714 static void CopyBW2Grey8(const void* srcImage, int srcRowBytes,
715                          void* dstImage, int dstRowBytes,
716                          int width, int height) {
717     const UInt8* srcRow = (UInt8*)srcImage;
718     UInt8* dstRow = (UInt8*)dstImage;
719     int wholeByteCount = width >> 3;
720     int remainingBitsCount = width & 7;
721     int i, j;
722 
723     while (height--) {
724         const UInt8* src8 = srcRow;
725         UInt8* dstByte = dstRow;
726         unsigned srcValue;
727 
728         srcRow += srcRowBytes;
729         dstRow += dstRowBytes;
730 
731         for (i = 0; i < wholeByteCount; i++) {
732             srcValue = *src8++;
733             for (j = 0; j < 8; j++) {
734                 *dstByte++ = (srcValue & 0x80) ? 0xFF : 0;
735                 srcValue <<= 1;
736             }
737         }
738         if (remainingBitsCount) {
739             srcValue = *src8;
740             for (j = 0; j < remainingBitsCount; j++) {
741                 *dstByte++ = (srcValue & 0x80) ? 0xFF : 0;
742                 srcValue <<= 1;
743             }
744         }
745     }
746 }
747 
748 #define Grey4ToAlpha255(value) (((value) << 4) + ((value) >> 3))
749 
CopyGrey4ToGrey8(const void * srcImage,int srcRowBytes,void * dstImage,int dstRowBytes,int width,int height)750 static void CopyGrey4ToGrey8(const void* srcImage, int srcRowBytes,
751                 void* dstImage, int dstRowBytes, int width, int height) {
752      const UInt8* srcRow = (UInt8*) srcImage;
753      UInt8* dstRow = (UInt8*) dstImage;
754      int i;
755 
756      while (height--) {
757          const UInt8* src8 = srcRow;
758          UInt8* dstByte = dstRow;
759          unsigned srcValue;
760 
761          srcRow += srcRowBytes;
762          dstRow += dstRowBytes;
763 
764          for (i = 0; i < width; i++) {
765              srcValue = *src8++;
766              *dstByte++ = Grey4ToAlpha255(srcValue & 0x0f);
767              *dstByte++ = Grey4ToAlpha255(srcValue >> 4);
768          }
769      }
770 }
771 
772 /* We need it because FT rows are often padded to 4 byte boundaries
773     and our internal format is not padded */
CopyFTSubpixelToSubpixel(const void * srcImage,int srcRowBytes,void * dstImage,int dstRowBytes,int width,int height)774 static void CopyFTSubpixelToSubpixel(const void* srcImage, int srcRowBytes,
775                                      void* dstImage, int dstRowBytes,
776                                      int width, int height) {
777     unsigned char *srcRow = (unsigned char *) srcImage;
778     unsigned char *dstRow = (unsigned char *) dstImage;
779 
780     while (height--) {
781         memcpy(dstRow, srcRow, width);
782         srcRow += srcRowBytes;
783         dstRow += dstRowBytes;
784     }
785 }
786 
787 /* We need it because FT rows are often padded to 4 byte boundaries
788    and our internal format is not padded */
CopyFTSubpixelVToSubpixel(const void * srcImage,int srcRowBytes,void * dstImage,int dstRowBytes,int width,int height)789 static void CopyFTSubpixelVToSubpixel(const void* srcImage, int srcRowBytes,
790                                       void* dstImage, int dstRowBytes,
791                                       int width, int height) {
792     unsigned char *srcRow = (unsigned char *) srcImage, *srcByte;
793     unsigned char *dstRow = (unsigned char *) dstImage, *dstByte;
794     int i;
795 
796     while (height > 0) {
797         srcByte = srcRow;
798         dstByte = dstRow;
799         for (i = 0; i < width; i++) {
800             *dstByte++ = *srcByte;
801             *dstByte++ = *(srcByte + srcRowBytes);
802             *dstByte++ = *(srcByte + 2*srcRowBytes);
803             srcByte++;
804         }
805         srcRow += 3*srcRowBytes;
806         dstRow += dstRowBytes;
807         height -= 3;
808     }
809 }
810 
811 
812 /* JDK does not use glyph images for fonts with a
813  * pixel size > 100 (see THRESHOLD in OutlineTextRenderer.java)
814  * so if the glyph bitmap image dimension is > 1024 pixels,
815  * something is up.
816  */
817 #define MAX_GLYPH_DIM 1024
818 
819 /*
820  * Class:     sun_font_FreetypeFontScaler
821  * Method:    getGlyphImageNative
822  * Signature: (Lsun/font/Font2D;JI)J
823  */
824 JNIEXPORT jlong JNICALL
Java_sun_font_FreetypeFontScaler_getGlyphImageNative(JNIEnv * env,jobject scaler,jobject font2D,jlong pScalerContext,jlong pScaler,jint glyphCode)825 Java_sun_font_FreetypeFontScaler_getGlyphImageNative(
826         JNIEnv *env, jobject scaler, jobject font2D,
827         jlong pScalerContext, jlong pScaler, jint glyphCode) {
828 
829     return getGlyphImageNativeInternal(
830         env, scaler, font2D,
831         pScalerContext, pScaler, glyphCode, JNI_TRUE);
832 }
833 
834 static jlong
getGlyphImageNativeInternal(JNIEnv * env,jobject scaler,jobject font2D,jlong pScalerContext,jlong pScaler,jint glyphCode,jboolean renderImage)835      getGlyphImageNativeInternal(
836         JNIEnv *env, jobject scaler, jobject font2D,
837         jlong pScalerContext, jlong pScaler, jint glyphCode,
838         jboolean renderImage) {
839 
840     static int PADBYTES = 3;
841     int error, imageSize;
842     UInt16 width, height, rowBytes;
843     GlyphInfo *glyphInfo;
844     int renderFlags = FT_LOAD_DEFAULT, target;
845     FT_GlyphSlot ftglyph;
846 
847     FTScalerContext* context =
848         (FTScalerContext*) jlong_to_ptr(pScalerContext);
849     FTScalerInfo *scalerInfo =
850              (FTScalerInfo*) jlong_to_ptr(pScaler);
851 
852     if (isNullScalerContext(context) || scalerInfo == NULL) {
853         return ptr_to_jlong(getNullGlyphImage());
854     }
855 
856     error = setupFTContext(env, font2D, scalerInfo, context);
857     if (error) {
858         invalidateJavaScaler(env, scaler, scalerInfo);
859         return ptr_to_jlong(getNullGlyphImage());
860     }
861 
862     /*
863      * When using Fractional metrics (linearly scaling advances) and
864      * greyscale antialiasing, disable hinting so that the glyph shapes
865      * are constant as size increases. This is good for animation as well
866      * as being compatible with what happened in earlier JDK versions
867      * which did not use freetype.
868      */
869     if (context->aaType == TEXT_AA_ON && context->fmType == TEXT_FM_ON) {
870          renderFlags |= FT_LOAD_NO_HINTING;
871      }
872 
873     if (!context->useSbits) {
874         renderFlags |= FT_LOAD_NO_BITMAP;
875     }
876 
877     /* NB: in case of non identity transform
878      we might also prefer to disable transform before hinting,
879      and apply it explicitly after hinting is performed.
880      Or we can disable hinting. */
881 
882     /* select appropriate hinting mode */
883     if (context->aaType == TEXT_AA_OFF) {
884         target = FT_LOAD_TARGET_MONO;
885     } else if (context->aaType == TEXT_AA_ON) {
886         target = FT_LOAD_TARGET_NORMAL;
887     } else if (context->aaType == TEXT_AA_LCD_HRGB ||
888                context->aaType == TEXT_AA_LCD_HBGR) {
889         target = FT_LOAD_TARGET_LCD;
890     } else {
891         target = FT_LOAD_TARGET_LCD_V;
892     }
893     renderFlags |= target;
894 
895     error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderFlags);
896     if (error) {
897         //do not destroy scaler yet.
898         //this can be problem of particular context (e.g. with bad transform)
899         return ptr_to_jlong(getNullGlyphImage());
900     }
901 
902     ftglyph = scalerInfo->face->glyph;
903 
904     /* apply styles */
905     if (context->doBold) { /* if bold style */
906         FT_GlyphSlot_Embolden(ftglyph);
907     }
908 
909     /* generate bitmap if it is not done yet
910      e.g. if algorithmic styling is performed and style was added to outline */
911     if (renderImage && (ftglyph->format == FT_GLYPH_FORMAT_OUTLINE)) {
912         FT_BBox bbox;
913         FT_Outline_Get_CBox(&(ftglyph->outline), &bbox);
914         int w = (int)((bbox.xMax>>6)-(bbox.xMin>>6));
915         int h = (int)((bbox.yMax>>6)-(bbox.yMin>>6));
916         if (w > MAX_GLYPH_DIM || h > MAX_GLYPH_DIM) {
917             glyphInfo = getNullGlyphImage();
918             return ptr_to_jlong(glyphInfo);
919         }
920         error = FT_Render_Glyph(ftglyph, FT_LOAD_TARGET_MODE(target));
921         if (error != 0) {
922             return ptr_to_jlong(getNullGlyphImage());
923         }
924     }
925 
926     if (renderImage) {
927         width  = (UInt16) ftglyph->bitmap.width;
928         rowBytes = width;
929         if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) {
930            rowBytes = PADBYTES + width + PADBYTES;
931         }
932         height = (UInt16) ftglyph->bitmap.rows;
933             if (width > MAX_GLYPH_DIM || height > MAX_GLYPH_DIM) {
934               glyphInfo = getNullGlyphImage();
935               return ptr_to_jlong(glyphInfo);
936             }
937      } else {
938         width = 0;
939         rowBytes = 0;
940         height = 0;
941      }
942 
943 
944     imageSize = rowBytes*height;
945     glyphInfo = (GlyphInfo*) calloc(sizeof(GlyphInfo) + imageSize, 1);
946     if (glyphInfo == NULL) {
947         glyphInfo = getNullGlyphImage();
948         return ptr_to_jlong(glyphInfo);
949     }
950     glyphInfo->cellInfo  = NULL;
951     glyphInfo->managed   = UNMANAGED_GLYPH;
952     glyphInfo->rowBytes  = rowBytes;
953     glyphInfo->width     = width;
954     glyphInfo->height    = height;
955 
956     if (renderImage) {
957         glyphInfo->topLeftX  = (float)  ftglyph->bitmap_left;
958         glyphInfo->topLeftY  = (float) -ftglyph->bitmap_top;
959 
960         if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD && width > 0) {
961             glyphInfo->width = width/3;
962             glyphInfo->topLeftX -= 1;
963             glyphInfo->width += 1;
964         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD_V) {
965             glyphInfo->height = glyphInfo->height/3;
966         }
967     }
968 
969     if (context->fmType == TEXT_FM_ON) {
970         float advh = FTFixedToFloat(ftglyph->linearHoriAdvance);
971         glyphInfo->advanceX =
972             (float) (advh * FTFixedToFloat(context->transform.xx));
973         glyphInfo->advanceY =
974             (float) - (advh * FTFixedToFloat(context->transform.yx));
975     } else {
976         if (!ftglyph->advance.y) {
977             glyphInfo->advanceX =
978                 (float) FT26Dot6ToInt(ftglyph->advance.x);
979             glyphInfo->advanceY = 0;
980         } else if (!ftglyph->advance.x) {
981             glyphInfo->advanceX = 0;
982             glyphInfo->advanceY =
983                 (float) FT26Dot6ToInt(-ftglyph->advance.y);
984         } else {
985             glyphInfo->advanceX = FT26Dot6ToFloat(ftglyph->advance.x);
986             glyphInfo->advanceY = FT26Dot6ToFloat(-ftglyph->advance.y);
987         }
988     }
989 
990     if (imageSize == 0) {
991         glyphInfo->image = NULL;
992     } else {
993         glyphInfo->image = (unsigned char*) glyphInfo + sizeof(GlyphInfo);
994         //convert result to output format
995         //output format is either 3 bytes per pixel (for subpixel modes)
996         // or 1 byte per pixel for AA and B&W
997         if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_MONO) {
998             /* convert from 8 pixels per byte to 1 byte per pixel */
999             CopyBW2Grey8(ftglyph->bitmap.buffer,
1000                          ftglyph->bitmap.pitch,
1001                          (void *) glyphInfo->image,
1002                          width,
1003                          width,
1004                          height);
1005         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_GRAY) {
1006             /* byte per pixel to byte per pixel => just copy */
1007             memcpy(glyphInfo->image, ftglyph->bitmap.buffer, imageSize);
1008         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_GRAY4) {
1009             /* 4 bits per pixel to byte per pixel */
1010             CopyGrey4ToGrey8(ftglyph->bitmap.buffer,
1011                              ftglyph->bitmap.pitch,
1012                              (void *) glyphInfo->image,
1013                              width,
1014                              width,
1015                              height);
1016         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD) {
1017             /* 3 bytes per pixel to 3 bytes per pixel */
1018             CopyFTSubpixelToSubpixel(ftglyph->bitmap.buffer,
1019                                      ftglyph->bitmap.pitch,
1020                                      (void *) (glyphInfo->image+PADBYTES),
1021                                      rowBytes,
1022                                      width,
1023                                      height);
1024         } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD_V) {
1025             /* 3 bytes per pixel to 3 bytes per pixel */
1026             CopyFTSubpixelVToSubpixel(ftglyph->bitmap.buffer,
1027                                       ftglyph->bitmap.pitch,
1028                                       (void *) glyphInfo->image,
1029                                       width*3,
1030                                       width,
1031                                       height);
1032             glyphInfo->rowBytes *=3;
1033         } else {
1034             free(glyphInfo);
1035             glyphInfo = getNullGlyphImage();
1036         }
1037     }
1038 
1039     return ptr_to_jlong(glyphInfo);
1040 }
1041 
1042 /*
1043  * Class:     sun_font_FreetypeFontScaler
1044  * Method:    disposeNativeScaler
1045  * Signature: (J)V
1046  */
1047 JNIEXPORT void JNICALL
Java_sun_font_FreetypeFontScaler_disposeNativeScaler(JNIEnv * env,jobject scaler,jobject font2D,jlong pScaler)1048 Java_sun_font_FreetypeFontScaler_disposeNativeScaler(
1049         JNIEnv *env, jobject scaler, jobject font2D, jlong pScaler) {
1050     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
1051 
1052     /* Freetype functions *may* cause callback to java
1053        that can use cached values. Make sure our cache is up to date.
1054        NB: scaler context is not important at this point, can use NULL. */
1055     int errCode = setupFTContext(env, font2D, scalerInfo, NULL);
1056     if (errCode) {
1057         return;
1058     }
1059 
1060     freeNativeResources(env, scalerInfo);
1061 }
1062 
1063 /*
1064  * Class:     sun_font_FreetypeFontScaler
1065  * Method:    getNumGlyphsNative
1066  * Signature: ()I
1067  */
1068 JNIEXPORT jint JNICALL
Java_sun_font_FreetypeFontScaler_getNumGlyphsNative(JNIEnv * env,jobject scaler,jlong pScaler)1069 Java_sun_font_FreetypeFontScaler_getNumGlyphsNative(
1070         JNIEnv *env, jobject scaler, jlong pScaler) {
1071     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
1072 
1073     if (scalerInfo == NULL || scalerInfo->face == NULL) { /* bad/null scaler */
1074         /* null scaler can render 1 glyph - "missing glyph" with code 0
1075            (all glyph codes requested by user are mapped to code 0 at
1076            validation step) */
1077         invalidateJavaScaler(env, scaler, scalerInfo);
1078         return (jint) 1;
1079     }
1080 
1081     return (jint) scalerInfo->face->num_glyphs;
1082 }
1083 
1084 /*
1085  * Class:     sun_font_FreetypeFontScaler
1086  * Method:    getMissingGlyphCodeNative
1087  * Signature: ()I
1088  */
1089 JNIEXPORT jint JNICALL
Java_sun_font_FreetypeFontScaler_getMissingGlyphCodeNative(JNIEnv * env,jobject scaler,jlong pScaler)1090 Java_sun_font_FreetypeFontScaler_getMissingGlyphCodeNative(
1091         JNIEnv *env, jobject scaler, jlong pScaler) {
1092 
1093     /* Is it always 0 for freetype? */
1094     return 0;
1095 }
1096 
1097 /*
1098  * Class:     sun_font_FreetypeFontScaler
1099  * Method:    getGlyphCodeNative
1100  * Signature: (C)I
1101  */
1102 JNIEXPORT jint JNICALL
Java_sun_font_FreetypeFontScaler_getGlyphCodeNative(JNIEnv * env,jobject scaler,jobject font2D,jlong pScaler,jchar charCode)1103 Java_sun_font_FreetypeFontScaler_getGlyphCodeNative(
1104         JNIEnv *env, jobject scaler,
1105         jobject font2D, jlong pScaler, jchar charCode) {
1106 
1107     FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
1108     int errCode;
1109 
1110     if (scaler == NULL || scalerInfo->face == NULL) { /* bad/null scaler */
1111         invalidateJavaScaler(env, scaler, scalerInfo);
1112         return 0;
1113     }
1114 
1115     /* Freetype functions *may* cause callback to java
1116        that can use cached values. Make sure our cache is up to date.
1117        Scaler context is not important here, can use NULL. */
1118     errCode = setupFTContext(env, font2D, scalerInfo, NULL);
1119     if (errCode) {
1120         return 0;
1121     }
1122 
1123     return FT_Get_Char_Index(scalerInfo->face, charCode);
1124 }
1125 
1126 
1127 #define FloatToF26Dot6(x) ((unsigned int) ((x)*64))
1128 
getFTOutline(JNIEnv * env,jobject font2D,FTScalerContext * context,FTScalerInfo * scalerInfo,jint glyphCode,jfloat xpos,jfloat ypos)1129 static FT_Outline* getFTOutline(JNIEnv* env, jobject font2D,
1130         FTScalerContext *context, FTScalerInfo* scalerInfo,
1131         jint glyphCode, jfloat xpos, jfloat ypos) {
1132     int renderFlags;
1133     FT_Error error;
1134     FT_GlyphSlot ftglyph;
1135 
1136     if (glyphCode >= INVISIBLE_GLYPHS ||
1137             isNullScalerContext(context) || scalerInfo == NULL) {
1138         return NULL;
1139     }
1140 
1141     error = setupFTContext(env, font2D, scalerInfo, context);
1142     if (error) {
1143         return NULL;
1144     }
1145 
1146     renderFlags = FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP;
1147 
1148     error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderFlags);
1149     if (error) {
1150         return NULL;
1151     }
1152 
1153     ftglyph = scalerInfo->face->glyph;
1154 
1155     /* apply styles */
1156     if (context->doBold) { /* if bold style */
1157         FT_GlyphSlot_Embolden(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