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