1 /*
2 * Copyright 2022 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 "PathIterator.h"
18
19 #include <jni.h>
20
21 #include <android/api-level.h>
22
23 #include <cstdlib>
24 #include <new>
25
26 #define JNI_CLASS_NAME "androidx/graphics/path/PathIteratorPreApi34Impl"
27 #define JNI_CLASS_NAME_CONVERTER "androidx/graphics/path/ConicConverter"
28
29 struct {
30 jclass jniClass;
31 jfieldID nativePath;
32 } sPath{};
33
createPathIterator(JNIEnv * env,jobject,jobject path_,jint conicEvaluation_,jfloat tolerance_)34 static jlong createPathIterator(JNIEnv* env, jobject,
35 jobject path_, jint conicEvaluation_, jfloat tolerance_) {
36
37 auto nativePath = static_cast<intptr_t>(env->GetLongField(path_, sPath.nativePath));
38 auto* path = reinterpret_cast<Path*>(nativePath);
39
40 Point* points;
41 Verb* verbs;
42 float* conicWeights;
43 int count;
44 PathIterator::VerbDirection direction;
45
46 const uint32_t apiLevel = android_get_device_api_level();
47 if (apiLevel >= 30) {
48 auto* ref = reinterpret_cast<PathRef30*>(path->pathRef);
49 points = ref->points;
50 verbs = ref->verbs;
51 conicWeights = ref->conicWeights;
52 count = ref->verbCount;
53 direction = PathIterator::VerbDirection::Forward;
54 } else if (apiLevel >= 26) {
55 auto* ref = reinterpret_cast<PathRef26*>(path->pathRef);
56 points = ref->points;
57 verbs = ref->verbs;
58 conicWeights = ref->conicWeights;
59 count = ref->verbCount;
60 direction = PathIterator::VerbDirection::Backward;
61 } else if (apiLevel >= 24) {
62 auto* ref = reinterpret_cast<PathRef24*>(path->pathRef);
63 points = ref->points;
64 verbs = ref->verbs;
65 conicWeights = ref->conicWeights;
66 count = ref->verbCount;
67 direction = PathIterator::VerbDirection::Backward;
68 } else {
69 auto* ref = path->pathRef;
70 points = ref->points;
71 verbs = ref->verbs;
72 conicWeights = ref->conicWeights;
73 count = ref->verbCount;
74 direction = PathIterator::VerbDirection::Backward;
75 }
76
77 PathIterator* iterator = static_cast<PathIterator*>(malloc(sizeof(PathIterator)));
78 return jlong(new(iterator) PathIterator(
79 points, verbs, conicWeights, count, direction,
80 PathIterator::ConicEvaluation(conicEvaluation_), tolerance_
81 ));
82 }
83
destroyPathIterator(JNIEnv *,jobject,jlong pathIterator_)84 static void destroyPathIterator(JNIEnv*, jobject, jlong pathIterator_) {
85 PathIterator* iterator = reinterpret_cast<PathIterator*>(pathIterator_);
86 iterator->~PathIterator();
87 free(iterator);
88 }
89
pathIteratorHasNext(JNIEnv *,jobject,jlong pathIterator_)90 static jboolean pathIteratorHasNext(JNIEnv*, jobject, jlong pathIterator_) {
91 return reinterpret_cast<PathIterator*>(pathIterator_)->hasNext();
92 }
93
conicToQuadraticsWrapper(JNIEnv * env,jobject,jfloatArray conicPoints,jint offset,jfloatArray quadraticPoints,jfloat weight,jfloat tolerance)94 static jint conicToQuadraticsWrapper(JNIEnv* env, jobject,
95 jfloatArray conicPoints, jint offset,
96 jfloatArray quadraticPoints,
97 jfloat weight, jfloat tolerance) {
98 float *conicData = env->GetFloatArrayElements(conicPoints, JNI_FALSE);
99 float *quadData = env->GetFloatArrayElements(quadraticPoints, JNI_FALSE);
100
101 int count = conicToQuadratics(reinterpret_cast<const Point*>(conicData + offset),
102 reinterpret_cast<Point*>(quadData),
103 env->GetArrayLength(quadraticPoints),
104 weight, tolerance);
105
106 env->ReleaseFloatArrayElements(conicPoints, conicData, JNI_ABORT);
107 env->ReleaseFloatArrayElements(quadraticPoints, quadData, 0);
108
109 return count;
110 }
111
pathIteratorNext(JNIEnv * env,jobject,jlong pathIterator_,jfloatArray points_,jint offset_)112 static jint pathIteratorNext(JNIEnv* env, jobject,
113 jlong pathIterator_, jfloatArray points_, jint offset_) {
114 auto pathIterator = reinterpret_cast<PathIterator*>(pathIterator_);
115 Point pointsData[4];
116 Verb verb = pathIterator->next(pointsData);
117
118 if (verb != Verb::Done && verb != Verb::Close) {
119 auto* floatsData = reinterpret_cast<jfloat*>(pointsData);
120 env->SetFloatArrayRegion(points_, offset_, 8, floatsData);
121 }
122
123 return static_cast<jint>(verb);
124 }
125
pathIteratorPeek(JNIEnv *,jobject,jlong pathIterator_)126 static jint pathIteratorPeek(JNIEnv*, jobject, jlong pathIterator_) {
127 return static_cast<jint>(reinterpret_cast<PathIterator *>(pathIterator_)->peek());
128 }
129
pathIteratorRawSize(JNIEnv *,jobject,jlong pathIterator_)130 static jint pathIteratorRawSize(JNIEnv*, jobject, jlong pathIterator_) {
131 return static_cast<jint>(reinterpret_cast<PathIterator *>(pathIterator_)->rawCount());
132 }
133
pathIteratorSize(JNIEnv *,jobject,jlong pathIterator_)134 static jint pathIteratorSize(JNIEnv*, jobject, jlong pathIterator_) {
135 return static_cast<jint>(reinterpret_cast<PathIterator *>(pathIterator_)->count());
136 }
137
JNI_OnLoad(JavaVM * vm,void *)138 JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
139 JNIEnv* env;
140 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
141 return -1;
142 }
143
144 sPath.jniClass = env->FindClass("android/graphics/Path");
145 if (sPath.jniClass == nullptr) return JNI_ERR;
146
147 sPath.nativePath = env->GetFieldID(sPath.jniClass, "mNativePath", "J");
148 if (sPath.nativePath == nullptr) return JNI_ERR;
149
150 {
151 jclass pathsClass = env->FindClass(JNI_CLASS_NAME);
152 if (pathsClass == nullptr) return JNI_ERR;
153
154 jint result;
155
156 const uint32_t apiLevel = android_get_device_api_level();
157 if (apiLevel >= 26) { // Android 8.0
158 static const JNINativeMethod methods[] = {
159 {
160 (char *) "createInternalPathIterator",
161 (char *) "(Landroid/graphics/Path;IF)J",
162 reinterpret_cast<void *>(createPathIterator)
163 },
164 {
165 (char *) "destroyInternalPathIterator",
166 (char *) "(J)V",
167 reinterpret_cast<void *>(destroyPathIterator)
168 },
169 {
170 (char *) "internalPathIteratorHasNext",
171 (char *) "(J)Z",
172 reinterpret_cast<void *>(pathIteratorHasNext)
173 },
174 {
175 (char *) "internalPathIteratorNext",
176 (char *) "(J[FI)I",
177 reinterpret_cast<void *>(pathIteratorNext)
178 },
179 {
180 (char *) "internalPathIteratorPeek",
181 (char *) "(J)I",
182 reinterpret_cast<void *>(pathIteratorPeek)
183 },
184 {
185 (char *) "internalPathIteratorRawSize",
186 (char *) "(J)I",
187 reinterpret_cast<void *>(pathIteratorRawSize)
188 },
189 {
190 (char *) "internalPathIteratorSize",
191 (char *) "(J)I",
192 reinterpret_cast<void *>(pathIteratorSize)
193 },
194 };
195
196 result = env->RegisterNatives(
197 pathsClass, methods, sizeof(methods) / sizeof(JNINativeMethod)
198 );
199 } else {
200 // Before Android 8, rely on the !bang JNI notation to speed up our JNI calls
201 static const JNINativeMethod methods[] = {
202 {
203 (char *) "createInternalPathIterator",
204 (char *) "(Landroid/graphics/Path;IF)J",
205 reinterpret_cast<void *>(createPathIterator)
206 },
207 {
208 (char *) "destroyInternalPathIterator",
209 (char *) "(J)V",
210 reinterpret_cast<void *>(destroyPathIterator)
211 },
212 {
213 (char *) "internalPathIteratorHasNext",
214 (char *) "!(J)Z",
215 reinterpret_cast<void *>(pathIteratorHasNext)
216 },
217 {
218 (char *) "internalPathIteratorNext",
219 (char *) "!(J[FI)I",
220 reinterpret_cast<void *>(pathIteratorNext)
221 },
222 {
223 (char *) "internalPathIteratorPeek",
224 (char *) "!(J)I",
225 reinterpret_cast<void *>(pathIteratorPeek)
226 },
227 {
228 (char *) "internalPathIteratorRawSize",
229 (char *) "!(J)I",
230 reinterpret_cast<void *>(pathIteratorRawSize)
231 },
232 {
233 (char *) "internalPathIteratorSize",
234 (char *) "!(J)I",
235 reinterpret_cast<void *>(pathIteratorSize)
236 },
237 };
238
239 result = env->RegisterNatives(
240 pathsClass, methods, sizeof(methods) / sizeof(JNINativeMethod)
241 );
242 }
243 if (result != JNI_OK) return result;
244
245 env->DeleteLocalRef(pathsClass);
246
247 jclass converterClass = env->FindClass(JNI_CLASS_NAME_CONVERTER);
248 if (converterClass == nullptr) return JNI_ERR;
249 static const JNINativeMethod methods2[] = {
250 {
251 (char*) "internalConicToQuadratics",
252 (char*) "([FI[FFF)I",
253 reinterpret_cast<void*>(conicToQuadraticsWrapper)
254 },
255 };
256
257 result = env->RegisterNatives(
258 converterClass, methods2, sizeof(methods2) / sizeof(JNINativeMethod)
259 );
260 if (result != JNI_OK) return result;
261
262 env->DeleteLocalRef(converterClass);
263 }
264
265 return JNI_VERSION_1_6;
266 }
267