summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apct-tests/perftests/core/src/android/graphics/perftests/PathIteratorPerfTest.java112
-rw-r--r--core/api/current.txt29
-rw-r--r--graphics/java/android/graphics/Path.java106
-rw-r--r--graphics/java/android/graphics/PathIterator.java303
-rw-r--r--libs/hwui/Android.bp1
-rw-r--r--libs/hwui/apex/LayoutlibLoader.cpp2
-rw-r--r--libs/hwui/apex/jni_runtime.cpp108
-rw-r--r--libs/hwui/jni/Path.cpp119
-rw-r--r--libs/hwui/jni/PathIterator.cpp81
9 files changed, 764 insertions, 97 deletions
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/PathIteratorPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/PathIteratorPerfTest.java
new file mode 100644
index 000000000000..6b739bc2f46c
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/PathIteratorPerfTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.graphics.perftests;
+
+import static android.graphics.PathIterator.VERB_DONE;
+
+import android.graphics.Path;
+import android.graphics.PathIterator;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+@LargeTest
+public class PathIteratorPerfTest {
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private Path constructCircularPath(int numSegments) {
+ Path path = new Path();
+ float angleIncrement = (float) (2 * Math.PI / numSegments);
+ float radius = 200f;
+ float prevX = 0f, prevY = 0f;
+ float angle = 0f;
+ for (int i = 0; i <= numSegments; ++i) {
+ float x = (float) Math.cos(angle) * radius;
+ float y = (float) Math.sin(angle) * radius;
+ if (i > 0) {
+ path.cubicTo(prevX, prevY, x, y, x, y);
+ } else {
+ path.moveTo(x, y);
+ }
+ prevX = x;
+ prevY = y;
+ angle += angleIncrement;
+ }
+ return path;
+ }
+
+ private void testNextSegmentImpl(int numSegments) {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ Path path = constructCircularPath(numSegments);
+ while (state.keepRunning()) {
+ PathIterator iterator = path.iterator();
+ PathIterator.Segment segment = iterator.next();
+ while (segment.getVerb() != VERB_DONE) {
+ segment = iterator.next();
+ }
+ }
+ }
+
+ private void testNextFloatsImpl(int numSegments) {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ float[] points = new float[8];
+ Path path = constructCircularPath(numSegments);
+ while (state.keepRunning()) {
+ PathIterator iterator = path.iterator();
+ int verb = iterator.next(points, 0);
+ while (verb != VERB_DONE) {
+ verb = iterator.next(points, 0);
+ }
+ }
+ }
+
+ @Test
+ public void testNextSegment10() {
+ testNextSegmentImpl(10);
+ }
+
+ @Test
+ public void testNextSegment100() {
+ testNextSegmentImpl(100);
+ }
+
+ @Test
+ public void testNextSegment1000() {
+ testNextSegmentImpl(1000);
+ }
+
+ @Test
+ public void testNextArray10() {
+ testNextFloatsImpl(10);
+ }
+
+ @Test
+ public void testNextArray100() {
+ testNextFloatsImpl(100);
+ }
+
+ @Test
+ public void testNextArray1000() {
+ testNextFloatsImpl(1000);
+ }
+
+}
diff --git a/core/api/current.txt b/core/api/current.txt
index 7ed1a4382054..ba61ca13604c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -15111,7 +15111,7 @@ package android.graphics {
field @NonNull public static final android.os.Parcelable.Creator<android.graphics.ParcelableColorSpace> CREATOR;
}
- public class Path {
+ public class Path implements java.lang.Iterable<android.graphics.PathIterator.Segment> {
ctor public Path();
ctor public Path(@Nullable android.graphics.Path);
method public void addArc(@NonNull android.graphics.RectF, float, float);
@@ -15134,13 +15134,18 @@ package android.graphics {
method public void arcTo(float, float, float, float, float, float, boolean);
method public void close();
method public void computeBounds(@NonNull android.graphics.RectF, boolean);
+ method public void conicTo(float, float, float, float, float);
method public void cubicTo(float, float, float, float, float, float);
method @NonNull public android.graphics.Path.FillType getFillType();
+ method public int getGenerationId();
method public void incReserve(int);
+ method public boolean interpolate(@NonNull android.graphics.Path, float, @NonNull android.graphics.Path);
method @Deprecated public boolean isConvex();
method public boolean isEmpty();
+ method public boolean isInterpolatable(@NonNull android.graphics.Path);
method public boolean isInverseFillType();
method public boolean isRect(@Nullable android.graphics.RectF);
+ method @NonNull public android.graphics.PathIterator iterator();
method public void lineTo(float, float);
method public void moveTo(float, float);
method public void offset(float, float, @Nullable android.graphics.Path);
@@ -15148,6 +15153,7 @@ package android.graphics {
method public boolean op(@NonNull android.graphics.Path, @NonNull android.graphics.Path.Op);
method public boolean op(@NonNull android.graphics.Path, @NonNull android.graphics.Path, @NonNull android.graphics.Path.Op);
method public void quadTo(float, float, float, float);
+ method public void rConicTo(float, float, float, float, float);
method public void rCubicTo(float, float, float, float, float, float);
method public void rLineTo(float, float);
method public void rMoveTo(float, float);
@@ -15196,6 +15202,27 @@ package android.graphics {
ctor public PathEffect();
}
+ public class PathIterator implements java.util.Iterator<android.graphics.PathIterator.Segment> {
+ method public boolean hasNext();
+ method @NonNull public int next(@NonNull float[], int);
+ method @NonNull public android.graphics.PathIterator.Segment next();
+ method @NonNull public int peek();
+ field public static final int VERB_CLOSE = 5; // 0x5
+ field public static final int VERB_CONIC = 3; // 0x3
+ field public static final int VERB_CUBIC = 4; // 0x4
+ field public static final int VERB_DONE = 6; // 0x6
+ field public static final int VERB_LINE = 1; // 0x1
+ field public static final int VERB_MOVE = 0; // 0x0
+ field public static final int VERB_QUAD = 2; // 0x2
+ }
+
+ public static class PathIterator.Segment {
+ ctor public PathIterator.Segment(@NonNull int, @NonNull float[], float);
+ method public float getConicWeight();
+ method @NonNull public float[] getPoints();
+ method @NonNull public int getVerb();
+ }
+
public class PathMeasure {
ctor public PathMeasure();
ctor public PathMeasure(android.graphics.Path, boolean);
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index afcaabebb9fb..052b9af09bc8 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -33,7 +33,7 @@ import libcore.util.NativeAllocationRegistry;
* (based on the paint's Style), or it can be used for clipping or to draw
* text on a path.
*/
-public class Path {
+public class Path implements Iterable<PathIterator.Segment> {
private static final NativeAllocationRegistry sRegistry =
NativeAllocationRegistry.createMalloced(
@@ -92,6 +92,17 @@ public class Path {
}
/**
+ * Returns an iterator over the segments of this path.
+ *
+ * @return the Iterator object
+ */
+ @NonNull
+ @Override
+ public PathIterator iterator() {
+ return new PathIterator(this);
+ }
+
+ /**
* The logical operations that can be performed when combining two paths.
*
* @see #op(Path, android.graphics.Path.Op)
@@ -383,6 +394,49 @@ public class Path {
}
/**
+ * Add a quadratic bezier from the last point, approaching control point
+ * (x1,y1), and ending at (x2,y2), weighted by <code>weight</code>. If no
+ * moveTo() call has been made for this contour, the first point is
+ * automatically set to (0,0).
+ *
+ * A weight of 1 is equivalent to calling {@link #quadTo(float, float, float, float)}.
+ * A weight of 0 is equivalent to calling {@link #lineTo(float, float)} to
+ * <code>(x1, y1)</code> followed by {@link #lineTo(float, float)} to <code>(x2, y2)</code>.
+ *
+ * @param x1 The x-coordinate of the control point on a conic curve
+ * @param y1 The y-coordinate of the control point on a conic curve
+ * @param x2 The x-coordinate of the end point on a conic curve
+ * @param y2 The y-coordinate of the end point on a conic curve
+ * @param weight The weight of the conic applied to the curve. A value of 1 is equivalent
+ * to a quadratic with the given control and anchor points and a value of 0 is
+ * equivalent to a line to the first and another line to the second point.
+ */
+ public void conicTo(float x1, float y1, float x2, float y2, float weight) {
+ nConicTo(mNativePath, x1, y1, x2, y2, weight);
+ }
+
+ /**
+ * Same as conicTo, but the coordinates are considered relative to the last
+ * point on this contour. If there is no previous point, then a moveTo(0,0)
+ * is inserted automatically.
+ *
+ * @param dx1 The amount to add to the x-coordinate of the last point on
+ * this contour, for the control point of a conic curve
+ * @param dy1 The amount to add to the y-coordinate of the last point on
+ * this contour, for the control point of a conic curve
+ * @param dx2 The amount to add to the x-coordinate of the last point on
+ * this contour, for the end point of a conic curve
+ * @param dy2 The amount to add to the y-coordinate of the last point on
+ * this contour, for the end point of a conic curve
+ * @param weight The weight of the conic applied to the curve. A value of 1 is equivalent
+ * to a quadratic with the given control and anchor points and a value of 0 is
+ * equivalent to a line to the first and another line to the second point.
+ */
+ public void rConicTo(float dx1, float dy1, float dx2, float dy2, float weight) {
+ nRConicTo(mNativePath, dx1, dy1, dx2, dy2, weight);
+ }
+
+ /**
* Add a cubic bezier from the last point, approaching control points
* (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
* made for this contour, the first point is automatically set to (0,0).
@@ -736,6 +790,46 @@ public class Path {
return nApproximate(mNativePath, acceptableError);
}
+ /**
+ * Returns the generation ID of this path. The generation ID changes
+ * whenever the path is modified. This can be used as an efficient way to
+ * check if a path has changed.
+ *
+ * @return The current generation ID for this path
+ */
+ public int getGenerationId() {
+ return nGetGenerationID(mNativePath);
+ }
+
+ /**
+ * Two paths can be interpolated, by calling {@link #interpolate(Path, float, Path)}, if they
+ * have exactly the same structure. That is, both paths must have the same
+ * operations, in the same order. If any of the operations are
+ * of type {@link PathIterator#VERB_CONIC}, then the weights of those conics must also match.
+ *
+ * @param otherPath The other <code>Path</code> being interpolated to from this one.
+ * @return true if interpolation is possible, false otherwise
+ */
+ public boolean isInterpolatable(@NonNull Path otherPath) {
+ return nIsInterpolatable(mNativePath, otherPath.mNativePath);
+ }
+
+ /**
+ * This method will linearly interpolate from this path to <code>otherPath</code> given
+ * the interpolation parameter <code>t</code>, returning the result in
+ * <code>interpolatedPath</code>. Interpolation will only succeed if the structures of the
+ * two paths match exactly, as discussed in {@link #isInterpolatable(Path)}.
+ *
+ * @param otherPath The other <code>Path</code> being interpolated to.
+ * @param t The interpolation parameter. A value of 0 results in a <code>Path</code>
+ * equivalent to this path, a value of 1 results in one equivalent to
+ * <code>otherPath</code>.
+ * @param interpolatedPath The interpolated results.
+ */
+ public boolean interpolate(@NonNull Path otherPath, float t, @NonNull Path interpolatedPath) {
+ return nInterpolate(mNativePath, otherPath.mNativePath, t, interpolatedPath.mNativePath);
+ }
+
// ------------------ Regular JNI ------------------------
private static native long nInit();
@@ -750,6 +844,10 @@ public class Path {
private static native void nRLineTo(long nPath, float dx, float dy);
private static native void nQuadTo(long nPath, float x1, float y1, float x2, float y2);
private static native void nRQuadTo(long nPath, float dx1, float dy1, float dx2, float dy2);
+ private static native void nConicTo(long nPath, float x1, float y1, float x2, float y2,
+ float weight);
+ private static native void nRConicTo(long nPath, float dx1, float dy1, float dx2, float dy2,
+ float weight);
private static native void nCubicTo(long nPath, float x1, float y1, float x2, float y2,
float x3, float y3);
private static native void nRCubicTo(long nPath, float x1, float y1, float x2, float y2,
@@ -777,6 +875,8 @@ public class Path {
private static native void nTransform(long nPath, long matrix);
private static native boolean nOp(long path1, long path2, int op, long result);
private static native float[] nApproximate(long nPath, float error);
+ private static native boolean nInterpolate(long startPath, long endPath, float t,
+ long interpolatedPath);
// ------------------ Fast JNI ------------------------
@@ -786,6 +886,10 @@ public class Path {
// ------------------ Critical JNI ------------------------
@CriticalNative
+ private static native int nGetGenerationID(long nativePath);
+ @CriticalNative
+ private static native boolean nIsInterpolatable(long startPath, long endPath);
+ @CriticalNative
private static native void nReset(long nPath);
@CriticalNative
private static native void nRewind(long nPath);
diff --git a/graphics/java/android/graphics/PathIterator.java b/graphics/java/android/graphics/PathIterator.java
new file mode 100644
index 000000000000..33b9a475ad21
--- /dev/null
+++ b/graphics/java/android/graphics/PathIterator.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.graphics;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.system.VMRuntime;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.lang.annotation.Retention;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+
+/**
+ * <code>PathIterator</code> can be used to query a given {@link Path} object, to discover its
+ * operations and point values.
+ */
+public class PathIterator implements Iterator<PathIterator.Segment> {
+
+ private final float[] mPointsArray;
+ private final long mPointsAddress;
+ private int mCachedVerb = -1;
+ private boolean mDone = false;
+ private final long mNativeIterator;
+ private final Path mPath;
+ private final int mPathGenerationId;
+ private static final int POINT_ARRAY_SIZE = 8;
+
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ PathIterator.class.getClassLoader(), nGetFinalizer());
+
+ /**
+ * The <code>Verb</code> indicates the operation for a given segment of a path. These
+ * operations correspond exactly to the primitive operations on {@link Path}, such as
+ * {@link Path#moveTo(float, float)} and {@link Path#lineTo(float, float)}.
+ */
+ @Retention(SOURCE)
+ @IntDef({VERB_MOVE, VERB_LINE, VERB_QUAD, VERB_CONIC, VERB_CUBIC, VERB_CLOSE, VERB_DONE})
+ @interface Verb {}
+ // these must match the values in SkPath.h
+ public static final int VERB_MOVE = 0;
+ public static final int VERB_LINE = 1;
+ public static final int VERB_QUAD = 2;
+ public static final int VERB_CONIC = 3;
+ public static final int VERB_CUBIC = 4;
+ public static final int VERB_CLOSE = 5;
+ public static final int VERB_DONE = 6;
+
+ /**
+ * Returns a {@link PathIterator} object for this path, which can be used to query the
+ * data (operations and points) in the path. Iterators can only be used on Path objects
+ * that have not been modified since the iterator was created. Calling
+ * {@link #next(float[], int)}, {@link #next()}, or {@link #hasNext()} on an
+ * iterator for a modified path will result in a {@link ConcurrentModificationException}.
+ *
+ * @param path The {@link Path} for which this iterator can be queried.
+ */
+ PathIterator(@NonNull Path path) {
+ 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);
+ sRegistry.registerNativeAllocation(this, mNativeIterator);
+ }
+
+ /**
+ * Returns the next verb in this iterator's {@link Path}, and fills entries in the
+ * <code>points</code> array with the point data (if any) for that operation.
+ * Each two floats represent the data for a single point of that operation.
+ * The number of pairs of floats supplied in the resulting array depends on the verb:
+ * <ul>
+ * <li>{@link #VERB_MOVE}: 1 pair (indices 0 to 1)</li>
+ * <li>{@link #VERB_LINE}: 2 pairs (indices 0 to 3)</li>
+ * <li>{@link #VERB_QUAD}: 3 pairs (indices 0 to 5)</li>
+ * <li>{@link #VERB_CONIC}: 3.5 pairs (indices 0 to 6), the seventh entry has the conic
+ * weight</li>
+ * <li>{@link #VERB_CUBIC}: 4 pairs (indices 0 to 7)</li>
+ * <li>{@link #VERB_CLOSE}: 0 pairs</li>
+ * <li>{@link #VERB_DONE}: 0 pairs</li>
+ * </ul>
+ * @param points The point data for this operation, must have at least
+ * 8 items available to hold up to 4 pairs of point values
+ * @param offset An offset into the <code>points</code> array where entries should be placed.
+ * @return the operation for the next element in the iteration
+ * @throws ArrayIndexOutOfBoundsException if the points array is too small
+ * @throws ConcurrentModificationException if the underlying path was modified
+ * since this iterator was created.
+ * @throws NoSuchElementException if the iteration has no more elements
+ */
+ @NonNull
+ public @Verb int next(@NonNull float[] points, int offset) {
+ if (points.length < offset + POINT_ARRAY_SIZE) {
+ throw new ArrayIndexOutOfBoundsException("points array must be able to "
+ + "hold at least 8 entries");
+ }
+ @Verb int returnVerb = getReturnVerb(mCachedVerb);
+ mCachedVerb = -1;
+ System.arraycopy(mPointsArray, 0, points, offset, POINT_ARRAY_SIZE);
+ return returnVerb;
+ }
+
+ /**
+ * Returns true if the there are more elements in this iterator to be returned.
+ * A return value of <code>false</code> means there are no more elements, and an
+ * ensuing call to {@link #next()} or {@link #next(float[], int)} )} will throw a
+ * {@link NoSuchElementException}.
+ *
+ * @return true if there are more elements to be iterated through, false otherwise
+ * @throws ConcurrentModificationException if the underlying path was modified
+ * since this iterator was created.
+ */
+ @Override
+ public boolean hasNext() {
+ try {
+ if (mCachedVerb == -1) {
+ mCachedVerb = nextInternal();
+ }
+ return true;
+ } catch (NoSuchElementException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the next verb in the iteration, or {@link #VERB_DONE} if there are no more
+ * elements.
+ *
+ * @return the next verb in the iteration, or {@link #VERB_DONE} if there are no more
+ * elements
+ * @throws ConcurrentModificationException if the underlying path was modified
+ * since this iterator was created.
+ */
+ @NonNull
+ public @Verb int peek() {
+ if (mPathGenerationId != mPath.getGenerationId()) {
+ throw new ConcurrentModificationException(
+ "Iterator cannot be used on modified Path");
+ }
+ if (mDone) {
+ return VERB_DONE;
+ }
+ return nPeek(mNativeIterator);
+ }
+
+ /**
+ * This is where the work is done for {@link #next()}. Using this internal method
+ * is helfpul for managing the cached segment used by {@link #hasNext()}.
+ *
+ * @return the segment to be returned by {@link #next()}
+ * @throws NoSuchElementException if the iteration has no more elements
+ */
+ @NonNull
+ private @Verb int nextInternal() {
+ if (mDone) {
+ throw new NoSuchElementException("No more path segments to iterate");
+ }
+ if (mPathGenerationId != mPath.getGenerationId()) {
+ throw new ConcurrentModificationException(
+ "Iterator cannot be used on modified Path");
+ }
+ @Verb int verb = nNext(mNativeIterator, mPointsAddress);
+ if (verb == VERB_DONE) {
+ mDone = true;
+ }
+ return verb;
+ }
+
+ /**
+ * Returns the next {@link Segment} element in this iterator.
+ *
+ * There are two versions of <code>next()</code>. This version is slightly more
+ * expensive at runtime, since it allocates a new {@link Segment} object with
+ * every call. The other version, {@link #next(float[], int)} requires no such allocation, but
+ * requires a little more manual effort to use.
+ *
+ * @return the next segment in this iterator
+ * @throws NoSuchElementException if the iteration has no more elements
+ * @throws ConcurrentModificationException if the underlying path was modified
+ * since this iterator was created.
+ */
+ @NonNull
+ @Override
+ public Segment next() {
+ @Verb int returnVerb = getReturnVerb(mCachedVerb);
+ mCachedVerb = -1;
+ float conicWeight = 0f;
+ if (returnVerb == VERB_CONIC) {
+ conicWeight = mPointsArray[6];
+ }
+ float[] returnPoints = new float[8];
+ System.arraycopy(mPointsArray, 0, returnPoints, 0, POINT_ARRAY_SIZE);
+ return new Segment(returnVerb, returnPoints, conicWeight);
+ }
+
+ private @Verb int getReturnVerb(int cachedVerb) {
+ switch (cachedVerb) {
+ case VERB_MOVE: return VERB_MOVE;
+ case VERB_LINE: return VERB_LINE;
+ case VERB_QUAD: return VERB_QUAD;
+ case VERB_CONIC: return VERB_CONIC;
+ case VERB_CUBIC: return VERB_CUBIC;
+ case VERB_CLOSE: return VERB_CLOSE;
+ case VERB_DONE: return VERB_DONE;
+ }
+ return nextInternal();
+ }
+
+ /**
+ * This class holds the data for a given segment in a path, as returned by
+ * {@link #next()}.
+ */
+ public static class Segment {
+ private final @Verb int mVerb;
+ private final float[] mPoints;
+ private final float mConicWeight;
+
+ /**
+ * The operation for this segment.
+ *
+ * @return the verb which indicates the operation happening in this segment
+ */
+ @NonNull
+ public @Verb int getVerb() {
+ return mVerb;
+ }
+
+ /**
+ * The point data for this segment.
+ *
+ * Each two floats represent the data for a single point of that operation.
+ * The number of pairs of floats supplied in the resulting array depends on the verb:
+ * <ul>
+ * <li>{@link #VERB_MOVE}: 1 pair (indices 0 to 1)</li>
+ * <li>{@link #VERB_LINE}: 2 pairs (indices 0 to 3)</li>
+ * <li>{@link #VERB_QUAD}: 3 pairs (indices 0 to 5)</li>
+ * <li>{@link #VERB_CONIC}: 4 pairs (indices 0 to 7), the last pair contains the
+ * conic weight twice</li>
+ * <li>{@link #VERB_CUBIC}: 4 pairs (indices 0 to 7)</li>
+ * <li>{@link #VERB_CLOSE}: 0 pairs</li>
+ * <li>{@link #VERB_DONE}: 0 pairs</li>
+ * </ul>
+ * @return the point data for this segment
+ */
+ @NonNull
+ public float[] getPoints() {
+ return mPoints;
+ }
+
+ /**
+ * The weight for the conic operation in this segment. If the verb in this segment
+ * is not equal to {@link #VERB_CONIC}, the weight value is undefined.
+ *
+ * @see Path#conicTo(float, float, float, float, float)
+ * @return the weight for the conic operation in this segment, if any
+ */
+ public float getConicWeight() {
+ return mConicWeight;
+ }
+
+ public Segment(@NonNull @Verb int verb, @NonNull float[] points, float conicWeight) {
+ mVerb = verb;
+ mPoints = points;
+ mConicWeight = conicWeight;
+ }
+ }
+
+ // ------------------ Regular JNI ------------------------
+
+ private static native long nCreate(long nativePath);
+ private static native long nGetFinalizer();
+
+ // ------------------ Critical JNI ------------------------
+
+ @CriticalNative
+ private static native int nNext(long nativeIterator, long pointsAddress);
+
+ @CriticalNative
+ private static native int nPeek(long nativeIterator);
+}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index ad9aa6cdd3d9..88f73d6cf251 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -345,6 +345,7 @@ cc_defaults {
"jni/PaintFilter.cpp",
"jni/Path.cpp",
"jni/PathEffect.cpp",
+ "jni/PathIterator.cpp",
"jni/PathMeasure.cpp",
"jni/Picture.cpp",
"jni/Region.cpp",
diff --git a/libs/hwui/apex/LayoutlibLoader.cpp b/libs/hwui/apex/LayoutlibLoader.cpp
index 942c0506321c..b7a15633ff6d 100644
--- a/libs/hwui/apex/LayoutlibLoader.cpp
+++ b/libs/hwui/apex/LayoutlibLoader.cpp
@@ -53,6 +53,7 @@ extern int register_android_graphics_FontFamily(JNIEnv* env);
extern int register_android_graphics_Matrix(JNIEnv* env);
extern int register_android_graphics_Paint(JNIEnv* env);
extern int register_android_graphics_Path(JNIEnv* env);
+extern int register_android_graphics_PathIterator(JNIEnv* env);
extern int register_android_graphics_PathMeasure(JNIEnv* env);
extern int register_android_graphics_Picture(JNIEnv* env);
extern int register_android_graphics_Region(JNIEnv* env);
@@ -100,6 +101,7 @@ static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = {
{"android.graphics.Paint", REG_JNI(register_android_graphics_Paint)},
{"android.graphics.Path", REG_JNI(register_android_graphics_Path)},
{"android.graphics.PathEffect", REG_JNI(register_android_graphics_PathEffect)},
+ {"android.graphics.PathIterator", REG_JNI(register_android_graphics_PathIterator)},
{"android.graphics.PathMeasure", REG_JNI(register_android_graphics_PathMeasure)},
{"android.graphics.Picture", REG_JNI(register_android_graphics_Picture)},
{"android.graphics.RecordingCanvas", REG_JNI(register_android_view_DisplayListCanvas)},
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index e1f5abd786bf..39725a55b594 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -59,6 +59,7 @@ extern int register_android_graphics_HardwareRendererObserver(JNIEnv* env);
extern int register_android_graphics_Matrix(JNIEnv* env);
extern int register_android_graphics_Paint(JNIEnv* env);
extern int register_android_graphics_Path(JNIEnv* env);
+extern int register_android_graphics_PathIterator(JNIEnv* env);
extern int register_android_graphics_PathMeasure(JNIEnv* env);
extern int register_android_graphics_Picture(JNIEnv*);
extern int register_android_graphics_Region(JNIEnv* env);
@@ -94,59 +95,60 @@ extern int register_android_view_ThreadedRenderer(JNIEnv* env);
};
#endif
-static const RegJNIRec gRegJNI[] = {
- REG_JNI(register_android_graphics_Canvas),
- // This needs to be before register_android_graphics_Graphics, or the latter
- // will not be able to find the jmethodID for ColorSpace.get().
- REG_JNI(register_android_graphics_ColorSpace),
- REG_JNI(register_android_graphics_Graphics),
- REG_JNI(register_android_graphics_Bitmap),
- REG_JNI(register_android_graphics_BitmapFactory),
- REG_JNI(register_android_graphics_BitmapRegionDecoder),
- REG_JNI(register_android_graphics_ByteBufferStreamAdaptor),
- REG_JNI(register_android_graphics_Camera),
- REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor),
- REG_JNI(register_android_graphics_CanvasProperty),
- REG_JNI(register_android_graphics_ColorFilter),
- REG_JNI(register_android_graphics_DrawFilter),
- REG_JNI(register_android_graphics_FontFamily),
- REG_JNI(register_android_graphics_HardwareRendererObserver),
- REG_JNI(register_android_graphics_ImageDecoder),
- REG_JNI(register_android_graphics_drawable_AnimatedImageDrawable),
- REG_JNI(register_android_graphics_Interpolator),
- REG_JNI(register_android_graphics_MaskFilter),
- REG_JNI(register_android_graphics_Matrix),
- REG_JNI(register_android_graphics_Movie),
- REG_JNI(register_android_graphics_NinePatch),
- REG_JNI(register_android_graphics_Paint),
- REG_JNI(register_android_graphics_Path),
- REG_JNI(register_android_graphics_PathMeasure),
- REG_JNI(register_android_graphics_PathEffect),
- REG_JNI(register_android_graphics_Picture),
- REG_JNI(register_android_graphics_Region),
- REG_JNI(register_android_graphics_Shader),
- REG_JNI(register_android_graphics_RenderEffect),
- REG_JNI(register_android_graphics_TextureLayer),
- REG_JNI(register_android_graphics_Typeface),
- REG_JNI(register_android_graphics_YuvImage),
- REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory),
- REG_JNI(register_android_graphics_animation_RenderNodeAnimator),
- REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable),
- REG_JNI(register_android_graphics_drawable_VectorDrawable),
- REG_JNI(register_android_graphics_fonts_Font),
- REG_JNI(register_android_graphics_fonts_FontFamily),
- REG_JNI(register_android_graphics_pdf_PdfDocument),
- REG_JNI(register_android_graphics_pdf_PdfEditor),
- REG_JNI(register_android_graphics_pdf_PdfRenderer),
- REG_JNI(register_android_graphics_text_MeasuredText),
- REG_JNI(register_android_graphics_text_LineBreaker),
- REG_JNI(register_android_graphics_text_TextShaper),
-
- REG_JNI(register_android_util_PathParser),
- REG_JNI(register_android_view_RenderNode),
- REG_JNI(register_android_view_DisplayListCanvas),
- REG_JNI(register_android_view_ThreadedRenderer),
-};
+ static const RegJNIRec gRegJNI[] = {
+ REG_JNI(register_android_graphics_Canvas),
+ // This needs to be before register_android_graphics_Graphics, or the latter
+ // will not be able to find the jmethodID for ColorSpace.get().
+ REG_JNI(register_android_graphics_ColorSpace),
+ REG_JNI(register_android_graphics_Graphics),
+ REG_JNI(register_android_graphics_Bitmap),
+ REG_JNI(register_android_graphics_BitmapFactory),
+ REG_JNI(register_android_graphics_BitmapRegionDecoder),
+ REG_JNI(register_android_graphics_ByteBufferStreamAdaptor),
+ REG_JNI(register_android_graphics_Camera),
+ REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor),
+ REG_JNI(register_android_graphics_CanvasProperty),
+ REG_JNI(register_android_graphics_ColorFilter),
+ REG_JNI(register_android_graphics_DrawFilter),
+ REG_JNI(register_android_graphics_FontFamily),
+ REG_JNI(register_android_graphics_HardwareRendererObserver),
+ REG_JNI(register_android_graphics_ImageDecoder),
+ REG_JNI(register_android_graphics_drawable_AnimatedImageDrawable),
+ REG_JNI(register_android_graphics_Interpolator),
+ REG_JNI(register_android_graphics_MaskFilter),
+ REG_JNI(register_android_graphics_Matrix),
+ REG_JNI(register_android_graphics_Movie),
+ REG_JNI(register_android_graphics_NinePatch),
+ REG_JNI(register_android_graphics_Paint),
+ REG_JNI(register_android_graphics_Path),
+ REG_JNI(register_android_graphics_PathIterator),
+ REG_JNI(register_android_graphics_PathMeasure),
+ REG_JNI(register_android_graphics_PathEffect),
+ REG_JNI(register_android_graphics_Picture),
+ REG_JNI(register_android_graphics_Region),
+ REG_JNI(register_android_graphics_Shader),
+ REG_JNI(register_android_graphics_RenderEffect),
+ REG_JNI(register_android_graphics_TextureLayer),
+ REG_JNI(register_android_graphics_Typeface),
+ REG_JNI(register_android_graphics_YuvImage),
+ REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory),
+ REG_JNI(register_android_graphics_animation_RenderNodeAnimator),
+ REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable),
+ REG_JNI(register_android_graphics_drawable_VectorDrawable),
+ REG_JNI(register_android_graphics_fonts_Font),
+ REG_JNI(register_android_graphics_fonts_FontFamily),
+ REG_JNI(register_android_graphics_pdf_PdfDocument),
+ REG_JNI(register_android_graphics_pdf_PdfEditor),
+ REG_JNI(register_android_graphics_pdf_PdfRenderer),
+ REG_JNI(register_android_graphics_text_MeasuredText),
+ REG_JNI(register_android_graphics_text_LineBreaker),
+ REG_JNI(register_android_graphics_text_TextShaper),
+
+ REG_JNI(register_android_util_PathParser),
+ REG_JNI(register_android_view_RenderNode),
+ REG_JNI(register_android_view_DisplayListCanvas),
+ REG_JNI(register_android_view_ThreadedRenderer),
+ };
} // namespace android
diff --git a/libs/hwui/jni/Path.cpp b/libs/hwui/jni/Path.cpp
index d67bcf221681..3694ce07b972 100644
--- a/libs/hwui/jni/Path.cpp
+++ b/libs/hwui/jni/Path.cpp
@@ -102,6 +102,18 @@ public:
obj->rQuadTo(dx1, dy1, dx2, dy2);
}
+ static void conicTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x1, jfloat y1, jfloat x2,
+ jfloat y2, jfloat weight) {
+ SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+ obj->conicTo(x1, y1, x2, y2, weight);
+ }
+
+ static void rConicTo(JNIEnv* env, jclass clazz, jlong objHandle, jfloat dx1, jfloat dy1,
+ jfloat dx2, jfloat dy2, jfloat weight) {
+ SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+ obj->rConicTo(dx1, dy1, dx2, dy2, weight);
+ }
+
static void cubicTo__FFFFFF(JNIEnv* env, jclass clazz, jlong objHandle, jfloat x1, jfloat y1,
jfloat x2, jfloat y2, jfloat x3, jfloat y3) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
@@ -209,6 +221,14 @@ public:
obj->setLastPt(dx, dy);
}
+ static jboolean interpolate(JNIEnv* env, jclass clazz, jlong startHandle, jlong endHandle,
+ jfloat t, jlong interpolatedHandle) {
+ SkPath* startPath = reinterpret_cast<SkPath*>(startHandle);
+ SkPath* endPath = reinterpret_cast<SkPath*>(endHandle);
+ SkPath* interpolatedPath = reinterpret_cast<SkPath*>(interpolatedHandle);
+ return startPath->interpolate(*endPath, t, interpolatedPath);
+ }
+
static void transform__MatrixPath(JNIEnv* env, jclass clazz, jlong objHandle, jlong matrixHandle,
jlong dstHandle) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
@@ -473,6 +493,16 @@ public:
// ---------------- @CriticalNative -------------------------
+ static jint getGenerationID(CRITICAL_JNI_PARAMS_COMMA jlong pathHandle) {
+ return (reinterpret_cast<SkPath*>(pathHandle)->getGenerationID());
+ }
+
+ static jboolean isInterpolatable(CRITICAL_JNI_PARAMS_COMMA jlong startHandle, jlong endHandle) {
+ SkPath* startPath = reinterpret_cast<SkPath*>(startHandle);
+ SkPath* endPath = reinterpret_cast<SkPath*>(endHandle);
+ return startPath->isInterpolatable(*endPath);
+ }
+
static void reset(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->reset();
@@ -506,48 +536,53 @@ public:
};
static const JNINativeMethod methods[] = {
- {"nInit","()J", (void*) SkPathGlue::init},
- {"nInit","(J)J", (void*) SkPathGlue::init_Path},
- {"nGetFinalizer", "()J", (void*) SkPathGlue::getFinalizer},
- {"nSet","(JJ)V", (void*) SkPathGlue::set},
- {"nComputeBounds","(JLandroid/graphics/RectF;)V", (void*) SkPathGlue::computeBounds},
- {"nIncReserve","(JI)V", (void*) SkPathGlue::incReserve},
- {"nMoveTo","(JFF)V", (void*) SkPathGlue::moveTo__FF},
- {"nRMoveTo","(JFF)V", (void*) SkPathGlue::rMoveTo},
- {"nLineTo","(JFF)V", (void*) SkPathGlue::lineTo__FF},
- {"nRLineTo","(JFF)V", (void*) SkPathGlue::rLineTo},
- {"nQuadTo","(JFFFF)V", (void*) SkPathGlue::quadTo__FFFF},
- {"nRQuadTo","(JFFFF)V", (void*) SkPathGlue::rQuadTo},
- {"nCubicTo","(JFFFFFF)V", (void*) SkPathGlue::cubicTo__FFFFFF},
- {"nRCubicTo","(JFFFFFF)V", (void*) SkPathGlue::rCubicTo},
- {"nArcTo","(JFFFFFFZ)V", (void*) SkPathGlue::arcTo},
- {"nClose","(J)V", (void*) SkPathGlue::close},
- {"nAddRect","(JFFFFI)V", (void*) SkPathGlue::addRect},
- {"nAddOval","(JFFFFI)V", (void*) SkPathGlue::addOval},
- {"nAddCircle","(JFFFI)V", (void*) SkPathGlue::addCircle},
- {"nAddArc","(JFFFFFF)V", (void*) SkPathGlue::addArc},
- {"nAddRoundRect","(JFFFFFFI)V", (void*) SkPathGlue::addRoundRectXY},
- {"nAddRoundRect","(JFFFF[FI)V", (void*) SkPathGlue::addRoundRect8},
- {"nAddPath","(JJFF)V", (void*) SkPathGlue::addPath__PathFF},
- {"nAddPath","(JJ)V", (void*) SkPathGlue::addPath__Path},
- {"nAddPath","(JJJ)V", (void*) SkPathGlue::addPath__PathMatrix},
- {"nOffset","(JFF)V", (void*) SkPathGlue::offset__FF},
- {"nSetLastPoint","(JFF)V", (void*) SkPathGlue::setLastPoint},
- {"nTransform","(JJJ)V", (void*) SkPathGlue::transform__MatrixPath},
- {"nTransform","(JJ)V", (void*) SkPathGlue::transform__Matrix},
- {"nOp","(JJIJ)Z", (void*) SkPathGlue::op},
- {"nApproximate", "(JF)[F", (void*) SkPathGlue::approximate},
-
- // ------- @FastNative below here ----------------------
- {"nIsRect","(JLandroid/graphics/RectF;)Z", (void*) SkPathGlue::isRect},
-
- // ------- @CriticalNative below here ------------------
- {"nReset","(J)V", (void*) SkPathGlue::reset},
- {"nRewind","(J)V", (void*) SkPathGlue::rewind},
- {"nIsEmpty","(J)Z", (void*) SkPathGlue::isEmpty},
- {"nIsConvex","(J)Z", (void*) SkPathGlue::isConvex},
- {"nGetFillType","(J)I", (void*) SkPathGlue::getFillType},
- {"nSetFillType","(JI)V", (void*) SkPathGlue::setFillType},
+ {"nInit", "()J", (void*)SkPathGlue::init},
+ {"nInit", "(J)J", (void*)SkPathGlue::init_Path},
+ {"nGetFinalizer", "()J", (void*)SkPathGlue::getFinalizer},
+ {"nSet", "(JJ)V", (void*)SkPathGlue::set},
+ {"nComputeBounds", "(JLandroid/graphics/RectF;)V", (void*)SkPathGlue::computeBounds},
+ {"nIncReserve", "(JI)V", (void*)SkPathGlue::incReserve},
+ {"nMoveTo", "(JFF)V", (void*)SkPathGlue::moveTo__FF},
+ {"nRMoveTo", "(JFF)V", (void*)SkPathGlue::rMoveTo},
+ {"nLineTo", "(JFF)V", (void*)SkPathGlue::lineTo__FF},
+ {"nRLineTo", "(JFF)V", (void*)SkPathGlue::rLineTo},
+ {"nQuadTo", "(JFFFF)V", (void*)SkPathGlue::quadTo__FFFF},
+ {"nRQuadTo", "(JFFFF)V", (void*)SkPathGlue::rQuadTo},
+ {"nConicTo", "(JFFFFF)V", (void*)SkPathGlue::conicTo},
+ {"nRConicTo", "(JFFFFF)V", (void*)SkPathGlue::rConicTo},
+ {"nCubicTo", "(JFFFFFF)V", (void*)SkPathGlue::cubicTo__FFFFFF},
+ {"nRCubicTo", "(JFFFFFF)V", (void*)SkPathGlue::rCubicTo},
+ {"nArcTo", "(JFFFFFFZ)V", (void*)SkPathGlue::arcTo},
+ {"nClose", "(J)V", (void*)SkPathGlue::close},
+ {"nAddRect", "(JFFFFI)V", (void*)SkPathGlue::addRect},
+ {"nAddOval", "(JFFFFI)V", (void*)SkPathGlue::addOval},
+ {"nAddCircle", "(JFFFI)V", (void*)SkPathGlue::addCircle},
+ {"nAddArc", "(JFFFFFF)V", (void*)SkPathGlue::addArc},
+ {"nAddRoundRect", "(JFFFFFFI)V", (void*)SkPathGlue::addRoundRectXY},
+ {"nAddRoundRect", "(JFFFF[FI)V", (void*)SkPathGlue::addRoundRect8},
+ {"nAddPath", "(JJFF)V", (void*)SkPathGlue::addPath__PathFF},
+ {"nAddPath", "(JJ)V", (void*)SkPathGlue::addPath__Path},
+ {"nAddPath", "(JJJ)V", (void*)SkPathGlue::addPath__PathMatrix},
+ {"nInterpolate", "(JJFJ)Z", (void*)SkPathGlue::interpolate},
+ {"nOffset", "(JFF)V", (void*)SkPathGlue::offset__FF},
+ {"nSetLastPoint", "(JFF)V", (void*)SkPathGlue::setLastPoint},
+ {"nTransform", "(JJJ)V", (void*)SkPathGlue::transform__MatrixPath},
+ {"nTransform", "(JJ)V", (void*)SkPathGlue::transform__Matrix},
+ {"nOp", "(JJIJ)Z", (void*)SkPathGlue::op},
+ {"nApproximate", "(JF)[F", (void*)SkPathGlue::approximate},
+
+ // ------- @FastNative below here ----------------------
+ {"nIsRect", "(JLandroid/graphics/RectF;)Z", (void*)SkPathGlue::isRect},
+
+ // ------- @CriticalNative below here ------------------
+ {"nGetGenerationID", "(J)I", (void*)SkPathGlue::getGenerationID},
+ {"nIsInterpolatable", "(JJ)Z", (void*)SkPathGlue::isInterpolatable},
+ {"nReset", "(J)V", (void*)SkPathGlue::reset},
+ {"nRewind", "(J)V", (void*)SkPathGlue::rewind},
+ {"nIsEmpty", "(J)Z", (void*)SkPathGlue::isEmpty},
+ {"nIsConvex", "(J)Z", (void*)SkPathGlue::isConvex},
+ {"nGetFillType", "(J)I", (void*)SkPathGlue::getFillType},
+ {"nSetFillType", "(JI)V", (void*)SkPathGlue::setFillType},
};
int register_android_graphics_Path(JNIEnv* env) {
diff --git a/libs/hwui/jni/PathIterator.cpp b/libs/hwui/jni/PathIterator.cpp
new file mode 100644
index 000000000000..3884342d8d37
--- /dev/null
+++ b/libs/hwui/jni/PathIterator.cpp
@@ -0,0 +1,81 @@
+/* libs/android_runtime/android/graphics/PathMeasure.cpp
+**
+** Copyright 2007, 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 <log/log.h>
+
+#include "GraphicsJNI.h"
+#include "SkPath.h"
+#include "SkPoint.h"
+
+namespace android {
+
+class SkPathIteratorGlue {
+public:
+ static void finalizer(SkPath::RawIter* obj) { delete obj; }
+
+ static jlong getFinalizer(JNIEnv* env, jclass clazz) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&finalizer));
+ }
+
+ static jlong create(JNIEnv* env, jobject clazz, jlong pathHandle) {
+ const SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+ return reinterpret_cast<jlong>(new SkPath::RawIter(*path));
+ }
+
+ // ---------------- @CriticalNative -------------------------
+
+ static jint peek(CRITICAL_JNI_PARAMS_COMMA jlong iteratorHandle) {
+ SkPath::RawIter* iterator = reinterpret_cast<SkPath::RawIter*>(iteratorHandle);
+ return iterator->peek();
+ }
+
+ static jint next(CRITICAL_JNI_PARAMS_COMMA jlong iteratorHandle, jlong pointsArray) {
+ static_assert(SkPath::kMove_Verb == 0, "SkPath::Verb unexpected index");
+ static_assert(SkPath::kLine_Verb == 1, "SkPath::Verb unexpected index");
+ static_assert(SkPath::kQuad_Verb == 2, "SkPath::Verb unexpected index");
+ static_assert(SkPath::kConic_Verb == 3, "SkPath::Verb unexpected index");
+ static_assert(SkPath::kCubic_Verb == 4, "SkPath::Verb unexpected index");
+ static_assert(SkPath::kClose_Verb == 5, "SkPath::Verb unexpected index");
+ static_assert(SkPath::kDone_Verb == 6, "SkPath::Verb unexpected index");
+
+ SkPath::RawIter* iterator = reinterpret_cast<SkPath::RawIter*>(iteratorHandle);
+ float* points = reinterpret_cast<float*>(pointsArray);
+ SkPath::Verb verb =
+ static_cast<SkPath::Verb>(iterator->next(reinterpret_cast<SkPoint*>(points)));
+ if (verb == SkPath::kConic_Verb) {
+ float weight = iterator->conicWeight();
+ points[6] = weight;
+ }
+ return static_cast<int>(verb);
+ }
+};
+
+static const JNINativeMethod methods[] = {
+ {"nCreate", "(J)J", (void*)SkPathIteratorGlue::create},
+ {"nGetFinalizer", "()J", (void*)SkPathIteratorGlue::getFinalizer},
+
+ // ------- @CriticalNative below here ------------------
+
+ {"nPeek", "(J)I", (void*)SkPathIteratorGlue::peek},
+ {"nNext", "(JJ)I", (void*)SkPathIteratorGlue::next},
+};
+
+int register_android_graphics_PathIterator(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "android/graphics/PathIterator", methods, NELEM(methods));
+}
+
+} // namespace android