Add APIs to make Android Path queryable and more useful
The underlying Skia implementation of Path has APIs for
querying the data in a path (via SkPath::Iter). It also exposes
the ability to add conic sections (conicTo()), which is useful
since a Path can contain conics (even if it was not created with
conics directly). And it adds a method to get the generationId
from a Path, used to see whether a Path changed since the
last time that Id was retrieved. Finally, it exposes a simple
interpolate() function to allow linear interpolation between
two paths.
This CL adds PathIterator to enable querying Path data, and
adds conicTo, isInterpolatable(), and interpolate() to Path,
all of which wrap the underlying, existing Skia functionality.
Bug: 157391114
Bug: 223586753
Test: Added PathIteratorTest plus new tests on PathTest
Change-Id: Iddadfde24dd75ac04d0a2f3ebca767613af25360
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index ad9aa6c..88f73d6 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -345,6 +345,7 @@
"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 942c050..b7a1563 100644
--- a/libs/hwui/apex/LayoutlibLoader.cpp
+++ b/libs/hwui/apex/LayoutlibLoader.cpp
@@ -53,6 +53,7 @@
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 @@
{"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 e1f5abd7..39725a5 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -59,6 +59,7 @@
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 @@
};
#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
diff --git a/libs/hwui/jni/Path.cpp b/libs/hwui/jni/Path.cpp
index d67bcf2..3694ce0 100644
--- a/libs/hwui/jni/Path.cpp
+++ b/libs/hwui/jni/Path.cpp
@@ -102,6 +102,18 @@
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 @@
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 @@
// ---------------- @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 @@
};
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) {
diff --git a/libs/hwui/jni/PathIterator.cpp b/libs/hwui/jni/PathIterator.cpp
new file mode 100644
index 0000000..3884342
--- /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