blob: 04e9dfd2706ff6f79c52a522b66a536f411c6a65 [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,
Roozbeh Pournader0bba7422017-09-28 12:35:10 -070058 std::vector<float>&& indents, std::vector<float>&& leftPaddings,
59 std::vector<float>&& rightPaddings, int32_t indentsAndPaddingsOffset)
Seigo Nonaka749e57e2017-08-29 15:00:05 -070060 : mFirstWidth(firstWidth), mFirstLineCount(firstLineCount), mRestWidth(restWidth),
Roozbeh Pournader0bba7422017-09-28 12:35:10 -070061 mIndents(std::move(indents)), mLeftPaddings(std::move(leftPaddings)),
62 mRightPaddings(std::move(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;
94 const std::vector<float> mIndents;
Roozbeh Pournader0bba7422017-09-28 12:35:10 -070095 const std::vector<float> mLeftPaddings;
96 const std::vector<float> mRightPaddings;
97 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
Roozbeh Pournader95c7a132015-05-12 12:01:06 -0700109// set text and set a number of parameters for creating a layout (width, tabstops, strategy,
110// hyphenFrequency)
Raph Levienc94f7422015-03-06 19:19:48 -0800111static void nSetupParagraph(JNIEnv* env, jclass, jlong nativePtr, jcharArray text, jint length,
112 jfloat firstWidth, jint firstWidthLineLimit, jfloat restWidth,
Seigo Nonaka09da71a2016-11-28 16:24:14 +0900113 jintArray variableTabStops, jint defaultTabStop, jint strategy, jint hyphenFrequency,
Roozbeh Pournader9a9179d2017-08-29 14:14:28 -0700114 jboolean isJustified, jintArray indents, jintArray leftPaddings, jintArray rightPaddings,
Roozbeh Pournader0bba7422017-09-28 12:35:10 -0700115 jint indentsAndPaddingsOffset) {
Seigo Nonakaae1aa852016-06-09 19:42:51 +0900116 minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
Raph Levienc94f7422015-03-06 19:19:48 -0800117 b->resize(length);
118 env->GetCharArrayRegion(text, 0, length, b->buffer());
119 b->setText();
Raph Levienc94f7422015-03-06 19:19:48 -0800120 if (variableTabStops == nullptr) {
121 b->setTabStops(nullptr, 0, defaultTabStop);
122 } else {
123 ScopedIntArrayRO stops(env, variableTabStops);
124 b->setTabStops(stops.get(), stops.size(), defaultTabStop);
125 }
Seigo Nonakaae1aa852016-06-09 19:42:51 +0900126 b->setStrategy(static_cast<minikin::BreakStrategy>(strategy));
127 b->setHyphenationFrequency(static_cast<minikin::HyphenationFrequency>(hyphenFrequency));
Seigo Nonaka09da71a2016-11-28 16:24:14 +0900128 b->setJustified(isJustified);
Seigo Nonakabafe1972017-08-24 15:30:29 -0700129
Roozbeh Pournader0bba7422017-09-28 12:35:10 -0700130 // TODO: copy indents and paddings only once when LineBreaker is started to be used.
Seigo Nonaka749e57e2017-08-29 15:00:05 -0700131 b->setLineWidthDelegate(std::make_unique<JNILineBreakerLineWidth>(
Roozbeh Pournader0bba7422017-09-28 12:35:10 -0700132 firstWidth, firstWidthLineLimit, restWidth, jintArrayToFloatVector(env, indents),
133 jintArrayToFloatVector(env, leftPaddings), jintArrayToFloatVector(env, rightPaddings),
134 indentsAndPaddingsOffset));
Raph Levienc94f7422015-03-06 19:19:48 -0800135}
Anish Athalyec8f9e622014-07-21 15:26:34 -0700136
Raph Levienc94f7422015-03-06 19:19:48 -0800137static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
Roozbeh Pournader737dfea2017-08-10 11:32:24 -0700138 jfloatArray recycleWidths, jfloatArray recycleAscents,
139 jfloatArray recycleDescents, jintArray recycleFlags,
Raph Levienc94f7422015-03-06 19:19:48 -0800140 jint recycleLength, size_t nBreaks, const jint* breaks,
Roozbeh Pournader737dfea2017-08-10 11:32:24 -0700141 const jfloat* widths, const jfloat* ascents, const jfloat* descents,
142 const jint* flags) {
Raph Levienc94f7422015-03-06 19:19:48 -0800143 if ((size_t)recycleLength < nBreaks) {
Anish Athalyec8f9e622014-07-21 15:26:34 -0700144 // have to reallocate buffers
Raph Levienc94f7422015-03-06 19:19:48 -0800145 recycleBreaks = env->NewIntArray(nBreaks);
146 recycleWidths = env->NewFloatArray(nBreaks);
Roozbeh Pournader737dfea2017-08-10 11:32:24 -0700147 recycleAscents = env->NewFloatArray(nBreaks);
148 recycleDescents = env->NewFloatArray(nBreaks);
Raph Levien26d443a2015-03-30 14:18:32 -0700149 recycleFlags = env->NewIntArray(nBreaks);
Anish Athalyec8f9e622014-07-21 15:26:34 -0700150
151 env->SetObjectField(recycle, gLineBreaks_fieldID.breaks, recycleBreaks);
152 env->SetObjectField(recycle, gLineBreaks_fieldID.widths, recycleWidths);
Roozbeh Pournader737dfea2017-08-10 11:32:24 -0700153 env->SetObjectField(recycle, gLineBreaks_fieldID.ascents, recycleAscents);
154 env->SetObjectField(recycle, gLineBreaks_fieldID.descents, recycleDescents);
Anish Athalyec8f9e622014-07-21 15:26:34 -0700155 env->SetObjectField(recycle, gLineBreaks_fieldID.flags, recycleFlags);
156 }
157 // copy data
Raph Levienc94f7422015-03-06 19:19:48 -0800158 env->SetIntArrayRegion(recycleBreaks, 0, nBreaks, breaks);
159 env->SetFloatArrayRegion(recycleWidths, 0, nBreaks, widths);
Roozbeh Pournader737dfea2017-08-10 11:32:24 -0700160 env->SetFloatArrayRegion(recycleAscents, 0, nBreaks, ascents);
161 env->SetFloatArrayRegion(recycleDescents, 0, nBreaks, descents);
Raph Levien26d443a2015-03-30 14:18:32 -0700162 env->SetIntArrayRegion(recycleFlags, 0, nBreaks, flags);
Raph Levien70616ec2015-03-04 10:41:30 -0800163}
164
Raph Levien4c1f12e2015-03-02 16:29:23 -0800165static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
Anish Athalyec8f9e622014-07-21 15:26:34 -0700166 jobject recycle, jintArray recycleBreaks,
Roozbeh Pournader737dfea2017-08-10 11:32:24 -0700167 jfloatArray recycleWidths, jfloatArray recycleAscents,
168 jfloatArray recycleDescents, jintArray recycleFlags,
Seigo Nonaka2dd1a552017-10-06 11:41:00 -0700169 jint recycleLength, jfloatArray charWidths) {
Seigo Nonakaae1aa852016-06-09 19:42:51 +0900170 minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
Raph Levien4c1f12e2015-03-02 16:29:23 -0800171
Raph Levienc94f7422015-03-06 19:19:48 -0800172 size_t nBreaks = b->computeBreaks();
Anish Athalye88b5b0b2014-06-24 14:39:43 -0700173
Roozbeh Pournader737dfea2017-08-10 11:32:24 -0700174 recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleAscents, recycleDescents,
175 recycleFlags, recycleLength, nBreaks, b->getBreaks(), b->getWidths(), b->getAscents(),
176 b->getDescents(), b->getFlags());
Anish Athalye88b5b0b2014-06-24 14:39:43 -0700177
Seigo Nonaka2dd1a552017-10-06 11:41:00 -0700178 env->SetFloatArrayRegion(charWidths, 0, b->size(), b->charWidths());
179
Raph Levien4c1f12e2015-03-02 16:29:23 -0800180 b->finish();
Anish Athalye88b5b0b2014-06-24 14:39:43 -0700181
Raph Levienc94f7422015-03-06 19:19:48 -0800182 return static_cast<jint>(nBreaks);
Anish Athalye88b5b0b2014-06-24 14:39:43 -0700183}
184
Raph Levien4c1f12e2015-03-02 16:29:23 -0800185static jlong nNewBuilder(JNIEnv*, jclass) {
Seigo Nonakaae1aa852016-06-09 19:42:51 +0900186 return reinterpret_cast<jlong>(new minikin::LineBreaker);
Raph Levien4c1f12e2015-03-02 16:29:23 -0800187}
188
189static void nFreeBuilder(JNIEnv*, jclass, jlong nativePtr) {
Seigo Nonakaae1aa852016-06-09 19:42:51 +0900190 delete reinterpret_cast<minikin::LineBreaker*>(nativePtr);
Raph Levien4c1f12e2015-03-02 16:29:23 -0800191}
192
193static void nFinishBuilder(JNIEnv*, jclass, jlong nativePtr) {
Seigo Nonakaae1aa852016-06-09 19:42:51 +0900194 minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
Raph Levien4c1f12e2015-03-02 16:29:23 -0800195 b->finish();
196}
197
Raph Levien70616ec2015-03-04 10:41:30 -0800198// Basically similar to Paint.getTextRunAdvances but with C++ interface
Seigo Nonaka5cc93482017-10-02 16:01:14 -0700199static void nAddStyleRun(JNIEnv* env, jclass, jlong nativePtr, jlong nativePaint, jint start,
Seigo Nonakaf90c9b62017-10-09 11:10:24 -0700200 jint end, jboolean isRtl) {
Seigo Nonakaae1aa852016-06-09 19:42:51 +0900201 minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
Raph Levien70616ec2015-03-04 10:41:30 -0800202 Paint* paint = reinterpret_cast<Paint*>(nativePaint);
Seigo Nonaka318ca042017-08-01 16:36:18 -0700203 const Typeface* typeface = paint->getAndroidTypeface();
Seigo Nonakaae1aa852016-06-09 19:42:51 +0900204 minikin::MinikinPaint minikinPaint;
Seigo Nonaka318ca042017-08-01 16:36:18 -0700205 const Typeface* resolvedTypeface = Typeface::resolveDefault(typeface);
Seigo Nonakac7064142017-02-10 16:53:31 +0900206 minikin::FontStyle style = MinikinUtils::prepareMinikinPaint(&minikinPaint, paint,
Seigo Nonakaae1aa852016-06-09 19:42:51 +0900207 typeface);
Seigo Nonaka5d518dc2017-08-31 14:01:54 -0700208
Seigo Nonakaf90c9b62017-10-09 11:10:24 -0700209 b->addStyleRun(&minikinPaint, resolvedTypeface->fFontCollection, style, start, end, isRtl);
Raph Levien70616ec2015-03-04 10:41:30 -0800210}
211
Seigo Nonakaf90c9b62017-10-09 11:10:24 -0700212static void nAddReplacementRun(JNIEnv* env, jclass, jlong nativePtr, jlong nativePaint,
213 jint start, jint end, jfloat width) {
Seigo Nonakaae1aa852016-06-09 19:42:51 +0900214 minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
Seigo Nonakaf90c9b62017-10-09 11:10:24 -0700215 Paint* paint = reinterpret_cast<Paint*>(nativePaint);
216 b->addReplacement(start, end, width, paint->getMinikinLangListId());
Raph Levien70616ec2015-03-04 10:41:30 -0800217}
218
Daniel Micay76f6a862015-09-19 17:31:01 -0400219static const JNINativeMethod gMethods[] = {
Raph Levien70616ec2015-03-04 10:41:30 -0800220 // TODO performance: many of these are candidates for fast jni, awaiting guidance
Raph Levien4c1f12e2015-03-02 16:29:23 -0800221 {"nNewBuilder", "()J", (void*) nNewBuilder},
222 {"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
223 {"nFinishBuilder", "(J)V", (void*) nFinishBuilder},
Roozbeh Pournader9a9179d2017-08-29 14:14:28 -0700224 {"nSetupParagraph", "(J[CIFIF[IIIIZ[I[I[II)V", (void*) nSetupParagraph},
Seigo Nonakaf90c9b62017-10-09 11:10:24 -0700225 {"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun},
226 {"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun},
Seigo Nonaka2dd1a552017-10-06 11:41:00 -0700227 {"nComputeLineBreaks", "(JLandroid/text/StaticLayout$LineBreaks;[I[F[F[F[II[F)I",
Raph Levien4c1f12e2015-03-02 16:29:23 -0800228 (void*) nComputeLineBreaks}
Anish Athalye88b5b0b2014-06-24 14:39:43 -0700229};
230
231int register_android_text_StaticLayout(JNIEnv* env)
232{
Andreas Gampe158b6c92014-11-21 14:03:34 -0800233 gLineBreaks_class = MakeGlobalRefOrDie(env,
234 FindClassOrDie(env, "android/text/StaticLayout$LineBreaks"));
Anish Athalyec8f9e622014-07-21 15:26:34 -0700235
Andreas Gampe158b6c92014-11-21 14:03:34 -0800236 gLineBreaks_fieldID.breaks = GetFieldIDOrDie(env, gLineBreaks_class, "breaks", "[I");
237 gLineBreaks_fieldID.widths = GetFieldIDOrDie(env, gLineBreaks_class, "widths", "[F");
Roozbeh Pournader737dfea2017-08-10 11:32:24 -0700238 gLineBreaks_fieldID.ascents = GetFieldIDOrDie(env, gLineBreaks_class, "ascents", "[F");
239 gLineBreaks_fieldID.descents = GetFieldIDOrDie(env, gLineBreaks_class, "descents", "[F");
Raph Levien26d443a2015-03-30 14:18:32 -0700240 gLineBreaks_fieldID.flags = GetFieldIDOrDie(env, gLineBreaks_class, "flags", "[I");
Anish Athalyec8f9e622014-07-21 15:26:34 -0700241
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800242 return RegisterMethodsOrDie(env, "android/text/StaticLayout", gMethods, NELEM(gMethods));
Anish Athalye88b5b0b2014-06-24 14:39:43 -0700243}
244
245}