1 /* 2 * Copyright (c) 2007, 2013, 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, 0x06000, 0x0, 0x10000) matrix to get oblique 409 outline. Therefore x coordinate will change by 0x06000*y. 410 Note that y coordinate does not change. */ 411 #define OBLIQUE_MODIFIER(y) (context->doItalize ? ((y)*6/16) : 0) 412 413 /* 414 * Class: sun_font_FreetypeFontScaler 415 * Method: getFontMetricsNative 416 * Signature: (Lsun/font/Font2D;J)Lsun/font/StrikeMetrics; 417 */ 418 JNIEXPORT jobject JNICALL 419 Java_sun_font_FreetypeFontScaler_getFontMetricsNative( 420 JNIEnv *env, jobject scaler, jobject font2D, 421 jlong pScalerContext, jlong pScaler) { 422 423 jobject metrics; 424 jfloat ax, ay, dx, dy, bx, by, lx, ly, mx, my; 425 jfloat f0 = 0.0; 426 FTScalerContext *context = 427 (FTScalerContext*) jlong_to_ptr(pScalerContext); 428 FTScalerInfo *scalerInfo = 429 (FTScalerInfo*) jlong_to_ptr(pScaler); 430 431 int errCode; 432 433 if (isNullScalerContext(context) || scalerInfo == NULL) { 434 return (*env)->NewObject(env, 435 sunFontIDs.strikeMetricsClass, 436 sunFontIDs.strikeMetricsCtr, 437 f0, f0, f0, f0, f0, f0, f0, f0, f0, f0); 438 } 439 440 errCode = setupFTContext(env, font2D, scalerInfo, context); 441 442 if (errCode) { 443 metrics = (*env)->NewObject(env, 444 sunFontIDs.strikeMetricsClass, 445 sunFontIDs.strikeMetricsCtr, 446 f0, f0, f0, f0, f0, f0, f0, f0, f0, f0); 447 invalidateJavaScaler(env, scaler, scalerInfo); 448 return metrics; 449 } 450 451 /* This is ugly and has to be reworked. 452 Freetype provide means to add style to glyph but 453 it seems there is no way to adjust metrics accordingly. 454 455 So, we have to do adust them explicitly and stay consistent with what 456 freetype does to outlines. */ 457 458 459 /**** Note: only some metrics are affected by styling ***/ 460 461 /* See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=657854 */ 462 #define FT_MulFixFloatShift6(a, b) (((float) (a)) * ((float) (b)) / 65536.0 / 64.0) 463 464 /* 465 * See FreeType source code: src/base/ftobjs.c ft_recompute_scaled_metrics() 466 * http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=1659 467 */ 468 /* ascent */ 469 ax = 0; 470 ay = -(jfloat) (FT_MulFixFloatShift6( 471 ((jlong) scalerInfo->face->ascender), 472 (jlong) scalerInfo->face->size->metrics.y_scale)); 473 /* descent */ 474 dx = 0; 475 dy = -(jfloat) (FT_MulFixFloatShift6( 476 ((jlong) scalerInfo->face->descender), 477 (jlong) scalerInfo->face->size->metrics.y_scale)); 478 /* baseline */ 479 bx = by = 0; 480 481 /* leading */ 482 lx = 0; 483 ly = (jfloat) (FT_MulFixFloatShift6( 484 (jlong) scalerInfo->face->height, 485 (jlong) scalerInfo->face->size->metrics.y_scale)) 486 + ay - dy; 487 /* max advance */ 488 mx = (jfloat) FT26Dot6ToFloat( 489 scalerInfo->face->size->metrics.max_advance + 490 OBLIQUE_MODIFIER(scalerInfo->face->size->metrics.height)); 491 my = 0; 492 493 metrics = (*env)->NewObject(env, 494 sunFontIDs.strikeMetricsClass, 495 sunFontIDs.strikeMetricsCtr, 496 ax, ay, dx, dy, bx, by, lx, ly, mx, my); 497 498 return metrics; 499 } 500 501 /* 502 * Class: sun_font_FreetypeFontScaler 503 * Method: getGlyphAdvanceNative 504 * Signature: (Lsun/font/Font2D;JI)F 505 */ 506 JNIEXPORT jfloat JNICALL 507 Java_sun_font_FreetypeFontScaler_getGlyphAdvanceNative( 508 JNIEnv *env, jobject scaler, jobject font2D, 509 jlong pScalerContext, jlong pScaler, jint glyphCode) { 510 511 /* This method is rarely used because requests for metrics are usually 512 coupled with request for bitmap and to large extend work can be reused 513 (to find out metrics we need to hint glyph). 514 So, we typically go through getGlyphImage code path. 515 516 For initial freetype implementation we delegate 517 all work to getGlyphImage but drop result image. 518 This is waste of work related to scan conversion and conversion from 519 freetype format to our format but for now this seems to be ok. 520 521 NB: investigate performance benefits of refactoring code 522 to avoid unnecesary work with bitmaps. */ 523 524 GlyphInfo *info; 525 jfloat advance; 526 jlong image; 527 528 image = Java_sun_font_FreetypeFontScaler_getGlyphImageNative( 529 env, scaler, font2D, pScalerContext, pScaler, glyphCode); 530 info = (GlyphInfo*) jlong_to_ptr(image); 531 532 advance = info->advanceX; 533 534 free(info); 535 536 return advance; 537 } 538 539 /* 540 * Class: sun_font_FreetypeFontScaler 541 * Method: getGlyphMetricsNative 542 * Signature: (Lsun/font/Font2D;JILjava/awt/geom/Point2D/Float;)V 543 */ 544 JNIEXPORT void JNICALL 545 Java_sun_font_FreetypeFontScaler_getGlyphMetricsNative( 546 JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext, 547 jlong pScaler, jint glyphCode, jobject metrics) { 548 549 /* As initial implementation we delegate all work to getGlyphImage 550 but drop result image. This is clearly waste of resorces. 551 552 TODO: investigate performance benefits of refactoring code 553 by avoiding bitmap generation and conversion from FT 554 bitmap format. */ 555 GlyphInfo *info; 556 557 jlong image = Java_sun_font_FreetypeFontScaler_getGlyphImageNative( 558 env, scaler, font2D, 559 pScalerContext, pScaler, glyphCode); 560 info = (GlyphInfo*) jlong_to_ptr(image); 561 562 (*env)->SetFloatField(env, metrics, sunFontIDs.xFID, info->advanceX); 563 (*env)->SetFloatField(env, metrics, sunFontIDs.yFID, info->advanceY); 564 565 free(info); 566 } 567 568 569 static GlyphInfo* getNullGlyphImage() { 570 GlyphInfo *glyphInfo = (GlyphInfo*) calloc(1, sizeof(GlyphInfo)); 571 return glyphInfo; 572 } 573 574 static void CopyBW2Grey8(const void* srcImage, int srcRowBytes, 575 void* dstImage, int dstRowBytes, 576 int width, int height) { 577 const UInt8* srcRow = (UInt8*)srcImage; 578 UInt8* dstRow = (UInt8*)dstImage; 579 int wholeByteCount = width >> 3; 580 int remainingBitsCount = width & 7; 581 int i, j; 582 583 while (height--) { 584 const UInt8* src8 = srcRow; 585 UInt8* dstByte = dstRow; 586 unsigned srcValue; 587 588 srcRow += srcRowBytes; 589 dstRow += dstRowBytes; 590 591 for (i = 0; i < wholeByteCount; i++) { 592 srcValue = *src8++; 593 for (j = 0; j < 8; j++) { 594 *dstByte++ = (srcValue & 0x80) ? 0xFF : 0; 595 srcValue <<= 1; 596 } 597 } 598 if (remainingBitsCount) { 599 srcValue = *src8; 600 for (j = 0; j < remainingBitsCount; j++) { 601 *dstByte++ = (srcValue & 0x80) ? 0xFF : 0; 602 srcValue <<= 1; 603 } 604 } 605 } 606 } 607 608 #define Grey4ToAlpha255(value) (((value) << 4) + ((value) >> 3)) 609 610 static void CopyGrey4ToGrey8(const void* srcImage, int srcRowBytes, 611 void* dstImage, int dstRowBytes, int width, int height) { 612 const UInt8* srcRow = (UInt8*) srcImage; 613 UInt8* dstRow = (UInt8*) dstImage; 614 int i; 615 616 while (height--) { 617 const UInt8* src8 = srcRow; 618 UInt8* dstByte = dstRow; 619 unsigned srcValue; 620 621 srcRow += srcRowBytes; 622 dstRow += dstRowBytes; 623 624 for (i = 0; i < width; i++) { 625 srcValue = *src8++; 626 *dstByte++ = Grey4ToAlpha255(srcValue & 0x0f); 627 *dstByte++ = Grey4ToAlpha255(srcValue >> 4); 628 } 629 } 630 } 631 632 /* We need it because FT rows are often padded to 4 byte boundaries 633 and our internal format is not padded */ 634 static void CopyFTSubpixelToSubpixel(const void* srcImage, int srcRowBytes, 635 void* dstImage, int dstRowBytes, 636 int width, int height) { 637 unsigned char *srcRow = (unsigned char *) srcImage; 638 unsigned char *dstRow = (unsigned char *) dstImage; 639 640 while (height--) { 641 memcpy(dstRow, srcRow, width); 642 srcRow += srcRowBytes; 643 dstRow += dstRowBytes; 644 } 645 } 646 647 /* We need it because FT rows are often padded to 4 byte boundaries 648 and our internal format is not padded */ 649 static void CopyFTSubpixelVToSubpixel(const void* srcImage, int srcRowBytes, 650 void* dstImage, int dstRowBytes, 651 int width, int height) { 652 unsigned char *srcRow = (unsigned char *) srcImage, *srcByte; 653 unsigned char *dstRow = (unsigned char *) dstImage, *dstByte; 654 int i; 655 656 while (height > 0) { 657 srcByte = srcRow; 658 dstByte = dstRow; 659 for (i = 0; i < width; i++) { 660 *dstByte++ = *srcByte; 661 *dstByte++ = *(srcByte + srcRowBytes); 662 *dstByte++ = *(srcByte + 2*srcRowBytes); 663 srcByte++; 664 } 665 srcRow += 3*srcRowBytes; 666 dstRow += dstRowBytes; 667 height -= 3; 668 } 669 } 670 671 672 /* 673 * Class: sun_font_FreetypeFontScaler 674 * Method: getGlyphImageNative 675 * Signature: (Lsun/font/Font2D;JI)J 676 */ 677 JNIEXPORT jlong JNICALL 678 Java_sun_font_FreetypeFontScaler_getGlyphImageNative( 679 JNIEnv *env, jobject scaler, jobject font2D, 680 jlong pScalerContext, jlong pScaler, jint glyphCode) { 681 682 int error, imageSize; 683 UInt16 width, height; 684 GlyphInfo *glyphInfo; 685 int renderFlags = FT_LOAD_RENDER, target; 686 FT_GlyphSlot ftglyph; 687 688 FTScalerContext* context = 689 (FTScalerContext*) jlong_to_ptr(pScalerContext); 690 FTScalerInfo *scalerInfo = 691 (FTScalerInfo*) jlong_to_ptr(pScaler); 692 693 if (isNullScalerContext(context) || scalerInfo == NULL) { 694 return ptr_to_jlong(getNullGlyphImage()); 695 } 696 697 error = setupFTContext(env, font2D, scalerInfo, context); 698 if (error) { 699 invalidateJavaScaler(env, scaler, scalerInfo); 700 return ptr_to_jlong(getNullGlyphImage()); 701 } 702 703 if (!context->useSbits) { 704 renderFlags |= FT_LOAD_NO_BITMAP; 705 } 706 707 /* NB: in case of non identity transform 708 we might also prefer to disable transform before hinting, 709 and apply it explicitly after hinting is performed. 710 Or we can disable hinting. */ 711 712 /* select appropriate hinting mode */ 713 if (context->aaType == TEXT_AA_OFF) { 714 target = FT_LOAD_TARGET_MONO; 715 } else if (context->aaType == TEXT_AA_ON) { 716 target = FT_LOAD_TARGET_NORMAL; 717 } else if (context->aaType == TEXT_AA_LCD_HRGB || 718 context->aaType == TEXT_AA_LCD_HBGR) { 719 target = FT_LOAD_TARGET_LCD; 720 } else { 721 target = FT_LOAD_TARGET_LCD_V; 722 } 723 renderFlags |= target; 724 725 error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderFlags); 726 if (error) { 727 //do not destroy scaler yet. 728 //this can be problem of particular context (e.g. with bad transform) 729 return ptr_to_jlong(getNullGlyphImage()); 730 } 731 732 ftglyph = scalerInfo->face->glyph; 733 734 /* apply styles */ 735 if (context->doBold) { /* if bold style */ 736 FT_GlyphSlot_Embolden(ftglyph); 737 } 738 if (context->doItalize) { /* if oblique */ 739 FT_GlyphSlot_Oblique(ftglyph); 740 } 741 742 /* generate bitmap if it is not done yet 743 e.g. if algorithmic styling is performed and style was added to outline */ 744 if (ftglyph->format == FT_GLYPH_FORMAT_OUTLINE) { 745 FT_Render_Glyph(ftglyph, FT_LOAD_TARGET_MODE(target)); 746 } 747 748 width = (UInt16) ftglyph->bitmap.width; 749 height = (UInt16) ftglyph->bitmap.rows; 750 751 imageSize = width*height; 752 glyphInfo = (GlyphInfo*) malloc(sizeof(GlyphInfo) + imageSize); 753 if (glyphInfo == NULL) { 754 glyphInfo = getNullGlyphImage(); 755 return ptr_to_jlong(glyphInfo); 756 } 757 glyphInfo->cellInfo = NULL; 758 glyphInfo->managed = UNMANAGED_GLYPH; 759 glyphInfo->rowBytes = width; 760 glyphInfo->width = width; 761 glyphInfo->height = height; 762 glyphInfo->topLeftX = (float) ftglyph->bitmap_left; 763 glyphInfo->topLeftY = (float) -ftglyph->bitmap_top; 764 765 if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) { 766 glyphInfo->width = width/3; 767 } else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) { 768 glyphInfo->height = glyphInfo->height/3; 769 } 770 771 if (context->fmType == TEXT_FM_ON) { 772 double advh = FTFixedToFloat(ftglyph->linearHoriAdvance); 773 glyphInfo->advanceX = 774 (float) (advh * FTFixedToFloat(context->transform.xx)); 775 glyphInfo->advanceY = 776 (float) (advh * FTFixedToFloat(context->transform.xy)); 777 } else { 778 if (!ftglyph->advance.y) { 779 glyphInfo->advanceX = 780 (float) FT26Dot6ToInt(ftglyph->advance.x); 781 glyphInfo->advanceY = 0; 782 } else if (!ftglyph->advance.x) { 783 glyphInfo->advanceX = 0; 784 glyphInfo->advanceY = 785 (float) FT26Dot6ToInt(-ftglyph->advance.y); 786 } else { 787 glyphInfo->advanceX = FT26Dot6ToFloat(ftglyph->advance.x); 788 glyphInfo->advanceY = FT26Dot6ToFloat(-ftglyph->advance.y); 789 } 790 } 791 792 if (imageSize == 0) { 793 glyphInfo->image = NULL; 794 } else { 795 glyphInfo->image = (unsigned char*) glyphInfo + sizeof(GlyphInfo); 796 //convert result to output format 797 //output format is either 3 bytes per pixel (for subpixel modes) 798 // or 1 byte per pixel for AA and B&W 799 if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { 800 /* convert from 8 pixels per byte to 1 byte per pixel */ 801 CopyBW2Grey8(ftglyph->bitmap.buffer, 802 ftglyph->bitmap.pitch, 803 (void *) glyphInfo->image, 804 width, 805 width, 806 height); 807 } else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) { 808 /* byte per pixel to byte per pixel => just copy */ 809 memcpy(glyphInfo->image, ftglyph->bitmap.buffer, imageSize); 810 } else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY4) { 811 /* 4 bits per pixel to byte per pixel */ 812 CopyGrey4ToGrey8(ftglyph->bitmap.buffer, 813 ftglyph->bitmap.pitch, 814 (void *) glyphInfo->image, 815 width, 816 width, 817 height); 818 } else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) { 819 /* 3 bytes per pixel to 3 bytes per pixel */ 820 CopyFTSubpixelToSubpixel(ftglyph->bitmap.buffer, 821 ftglyph->bitmap.pitch, 822 (void *) glyphInfo->image, 823 width, 824 width, 825 height); 826 } else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) { 827 /* 3 bytes per pixel to 3 bytes per pixel */ 828 CopyFTSubpixelVToSubpixel(ftglyph->bitmap.buffer, 829 ftglyph->bitmap.pitch, 830 (void *) glyphInfo->image, 831 width*3, 832 width, 833 height); 834 glyphInfo->rowBytes *=3; 835 } else { 836 free(glyphInfo); 837 glyphInfo = getNullGlyphImage(); 838 } 839 } 840 841 return ptr_to_jlong(glyphInfo); 842 } 843 844 845 /* 846 * Class: sun_font_FreetypeFontScaler 847 * Method: getLayoutTableCacheNative 848 * Signature: (J)J 849 */ 850 JNIEXPORT jlong JNICALL 851 Java_sun_font_FreetypeFontScaler_getLayoutTableCacheNative( 852 JNIEnv *env, jobject scaler, jlong pScaler) { 853 FTScalerInfo *scalerInfo = (FTScalerInfo*) jlong_to_ptr(pScaler); 854 855 if (scalerInfo == NULL) { 856 invalidateJavaScaler(env, scaler, scalerInfo); 857 return 0L; 858 } 859 860 // init layout table cache in font 861 // we're assuming the font is a file font and moreover it is Truetype font 862 // otherwise we shouldn't be able to get here... 863 if (scalerInfo->layoutTables == NULL) { 864 scalerInfo->layoutTables = newLayoutTableCache(); 865 } 866 867 return ptr_to_jlong(scalerInfo->layoutTables); 868 } 869 870 /* 871 * Class: sun_font_FreetypeFontScaler 872 * Method: disposeNativeScaler 873 * Signature: (J)V 874 */ 875 JNIEXPORT void JNICALL 876 Java_sun_font_FreetypeFontScaler_disposeNativeScaler( 877 JNIEnv *env, jobject scaler, jobject font2D, jlong pScaler) { 878 FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler); 879 880 /* Freetype functions *may* cause callback to java 881 that can use cached values. Make sure our cache is up to date. 882 NB: scaler context is not important at this point, can use NULL. */ 883 int errCode = setupFTContext(env, font2D, scalerInfo, NULL); 884 if (errCode) { 885 return; 886 } 887 888 freeNativeResources(env, scalerInfo); 889 } 890 891 /* 892 * Class: sun_font_FreetypeFontScaler 893 * Method: getNumGlyphsNative 894 * Signature: ()I 895 */ 896 JNIEXPORT jint JNICALL 897 Java_sun_font_FreetypeFontScaler_getNumGlyphsNative( 898 JNIEnv *env, jobject scaler, jlong pScaler) { 899 FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler); 900 901 if (scalerInfo == NULL || scalerInfo->face == NULL) { /* bad/null scaler */ 902 /* null scaler can render 1 glyph - "missing glyph" with code 0 903 (all glyph codes requested by user are mapped to code 0 at 904 validation step) */ 905 invalidateJavaScaler(env, scaler, scalerInfo); 906 return (jint) 1; 907 } 908 909 return (jint) scalerInfo->face->num_glyphs; 910 } 911 912 /* 913 * Class: sun_font_FreetypeFontScaler 914 * Method: getMissingGlyphCodeNative 915 * Signature: ()I 916 */ 917 JNIEXPORT jint JNICALL 918 Java_sun_font_FreetypeFontScaler_getMissingGlyphCodeNative( 919 JNIEnv *env, jobject scaler, jlong pScaler) { 920 921 /* Is it always 0 for freetype? */ 922 return 0; 923 } 924 925 /* 926 * Class: sun_font_FreetypeFontScaler 927 * Method: getGlyphCodeNative 928 * Signature: (C)I 929 */ 930 JNIEXPORT jint JNICALL 931 Java_sun_font_FreetypeFontScaler_getGlyphCodeNative( 932 JNIEnv *env, jobject scaler, 933 jobject font2D, jlong pScaler, jchar charCode) { 934 935 FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler); 936 int errCode; 937 938 if (scaler == NULL || scalerInfo->face == NULL) { /* bad/null scaler */ 939 invalidateJavaScaler(env, scaler, scalerInfo); 940 return 0; 941 } 942 943 /* Freetype functions *may* cause callback to java 944 that can use cached values. Make sure our cache is up to date. 945 Scaler context is not important here, can use NULL. */ 946 errCode = setupFTContext(env, font2D, scalerInfo, NULL); 947 if (errCode) { 948 return 0; 949 } 950 951 return FT_Get_Char_Index(scalerInfo->face, charCode); 952 } 953 954 955 #define FloatToF26Dot6(x) ((unsigned int) ((x)*64)) 956 957 static FT_Outline* getFTOutline(JNIEnv* env, jobject font2D, 958 FTScalerContext *context, FTScalerInfo* scalerInfo, 959 jint glyphCode, jfloat xpos, jfloat ypos) { 960 int renderFlags; 961 FT_Error error; 962 FT_GlyphSlot ftglyph; 963 964 if (glyphCode >= INVISIBLE_GLYPHS || 965 isNullScalerContext(context) || scalerInfo == NULL) { 966 return NULL; 967 } 968 969 error = setupFTContext(env, font2D, scalerInfo, context); 970 if (error) { 971 return NULL; 972 } 973 974 renderFlags = FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP; 975 976 error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderFlags); 977 if (error) { 978 return NULL; 979 } 980 981 ftglyph = scalerInfo->face->glyph; 982 983 /* apply styles */ 984 if (context->doBold) { /* if bold style */ 985 FT_GlyphSlot_Embolden(ftglyph); 986 } 987 if (context->doItalize) { /* if oblique */ 988 FT_GlyphSlot_Oblique(ftglyph); 989 } 990 991 FT_Outline_Translate(&ftglyph->outline, 992 FloatToF26Dot6(xpos), 993 -FloatToF26Dot6(ypos)); 994 995 return &ftglyph->outline; 996 } 997 998 #define F26Dot6ToFloat(n) (((float)(n))/((float) 64)) 999 1000 /* Types of GeneralPath segments. 1001 TODO: pull constants from other place? */ 1002 1003 #define SEG_UNKNOWN -1 1004 #define SEG_MOVETO 0 1005 #define SEG_LINETO 1 1006 #define SEG_QUADTO 2 1007 #define SEG_CUBICTO 3 1008 #define SEG_CLOSE 4 1009 1010 #define WIND_NON_ZERO 0 1011 #define WIND_EVEN_ODD 1 1012 1013 /* Placeholder to accumulate GeneralPath data */ 1014 typedef struct { 1015 jint numTypes; 1016 jint numCoords; 1017 jint lenTypes; 1018 jint lenCoords; 1019 jint wr; 1020 jbyte* pointTypes; 1021 jfloat* pointCoords; 1022 } GPData; 1023 1024 /* returns 0 on failure */ 1025 static int allocateSpaceForGP(GPData* gpdata, int npoints, int ncontours) { 1026 int maxTypes, maxCoords; 1027 1028 /* we may have up to N intermediate points per contour 1029 (and for each point can actually cause new curve to be generated) 1030 In addition we can also have 2 extra point per outline. 1031 */ 1032 maxTypes = 2*npoints + 2*ncontours; 1033 maxCoords = 4*(npoints + 2*ncontours); //we may need to insert 1034 //up to n-1 intermediate points 1035 1036 /* first usage - allocate space and intialize all fields */ 1037 if (gpdata->pointTypes == NULL || gpdata->pointCoords == NULL) { 1038 gpdata->lenTypes = maxTypes; 1039 gpdata->lenCoords = maxCoords; 1040 gpdata->pointTypes = (jbyte*) 1041 malloc(gpdata->lenTypes*sizeof(jbyte)); 1042 gpdata->pointCoords = (jfloat*) 1043 malloc(gpdata->lenCoords*sizeof(jfloat)); 1044 gpdata->numTypes = 0; 1045 gpdata->numCoords = 0; 1046 gpdata->wr = WIND_NON_ZERO; /* By default, outlines are filled 1047 using the non-zero winding rule. */ 1048 } else { 1049 /* do we have enough space? */ 1050 if (gpdata->lenTypes - gpdata->numTypes < maxTypes) { 1051 gpdata->lenTypes += maxTypes; 1052 gpdata->pointTypes = (jbyte*) 1053 realloc(gpdata->pointTypes, gpdata->lenTypes*sizeof(jbyte)); 1054 } 1055 1056 if (gpdata->lenCoords - gpdata->numCoords < maxCoords) { 1057 gpdata->lenCoords += maxCoords; 1058 gpdata->pointCoords = (jfloat*) 1059 realloc(gpdata->pointCoords, gpdata->lenCoords*sizeof(jfloat)); 1060 } 1061 } 1062 1063 /* failure if any of mallocs failed */ 1064 if (gpdata->pointTypes == NULL || gpdata->pointCoords == NULL) 1065 return 0; 1066 else 1067 return 1; 1068 } 1069 1070 static void addSeg(GPData *gp, jbyte type) { 1071 gp->pointTypes[gp->numTypes++] = type; 1072 } 1073 1074 static void addCoords(GPData *gp, FT_Vector *p) { 1075 gp->pointCoords[gp->numCoords++] = F26Dot6ToFloat(p->x); 1076 gp->pointCoords[gp->numCoords++] = -F26Dot6ToFloat(p->y); 1077 } 1078 1079 static int moveTo(FT_Vector *to, GPData *gp) { 1080 if (gp->numCoords) 1081 addSeg(gp, SEG_CLOSE); 1082 addCoords(gp, to); 1083 addSeg(gp, SEG_MOVETO); 1084 return FT_Err_Ok; 1085 } 1086 1087 static int lineTo(FT_Vector *to, GPData *gp) { 1088 addCoords(gp, to); 1089 addSeg(gp, SEG_LINETO); 1090 return FT_Err_Ok; 1091 } 1092 1093 static int conicTo(FT_Vector *control, FT_Vector *to, GPData *gp) { 1094 addCoords(gp, control); 1095 addCoords(gp, to); 1096 addSeg(gp, SEG_QUADTO); 1097 return FT_Err_Ok; 1098 } 1099 1100 static int cubicTo(FT_Vector *control1, 1101 FT_Vector *control2, 1102 FT_Vector *to, 1103 GPData *gp) { 1104 addCoords(gp, control1); 1105 addCoords(gp, control2); 1106 addCoords(gp, to); 1107 addSeg(gp, SEG_CUBICTO); 1108 return FT_Err_Ok; 1109 } 1110 1111 static void addToGP(GPData* gpdata, FT_Outline*outline) { 1112 static const FT_Outline_Funcs outline_funcs = { 1113 (FT_Outline_MoveToFunc) moveTo, 1114 (FT_Outline_LineToFunc) lineTo, 1115 (FT_Outline_ConicToFunc) conicTo, 1116 (FT_Outline_CubicToFunc) cubicTo, 1117 0, /* shift */ 1118 0, /* delta */ 1119 }; 1120 1121 FT_Outline_Decompose(outline, &outline_funcs, gpdata); 1122 if (gpdata->numCoords) 1123 addSeg(gpdata, SEG_CLOSE); 1124 1125 /* If set to 1, the outline will be filled using the even-odd fill rule */ 1126 if (outline->flags & FT_OUTLINE_EVEN_ODD_FILL) { 1127 gpdata->wr = WIND_EVEN_ODD; 1128 } 1129 } 1130 1131 static void freeGP(GPData* gpdata) { 1132 if (gpdata->pointCoords != NULL) { 1133 free(gpdata->pointCoords); 1134 gpdata->pointCoords = NULL; 1135 gpdata->numCoords = 0; 1136 gpdata->lenCoords = 0; 1137 } 1138 if (gpdata->pointTypes != NULL) { 1139 free(gpdata->pointTypes); 1140 gpdata->pointTypes = NULL; 1141 gpdata->numTypes = 0; 1142 gpdata->lenTypes = 0; 1143 } 1144 } 1145 1146 static jobject getGlyphGeneralPath(JNIEnv* env, jobject font2D, 1147 FTScalerContext *context, FTScalerInfo *scalerInfo, 1148 jint glyphCode, jfloat xpos, jfloat ypos) { 1149 1150 FT_Outline* outline; 1151 jobject gp = NULL; 1152 jbyteArray types; 1153 jfloatArray coords; 1154 GPData gpdata; 1155 1156 outline = getFTOutline(env, font2D, context, scalerInfo, 1157 glyphCode, xpos, ypos); 1158 1159 if (outline == NULL || outline->n_points == 0) { 1160 return gp; 1161 } 1162 1163 gpdata.pointTypes = NULL; 1164 gpdata.pointCoords = NULL; 1165 if (!allocateSpaceForGP(&gpdata, outline->n_points, outline->n_contours)) { 1166 return gp; 1167 } 1168 1169 addToGP(&gpdata, outline); 1170 1171 types = (*env)->NewByteArray(env, gpdata.numTypes); 1172 coords = (*env)->NewFloatArray(env, gpdata.numCoords); 1173 1174 if (types && coords) { 1175 (*env)->SetByteArrayRegion(env, types, 0, 1176 gpdata.numTypes, 1177 gpdata.pointTypes); 1178 (*env)->SetFloatArrayRegion(env, coords, 0, 1179 gpdata.numCoords, 1180 gpdata.pointCoords); 1181 gp = (*env)->NewObject(env, 1182 sunFontIDs.gpClass, 1183 sunFontIDs.gpCtr, 1184 gpdata.wr, 1185 types, 1186 gpdata.numTypes, 1187 coords, 1188 gpdata.numCoords); 1189 } 1190 1191 freeGP(&gpdata); 1192 1193 return gp; 1194 } 1195 1196 /* 1197 * Class: sun_font_FreetypeFontScaler 1198 * Method: getGlyphOutlineNative 1199 * Signature: (Lsun/font/Font2D;JIFF)Ljava/awt/geom/GeneralPath; 1200 */ 1201 JNIEXPORT jobject JNICALL 1202 Java_sun_font_FreetypeFontScaler_getGlyphOutlineNative( 1203 JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext, 1204 jlong pScaler, jint glyphCode, jfloat xpos, jfloat ypos) { 1205 1206 FTScalerContext *context = 1207 (FTScalerContext*) jlong_to_ptr(pScalerContext); 1208 FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler); 1209 1210 jobject gp = getGlyphGeneralPath(env, 1211 font2D, 1212 context, 1213 scalerInfo, 1214 glyphCode, 1215 xpos, 1216 ypos); 1217 if (gp == NULL) { /* can be legal */ 1218 gp = (*env)->NewObject(env, 1219 sunFontIDs.gpClass, 1220 sunFontIDs.gpCtrEmpty); 1221 } 1222 return gp; 1223 } 1224 1225 /* 1226 * Class: sun_font_FreetypeFontScaler 1227 * Method: getGlyphOutlineBoundsNative 1228 * Signature: (Lsun/font/Font2D;JI)Ljava/awt/geom/Rectangle2D/Float; 1229 */ 1230 JNIEXPORT jobject JNICALL 1231 Java_sun_font_FreetypeFontScaler_getGlyphOutlineBoundsNative( 1232 JNIEnv *env, jobject scaler, jobject font2D, 1233 jlong pScalerContext, jlong pScaler, jint glyphCode) { 1234 1235 FT_Outline *outline; 1236 FT_BBox bbox; 1237 int error; 1238 jobject bounds; 1239 1240 FTScalerContext *context = 1241 (FTScalerContext*) jlong_to_ptr(pScalerContext); 1242 FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler); 1243 1244 outline = getFTOutline(env, font2D, context, scalerInfo, glyphCode, 0, 0); 1245 if (outline == NULL || outline->n_points == 0) { 1246 /* it is legal case, e.g. invisible glyph */ 1247 bounds = (*env)->NewObject(env, 1248 sunFontIDs.rect2DFloatClass, 1249 sunFontIDs.rect2DFloatCtr); 1250 return bounds; 1251 } 1252 1253 error = FT_Outline_Get_BBox(outline, &bbox); 1254 1255 //convert bbox 1256 if (error || bbox.xMin >= bbox.xMax || bbox.yMin >= bbox.yMax) { 1257 bounds = (*env)->NewObject(env, 1258 sunFontIDs.rect2DFloatClass, 1259 sunFontIDs.rect2DFloatCtr); 1260 } else { 1261 bounds = (*env)->NewObject(env, 1262 sunFontIDs.rect2DFloatClass, 1263 sunFontIDs.rect2DFloatCtr4, 1264 F26Dot6ToFloat(bbox.xMin), 1265 F26Dot6ToFloat(-bbox.yMax), 1266 F26Dot6ToFloat(bbox.xMax-bbox.xMin), 1267 F26Dot6ToFloat(bbox.yMax-bbox.yMin)); 1268 } 1269 1270 return bounds; 1271 } 1272 1273 /* 1274 * Class: sun_font_FreetypeFontScaler 1275 * Method: getGlyphVectorOutlineNative 1276 * Signature: (Lsun/font/Font2D;J[IIFF)Ljava/awt/geom/GeneralPath; 1277 */ 1278 JNIEXPORT jobject 1279 JNICALL 1280 Java_sun_font_FreetypeFontScaler_getGlyphVectorOutlineNative( 1281 JNIEnv *env, jobject scaler, jobject font2D, 1282 jlong pScalerContext, jlong pScaler, 1283 jintArray glyphArray, jint numGlyphs, jfloat xpos, jfloat ypos) { 1284 1285 FT_Outline* outline; 1286 jobject gp = NULL; 1287 jbyteArray types; 1288 jfloatArray coords; 1289 GPData gpdata; 1290 int i; 1291 jint *glyphs; 1292 1293 FTScalerContext *context = 1294 (FTScalerContext*) jlong_to_ptr(pScalerContext); 1295 FTScalerInfo *scalerInfo = 1296 (FTScalerInfo*) jlong_to_ptr(pScaler); 1297 1298 glyphs = NULL; 1299 if (numGlyphs > 0 && 0xffffffffu / sizeof(jint) >= numGlyphs) { 1300 glyphs = (jint*) malloc(numGlyphs*sizeof(jint)); 1301 } 1302 if (glyphs == NULL) { 1303 // We reach here if: 1304 // 1. numGlyphs <= 0, 1305 // 2. overflow check failed, or 1306 // 3. malloc failed. 1307 gp = (*env)->NewObject(env, sunFontIDs.gpClass, sunFontIDs.gpCtrEmpty); 1308 return gp; 1309 } 1310 1311 (*env)->GetIntArrayRegion(env, glyphArray, 0, numGlyphs, glyphs); 1312 1313 gpdata.numCoords = 0; 1314 for (i=0; i<numGlyphs;i++) { 1315 if (glyphs[i] >= INVISIBLE_GLYPHS) { 1316 continue; 1317 } 1318 outline = getFTOutline(env, 1319 font2D, 1320 context, 1321 scalerInfo, 1322 glyphs[i], 1323 xpos, ypos); 1324 1325 if (outline == NULL || outline->n_points == 0) { 1326 continue; 1327 } 1328 1329 gpdata.pointTypes = NULL; 1330 gpdata.pointCoords = NULL; 1331 if (!allocateSpaceForGP(&gpdata, outline->n_points, 1332 outline->n_contours)) { 1333 break; 1334 } 1335 1336 addToGP(&gpdata, outline); 1337 } 1338 free(glyphs); 1339 1340 if (gpdata.numCoords != 0) { 1341 types = (*env)->NewByteArray(env, gpdata.numTypes); 1342 coords = (*env)->NewFloatArray(env, gpdata.numCoords); 1343 1344 if (types && coords) { 1345 (*env)->SetByteArrayRegion(env, types, 0, 1346 gpdata.numTypes, gpdata.pointTypes); 1347 (*env)->SetFloatArrayRegion(env, coords, 0, 1348 gpdata.numCoords, gpdata.pointCoords); 1349 1350 gp=(*env)->NewObject(env, 1351 sunFontIDs.gpClass, 1352 sunFontIDs.gpCtr, 1353 gpdata.wr, 1354 types, 1355 gpdata.numTypes, 1356 coords, 1357 gpdata.numCoords); 1358 return gp; 1359 } 1360 } 1361 return (*env)->NewObject(env, sunFontIDs.gpClass, sunFontIDs.gpCtrEmpty); 1362 } 1363 1364 JNIEXPORT jlong JNICALL 1365 Java_sun_font_FreetypeFontScaler_getUnitsPerEMNative( 1366 JNIEnv *env, jobject scaler, jlong pScaler) { 1367 1368 FTScalerInfo *s = (FTScalerInfo* ) jlong_to_ptr(pScaler); 1369 1370 /* Freetype doc says: 1371 The number of font units per EM square for this face. 1372 This is typically 2048 for TrueType fonts, and 1000 for Type 1 fonts. 1373 Only relevant for scalable formats. 1374 However, layout engine might be not tested with anything but 2048. 1375 1376 NB: test it! */ 1377 if (s != NULL) { 1378 return s->face->units_per_EM; 1379 } 1380 return 2048; 1381 } 1382 1383 /* This native method is called by the OpenType layout engine. */ 1384 JNIEXPORT jobject JNICALL 1385 Java_sun_font_FreetypeFontScaler_getGlyphPointNative( 1386 JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext, 1387 jlong pScaler, jint glyphCode, jint pointNumber) { 1388 1389 FT_Outline* outline; 1390 jobject point = NULL; 1391 jfloat x=0, y=0; 1392 FTScalerContext *context = 1393 (FTScalerContext*) jlong_to_ptr(pScalerContext); 1394 FTScalerInfo *scalerInfo = (FTScalerInfo*) jlong_to_ptr(pScaler); 1395 1396 outline = getFTOutline(env, font2D, context, scalerInfo, glyphCode, 0, 0); 1397 1398 if (outline != NULL && outline->n_points > pointNumber) { 1399 x = F26Dot6ToFloat(outline->points[pointNumber].x); 1400 y = -F26Dot6ToFloat(outline->points[pointNumber].y); 1401 } 1402 1403 return (*env)->NewObject(env, sunFontIDs.pt2DFloatClass, 1404 sunFontIDs.pt2DFloatCtr, x, y); 1405 }