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
36static jclass gvdClass = 0;
37static const char* gvdClassName = "sun/font/GlyphLayout$GVData";
38static jfieldID gvdCountFID = 0;
39static jfieldID gvdFlagsFID = 0;
40static jfieldID gvdGlyphsFID = 0;
41static jfieldID gvdPositionsFID = 0;
42static jfieldID gvdIndicesFID = 0;
43static jmethodID gvdGrowMID = 0;
44static int jniInited = 0;
45
46static 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
51static 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
56static 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.
74jboolean 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
169static 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
199JDKFontInfo*
200 createJDKFontInfo(JNIEnv *env,
201 jobject font2D,
202 jobject fontStrike,
203 jfloat ptSize,
204 jlong pNativeFont,
205 jfloatArray matrix,
206 jboolean aat) {
207
208
209 JDKFontInfo *fi = (JDKFontInfo*)malloc(sizeof(JDKFontInfo));
210 if (!fi) {
211 return NULL;
212 }
213 fi->env = env; // this is valid only for the life of this JNI call.
214 fi->font2D = font2D;
215 fi->fontStrike = fontStrike;
216 fi->nativeFont = pNativeFont;
217 fi->aat = aat;
218 (*env)->GetFloatArrayRegion(env, matrix, 0, 4, fi->matrix);
219 fi->ptSize = ptSize;
220 fi->xPtSize = euclidianDistance(fi->matrix[0], fi->matrix[1]);
221 fi->yPtSize = euclidianDistance(fi->matrix[2], fi->matrix[3]);
222 if (!aat && (getenv("HB_NODEVTX") != NULL)) {
223 fi->devScale = fi->xPtSize / fi->ptSize;
224 } else {
225 fi->devScale = 1.0f;
226 }
227 return fi;
228}
229
230
231#define TYPO_KERN 0x00000001
232#define TYPO_LIGA 0x00000002
233#define TYPO_RTL 0x80000000
234
235JNIEXPORT jboolean JNICALL Java_sun_font_SunLayoutEngine_shape
236 (JNIEnv *env, jclass cls,
237 jobject font2D,
238 jobject fontStrike,
239 jfloat ptSize,
240 jfloatArray matrix,
241 jlong pNativeFont,
242 jlong pFace,
243 jboolean aat,
244 jcharArray text,
245 jobject gvdata,
246 jint script,
247 jint offset,
248 jint limit,
249 jint baseIndex,
250 jobject startPt,
251 jint flags,
252 jint slot) {
253
254 hb_buffer_t *buffer;
255 hb_face_t* hbface;
256 hb_font_t* hbfont;
257 jchar *chars;
258 jsize len;
259 int glyphCount;
260 hb_glyph_info_t *glyphInfo;
261 hb_glyph_position_t *glyphPos;
262 hb_direction_t direction = HB_DIRECTION_LTR;
263 hb_feature_t *features = NULL;
264 int featureCount = 0;
265 char* kern = (flags & TYPO_KERN) ? "kern" : "-kern";
266 char* liga = (flags & TYPO_LIGA) ? "liga" : "-liga";
267 jboolean ret;
268 unsigned int buflen;
269
270 JDKFontInfo *jdkFontInfo =
271 createJDKFontInfo(env, font2D, fontStrike, ptSize,
272 pNativeFont, matrix, aat);
273 if (!jdkFontInfo) {
274 return JNI_FALSE;
275 }
276 jdkFontInfo->env = env; // this is valid only for the life of this JNI call.
277 jdkFontInfo->font2D = font2D;
278 jdkFontInfo->fontStrike = fontStrike;
279
280 hbface = (hb_face_t*) jlong_to_ptr(pFace);
281 hbfont = hb_jdk_font_create(hbface, jdkFontInfo, NULL);
282
283 buffer = hb_buffer_create();
284 hb_buffer_set_script(buffer, getHBScriptCode(script));
285 hb_buffer_set_language(buffer,
286 hb_ot_tag_to_language(HB_OT_TAG_DEFAULT_LANGUAGE));
287 if ((flags & TYPO_RTL) != 0) {
288 direction = HB_DIRECTION_RTL;
289 }
290 hb_buffer_set_direction(buffer, direction);
291 hb_buffer_set_cluster_level(buffer,
292 HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
293
294 chars = (*env)->GetCharArrayElements(env, text, NULL);
295 if ((*env)->ExceptionCheck(env)) {
296 hb_buffer_destroy(buffer);
297 hb_font_destroy(hbfont);
298 free((void*)jdkFontInfo);
299 return JNI_FALSE;
300 }
301 len = (*env)->GetArrayLength(env, text);
302
303 hb_buffer_add_utf16(buffer, chars, len, offset, limit-offset);
304
305 features = calloc(2, sizeof(hb_feature_t));
306 if (features) {
307 hb_feature_from_string(kern, -1, &features[featureCount++]);
308 hb_feature_from_string(liga, -1, &features[featureCount++]);
309 }
310
311 hb_shape_full(hbfont, buffer, features, featureCount, 0);
312 glyphCount = hb_buffer_get_length(buffer);
313 glyphInfo = hb_buffer_get_glyph_infos(buffer, 0);
314 glyphPos = hb_buffer_get_glyph_positions(buffer, &buflen);
315
316 ret = storeGVData(env, gvdata, slot, baseIndex, offset, startPt,
317 limit - offset, glyphCount, glyphInfo, glyphPos,
318 jdkFontInfo->devScale);
319
320 hb_buffer_destroy (buffer);
321 hb_font_destroy(hbfont);
322 free((void*)jdkFontInfo);
323 if (features != NULL) free(features);
324 (*env)->ReleaseCharArrayElements(env, text, chars, JNI_ABORT);
325 return ret;
326}
327
328