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