summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Michael Hoisie <hoisie@google.com> 2024-08-06 00:12:32 +0000
committer Michael Hoisie <hoisie@google.com> 2024-09-27 19:29:22 +0000
commitc7bd207395fc64249cfb1273f693ec5f64e6089d (patch)
tree3e11f6fa2cf8d2321bb841b4d4ae1a0745bc0785
parent6b09d0b0c2cd21edebd3be722baffa47f2faff02 (diff)
Add an alternative to PathIterator.nNext for host
PathIterator uses VMRuntime.newNonMovableArray and VMRuntime.addressOf to create a region of memory for iterating over path segments. However, VMRuntime.newNonMovableArray and VMRuntime.addressOf are libcore-only and not supported on the host JVM. This causes problems when Compose animations run on host runtimes such as LayoutLib, Ravenwood, and Robolectric. Add a variant method PathIterator.nNextHost that passes the float array (not just the address) to a regular JNI method. An alternative to this would be to convert `nNext` to a `FastNative`, but that would slow it down on Android. Another alternative would be to require LayoutLib/Ravenwood/Robolectric to use some DirectByteBuffer hacks to simulate a non-movable address, but that would require each framework to delegate/fake/shadow parts of PathIterator. This approach solves it in one fell swoop. Flag: NONE host-only change Bug: 354621198 Bug: 357624564 Test: cts PathIteratorTest Change-Id: Idfd2b6e41b499efffbbf5cbef343183981edd7b2
-rw-r--r--graphics/java/android/graphics/PathIterator.java19
-rw-r--r--libs/hwui/jni/PathIterator.cpp14
2 files changed, 29 insertions, 4 deletions
diff --git a/graphics/java/android/graphics/PathIterator.java b/graphics/java/android/graphics/PathIterator.java
index 48b29f4e81f4..d7caabf9f91b 100644
--- a/graphics/java/android/graphics/PathIterator.java
+++ b/graphics/java/android/graphics/PathIterator.java
@@ -44,6 +44,8 @@ public class PathIterator implements Iterator<PathIterator.Segment> {
private final Path mPath;
private final int mPathGenerationId;
private static final int POINT_ARRAY_SIZE = 8;
+ private static final boolean IS_DALVIK = "dalvik".equalsIgnoreCase(
+ System.getProperty("java.vm.name"));
private static final NativeAllocationRegistry sRegistry =
NativeAllocationRegistry.createMalloced(
@@ -80,9 +82,14 @@ public class PathIterator implements Iterator<PathIterator.Segment> {
mPath = path;
mNativeIterator = nCreate(mPath.mNativePath);
mPathGenerationId = mPath.getGenerationId();
- final VMRuntime runtime = VMRuntime.getRuntime();
- mPointsArray = (float[]) runtime.newNonMovableArray(float.class, POINT_ARRAY_SIZE);
- mPointsAddress = runtime.addressOf(mPointsArray);
+ if (IS_DALVIK) {
+ final VMRuntime runtime = VMRuntime.getRuntime();
+ mPointsArray = (float[]) runtime.newNonMovableArray(float.class, POINT_ARRAY_SIZE);
+ mPointsAddress = runtime.addressOf(mPointsArray);
+ } else {
+ mPointsArray = new float[POINT_ARRAY_SIZE];
+ mPointsAddress = 0;
+ }
sRegistry.registerNativeAllocation(this, mNativeIterator);
}
@@ -177,7 +184,8 @@ public class PathIterator implements Iterator<PathIterator.Segment> {
throw new ConcurrentModificationException(
"Iterator cannot be used on modified Path");
}
- @Verb int verb = nNext(mNativeIterator, mPointsAddress);
+ @Verb int verb = IS_DALVIK
+ ? nNext(mNativeIterator, mPointsAddress) : nNextHost(mNativeIterator, mPointsArray);
if (verb == VERB_DONE) {
mDone = true;
}
@@ -287,6 +295,9 @@ public class PathIterator implements Iterator<PathIterator.Segment> {
private static native long nCreate(long nativePath);
private static native long nGetFinalizer();
+ /* nNextHost should be used for host runtimes, e.g. LayoutLib */
+ private static native int nNextHost(long nativeIterator, float[] points);
+
// ------------------ Critical JNI ------------------------
@CriticalNative
diff --git a/libs/hwui/jni/PathIterator.cpp b/libs/hwui/jni/PathIterator.cpp
index 3884342d8d37..e9de6555935d 100644
--- a/libs/hwui/jni/PathIterator.cpp
+++ b/libs/hwui/jni/PathIterator.cpp
@@ -20,6 +20,7 @@
#include "GraphicsJNI.h"
#include "SkPath.h"
#include "SkPoint.h"
+#include "graphics_jni_helpers.h"
namespace android {
@@ -36,6 +37,18 @@ public:
return reinterpret_cast<jlong>(new SkPath::RawIter(*path));
}
+ // A variant of 'next' (below) that is compatible with the host JVM.
+ static jint nextHost(JNIEnv* env, jclass clazz, jlong iteratorHandle, jfloatArray pointsArray) {
+ jfloat* points = env->GetFloatArrayElements(pointsArray, 0);
+#ifdef __ANDROID__
+ jint result = next(iteratorHandle, reinterpret_cast<jlong>(points));
+#else
+ jint result = next(env, clazz, iteratorHandle, reinterpret_cast<jlong>(points));
+#endif
+ env->ReleaseFloatArrayElements(pointsArray, points, 0);
+ return result;
+ }
+
// ---------------- @CriticalNative -------------------------
static jint peek(CRITICAL_JNI_PARAMS_COMMA jlong iteratorHandle) {
@@ -72,6 +85,7 @@ static const JNINativeMethod methods[] = {
{"nPeek", "(J)I", (void*)SkPathIteratorGlue::peek},
{"nNext", "(JJ)I", (void*)SkPathIteratorGlue::next},
+ {"nNextHost", "(J[F)I", (void*)SkPathIteratorGlue::nextHost},
};
int register_android_graphics_PathIterator(JNIEnv* env) {