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 static jlong 615 getGlyphImageNativeInternal( 616 JNIEnv *env, jobject scaler, jobject font2D, 617 jlong pScalerContext, jlong pScaler, jint glyphCode, 618 jboolean renderImage); 619 620 /* 621 * Class: sun_font_FreetypeFontScaler 622 * Method: getGlyphAdvanceNative 623 * Signature: (Lsun/font/Font2D;JI)F 624 */ 625 JNIEXPORT jfloat JNICALL 626 Java_sun_font_FreetypeFontScaler_getGlyphAdvanceNative( 627 JNIEnv *env, jobject scaler, jobject font2D, 628 jlong pScalerContext, jlong pScaler, jint glyphCode) { 629 630 /* This method is rarely used because requests for metrics are usually 631 * coupled with a request for the bitmap and to a large extent the 632 * work can be reused (to find out metrics we may need to hint the glyph). 633 * So, we typically go through the getGlyphImage code path. 634 * When we do get here, we need to pass a parameter which indicates 635 * that we don't need freetype to render the bitmap, and consequently 636 * don't need to allocate our own storage either. 637 * This is also important when enter here requesting metrics for sizes 638 * of text which a large size would be rejected for a bitmap but we 639 * still need the metrics. 640 */ 641 642 GlyphInfo *info; 643 jfloat advance = 0.0f; 644 jlong image; 645 646 image = getGlyphImageNativeInternal( 647 env, scaler, font2D, pScalerContext, pScaler, glyphCode, JNI_FALSE); 648 info = (GlyphInfo*) jlong_to_ptr(image); 649 650 if (info != NULL) { 651 advance = info->advanceX; 652 free(info); 653 } 654 655 return advance; 656 } 657 658 /* 659 * Class: sun_font_FreetypeFontScaler 660 * Method: getGlyphMetricsNative 661 * Signature: (Lsun/font/Font2D;JILjava/awt/geom/Point2D/Float;)V 662 */ 663 JNIEXPORT void JNICALL 664 Java_sun_font_FreetypeFontScaler_getGlyphMetricsNative( 665 JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext, 666 jlong pScaler, jint glyphCode, jobject metrics) { 667 668 /* See the comments in getGlyphMetricsNative. They apply here too. */ 669 GlyphInfo *info; 670 671 jlong image = getGlyphImageNativeInternal( 672 env, scaler, font2D, 673 pScalerContext, pScaler, glyphCode, JNI_FALSE); 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 return getGlyphImageNativeInternal( 808 env, scaler, font2D, 809 pScalerContext, pScaler, glyphCode, JNI_TRUE); 810 } 811 812 static jlong 813 getGlyphImageNativeInternal( 814 JNIEnv *env, jobject scaler, jobject font2D, 815 jlong pScalerContext, jlong pScaler, jint glyphCode, 816 jboolean renderImage) { 817 818 int error, imageSize; 819 UInt16 width, height; 820 GlyphInfo *glyphInfo; 821 int renderFlags = FT_LOAD_DEFAULT, target; 822 FT_GlyphSlot ftglyph; 823 824 FTScalerContext* context = 825 (FTScalerContext*) jlong_to_ptr(pScalerContext); 826 FTScalerInfo *scalerInfo = 827 (FTScalerInfo*) jlong_to_ptr(pScaler); 828 829 if (isNullScalerContext(context) || scalerInfo == NULL) { 830 return ptr_to_jlong(getNullGlyphImage()); 831 } 832 833 error = setupFTContext(env, font2D, scalerInfo, context); 834 if (error) { 835 invalidateJavaScaler(env, scaler, scalerInfo); 836 return ptr_to_jlong(getNullGlyphImage()); 837 } 838 839 if (!context->useSbits) { 840 renderFlags |= FT_LOAD_NO_BITMAP; 841 } 842 843 /* NB: in case of non identity transform 844 we might also prefer to disable transform before hinting, 845 and apply it explicitly after hinting is performed. 846 Or we can disable hinting. */ 847 848 /* select appropriate hinting mode */ 849 if (context->aaType == TEXT_AA_OFF) { 850 target = FT_LOAD_TARGET_MONO; 851 } else if (context->aaType == TEXT_AA_ON) { 852 target = FT_LOAD_TARGET_NORMAL; 853 } else if (context->aaType == TEXT_AA_LCD_HRGB || 854 context->aaType == TEXT_AA_LCD_HBGR) { 855 target = FT_LOAD_TARGET_LCD; 856 } else { 857 target = FT_LOAD_TARGET_LCD_V; 858 } 859 renderFlags |= target; 860 861 error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderFlags); 862 if (error) { 863 //do not destroy scaler yet. 864 //this can be problem of particular context (e.g. with bad transform) 865 return ptr_to_jlong(getNullGlyphImage()); 866 } 867 868 ftglyph = scalerInfo->face->glyph; 869 870 /* apply styles */ 871 if (context->doBold) { /* if bold style */ 872 FT_GlyphSlot_Embolden(ftglyph); 873 } 874 if (context->doItalize) { /* if oblique */ 875 FT_GlyphSlot_Oblique(ftglyph); 876 } 877 878 /* generate bitmap if it is not done yet 879 e.g. if algorithmic styling is performed and style was added to outline */ 880 if (renderImage && (ftglyph->format == FT_GLYPH_FORMAT_OUTLINE)) { 881 FT_BBox bbox; 882 FT_Outline_Get_CBox(&(ftglyph->outline), &bbox); 883 int w = (int)((bbox.xMax>>6)-(bbox.xMin>>6)); 884 int h = (int)((bbox.yMax>>6)-(bbox.yMin>>6)); 885 if (w > MAX_GLYPH_DIM || h > MAX_GLYPH_DIM) { 886 glyphInfo = getNullGlyphImage(); 887 return ptr_to_jlong(glyphInfo); 888 } 889 error = FT_Render_Glyph(ftglyph, FT_LOAD_TARGET_MODE(target)); 890 if (error != 0) { 891 return ptr_to_jlong(getNullGlyphImage()); 892 } 893 } 894 895 if (renderImage) { 896 width = (UInt16) ftglyph->bitmap.width; 897 height = (UInt16) ftglyph->bitmap.rows; 898 if (width > MAX_GLYPH_DIM || height > MAX_GLYPH_DIM) { 899 glyphInfo = getNullGlyphImage(); 900 return ptr_to_jlong(glyphInfo); 901 } 902 } else { 903 width = 0; 904 height = 0; 905 } 906 907 908 imageSize = width*height; 909 glyphInfo = (GlyphInfo*) malloc(sizeof(GlyphInfo) + imageSize); 910 if (glyphInfo == NULL) { 911 glyphInfo = getNullGlyphImage(); 912 return ptr_to_jlong(glyphInfo); 913 } 914 glyphInfo->cellInfo = NULL; 915 glyphInfo->managed = UNMANAGED_GLYPH; 916 glyphInfo->rowBytes = width; 917 glyphInfo->width = width; 918 glyphInfo->height = height; 919 920 if (renderImage) { 921 glyphInfo->topLeftX = (float) ftglyph->bitmap_left; 922 glyphInfo->topLeftY = (float) -ftglyph->bitmap_top; 923 924 if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) { 925 glyphInfo->width = width/3; 926 } else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) { 927 glyphInfo->height = glyphInfo->height/3; 928 } 929 } 930 931 if (context->fmType == TEXT_FM_ON) { 932 double advh = FTFixedToFloat(ftglyph->linearHoriAdvance); 933 glyphInfo->advanceX = 934 (float) (advh * FTFixedToFloat(context->transform.xx)); 935 glyphInfo->advanceY = 936 (float) (advh * FTFixedToFloat(context->transform.xy)); 937 } else { 938 if (!ftglyph->advance.y) { 939 glyphInfo->advanceX = 940 (float) FT26Dot6ToInt(ftglyph->advance.x); 941 glyphInfo->advanceY = 0; 942 } else if (!ftglyph->advance.x) { 943 glyphInfo->advanceX = 0; 944 glyphInfo->advanceY = 945 (float) FT26Dot6ToInt(-ftglyph->advance.y); 946 } else { 947 glyphInfo->advanceX = FT26Dot6ToFloat(ftglyph->advance.x); 948 glyphInfo->advanceY = FT26Dot6ToFloat(-ftglyph->advance.y); 949 } 950 } 951 952 if (imageSize == 0) { 953 glyphInfo->image = NULL; 954 } else { 955 glyphInfo->image = (unsigned char*) glyphInfo + sizeof(GlyphInfo); 956 //convert result to output format 957 //output format is either 3 bytes per pixel (for subpixel modes) 958 // or 1 byte per pixel for AA and B&W 959 if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { 960 /* convert from 8 pixels per byte to 1 byte per pixel */ 961 CopyBW2Grey8(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_GRAY) { 968 /* byte per pixel to byte per pixel => just copy */ 969 memcpy(glyphInfo->image, ftglyph->bitmap.buffer, imageSize); 970 } else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY4) { 971 /* 4 bits per pixel to byte per pixel */ 972 CopyGrey4ToGrey8(ftglyph->bitmap.buffer, 973 ftglyph->bitmap.pitch, 974 (void *) glyphInfo->image, 975 width, 976 width, 977 height); 978 } else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) { 979 /* 3 bytes per pixel to 3 bytes per pixel */ 980 CopyFTSubpixelToSubpixel(ftglyph->bitmap.buffer, 981 ftglyph->bitmap.pitch, 982 (void *) glyphInfo->image, 983 width, 984 width, 985 height); 986 } else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) { 987 /* 3 bytes per pixel to 3 bytes per pixel */ 988 CopyFTSubpixelVToSubpixel(ftglyph->bitmap.buffer, 989 ftglyph->bitmap.pitch, 990 (void *) glyphInfo->image, 991 width*3, 992 width, 993 height); 994 glyphInfo->rowBytes *=3; 995 } else { 996 free(glyphInfo); 997 glyphInfo = getNullGlyphImage(); 998 } 999 } 1000 1001 return ptr_to_jlong(glyphInfo); 1002 } 1003 1004 /* 1005 * Class: sun_font_FreetypeFontScaler 1006 * Method: disposeNativeScaler 1007 * Signature: (J)V 1008 */ 1009 JNIEXPORT void JNICALL 1010 Java_sun_font_FreetypeFontScaler_disposeNativeScaler( 1011 JNIEnv *env, jobject scaler, jobject font2D, jlong pScaler) { 1012 FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler); 1013 1014 /* Freetype functions *may* cause callback to java 1015 that can use cached values. Make sure our cache is up to date. 1016 NB: scaler context is not important at this point, can use NULL. */ 1017 int errCode = setupFTContext(env, font2D, scalerInfo, NULL); 1018 if (errCode) { 1019 return; 1020 } 1021 1022 freeNativeResources(env, scalerInfo); 1023 } 1024 1025 /* 1026 * Class: sun_font_FreetypeFontScaler 1027 * Method: getNumGlyphsNative 1028 * Signature: ()I 1029 */ 1030 JNIEXPORT jint JNICALL 1031 Java_sun_font_FreetypeFontScaler_getNumGlyphsNative( 1032 JNIEnv *env, jobject scaler, jlong pScaler) { 1033 FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler); 1034 1035 if (scalerInfo == NULL || scalerInfo->face == NULL) { /* bad/null scaler */ 1036 /* null scaler can render 1 glyph - "missing glyph" with code 0 1037 (all glyph codes requested by user are mapped to code 0 at 1038 validation step) */ 1039 invalidateJavaScaler(env, scaler, scalerInfo); 1040 return (jint) 1; 1041 } 1042 1043 return (jint) scalerInfo->face->num_glyphs; 1044 } 1045 1046 /* 1047 * Class: sun_font_FreetypeFontScaler 1048 * Method: getMissingGlyphCodeNative 1049 * Signature: ()I 1050 */ 1051 JNIEXPORT jint JNICALL 1052 Java_sun_font_FreetypeFontScaler_getMissingGlyphCodeNative( 1053 JNIEnv *env, jobject scaler, jlong pScaler) { 1054 1055 /* Is it always 0 for freetype? */ 1056 return 0; 1057 } 1058 1059 /* 1060 * Class: sun_font_FreetypeFontScaler 1061 * Method: getGlyphCodeNative 1062 * Signature: (C)I 1063 */ 1064 JNIEXPORT jint JNICALL 1065 Java_sun_font_FreetypeFontScaler_getGlyphCodeNative( 1066 JNIEnv *env, jobject scaler, 1067 jobject font2D, jlong pScaler, jchar charCode) { 1068 1069 FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler); 1070 int errCode; 1071 1072 if (scaler == NULL || scalerInfo->face == NULL) { /* bad/null scaler */ 1073 invalidateJavaScaler(env, scaler, scalerInfo); 1074 return 0; 1075 } 1076 1077 /* Freetype functions *may* cause callback to java 1078 that can use cached values. Make sure our cache is up to date. 1079 Scaler context is not important here, can use NULL. */ 1080 errCode = setupFTContext(env, font2D, scalerInfo, NULL); 1081 if (errCode) { 1082 return 0; 1083 } 1084 1085 return FT_Get_Char_Index(scalerInfo->face, charCode); 1086 } 1087 1088 1089 #define FloatToF26Dot6(x) ((unsigned int) ((x)*64)) 1090 1091 static FT_Outline* getFTOutline(JNIEnv* env, jobject font2D, 1092 FTScalerContext *context, FTScalerInfo* scalerInfo, 1093 jint glyphCode, jfloat xpos, jfloat ypos) { 1094 int renderFlags; 1095 FT_Error error; 1096 FT_GlyphSlot ftglyph; 1097 1098 if (glyphCode >= INVISIBLE_GLYPHS || 1099 isNullScalerContext(context) || scalerInfo == NULL) { 1100 return NULL; 1101 } 1102 1103 error = setupFTContext(env, font2D, scalerInfo, context); 1104 if (error) { 1105 return NULL; 1106 } 1107 1108 renderFlags = FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP; 1109 1110 error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderFlags); 1111 if (error) { 1112 return NULL; 1113 } 1114 1115 ftglyph = scalerInfo->face->glyph; 1116 1117 /* apply styles */ 1118 if (context->doBold) { /* if bold style */ 1119 FT_GlyphSlot_Embolden(ftglyph); 1120 } 1121 if (context->doItalize) { /* if oblique */ 1122 FT_GlyphSlot_Oblique(ftglyph); 1123 } 1124 1125 FT_Outline_Translate(&ftglyph->outline, 1126 FloatToF26Dot6(xpos), 1127 -FloatToF26Dot6(ypos)); 1128 1129 return &ftglyph->outline; 1130 } 1131 1132 #define F26Dot6ToFloat(n) (((float)(n))/((float) 64)) 1133 1134 /* Types of GeneralPath segments. 1135 TODO: pull constants from other place? */ 1136 1137 #define SEG_UNKNOWN -1 1138 #define SEG_MOVETO 0 1139 #define SEG_LINETO 1 1140 #define SEG_QUADTO 2 1141 #define SEG_CUBICTO 3 1142 #define SEG_CLOSE 4 1143 1144 #define WIND_NON_ZERO 0 1145 #define WIND_EVEN_ODD 1 1146 1147 /* Placeholder to accumulate GeneralPath data */ 1148 typedef struct { 1149 jint numTypes; 1150 jint numCoords; 1151 jint lenTypes; 1152 jint lenCoords; 1153 jint wr; 1154 jbyte* pointTypes; 1155 jfloat* pointCoords; 1156 } GPData; 1157 1158 /* returns 0 on failure */ 1159 static int allocateSpaceForGP(GPData* gpdata, int npoints, int ncontours) { 1160 int maxTypes, maxCoords; 1161 1162 /* we may have up to N intermediate points per contour 1163 (and for each point can actually cause new curve to be generated) 1164 In addition we can also have 2 extra point per outline. 1165 */ 1166 maxTypes = 2*npoints + 2*ncontours; 1167 maxCoords = 4*(npoints + 2*ncontours); //we may need to insert 1168 //up to n-1 intermediate points 1169 1170 /* first usage - allocate space and intialize all fields */ 1171 if (gpdata->pointTypes == NULL || gpdata->pointCoords == NULL) { 1172 gpdata->lenTypes = maxTypes; 1173 gpdata->lenCoords = maxCoords; 1174 gpdata->pointTypes = (jbyte*) 1175 malloc(gpdata->lenTypes*sizeof(jbyte)); 1176 gpdata->pointCoords = (jfloat*) 1177 malloc(gpdata->lenCoords*sizeof(jfloat)); 1178 gpdata->numTypes = 0; 1179 gpdata->numCoords = 0; 1180 gpdata->wr = WIND_NON_ZERO; /* By default, outlines are filled 1181 using the non-zero winding rule. */ 1182 } else { 1183 /* do we have enough space? */ 1184 if (gpdata->lenTypes - gpdata->numTypes < maxTypes) { 1185 gpdata->lenTypes += maxTypes; 1186 gpdata->pointTypes = (jbyte*) 1187 realloc(gpdata->pointTypes, gpdata->lenTypes*sizeof(jbyte)); 1188 } 1189 1190 if (gpdata->lenCoords - gpdata->numCoords < maxCoords) { 1191 gpdata->lenCoords += maxCoords; 1192 gpdata->pointCoords = (jfloat*) 1193 realloc(gpdata->pointCoords, gpdata->lenCoords*sizeof(jfloat)); 1194 } 1195 } 1196 1197 /* failure if any of mallocs failed */ 1198 if (gpdata->pointTypes == NULL || gpdata->pointCoords == NULL) 1199 return 0; 1200 else 1201 return 1; 1202 } 1203 1204 static void addSeg(GPData *gp, jbyte type) { 1205 gp->pointTypes[gp->numTypes++] = type; 1206 } 1207 1208 static void addCoords(GPData *gp, FT_Vector *p) { 1209 gp->pointCoords[gp->numCoords++] = F26Dot6ToFloat(p->x); 1210 gp->pointCoords[gp->numCoords++] = -F26Dot6ToFloat(p->y); 1211 } 1212 1213 static int moveTo(FT_Vector *to, GPData *gp) { 1214 if (gp->numCoords) 1215 addSeg(gp, SEG_CLOSE); 1216 addCoords(gp, to); 1217 addSeg(gp, SEG_MOVETO); 1218 return FT_Err_Ok; 1219 } 1220 1221 static int lineTo(FT_Vector *to, GPData *gp) { 1222 addCoords(gp, to); 1223 addSeg(gp, SEG_LINETO); 1224 return FT_Err_Ok; 1225 } 1226 1227 static int conicTo(FT_Vector *control, FT_Vector *to, GPData *gp) { 1228 addCoords(gp, control); 1229 addCoords(gp, to); 1230 addSeg(gp, SEG_QUADTO); 1231 return FT_Err_Ok; 1232 } 1233 1234 static int cubicTo(FT_Vector *control1, 1235 FT_Vector *control2, 1236 FT_Vector *to, 1237 GPData *gp) { 1238 addCoords(gp, control1); 1239 addCoords(gp, control2); 1240 addCoords(gp, to); 1241 addSeg(gp, SEG_CUBICTO); 1242 return FT_Err_Ok; 1243 } 1244 1245 static void addToGP(GPData* gpdata, FT_Outline*outline) { 1246 static const FT_Outline_Funcs outline_funcs = { 1247 (FT_Outline_MoveToFunc) moveTo, 1248 (FT_Outline_LineToFunc) lineTo, 1249 (FT_Outline_ConicToFunc) conicTo, 1250 (FT_Outline_CubicToFunc) cubicTo, 1251 0, /* shift */ 1252 0, /* delta */ 1253 }; 1254 1255 FT_Outline_Decompose(outline, &outline_funcs, gpdata); 1256 if (gpdata->numCoords) 1257 addSeg(gpdata, SEG_CLOSE); 1258 1259 /* If set to 1, the outline will be filled using the even-odd fill rule */ 1260 if (outline->flags & FT_OUTLINE_EVEN_ODD_FILL) { 1261 gpdata->wr = WIND_EVEN_ODD; 1262 } 1263 } 1264 1265 static void freeGP(GPData* gpdata) { 1266 if (gpdata->pointCoords != NULL) { 1267 free(gpdata->pointCoords); 1268 gpdata->pointCoords = NULL; 1269 gpdata->numCoords = 0; 1270 gpdata->lenCoords = 0; 1271 } 1272 if (gpdata->pointTypes != NULL) { 1273 free(gpdata->pointTypes); 1274 gpdata->pointTypes = NULL; 1275 gpdata->numTypes = 0; 1276 gpdata->lenTypes = 0; 1277 } 1278 } 1279 1280 static jobject getGlyphGeneralPath(JNIEnv* env, jobject font2D, 1281 FTScalerContext *context, FTScalerInfo *scalerInfo, 1282 jint glyphCode, jfloat xpos, jfloat ypos) { 1283 1284 FT_Outline* outline; 1285 jobject gp = NULL; 1286 jbyteArray types; 1287 jfloatArray coords; 1288 GPData gpdata; 1289 1290 outline = getFTOutline(env, font2D, context, scalerInfo, 1291 glyphCode, xpos, ypos); 1292 1293 if (outline == NULL || outline->n_points == 0) { 1294 return gp; 1295 } 1296 1297 gpdata.pointTypes = NULL; 1298 gpdata.pointCoords = NULL; 1299 if (!allocateSpaceForGP(&gpdata, outline->n_points, outline->n_contours)) { 1300 return gp; 1301 } 1302 1303 addToGP(&gpdata, outline); 1304 1305 types = (*env)->NewByteArray(env, gpdata.numTypes); 1306 coords = (*env)->NewFloatArray(env, gpdata.numCoords); 1307 1308 if (types && coords) { 1309 (*env)->SetByteArrayRegion(env, types, 0, 1310 gpdata.numTypes, 1311 gpdata.pointTypes); 1312 (*env)->SetFloatArrayRegion(env, coords, 0, 1313 gpdata.numCoords, 1314 gpdata.pointCoords); 1315 gp = (*env)->NewObject(env, 1316 sunFontIDs.gpClass, 1317 sunFontIDs.gpCtr, 1318 gpdata.wr, 1319 types, 1320 gpdata.numTypes, 1321 coords, 1322 gpdata.numCoords); 1323 } 1324 1325 freeGP(&gpdata); 1326 1327 return gp; 1328 } 1329 1330 /* 1331 * Class: sun_font_FreetypeFontScaler 1332 * Method: getGlyphOutlineNative 1333 * Signature: (Lsun/font/Font2D;JIFF)Ljava/awt/geom/GeneralPath; 1334 */ 1335 JNIEXPORT jobject JNICALL 1336 Java_sun_font_FreetypeFontScaler_getGlyphOutlineNative( 1337 JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext, 1338 jlong pScaler, jint glyphCode, jfloat xpos, jfloat ypos) { 1339 1340 FTScalerContext *context = 1341 (FTScalerContext*) jlong_to_ptr(pScalerContext); 1342 FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler); 1343 1344 jobject gp = getGlyphGeneralPath(env, 1345 font2D, 1346 context, 1347 scalerInfo, 1348 glyphCode, 1349 xpos, 1350 ypos); 1351 if (gp == NULL) { /* can be legal */ 1352 gp = (*env)->NewObject(env, 1353 sunFontIDs.gpClass, 1354 sunFontIDs.gpCtrEmpty); 1355 } 1356 return gp; 1357 } 1358 1359 /* 1360 * Class: sun_font_FreetypeFontScaler 1361 * Method: getGlyphOutlineBoundsNative 1362 * Signature: (Lsun/font/Font2D;JI)Ljava/awt/geom/Rectangle2D/Float; 1363 */ 1364 JNIEXPORT jobject JNICALL 1365 Java_sun_font_FreetypeFontScaler_getGlyphOutlineBoundsNative( 1366 JNIEnv *env, jobject scaler, jobject font2D, 1367 jlong pScalerContext, jlong pScaler, jint glyphCode) { 1368 1369 FT_Outline *outline; 1370 FT_BBox bbox; 1371 int error; 1372 jobject bounds; 1373 1374 FTScalerContext *context = 1375 (FTScalerContext*) jlong_to_ptr(pScalerContext); 1376 FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler); 1377 1378 outline = getFTOutline(env, font2D, context, scalerInfo, glyphCode, 0, 0); 1379 if (outline == NULL || outline->n_points == 0) { 1380 /* it is legal case, e.g. invisible glyph */ 1381 bounds = (*env)->NewObject(env, 1382 sunFontIDs.rect2DFloatClass, 1383 sunFontIDs.rect2DFloatCtr); 1384 return bounds; 1385 } 1386 1387 error = FT_Outline_Get_BBox(outline, &bbox); 1388 1389 //convert bbox 1390 if (error || bbox.xMin >= bbox.xMax || bbox.yMin >= bbox.yMax) { 1391 bounds = (*env)->NewObject(env, 1392 sunFontIDs.rect2DFloatClass, 1393 sunFontIDs.rect2DFloatCtr); 1394 } else { 1395 bounds = (*env)->NewObject(env, 1396 sunFontIDs.rect2DFloatClass, 1397 sunFontIDs.rect2DFloatCtr4, 1398 F26Dot6ToFloat(bbox.xMin), 1399 F26Dot6ToFloat(-bbox.yMax), 1400 F26Dot6ToFloat(bbox.xMax-bbox.xMin), 1401 F26Dot6ToFloat(bbox.yMax-bbox.yMin)); 1402 } 1403 1404 return bounds; 1405 } 1406 1407 /* 1408 * Class: sun_font_FreetypeFontScaler 1409 * Method: getGlyphVectorOutlineNative 1410 * Signature: (Lsun/font/Font2D;J[IIFF)Ljava/awt/geom/GeneralPath; 1411 */ 1412 JNIEXPORT jobject 1413 JNICALL 1414 Java_sun_font_FreetypeFontScaler_getGlyphVectorOutlineNative( 1415 JNIEnv *env, jobject scaler, jobject font2D, 1416 jlong pScalerContext, jlong pScaler, 1417 jintArray glyphArray, jint numGlyphs, jfloat xpos, jfloat ypos) { 1418 1419 FT_Outline* outline; 1420 jobject gp = NULL; 1421 jbyteArray types; 1422 jfloatArray coords; 1423 GPData gpdata; 1424 int i; 1425 jint *glyphs; 1426 1427 FTScalerContext *context = 1428 (FTScalerContext*) jlong_to_ptr(pScalerContext); 1429 FTScalerInfo *scalerInfo = 1430 (FTScalerInfo*) jlong_to_ptr(pScaler); 1431 1432 glyphs = NULL; 1433 if (numGlyphs > 0 && 0xffffffffu / sizeof(jint) >= numGlyphs) { 1434 glyphs = (jint*) malloc(numGlyphs*sizeof(jint)); 1435 } 1436 if (glyphs == NULL) { 1437 // We reach here if: 1438 // 1. numGlyphs <= 0, 1439 // 2. overflow check failed, or 1440 // 3. malloc failed. 1441 gp = (*env)->NewObject(env, sunFontIDs.gpClass, sunFontIDs.gpCtrEmpty); 1442 return gp; 1443 } 1444 1445 (*env)->GetIntArrayRegion(env, glyphArray, 0, numGlyphs, glyphs); 1446 1447 gpdata.numCoords = 0; 1448 for (i=0; i<numGlyphs;i++) { 1449 if (glyphs[i] >= INVISIBLE_GLYPHS) { 1450 continue; 1451 } 1452 outline = getFTOutline(env, 1453 font2D, 1454 context, 1455 scalerInfo, 1456 glyphs[i], 1457 xpos, ypos); 1458 1459 if (outline == NULL || outline->n_points == 0) { 1460 continue; 1461 } 1462 1463 gpdata.pointTypes = NULL; 1464 gpdata.pointCoords = NULL; 1465 if (!allocateSpaceForGP(&gpdata, outline->n_points, 1466 outline->n_contours)) { 1467 break; 1468 } 1469 1470 addToGP(&gpdata, outline); 1471 } 1472 free(glyphs); 1473 1474 if (gpdata.numCoords != 0) { 1475 types = (*env)->NewByteArray(env, gpdata.numTypes); 1476 coords = (*env)->NewFloatArray(env, gpdata.numCoords); 1477 1478 if (types && coords) { 1479 (*env)->SetByteArrayRegion(env, types, 0, 1480 gpdata.numTypes, gpdata.pointTypes); 1481 (*env)->SetFloatArrayRegion(env, coords, 0, 1482 gpdata.numCoords, gpdata.pointCoords); 1483 1484 gp=(*env)->NewObject(env, 1485 sunFontIDs.gpClass, 1486 sunFontIDs.gpCtr, 1487 gpdata.wr, 1488 types, 1489 gpdata.numTypes, 1490 coords, 1491 gpdata.numCoords); 1492 return gp; 1493 } 1494 } 1495 return (*env)->NewObject(env, sunFontIDs.gpClass, sunFontIDs.gpCtrEmpty); 1496 } 1497 1498 JNIEXPORT jlong JNICALL 1499 Java_sun_font_FreetypeFontScaler_getUnitsPerEMNative( 1500 JNIEnv *env, jobject scaler, jlong pScaler) { 1501 1502 FTScalerInfo *s = (FTScalerInfo* ) jlong_to_ptr(pScaler); 1503 1504 /* Freetype doc says: 1505 The number of font units per EM square for this face. 1506 This is typically 2048 for TrueType fonts, and 1000 for Type 1 fonts. 1507 Only relevant for scalable formats. 1508 However, layout engine might be not tested with anything but 2048. 1509 1510 NB: test it! */ 1511 if (s != NULL) { 1512 return s->face->units_per_EM; 1513 } 1514 return 2048; 1515 } 1516 1517 /* This native method is called by the OpenType layout engine. */ 1518 JNIEXPORT jobject JNICALL 1519 Java_sun_font_FreetypeFontScaler_getGlyphPointNative( 1520 JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext, 1521 jlong pScaler, jint glyphCode, jint pointNumber) { 1522 1523 FT_Outline* outline; 1524 jobject point = NULL; 1525 jfloat x=0, y=0; 1526 FTScalerContext *context = 1527 (FTScalerContext*) jlong_to_ptr(pScalerContext); 1528 FTScalerInfo *scalerInfo = (FTScalerInfo*) jlong_to_ptr(pScaler); 1529 1530 outline = getFTOutline(env, font2D, context, scalerInfo, glyphCode, 0, 0); 1531 1532 if (outline != NULL && outline->n_points > pointNumber) { 1533 x = F26Dot6ToFloat(outline->points[pointNumber].x); 1534 y = -F26Dot6ToFloat(outline->points[pointNumber].y); 1535 } 1536 1537 return (*env)->NewObject(env, sunFontIDs.pt2DFloatClass, 1538 sunFontIDs.pt2DFloatCtr, x, y); 1539 }