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