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