Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "StaticLayout"
     18 
     19 #include "ScopedIcuLocale.h"
     20 #include "unicode/locid.h"
     21 #include "unicode/brkiter.h"
     22 #include "utils/misc.h"
     23 #include "utils/Log.h"
     24 #include <nativehelper/ScopedStringChars.h>
     25 #include <nativehelper/ScopedPrimitiveArray.h>
     26 #include <nativehelper/JNIHelp.h>
     27 #include "core_jni_helpers.h"
     28 #include "scoped_nullable_primitive_array.h"
     29 #include <cstdint>
     30 #include <vector>
     31 #include <list>
     32 #include <algorithm>
     33 
     34 #include "SkPaint.h"
     35 #include "SkTypeface.h"
     36 #include <hwui/MinikinSkia.h>
     37 #include <hwui/MinikinUtils.h>
     38 #include <hwui/Paint.h>
     39 #include <minikin/FontCollection.h>
     40 #include <minikin/AndroidLineBreakerHelper.h>
     41 #include <minikin/MinikinFont.h>
     42 
     43 namespace android {
     44 
     45 struct JLineBreaksID {
     46     jfieldID breaks;
     47     jfieldID widths;
     48     jfieldID ascents;
     49     jfieldID descents;
     50     jfieldID flags;
     51 };
     52 
     53 static jclass gLineBreaks_class;
     54 static JLineBreaksID gLineBreaks_fieldID;
     55 
     56 static inline std::vector<float> jintArrayToFloatVector(JNIEnv* env, jintArray javaArray) {
     57     if (javaArray == nullptr) {
     58          return std::vector<float>();
     59     } else {
     60         ScopedIntArrayRO intArr(env, javaArray);
     61         return std::vector<float>(intArr.get(), intArr.get() + intArr.size());
     62     }
     63 }
     64 
     65 static inline minikin::android::StaticLayoutNative* toNative(jlong ptr) {
     66     return reinterpret_cast<minikin::android::StaticLayoutNative*>(ptr);
     67 }
     68 
     69 // set text and set a number of parameters for creating a layout (width, tabstops, strategy,
     70 // hyphenFrequency)
     71 static jlong nInit(JNIEnv* env, jclass /* unused */,
     72         jint breakStrategy, jint hyphenationFrequency, jboolean isJustified,
     73         jintArray indents, jintArray leftPaddings, jintArray rightPaddings) {
     74     return reinterpret_cast<jlong>(new minikin::android::StaticLayoutNative(
     75             static_cast<minikin::BreakStrategy>(breakStrategy),
     76             static_cast<minikin::HyphenationFrequency>(hyphenationFrequency),
     77             isJustified,
     78             jintArrayToFloatVector(env, indents),
     79             jintArrayToFloatVector(env, leftPaddings),
     80             jintArrayToFloatVector(env, rightPaddings)));
     81 }
     82 
     83 // CriticalNative
     84 static void nFinish(jlong nativePtr) {
     85     delete toNative(nativePtr);
     86 }
     87 
     88 static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
     89                         jfloatArray recycleWidths, jfloatArray recycleAscents,
     90                         jfloatArray recycleDescents, jintArray recycleFlags,
     91                         jint recycleLength, const minikin::LineBreakResult& result) {
     92     const size_t nBreaks = result.breakPoints.size();
     93     if ((size_t)recycleLength < nBreaks) {
     94         // have to reallocate buffers
     95         recycleBreaks = env->NewIntArray(nBreaks);
     96         recycleWidths = env->NewFloatArray(nBreaks);
     97         recycleAscents = env->NewFloatArray(nBreaks);
     98         recycleDescents = env->NewFloatArray(nBreaks);
     99         recycleFlags = env->NewIntArray(nBreaks);
    100 
    101         env->SetObjectField(recycle, gLineBreaks_fieldID.breaks, recycleBreaks);
    102         env->SetObjectField(recycle, gLineBreaks_fieldID.widths, recycleWidths);
    103         env->SetObjectField(recycle, gLineBreaks_fieldID.ascents, recycleAscents);
    104         env->SetObjectField(recycle, gLineBreaks_fieldID.descents, recycleDescents);
    105         env->SetObjectField(recycle, gLineBreaks_fieldID.flags, recycleFlags);
    106     }
    107     // copy data
    108     env->SetIntArrayRegion(recycleBreaks, 0, nBreaks, result.breakPoints.data());
    109     env->SetFloatArrayRegion(recycleWidths, 0, nBreaks, result.widths.data());
    110     env->SetFloatArrayRegion(recycleAscents, 0, nBreaks, result.ascents.data());
    111     env->SetFloatArrayRegion(recycleDescents, 0, nBreaks, result.descents.data());
    112     env->SetIntArrayRegion(recycleFlags, 0, nBreaks, result.flags.data());
    113 }
    114 
    115 static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
    116         // Inputs
    117         jcharArray javaText,
    118         jlong measuredTextPtr,
    119         jint length,
    120         jfloat firstWidth,
    121         jint firstWidthLineCount,
    122         jfloat restWidth,
    123         jintArray variableTabStops,
    124         jint defaultTabStop,
    125         jint indentsOffset,
    126 
    127         // Outputs
    128         jobject recycle,
    129         jint recycleLength,
    130         jintArray recycleBreaks,
    131         jfloatArray recycleWidths,
    132         jfloatArray recycleAscents,
    133         jfloatArray recycleDescents,
    134         jintArray recycleFlags,
    135         jfloatArray charWidths) {
    136 
    137     minikin::android::StaticLayoutNative* builder = toNative(nativePtr);
    138 
    139     ScopedCharArrayRO text(env, javaText);
    140     ScopedNullableIntArrayRO tabStops(env, variableTabStops);
    141 
    142     minikin::U16StringPiece u16Text(text.get(), length);
    143     minikin::MeasuredText* measuredText = reinterpret_cast<minikin::MeasuredText*>(measuredTextPtr);
    144     minikin::LineBreakResult result = builder->computeBreaks(
    145             u16Text, *measuredText, firstWidth, firstWidthLineCount, restWidth, indentsOffset,
    146             tabStops.get(), tabStops.size(), defaultTabStop);
    147 
    148     recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleAscents, recycleDescents,
    149             recycleFlags, recycleLength, result);
    150 
    151     env->SetFloatArrayRegion(charWidths, 0, measuredText->widths.size(),
    152                              measuredText->widths.data());
    153 
    154     return static_cast<jint>(result.breakPoints.size());
    155 }
    156 
    157 static const JNINativeMethod gMethods[] = {
    158     // Fast Natives
    159     {"nInit", "("
    160         "I"  // breakStrategy
    161         "I"  // hyphenationFrequency
    162         "Z"  // isJustified
    163         "[I"  // indents
    164         "[I"  // left paddings
    165         "[I"  // right paddings
    166         ")J", (void*) nInit},
    167 
    168     // Critical Natives
    169     {"nFinish", "(J)V", (void*) nFinish},
    170 
    171     // Regular JNI
    172     {"nComputeLineBreaks", "("
    173         "J"  // nativePtr
    174 
    175         // Inputs
    176         "[C"  // text
    177         "J"  // MeasuredParagraph ptr.
    178         "I"  // length
    179         "F"  // firstWidth
    180         "I"  // firstWidthLineCount
    181         "F"  // restWidth
    182         "[I"  // variableTabStops
    183         "I"  // defaultTabStop
    184         "I"  // indentsOffset
    185 
    186         // Outputs
    187         "Landroid/text/StaticLayout$LineBreaks;"  // recycle
    188         "I"  // recycleLength
    189         "[I"  // recycleBreaks
    190         "[F"  // recycleWidths
    191         "[F"  // recycleAscents
    192         "[F"  // recycleDescents
    193         "[I"  // recycleFlags
    194         "[F"  // charWidths
    195         ")I", (void*) nComputeLineBreaks}
    196 };
    197 
    198 int register_android_text_StaticLayout(JNIEnv* env)
    199 {
    200     gLineBreaks_class = MakeGlobalRefOrDie(env,
    201             FindClassOrDie(env, "android/text/StaticLayout$LineBreaks"));
    202 
    203     gLineBreaks_fieldID.breaks = GetFieldIDOrDie(env, gLineBreaks_class, "breaks", "[I");
    204     gLineBreaks_fieldID.widths = GetFieldIDOrDie(env, gLineBreaks_class, "widths", "[F");
    205     gLineBreaks_fieldID.ascents = GetFieldIDOrDie(env, gLineBreaks_class, "ascents", "[F");
    206     gLineBreaks_fieldID.descents = GetFieldIDOrDie(env, gLineBreaks_class, "descents", "[F");
    207     gLineBreaks_fieldID.flags = GetFieldIDOrDie(env, gLineBreaks_class, "flags", "[I");
    208 
    209     return RegisterMethodsOrDie(env, "android/text/StaticLayout", gMethods, NELEM(gMethods));
    210 }
    211 
    212 }
    213