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