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 "ScopedStringChars.h"
     25 #include "ScopedPrimitiveArray.h"
     26 #include "JNIHelp.h"
     27 #include "core_jni_helpers.h"
     28 #include <cstdint>
     29 #include <vector>
     30 #include <list>
     31 #include <algorithm>
     32 
     33 #include "SkPaint.h"
     34 #include "SkTypeface.h"
     35 #include <hwui/MinikinSkia.h>
     36 #include <hwui/MinikinUtils.h>
     37 #include <hwui/Paint.h>
     38 #include <minikin/FontCollection.h>
     39 #include <minikin/LineBreaker.h>
     40 #include <minikin/MinikinFont.h>
     41 
     42 namespace android {
     43 
     44 struct JLineBreaksID {
     45     jfieldID breaks;
     46     jfieldID widths;
     47     jfieldID flags;
     48 };
     49 
     50 static jclass gLineBreaks_class;
     51 static JLineBreaksID gLineBreaks_fieldID;
     52 
     53 // set text and set a number of parameters for creating a layout (width, tabstops, strategy,
     54 // hyphenFrequency)
     55 static void nSetupParagraph(JNIEnv* env, jclass, jlong nativePtr, jcharArray text, jint length,
     56         jfloat firstWidth, jint firstWidthLineLimit, jfloat restWidth,
     57         jintArray variableTabStops, jint defaultTabStop, jint strategy, jint hyphenFrequency) {
     58     LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
     59     b->resize(length);
     60     env->GetCharArrayRegion(text, 0, length, b->buffer());
     61     b->setText();
     62     b->setLineWidths(firstWidth, firstWidthLineLimit, restWidth);
     63     if (variableTabStops == nullptr) {
     64         b->setTabStops(nullptr, 0, defaultTabStop);
     65     } else {
     66         ScopedIntArrayRO stops(env, variableTabStops);
     67         b->setTabStops(stops.get(), stops.size(), defaultTabStop);
     68     }
     69     b->setStrategy(static_cast<BreakStrategy>(strategy));
     70     b->setHyphenationFrequency(static_cast<HyphenationFrequency>(hyphenFrequency));
     71 }
     72 
     73 static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
     74                         jfloatArray recycleWidths, jintArray recycleFlags,
     75                         jint recycleLength, size_t nBreaks, const jint* breaks,
     76                         const jfloat* widths, const jint* flags) {
     77     if ((size_t)recycleLength < nBreaks) {
     78         // have to reallocate buffers
     79         recycleBreaks = env->NewIntArray(nBreaks);
     80         recycleWidths = env->NewFloatArray(nBreaks);
     81         recycleFlags = env->NewIntArray(nBreaks);
     82 
     83         env->SetObjectField(recycle, gLineBreaks_fieldID.breaks, recycleBreaks);
     84         env->SetObjectField(recycle, gLineBreaks_fieldID.widths, recycleWidths);
     85         env->SetObjectField(recycle, gLineBreaks_fieldID.flags, recycleFlags);
     86     }
     87     // copy data
     88     env->SetIntArrayRegion(recycleBreaks, 0, nBreaks, breaks);
     89     env->SetFloatArrayRegion(recycleWidths, 0, nBreaks, widths);
     90     env->SetIntArrayRegion(recycleFlags, 0, nBreaks, flags);
     91 }
     92 
     93 static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
     94                                jobject recycle, jintArray recycleBreaks,
     95                                jfloatArray recycleWidths, jintArray recycleFlags,
     96                                jint recycleLength) {
     97     LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
     98 
     99     size_t nBreaks = b->computeBreaks();
    100 
    101     recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleFlags, recycleLength,
    102             nBreaks, b->getBreaks(), b->getWidths(), b->getFlags());
    103 
    104     b->finish();
    105 
    106     return static_cast<jint>(nBreaks);
    107 }
    108 
    109 static jlong nNewBuilder(JNIEnv*, jclass) {
    110     return reinterpret_cast<jlong>(new LineBreaker);
    111 }
    112 
    113 static void nFreeBuilder(JNIEnv*, jclass, jlong nativePtr) {
    114     delete reinterpret_cast<LineBreaker*>(nativePtr);
    115 }
    116 
    117 static void nFinishBuilder(JNIEnv*, jclass, jlong nativePtr) {
    118     LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
    119     b->finish();
    120 }
    121 
    122 static jlong nLoadHyphenator(JNIEnv* env, jclass, jobject buffer, jint offset) {
    123     const uint8_t* bytebuf = nullptr;
    124     if (buffer != nullptr) {
    125         void* rawbuf = env->GetDirectBufferAddress(buffer);
    126         if (rawbuf != nullptr) {
    127             bytebuf = reinterpret_cast<const uint8_t*>(rawbuf) + offset;
    128         } else {
    129             ALOGE("failed to get direct buffer address");
    130         }
    131     }
    132     Hyphenator* hyphenator = Hyphenator::loadBinary(bytebuf);
    133     return reinterpret_cast<jlong>(hyphenator);
    134 }
    135 
    136 static void nSetLocale(JNIEnv* env, jclass, jlong nativePtr, jstring javaLocaleName,
    137         jlong nativeHyphenator) {
    138     ScopedIcuLocale icuLocale(env, javaLocaleName);
    139     LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
    140     Hyphenator* hyphenator = reinterpret_cast<Hyphenator*>(nativeHyphenator);
    141 
    142     if (icuLocale.valid()) {
    143         b->setLocale(icuLocale.locale(), hyphenator);
    144     }
    145 }
    146 
    147 static void nSetIndents(JNIEnv* env, jclass, jlong nativePtr, jintArray indents) {
    148     ScopedIntArrayRO indentArr(env, indents);
    149     std::vector<float> indentVec(indentArr.get(), indentArr.get() + indentArr.size());
    150     LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
    151     b->setIndents(indentVec);
    152 }
    153 
    154 // Basically similar to Paint.getTextRunAdvances but with C++ interface
    155 static jfloat nAddStyleRun(JNIEnv* env, jclass, jlong nativePtr,
    156         jlong nativePaint, jlong nativeTypeface, jint start, jint end, jboolean isRtl) {
    157     LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
    158     Paint* paint = reinterpret_cast<Paint*>(nativePaint);
    159     Typeface* typeface = reinterpret_cast<Typeface*>(nativeTypeface);
    160     FontCollection *font;
    161     MinikinPaint minikinPaint;
    162     FontStyle style = MinikinUtils::prepareMinikinPaint(&minikinPaint, &font, paint, typeface);
    163     return b->addStyleRun(&minikinPaint, font, style, start, end, isRtl);
    164 }
    165 
    166 // Accept width measurements for the run, passed in from Java
    167 static void nAddMeasuredRun(JNIEnv* env, jclass, jlong nativePtr,
    168         jint start, jint end, jfloatArray widths) {
    169     LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
    170     env->GetFloatArrayRegion(widths, start, end - start, b->charWidths() + start);
    171     b->addStyleRun(nullptr, nullptr, FontStyle{}, start, end, false);
    172 }
    173 
    174 static void nAddReplacementRun(JNIEnv* env, jclass, jlong nativePtr,
    175         jint start, jint end, jfloat width) {
    176     LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
    177     b->addReplacement(start, end, width);
    178 }
    179 
    180 static void nGetWidths(JNIEnv* env, jclass, jlong nativePtr, jfloatArray widths) {
    181     LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
    182     env->SetFloatArrayRegion(widths, 0, b->size(), b->charWidths());
    183 }
    184 
    185 static const JNINativeMethod gMethods[] = {
    186     // TODO performance: many of these are candidates for fast jni, awaiting guidance
    187     {"nNewBuilder", "()J", (void*) nNewBuilder},
    188     {"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
    189     {"nFinishBuilder", "(J)V", (void*) nFinishBuilder},
    190     {"nLoadHyphenator", "(Ljava/nio/ByteBuffer;I)J", (void*) nLoadHyphenator},
    191     {"nSetLocale", "(JLjava/lang/String;J)V", (void*) nSetLocale},
    192     {"nSetupParagraph", "(J[CIFIF[IIII)V", (void*) nSetupParagraph},
    193     {"nSetIndents", "(J[I)V", (void*) nSetIndents},
    194     {"nAddStyleRun", "(JJJIIZ)F", (void*) nAddStyleRun},
    195     {"nAddMeasuredRun", "(JII[F)V", (void*) nAddMeasuredRun},
    196     {"nAddReplacementRun", "(JIIF)V", (void*) nAddReplacementRun},
    197     {"nGetWidths", "(J[F)V", (void*) nGetWidths},
    198     {"nComputeLineBreaks", "(JLandroid/text/StaticLayout$LineBreaks;[I[F[II)I",
    199         (void*) nComputeLineBreaks}
    200 };
    201 
    202 int register_android_text_StaticLayout(JNIEnv* env)
    203 {
    204     gLineBreaks_class = MakeGlobalRefOrDie(env,
    205             FindClassOrDie(env, "android/text/StaticLayout$LineBreaks"));
    206 
    207     gLineBreaks_fieldID.breaks = GetFieldIDOrDie(env, gLineBreaks_class, "breaks", "[I");
    208     gLineBreaks_fieldID.widths = GetFieldIDOrDie(env, gLineBreaks_class, "widths", "[F");
    209     gLineBreaks_fieldID.flags = GetFieldIDOrDie(env, gLineBreaks_class, "flags", "[I");
    210 
    211     return RegisterMethodsOrDie(env, "android/text/StaticLayout", gMethods, NELEM(gMethods));
    212 }
    213 
    214 }
    215