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