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