blob: c1419ba6c7c6f1a74ba7733109d152afe8bffe0e [file] [log] [blame]
Anish Athalye88b5b0b2014-06-24 14:39:43 -07001/*
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"
Steven Moreland2279b252017-07-19 09:50:45 -070024#include <nativehelper/ScopedStringChars.h>
25#include <nativehelper/ScopedPrimitiveArray.h>
26#include <nativehelper/JNIHelp.h>
Andreas Gampeed6b9df2014-11-20 22:02:20 -080027#include "core_jni_helpers.h"
Anish Athalyec8f9e622014-07-21 15:26:34 -070028#include <cstdint>
Anish Athalye88b5b0b2014-06-24 14:39:43 -070029#include <vector>
Anish Athalyec8f9e622014-07-21 15:26:34 -070030#include <list>
31#include <algorithm>
Anish Athalye88b5b0b2014-06-24 14:39:43 -070032
Raph Levien70616ec2015-03-04 10:41:30 -080033#include "SkPaint.h"
34#include "SkTypeface.h"
sergeyvdccca442016-03-21 15:38:21 -070035#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>
Raph Levien4c1f12e2015-03-02 16:29:23 -080041
Anish Athalye88b5b0b2014-06-24 14:39:43 -070042namespace android {
43
Anish Athalyec8f9e622014-07-21 15:26:34 -070044struct JLineBreaksID {
45 jfieldID breaks;
46 jfieldID widths;
Roozbeh Pournader737dfea2017-08-10 11:32:24 -070047 jfieldID ascents;
48 jfieldID descents;
Anish Athalyec8f9e622014-07-21 15:26:34 -070049 jfieldID flags;
50};
51
52static jclass gLineBreaks_class;
53static JLineBreaksID gLineBreaks_fieldID;
54
Seigo Nonaka749e57e2017-08-29 15:00:05 -070055class JNILineBreakerLineWidth : public minikin::LineBreaker::LineWidthDelegate {
56 public:
57 JNILineBreakerLineWidth(float firstWidth, int32_t firstLineCount, float restWidth,
Seigo Nonakab90e08f2017-10-20 15:23:51 -070058 const std::vector<float>& indents, const std::vector<float>& leftPaddings,
59 const std::vector<float>& rightPaddings, int32_t indentsAndPaddingsOffset)
Seigo Nonaka749e57e2017-08-29 15:00:05 -070060 : mFirstWidth(firstWidth), mFirstLineCount(firstLineCount), mRestWidth(restWidth),
Seigo Nonakab90e08f2017-10-20 15:23:51 -070061 mIndents(indents), mLeftPaddings(leftPaddings),
62 mRightPaddings(rightPaddings), mOffset(indentsAndPaddingsOffset) {}
Seigo Nonaka749e57e2017-08-29 15:00:05 -070063
64 float getLineWidth(size_t lineNo) override {
65 const float width = ((ssize_t)lineNo < (ssize_t)mFirstLineCount)
66 ? mFirstWidth : mRestWidth;
Roozbeh Pournader0bba7422017-09-28 12:35:10 -070067 return width - get(mIndents, lineNo);
68 }
Seigo Nonaka749e57e2017-08-29 15:00:05 -070069
Roozbeh Pournader0bba7422017-09-28 12:35:10 -070070 float getLeftPadding(size_t lineNo) override {
71 return get(mLeftPaddings, lineNo);
72 }
73
74 float getRightPadding(size_t lineNo) override {
75 return get(mRightPaddings, lineNo);
Seigo Nonaka749e57e2017-08-29 15:00:05 -070076 }
77
78 private:
Roozbeh Pournader0bba7422017-09-28 12:35:10 -070079 float get(const std::vector<float>& vec, size_t lineNo) {
80 if (vec.empty()) {
81 return 0;
82 }
83 const size_t index = lineNo + mOffset;
84 if (index < vec.size()) {
85 return vec[index];
86 } else {
87 return vec.back();
88 }
89 }
90
Seigo Nonaka749e57e2017-08-29 15:00:05 -070091 const float mFirstWidth;
92 const int32_t mFirstLineCount;
93 const float mRestWidth;
Seigo Nonakab90e08f2017-10-20 15:23:51 -070094 const std::vector<float>& mIndents;
95 const std::vector<float>& mLeftPaddings;
96 const std::vector<float>& mRightPaddings;
Roozbeh Pournader0bba7422017-09-28 12:35:10 -070097 const int32_t mOffset;
Seigo Nonaka749e57e2017-08-29 15:00:05 -070098};
99
Roozbeh Pournader0bba7422017-09-28 12:35:10 -0700100static inline std::vector<float> jintArrayToFloatVector(JNIEnv* env, jintArray javaArray) {
101 if (javaArray == nullptr) {
102 return std::vector<float>();
103 } else {
104 ScopedIntArrayRO intArr(env, javaArray);
105 return std::vector<float>(intArr.get(), intArr.get() + intArr.size());
106 }
107}
108
Seigo Nonakab90e08f2017-10-20 15:23:51 -0700109class Run {
110 public:
111 Run(int32_t start, int32_t end) : mStart(start), mEnd(end) {}
112 virtual ~Run() {}
113
114 virtual void addTo(minikin::LineBreaker* lineBreaker) = 0;
115
116 protected:
117 const int32_t mStart;
118 const int32_t mEnd;
119
120 private:
121 // Forbid copy and assign.
122 Run(const Run&) = delete;
123 void operator=(const Run&) = delete;
124};
125
126class StyleRun : public Run {
127 public:
128 StyleRun(int32_t start, int32_t end, minikin::MinikinPaint&& paint,
129 std::shared_ptr<minikin::FontCollection>&& collection,
130 minikin::FontStyle&& style, bool isRtl)
131 : Run(start, end), mPaint(std::move(paint)), mCollection(std::move(collection)),
132 mStyle(std::move(style)), mIsRtl(isRtl) {}
133
134 void addTo(minikin::LineBreaker* lineBreaker) override {
135 lineBreaker->addStyleRun(&mPaint, mCollection, mStyle, mStart, mEnd, mIsRtl);
136 }
137
138 private:
139 minikin::MinikinPaint mPaint;
140 std::shared_ptr<minikin::FontCollection> mCollection;
141 minikin::FontStyle mStyle;
142 const bool mIsRtl;
143};
144
145class Replacement : public Run {
146 public:
147 Replacement(int32_t start, int32_t end, float width, uint32_t localeListId)
148 : Run(start, end), mWidth(width), mLocaleListId(localeListId) {}
149
150 void addTo(minikin::LineBreaker* lineBreaker) override {
151 lineBreaker->addReplacement(mStart, mEnd, mWidth, mLocaleListId);
152 }
153
154 private:
155 const float mWidth;
156 const uint32_t mLocaleListId;
157};
158
159class StaticLayoutNative {
160 public:
161 StaticLayoutNative(
162 minikin::BreakStrategy strategy, minikin::HyphenationFrequency frequency,
163 bool isJustified, std::vector<float>&& indents, std::vector<float>&& leftPaddings,
164 std::vector<float>&& rightPaddings)
165 : mStrategy(strategy), mFrequency(frequency), mIsJustified(isJustified),
166 mIndents(std::move(indents)), mLeftPaddings(std::move(leftPaddings)),
167 mRightPaddings(std::move(rightPaddings)) {}
168
169 void addStyleRun(int32_t start, int32_t end, minikin::MinikinPaint&& paint,
170 std::shared_ptr<minikin::FontCollection> collection,
171 minikin::FontStyle&& style, bool isRtl) {
172 mRuns.emplace_back(std::make_unique<StyleRun>(
173 start, end, std::move(paint), std::move(collection), std::move(style), isRtl));
174 }
175
176 void addReplacementRun(int32_t start, int32_t end, float width, uint32_t localeListId) {
177 mRuns.emplace_back(std::make_unique<Replacement>(start, end, width, localeListId));
178 }
179
180 // Only valid while this instance is alive.
181 inline std::unique_ptr<minikin::LineBreaker::LineWidthDelegate> buildLineWidthDelegate(
182 float firstWidth, int32_t firstLineCount, float restWidth,
183 int32_t indentsAndPaddingsOffset) {
184 return std::make_unique<JNILineBreakerLineWidth>(
185 firstWidth, firstLineCount, restWidth, mIndents, mLeftPaddings, mRightPaddings,
186 indentsAndPaddingsOffset);
187 }
188
189 void addRuns(minikin::LineBreaker* lineBreaker) {
190 for (const auto& run : mRuns) {
191 run->addTo(lineBreaker);
192 }
193 }
194
195 void clearRuns() {
196 mRuns.clear();
197 }
198
199 inline minikin::BreakStrategy getStrategy() const { return mStrategy; }
200 inline minikin::HyphenationFrequency getFrequency() const { return mFrequency; }
201 inline bool isJustified() const { return mIsJustified; }
202
203 private:
204 const minikin::BreakStrategy mStrategy;
205 const minikin::HyphenationFrequency mFrequency;
206 const bool mIsJustified;
207 const std::vector<float> mIndents;
208 const std::vector<float> mLeftPaddings;
209 const std::vector<float> mRightPaddings;
210
211 std::vector<std::unique_ptr<Run>> mRuns;
212};
213
214static inline StaticLayoutNative* toNative(jlong ptr) {
215 return reinterpret_cast<StaticLayoutNative*>(ptr);
216}
217
Roozbeh Pournader95c7a132015-05-12 12:01:06 -0700218// set text and set a number of parameters for creating a layout (width, tabstops, strategy,
219// hyphenFrequency)
Seigo Nonakab90e08f2017-10-20 15:23:51 -0700220static jlong nInit(JNIEnv* env, jclass /* unused */,
221 jint breakStrategy, jint hyphenationFrequency, jboolean isJustified,
222 jintArray indents, jintArray leftPaddings, jintArray rightPaddings) {
223 return reinterpret_cast<jlong>(new StaticLayoutNative(
224 static_cast<minikin::BreakStrategy>(breakStrategy),
225 static_cast<minikin::HyphenationFrequency>(hyphenationFrequency),
226 isJustified,
227 jintArrayToFloatVector(env, indents),
228 jintArrayToFloatVector(env, leftPaddings),
229 jintArrayToFloatVector(env, rightPaddings)));
230}
Seigo Nonakabafe1972017-08-24 15:30:29 -0700231
Seigo Nonakab90e08f2017-10-20 15:23:51 -0700232// CriticalNative
233static void nFinish(jlong nativePtr) {
234 delete toNative(nativePtr);
Raph Levienc94f7422015-03-06 19:19:48 -0800235}
Anish Athalyec8f9e622014-07-21 15:26:34 -0700236
Raph Levienc94f7422015-03-06 19:19:48 -0800237static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
Roozbeh Pournader737dfea2017-08-10 11:32:24 -0700238 jfloatArray recycleWidths, jfloatArray recycleAscents,
239 jfloatArray recycleDescents, jintArray recycleFlags,
Raph Levienc94f7422015-03-06 19:19:48 -0800240 jint recycleLength, size_t nBreaks, const jint* breaks,
Roozbeh Pournader737dfea2017-08-10 11:32:24 -0700241 const jfloat* widths, const jfloat* ascents, const jfloat* descents,
242 const jint* flags) {
Raph Levienc94f7422015-03-06 19:19:48 -0800243 if ((size_t)recycleLength < nBreaks) {
Anish Athalyec8f9e622014-07-21 15:26:34 -0700244 // have to reallocate buffers
Raph Levienc94f7422015-03-06 19:19:48 -0800245 recycleBreaks = env->NewIntArray(nBreaks);
246 recycleWidths = env->NewFloatArray(nBreaks);
Roozbeh Pournader737dfea2017-08-10 11:32:24 -0700247 recycleAscents = env->NewFloatArray(nBreaks);
248 recycleDescents = env->NewFloatArray(nBreaks);
Raph Levien26d443a2015-03-30 14:18:32 -0700249 recycleFlags = env->NewIntArray(nBreaks);
Anish Athalyec8f9e622014-07-21 15:26:34 -0700250
251 env->SetObjectField(recycle, gLineBreaks_fieldID.breaks, recycleBreaks);
252 env->SetObjectField(recycle, gLineBreaks_fieldID.widths, recycleWidths);
Roozbeh Pournader737dfea2017-08-10 11:32:24 -0700253 env->SetObjectField(recycle, gLineBreaks_fieldID.ascents, recycleAscents);
254 env->SetObjectField(recycle, gLineBreaks_fieldID.descents, recycleDescents);
Anish Athalyec8f9e622014-07-21 15:26:34 -0700255 env->SetObjectField(recycle, gLineBreaks_fieldID.flags, recycleFlags);
256 }
257 // copy data
Raph Levienc94f7422015-03-06 19:19:48 -0800258 env->SetIntArrayRegion(recycleBreaks, 0, nBreaks, breaks);
259 env->SetFloatArrayRegion(recycleWidths, 0, nBreaks, widths);
Roozbeh Pournader737dfea2017-08-10 11:32:24 -0700260 env->SetFloatArrayRegion(recycleAscents, 0, nBreaks, ascents);
261 env->SetFloatArrayRegion(recycleDescents, 0, nBreaks, descents);
Raph Levien26d443a2015-03-30 14:18:32 -0700262 env->SetIntArrayRegion(recycleFlags, 0, nBreaks, flags);
Raph Levien70616ec2015-03-04 10:41:30 -0800263}
264
Raph Levien4c1f12e2015-03-02 16:29:23 -0800265static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
Seigo Nonakab90e08f2017-10-20 15:23:51 -0700266 // Inputs
267 jcharArray text,
268 jint length,
269 jfloat firstWidth,
270 jint firstWidthLineCount,
271 jfloat restWidth,
272 jintArray variableTabStops,
273 jint defaultTabStop,
274 jint indentsOffset,
Raph Levien4c1f12e2015-03-02 16:29:23 -0800275
Seigo Nonakab90e08f2017-10-20 15:23:51 -0700276 // Outputs
277 jobject recycle,
278 jint recycleLength,
279 jintArray recycleBreaks,
280 jfloatArray recycleWidths,
281 jfloatArray recycleAscents,
282 jfloatArray recycleDescents,
283 jintArray recycleFlags,
284 jfloatArray charWidths) {
285
286 StaticLayoutNative* builder = toNative(nativePtr);
287
288 // TODO: Reorganize minikin APIs.
289 minikin::LineBreaker b;
290 b.resize(length);
291 env->GetCharArrayRegion(text, 0, length, b.buffer());
292 b.setText();
293 if (variableTabStops == nullptr) {
294 b.setTabStops(nullptr, 0, defaultTabStop);
295 } else {
296 ScopedIntArrayRO stops(env, variableTabStops);
297 b.setTabStops(stops.get(), stops.size(), defaultTabStop);
298 }
299 b.setStrategy(builder->getStrategy());
300 b.setHyphenationFrequency(builder->getFrequency());
301 b.setJustified(builder->isJustified());
302 b.setLineWidthDelegate(builder->buildLineWidthDelegate(
303 firstWidth, firstWidthLineCount, restWidth, indentsOffset));
304
305 builder->addRuns(&b);
306
307 size_t nBreaks = b.computeBreaks();
Anish Athalye88b5b0b2014-06-24 14:39:43 -0700308
Roozbeh Pournader737dfea2017-08-10 11:32:24 -0700309 recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleAscents, recycleDescents,
Seigo Nonakab90e08f2017-10-20 15:23:51 -0700310 recycleFlags, recycleLength, nBreaks, b.getBreaks(), b.getWidths(), b.getAscents(),
311 b.getDescents(), b.getFlags());
Anish Athalye88b5b0b2014-06-24 14:39:43 -0700312
Seigo Nonakab90e08f2017-10-20 15:23:51 -0700313 env->SetFloatArrayRegion(charWidths, 0, b.size(), b.charWidths());
Seigo Nonaka2dd1a552017-10-06 11:41:00 -0700314
Seigo Nonakab90e08f2017-10-20 15:23:51 -0700315 b.finish();
316 builder->clearRuns();
Anish Athalye88b5b0b2014-06-24 14:39:43 -0700317
Raph Levienc94f7422015-03-06 19:19:48 -0800318 return static_cast<jint>(nBreaks);
Anish Athalye88b5b0b2014-06-24 14:39:43 -0700319}
320
Raph Levien70616ec2015-03-04 10:41:30 -0800321// Basically similar to Paint.getTextRunAdvances but with C++ interface
Seigo Nonakab90e08f2017-10-20 15:23:51 -0700322// CriticalNative
323static void nAddStyleRun(jlong nativePtr, jlong nativePaint, jint start, jint end, jboolean isRtl) {
324 StaticLayoutNative* builder = toNative(nativePtr);
Raph Levien70616ec2015-03-04 10:41:30 -0800325 Paint* paint = reinterpret_cast<Paint*>(nativePaint);
Seigo Nonaka318ca042017-08-01 16:36:18 -0700326 const Typeface* typeface = paint->getAndroidTypeface();
Seigo Nonakaae1aa852016-06-09 19:42:51 +0900327 minikin::MinikinPaint minikinPaint;
Seigo Nonaka318ca042017-08-01 16:36:18 -0700328 const Typeface* resolvedTypeface = Typeface::resolveDefault(typeface);
Seigo Nonakac7064142017-02-10 16:53:31 +0900329 minikin::FontStyle style = MinikinUtils::prepareMinikinPaint(&minikinPaint, paint,
Seigo Nonakaae1aa852016-06-09 19:42:51 +0900330 typeface);
Seigo Nonaka5d518dc2017-08-31 14:01:54 -0700331
Seigo Nonakab90e08f2017-10-20 15:23:51 -0700332 builder->addStyleRun(
333 start, end, std::move(minikinPaint), resolvedTypeface->fFontCollection, std::move(style),
334 isRtl);
Raph Levien70616ec2015-03-04 10:41:30 -0800335}
336
Seigo Nonakab90e08f2017-10-20 15:23:51 -0700337// CriticalNative
338static void nAddReplacementRun(jlong nativePtr, jlong nativePaint, jint start, jint end,
339 jfloat width) {
340 StaticLayoutNative* builder = toNative(nativePtr);
Seigo Nonakaf90c9b62017-10-09 11:10:24 -0700341 Paint* paint = reinterpret_cast<Paint*>(nativePaint);
Seigo Nonakab90e08f2017-10-20 15:23:51 -0700342 builder->addReplacementRun(start, end, width, paint->getMinikinLangListId());
Raph Levien70616ec2015-03-04 10:41:30 -0800343}
344
Daniel Micay76f6a862015-09-19 17:31:01 -0400345static const JNINativeMethod gMethods[] = {
Seigo Nonakab90e08f2017-10-20 15:23:51 -0700346 // Fast Natives
347 {"nInit", "("
348 "I" // breakStrategy
349 "I" // hyphenationFrequency
350 "Z" // isJustified
351 "[I" // indents
352 "[I" // left paddings
353 "[I" // right paddings
354 ")J", (void*) nInit},
355
356 // Critical Natives
357 {"nFinish", "(J)V", (void*) nFinish},
Seigo Nonakaf90c9b62017-10-09 11:10:24 -0700358 {"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun},
359 {"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun},
Seigo Nonakab90e08f2017-10-20 15:23:51 -0700360
361 // Regular JNI
362 {"nComputeLineBreaks", "("
363 "J" // nativePtr
364
365 // Inputs
366 "[C" // text
367 "I" // length
368 "F" // firstWidth
369 "I" // firstWidthLineCount
370 "F" // restWidth
371 "[I" // variableTabStops
372 "I" // defaultTabStop
373 "I" // indentsOffset
374
375 // Outputs
376 "Landroid/text/StaticLayout$LineBreaks;" // recycle
377 "I" // recycleLength
378 "[I" // recycleBreaks
379 "[F" // recycleWidths
380 "[F" // recycleAscents
381 "[F" // recycleDescents
382 "[I" // recycleFlags
383 "[F" // charWidths
384 ")I", (void*) nComputeLineBreaks}
Anish Athalye88b5b0b2014-06-24 14:39:43 -0700385};
386
387int register_android_text_StaticLayout(JNIEnv* env)
388{
Andreas Gampe158b6c92014-11-21 14:03:34 -0800389 gLineBreaks_class = MakeGlobalRefOrDie(env,
390 FindClassOrDie(env, "android/text/StaticLayout$LineBreaks"));
Anish Athalyec8f9e622014-07-21 15:26:34 -0700391
Andreas Gampe158b6c92014-11-21 14:03:34 -0800392 gLineBreaks_fieldID.breaks = GetFieldIDOrDie(env, gLineBreaks_class, "breaks", "[I");
393 gLineBreaks_fieldID.widths = GetFieldIDOrDie(env, gLineBreaks_class, "widths", "[F");
Roozbeh Pournader737dfea2017-08-10 11:32:24 -0700394 gLineBreaks_fieldID.ascents = GetFieldIDOrDie(env, gLineBreaks_class, "ascents", "[F");
395 gLineBreaks_fieldID.descents = GetFieldIDOrDie(env, gLineBreaks_class, "descents", "[F");
Raph Levien26d443a2015-03-30 14:18:32 -0700396 gLineBreaks_fieldID.flags = GetFieldIDOrDie(env, gLineBreaks_class, "flags", "[I");
Anish Athalyec8f9e622014-07-21 15:26:34 -0700397
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800398 return RegisterMethodsOrDie(env, "android/text/StaticLayout", gMethods, NELEM(gMethods));
Anish Athalye88b5b0b2014-06-24 14:39:43 -0700399}
400
401}