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 if (info != NULL) { 677 (*env)->SetFloatField(env, metrics, sunFontIDs.xFID, info->advanceX); 678 (*env)->SetFloatField(env, metrics, sunFontIDs.yFID, info->advanceY); 679 free(info); 680 } else { 681 (*env)->SetFloatField(env, metrics, sunFontIDs.xFID, 0.0f); 682 (*env)->SetFloatField(env, metrics, sunFontIDs.yFID, 0.0f); 683 } 684 } 685 686 687 static GlyphInfo* getNullGlyphImage() { 688 GlyphInfo *glyphInfo = (GlyphInfo*) calloc(1, sizeof(GlyphInfo)); 689 return glyphInfo; 690 } 691 692 static void CopyBW2Grey8(const void* srcImage, int srcRowBytes, 693 void* dstImage, int dstRowBytes, 694 int width, int height) { 695 const UInt8* srcRow = (UInt8*)srcImage; 696 UInt8* dstRow = (UInt8*)dstImage; 697 int wholeByteCount = width >> 3; 698 int remainingBitsCount = width & 7; 699 int i, j; 700 701 while (height--) { 702 const UInt8* src8 = srcRow; 703 UInt8* dstByte = dstRow; 704 unsigned srcValue; 705 706 srcRow += srcRowBytes; 707 dstRow += dstRowBytes; 708 709 for (i = 0; i < wholeByteCount; i++) { 710 srcValue = *src8++; 711 for (j = 0; j < 8; j++) { 712 *dstByte++ = (srcValue & 0x80) ? 0xFF : 0; 713 srcValue <<= 1; 714 } 715 } 716 if (remainingBitsCount) { 717 srcValue = *src8; 718 for (j = 0; j < remainingBitsCount; j++) { 719 *dstByte++ = (srcValue & 0x80) ? 0xFF : 0; 720 srcValue <<= 1; 721 } 722 } 723 } 724 } 725 726 #define Grey4ToAlpha255(value) (((value) << 4) + ((value) >> 3)) 727 728 static void CopyGrey4ToGrey8(const void* srcImage, int srcRowBytes, 729 void* dstImage, int dstRowBytes, int width, int height) { 730 const UInt8* srcRow = (UInt8*) srcImage; 731 UInt8* dstRow = (UInt8*) dstImage; 732 int i; 733 734 while (height--) { 735 const UInt8* src8 = srcRow; 736 UInt8* dstByte = dstRow; 737 unsigned srcValue; 738 739 srcRow += srcRowBytes; 740 dstRow += dstRowBytes; 741 742 for (i = 0; i < width; i++) { 743 srcValue = *src8++; 744 *dstByte++ = Grey4ToAlpha255(srcValue & 0x0f); 745 *dstByte++ = Grey4ToAlpha255(srcValue >> 4); 746 } 747 } 748 } 749 750 /* We need it because FT rows are often padded to 4 byte boundaries 751 and our internal format is not padded */ 752 static void CopyFTSubpixelToSubpixel(const void* srcImage, int srcRowBytes, 753 void* dstImage, int dstRowBytes, 754 int width, int height) { 755 unsigned char *srcRow = (unsigned char *) srcImage; 756 unsigned char *dstRow = (unsigned char *) dstImage; 757 758 while (height--) { 759 memcpy(dstRow, srcRow, width); 760 srcRow += srcRowBytes; 761 dstRow += dstRowBytes; 762 } 763 } 764 765 /* We need it because FT rows are often padded to 4 byte boundaries 766 and our internal format is not padded */ 767 static void CopyFTSubpixelVToSubpixel(const void* srcImage, int srcRowBytes, 768 void* dstImage, int dstRowBytes, 769 int width, int height) { 770 unsigned char *srcRow = (unsigned char *) srcImage, *srcByte; 771 unsigned char *dstRow = (unsigned char *) dstImage, *dstByte; 772 int i; 773 774 while (height > 0) { 775 srcByte = srcRow; 776 dstByte = dstRow; 777 for (i = 0; i < width; i++) { 778 *dstByte++ = *srcByte; 779 *dstByte++ = *(srcByte + srcRowBytes); 780 *dstByte++ = *(srcByte + 2*srcRowBytes); 781 srcByte++; 782 } 783 srcRow += 3*srcRowBytes; 784 dstRow += dstRowBytes; 785 height -= 3; 786 } 787 } 788 789 790 /* JDK does not use glyph images for fonts with a 791 * pixel size > 100 (see THRESHOLD in OutlineTextRenderer.java) 792 * so if the glyph bitmap image dimension is > 1024 pixels, 793 * something is up. 794 */ 795 #define MAX_GLYPH_DIM 1024 796 797 /* 798 * Class: sun_font_FreetypeFontScaler 799 * Method: getGlyphImageNative 800 * Signature: (Lsun/font/Font2D;JI)J 801 */ 802 JNIEXPORT jlong JNICALL 803 Java_sun_font_FreetypeFontScaler_getGlyphImageNative( 804 JNIEnv *env, jobject scaler, jobject font2D, 805 jlong pScalerContext, jlong pScaler, jint glyphCode) { 806 807 int error, imageSize; 808 UInt16 width, height; 809 GlyphInfo *glyphInfo; 810 int renderFlags = FT_LOAD_DEFAULT, target; 811 FT_GlyphSlot ftglyph; 812 813 FTScalerContext* context = 814 (FTScalerContext*) jlong_to_ptr(pScalerContext); 815 FTScalerInfo *scalerInfo = 816 (FTScalerInfo*) jlong_to_ptr(pScaler); 817 818 if (isNullScalerContext(context) || scalerInfo == NULL) { 819 return ptr_to_jlong(getNullGlyphImage()); 820 } 821 822 error = setupFTContext(env, font2D, scalerInfo, context); 823 if (error) { 824 invalidateJavaScaler(env, scaler, scalerInfo); 825 return ptr_to_jlong(getNullGlyphImage()); 826 } 827 828 if (!context->useSbits) { 829 renderFlags |= FT_LOAD_NO_BITMAP; 830 } 831 832 /* NB: in case of non identity transform 833 we might also prefer to disable transform before hinting, 834 and apply it explicitly after hinting is performed. 835 Or we can disable hinting. */ 836 837 /* select appropriate hinting mode */ 838 if (context->aaType == TEXT_AA_OFF) { 839 target = FT_LOAD_TARGET_MONO; 840 } else if (context->aaType == TEXT_AA_ON) { 841 target = FT_LOAD_TARGET_NORMAL; 842 } else if (context->aaType == TEXT_AA_LCD_HRGB || 843 context->aaType == TEXT_AA_LCD_HBGR) { 844 target = FT_LOAD_TARGET_LCD; 845 } else { 846 target = FT_LOAD_TARGET_LCD_V; 847 } 848 renderFlags |= target; 849 850 error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderFlags); 851 if (error) { 852 //do not destroy scaler yet. 853 //this can be problem of particular context (e.g. with bad transform) 854 return ptr_to_jlong(getNullGlyphImage()); 855 } 856 857 ftglyph = scalerInfo->face->glyph; 858 859 /* apply styles */ 860 if (context->doBold) { /* if bold style */ 861 FT_GlyphSlot_Embolden(ftglyph); 862 } 863 if (context->doItalize) { /* if oblique */ 864 FT_GlyphSlot_Oblique(ftglyph); 865 } 866 867 /* generate bitmap if it is not done yet 868 e.g. if algorithmic styling is performed and style was added to outline */ 869 if (ftglyph->format == FT_GLYPH_FORMAT_OUTLINE) { 870 FT_BBox bbox; 871 FT_Outline_Get_CBox(&(ftglyph->outline), &bbox); 872 int w = (int)((bbox.xMax>>6)-(bbox.xMin>>6)); 873 int h = (int)((bbox.yMax>>6)-(bbox.yMin>>6)); 874 if (w > MAX_GLYPH_DIM || h > MAX_GLYPH_DIM) { 875 glyphInfo = getNullGlyphImage(); 876 return ptr_to_jlong(glyphInfo); 877 } 878 error = FT_Render_Glyph(ftglyph, FT_LOAD_TARGET_MODE(target)); 879 if (error != 0) { 880 return ptr_to_jlong(getNullGlyphImage()); 881 } 882 } 883 884 width = (UInt16) ftglyph->bitmap.width; 885 height = (UInt16) ftglyph->bitmap.rows; 886 if (width > MAX_GLYPH_DIM || height > MAX_GLYPH_DIM) { 887 glyphInfo = getNullGlyphImage(); 888 return ptr_to_jlong(glyphInfo); 889 } 890 891 892 imageSize = width*height; 893 glyphInfo = (GlyphInfo*) malloc(sizeof(GlyphInfo) + imageSize); 894 if (glyphInfo == NULL) { 895 glyphInfo = getNullGlyphImage(); 896 return ptr_to_jlong(glyphInfo); 897 } 898 glyphInfo->cellInfo = NULL; 899 glyphInfo->managed = UNMANAGED_GLYPH; 900 glyphInfo->rowBytes = width; 901 glyphInfo->width = width; 902 glyphInfo->height = height; 903 glyphInfo->topLeftX = (float) ftglyph->bitmap_left; 904 glyphInfo->topLeftY = (float) -ftglyph->bitmap_top; 905 906 if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) { 907 glyphInfo->width = width/3; 908 } else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) { 909 glyphInfo->height = glyphInfo->height/3; 910 } 911 912 if (context->fmType == TEXT_FM_ON) { 913 double advh = FTFixedToFloat(ftglyph->linearHoriAdvance); 914 glyphInfo->advanceX = 915 (float) (advh * FTFixedToFloat(context->transform.xx)); 916 glyphInfo->advanceY = 917 (float) (advh * FTFixedToFloat(context->transform.xy)); 918 } else { 919 if (!ftglyph->advance.y) { 920 glyphInfo->advanceX = 921 (float) FT26Dot6ToInt(ftglyph->advance.x); 922 glyphInfo->advanceY = 0; 923 } else if (!ftglyph->advance.x) { 924 glyphInfo->advanceX = 0; 925 glyphInfo->advanceY = 926 (float) FT26Dot6ToInt(-ftglyph->advance.y); 927 } else { 928 glyphInfo->advanceX = FT26Dot6ToFloat(ftglyph->advance.x); 929 glyphInfo->advanceY = FT26Dot6ToFloat(-ftglyph->advance.y); 930 } 931 } 932 933 if (imageSize == 0) { 934 glyphInfo->image = NULL; 935 } else { 936 glyphInfo->image = (unsigned char*) glyphInfo + sizeof(GlyphInfo); 937 //convert result to output format 938 //output format is either 3 bytes per pixel (for subpixel modes) 939 // or 1 byte per pixel for AA and B&W 940 if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { 941 /* convert from 8 pixels per byte to 1 byte per pixel */ 942 CopyBW2Grey8(ftglyph->bitmap.buffer, 943 ftglyph->bitmap.pitch, 944 (void *) glyphInfo->image, 945 width, 946 width, 947 height); 948 } else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) { 949 /* byte per pixel to byte per pixel => just copy */ 950 memcpy(glyphInfo->image, ftglyph->bitmap.buffer, imageSize); 951 } else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY4) { 952 /* 4 bits per pixel to byte per pixel */ 953 CopyGrey4ToGrey8(ftglyph->bitmap.buffer, 954 ftglyph->bitmap.pitch, 955 (void *) glyphInfo->image, 956 width, 957 width, 958 height); 959 } else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) { 960 /* 3 bytes per pixel to 3 bytes per pixel */ 961 CopyFTSubpixelToSubpixel(ftglyph->bitmap.buffer, 962 ftglyph->bitmap.pitch, 963 (void *) glyphInfo->image, 964 width, 965 width, 966 height); 967 } else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) { 968 /* 3 bytes per pixel to 3 bytes per pixel */ 969 CopyFTSubpixelVToSubpixel(ftglyph->bitmap.buffer, 970 ftglyph->bitmap.pitch, 971 (void *) glyphInfo->image, 972 width*3, 973 width, 974 height); 975 glyphInfo->rowBytes *=3; 976 } else { 977 free(glyphInfo); 978 glyphInfo = getNullGlyphImage(); 979 } 980 } 981 982 return ptr_to_jlong(glyphInfo); 983 } 984 985 /* 986 * Class: sun_font_FreetypeFontScaler 987 * Method: disposeNativeScaler 988 * Signature: (J)V 989 */ 990 JNIEXPORT void JNICALL 991 Java_sun_font_FreetypeFontScaler_disposeNativeScaler( 992 JNIEnv *env, jobject scaler, jobject font2D, jlong pScaler) { 993 FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler); 994 995 /* Freetype functions *may* cause callback to java 996 that can use cached values. Make sure our cache is up to date. 997 NB: scaler context is not important at this point, can use NULL. */ 998 int errCode = setupFTContext(env, font2D, scalerInfo, NULL); 999 if (errCode) { 1000 return; 1001 } 1002 1003 freeNativeResources(env, scalerInfo); 1004 } 1005 1006 /* 1007 * Class: sun_font_FreetypeFontScaler 1008 * Method: getNumGlyphsNative 1009 * Signature: ()I 1010 */ 1011 JNIEXPORT jint JNICALL 1012 Java_sun_font_FreetypeFontScaler_getNumGlyphsNative( 1013 JNIEnv *env, jobject scaler, jlong pScaler) { 1014 FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler); 1015 1016 if (scalerInfo == NULL || scalerInfo->face == NULL) { /* bad/null scaler */ 1017 /* null scaler can render 1 glyph - "missing glyph" with code 0 1018 (all glyph codes requested by user are mapped to code 0 at 1019 validation step) */ 1020 invalidateJavaScaler(env, scaler, scalerInfo); 1021 return (jint) 1; 1022 } 1023 1024 return (jint) scalerInfo->face->num_glyphs; 1025 } 1026 1027 /* 1028 * Class: sun_font_FreetypeFontScaler 1029 * Method: getMissingGlyphCodeNative 1030 * Signature: ()I 1031 */ 1032 JNIEXPORT jint JNICALL 1033 Java_sun_font_FreetypeFontScaler_getMissingGlyphCodeNative( 1034 JNIEnv *env, jobject scaler, jlong pScaler) { 1035 1036 /* Is it always 0 for freetype? */ 1037 return 0; 1038 } 1039 1040 /* 1041 * Class: sun_font_FreetypeFontScaler 1042 * Method: getGlyphCodeNative 1043 * Signature: (C)I 1044 */ 1045 JNIEXPORT jint JNICALL 1046 Java_sun_font_FreetypeFontScaler_getGlyphCodeNative( 1047 JNIEnv *env, jobject scaler, 1048 jobject font2D, jlong pScaler, jchar charCode) { 1049 1050 FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler); 1051 int errCode; 1052 1053 if (scaler == NULL || scalerInfo->face == NULL) { /* bad/null scaler */ 1054 invalidateJavaScaler(env, scaler, scalerInfo); 1055 return 0; 1056 } 1057 1058 /* Freetype functions *may* cause callback to java 1059 that can use cached values. Make sure our cache is up to date. 1060 Scaler context is not important here, can use NULL. */ 1061 errCode = setupFTContext(env, font2D, scalerInfo, NULL); 1062 if (errCode) { 1063 return 0; 1064 } 1065 1066 return FT_Get_Char_Index(scalerInfo->face, charCode); 1067 } 1068 1069 1070 #define FloatToF26Dot6(x) ((unsigned int) ((x)*64)) 1071 1072 static FT_Outline* getFTOutline(JNIEnv* env, jobject font2D, 1073 FTScalerContext *context, FTScalerInfo* scalerInfo, 1074 jint glyphCode, jfloat xpos, jfloat ypos) { 1075 int renderFlags; 1076 FT_Error error; 1077 FT_GlyphSlot ftglyph; 1078 1079 if (glyphCode >= INVISIBLE_GLYPHS || 1080 isNullScalerContext(context) || scalerInfo == NULL) { 1081 return NULL; 1082 } 1083 1084 error = setupFTContext(env, font2D, scalerInfo, context); 1085 if (error) { 1086 return NULL; 1087 } 1088 1089 renderFlags = FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP; 1090 1091 error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderFlags); 1092 if (error) { 1093 return NULL; 1094 } 1095 1096 ftglyph = scalerInfo->face->glyph; 1097 1098 /* apply styles */ 1099 if (context->doBold) { /* if bold style */ 1100 FT_GlyphSlot_Embolden(ftglyph); 1101 } 1102 if (context->doItalize) { /* if oblique */ 1103 FT_GlyphSlot_Oblique(ftglyph); 1104 } 1105 1106 FT_Outline_Translate(&ftglyph->outline, 1107 FloatToF26Dot6(xpos), 1108 -FloatToF26Dot6(ypos)); 1109 1110 return &ftglyph->outline; 1111 } 1112 1113 #define F26Dot6ToFloat(n) (((float)(n))/((float) 64)) 1114 1115 /* Types of GeneralPath segments. 1116 TODO: pull constants from other place? */ 1117 1118 #define SEG_UNKNOWN -1 1119 #define SEG_MOVETO 0 1120 #define SEG_LINETO 1 1121 #define SEG_QUADTO 2 1122 #define SEG_CUBICTO 3 1123 #define SEG_CLOSE 4 1124 1125 #define WIND_NON_ZERO 0 1126 #define WIND_EVEN_ODD 1 1127 1128 /* Placeholder to accumulate GeneralPath data */ 1129 typedef struct { 1130 jint numTypes; 1131 jint numCoords; 1132 jint lenTypes; 1133 jint lenCoords; 1134 jint wr; 1135 jbyte* pointTypes; 1136 jfloat* pointCoords; 1137 } GPData; 1138 1139 /* returns 0 on failure */ 1140 static int allocateSpaceForGP(GPData* gpdata, int npoints, int ncontours) { 1141 int maxTypes, maxCoords; 1142 1143 /* we may have up to N intermediate points per contour 1144 (and for each point can actually cause new curve to be generated) 1145 In addition we can also have 2 extra point per outline. 1146 */ 1147 maxTypes = 2*npoints + 2*ncontours; 1148 maxCoords = 4*(npoints + 2*ncontours); //we may need to insert 1149 //up to n-1 intermediate points 1150 1151 /* first usage - allocate space and intialize all fields */ 1152 if (gpdata->pointTypes == NULL || gpdata->pointCoords == NULL) { 1153 gpdata->lenTypes = maxTypes; 1154 gpdata->lenCoords = maxCoords; 1155 gpdata->pointTypes = (jbyte*) 1156 malloc(gpdata->lenTypes*sizeof(jbyte)); 1157 gpdata->pointCoords = (jfloat*) 1158 malloc(gpdata->lenCoords*sizeof(jfloat)); 1159 gpdata->numTypes = 0; 1160 gpdata->numCoords = 0; 1161 gpdata->wr = WIND_NON_ZERO; /* By default, outlines are filled 1162 using the non-zero winding rule. */ 1163 } else { 1164 /* do we have enough space? */ 1165 if (gpdata->lenTypes - gpdata->numTypes < maxTypes) { 1166 gpdata->lenTypes += maxTypes; 1167 gpdata->pointTypes = (jbyte*) 1168 realloc(gpdata->pointTypes, gpdata->lenTypes*sizeof(jbyte)); 1169 } 1170 1171 if (gpdata->lenCoords - gpdata->numCoords < maxCoords) { 1172 gpdata->lenCoords += maxCoords; 1173 gpdata->pointCoords = (jfloat*) 1174 realloc(gpdata->pointCoords, gpdata->lenCoords*sizeof(jfloat)); 1175 } 1176 } 1177 1178 /* failure if any of mallocs failed */ 1179 if (gpdata->pointTypes == NULL || gpdata->pointCoords == NULL) 1180 return 0; 1181 else 1182 return 1; 1183 } 1184 1185 static void addSeg(GPData *gp, jbyte type) { 1186 gp->pointTypes[gp->numTypes++] = type; 1187 } 1188 1189 static void addCoords(GPData *gp, FT_Vector *p) { 1190 gp->pointCoords[gp->numCoords++] = F26Dot6ToFloat(p->x); 1191 gp->pointCoords[gp->numCoords++] = -F26Dot6ToFloat(p->y); 1192 } 1193 1194 static int moveTo(FT_Vector *to, GPData *gp) { 1195 if (gp->numCoords) 1196 addSeg(gp, SEG_CLOSE); 1197 addCoords(gp, to); 1198 addSeg(gp, SEG_MOVETO); 1199 return FT_Err_Ok; 1200 } 1201 1202 static int lineTo(FT_Vector *to, GPData *gp) { 1203 addCoords(gp, to); 1204 addSeg(gp, SEG_LINETO); 1205 return FT_Err_Ok; 1206 } 1207 1208 static int conicTo(FT_Vector *control, FT_Vector *to, GPData *gp) { 1209 addCoords(gp, control); 1210 addCoords(gp, to); 1211 addSeg(gp, SEG_QUADTO); 1212 return FT_Err_Ok; 1213 } 1214 1215 static int cubicTo(FT_Vector *control1, 1216 FT_Vector *control2, 1217 FT_Vector *to, 1218 GPData *gp) { 1219 addCoords(gp, control1); 1220 addCoords(gp, control2); 1221 addCoords(gp, to); 1222 addSeg(gp, SEG_CUBICTO); 1223 return FT_Err_Ok; 1224 } 1225 1226 static void addToGP(GPData* gpdata, FT_Outline*outline) { 1227 static const FT_Outline_Funcs outline_funcs = { 1228 (FT_Outline_MoveToFunc) moveTo, 1229 (FT_Outline_LineToFunc) lineTo, 1230 (FT_Outline_ConicToFunc) conicTo, 1231 (FT_Outline_CubicToFunc) cubicTo, 1232 0, /* shift */ 1233 0, /* delta */ 1234 }; 1235 1236 FT_Outline_Decompose(outline, &outline_funcs, gpdata); 1237 if (gpdata->numCoords) 1238 addSeg(gpdata, SEG_CLOSE); 1239 1240 /* If set to 1, the outline will be filled using the even-odd fill rule */ 1241 if (outline->flags & FT_OUTLINE_EVEN_ODD_FILL) { 1242 gpdata->wr = WIND_EVEN_ODD; 1243 } 1244 } 1245 1246 static void freeGP(GPData* gpdata) { 1247 if (gpdata->pointCoords != NULL) { 1248 free(gpdata->pointCoords); 1249 gpdata->pointCoords = NULL; 1250 gpdata->numCoords = 0; 1251 gpdata->lenCoords = 0; 1252 } 1253 if (gpdata->pointTypes != NULL) { 1254 free(gpdata->pointTypes); 1255 gpdata->pointTypes = NULL; 1256 gpdata->numTypes = 0; 1257 gpdata->lenTypes = 0; 1258 } 1259 } 1260 1261 static jobject getGlyphGeneralPath(JNIEnv* env, jobject font2D, 1262 FTScalerContext *context, FTScalerInfo *scalerInfo, 1263 jint glyphCode, jfloat xpos, jfloat ypos) { 1264 1265 FT_Outline* outline; 1266 jobject gp = NULL; 1267 jbyteArray types; 1268 jfloatArray coords; 1269 GPData gpdata; 1270 1271 outline = getFTOutline(env, font2D, context, scalerInfo, 1272 glyphCode, xpos, ypos); 1273 1274 if (outline == NULL || outline->n_points == 0) { 1275 return gp; 1276 } 1277 1278 gpdata.pointTypes = NULL; 1279 gpdata.pointCoords = NULL; 1280 if (!allocateSpaceForGP(&gpdata, outline->n_points, outline->n_contours)) { 1281 return gp; 1282 } 1283 1284 addToGP(&gpdata, outline); 1285 1286 types = (*env)->NewByteArray(env, gpdata.numTypes); 1287 coords = (*env)->NewFloatArray(env, gpdata.numCoords); 1288 1289 if (types && coords) { 1290 (*env)->SetByteArrayRegion(env, types, 0, 1291 gpdata.numTypes, 1292 gpdata.pointTypes); 1293 (*env)->SetFloatArrayRegion(env, coords, 0, 1294 gpdata.numCoords, 1295 gpdata.pointCoords); 1296 gp = (*env)->NewObject(env, 1297 sunFontIDs.gpClass, 1298 sunFontIDs.gpCtr, 1299 gpdata.wr, 1300 types, 1301 gpdata.numTypes, 1302 coords, 1303 gpdata.numCoords); 1304 } 1305 1306 freeGP(&gpdata); 1307 1308 return gp; 1309 } 1310 1311 /* 1312 * Class: sun_font_FreetypeFontScaler 1313 * Method: getGlyphOutlineNative 1314 * Signature: (Lsun/font/Font2D;JIFF)Ljava/awt/geom/GeneralPath; 1315 */ 1316 JNIEXPORT jobject JNICALL 1317 Java_sun_font_FreetypeFontScaler_getGlyphOutlineNative( 1318 JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext, 1319 jlong pScaler, jint glyphCode, jfloat xpos, jfloat ypos) { 1320 1321 FTScalerContext *context = 1322 (FTScalerContext*) jlong_to_ptr(pScalerContext); 1323 FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler); 1324 1325 jobject gp = getGlyphGeneralPath(env, 1326 font2D, 1327 context, 1328 scalerInfo, 1329 glyphCode, 1330 xpos, 1331 ypos); 1332 if (gp == NULL) { /* can be legal */ 1333 gp = (*env)->NewObject(env, 1334 sunFontIDs.gpClass, 1335 sunFontIDs.gpCtrEmpty); 1336 } 1337 return gp; 1338 } 1339 1340 /* 1341 * Class: sun_font_FreetypeFontScaler 1342 * Method: getGlyphOutlineBoundsNative 1343 * Signature: (Lsun/font/Font2D;JI)Ljava/awt/geom/Rectangle2D/Float; 1344 */ 1345 JNIEXPORT jobject JNICALL 1346 Java_sun_font_FreetypeFontScaler_getGlyphOutlineBoundsNative( 1347 JNIEnv *env, jobject scaler, jobject font2D, 1348 jlong pScalerContext, jlong pScaler, jint glyphCode) { 1349 1350 FT_Outline *outline; 1351 FT_BBox bbox; 1352 int error; 1353 jobject bounds; 1354 1355 FTScalerContext *context = 1356 (FTScalerContext*) jlong_to_ptr(pScalerContext); 1357 FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler); 1358 1359 outline = getFTOutline(env, font2D, context, scalerInfo, glyphCode, 0, 0); 1360 if (outline == NULL || outline->n_points == 0) { 1361 /* it is legal case, e.g. invisible glyph */ 1362 bounds = (*env)->NewObject(env, 1363 sunFontIDs.rect2DFloatClass, 1364 sunFontIDs.rect2DFloatCtr); 1365 return bounds; 1366 } 1367 1368 error = FT_Outline_Get_BBox(outline, &bbox); 1369 1370 //convert bbox 1371 if (error || bbox.xMin >= bbox.xMax || bbox.yMin >= bbox.yMax) { 1372 bounds = (*env)->NewObject(env, 1373 sunFontIDs.rect2DFloatClass, 1374 sunFontIDs.rect2DFloatCtr); 1375 } else { 1376 bounds = (*env)->NewObject(env, 1377 sunFontIDs.rect2DFloatClass, 1378 sunFontIDs.rect2DFloatCtr4, 1379 F26Dot6ToFloat(bbox.xMin), 1380 F26Dot6ToFloat(-bbox.yMax), 1381 F26Dot6ToFloat(bbox.xMax-bbox.xMin), 1382 F26Dot6ToFloat(bbox.yMax-bbox.yMin)); 1383 } 1384 1385 return bounds; 1386 } 1387 1388 /* 1389 * Class: sun_font_FreetypeFontScaler 1390 * Method: getGlyphVectorOutlineNative 1391 * Signature: (Lsun/font/Font2D;J[IIFF)Ljava/awt/geom/GeneralPath; 1392 */ 1393 JNIEXPORT jobject 1394 JNICALL 1395 Java_sun_font_FreetypeFontScaler_getGlyphVectorOutlineNative( 1396 JNIEnv *env, jobject scaler, jobject font2D, 1397 jlong pScalerContext, jlong pScaler, 1398 jintArray glyphArray, jint numGlyphs, jfloat xpos, jfloat ypos) { 1399 1400 FT_Outline* outline; 1401 jobject gp = NULL; 1402 jbyteArray types; 1403 jfloatArray coords; 1404 GPData gpdata; 1405 int i; 1406 jint *glyphs; 1407 1408 FTScalerContext *context = 1409 (FTScalerContext*) jlong_to_ptr(pScalerContext); 1410 FTScalerInfo *scalerInfo = 1411 (FTScalerInfo*) jlong_to_ptr(pScaler); 1412 1413 glyphs = NULL; 1414 if (numGlyphs > 0 && 0xffffffffu / sizeof(jint) >= numGlyphs) { 1415 glyphs = (jint*) malloc(numGlyphs*sizeof(jint)); 1416 } 1417 if (glyphs == NULL) { 1418 // We reach here if: 1419 // 1. numGlyphs <= 0, 1420 // 2. overflow check failed, or 1421 // 3. malloc failed. 1422 gp = (*env)->NewObject(env, sunFontIDs.gpClass, sunFontIDs.gpCtrEmpty); 1423 return gp; 1424 } 1425 1426 (*env)->GetIntArrayRegion(env, glyphArray, 0, numGlyphs, glyphs); 1427 1428 gpdata.numCoords = 0; 1429 for (i=0; i<numGlyphs;i++) { 1430 if (glyphs[i] >= INVISIBLE_GLYPHS) { 1431 continue; 1432 } 1433 outline = getFTOutline(env, 1434 font2D, 1435 context, 1436 scalerInfo, 1437 glyphs[i], 1438 xpos, ypos); 1439 1440 if (outline == NULL || outline->n_points == 0) { 1441 continue; 1442 } 1443 1444 gpdata.pointTypes = NULL; 1445 gpdata.pointCoords = NULL; 1446 if (!allocateSpaceForGP(&gpdata, outline->n_points, 1447 outline->n_contours)) { 1448 break; 1449 } 1450 1451 addToGP(&gpdata, outline); 1452 } 1453 free(glyphs); 1454 1455 if (gpdata.numCoords != 0) { 1456 types = (*env)->NewByteArray(env, gpdata.numTypes); 1457 coords = (*env)->NewFloatArray(env, gpdata.numCoords); 1458 1459 if (types && coords) { 1460 (*env)->SetByteArrayRegion(env, types, 0, 1461 gpdata.numTypes, gpdata.pointTypes); 1462 (*env)->SetFloatArrayRegion(env, coords, 0, 1463 gpdata.numCoords, gpdata.pointCoords); 1464 1465 gp=(*env)->NewObject(env, 1466 sunFontIDs.gpClass, 1467 sunFontIDs.gpCtr, 1468 gpdata.wr, 1469 types, 1470 gpdata.numTypes, 1471 coords, 1472 gpdata.numCoords); 1473 return gp; 1474 } 1475 } 1476 return (*env)->NewObject(env, sunFontIDs.gpClass, sunFontIDs.gpCtrEmpty); 1477 } 1478 1479 JNIEXPORT jlong JNICALL 1480 Java_sun_font_FreetypeFontScaler_getUnitsPerEMNative( 1481 JNIEnv *env, jobject scaler, jlong pScaler) { 1482 1483 FTScalerInfo *s = (FTScalerInfo* ) jlong_to_ptr(pScaler); 1484 1485 /* Freetype doc says: 1486 The number of font units per EM square for this face. 1487 This is typically 2048 for TrueType fonts, and 1000 for Type 1 fonts. 1488 Only relevant for scalable formats. 1489 However, layout engine might be not tested with anything but 2048. 1490 1491 NB: test it! */ 1492 if (s != NULL) { 1493 return s->face->units_per_EM; 1494 } 1495 return 2048; 1496 } 1497 1498 /* This native method is called by the OpenType layout engine. */ 1499 JNIEXPORT jobject JNICALL 1500 Java_sun_font_FreetypeFontScaler_getGlyphPointNative( 1501 JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext, 1502 jlong pScaler, jint glyphCode, jint pointNumber) { 1503 1504 FT_Outline* outline; 1505 jobject point = NULL; 1506 jfloat x=0, y=0; 1507 FTScalerContext *context = 1508 (FTScalerContext*) jlong_to_ptr(pScalerContext); 1509 FTScalerInfo *scalerInfo = (FTScalerInfo*) jlong_to_ptr(pScaler); 1510 1511 outline = getFTOutline(env, font2D, context, scalerInfo, glyphCode, 0, 0); 1512 1513 if (outline != NULL && outline->n_points > pointNumber) { 1514 x = F26Dot6ToFloat(outline->points[pointNumber].x); 1515 y = -F26Dot6ToFloat(outline->points[pointNumber].y); 1516 } 1517 1518 return (*env)->NewObject(env, sunFontIDs.pt2DFloatClass, 1519 sunFontIDs.pt2DFloatCtr, x, y); 1520 }