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