blob: 9997387d980566f931ac4db054540e876cba5e09 [file] [log] [blame]
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "PathIterator.h"
#include <jni.h>
#include <sys/system_properties.h>
#include <mutex>
#define JNI_CLASS_NAME "androidx/graphics/path/PathIteratorPreApi34Impl"
#define JNI_CLASS_NAME_CONVERTER "androidx/graphics/path/ConicConverter"
#if !defined(NDEBUG)
#include <android/log.h>
#define ANDROID_LOG_TAG "PathIterator"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, ANDROID_LOG_TAG, __VA_ARGS__)
#endif
struct {
jclass jniClass;
jfieldID nativePath;
} sPath{};
uint32_t sApiLevel = 0;
std::once_flag sApiLevelOnceFlag;
static uint32_t api_level() {
std::call_once(sApiLevelOnceFlag, []() {
char sdkVersion[PROP_VALUE_MAX];
__system_property_get("ro.build.version.sdk", sdkVersion);
sApiLevel = atoi(sdkVersion); // NOLINT(cert-err34-c)
});
return sApiLevel;
}
static jlong createPathIterator(JNIEnv* env, jobject,
jobject path_, jint conicEvaluation_, jfloat tolerance_) {
auto nativePath = static_cast<intptr_t>(env->GetLongField(path_, sPath.nativePath));
auto* path = reinterpret_cast<Path*>(nativePath);
Point* points;
Verb* verbs;
float* conicWeights;
int count;
PathIterator::VerbDirection direction;
const uint32_t apiLevel = api_level();
if (apiLevel >= 30) {
auto* ref = reinterpret_cast<PathRef30*>(path->pathRef);
points = ref->points;
verbs = ref->verbs;
conicWeights = ref->conicWeights;
count = ref->verbCount;
direction = PathIterator::VerbDirection::Forward;
} else if (apiLevel >= 26) {
auto* ref = reinterpret_cast<PathRef26*>(path->pathRef);
points = ref->points;
verbs = ref->verbs;
conicWeights = ref->conicWeights;
count = ref->verbCount;
direction = PathIterator::VerbDirection::Backward;
} else if (apiLevel >= 24) {
auto* ref = reinterpret_cast<PathRef24*>(path->pathRef);
points = ref->points;
verbs = ref->verbs;
conicWeights = ref->conicWeights;
count = ref->verbCount;
direction = PathIterator::VerbDirection::Backward;
} else {
auto* ref = path->pathRef;
points = ref->points;
verbs = ref->verbs;
conicWeights = ref->conicWeights;
count = ref->verbCount;
direction = PathIterator::VerbDirection::Backward;
}
return jlong(new PathIterator(points, verbs, conicWeights, count, direction));
}
static void destroyPathIterator(JNIEnv*, jobject, jlong pathIterator_) {
delete reinterpret_cast<PathIterator*>(pathIterator_);
}
static jboolean pathIteratorHasNext(JNIEnv*, jobject, jlong pathIterator_) {
return reinterpret_cast<PathIterator*>(pathIterator_)->hasNext();
}
static jint conicToQuadraticsWrapper(JNIEnv* env, jobject,
jfloatArray conicPoints, jfloatArray quadraticPoints,
jfloat weight, jfloat tolerance, jint offset) {
float *conicData1 = env->GetFloatArrayElements(conicPoints, JNI_FALSE);
float *quadData1 = env->GetFloatArrayElements(quadraticPoints, JNI_FALSE);
int quadDataSize = env->GetArrayLength(quadraticPoints);
int count = conicToQuadratics(reinterpret_cast<Point *>(conicData1 + offset),
reinterpret_cast<Point *>(quadData1),
env->GetArrayLength(quadraticPoints),
weight, tolerance);
env->ReleaseFloatArrayElements(conicPoints, conicData1, 0);
env->ReleaseFloatArrayElements(quadraticPoints, quadData1, 0);
return count;
}
static jint pathIteratorNext(JNIEnv* env, jobject,
jlong pathIterator_, jfloatArray points_, jint offset_) {
auto pathIterator = reinterpret_cast<PathIterator*>(pathIterator_);
Point pointsData[4];
Verb verb = pathIterator->next(pointsData);
if (verb != Verb::Done && verb != Verb::Close) {
auto* floatsData = reinterpret_cast<jfloat*>(pointsData);
env->SetFloatArrayRegion(points_, offset_, 8, floatsData);
}
return static_cast<jint>(verb);
}
static jint pathIteratorPeek(JNIEnv*, jobject, jlong pathIterator_) {
return static_cast<jint>(reinterpret_cast<PathIterator *>(pathIterator_)->peek());
}
static jint pathIteratorRawSize(JNIEnv*, jobject, jlong pathIterator_) {
return static_cast<jint>(reinterpret_cast<PathIterator *>(pathIterator_)->rawCount());
}
static jint pathIteratorSize(JNIEnv*, jobject, jlong pathIterator_) {
return static_cast<jint>(reinterpret_cast<PathIterator *>(pathIterator_)->count());
}
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
sPath.jniClass = env->FindClass("android/graphics/Path");
if (sPath.jniClass == nullptr) return JNI_ERR;
sPath.nativePath = env->GetFieldID(sPath.jniClass, "mNativePath", "J");
if (sPath.nativePath == nullptr) return JNI_ERR;
{
jclass pathsClass = env->FindClass(JNI_CLASS_NAME);
if (pathsClass == nullptr) return JNI_ERR;
static const JNINativeMethod methods[] = {
{
(char*) "createInternalPathIterator",
(char*) "(Landroid/graphics/Path;IF)J",
reinterpret_cast<void*>(createPathIterator)
},
{
(char*) "destroyInternalPathIterator",
(char*) "(J)V",
reinterpret_cast<void*>(destroyPathIterator)
},
{
(char*) "internalPathIteratorHasNext",
(char*) "(J)Z",
reinterpret_cast<void*>(pathIteratorHasNext)
},
{
(char*) "internalPathIteratorNext",
(char*) "(J[FI)I",
reinterpret_cast<void*>(pathIteratorNext)
},
{
(char*) "internalPathIteratorPeek",
(char*) "(J)I",
reinterpret_cast<void*>(pathIteratorPeek)
},
{
(char*) "internalPathIteratorRawSize",
(char*) "(J)I",
reinterpret_cast<void*>(pathIteratorRawSize)
},
{
(char*) "internalPathIteratorSize",
(char*) "(J)I",
reinterpret_cast<void*>(pathIteratorSize)
},
};
int result = env->RegisterNatives(
pathsClass, methods, sizeof(methods) / sizeof(JNINativeMethod)
);
if (result != JNI_OK) return result;
env->DeleteLocalRef(pathsClass);
jclass converterClass = env->FindClass(JNI_CLASS_NAME_CONVERTER);
if (converterClass == nullptr) return JNI_ERR;
static const JNINativeMethod methods2[] = {
{
(char *) "internalConicToQuadratics",
(char *) "([F[FFFI)I",
reinterpret_cast<void *>(conicToQuadraticsWrapper)
},
};
result = env->RegisterNatives(
converterClass, methods2, sizeof(methods2) / sizeof(JNINativeMethod)
);
if (result != JNI_OK) return result;
env->DeleteLocalRef(converterClass);
}
return JNI_VERSION_1_6;
}