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