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 /*
 279      features = malloc(sizeof(hb_feature_t) * numFeatures);
 280      if (features) {
 281          hb_feature_from_string("kern", -1, &features[featureCount++]);
 282          hb_feature_from_string("liga", -1, &features[featureCount++]);
 283      }
 284 */
 285 
 286      chars = (*env)->GetCharArrayElements(env, text, NULL);
 287      len = (*env)->GetArrayLength(env, text);
 288 
 289      hb_buffer_add_utf16(buffer, chars, len, offset, limit-offset);
 290 
 291      hb_shape_full(hbfont, buffer, features, featureCount, 0);
 292      glyphCount = hb_buffer_get_length(buffer);
 293      glyphInfo = hb_buffer_get_glyph_infos(buffer, 0);
 294      glyphPos = hb_buffer_get_glyph_positions(buffer, &buflen);
 295      for (i = 0; i < glyphCount; i++) {
 296          int index = glyphInfo[i].codepoint;
 297          int xadv = (glyphPos[i].x_advance);
 298          int yadv = (glyphPos[i].y_advance);
 299      }
 300      // On "input" HB assigns a cluster index to each character in UTF-16.
 301      // On output where a sequence of characters have been mapped to
 302      // a glyph they are all mapped to the cluster index of the first character.
 303      // The next cluster index will be that of the first character in the
 304      // next cluster. So cluster indexes may 'skip' on output.
 305      // This can also happen if there are supplementary code-points
 306      // such that two UTF-16 characters are needed to make one codepoint.
 307      // In RTL text you need to count down.
 308      // So the following code tries to build the reverse map as expected
 309      // by calling code.
 310 
 311      storeGVData(env, gvdata, slot, baseIndex, startPt,
 312                  glyphCount, glyphInfo, glyphPos, direction);
 313 
 314      hb_buffer_destroy (buffer);
 315      hb_font_destroy(hbfont);
 316      free((void*)jdkFontInfo);
 317      if (features != NULL) free(features);
 318 
 319      return JNI_TRUE;
 320 }
 321