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