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