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