1 /*
   2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   3  *
   4  * This code is free software; you can redistribute it and/or modify it
   5  * under the terms of the GNU General Public License version 2 only, as
   6  * published by the Free Software Foundation.  Oracle designates this
   7  * particular file as subject to the "Classpath" exception as provided
   8  * by Oracle in the LICENSE file that accompanied this code.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  */
  24 
  25 /* Header for class sun_font_SunLayoutEngine */
  26 
  27 #include <jni_util.h>
  28 #include <stdlib.h>
  29 
  30 #include "FontInstanceAdapter.h"
  31 #ifdef USE_EXTERNAL_ICU_LE
  32 #include <layout/LayoutEngine.h>
  33 #else
  34 #include "layout/LayoutEngine.h"
  35 #endif
  36 #include "sun_font_SunLayoutEngine.h"
  37 #include "sunfontids.h"
  38 
  39 void getFloat(JNIEnv* env, jobject pt, jfloat &x, jfloat &y) {
  40     x = env->GetFloatField(pt, sunFontIDs.xFID);
  41     y = env->GetFloatField(pt, sunFontIDs.yFID);
  42 }
  43 
  44 void putFloat(JNIEnv* env, jobject pt, jfloat x, jfloat y) {
  45     env->SetFloatField(pt, sunFontIDs.xFID, x);
  46     env->SetFloatField(pt, sunFontIDs.yFID, y);
  47 }
  48 
  49 static jclass gvdClass = 0;
  50 static const char* gvdClassName = "sun/font/GlyphLayout$GVData";
  51 static jfieldID gvdCountFID = 0;
  52 static jfieldID gvdFlagsFID = 0;
  53 static jfieldID gvdGlyphsFID = 0;
  54 static jfieldID gvdPositionsFID = 0;
  55 static jfieldID gvdIndicesFID = 0;
  56 
  57 #define TYPO_RTL 0x80000000
  58 #define TYPO_MASK 0x7
  59 
  60 JNIEXPORT void JNICALL
  61 Java_sun_font_SunLayoutEngine_initGVIDs
  62     (JNIEnv *env, jclass cls) {
  63     gvdClass = env->FindClass(gvdClassName);
  64     if (!gvdClass) {
  65         JNU_ThrowClassNotFoundException(env, gvdClassName);
  66         return;
  67     }
  68     gvdClass = (jclass)env->NewGlobalRef(gvdClass);
  69       if (!gvdClass) {
  70         JNU_ThrowInternalError(env, "could not create global ref");
  71         return;
  72     }
  73     gvdCountFID = env->GetFieldID(gvdClass, "_count", "I");
  74     if (!gvdCountFID) {
  75       gvdClass = 0;
  76       JNU_ThrowNoSuchFieldException(env, "_count");
  77       return;
  78     }
  79 
  80     gvdFlagsFID = env->GetFieldID(gvdClass, "_flags", "I");
  81     if (!gvdFlagsFID) {
  82       gvdClass = 0;
  83       JNU_ThrowNoSuchFieldException(env, "_flags");
  84       return;
  85     }
  86 
  87     gvdGlyphsFID = env->GetFieldID(gvdClass, "_glyphs", "[I");
  88     if (!gvdGlyphsFID) {
  89       gvdClass = 0;
  90       JNU_ThrowNoSuchFieldException(env, "_glyphs");
  91       return;
  92     }
  93 
  94     gvdPositionsFID = env->GetFieldID(gvdClass, "_positions", "[F");
  95     if (!gvdPositionsFID) {
  96       gvdClass = 0;
  97       JNU_ThrowNoSuchFieldException(env, "_positions");
  98       return;
  99     }
 100 
 101     gvdIndicesFID = env->GetFieldID(gvdClass, "_indices", "[I");
 102     if (!gvdIndicesFID) {
 103       gvdClass = 0;
 104       JNU_ThrowNoSuchFieldException(env, "_indices");
 105       return;
 106     }
 107 }
 108 
 109 int putGV(JNIEnv* env, jint gmask, jint baseIndex, jobject gvdata, const LayoutEngine* engine, int glyphCount) {
 110     int count = env->GetIntField(gvdata, gvdCountFID);
 111 
 112     jarray glyphArray = (jarray)env->GetObjectField(gvdata, gvdGlyphsFID);
 113     if (IS_NULL(glyphArray)) {
 114       JNU_ThrowInternalError(env, "glypharray null");
 115       return 0;
 116     }
 117     jint capacity = env->GetArrayLength(glyphArray);
 118     if (count + glyphCount > capacity) {
 119       JNU_ThrowArrayIndexOutOfBoundsException(env, "");
 120       return 0;
 121     }
 122 
 123     jarray posArray = (jarray)env->GetObjectField(gvdata, gvdPositionsFID);
 124     if (IS_NULL(glyphArray)) {
 125       JNU_ThrowInternalError(env, "positions array null");
 126       return 0;
 127     }
 128     jarray inxArray = (jarray)env->GetObjectField(gvdata, gvdIndicesFID);
 129     if (IS_NULL(inxArray)) {
 130       JNU_ThrowInternalError(env, "indices array null");
 131       return 0;
 132     }
 133 
 134     int countDelta = 0;
 135 
 136     // le_uint32 is the same size as jint... forever, we hope
 137     le_uint32* glyphs = (le_uint32*)env->GetPrimitiveArrayCritical(glyphArray, NULL);
 138     if (glyphs) {
 139       jfloat* positions = (jfloat*)env->GetPrimitiveArrayCritical(posArray, NULL);
 140       if (positions) {
 141         jint* indices = (jint*)env->GetPrimitiveArrayCritical(inxArray, NULL);
 142         if (indices) {
 143           LEErrorCode status = (LEErrorCode)0;
 144           engine->getGlyphs(glyphs + count, gmask, status);
 145           engine->getGlyphPositions(positions + (count * 2), status);
 146           engine->getCharIndices((le_int32*)(indices + count), baseIndex, status);
 147 
 148           countDelta = glyphCount;
 149 
 150           // !!! need engine->getFlags to signal positions, indices data
 151           /* "0" arg used instead of JNI_COMMIT as we want the carray
 152            * to be freed by any VM that actually passes us a copy.
 153            */
 154           env->ReleasePrimitiveArrayCritical(inxArray, indices, 0);
 155         }
 156         env->ReleasePrimitiveArrayCritical(posArray, positions, 0);
 157       }
 158       env->ReleasePrimitiveArrayCritical(glyphArray, glyphs, 0);
 159     }
 160 
 161     if (countDelta) {
 162       count += countDelta;
 163       env->SetIntField(gvdata, gvdCountFID, count);
 164     }
 165 
 166   return 1;
 167 }
 168 
 169 /*
 170  * Class:     sun_font_SunLayoutEngine
 171  * Method:    nativeLayout
 172  * Signature: (Lsun/font/FontStrike;[CIIIIZLjava/awt/geom/Point2D$Float;Lsun/font/GlyphLayout$GVData;)V
 173  */
 174 JNIEXPORT void JNICALL Java_sun_font_SunLayoutEngine_nativeLayout
 175    (JNIEnv *env, jclass cls, jobject font2d, jobject strike, jfloatArray matrix, jint gmask,
 176    jint baseIndex, jcharArray text, jint start, jint limit, jint min, jint max,
 177    jint script, jint lang, jint typo_flags, jobject pt, jobject gvdata,
 178    jlong upem, jlong layoutTables)
 179 {
 180     //  fprintf(stderr, "nl font: %x strike: %x script: %d\n", font2d, strike, script); fflush(stderr);
 181   float mat[4];
 182   env->GetFloatArrayRegion(matrix, 0, 4, mat);
 183   FontInstanceAdapter fia(env, font2d, strike, mat, 72, 72, (le_int32) upem, (TTLayoutTableCache *) layoutTables);
 184   LEErrorCode success = LE_NO_ERROR;
 185   LayoutEngine *engine = LayoutEngine::layoutEngineFactory(&fia, script, lang, typo_flags & TYPO_MASK, success);
 186 
 187   if (min < 0) min = 0; if (max < min) max = min; /* defensive coding */
 188   // have to copy, yuck, since code does upcalls now.  this will be soooo slow
 189   jint len = max - min;
 190   jchar buffer[256];
 191   jchar* chars = buffer;
 192   if (len > 256) {
 193     size_t size = len * sizeof(jchar);
 194     if (size / sizeof(jchar) != len) {
 195       return;
 196     }
 197     chars = (jchar*)malloc(size);
 198     if (chars == 0) {
 199       return;
 200     }
 201   }
 202   //  fprintf(stderr, "nl chars: %x text: %x min %d len %d typo %x\n", chars, text, min, len, typo_flags); fflush(stderr);
 203 
 204   env->GetCharArrayRegion(text, min, len, chars);
 205 
 206   jfloat x, y;
 207   getFloat(env, pt, x, y);
 208   jboolean rtl = (typo_flags & TYPO_RTL) != 0;
 209   int glyphCount = engine->layoutChars(chars, start - min, limit - start, len, rtl, x, y, success);
 210   //   fprintf(stderr, "sle nl len %d -> gc: %d\n", len, glyphCount); fflush(stderr);
 211 
 212   engine->getGlyphPosition(glyphCount, x, y, success);
 213 
 214   //  fprintf(stderr, "layout glyphs: %d x: %g y: %g\n", glyphCount, x, y); fflush(stderr);
 215 
 216   if (putGV(env, gmask, baseIndex, gvdata, engine, glyphCount)) {
 217     // !!! hmmm, could use current value in positions array of GVData...
 218     putFloat(env, pt, x, y);
 219   }
 220 
 221   if (chars != buffer) {
 222     free(chars);
 223   }
 224 
 225   delete engine;
 226 
 227 }