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
This commit is contained in:
Romain Guy
2013-07-29 19:17:59 -07:00
parent 93d34a61db
commit 8018c8db82
5 changed files with 214 additions and 20 deletions

View File

@@ -9432,6 +9432,8 @@ package android.graphics {
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 @@ package android.graphics {
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);
}

View File

@@ -25,6 +25,7 @@
#include <android_runtime/AndroidRuntime.h>
#include "SkPath.h"
#include "pathops/SkPathOps.h"
#include <Caches.h>
@@ -67,8 +68,7 @@ public:
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 @@ public:
}
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 @@ public:
}
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 @@ public:
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 @@ static JNINativeMethod methods[] = {
{"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) {

View File

@@ -103,21 +103,106 @@ public class Path {
}
}
/** 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 @@ public class Path {
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);
}

View File

@@ -40,6 +40,16 @@
</intent-filter>
</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"
@@ -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>

View File

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