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