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
55typedef 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
78typedef 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. */
94int z_verbose;
95void z_error(char *s) {}
96#endif
97
98/**************** Error handling utilities *****************/
99
100static jmethodID invalidateScalerMID;
101
102JNIEXPORT void JNICALL
103Java_sun_font_FreetypeFontScaler_initIDs(
104 JNIEnv *env, jobject scaler, jclass FFSClass) {
105 invalidateScalerMID =
106 (*env)->GetMethodID(env, FFSClass, "invalidateScaler", "()V");
107}
108
109static 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 */
136static 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
147static 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
211typedef 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 */
219static 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 */
262JNIEXPORT jlong JNICALL
263Java_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
373static 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
383JNIEXPORT jlong JNICALL
384Java_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
433static 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 */
475JNIEXPORT jobject JNICALL
476Java_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 */
577JNIEXPORT jfloat JNICALL
578Java_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 */
615JNIEXPORT void JNICALL
616Java_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
640static GlyphInfo* getNullGlyphImage() {
641 GlyphInfo *glyphInfo = (GlyphInfo*) calloc(1, sizeof(GlyphInfo));
642 return glyphInfo;
643}
644
645static 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
681static 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 */
705static 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 */
720static 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 */
748JNIEXPORT jlong JNICALL
749Java_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 */
923JNIEXPORT void JNICALL
924Java_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 */
944JNIEXPORT jint JNICALL
945Java_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 */
965JNIEXPORT jint JNICALL
966Java_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 */
978JNIEXPORT jint JNICALL
979Java_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
1005static 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 */
1062typedef 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 */
1073static 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
1118static void addSeg(GPData *gp, jbyte type) {
1119 gp->pointTypes[gp->numTypes++] = type;
1120}
1121
1122static 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
1127static 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
1135static int lineTo(FT_Vector *to, GPData *gp) {
1136 addCoords(gp, to);
1137 addSeg(gp, SEG_LINETO);
1138 return FT_Err_Ok;
1139}
1140
1141static 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
1148static 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
1159static 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
1179static 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
1194static 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 */
1249JNIEXPORT jobject JNICALL
1250Java_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 */
1278JNIEXPORT jobject JNICALL
1279Java_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 */
1326JNIEXPORT jobject
1327JNICALL
1328Java_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
1412JNIEXPORT jlong JNICALL
1413Java_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. */
1432JNIEXPORT jobject JNICALL
1433Java_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