Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c7bd2073 authored by Michael Hoisie's avatar Michael Hoisie
Browse files

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
parent 6b09d0b0
Loading
Loading
Loading
Loading
+15 −4
Original line number Diff line number Diff line
@@ -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();
        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
+14 −0
Original line number Diff line number Diff line
@@ -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) {