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