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