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 #undef LOG_TAG
18 #define LOG_TAG "LineBreaker"
19 
20 #include "utils/misc.h"
21 #include "utils/Log.h"
22 #include "graphics_jni_helpers.h"
23 #include <nativehelper/ScopedStringChars.h>
24 #include <nativehelper/ScopedPrimitiveArray.h>
25 #include "scoped_nullable_primitive_array.h"
26 #include <cstdint>
27 #include <vector>
28 #include <list>
29 #include <algorithm>
30 
31 #include "SkPaint.h"
32 #include "SkTypeface.h"
33 #include <hwui/MinikinSkia.h>
34 #include <hwui/MinikinUtils.h>
35 #include <hwui/Paint.h>
36 #include <minikin/FontCollection.h>
37 #include <minikin/AndroidLineBreakerHelper.h>
38 #include <minikin/MinikinFont.h>
39 
40 namespace android {
41 
jintArrayToFloatVector(JNIEnv * env,jintArray javaArray)42 static inline std::vector<float> jintArrayToFloatVector(JNIEnv* env, jintArray javaArray) {
43     if (javaArray == nullptr) {
44          return std::vector<float>();
45     } else {
46         ScopedIntArrayRO intArr(env, javaArray);
47         return std::vector<float>(intArr.get(), intArr.get() + intArr.size());
48     }
49 }
50 
toNative(jlong ptr)51 static inline minikin::android::StaticLayoutNative* toNative(jlong ptr) {
52     return reinterpret_cast<minikin::android::StaticLayoutNative*>(ptr);
53 }
54 
55 // set text and set a number of parameters for creating a layout (width, tabstops, strategy,
56 // hyphenFrequency)
nInit(JNIEnv * env,jclass,jint breakStrategy,jint hyphenationFrequency,jboolean isJustified,jintArray indents)57 static jlong nInit(JNIEnv* env, jclass /* unused */,
58         jint breakStrategy, jint hyphenationFrequency, jboolean isJustified, jintArray indents) {
59     return reinterpret_cast<jlong>(new minikin::android::StaticLayoutNative(
60             static_cast<minikin::BreakStrategy>(breakStrategy),
61             static_cast<minikin::HyphenationFrequency>(hyphenationFrequency),
62             isJustified,
63             jintArrayToFloatVector(env, indents)));
64 }
65 
nFinish(jlong nativePtr)66 static void nFinish(jlong nativePtr) {
67     delete toNative(nativePtr);
68 }
69 
70 // CriticalNative
nGetReleaseFunc(CRITICAL_JNI_PARAMS)71 static jlong nGetReleaseFunc(CRITICAL_JNI_PARAMS) {
72     return reinterpret_cast<jlong>(nFinish);
73 }
74 
nComputeLineBreaks(JNIEnv * env,jclass,jlong nativePtr,jcharArray javaText,jlong measuredTextPtr,jint length,jfloat firstWidth,jint firstWidthLineCount,jfloat restWidth,jfloatArray variableTabStops,jfloat defaultTabStop,jint indentsOffset)75 static jlong nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
76         // Inputs
77         jcharArray javaText,
78         jlong measuredTextPtr,
79         jint length,
80         jfloat firstWidth,
81         jint firstWidthLineCount,
82         jfloat restWidth,
83         jfloatArray variableTabStops,
84         jfloat defaultTabStop,
85         jint indentsOffset) {
86     minikin::android::StaticLayoutNative* builder = toNative(nativePtr);
87 
88     ScopedCharArrayRO text(env, javaText);
89     ScopedNullableFloatArrayRO tabStops(env, variableTabStops);
90 
91     minikin::U16StringPiece u16Text(text.get(), length);
92     minikin::MeasuredText* measuredText = reinterpret_cast<minikin::MeasuredText*>(measuredTextPtr);
93 
94     std::unique_ptr<minikin::LineBreakResult> result =
95           std::make_unique<minikin::LineBreakResult>(builder->computeBreaks(
96                 u16Text, *measuredText, firstWidth, firstWidthLineCount, restWidth, indentsOffset,
97                 tabStops.get(), tabStops.size(), defaultTabStop));
98     return reinterpret_cast<jlong>(result.release());
99 }
100 
nGetLineCount(CRITICAL_JNI_PARAMS_COMMA jlong ptr)101 static jint nGetLineCount(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
102     return reinterpret_cast<minikin::LineBreakResult*>(ptr)->breakPoints.size();
103 }
104 
nGetLineBreakOffset(CRITICAL_JNI_PARAMS_COMMA jlong ptr,jint i)105 static jint nGetLineBreakOffset(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
106     return reinterpret_cast<minikin::LineBreakResult*>(ptr)->breakPoints[i];
107 }
108 
nGetLineWidth(CRITICAL_JNI_PARAMS_COMMA jlong ptr,jint i)109 static jfloat nGetLineWidth(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
110     return reinterpret_cast<minikin::LineBreakResult*>(ptr)->widths[i];
111 }
112 
nGetLineAscent(CRITICAL_JNI_PARAMS_COMMA jlong ptr,jint i)113 static jfloat nGetLineAscent(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
114     return reinterpret_cast<minikin::LineBreakResult*>(ptr)->ascents[i];
115 }
116 
nGetLineDescent(CRITICAL_JNI_PARAMS_COMMA jlong ptr,jint i)117 static jfloat nGetLineDescent(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
118     return reinterpret_cast<minikin::LineBreakResult*>(ptr)->descents[i];
119 }
120 
nGetLineFlag(CRITICAL_JNI_PARAMS_COMMA jlong ptr,jint i)121 static jint nGetLineFlag(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
122     return reinterpret_cast<minikin::LineBreakResult*>(ptr)->flags[i];
123 }
124 
nReleaseResult(jlong ptr)125 static void nReleaseResult(jlong ptr) {
126     delete reinterpret_cast<minikin::LineBreakResult*>(ptr);
127 }
128 
nGetReleaseResultFunc(CRITICAL_JNI_PARAMS)129 static jlong nGetReleaseResultFunc(CRITICAL_JNI_PARAMS) {
130     return reinterpret_cast<jlong>(nReleaseResult);
131 }
132 
133 static const JNINativeMethod gMethods[] = {
134     // Fast Natives
135     {"nInit", "("
136         "I"  // breakStrategy
137         "I"  // hyphenationFrequency
138         "Z"  // isJustified
139         "[I"  // indents
140         ")J", (void*) nInit},
141 
142     // Critical Natives
143     {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc},
144 
145     // Regular JNI
146     {"nComputeLineBreaks", "("
147         "J"  // nativePtr
148         "[C"  // text
149         "J"  // MeasuredParagraph ptr.
150         "I"  // length
151         "F"  // firstWidth
152         "I"  // firstWidthLineCount
153         "F"  // restWidth
154         "[F"  // variableTabStops
155         "F"  // defaultTabStop
156         "I"  // indentsOffset
157         ")J", (void*) nComputeLineBreaks},
158 
159     // Result accessors, CriticalNatives
160     {"nGetLineCount", "(J)I", (void*)nGetLineCount},
161     {"nGetLineBreakOffset", "(JI)I", (void*)nGetLineBreakOffset},
162     {"nGetLineWidth", "(JI)F", (void*)nGetLineWidth},
163     {"nGetLineAscent", "(JI)F", (void*)nGetLineAscent},
164     {"nGetLineDescent", "(JI)F", (void*)nGetLineDescent},
165     {"nGetLineFlag", "(JI)I", (void*)nGetLineFlag},
166     {"nGetReleaseResultFunc", "()J", (void*)nGetReleaseResultFunc},
167 };
168 
register_android_graphics_text_LineBreaker(JNIEnv * env)169 int register_android_graphics_text_LineBreaker(JNIEnv* env) {
170     return RegisterMethodsOrDie(env, "android/graphics/text/LineBreaker", gMethods,
171                                 NELEM(gMethods));
172 }
173 
174 }
175