Use device-space stroke width for SkDraw::drawRect() quick-reject

The stroke width needs to be CTM-adjusted when applied to device space
rects.

BUG=skia:3313
R=reed@google.com

Review URL: https://codereview.chromium.org/801353008
diff --git a/gyp/tests.gypi b/gyp/tests.gypi
index ad8525c..d59b262 100644
--- a/gyp/tests.gypi
+++ b/gyp/tests.gypi
@@ -177,6 +177,7 @@
     '../tests/RecordTest.cpp',
     '../tests/RecorderTest.cpp',
     '../tests/RecordingXfermodeTest.cpp',
+    '../tests/RectTest.cpp',
     '../tests/RefCntTest.cpp',
     '../tests/RefDictTest.cpp',
     '../tests/RegionTest.cpp',
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index 6007532..f69137f 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.cpp
@@ -735,6 +735,16 @@
     }
 }
 
+static inline SkPoint compute_stroke_size(const SkPaint& paint, const SkMatrix& matrix) {
+    SkASSERT(matrix.rectStaysRect());
+    SkASSERT(SkPaint::kFill_Style != paint.getStyle());
+
+    SkVector size;
+    SkPoint pt = { paint.getStrokeWidth(), paint.getStrokeWidth() };
+    matrix.mapVectors(&size, &pt, 1);
+    return SkPoint::Make(SkScalarAbs(size.fX), SkScalarAbs(size.fY));
+}
+
 static bool easy_rect_join(const SkPaint& paint, const SkMatrix& matrix,
                            SkPoint* strokeSize) {
     if (SkPaint::kMiter_Join != paint.getStrokeJoin() ||
@@ -742,11 +752,7 @@
         return false;
     }
 
-    SkASSERT(matrix.rectStaysRect());
-    SkPoint pt = { paint.getStrokeWidth(), paint.getStrokeWidth() };
-    matrix.mapVectors(strokeSize, &pt, 1);
-    strokeSize->fX = SkScalarAbs(strokeSize->fX);
-    strokeSize->fY = SkScalarAbs(strokeSize->fY);
+    *strokeSize = compute_stroke_size(paint, matrix);
     return true;
 }
 
@@ -822,26 +828,27 @@
     }
 
     SkRect devRect;
-    if (paintMatrix) {
-        // skip the paintMatrix when transforming the rect by the CTM
-        fMatrix->mapPoints(rect_points(devRect), rect_points(*postPaintRect), 2);
-    } else {
-        fMatrix->mapPoints(rect_points(devRect), rect_points(prePaintRect), 2);
-    }
-    // transform rect into devRect
+    const SkRect& paintRect = paintMatrix ? *postPaintRect : prePaintRect;
+    // skip the paintMatrix when transforming the rect by the CTM
+    fMatrix->mapPoints(rect_points(devRect), rect_points(paintRect), 2);
     devRect.sort();
 
     // look for the quick exit, before we build a blitter
-    SkIRect ir = devRect.roundOut();
+    SkRect bbox = devRect;
     if (paint.getStyle() != SkPaint::kFill_Style) {
         // extra space for hairlines
         if (paint.getStrokeWidth() == 0) {
-            ir.outset(1, 1);
+            bbox.outset(1, 1);
         } else {
-            SkScalar radius = SkScalarHalf(paint.getStrokeWidth());
-            ir.outset(radius, radius);
+            // For kStroke_RectType, strokeSize is already computed.
+            const SkPoint& ssize = (kStroke_RectType == rtype)
+                ? strokeSize
+                : compute_stroke_size(paint, *fMatrix);
+            bbox.outset(SkScalarHalf(ssize.x()), SkScalarHalf(ssize.y()));
         }
     }
+
+    SkIRect ir = bbox.roundOut();
     if (fRC->quickReject(ir)) {
         return;
     }
diff --git a/tests/RectTest.cpp b/tests/RectTest.cpp
new file mode 100644
index 0000000..be77a87
--- /dev/null
+++ b/tests/RectTest.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkRect.h"
+#include "Test.h"
+
+static bool has_green_pixels(const SkBitmap& bm) {
+    for (int j = 0; j < bm.height(); ++j) {
+        for (int i = 0; i < bm.width(); ++i) {
+            if (SkColorGetG(bm.getColor(i, j))) {
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+static void test_stroke_width_clipping(skiatest::Reporter* reporter) {
+    SkBitmap bm;
+    bm.allocN32Pixels(100, 10);
+    bm.eraseColor(SK_ColorTRANSPARENT);
+
+    SkCanvas canvas(bm);
+    SkPaint paint;
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setStrokeWidth(10);
+    paint.setColor(0xff00ff00);
+
+    // clip out the left half of our canvas
+    canvas.clipRect(SkRect::MakeXYWH(51, 0, 49, 100));
+
+    // no stroke bleed should be visible
+    canvas.drawRect(SkRect::MakeWH(44, 100), paint);
+    REPORTER_ASSERT(reporter, !has_green_pixels(bm));
+
+    // right stroke edge should bleed into the visible area
+    canvas.scale(2, 2);
+    canvas.drawRect(SkRect::MakeWH(22, 50), paint);
+    REPORTER_ASSERT(reporter, has_green_pixels(bm));
+}
+
+DEF_TEST(Rect, reporter) {
+    test_stroke_width_clipping(reporter);
+}