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