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