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