| 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 |  | 
|---|