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