blob: 24baba4512c1cf10eaf31468a5a80213cc8c369d [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
Anish Athalye88b5b0b2014-06-24 14:39:43 -070019#include "utils/misc.h"
20#include "utils/Log.h"
Steven Moreland2279b252017-07-19 09:50:45 -070021#include <nativehelper/ScopedStringChars.h>
22#include <nativehelper/ScopedPrimitiveArray.h>
23#include <nativehelper/JNIHelp.h>
Andreas Gampeed6b9df2014-11-20 22:02:20 -080024#include "core_jni_helpers.h"
Seigo Nonakabeb16402017-11-22 12:07:16 -080025#include "scoped_nullable_primitive_array.h"
Anish Athalyec8f9e622014-07-21 15:26:34 -070026#include <cstdint>
Anish Athalye88b5b0b2014-06-24 14:39:43 -070027#include <vector>
Anish Athalyec8f9e622014-07-21 15:26:34 -070028#include <list>
29#include <algorithm>
Anish Athalye88b5b0b2014-06-24 14:39:43 -070030
Raph Levien70616ec2015-03-04 10:41:30 -080031#include "SkPaint.h"
32#include "SkTypeface.h"
sergeyvdccca442016-03-21 15:38:21 -070033#include <hwui/MinikinSkia.h>
34#include <hwui/MinikinUtils.h>
35#include <hwui/Paint.h>
36#include <minikin/FontCollection.h>
Seigo Nonaka3e7deec2017-11-15 12:06:24 -080037#include <minikin/AndroidLineBreakerHelper.h>
sergeyvdccca442016-03-21 15:38:21 -070038#include <minikin/MinikinFont.h>
Raph Levien4c1f12e2015-03-02 16:29:23 -080039
Anish Athalye88b5b0b2014-06-24 14:39:43 -070040namespace android {
41
Anish Athalyec8f9e622014-07-21 15:26:34 -070042struct JLineBreaksID {
43 jfieldID breaks;
44 jfieldID widths;
Roozbeh Pournader737dfea2017-08-10 11:32:24 -070045 jfieldID ascents;
46 jfieldID descents;
Anish Athalyec8f9e622014-07-21 15:26:34 -070047 jfieldID flags;
48};
49
50static jclass gLineBreaks_class;
51static JLineBreaksID gLineBreaks_fieldID;
52
Roozbeh Pournader0bba7422017-09-28 12:35:10 -070053static inline std::vector<float> jintArrayToFloatVector(JNIEnv* env, jintArray javaArray) {
54 if (javaArray == nullptr) {
55 return std::vector<float>();
56 } else {
57 ScopedIntArrayRO intArr(env, javaArray);
58 return std::vector<float>(intArr.get(), intArr.get() + intArr.size());
59 }
60}
61
Seigo Nonaka3e7deec2017-11-15 12:06:24 -080062static inline minikin::android::StaticLayoutNative* toNative(jlong ptr) {
63 return reinterpret_cast<minikin::android::StaticLayoutNative*>(ptr);
Seigo Nonakab90e08f2017-10-20 15:23:51 -070064}
65
Roozbeh Pournader95c7a132015-05-12 12:01:06 -070066// set text and set a number of parameters for creating a layout (width, tabstops, strategy,
67// hyphenFrequency)
Seigo Nonakab90e08f2017-10-20 15:23:51 -070068static jlong nInit(JNIEnv* env, jclass /* unused */,
69 jint breakStrategy, jint hyphenationFrequency, jboolean isJustified,
70 jintArray indents, jintArray leftPaddings, jintArray rightPaddings) {
Seigo Nonaka3e7deec2017-11-15 12:06:24 -080071 return reinterpret_cast<jlong>(new minikin::android::StaticLayoutNative(
Seigo Nonakab90e08f2017-10-20 15:23:51 -070072 static_cast<minikin::BreakStrategy>(breakStrategy),
73 static_cast<minikin::HyphenationFrequency>(hyphenationFrequency),
74 isJustified,
75 jintArrayToFloatVector(env, indents),
76 jintArrayToFloatVector(env, leftPaddings),
77 jintArrayToFloatVector(env, rightPaddings)));
78}
Seigo Nonakabafe1972017-08-24 15:30:29 -070079
Seigo Nonakab90e08f2017-10-20 15:23:51 -070080// CriticalNative
81static void nFinish(jlong nativePtr) {
82 delete toNative(nativePtr);
Raph Levienc94f7422015-03-06 19:19:48 -080083}
Anish Athalyec8f9e622014-07-21 15:26:34 -070084
Raph Levienc94f7422015-03-06 19:19:48 -080085static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
Roozbeh Pournader737dfea2017-08-10 11:32:24 -070086 jfloatArray recycleWidths, jfloatArray recycleAscents,
87 jfloatArray recycleDescents, jintArray recycleFlags,
Seigo Nonakabeb16402017-11-22 12:07:16 -080088 jint recycleLength, const minikin::LineBreakResult& result) {
89 const size_t nBreaks = result.breakPoints.size();
Raph Levienc94f7422015-03-06 19:19:48 -080090 if ((size_t)recycleLength < nBreaks) {
Anish Athalyec8f9e622014-07-21 15:26:34 -070091 // have to reallocate buffers
Raph Levienc94f7422015-03-06 19:19:48 -080092 recycleBreaks = env->NewIntArray(nBreaks);
93 recycleWidths = env->NewFloatArray(nBreaks);
Roozbeh Pournader737dfea2017-08-10 11:32:24 -070094 recycleAscents = env->NewFloatArray(nBreaks);
95 recycleDescents = env->NewFloatArray(nBreaks);
Raph Levien26d443a2015-03-30 14:18:32 -070096 recycleFlags = env->NewIntArray(nBreaks);
Anish Athalyec8f9e622014-07-21 15:26:34 -070097
98 env->SetObjectField(recycle, gLineBreaks_fieldID.breaks, recycleBreaks);
99 env->SetObjectField(recycle, gLineBreaks_fieldID.widths, recycleWidths);
Roozbeh Pournader737dfea2017-08-10 11:32:24 -0700100 env->SetObjectField(recycle, gLineBreaks_fieldID.ascents, recycleAscents);
101 env->SetObjectField(recycle, gLineBreaks_fieldID.descents, recycleDescents);
Anish Athalyec8f9e622014-07-21 15:26:34 -0700102 env->SetObjectField(recycle, gLineBreaks_fieldID.flags, recycleFlags);
103 }
104 // copy data
Seigo Nonakabeb16402017-11-22 12:07:16 -0800105 env->SetIntArrayRegion(recycleBreaks, 0, nBreaks, result.breakPoints.data());
106 env->SetFloatArrayRegion(recycleWidths, 0, nBreaks, result.widths.data());
107 env->SetFloatArrayRegion(recycleAscents, 0, nBreaks, result.ascents.data());
108 env->SetFloatArrayRegion(recycleDescents, 0, nBreaks, result.descents.data());
109 env->SetIntArrayRegion(recycleFlags, 0, nBreaks, result.flags.data());
Raph Levien70616ec2015-03-04 10:41:30 -0800110}
111
Raph Levien4c1f12e2015-03-02 16:29:23 -0800112static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
Seigo Nonakab90e08f2017-10-20 15:23:51 -0700113 // Inputs
Seigo Nonakaef94f492017-11-10 16:11:33 -0800114 jcharArray javaText,
Seigo Nonaka6f1868b2017-12-01 16:24:19 -0800115 jlong measuredTextPtr,
Seigo Nonakab90e08f2017-10-20 15:23:51 -0700116 jint length,
117 jfloat firstWidth,
118 jint firstWidthLineCount,
119 jfloat restWidth,
120 jintArray variableTabStops,
121 jint defaultTabStop,
122 jint indentsOffset,
Raph Levien4c1f12e2015-03-02 16:29:23 -0800123
Seigo Nonakab90e08f2017-10-20 15:23:51 -0700124 // Outputs
125 jobject recycle,
126 jint recycleLength,
127 jintArray recycleBreaks,
128 jfloatArray recycleWidths,
129 jfloatArray recycleAscents,
130 jfloatArray recycleDescents,
131 jintArray recycleFlags,
132 jfloatArray charWidths) {
133
Seigo Nonaka3e7deec2017-11-15 12:06:24 -0800134 minikin::android::StaticLayoutNative* builder = toNative(nativePtr);
Seigo Nonakab90e08f2017-10-20 15:23:51 -0700135
Seigo Nonakaef94f492017-11-10 16:11:33 -0800136 ScopedCharArrayRO text(env, javaText);
Seigo Nonakabeb16402017-11-22 12:07:16 -0800137 ScopedNullableIntArrayRO tabStops(env, variableTabStops);
Seigo Nonakaef94f492017-11-10 16:11:33 -0800138
Seigo Nonakabeb16402017-11-22 12:07:16 -0800139 minikin::U16StringPiece u16Text(text.get(), length);
Seigo Nonaka6f1868b2017-12-01 16:24:19 -0800140 minikin::MeasuredText* measuredText = reinterpret_cast<minikin::MeasuredText*>(measuredTextPtr);
Seigo Nonakabeb16402017-11-22 12:07:16 -0800141 minikin::LineBreakResult result = builder->computeBreaks(
Seigo Nonaka6f1868b2017-12-01 16:24:19 -0800142 u16Text, *measuredText, firstWidth, firstWidthLineCount, restWidth, indentsOffset,
Seigo Nonakabeb16402017-11-22 12:07:16 -0800143 tabStops.get(), tabStops.size(), defaultTabStop);
Anish Athalye88b5b0b2014-06-24 14:39:43 -0700144
Roozbeh Pournader737dfea2017-08-10 11:32:24 -0700145 recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleAscents, recycleDescents,
Seigo Nonakabeb16402017-11-22 12:07:16 -0800146 recycleFlags, recycleLength, result);
Anish Athalye88b5b0b2014-06-24 14:39:43 -0700147
Seigo Nonaka6f1868b2017-12-01 16:24:19 -0800148 env->SetFloatArrayRegion(charWidths, 0, measuredText->widths.size(),
149 measuredText->widths.data());
Anish Athalye88b5b0b2014-06-24 14:39:43 -0700150
Seigo Nonakabeb16402017-11-22 12:07:16 -0800151 return static_cast<jint>(result.breakPoints.size());
Anish Athalye88b5b0b2014-06-24 14:39:43 -0700152}
153
Daniel Micay76f6a862015-09-19 17:31:01 -0400154static const JNINativeMethod gMethods[] = {
Seigo Nonakab90e08f2017-10-20 15:23:51 -0700155 // Fast Natives
156 {"nInit", "("
157 "I" // breakStrategy
158 "I" // hyphenationFrequency
159 "Z" // isJustified
160 "[I" // indents
161 "[I" // left paddings
162 "[I" // right paddings
163 ")J", (void*) nInit},
164
165 // Critical Natives
166 {"nFinish", "(J)V", (void*) nFinish},
Seigo Nonakab90e08f2017-10-20 15:23:51 -0700167
168 // Regular JNI
169 {"nComputeLineBreaks", "("
170 "J" // nativePtr
171
172 // Inputs
173 "[C" // text
Seigo Nonaka9d3bd082018-01-11 10:02:12 -0800174 "J" // MeasuredParagraph ptr.
Seigo Nonakab90e08f2017-10-20 15:23:51 -0700175 "I" // length
176 "F" // firstWidth
177 "I" // firstWidthLineCount
178 "F" // restWidth
179 "[I" // variableTabStops
180 "I" // defaultTabStop
181 "I" // indentsOffset
182
183 // Outputs
184 "Landroid/text/StaticLayout$LineBreaks;" // recycle
185 "I" // recycleLength
186 "[I" // recycleBreaks
187 "[F" // recycleWidths
188 "[F" // recycleAscents
189 "[F" // recycleDescents
190 "[I" // recycleFlags
191 "[F" // charWidths
192 ")I", (void*) nComputeLineBreaks}
Anish Athalye88b5b0b2014-06-24 14:39:43 -0700193};
194
195int register_android_text_StaticLayout(JNIEnv* env)
196{
Andreas Gampe158b6c92014-11-21 14:03:34 -0800197 gLineBreaks_class = MakeGlobalRefOrDie(env,
198 FindClassOrDie(env, "android/text/StaticLayout$LineBreaks"));
Anish Athalyec8f9e622014-07-21 15:26:34 -0700199
Andreas Gampe158b6c92014-11-21 14:03:34 -0800200 gLineBreaks_fieldID.breaks = GetFieldIDOrDie(env, gLineBreaks_class, "breaks", "[I");
201 gLineBreaks_fieldID.widths = GetFieldIDOrDie(env, gLineBreaks_class, "widths", "[F");
Roozbeh Pournader737dfea2017-08-10 11:32:24 -0700202 gLineBreaks_fieldID.ascents = GetFieldIDOrDie(env, gLineBreaks_class, "ascents", "[F");
203 gLineBreaks_fieldID.descents = GetFieldIDOrDie(env, gLineBreaks_class, "descents", "[F");
Raph Levien26d443a2015-03-30 14:18:32 -0700204 gLineBreaks_fieldID.flags = GetFieldIDOrDie(env, gLineBreaks_class, "flags", "[I");
Anish Athalyec8f9e622014-07-21 15:26:34 -0700205
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800206 return RegisterMethodsOrDie(env, "android/text/StaticLayout", gMethods, NELEM(gMethods));
Anish Athalye88b5b0b2014-06-24 14:39:43 -0700207}
208
209}