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