Merge "Add APIs to make Android Path queryable and more useful"

This commit is contained in:
Chet Haase
2022-08-29 16:38:40 +00:00
committed by Android (Google) Code Review
9 changed files with 761 additions and 94 deletions

View File

@@ -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);
}
}

View File

@@ -15124,7 +15124,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);
@@ -15147,13 +15147,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);
@@ -15161,6 +15166,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);
@@ -15209,6 +15215,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);

View File

@@ -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(
@@ -91,6 +91,17 @@ public class Path {
nSet(mNativePath, src.mNativePath);
}
/**
* 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.
*
@@ -382,6 +393,49 @@ public class Path {
nRQuadTo(mNativePath, dx1, dy1, dx2, dy2);
}
/**
* 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
@@ -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 ------------------------
@@ -785,6 +885,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

View File

@@ -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);
}

View File

@@ -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",

View File

@@ -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)},

View File

@@ -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),
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),
};
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

View File

@@ -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},
{"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},
// ------- @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},
// ------- @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) {

View File

@@ -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