1 /* 2 * Copyright (c) 2015, 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_util.h> 27 #include <stdlib.h> 28 #include "hb.h" 29 #include "hb-jdk.h" 30 #include "hb-ot.h" 31 #ifdef MACOSX 32 #include "hb-coretext.h" 33 #endif 34 #include "scriptMapping.h" 35 36 static jclass gvdClass = 0; 37 static const char* gvdClassName = "sun/font/GlyphLayout$GVData"; 38 static jfieldID gvdCountFID = 0; 39 static jfieldID gvdFlagsFID = 0; 40 static jfieldID gvdGlyphsFID = 0; 41 static jfieldID gvdPositionsFID = 0; 42 static jfieldID gvdIndicesFID = 0; 43 static jmethodID gvdGrowMID = 0; 44 static int jniInited = 0; 45 46 static void getFloat(JNIEnv* env, jobject pt, jfloat *x, jfloat *y) { 47 *x = (*env)->GetFloatField(env, pt, sunFontIDs.xFID); 48 *y = (*env)->GetFloatField(env, pt, sunFontIDs.yFID); 49 } 50 51 static void putFloat(JNIEnv* env, jobject pt, jfloat x, jfloat y) { 52 (*env)->SetFloatField(env, pt, sunFontIDs.xFID, x); 53 (*env)->SetFloatField(env, pt, sunFontIDs.yFID, y); 54 } 55 56 static int init_JNI_IDs(JNIEnv *env) { 57 if (jniInited) { 58 return jniInited; 59 } 60 CHECK_NULL_RETURN(gvdClass = (*env)->FindClass(env, gvdClassName), 0); 61 CHECK_NULL_RETURN(gvdClass = (jclass)(*env)->NewGlobalRef(env, gvdClass), 0); 62 CHECK_NULL_RETURN(gvdCountFID = (*env)->GetFieldID(env, gvdClass, "_count", "I"), 0); 63 CHECK_NULL_RETURN(gvdFlagsFID = (*env)->GetFieldID(env, gvdClass, "_flags", "I"), 0); 64 CHECK_NULL_RETURN(gvdGlyphsFID = (*env)->GetFieldID(env, gvdClass, "_glyphs", "[I"), 0); 65 CHECK_NULL_RETURN(gvdPositionsFID = (*env)->GetFieldID(env, gvdClass, "_positions", "[F"), 0); 66 CHECK_NULL_RETURN(gvdIndicesFID = (*env)->GetFieldID(env, gvdClass, "_indices", "[I"), 0); 67 CHECK_NULL_RETURN(gvdGrowMID = (*env)->GetMethodID(env, gvdClass, "grow", "()V"), 0); 68 jniInited = 1; 69 return jniInited; 70 } 71 72 // gmask is the composite font slot mask 73 // baseindex is to be added to the character (code point) index. 74 jboolean storeGVData(JNIEnv* env, 75 jobject gvdata, jint slot, 76 jint baseIndex, int offset, jobject startPt, 77 int charCount, int glyphCount, hb_glyph_info_t *glyphInfo, 78 hb_glyph_position_t *glyphPos, float devScale) { 79 80 int i, needToGrow; 81 float x=0, y=0; 82 float startX, startY, advX, advY; 83 float scale = 1.0f / HBFloatToFixedScale / devScale; 84 unsigned int* glyphs; 85 float* positions; 86 int initialCount, glyphArrayLen, posArrayLen, maxGlyphs, storeadv, maxStore; 87 unsigned int* indices; 88 jarray glyphArray, posArray, inxArray; 89 90 if (!init_JNI_IDs(env)) { 91 return JNI_FALSE; 92 } 93 94 initialCount = (*env)->GetIntField(env, gvdata, gvdCountFID); 95 do { 96 glyphArray = (jarray)(*env)->GetObjectField(env, gvdata, gvdGlyphsFID); 97 posArray = (jarray)(*env)->GetObjectField(env, gvdata, gvdPositionsFID); 98 inxArray = (jarray)(*env)->GetObjectField(env, gvdata, gvdIndicesFID); 99 if (glyphArray == NULL || posArray == NULL || inxArray == NULL) { 100 JNU_ThrowArrayIndexOutOfBoundsException(env, ""); 101 return JNI_FALSE; 102 } 103 glyphArrayLen = (*env)->GetArrayLength(env, glyphArray); 104 posArrayLen = (*env)->GetArrayLength(env, posArray); 105 maxGlyphs = (charCount > glyphCount) ? charCount : glyphCount; 106 maxStore = maxGlyphs + initialCount; 107 needToGrow = (maxStore > glyphArrayLen) || 108 (maxStore * 2 + 2 > posArrayLen); 109 if (needToGrow) { 110 (*env)->CallVoidMethod(env, gvdata, gvdGrowMID); 111 if ((*env)->ExceptionCheck(env)) { 112 return JNI_FALSE; 113 } 114 } 115 } while (needToGrow); 116 117 getFloat(env, startPt, &startX, &startY); 118 119 glyphs = 120 (unsigned int*)(*env)->GetPrimitiveArrayCritical(env, glyphArray, NULL); 121 if (glyphs == NULL) { 122 return JNI_FALSE; 123 } 124 positions = (jfloat*)(*env)->GetPrimitiveArrayCritical(env, posArray, NULL); 125 if (positions == NULL) { 126 (*env)->ReleasePrimitiveArrayCritical(env, glyphArray, glyphs, 0); 127 return JNI_FALSE; 128 } 129 indices = 130 (unsigned int*)(*env)->GetPrimitiveArrayCritical(env, inxArray, NULL); 131 if (indices == NULL) { 132 (*env)->ReleasePrimitiveArrayCritical(env, glyphArray, glyphs, 0); 133 (*env)->ReleasePrimitiveArrayCritical(env, posArray, positions, 0); 134 return JNI_FALSE; 135 } 136 137 for (i = 0; i < glyphCount; i++) { 138 int storei = i + initialCount; 139 int cluster = glyphInfo[i].cluster - offset; 140 indices[storei] = baseIndex + cluster; 141 glyphs[storei] = (unsigned int)(glyphInfo[i].codepoint | slot); 142 positions[storei*2] = startX + x + glyphPos[i].x_offset * scale; 143 positions[(storei*2)+1] = startY + y - glyphPos[i].y_offset * scale; 144 x += glyphPos[i].x_advance * scale; 145 y += glyphPos[i].y_advance * scale; 146 storei++; 147 } 148 storeadv = initialCount + glyphCount; 149 // The final slot in the positions array is important 150 // because when the GlyphVector is created from this 151 // data it determines the overall advance of the glyphvector 152 // and this is used in positioning the next glyphvector 153 // during rendering where text is broken into runs. 154 // We also need to report it back into "pt", so layout can 155 // pass it back down for that next run in this code. 156 advX = startX + x; 157 advY = startY + y; 158 positions[(storeadv*2)] = advX; 159 positions[(storeadv*2)+1] = advY; 160 (*env)->ReleasePrimitiveArrayCritical(env, glyphArray, glyphs, 0); 161 (*env)->ReleasePrimitiveArrayCritical(env, posArray, positions, 0); 162 (*env)->ReleasePrimitiveArrayCritical(env, inxArray, indices, 0); 163 putFloat(env, startPt, advX, advY); 164 (*env)->SetIntField(env, gvdata, gvdCountFID, storeadv); 165 166 return JNI_TRUE; 167 } 168 169 static float euclidianDistance(float a, float b) 170 { 171 float root; 172 if (a < 0) { 173 a = -a; 174 } 175 176 if (b < 0) { 177 b = -b; 178 } 179 180 if (a == 0) { 181 return b; 182 } 183 184 if (b == 0) { 185 return a; 186 } 187 188 /* Do an initial approximation, in root */ 189 root = a > b ? a + (b / 2) : b + (a / 2); 190 191 /* An unrolled Newton-Raphson iteration sequence */ 192 root = (root + (a * (a / root)) + (b * (b / root)) + 1) / 2; 193 root = (root + (a * (a / root)) + (b * (b / root)) + 1) / 2; 194 root = (root + (a * (a / root)) + (b * (b / root)) + 1) / 2; 195 196 return root; 197 } 198 199 JDKFontInfo* 200 createJDKFontInfo(JNIEnv *env, 201 jobject font2D, 202 jobject fontStrike, 203 jfloat ptSize, 204 jlong pScaler, 205 jlong pNativeFont, 206 jfloatArray matrix, 207 jboolean aat) { 208 209 210 JDKFontInfo *fi = (JDKFontInfo*)malloc(sizeof(JDKFontInfo)); 211 if (!fi) { 212 return NULL; 213 } 214 fi->env = env; // this is valid only for the life of this JNI call. 215 fi->font2D = font2D; 216 fi->fontStrike = fontStrike; 217 fi->nativeFont = pNativeFont; 218 fi->aat = aat; 219 (*env)->GetFloatArrayRegion(env, matrix, 0, 4, fi->matrix); 220 fi->ptSize = ptSize; 221 fi->xPtSize = euclidianDistance(fi->matrix[0], fi->matrix[1]); 222 fi->yPtSize = euclidianDistance(fi->matrix[2], fi->matrix[3]); 223 if (!aat && (getenv("HB_NODEVTX") != NULL)) { 224 fi->devScale = fi->xPtSize / fi->ptSize; 225 } else { 226 fi->devScale = 1.0f; 227 } 228 return fi; 229 } 230 231 232 #define TYPO_KERN 0x00000001 233 #define TYPO_LIGA 0x00000002 234 #define TYPO_RTL 0x80000000 235 236 JNIEXPORT jboolean JNICALL Java_sun_font_SunLayoutEngine_shape 237 (JNIEnv *env, jclass cls, 238 jobject font2D, 239 jobject fontStrike, 240 jfloat ptSize, 241 jfloatArray matrix, 242 jlong pScaler, 243 jlong pNativeFont, 244 jlong pFace, 245 jboolean aat, 246 jcharArray text, 247 jobject gvdata, 248 jint script, 249 jint offset, 250 jint limit, 251 jint baseIndex, 252 jobject startPt, 253 jint flags, 254 jint slot) { 255 256 hb_buffer_t *buffer; 257 hb_face_t* hbface; 258 hb_font_t* hbfont; 259 jchar *chars; 260 jsize len; 261 int glyphCount; 262 hb_glyph_info_t *glyphInfo; 263 hb_glyph_position_t *glyphPos; 264 hb_direction_t direction = HB_DIRECTION_LTR; 265 hb_feature_t *features = NULL; 266 int featureCount = 0; 267 char* kern = (flags & TYPO_KERN) ? "kern" : "-kern"; 268 char* liga = (flags & TYPO_LIGA) ? "liga" : "-liga"; 269 jboolean ret; 270 unsigned int buflen; 271 272 JDKFontInfo *jdkFontInfo = 273 createJDKFontInfo(env, font2D, fontStrike, ptSize, 274 pScaler, pNativeFont, matrix, aat); 275 if (!jdkFontInfo) { 276 return JNI_FALSE; 277 } 278 jdkFontInfo->env = env; // this is valid only for the life of this JNI call. 279 jdkFontInfo->font2D = font2D; 280 jdkFontInfo->fontStrike = fontStrike; 281 282 hbface = (hb_face_t*) jlong_to_ptr(pFace); 283 hbfont = hb_jdk_font_create(hbface, jdkFontInfo, NULL); 284 285 buffer = hb_buffer_create(); 286 hb_buffer_set_script(buffer, getHBScriptCode(script)); 287 hb_buffer_set_language(buffer, 288 hb_ot_tag_to_language(HB_OT_TAG_DEFAULT_LANGUAGE)); 289 if ((flags & TYPO_RTL) != 0) { 290 direction = HB_DIRECTION_RTL; 291 } 292 hb_buffer_set_direction(buffer, direction); 293 hb_buffer_set_cluster_level(buffer, 294 HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); 295 296 chars = (*env)->GetCharArrayElements(env, text, NULL); 297 if ((*env)->ExceptionCheck(env)) { 298 hb_buffer_destroy(buffer); 299 hb_font_destroy(hbfont); 300 free((void*)jdkFontInfo); 301 return JNI_FALSE; 302 } 303 len = (*env)->GetArrayLength(env, text); 304 305 hb_buffer_add_utf16(buffer, chars, len, offset, limit-offset); 306 307 features = calloc(2, sizeof(hb_feature_t)); 308 if (features) { 309 hb_feature_from_string(kern, -1, &features[featureCount++]); 310 hb_feature_from_string(liga, -1, &features[featureCount++]); 311 } 312 313 hb_shape_full(hbfont, buffer, features, featureCount, 0); 314 glyphCount = hb_buffer_get_length(buffer); 315 glyphInfo = hb_buffer_get_glyph_infos(buffer, 0); 316 glyphPos = hb_buffer_get_glyph_positions(buffer, &buflen); 317 318 ret = storeGVData(env, gvdata, slot, baseIndex, offset, startPt, 319 limit - offset, glyphCount, glyphInfo, glyphPos, 320 jdkFontInfo->devScale); 321 322 hb_buffer_destroy (buffer); 323 hb_font_destroy(hbfont); 324 free((void*)jdkFontInfo); 325 if (features != NULL) free(features); 326 (*env)->ReleaseCharArrayElements(env, text, chars, JNI_ABORT); 327 return ret; 328 } 329