am 7eddc21b: Merge "CameraITS: check the reported crop region" into lmp-sprout-dev

* commit '7eddc21b70584fdede5af2b9d060deae63ee76f8':
  CameraITS: check the reported crop region
  Add tests for non-rectangular clipping
diff --git a/apps/CameraITS/pymodules/its/caps.py b/apps/CameraITS/pymodules/its/caps.py
index 6caebc0..b27e75e 100644
--- a/apps/CameraITS/pymodules/its/caps.py
+++ b/apps/CameraITS/pymodules/its/caps.py
@@ -152,6 +152,18 @@
     """
     return manual_sensor(props) and manual_post_proc(props)
 
+def freeform_crop(props):
+    """Returns whether a device supports freefrom cropping.
+
+    Args:
+        props: Camera properties object.
+
+    Return:
+        Boolean.
+    """
+    return props.has_key("android.scaler.croppingType") and \
+           props["android.scaler.croppingType"] == 1
+
 class __UnitTest(unittest.TestCase):
     """Run a suite of unit tests on this module.
     """
diff --git a/apps/CameraITS/pymodules/its/objects.py b/apps/CameraITS/pymodules/its/objects.py
index d11ef84..809a98a 100644
--- a/apps/CameraITS/pymodules/its/objects.py
+++ b/apps/CameraITS/pymodules/its/objects.py
@@ -159,6 +159,25 @@
     req = manual_capture_request(s,e)
     return req, out_spec
 
+def get_max_digital_zoom(props):
+    """Returns the maximum amount of zooming possible by the camera device.
+
+    Args:
+        props: the object returned from its.device.get_camera_properties().
+
+    Return:
+        A float indicating the maximum amount of zooming possible by the
+        camera device.
+    """
+
+    maxz = 1.0
+
+    if props.has_key("android.scaler.availableMaxDigitalZoom"):
+        maxz = props["android.scaler.availableMaxDigitalZoom"]
+
+    return maxz
+
+
 class __UnitTest(unittest.TestCase):
     """Run a suite of unit tests on this module.
     """
diff --git a/apps/CameraITS/tests/scene1/test_crop_region_raw.py b/apps/CameraITS/tests/scene1/test_crop_region_raw.py
index 94c8e2b..07343dd 100644
--- a/apps/CameraITS/tests/scene1/test_crop_region_raw.py
+++ b/apps/CameraITS/tests/scene1/test_crop_region_raw.py
@@ -20,12 +20,32 @@
 import numpy
 import os.path
 
+
+def check_crop_region(expected, reported, active, err_threshold):
+    """Check if the reported region is within the tolerance.
+
+    Args:
+        expected: expected crop region
+        reported: reported crop region
+        active: active resolution
+        err_threshold: error threshold for the active resolution
+    """
+
+    ex = (active["right"] - active["left"]) * err_threshold
+    ey = (active["bottom"] - active["top"]) * err_threshold
+
+    assert ((abs(expected["left"] - reported["left"]) <= ex) and
+            (abs(expected["right"] - reported["right"]) <= ex) and
+            (abs(expected["top"] - reported["top"]) <= ey) and
+            (abs(expected["bottom"] - reported["bottom"]) <= ey))
+
 def main():
     """Test that raw streams are not croppable.
     """
     NAME = os.path.basename(__file__).split(".")[0]
 
     DIFF_THRESH = 0.05
+    CROP_REGION_ERROR_THRESHOLD = 0.01
 
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
@@ -39,6 +59,13 @@
         aw, ah = a["right"] - a["left"], a["bottom"] - a["top"]
         print "Active sensor region: (%d,%d %dx%d)" % (ax, ay, aw, ah)
 
+        full_region = {
+            "left": 0,
+            "top": 0,
+            "right": aw,
+            "bottom": ah
+        }
+
         # Capture without a crop region.
         # Use a manual request with a linear tonemap so that the YUV and RAW
         # should look the same (once converted by the its.image module).
@@ -46,31 +73,42 @@
         req = its.objects.manual_capture_request(s,e, True)
         cap1_raw, cap1_yuv = cam.do_capture(req, cam.CAP_RAW_YUV)
 
-        # Capture with a center crop region.
+        # Calculate a center crop region.
+        zoom = min(3.0, its.objects.get_max_digital_zoom(props))
+        assert(zoom >= 1)
+        cropw = aw / zoom
+        croph = ah / zoom
+
         req["android.scaler.cropRegion"] = {
-                "top": ay + ah/3,
-                "left": ax + aw/3,
-                "right": ax + 2*aw/3,
-                "bottom": ay + 2*ah/3}
+            "left": aw / 2 - cropw / 2,
+            "top": ah / 2 - croph / 2,
+            "right": aw / 2 + cropw / 2,
+            "bottom": ah / 2 + croph / 2
+        }
+
+        # when both YUV and RAW are requested, the crop region that's
+        # applied to YUV should be reported.
+        crop_region = req["android.scaler.cropRegion"]
+        if crop_region == full_region:
+            crop_region_err_thresh = 0.0
+        else:
+            crop_region_err_thresh = CROP_REGION_ERROR_THRESHOLD
+
         cap2_raw, cap2_yuv = cam.do_capture(req, cam.CAP_RAW_YUV)
 
-        reported_crops = []
         imgs = {}
-        for s,cap in [("yuv_full",cap1_yuv), ("raw_full",cap1_raw),
-                ("yuv_crop",cap2_yuv), ("raw_crop",cap2_raw)]:
+        for s, cap, cr, err_delta in [("yuv_full", cap1_yuv, full_region, 0),
+                      ("raw_full", cap1_raw, full_region, 0),
+                      ("yuv_crop", cap2_yuv, crop_region, crop_region_err_thresh),
+                      ("raw_crop", cap2_raw, crop_region, crop_region_err_thresh)]:
             img = its.image.convert_capture_to_rgb_image(cap, props=props)
             its.image.write_image(img, "%s_%s.jpg" % (NAME, s))
             r = cap["metadata"]["android.scaler.cropRegion"]
-            x, y = a["left"], a["top"]
-            w, h = a["right"] - a["left"], a["bottom"] - a["top"]
-            reported_crops.append((x,y,w,h))
+            x, y = r["left"], r["top"]
+            w, h = r["right"] - r["left"], r["bottom"] - r["top"]
             imgs[s] = img
-            print "Crop on %s: (%d,%d %dx%d)" % (s, x,y,w,h)
-
-        # The metadata should report uncropped for all shots (since there is
-        # at least 1 uncropped stream in each case).
-        for (x,y,w,h) in reported_crops:
-            assert((ax,ay,aw,ah) == (x,y,w,h))
+            print "Crop on %s: (%d,%d %dx%d)" % (s, x, y, w, h)
+            check_crop_region(cr, r, a, err_delta)
 
         # Also check the image content; 3 of the 4 shots should match.
         # Note that all the shots are RGB below; the variable names correspond
diff --git a/apps/CameraITS/tests/scene1/test_crop_regions.py b/apps/CameraITS/tests/scene1/test_crop_regions.py
index da0cd0a..5824363 100644
--- a/apps/CameraITS/tests/scene1/test_crop_regions.py
+++ b/apps/CameraITS/tests/scene1/test_crop_regions.py
@@ -35,7 +35,8 @@
 
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
-        if not its.caps.compute_target_exposure(props):
+        if (not its.caps.compute_target_exposure(props) or
+            not its.caps.freeform_crop(props)):
             print "Test skipped"
             return
 
@@ -46,7 +47,7 @@
         print "Active sensor region (%d,%d %dx%d)" % (ax, ay, aw, ah)
 
         # Uses a 2x digital zoom.
-        assert(props['android.scaler.availableMaxDigitalZoom'] >= 2)
+        assert(its.objects.get_max_digital_zoom(props) >= 2)
 
         # Capture a full frame.
         req = its.objects.manual_capture_request(s,e)
diff --git a/tests/tests/uirendering/res/layout/simple_rect_layout.xml b/tests/tests/uirendering/res/layout/simple_rect_layout.xml
index 24c9b6b..e64c4e9 100644
--- a/tests/tests/uirendering/res/layout/simple_rect_layout.xml
+++ b/tests/tests/uirendering/res/layout/simple_rect_layout.xml
@@ -17,11 +17,10 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:background="#f00">
+    android:layout_height="match_parent">
 
-    <View android:layout_width="180px"
-        android:layout_height="120px"
-        android:background="#0f0" />
+    <View android:layout_width="100px"
+        android:layout_height="100px"
+        android:background="#00f" />
 
 </LinearLayout>
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
index 3088142..afbad65 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
@@ -16,8 +16,6 @@
 
 package android.uirendering.cts.testclasses;
 
-import com.android.cts.uirendering.R;
-
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
@@ -31,8 +29,7 @@
 import android.uirendering.cts.bitmapverifiers.RectVerifier;
 import android.uirendering.cts.testinfrastructure.ActivityTestBase;
 import android.uirendering.cts.testinfrastructure.CanvasClient;
-import android.uirendering.cts.testinfrastructure.ViewInitializer;
-import android.view.View;
+import com.android.cts.uirendering.R;
 
 public class ExactCanvasTests extends ActivityTestBase {
     private final BitmapComparer mExactComparer = new ExactComparer();
@@ -212,14 +209,4 @@
                 .addLayout(R.layout.blue_padded_square, null)
                 .runWithVerifier(verifier);
     }
-
-    @SmallTest
-    public void testClipping() {
-        createTest().addLayout(R.layout.simple_red_layout, new ViewInitializer() {
-            @Override
-            public void intializeView(View view) {
-                view.setClipBounds(new Rect(0, 0, 50, 50));
-            }
-        }).runWithComparer(mExactComparer);
-    }
 }
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
new file mode 100644
index 0000000..8df8057
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
@@ -0,0 +1,146 @@
+package android.uirendering.cts.testclasses;
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Point;
+import android.graphics.Typeface;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.uirendering.cts.bitmapcomparers.MSSIMComparer;
+import android.uirendering.cts.bitmapverifiers.SamplePointVerifier;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.uirendering.cts.testinfrastructure.CanvasClient;
+import android.uirendering.cts.testinfrastructure.ViewInitializer;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.cts.uirendering.R;
+
+public class PathClippingTests extends ActivityTestBase {
+    // draw circle with whole in it, with stroked circle
+    static final CanvasClient sCircleDrawCanvasClient = new CanvasClient() {
+        @Override
+        public String getDebugString() {
+            return "StrokedCircleDraw";
+        }
+
+        @Override
+        public void draw(Canvas canvas, int width, int height) {
+            Paint paint = new Paint();
+            paint.setAntiAlias(false);
+            paint.setColor(Color.BLUE);
+            paint.setStyle(Paint.Style.STROKE);
+            paint.setStrokeWidth(20);
+            canvas.drawCircle(50, 50, 40, paint);
+        }
+    };
+
+    // draw circle with whole in it, by path operations + path clipping
+    static final CanvasClient sCircleClipCanvasClient = new CanvasClient() {
+        @Override
+        public String getDebugString() {
+            return "CircleClipDraw";
+        }
+
+        @Override
+        public void draw(Canvas canvas, int width, int height) {
+            canvas.save();
+
+            Path path = new Path();
+            path.addCircle(50, 50, 50, Path.Direction.CW);
+            path.addCircle(50, 50, 30, Path.Direction.CCW);
+
+            canvas.clipPath(path);
+            canvas.drawColor(Color.BLUE);
+
+            canvas.restore();
+        }
+    };
+
+    @SmallTest
+    public void testCircleWithCircle() {
+        createTest()
+                .addCanvasClient(sCircleDrawCanvasClient, false)
+                .addCanvasClient(sCircleClipCanvasClient)
+                .runWithComparer(new MSSIMComparer(0.90));
+    }
+
+    @SmallTest
+    public void testCircleWithPoints() {
+        createTest()
+                .addCanvasClient(sCircleClipCanvasClient)
+                .runWithVerifier(new SamplePointVerifier(
+                        new Point[] {
+                                // inside of circle
+                                new Point(50, 50),
+                                // on circle
+                                new Point(50 + 32, 50 + 32),
+                                // outside of circle
+                                new Point(50 + 38, 50 + 38),
+                                new Point(100, 100)
+                        },
+                        new int[] {
+                                Color.WHITE,
+                                Color.BLUE,
+                                Color.WHITE,
+                                Color.WHITE,
+                        }));
+    }
+
+    @SmallTest
+    public void testViewRotate() {
+        createTest()
+                .addLayout(R.layout.blue_padded_layout, new ViewInitializer() {
+                    @Override
+                    public void intializeView(View view) {
+                        ViewGroup rootView = (ViewGroup) view;
+                        rootView.setClipChildren(true);
+                        View childView = rootView.getChildAt(0);
+                        childView.setPivotX(50);
+                        childView.setPivotY(50);
+                        childView.setRotation(45f);
+
+                    }
+                })
+                .runWithVerifier(new SamplePointVerifier(
+                        new Point[] {
+                                // inside of rotated rect
+                                new Point(50, 50),
+                                new Point(50 + 32, 50 + 32),
+                                // outside of rotated rect
+                                new Point(50 + 38, 50 + 38),
+                                new Point(100, 100)
+                        },
+                        new int[] {
+                                Color.BLUE,
+                                Color.BLUE,
+                                Color.WHITE,
+                                Color.WHITE,
+                        }));
+    }
+
+    @SmallTest
+    public void testTextClip() {
+        createTest()
+                .addCanvasClient(new CanvasClient() {
+                    @Override
+                    public void draw(Canvas canvas, int width, int height) {
+                        canvas.save();
+
+                        Path path = new Path();
+                        path.addCircle(0, 50, 50, Path.Direction.CW);
+                        path.addCircle(100, 50, 50, Path.Direction.CW);
+                        canvas.clipPath(path);
+
+                        Paint paint = new Paint();
+                        paint.setAntiAlias(true);
+                        paint.setTextSize(100);
+                        paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
+                        canvas.drawText("STRING", 0, 100, paint);
+
+                        canvas.restore();
+                    }
+                })
+                .runWithComparer(new MSSIMComparer(0.90));
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java
index e2037f7..7a16e3c 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java
@@ -18,12 +18,12 @@
 
     public UnclippedBlueView(Context context, AttributeSet attrs, int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
-        setWillNotDraw(false);
     }
 
     public UnclippedBlueView(Context context, AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+        setWillNotDraw(false);
     }
 
     @Override