diff options
-rw-r--r-- | apct-tests/perftests/core/src/android/graphics/perftests/PathIteratorPerfTest.java | 112 | ||||
-rw-r--r-- | core/api/current.txt | 29 | ||||
-rw-r--r-- | graphics/java/android/graphics/Path.java | 106 | ||||
-rw-r--r-- | graphics/java/android/graphics/PathIterator.java | 303 | ||||
-rw-r--r-- | libs/hwui/Android.bp | 1 | ||||
-rw-r--r-- | libs/hwui/apex/LayoutlibLoader.cpp | 2 | ||||
-rw-r--r-- | libs/hwui/apex/jni_runtime.cpp | 108 | ||||
-rw-r--r-- | libs/hwui/jni/Path.cpp | 119 | ||||
-rw-r--r-- | libs/hwui/jni/PathIterator.cpp | 81 |
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 |