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 
getFloat(JNIEnv * env,jobject pt,jfloat * x,jfloat * y)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 
putFloat(JNIEnv * env,jobject pt,jfloat x,jfloat y)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 
init_JNI_IDs(JNIEnv * env)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.
storeGVData(JNIEnv * env,jobject gvdata,jint slot,jint baseIndex,int offset,jobject startPt,int charCount,int glyphCount,hb_glyph_info_t * glyphInfo,hb_glyph_position_t * glyphPos,float devScale)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 
euclidianDistance(float a,float b)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*
createJDKFontInfo(JNIEnv * env,jobject font2D,jobject fontStrike,jfloat ptSize,jlong pNativeFont,jfloatArray matrix,jboolean aat)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 
Java_sun_font_SunLayoutEngine_shape(JNIEnv * env,jclass cls,jobject font2D,jobject fontStrike,jfloat ptSize,jfloatArray matrix,jlong pNativeFont,jlong pFace,jboolean aat,jcharArray text,jobject gvdata,jint script,jint offset,jint limit,jint baseIndex,jobject startPt,jint flags,jint slot)235 JNIEXPORT 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