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