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