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