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