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