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