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