diff options
author | 2024-08-06 00:12:32 +0000 | |
---|---|---|
committer | 2024-09-27 19:29:22 +0000 | |
commit | c7bd207395fc64249cfb1273f693ec5f64e6089d (patch) | |
tree | 3e11f6fa2cf8d2321bb841b4d4ae1a0745bc0785 | |
parent | 6b09d0b0c2cd21edebd3be722baffa47f2faff02 (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.java | 19 | ||||
-rw-r--r-- | libs/hwui/jni/PathIterator.cpp | 14 |
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) { |