Add path ops API
Path ops can be used to combine two paths instances in a single path
object. The following operations can be used:
- Difference
- Reverse difference
- Union
- XOR
- Intersection
To use the API:
Path p1 = createCircle();
Path p2 = createRect();
Path result = new Path();
result.op(p1, p2, Path.Op.DIFFERENCE);
This code will subtract the rectangle from the circle and generate
the resulting path in "result."
Change-Id: Ic25244665b6691a7df0b0002a09da73d937b553b
diff --git a/api/current.txt b/api/current.txt
index 6bb40e1..4bacfa8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9432,6 +9432,8 @@
method public void moveTo(float, float);
method public void offset(float, float, android.graphics.Path);
method public void offset(float, float);
+ method public boolean op(android.graphics.Path, android.graphics.Path.Op);
+ method public boolean op(android.graphics.Path, android.graphics.Path, android.graphics.Path.Op);
method public void quadTo(float, float, float, float);
method public void rCubicTo(float, float, float, float, float, float);
method public void rLineTo(float, float);
@@ -9463,6 +9465,16 @@
enum_constant public static final android.graphics.Path.FillType WINDING;
}
+ public static final class Path.Op extends java.lang.Enum {
+ method public static android.graphics.Path.Op valueOf(java.lang.String);
+ method public static final android.graphics.Path.Op[] values();
+ enum_constant public static final android.graphics.Path.Op DIFFERENCE;
+ enum_constant public static final android.graphics.Path.Op INTERSECT;
+ enum_constant public static final android.graphics.Path.Op REVERSE_DIFFERENCE;
+ enum_constant public static final android.graphics.Path.Op UNION;
+ enum_constant public static final android.graphics.Path.Op XOR;
+ }
+
public class PathDashPathEffect extends android.graphics.PathEffect {
ctor public PathDashPathEffect(android.graphics.Path, float, float, android.graphics.PathDashPathEffect.Style);
}
diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp
index eb9e004..ab7f1dc 100644
--- a/core/jni/android/graphics/Path.cpp
+++ b/core/jni/android/graphics/Path.cpp
@@ -25,6 +25,7 @@
#include <android_runtime/AndroidRuntime.h>
#include "SkPath.h"
+#include "pathops/SkPathOps.h"
#include <Caches.h>
@@ -67,8 +68,7 @@
return obj->getFillType();
}
- static void setFillType(JNIEnv* env, jobject clazz, SkPath* path,
- SkPath::FillType ft) {
+ static void setFillType(JNIEnv* env, jobject clazz, SkPath* path, SkPath::FillType ft) {
path->setFillType(ft);
}
@@ -200,7 +200,7 @@
}
static void addRoundRectXY(JNIEnv* env, jobject clazz, SkPath* obj, jobject rect,
- jfloat rx, jfloat ry, SkPath::Direction dir) {
+ jfloat rx, jfloat ry, SkPath::Direction dir) {
SkRect rect_;
GraphicsJNI::jrectf_to_rect(env, rect, &rect_);
SkScalar rx_ = SkFloatToScalar(rx);
@@ -209,7 +209,7 @@
}
static void addRoundRect8(JNIEnv* env, jobject, SkPath* obj, jobject rect,
- jfloatArray array, SkPath::Direction dir) {
+ jfloatArray array, SkPath::Direction dir) {
SkRect rect_;
GraphicsJNI::jrectf_to_rect(env, rect, &rect_);
AutoJavaFloatArray afa(env, array, 8);
@@ -261,7 +261,10 @@
static void transform__Matrix(JNIEnv* env, jobject clazz, SkPath* obj, SkMatrix* matrix) {
obj->transform(*matrix);
}
-
+
+ static jboolean op(JNIEnv* env, jobject clazz, SkPath* p1, SkPath* p2, SkPathOp op, SkPath* r) {
+ return Op(*p1, *p2, op, r);
+ }
};
static JNINativeMethod methods[] = {
@@ -301,7 +304,8 @@
{"native_offset","(IFF)V", (void*) SkPathGlue::offset__FF},
{"native_setLastPoint","(IFF)V", (void*) SkPathGlue::setLastPoint},
{"native_transform","(III)V", (void*) SkPathGlue::transform__MatrixPath},
- {"native_transform","(II)V", (void*) SkPathGlue::transform__Matrix}
+ {"native_transform","(II)V", (void*) SkPathGlue::transform__Matrix},
+ {"native_op","(IIII)Z", (void*) SkPathGlue::op}
};
int register_android_graphics_Path(JNIEnv* env) {
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index 157c7d1..ef858eb 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -103,21 +103,106 @@
}
}
- /** Enum for the ways a path may be filled
- */
+ /**
+ * The logical operations that can be performed when combining two paths.
+ *
+ * @see #op(Path, android.graphics.Path.Op)
+ * @see #op(Path, Path, android.graphics.Path.Op)
+ */
+ public enum Op {
+ /**
+ * Subtract the second path from the first path.
+ */
+ DIFFERENCE,
+ /**
+ * Intersect the two paths.
+ */
+ INTERSECT,
+ /**
+ * Union (inclusive-or) the two paths.
+ */
+ UNION,
+ /**
+ * Exclusive-or the two paths.
+ */
+ XOR,
+ /**
+ * Subtract the first path from the second path.
+ */
+ REVERSE_DIFFERENCE
+ }
+
+ /**
+ * Set this path to the result of applying the Op to this path and the specified path.
+ * The resulting path will be constructed from non-overlapping contours.
+ * The curve order is reduced where possible so that cubics may be turned
+ * into quadratics, and quadratics maybe turned into lines.
+ *
+ * @param path The second operand (for difference, the subtrahend)
+ *
+ * @return True if operation succeeded, false otherwise and this path remains unmodified.
+ *
+ * @see Op
+ * @see #op(Path, Path, android.graphics.Path.Op)
+ */
+ public boolean op(Path path, Op op) {
+ return op(this, path, op);
+ }
+
+ /**
+ * Set this path to the result of applying the Op to the two specified paths.
+ * The resulting path will be constructed from non-overlapping contours.
+ * The curve order is reduced where possible so that cubics may be turned
+ * into quadratics, and quadratics maybe turned into lines.
+ *
+ * @param path1 The first operand (for difference, the minuend)
+ * @param path2 The second operand (for difference, the subtrahend)
+ *
+ * @return True if operation succeeded, false otherwise and this path remains unmodified.
+ *
+ * @see Op
+ * @see #op(Path, android.graphics.Path.Op)
+ */
+ public boolean op(Path path1, Path path2, Op op) {
+ if (native_op(path1.mNativePath, path2.mNativePath, op.ordinal(), this.mNativePath)) {
+ isSimplePath = false;
+ rects = null;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Enum for the ways a path may be filled.
+ */
public enum FillType {
// these must match the values in SkPath.h
+ /**
+ * Specifies that "inside" is computed by a non-zero sum of signed
+ * edge crossings.
+ */
WINDING (0),
+ /**
+ * Specifies that "inside" is computed by an odd number of edge
+ * crossings.
+ */
EVEN_ODD (1),
+ /**
+ * Same as {@link #WINDING}, but draws outside of the path, rather than inside.
+ */
INVERSE_WINDING (2),
+ /**
+ * Same as {@link #EVEN_ODD}, but draws outside of the path, rather than inside.
+ */
INVERSE_EVEN_ODD(3);
FillType(int ni) {
nativeInt = ni;
}
+
final int nativeInt;
}
-
+
// these must be in the same order as their native values
static final FillType[] sFillTypeArray = {
FillType.WINDING,
@@ -644,24 +729,20 @@
private static native void native_addRect(int nPath, float left, float top,
float right, float bottom, int dir);
private static native void native_addOval(int nPath, RectF oval, int dir);
- private static native void native_addCircle(int nPath, float x, float y,
- float radius, int dir);
+ private static native void native_addCircle(int nPath, float x, float y, float radius, int dir);
private static native void native_addArc(int nPath, RectF oval,
float startAngle, float sweepAngle);
private static native void native_addRoundRect(int nPath, RectF rect,
float rx, float ry, int dir);
- private static native void native_addRoundRect(int nPath, RectF r,
- float[] radii, int dir);
- private static native void native_addPath(int nPath, int src, float dx,
- float dy);
+ private static native void native_addRoundRect(int nPath, RectF r, float[] radii, int dir);
+ private static native void native_addPath(int nPath, int src, float dx, float dy);
private static native void native_addPath(int nPath, int src);
private static native void native_addPath(int nPath, int src, int matrix);
- private static native void native_offset(int nPath, float dx, float dy,
- int dst_path);
+ private static native void native_offset(int nPath, float dx, float dy, int dst_path);
private static native void native_offset(int nPath, float dx, float dy);
private static native void native_setLastPoint(int nPath, float dx, float dy);
- private static native void native_transform(int nPath, int matrix,
- int dst_path);
+ private static native void native_transform(int nPath, int matrix, int dst_path);
private static native void native_transform(int nPath, int matrix);
+ private static native boolean native_op(int path1, int path2, int op, int result);
private static native void finalizer(int nPath);
}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index bdd8aa6..1bb0db0 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -41,6 +41,16 @@
</activity>
<activity
+ android:name="PathOpsActivity"
+ android:label="Path/Ops"
+ android:theme="@android:style/Theme.Holo.Light">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.hwui.TEST" />
+ </intent-filter>
+ </activity>
+
+ <activity
android:name="AssetsAtlasActivity"
android:label="Atlas/Framework"
android:theme="@android:style/Theme.Holo.Light">
@@ -735,7 +745,7 @@
android:name="PathsCacheActivity"
android:label="Path/Cache">
<intent-filter>
- <action android:name="android.intent.action.MAIN" />
+ <action android:name="android.intent.action.MAIN" />`
<category android:name="com.android.test.hwui.TEST" />
</intent-filter>
</activity>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PathOpsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/PathOpsActivity.java
new file mode 100644
index 0000000..b9927ac
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PathOpsActivity.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2010 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 com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class PathOpsActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final PathsView view = new PathsView(this);
+ setContentView(view);
+ }
+
+ public static class PathsView extends View {
+ private final Paint mPaint;
+ private Path[] mPaths;
+ private float mSize;
+
+
+ public PathsView(Context c) {
+ super(c);
+
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+ mPaint.setStyle(Paint.Style.FILL);
+ mPaint.setColor(Color.RED);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ Path.Op[] ops = Path.Op.values();
+ mPaths = new Path[ops.length];
+
+ mSize = w / (ops.length * 2.0f);
+
+ Path p1 = new Path();
+ p1.addRect(0.0f, 0.0f, mSize, mSize, Path.Direction.CW);
+
+ Path p2 = new Path();
+ p2.addCircle(mSize, mSize, mSize / 2.0f, Path.Direction.CW);
+
+ for (int i = 0; i < ops.length; i++) {
+ mPaths[i] = new Path();
+ if (!mPaths[i].op(p1, p2, ops[i])) {
+ Log.d("PathOps", ops[i].name() + " failed!");
+ }
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ canvas.translate(mSize * 0.2f, getHeight() / 2.0f);
+ for (Path path : mPaths) {
+ canvas.drawPath(path, mPaint);
+ canvas.translate(mSize * 1.8f, 0.0f);
+ }
+ }
+ }
+}