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