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