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 #include "LayoutEngine.h"
  32 #include "sun_font_SunLayoutEngine.h"
  33 #include "sunfontids.h"
  34 
  35 void getFloat(JNIEnv* env, jobject pt, jfloat &x, jfloat &y) {
  36     x = env->GetFloatField(pt, sunFontIDs.xFID);
  37     y = env->GetFloatField(pt, sunFontIDs.yFID);
  38 }
  39 
  40 void putFloat(JNIEnv* env, jobject pt, jfloat x, jfloat y) {
  41     env->SetFloatField(pt, sunFontIDs.xFID, x);
  42     env->SetFloatField(pt, sunFontIDs.yFID, y);
  43 }
  44 
  45 static jclass gvdClass = 0;
  46 static const char* gvdClassName = "sun/font/GlyphLayout$GVData";
  47 static jfieldID gvdCountFID = 0;
  48 static jfieldID gvdFlagsFID = 0;
  49 static jfieldID gvdGlyphsFID = 0;
  50 static jfieldID gvdPositionsFID = 0;
  51 static jfieldID gvdIndicesFID = 0;
  52 
  53 #define TYPO_RTL 0x80000000
  54 #define TYPO_MASK 0x7
  55 
  56 JNIEXPORT void JNICALL
  57 Java_sun_font_SunLayoutEngine_initGVIDs
  58     (JNIEnv *env, jclass cls) {
  59     gvdClass = env->FindClass(gvdClassName);
  60     if (!gvdClass) {
  61         JNU_ThrowClassNotFoundException(env, gvdClassName);
  62         return;
  63     }
  64     gvdClass = (jclass)env->NewGlobalRef(gvdClass);
  65       if (!gvdClass) {
  66         JNU_ThrowInternalError(env, "could not create global ref");
  67         return;
  68     }
  69     gvdCountFID = env->GetFieldID(gvdClass, "_count", "I");
  70     if (!gvdCountFID) {
  71       gvdClass = 0;
  72       JNU_ThrowNoSuchFieldException(env, "_count");
  73       return;
  74     }
  75 
  76     gvdFlagsFID = env->GetFieldID(gvdClass, "_flags", "I");
  77     if (!gvdFlagsFID) {
  78       gvdClass = 0;
  79       JNU_ThrowNoSuchFieldException(env, "_flags");
  80       return;
  81     }
  82 
  83     gvdGlyphsFID = env->GetFieldID(gvdClass, "_glyphs", "[I");
  84     if (!gvdGlyphsFID) {
  85       gvdClass = 0;
  86       JNU_ThrowNoSuchFieldException(env, "_glyphs");
  87       return;
  88     }
  89 
  90     gvdPositionsFID = env->GetFieldID(gvdClass, "_positions", "[F");
  91     if (!gvdPositionsFID) {
  92       gvdClass = 0;
  93       JNU_ThrowNoSuchFieldException(env, "_positions");
  94       return;
  95     }
  96 
  97     gvdIndicesFID = env->GetFieldID(gvdClass, "_indices", "[I");
  98     if (!gvdIndicesFID) {
  99       gvdClass = 0;
 100       JNU_ThrowNoSuchFieldException(env, "_indices");
 101       return;
 102     }
 103 }
 104 
 105 int putGV(JNIEnv* env, jint gmask, jint baseIndex, jobject gvdata, const LayoutEngine* engine, int glyphCount) {
 106     int count = env->GetIntField(gvdata, gvdCountFID);
 107     if (count < 0) {
 108       JNU_ThrowInternalError(env, "count negative");
 109       return 0;
 110     }
 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   if (engine == NULL) {
 187     env->SetIntField(gvdata, gvdCountFID, -1); // flag failure
 188     return;
 189   }
 190 
 191   if (min < 0) min = 0; if (max < min) max = min; /* defensive coding */
 192   // have to copy, yuck, since code does upcalls now.  this will be soooo slow
 193   jint len = max - min;
 194   jchar buffer[256];
 195   jchar* chars = buffer;
 196   if (len > 256) {
 197     size_t size = len * sizeof(jchar);
 198     if (size / sizeof(jchar) != len) {
 199       return;
 200     }
 201     chars = (jchar*)malloc(size);
 202     if (chars == 0) {
 203       return;
 204     }
 205   }
 206   //  fprintf(stderr, "nl chars: %x text: %x min %d len %d typo %x\n", chars, text, min, len, typo_flags); fflush(stderr);
 207 
 208   env->GetCharArrayRegion(text, min, len, chars);
 209 
 210   jfloat x, y;
 211   getFloat(env, pt, x, y);
 212   jboolean rtl = (typo_flags & TYPO_RTL) != 0;
 213   int glyphCount = engine->layoutChars(chars, start - min, limit - start, len, rtl, x, y, success);
 214     // fprintf(stderr, "sle nl len %d -> gc: %d\n", len, glyphCount); fflush(stderr);
 215 
 216   engine->getGlyphPosition(glyphCount, x, y, success);
 217 
 218    // fprintf(stderr, "layout glyphs: %d x: %g y: %g\n", glyphCount, x, y); fflush(stderr);
 219    if (LE_FAILURE(success)) {
 220        env->SetIntField(gvdata, gvdCountFID, -1); // flag failure
 221    } else {
 222       if (putGV(env, gmask, baseIndex, gvdata, engine, glyphCount)) {
 223         // !!! hmmm, could use current value in positions array of GVData...
 224         putFloat(env, pt, x, y);
 225       }
 226    }
 227 
 228   if (chars != buffer) {
 229     free(chars);
 230   }
 231 
 232   delete engine;
 233 
 234 }