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 40 namespace android { 41 42 struct JLineBreaksID { 43 jfieldID breaks; 44 jfieldID widths; 45 jfieldID flags; 46 }; 47 48 static jclass gLineBreaks_class; 49 static JLineBreaksID gLineBreaks_fieldID; 50 51 // set text and set a number of parameters for creating a layout (width, tabstops, strategy, 52 // hyphenFrequency) 53 static 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 71 static 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 91 static 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 107 static jlong nNewBuilder(JNIEnv*, jclass) { 108 return reinterpret_cast<jlong>(new LineBreaker); 109 } 110 111 static void nFreeBuilder(JNIEnv*, jclass, jlong nativePtr) { 112 delete reinterpret_cast<LineBreaker*>(nativePtr); 113 } 114 115 static void nFinishBuilder(JNIEnv*, jclass, jlong nativePtr) { 116 LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr); 117 b->finish(); 118 } 119 120 static jlong nLoadHyphenator(JNIEnv* env, jclass, jstring patternData) { 121 ScopedStringChars str(env, patternData); 122 Hyphenator* hyphenator = Hyphenator::load(str.get(), str.size()); 123 return reinterpret_cast<jlong>(hyphenator); 124 } 125 126 static void nSetLocale(JNIEnv* env, jclass, jlong nativePtr, jstring javaLocaleName, 127 jlong nativeHyphenator) { 128 ScopedIcuLocale icuLocale(env, javaLocaleName); 129 LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr); 130 Hyphenator* hyphenator = reinterpret_cast<Hyphenator*>(nativeHyphenator); 131 132 if (icuLocale.valid()) { 133 b->setLocale(icuLocale.locale(), hyphenator); 134 } 135 } 136 137 static void nSetIndents(JNIEnv* env, jclass, jlong nativePtr, jintArray indents) { 138 ScopedIntArrayRO indentArr(env, indents); 139 std::vector<float> indentVec(indentArr.get(), indentArr.get() + indentArr.size()); 140 LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr); 141 b->setIndents(indentVec); 142 } 143 144 // Basically similar to Paint.getTextRunAdvances but with C++ interface 145 static jfloat nAddStyleRun(JNIEnv* env, jclass, jlong nativePtr, 146 jlong nativePaint, jlong nativeTypeface, jint start, jint end, jboolean isRtl) { 147 LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr); 148 Paint* paint = reinterpret_cast<Paint*>(nativePaint); 149 TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(nativeTypeface); 150 FontCollection *font; 151 MinikinPaint minikinPaint; 152 FontStyle style = MinikinUtils::prepareMinikinPaint(&minikinPaint, &font, paint, typeface); 153 return b->addStyleRun(&minikinPaint, font, style, start, end, isRtl); 154 } 155 156 // Accept width measurements for the run, passed in from Java 157 static void nAddMeasuredRun(JNIEnv* env, jclass, jlong nativePtr, 158 jint start, jint end, jfloatArray widths) { 159 LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr); 160 env->GetFloatArrayRegion(widths, start, end - start, b->charWidths() + start); 161 b->addStyleRun(nullptr, nullptr, FontStyle{}, start, end, false); 162 } 163 164 static void nAddReplacementRun(JNIEnv* env, jclass, jlong nativePtr, 165 jint start, jint end, jfloat width) { 166 LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr); 167 b->addReplacement(start, end, width); 168 } 169 170 static void nGetWidths(JNIEnv* env, jclass, jlong nativePtr, jfloatArray widths) { 171 LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr); 172 env->SetFloatArrayRegion(widths, 0, b->size(), b->charWidths()); 173 } 174 175 static JNINativeMethod gMethods[] = { 176 // TODO performance: many of these are candidates for fast jni, awaiting guidance 177 {"nNewBuilder", "()J", (void*) nNewBuilder}, 178 {"nFreeBuilder", "(J)V", (void*) nFreeBuilder}, 179 {"nFinishBuilder", "(J)V", (void*) nFinishBuilder}, 180 {"nLoadHyphenator", "(Ljava/lang/String;)J", (void*) nLoadHyphenator}, 181 {"nSetLocale", "(JLjava/lang/String;J)V", (void*) nSetLocale}, 182 {"nSetupParagraph", "(J[CIFIF[IIII)V", (void*) nSetupParagraph}, 183 {"nSetIndents", "(J[I)V", (void*) nSetIndents}, 184 {"nAddStyleRun", "(JJJIIZ)F", (void*) nAddStyleRun}, 185 {"nAddMeasuredRun", "(JII[F)V", (void*) nAddMeasuredRun}, 186 {"nAddReplacementRun", "(JIIF)V", (void*) nAddReplacementRun}, 187 {"nGetWidths", "(J[F)V", (void*) nGetWidths}, 188 {"nComputeLineBreaks", "(JLandroid/text/StaticLayout$LineBreaks;[I[F[II)I", 189 (void*) nComputeLineBreaks} 190 }; 191 192 int register_android_text_StaticLayout(JNIEnv* env) 193 { 194 gLineBreaks_class = MakeGlobalRefOrDie(env, 195 FindClassOrDie(env, "android/text/StaticLayout$LineBreaks")); 196 197 gLineBreaks_fieldID.breaks = GetFieldIDOrDie(env, gLineBreaks_class, "breaks", "[I"); 198 gLineBreaks_fieldID.widths = GetFieldIDOrDie(env, gLineBreaks_class, "widths", "[F"); 199 gLineBreaks_fieldID.flags = GetFieldIDOrDie(env, gLineBreaks_class, "flags", "[I"); 200 201 return RegisterMethodsOrDie(env, "android/text/StaticLayout", gMethods, NELEM(gMethods)); 202 } 203 204 } 205