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 int jniInited = 0; 44 45 static void getFloat(JNIEnv* env, jobject pt, jfloat *x, jfloat *y) { 46 *x = (*env)->GetFloatField(env, pt, sunFontIDs.xFID); 47 *y = (*env)->GetFloatField(env, pt, sunFontIDs.yFID); 48 } 49 50 static void putFloat(JNIEnv* env, jobject pt, jfloat x, jfloat y) { 51 (*env)->SetFloatField(env, pt, sunFontIDs.xFID, x); 52 (*env)->SetFloatField(env, pt, sunFontIDs.yFID, y); 53 } 54 55 static int init_JNI_IDs(JNIEnv *env) { 56 if (jniInited) { 57 return jniInited; 58 } 59 CHECK_NULL_RETURN(gvdClass = (*env)->FindClass(env, gvdClassName), 0); 60 CHECK_NULL_RETURN(gvdClass = (jclass)(*env)->NewGlobalRef(env, gvdClass), 0); 61 CHECK_NULL_RETURN(gvdCountFID = (*env)->GetFieldID(env, gvdClass, "_count", "I"), 0); 62 CHECK_NULL_RETURN(gvdFlagsFID = (*env)->GetFieldID(env, gvdClass, "_flags", "I"), 0); 63 CHECK_NULL_RETURN(gvdGlyphsFID = (*env)->GetFieldID(env, gvdClass, "_glyphs", "[I"), 0); 64 CHECK_NULL_RETURN(gvdPositionsFID = (*env)->GetFieldID(env, gvdClass, "_positions", "[F"), 0); 65 CHECK_NULL_RETURN(gvdIndicesFID = (*env)->GetFieldID(env, gvdClass, "_indices", "[I"), 0); 66 jniInited = 1; 67 return jniInited; 68 } 69 70 // gmask is the composite font slot mask 71 // baseindex is to be added to the character (code point) index. 72 int storeGVData(JNIEnv* env, 73 jobject gvdata, jint slot, jint baseIndex, jobject startPt, 74 int glyphCount, hb_glyph_info_t *glyphInfo, 75 hb_glyph_position_t *glyphPos, hb_direction_t direction) { 76 77 int i; 78 float x=0, y=0; 79 float startX, startY; 80 float scale = 1.0f/64.0f; 81 unsigned int* glyphs; 82 float* positions; 83 int initialCount, glyphArrayLen, posArrayLen, maxGlyphs, storeadv; 84 unsigned int* indices; 85 jarray glyphArray, posArray, inxArray; 86 87 if (!init_JNI_IDs(env)) { 88 return 0; 89 } 90 91 initialCount = (*env)->GetIntField(env, gvdata, gvdCountFID); 92 glyphArray = 93 (jarray)(*env)->GetObjectField(env, gvdata, gvdGlyphsFID); 94 posArray = 95 (jarray)(*env)->GetObjectField(env, gvdata, gvdPositionsFID); 96 97 if (glyphArray == NULL || posArray == NULL) 98 { 99 JNU_ThrowArrayIndexOutOfBoundsException(env, ""); 100 return 0; 101 } 102 103 // The Java code catches the IIOBE and expands the storage 104 // and re-invokes layout. I suppose this is expected to be rare 105 // because at least in a single threaded case there should be 106 // re-use of the same container, but it is a little wasteful/distateful. 107 glyphArrayLen = (*env)->GetArrayLength(env, glyphArray); 108 posArrayLen = (*env)->GetArrayLength(env, posArray); 109 maxGlyphs = glyphCount + initialCount; 110 if ((maxGlyphs > glyphArrayLen) || 111 (maxGlyphs * 2 + 2 > posArrayLen)) 112 { 113 JNU_ThrowArrayIndexOutOfBoundsException(env, ""); 114 return 0; 115 } 116 117 getFloat(env, startPt, &startX, &startY); 118 119 glyphs = 120 (unsigned int*)(*env)->GetPrimitiveArrayCritical(env, glyphArray, NULL); 121 positions = (jfloat*)(*env)->GetPrimitiveArrayCritical(env, posArray, NULL); 122 for (i = 0; i < glyphCount; i++) { 123 int storei = i + initialCount; 124 int index = glyphInfo[i].codepoint | slot; 125 if (i<glyphCount)glyphs[storei] = (unsigned int)index; 126 positions[(storei*2)] = startX + x + glyphPos[i].x_offset * scale; 127 positions[(storei*2)+1] = startY + y - glyphPos[i].y_offset * scale; 128 x += glyphPos[i].x_advance * scale; 129 y += glyphPos[i].y_advance * scale; 130 } 131 storeadv = initialCount+glyphCount; 132 // The final slot in the positions array is important 133 // because when the GlyphVector is created from this 134 // data it determines the overall advance of the glyphvector 135 // and this is used in positioning the next glyphvector 136 // during rendering where text is broken into runs. 137 // We also need to report it back into "pt", so layout can 138 // pass it back down for that next run in this code. 139 positions[(storeadv*2)] = startX + x; 140 positions[(storeadv*2)+1] = startY + y; 141 (*env)->ReleasePrimitiveArrayCritical(env, glyphArray, glyphs, 0); 142 (*env)->ReleasePrimitiveArrayCritical(env, posArray, positions, 0); 143 putFloat(env, startPt,positions[(storeadv*2)],positions[(storeadv*2)+1] ); 144 inxArray = 145 (jarray)(*env)->GetObjectField(env, gvdata, gvdIndicesFID); 146 indices = 147 (unsigned int*)(*env)->GetPrimitiveArrayCritical(env, inxArray, NULL); 148 for (i = 0; i < glyphCount; i++) { 149 int cluster = glyphInfo[i].cluster; 150 if (direction == HB_DIRECTION_LTR) { 151 // I need to understand what hb does when processing a substring 152 // I expected the cluster index to be from the start of the text 153 // to process. 154 // Instead it appears to be from the start of the whole thing. 155 indices[i+initialCount] = cluster; 156 } else { 157 indices[i+initialCount] = baseIndex + glyphCount -1 -i; 158 } 159 } 160 (*env)->ReleasePrimitiveArrayCritical(env, inxArray, indices, 0); 161 (*env)->SetIntField(env, gvdata, gvdCountFID, initialCount+glyphCount); 162 return initialCount+glyphCount; 163 } 164 165 static float euclidianDistance(float a, float b) 166 { 167 float root; 168 if (a < 0) { 169 a = -a; 170 } 171 172 if (b < 0) { 173 b = -b; 174 } 175 176 if (a == 0) { 177 return b; 178 } 179 180 if (b == 0) { 181 return a; 182 } 183 184 /* Do an initial approximation, in root */ 185 root = a > b ? a + (b / 2) : b + (a / 2); 186 187 /* An unrolled Newton-Raphson iteration sequence */ 188 root = (root + (a * (a / root)) + (b * (b / root)) + 1) / 2; 189 root = (root + (a * (a / root)) + (b * (b / root)) + 1) / 2; 190 root = (root + (a * (a / root)) + (b * (b / root)) + 1) / 2; 191 192 return root; 193 } 194 195 JDKFontInfo* 196 createJDKFontInfo(JNIEnv *env, 197 jobject font2D, 198 jobject fontStrike, 199 jfloat ptSize, 200 jlong pScaler, 201 jlong pNativeFont, 202 jfloatArray matrix, 203 jboolean aat) { 204 205 206 JDKFontInfo *fi = (JDKFontInfo*)malloc(sizeof(JDKFontInfo)); 207 if (!fi) { 208 return NULL; 209 } 210 fi->env = env; // this is valid only for the life of this JNI call. 211 fi->font2D = font2D; 212 fi->fontStrike = fontStrike; 213 fi->nativeFont = pNativeFont; 214 fi->aat = aat; 215 (*env)->GetFloatArrayRegion(env, matrix, 0, 4, fi->matrix); 216 fi->ptSize = ptSize; 217 fi->xPtSize = euclidianDistance(fi->matrix[0], fi->matrix[1]); 218 fi->yPtSize = euclidianDistance(fi->matrix[2], fi->matrix[3]); 219 220 return fi; 221 } 222 223 224 #define TYPO_RTL 0x80000000 225 226 JNIEXPORT jboolean JNICALL Java_sun_font_SunLayoutEngine_shape 227 (JNIEnv *env, jclass cls, 228 jobject font2D, 229 jobject fontStrike, 230 jfloat ptSize, 231 jfloatArray matrix, 232 jlong pScaler, 233 jlong pNativeFont, 234 jboolean aat, 235 jcharArray text, 236 jobject gvdata, 237 jint script, 238 jint offset, 239 jint limit, 240 jint baseIndex, 241 jobject startPt, 242 jint flags, 243 jint slot) { 244 245 hb_buffer_t *buffer; 246 hb_font_t* hbfont; 247 jchar *chars; 248 jsize len; 249 int glyphCount; 250 hb_glyph_info_t *glyphInfo; 251 hb_glyph_position_t *glyphPos; 252 hb_direction_t direction = HB_DIRECTION_LTR; 253 hb_feature_t *features = NULL; 254 int featureCount = 0; 255 256 int i; 257 unsigned int buflen; 258 259 JDKFontInfo *jdkFontInfo = 260 createJDKFontInfo(env, font2D, fontStrike, ptSize, 261 pScaler, pNativeFont, matrix, aat); 262 if (!jdkFontInfo) { 263 return JNI_FALSE; 264 } 265 jdkFontInfo->env = env; // this is valid only for the life of this JNI call. 266 jdkFontInfo->font2D = font2D; 267 jdkFontInfo->fontStrike = fontStrike; 268 269 hbfont = hb_jdk_font_create(jdkFontInfo, NULL); 270 271 buffer = hb_buffer_create(); 272 hb_buffer_set_script(buffer, getHBScriptCode(script)); 273 hb_buffer_set_language(buffer, 274 hb_ot_tag_to_language(HB_OT_TAG_DEFAULT_LANGUAGE)); 275 if ((flags & TYPO_RTL) != 0) { 276 direction = HB_DIRECTION_RTL; 277 } 278 hb_buffer_set_direction(buffer, direction); 279 280 chars = (*env)->GetCharArrayElements(env, text, NULL); 281 len = (*env)->GetArrayLength(env, text); 282 283 hb_buffer_add_utf16(buffer, chars, len, offset, limit-offset); 284 285 hb_shape_full(hbfont, buffer, features, featureCount, 0); 286 glyphCount = hb_buffer_get_length(buffer); 287 glyphInfo = hb_buffer_get_glyph_infos(buffer, 0); 288 glyphPos = hb_buffer_get_glyph_positions(buffer, &buflen); 289 for (i = 0; i < glyphCount; i++) { 290 int index = glyphInfo[i].codepoint; 291 int xadv = (glyphPos[i].x_advance); 292 int yadv = (glyphPos[i].y_advance); 293 } 294 // On "input" HB assigns a cluster index to each character in UTF-16. 295 // On output where a sequence of characters have been mapped to 296 // a glyph they are all mapped to the cluster index of the first character. 297 // The next cluster index will be that of the first character in the 298 // next cluster. So cluster indexes may 'skip' on output. 299 // This can also happen if there are supplementary code-points 300 // such that two UTF-16 characters are needed to make one codepoint. 301 // In RTL text you need to count down. 302 // So the following code tries to build the reverse map as expected 303 // by calling code. 304 305 storeGVData(env, gvdata, slot, baseIndex, startPt, 306 glyphCount, glyphInfo, glyphPos, direction); 307 308 hb_buffer_destroy (buffer); 309 hb_font_destroy(hbfont); 310 free((void*)jdkFontInfo); 311 if (features != NULL) free(features); 312 313 return JNI_TRUE; 314 } 315