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