Revert "Revert "add tiler for SkDraw""

This reverts commit 461ef7af88cc966007c464130a971ec86c803f1d.

Prev CL to SkScan_Hairline.cpp fixed the bug that caused the earlier revert.

Bug: skia:
Change-Id: Ifd9a364c7546175be292f726e19465b72196b45e
Reviewed-on: https://skia-review.googlesource.com/112723
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Mike Reed <reed@google.com>
diff --git a/gm/hugepath.cpp b/gm/hugepath.cpp
index 83dff9e..4a691b8 100644
--- a/gm/hugepath.cpp
+++ b/gm/hugepath.cpp
@@ -8,6 +8,7 @@
 #include "gm.h"
 #include "SkCanvas.h"
 #include "SkPath.h"
+#include "SkSurface.h"
 
 DEF_SIMPLE_GM(path_huge_crbug_800804, canvas, 50, 600) {
     SkPaint paint;
@@ -35,3 +36,35 @@
     }
 }
 
+// Test that we can draw into a huge surface ( > 64K ) and still retain paths and antialiasing.
+DEF_SIMPLE_GM(path_huge_aa, canvas, 200, 200) {
+    auto proc = [](SkCanvas* canvas, int w, int h) {
+        SkAutoCanvasRestore acr(canvas, true);
+
+        auto surf = SkSurface::MakeRasterN32Premul(w, h);
+        auto can = surf->getCanvas();
+
+        SkPaint paint;
+        SkPath path;
+        path.addRoundRect(SkRect::MakeXYWH(4, 4, w - 8, h - 8), 12, 12);
+
+        canvas->save();
+        canvas->clipRect(SkRect::MakeXYWH(4, 4, 64, 64));
+        can->drawPath(path, paint);
+        surf->draw(canvas, 64 - w, 0, nullptr);
+        canvas->restore();
+
+        canvas->translate(80, 0);
+        canvas->save();
+        canvas->clipRect(SkRect::MakeXYWH(4, 4, 64, 64));
+        can->clear(0);
+        paint.setAntiAlias(true);
+        can->drawPath(path, paint);
+        surf->draw(canvas, 64 - w, 0, nullptr);
+        canvas->restore();
+    };
+
+    proc(canvas, 100, 60);
+    canvas->translate(0, 80);
+    proc(canvas, 100 * 1024, 60);
+}
diff --git a/gn/core.gni b/gn/core.gni
index 12a74ae..3d1b399 100644
--- a/gn/core.gni
+++ b/gn/core.gni
@@ -106,7 +106,6 @@
   "$_src/core/SkDescriptor.h",
   "$_src/core/SkDevice.cpp",
   "$_src/core/SkDevice.h",
-  "$_src/core/SkDeviceLooper.cpp",
   "$_src/core/SkDeviceProfile.cpp",
   "$_src/core/SkDiscardableMemory.h",
   "$_src/lazy/SkDiscardableMemoryPool.cpp",
diff --git a/gn/tests.gni b/gn/tests.gni
index 50cca54..cde24b0 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -53,7 +53,6 @@
   "$_tests/DeferredDisplayListTest.cpp",
   "$_tests/DequeTest.cpp",
   "$_tests/DetermineDomainModeTest.cpp",
-  "$_tests/DeviceLooperTest.cpp",
   "$_tests/DeviceTest.cpp",
   "$_tests/DFPathRendererTest.cpp",
   "$_tests/DiscardableMemoryPoolTest.cpp",
diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp
index 3d66ba6..2197244 100644
--- a/src/core/SkBitmapDevice.cpp
+++ b/src/core/SkBitmapDevice.cpp
@@ -23,6 +23,116 @@
 #include "SkTLazy.h"
 #include "SkVertices.h"
 
+class SkDrawTiler {
+    enum {
+        // 8K is 1 too big, since 8K << supersample == 32768 which is too big for SkFixed
+        kMaxDim = 8192 - 1
+    };
+
+    SkBitmapDevice* fDevice;
+    SkPixmap        fRootPixmap;
+
+    // Used for tiling and non-tiling
+    SkDraw          fDraw;
+
+    // fCurr... are only used if fNeedTiling
+    SkMatrix        fTileMatrix;
+    SkRasterClip    fTileRC;
+    SkIPoint        fCurrOrigin, fOrigin;
+
+    bool            fDone, fNeedsTiling;
+
+public:
+    SkDrawTiler(SkBitmapDevice* dev) : fDevice(dev) {
+        // we need fDst to be set, and if we're actually drawing, to dirty the genID
+        if (!dev->accessPixels(&fRootPixmap)) {
+            // NoDrawDevice uses us (why?) so we have to catch this case w/ no pixels
+            fRootPixmap.reset(dev->imageInfo(), nullptr, 0);
+        }
+
+        fDone = false;
+        fNeedsTiling = fRootPixmap.width() > kMaxDim || fRootPixmap.height() > kMaxDim;
+        fOrigin.set(0, 0);
+        fCurrOrigin = fOrigin;
+
+        if (fNeedsTiling) {
+            // fDraw.fDst is reset each time in setupTileDraw()
+            fDraw.fMatrix = &fTileMatrix;
+            fDraw.fRC = &fTileRC;
+        } else {
+            fDraw.fDst = fRootPixmap;
+            fDraw.fMatrix = &dev->ctm();
+            fDraw.fRC = &dev->fRCStack.rc();
+        }
+    }
+
+    bool needsTiling() const { return fNeedsTiling; }
+
+    const SkDraw* next() {
+        if (fDone) {
+            return nullptr;
+        }
+        if (fNeedsTiling) {
+            do {
+                this->setupTileDraw();  // might set the clip to empty
+                this->stepOrigin();     // might set fDone to true
+            } while (!fDone && fTileRC.isEmpty());
+            // if we exit the loop and we're still empty, we're (past) done
+            if (fTileRC.isEmpty()) {
+                SkASSERT(fDone);
+                return nullptr;
+            }
+            SkASSERT(!fTileRC.isEmpty());
+        } else {
+            fDone = true;   // only draw untiled once
+        }
+        return &fDraw;
+    }
+
+    int curr_x() const { return fCurrOrigin.x(); }
+    int curr_y() const { return fCurrOrigin.y(); }
+
+private:
+    void setupTileDraw() {
+        SkASSERT(!fDone);
+        SkIRect bounds = SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(), kMaxDim, kMaxDim);
+        SkASSERT(!bounds.isEmpty());
+        bool success = fRootPixmap.extractSubset(&fDraw.fDst, bounds);
+        SkASSERT_RELEASE(success);
+        // now don't use bounds, since fDst has the clipped dimensions.
+
+        fTileMatrix = fDevice->ctm();
+        fTileMatrix.postTranslate(SkIntToScalar(-fOrigin.x()), SkIntToScalar(-fOrigin.y()));
+        fDevice->fRCStack.rc().translate(-fOrigin.x(), -fOrigin.y(), &fTileRC);
+        fTileRC.op(SkIRect::MakeWH(fDraw.fDst.width(), fDraw.fDst.height()),
+                   SkRegion::kIntersect_Op);
+
+        fCurrOrigin = fOrigin;
+    }
+
+    void stepOrigin() {
+        SkASSERT(!fDone);
+        SkASSERT(fNeedsTiling);
+        fOrigin.fX += kMaxDim;
+        if (fOrigin.fX >= fRootPixmap.width()) {    // too far
+            fOrigin.fX = 0;
+            fOrigin.fY += kMaxDim;
+            if (fOrigin.fY >= fRootPixmap.height()) {
+                fDone = true;   // way too far
+            }
+        }
+    }
+};
+
+#define LOOP_TILER(code)                                    \
+    SkDrawTiler priv_tiler(this);                           \
+    while (const SkDraw* priv_draw = priv_tiler.next()) {   \
+        priv_draw->code;                                    \
+    }
+#define TILER_X(x)  (x) - priv_tiler.curr_x()
+#define TILER_Y(y)  (y) - priv_tiler.curr_y()
+
+
 class SkColorTable;
 
 static bool valid_for_bitmap_device(const SkImageInfo& info,
@@ -173,30 +283,17 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-class SkBitmapDevice::BDDraw : public SkDraw {
-public:
-    BDDraw(SkBitmapDevice* dev) {
-        // we need fDst to be set, and if we're actually drawing, to dirty the genID
-        if (!dev->accessPixels(&fDst)) {
-            // NoDrawDevice uses us (why?) so we have to catch this case w/ no pixels
-            fDst.reset(dev->imageInfo(), nullptr, 0);
-        }
-        fMatrix = &dev->ctm();
-        fRC = &dev->fRCStack.rc();
-    }
-};
-
 void SkBitmapDevice::drawPaint(const SkPaint& paint) {
-    BDDraw(this).drawPaint(paint);
+    LOOP_TILER( drawPaint(paint))
 }
 
 void SkBitmapDevice::drawPoints(SkCanvas::PointMode mode, size_t count,
                                 const SkPoint pts[], const SkPaint& paint) {
-    BDDraw(this).drawPoints(mode, count, pts, paint, nullptr);
+    LOOP_TILER( drawPoints(mode, count, pts, paint, nullptr))
 }
 
 void SkBitmapDevice::drawRect(const SkRect& r, const SkPaint& paint) {
-    BDDraw(this).drawRect(r, paint);
+    LOOP_TILER( drawRect(r, paint))
 }
 
 void SkBitmapDevice::drawOval(const SkRect& oval, const SkPaint& paint) {
@@ -216,21 +313,27 @@
     // required to override drawRRect.
     this->drawPath(path, paint, nullptr, true);
 #else
-    BDDraw(this).drawRRect(rrect, paint);
+    LOOP_TILER( drawRRect(rrect, paint))
 #endif
 }
 
 void SkBitmapDevice::drawPath(const SkPath& path,
                               const SkPaint& paint, const SkMatrix* prePathMatrix,
                               bool pathIsMutable) {
-    BDDraw(this).drawPath(path, paint, prePathMatrix, pathIsMutable);
+    SkDrawTiler tiler(this);
+    if (tiler.needsTiling()) {
+        pathIsMutable = false;
+    }
+    while (const SkDraw* draw = tiler.next()) {
+        draw->drawPath(path, paint, prePathMatrix, pathIsMutable);
+    }
 }
 
 void SkBitmapDevice::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
                                 const SkPaint& paint) {
     SkMatrix matrix = SkMatrix::MakeTrans(x, y);
     LogDrawScaleFactor(SkMatrix::Concat(this->ctm(), matrix), paint.getFilterQuality());
-    BDDraw(this).drawBitmap(bitmap, matrix, nullptr, paint);
+    LOOP_TILER( drawBitmap(bitmap, matrix, nullptr, paint))
 }
 
 static inline bool CanApplyDstMatrixAsCTM(const SkMatrix& m, const SkPaint& paint) {
@@ -325,7 +428,7 @@
         // matrix with the CTM, and try to call drawSprite if it can. If not,
         // it will make a shader and call drawRect, as we do below.
         if (CanApplyDstMatrixAsCTM(matrix, paint)) {
-            BDDraw(this).drawBitmap(*bitmapPtr, matrix, dstPtr, paint);
+            LOOP_TILER( drawBitmap(*bitmapPtr, matrix, dstPtr, paint))
             return;
         }
     }
@@ -354,25 +457,25 @@
 }
 
 void SkBitmapDevice::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& paint) {
-    BDDraw(this).drawSprite(bitmap, x, y, paint);
+    LOOP_TILER( drawSprite(bitmap, TILER_X(x), TILER_Y(y), paint))
 }
 
 void SkBitmapDevice::drawText(const void* text, size_t len,
                               SkScalar x, SkScalar y, const SkPaint& paint) {
-    BDDraw(this).drawText((const char*)text, len, x, y, paint, &fSurfaceProps);
+    LOOP_TILER( drawText((const char*)text, len, x, y, paint, &fSurfaceProps))
 }
 
 void SkBitmapDevice::drawPosText(const void* text, size_t len, const SkScalar xpos[],
                                  int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) {
-    BDDraw(this).drawPosText((const char*)text, len, xpos, scalarsPerPos, offset, paint,
-                             &fSurfaceProps);
+    LOOP_TILER( drawPosText((const char*)text, len, xpos, scalarsPerPos, offset, paint,
+                            &fSurfaceProps))
 }
 
 void SkBitmapDevice::drawVertices(const SkVertices* vertices, SkBlendMode bmode,
                                   const SkPaint& paint) {
-    BDDraw(this).drawVertices(vertices->mode(), vertices->vertexCount(), vertices->positions(),
-                              vertices->texCoords(), vertices->colors(), bmode,
-                              vertices->indices(), vertices->indexCount(), paint);
+    LOOP_TILER( drawVertices(vertices->mode(), vertices->vertexCount(), vertices->positions(),
+                             vertices->texCoords(), vertices->colors(), bmode,
+                             vertices->indices(), vertices->indexCount(), paint))
 }
 
 void SkBitmapDevice::drawDevice(SkBaseDevice* device, int x, int y, const SkPaint& origPaint) {
@@ -384,7 +487,8 @@
         paint.writable()->setMaskFilter(paint->getMaskFilter()->makeWithLocalMatrix(this->ctm()));
     }
 
-    BDDraw(this).drawSprite(static_cast<SkBitmapDevice*>(device)->fBitmap, x, y, *paint);
+    LOOP_TILER( drawSprite(static_cast<SkBitmapDevice*>(device)->fBitmap,
+                           TILER_X(x), TILER_Y(y), *paint))
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkBitmapDevice.h b/src/core/SkBitmapDevice.h
index b890066..dbe04fa 100644
--- a/src/core/SkBitmapDevice.h
+++ b/src/core/SkBitmapDevice.h
@@ -142,6 +142,7 @@
     friend struct DeviceCM; //for setMatrixClip
     friend class SkDraw;
     friend class SkDrawIter;
+    friend class SkDrawTiler;
     friend class SkDeviceFilteredPaint;
     friend class SkSurface_Raster;
     friend class SkThreadedBMPDevice; // to copy fRCStack
diff --git a/src/core/SkDeviceLooper.cpp b/src/core/SkDeviceLooper.cpp
deleted file mode 100644
index cea9270..0000000
--- a/src/core/SkDeviceLooper.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkDeviceLooper.h"
-
-SkDeviceLooper::SkDeviceLooper(const SkPixmap& base, const SkRasterClip& rc, const SkIRect& bounds,
-                               bool aa)
-    : fBaseDst(base)
-    , fBaseRC(rc)
-    , fDelta(aa ? kAA_Delta : kBW_Delta)
-{
-    // sentinels that next() has not yet been called, and so our mapper functions
-    // should not be called either.
-    fCurrDst = nullptr;
-    fCurrRC = nullptr;
-
-    if (!rc.isEmpty()) {
-        // clip must be contained by the bitmap
-        SkASSERT(SkIRect::MakeWH(base.width(), base.height()).contains(rc.getBounds()));
-    }
-
-    if (rc.isEmpty() || !fClippedBounds.intersect(bounds, rc.getBounds())) {
-        fState = kDone_State;
-    } else if (this->fitsInDelta(fClippedBounds)) {
-        fState = kSimple_State;
-    } else {
-        // back up by 1 DX, so that next() will put us in a correct starting
-        // position.
-        fCurrOffset.set(fClippedBounds.left() - fDelta,
-                        fClippedBounds.top());
-        fState = kComplex_State;
-    }
-}
-
-SkDeviceLooper::~SkDeviceLooper() {}
-
-void SkDeviceLooper::mapRect(SkRect* dst, const SkRect& src) const {
-    SkASSERT(kDone_State != fState);
-    SkASSERT(fCurrDst);
-    SkASSERT(fCurrRC);
-
-    *dst = src;
-    dst->offset(SkIntToScalar(-fCurrOffset.fX),
-                SkIntToScalar(-fCurrOffset.fY));
-}
-
-void SkDeviceLooper::mapMatrix(SkMatrix* dst, const SkMatrix& src) const {
-    SkASSERT(kDone_State != fState);
-    SkASSERT(fCurrDst);
-    SkASSERT(fCurrRC);
-
-    *dst = src;
-    dst->postTranslate(SkIntToScalar(-fCurrOffset.fX), SkIntToScalar(-fCurrOffset.fY));
-}
-
-bool SkDeviceLooper::computeCurrBitmapAndClip() {
-    SkASSERT(kComplex_State == fState);
-
-    SkIRect r = SkIRect::MakeXYWH(fCurrOffset.x(), fCurrOffset.y(),
-                                  fDelta, fDelta);
-    if (!fBaseDst.extractSubset(&fSubsetDst, r)) {
-        fSubsetRC.setEmpty();
-    } else {
-        fBaseRC.translate(-r.left(), -r.top(), &fSubsetRC);
-        (void)fSubsetRC.op(SkIRect::MakeWH(fDelta, fDelta), SkRegion::kIntersect_Op);
-    }
-
-    fCurrDst = &fSubsetDst;
-    fCurrRC = &fSubsetRC;
-    return !fCurrRC->isEmpty();
-}
-
-static bool next_tile(const SkIRect& boundary, int delta, SkIPoint* offset) {
-    // can we move to the right?
-    if (offset->x() + delta < boundary.right()) {
-        offset->fX += delta;
-        return true;
-    }
-
-    // reset to the left, but move down a row
-    offset->fX = boundary.left();
-    if (offset->y() + delta < boundary.bottom()) {
-        offset->fY += delta;
-        return true;
-    }
-
-    // offset is now outside of boundary, so we're done
-    return false;
-}
-
-bool SkDeviceLooper::next() {
-    switch (fState) {
-        case kDone_State:
-            // in theory, we should not get called here, since we must have
-            // previously returned false, but we check anyway.
-            break;
-
-        case kSimple_State:
-            // first time for simple
-            if (nullptr == fCurrDst) {
-                fCurrDst = &fBaseDst;
-                fCurrRC = &fBaseRC;
-                fCurrOffset.set(0, 0);
-                return true;
-            }
-            // 2nd time for simple, we are done
-            break;
-
-        case kComplex_State:
-            // need to propogate fCurrOffset through clippedbounds
-            // left to right, until we wrap around and move down
-
-            while (next_tile(fClippedBounds, fDelta, &fCurrOffset)) {
-                if (this->computeCurrBitmapAndClip()) {
-                    return true;
-                }
-            }
-            break;
-    }
-    fState = kDone_State;
-    return false;
-}
diff --git a/src/core/SkDeviceLooper.h b/src/core/SkDeviceLooper.h
deleted file mode 100644
index dd346d7..0000000
--- a/src/core/SkDeviceLooper.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkDeviceLooper_DEFINED
-#define SkDeviceLooper_DEFINED
-
-#include "SkBitmap.h"
-#include "SkMatrix.h"
-#include "SkRasterClip.h"
-
-/**
- *  Helper class to manage "tiling" a large coordinate space into managable
- *  chunks, where managable means areas that are <= some max critical coordinate
- *  size.
- *
- *  The constructor takes an antialiasing bool, which affects what this maximum
- *  allowable size is: If we're drawing BW, then we need coordinates to stay
- *  safely within fixed-point range (we use +- 16K, to give ourselves room to
- *  add/subtract two fixed values and still be in range. If we're drawing AA,
- *  then we reduce that size by the amount that the supersampler scan converter
- *  needs (at the moment, that is 4X, so the "safe" range is +- 4K).
- *
- *  For performance reasons, the class first checks to see if any help is needed
- *  at all, and if not (i.e. the specified bounds and base bitmap area already
- *  in the safe-zone, then the class does nothing (effectively).
- */
-class SkDeviceLooper {
-public:
-    SkDeviceLooper(const SkPixmap& base, const SkRasterClip&, const SkIRect& bounds, bool aa);
-    ~SkDeviceLooper();
-
-    const SkPixmap& getPixmap() const {
-        SkASSERT(kDone_State != fState);
-        SkASSERT(fCurrDst);
-        return *fCurrDst;
-    }
-
-    const SkRasterClip& getRC() const {
-        SkASSERT(kDone_State != fState);
-        SkASSERT(fCurrRC);
-        return *fCurrRC;
-    }
-
-    void mapRect(SkRect* dst, const SkRect& src) const;
-    void mapMatrix(SkMatrix* dst, const SkMatrix& src) const;
-
-    /**
-     *  Call next to setup the looper to return a valid coordinate chunk.
-     *  Each time this returns true, it is safe to call mapRect() and
-     *  mapMatrix(), to convert from "global" coordinate values to ones that
-     *  are local to this chunk.
-     *
-     *  When next() returns false, the list of chunks is done, and mapRect()
-     *  and mapMatrix() should no longer be called.
-     */
-    bool next();
-
-private:
-    const SkPixmap&     fBaseDst;
-    const SkRasterClip& fBaseRC;
-
-    enum State {
-        kDone_State,    // iteration is complete, getters will assert
-        kSimple_State,  // no translate/clip mods needed
-        kComplex_State
-    };
-
-    // storage for our tiled versions. Perhaps could use SkTLazy
-    SkPixmap            fSubsetDst;
-    SkRasterClip        fSubsetRC;
-
-    const SkPixmap*     fCurrDst;
-    const SkRasterClip* fCurrRC;
-    SkIRect             fClippedBounds;
-    SkIPoint            fCurrOffset;
-    int                 fDelta;
-    State               fState;
-
-    enum Delta {
-        kBW_Delta = 1 << 14,        // 16K, gives room to spare for fixedpoint
-        kAA_Delta = kBW_Delta >> 2  // supersample 4x
-    };
-
-    bool fitsInDelta(const SkIRect& r) const {
-        return r.right() < fDelta && r.bottom() < fDelta;
-    }
-
-    bool computeCurrBitmapAndClip();
-};
-
-#endif
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index 21a89ae..cf06b7b 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.cpp
@@ -14,7 +14,6 @@
 #include "SkCanvas.h"
 #include "SkColorData.h"
 #include "SkDevice.h"
-#include "SkDeviceLooper.h"
 #include "SkDraw.h"
 #include "SkDrawProcs.h"
 #include "SkFindAndPlaceGlyph.h"
@@ -785,8 +784,7 @@
         }
     }
 
-    if (!SkRectPriv::MakeLargeS32().contains(bbox)) {
-        // bbox.roundOut() is undefined; use slow path.
+    if (!SkRectPriv::FitsInFixed(bbox) && rtype != kHair_RectType) {
         draw_rect_as_path(*this, prePaintRect, paint, matrix);
         return;
     }
@@ -796,45 +794,37 @@
         return;
     }
 
-    SkDeviceLooper looper(fDst, *fRC, ir, paint.isAntiAlias());
-    while (looper.next()) {
-        SkRect localDevRect;
-        looper.mapRect(&localDevRect, devRect);
-        SkMatrix localMatrix;
-        looper.mapMatrix(&localMatrix, *matrix);
+    SkAutoBlitterChoose blitterStorage(fDst, *matrix, paint);
+    const SkRasterClip& clip = *fRC;
+    SkBlitter*          blitter = blitterStorage.get();
 
-        SkAutoBlitterChoose blitterStorage(looper.getPixmap(), localMatrix, paint);
-        const SkRasterClip& clip = looper.getRC();
-        SkBlitter*          blitter = blitterStorage.get();
-
-        // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter
-        // case we are also hairline (if we've gotten to here), which devolves to
-        // effectively just kFill
-        switch (rtype) {
-            case kFill_RectType:
-                if (paint.isAntiAlias()) {
-                    SkScan::AntiFillRect(localDevRect, clip, blitter);
-                } else {
-                    SkScan::FillRect(localDevRect, clip, blitter);
-                }
-                break;
-            case kStroke_RectType:
-                if (paint.isAntiAlias()) {
-                    SkScan::AntiFrameRect(localDevRect, strokeSize, clip, blitter);
-                } else {
-                    SkScan::FrameRect(localDevRect, strokeSize, clip, blitter);
-                }
-                break;
-            case kHair_RectType:
-                if (paint.isAntiAlias()) {
-                    SkScan::AntiHairRect(localDevRect, clip, blitter);
-                } else {
-                    SkScan::HairRect(localDevRect, clip, blitter);
-                }
-                break;
-            default:
-                SkDEBUGFAIL("bad rtype");
-        }
+    // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter
+    // case we are also hairline (if we've gotten to here), which devolves to
+    // effectively just kFill
+    switch (rtype) {
+        case kFill_RectType:
+            if (paint.isAntiAlias()) {
+                SkScan::AntiFillRect(devRect, clip, blitter);
+            } else {
+                SkScan::FillRect(devRect, clip, blitter);
+            }
+            break;
+        case kStroke_RectType:
+            if (paint.isAntiAlias()) {
+                SkScan::AntiFrameRect(devRect, strokeSize, clip, blitter);
+            } else {
+                SkScan::FrameRect(devRect, strokeSize, clip, blitter);
+            }
+            break;
+        case kHair_RectType:
+            if (paint.isAntiAlias()) {
+                SkScan::AntiHairRect(devRect, clip, blitter);
+            } else {
+                SkScan::HairRect(devRect, clip, blitter);
+            }
+            break;
+        default:
+            SkDEBUGFAIL("bad rtype");
     }
 }
 
diff --git a/tests/DeviceLooperTest.cpp b/tests/DeviceLooperTest.cpp
deleted file mode 100644
index c8eb3c7..0000000
--- a/tests/DeviceLooperTest.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkAutoPixmapStorage.h"
-#include "SkDeviceLooper.h"
-#include "SkRasterClip.h"
-#include "Test.h"
-
-static void make_pm(SkAutoPixmapStorage* pixmap, int w, int h) {
-    pixmap->alloc(SkImageInfo::Make(w, h, kAlpha_8_SkColorType, kPremul_SkAlphaType));
-}
-
-static bool equal(const SkRasterClip& a, const SkRasterClip& b) {
-    if (a.isBW()) {
-        return b.isBW() && a.bwRgn() == b.bwRgn();
-    } else {
-        return a.isAA() && a.aaRgn() == b.aaRgn();
-    }
-}
-
-static const struct {
-    SkISize fDevSize;
-    SkIRect fRCBounds;
-    SkIRect fRect;
-} gRec[] = {
-    { { 4000, 10 }, { 0, 0, 4000, 10 }, { 0, 0, 4000, 4000 } },
-    { { 10, 4000 }, { 0, 0, 10, 4000 }, { 0, 0, 4000, 4000 } },
-    // very large devce, small rect
-    { { 32000, 10 }, { 0, 0, 32000, 10 }, { 0, 0, 4000, 4000 } },
-    { { 10, 32000 }, { 0, 0, 10, 32000 }, { 0, 0, 4000, 4000 } },
-    // very large device, small clip
-    { { 32000, 10 }, { 0, 0, 4000, 10 }, { 0, 0, 32000, 32000 } },
-    { { 10, 32000 }, { 0, 0, 10, 4000 }, { 0, 0, 32000, 32000 } },
-};
-
-static void test_simple(skiatest::Reporter* reporter) {
-
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
-        SkAutoPixmapStorage pmap;
-        make_pm(&pmap, gRec[i].fDevSize.width(), gRec[i].fDevSize.height());
-
-        SkRasterClip rc(gRec[i].fRCBounds);
-
-        for (int aa = 0; aa <= 1; ++aa) {
-            SkDeviceLooper looper(pmap, rc, gRec[i].fRect, SkToBool(aa));
-
-            bool valid = looper.next();
-            REPORTER_ASSERT(reporter, valid);
-            if (valid) {
-                REPORTER_ASSERT(reporter, looper.getPixmap().width() == pmap.width());
-                REPORTER_ASSERT(reporter, looper.getPixmap().height() == pmap.height());
-                REPORTER_ASSERT(reporter, equal(looper.getRC(), rc));
-
-                REPORTER_ASSERT(reporter, !looper.next());
-            }
-        }
-        // test that a rect that doesn't intersect returns no loops
-        {
-            SkIRect r = rc.getBounds();
-            r.offset(r.width(), 0);
-            SkDeviceLooper looper(pmap, rc, r, false);
-            REPORTER_ASSERT(reporter, !looper.next());
-        }
-    }
-}
-
-// mask-bits are interpreted as the areas where the clip is visible
-//  [ 0x01  0x02 ]
-//  [ 0x04  0x08 ]
-//
-static void make_rgn(SkRegion* rgn, int w, int h, unsigned mask) {
-    SkASSERT(SkAlign2(w));
-    SkASSERT(SkAlign2(h));
-    w >>= 1;
-    h >>= 1;
-    const SkIRect baseR = SkIRect::MakeWH(w, h);
-
-    int bit = 1;
-    for (int y = 0; y <= 1; ++y) {
-        for (int x = 0; x <= 1; ++x) {
-            if (mask & bit) {
-                SkIRect r = baseR;
-                r.offset(x * w, y * h);
-                rgn->op(r, SkRegion::kUnion_Op);
-            }
-            bit <<= 1;
-        }
-    }
-}
-
-static void test_complex(skiatest::Reporter* reporter) {
-    // choose size values that will result in 4 quadrants, given fAA setting
-    const int BW_SIZE = 17 * 1000;
-    const int AA_SIZE = 7 * 1000;
-
-    struct {
-        SkISize fSize;
-        bool    fAA;
-    } const gRec[] = {
-        { { BW_SIZE, BW_SIZE }, false },
-        { {  AA_SIZE, AA_SIZE }, true },
-    };
-
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
-        const int w = gRec[i].fSize.width();
-        const int h = gRec[i].fSize.height();
-
-        SkAutoPixmapStorage pmap;
-        make_pm(&pmap, w, h);
-
-        const SkIRect rect = SkIRect::MakeWH(w, h);
-
-        // mask-bits are interpreted as the areas where the clip is visible
-        //  [ 0x01  0x02 ]
-        //  [ 0x04  0x08 ]
-        //
-        for (int mask = 0; mask <= 15; ++mask) {
-            SkRegion rgn;
-            make_rgn(&rgn, w, h, mask);
-
-            SkRasterClip rc;
-            rc.op(rgn, SkRegion::kReplace_Op);
-
-            SkDeviceLooper looper(pmap, rc, rect, gRec[i].fAA);
-            while (looper.next()) {
-                REPORTER_ASSERT(reporter, !looper.getRC().isEmpty());
-            }
-        }
-    }
-}
-
-DEF_TEST(DeviceLooper, reporter) {
-    test_simple(reporter);
-    test_complex(reporter);
-}