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