Pull in Chromium's version of GatherPixelRefs

https://codereview.chromium.org/134473002/



git-svn-id: http://skia.googlecode.com/svn/trunk@13038 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gyp/utils.gyp b/gyp/utils.gyp
index 608262e..ebac571 100644
--- a/gyp/utils.gyp
+++ b/gyp/utils.gyp
@@ -42,6 +42,7 @@
         '../include/utils/SkDebugUtils.h',
         '../include/utils/SkDeferredCanvas.h',
         '../include/utils/SkDumpCanvas.h',
+        '../include/utils/SkGatherPixelRefsAndRects.h',
         '../include/utils/SkInterpolator.h',
         '../include/utils/SkLayer.h',
         '../include/utils/SkMatrix44.h',
@@ -76,6 +77,7 @@
         '../src/utils/SkDeferredCanvas.cpp',
         '../src/utils/SkDumpCanvas.cpp',
         '../src/utils/SkFloatUtils.h',
+        '../src/utils/SkGatherPixelRefsAndRects.cpp',
         '../src/utils/SkInterpolator.cpp',
         '../src/utils/SkLayer.cpp',
         '../src/utils/SkMatrix44.cpp',
diff --git a/include/utils/SkPictureUtils.h b/include/utils/SkPictureUtils.h
index 5e6b051..c35aca8 100644
--- a/include/utils/SkPictureUtils.h
+++ b/include/utils/SkPictureUtils.h
@@ -9,6 +9,7 @@
 #define SkPictureUtils_DEFINED
 
 #include "SkPicture.h"
+#include "SkTDArray.h"
 
 class SkData;
 struct SkRect;
@@ -26,6 +27,57 @@
      *  and remains unchanged.
      */
     static SkData* GatherPixelRefs(SkPicture* pict, const SkRect& area);
+
+    /**
+     * SkPixelRefContainer provides a base class for more elaborate pixel ref
+     * query structures (e.g., rtrees, quad-trees, etc.)
+     */
+    class SkPixelRefContainer : public SkRefCnt {
+    public:
+        virtual void add(SkPixelRef* pr, const SkRect& rect) = 0;
+
+        // The returned array may contain duplicates
+        virtual void query(const SkRect& queryRect, SkTDArray<SkPixelRef*> *result) = 0;
+
+    private:
+        typedef SkRefCnt INHERITED;
+    };
+
+    // Simple query structure that just stores a linked list of pixel refs
+    // and rects.
+    class SkPixelRefsAndRectsList : public SkPixelRefContainer {
+    public:
+        virtual void add(SkPixelRef* pr, const SkRect& rect) SK_OVERRIDE {
+            PixelRefAndRect *dst = fArray.append();
+
+            dst->fPixelRef = pr;
+            dst->fRect = rect;
+        }
+
+        virtual void query(const SkRect& queryRect, SkTDArray<SkPixelRef*> *result) SK_OVERRIDE {
+            for (int i = 0; i < fArray.count(); ++i) {
+                if (SkRect::Intersects(fArray[i].fRect, queryRect)) {
+                    *result->append() = fArray[i].fPixelRef;
+                }
+            }
+        }
+
+    private:
+        struct PixelRefAndRect {
+            SkPixelRef* fPixelRef;
+            SkRect      fRect;
+        };
+
+        SkTDArray<PixelRefAndRect> fArray;
+
+        typedef SkPixelRefContainer INHERITED;
+    };
+
+    /**
+     *  Fill the provided pixel ref container with the picture's pixel ref
+     *  and rect information.
+     */
+    static void GatherPixelRefsAndRects(SkPicture* pict, SkPixelRefContainer* prCont);
 };
 
 #endif
diff --git a/src/utils/SkGatherPixelRefsAndRects.cpp b/src/utils/SkGatherPixelRefsAndRects.cpp
new file mode 100644
index 0000000..7b3be55
--- /dev/null
+++ b/src/utils/SkGatherPixelRefsAndRects.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkGatherPixelRefsAndRects.h"
+#include "SkNoSaveLayerCanvas.h"
+#include "SkPictureUtils.h"
+
+void SkPictureUtils::GatherPixelRefsAndRects(SkPicture* pict,
+                                             SkPictureUtils::SkPixelRefContainer* prCont) {
+    if (0 == pict->width() || 0 == pict->height()) {
+        return ;
+    }
+
+    SkGatherPixelRefsAndRectsDevice device(pict->width(), pict->height(), prCont);
+    SkNoSaveLayerCanvas canvas(&device);
+
+    canvas.clipRect(SkRect::MakeWH(SkIntToScalar(pict->width()), 
+                                   SkIntToScalar(pict->height())),
+                    SkRegion::kIntersect_Op, false);
+    canvas.drawPicture(*pict);
+}
diff --git a/src/utils/SkGatherPixelRefsAndRects.h b/src/utils/SkGatherPixelRefsAndRects.h
new file mode 100644
index 0000000..5548472
--- /dev/null
+++ b/src/utils/SkGatherPixelRefsAndRects.h
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkGatherPixelRefsAndRects_DEFINED
+#define SkGatherPixelRefsAndRects_DEFINED
+
+#include "SkBitmap.h"
+#include "SkDevice.h"
+#include "SkDraw.h"
+#include "SkPictureUtils.h"
+#include "SkRasterClip.h"
+#include "SkRefCnt.h"
+#include "SkRRect.h"
+#include "SkTypes.h"
+
+// This GatherPixelRefs device passes all discovered pixel refs and their
+// device bounds to the user provided SkPixelRefContainer-derived object
+class SkGatherPixelRefsAndRectsDevice : public SkBaseDevice {
+public:
+    SK_DECLARE_INST_COUNT(SkGatherPixelRefsAndRectsDevice)
+
+    SkGatherPixelRefsAndRectsDevice(int width, int height, 
+                                    SkPictureUtils::SkPixelRefContainer* prCont) {
+        fSize.set(width, height);
+        fPRCont = prCont;
+        SkSafeRef(fPRCont);
+        fEmptyBitmap.setConfig(SkBitmap::kNo_Config, width, height);
+    }
+
+    virtual ~SkGatherPixelRefsAndRectsDevice() {
+        SkSafeUnref(fPRCont);
+    }
+
+    virtual uint32_t getDeviceCapabilities() SK_OVERRIDE { return 0; }
+
+    virtual int width() const SK_OVERRIDE { return fSize.width(); }
+    virtual int height() const SK_OVERRIDE { return fSize.height(); }
+    virtual bool isOpaque() const SK_OVERRIDE { return false; }
+    virtual SkBitmap::Config config() const SK_OVERRIDE {
+        return SkBitmap::kNo_Config;
+    }
+    virtual void writePixels(const SkBitmap& bitmap, int x, int y,
+                             SkCanvas::Config8888 config8888) SK_OVERRIDE {
+        NotSupported();
+    }
+    virtual GrRenderTarget* accessRenderTarget() SK_OVERRIDE { return NULL; }
+
+protected:
+    virtual bool filterTextFlags(const SkPaint& paint, TextFlags*) SK_OVERRIDE {
+        return false;
+    }
+    virtual void clear(SkColor color) SK_OVERRIDE {
+        NothingToDo();
+    }
+    virtual void drawPaint(const SkDraw& draw, const SkPaint& paint) SK_OVERRIDE {
+        SkBitmap bm;
+
+        if (GetBitmapFromPaint(paint, &bm)) {
+            SkRect clipRect = SkRect::Make(draw.fRC->getBounds());
+            fPRCont->add(bm.pixelRef(), clipRect);
+        }
+    }
+    virtual void drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
+                            const SkPoint points[], const SkPaint& paint) SK_OVERRIDE {
+        SkBitmap bm;
+        if (!GetBitmapFromPaint(paint, &bm)) {
+            return;
+        }
+
+        if (0 == count) {
+            return;
+        }
+
+        SkPoint min = points[0];
+        SkPoint max = points[0];
+        for (size_t i = 1; i < count; ++i) {
+            const SkPoint& point = points[i];
+
+            min.set(SkMinScalar(min.x(), point.x()), SkMinScalar(min.y(), point.y()));
+            max.set(SkMaxScalar(max.x(), point.x()), SkMaxScalar(max.y(), point.y()));
+        }
+
+        SkRect bounds = SkRect::MakeLTRB(min.x(), min.y(), max.x()+1, max.y()+1);
+
+        this->drawRect(draw, bounds, paint);
+    }
+    virtual void drawRect(const SkDraw& draw, const SkRect& rect,
+                          const SkPaint& paint) SK_OVERRIDE {
+        SkBitmap bm;
+        if (GetBitmapFromPaint(paint, &bm)) {
+            SkRect mappedRect;
+            draw.fMatrix->mapRect(&mappedRect, rect);
+            SkRect clipRect = SkRect::Make(draw.fRC->getBounds());
+            mappedRect.intersect(clipRect);
+            fPRCont->add(bm.pixelRef(), mappedRect);
+        }
+    }
+    virtual void drawOval(const SkDraw& draw, const SkRect& rect,
+                          const SkPaint& paint) SK_OVERRIDE {
+        this->drawRect(draw, rect, paint);
+    }
+    virtual void drawRRect(const SkDraw& draw, const SkRRect& rrect,
+                           const SkPaint& paint) SK_OVERRIDE {
+        this->drawRect(draw, rrect.rect(), paint);
+    }
+    virtual void drawPath(const SkDraw& draw, const SkPath& path,
+                          const SkPaint& paint, const SkMatrix* prePathMatrix,
+                          bool pathIsMutable) SK_OVERRIDE {
+        SkBitmap bm;
+        if (!GetBitmapFromPaint(paint, &bm)) {
+            return;
+        }
+
+        SkRect pathBounds = path.getBounds();
+        if (NULL != prePathMatrix) {
+            prePathMatrix->mapRect(&pathBounds);
+        } 
+
+        this->drawRect(draw, pathBounds, paint);
+    }
+    virtual void drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
+                            const SkMatrix& matrix, const SkPaint& paint) SK_OVERRIDE {
+        SkMatrix totMatrix;
+        totMatrix.setConcat(*draw.fMatrix, matrix);
+
+        SkRect bitmapRect = SkRect::MakeWH(SkIntToScalar(bitmap.width()), 
+                                           SkIntToScalar(bitmap.height()));
+        SkRect mappedRect;
+        totMatrix.mapRect(&mappedRect, bitmapRect);
+        fPRCont->add(bitmap.pixelRef(), mappedRect);
+
+        SkBitmap paintBitmap;
+        if (GetBitmapFromPaint(paint, &paintBitmap)) {
+            fPRCont->add(paintBitmap.pixelRef(), mappedRect);
+        }
+    }
+    virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
+                            int x, int y, const SkPaint& paint) SK_OVERRIDE {
+        // Sprites aren't affected by current matrix, so we can't reuse drawRect.
+        SkMatrix matrix;
+        matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
+
+        SkRect bitmapRect = SkRect::MakeWH(SkIntToScalar(bitmap.width()), 
+                                           SkIntToScalar(bitmap.height()));
+        SkRect mappedRect;
+        matrix.mapRect(&mappedRect, bitmapRect);
+        fPRCont->add(bitmap.pixelRef(), mappedRect);
+
+        SkBitmap paintBitmap;
+        if (GetBitmapFromPaint(paint, &paintBitmap)) {
+            fPRCont->add(paintBitmap.pixelRef(), mappedRect);
+        }
+    }
+    virtual void drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
+                                const SkRect* srcOrNull, const SkRect& dst,
+                                const SkPaint& paint,
+                                SkCanvas::DrawBitmapRectFlags flags) SK_OVERRIDE {
+        SkRect bitmapRect = SkRect::MakeWH(SkIntToScalar(bitmap.width()), 
+                                           SkIntToScalar(bitmap.height()));
+        SkMatrix matrix;
+        matrix.setRectToRect(bitmapRect, dst, SkMatrix::kFill_ScaleToFit);
+        this->drawBitmap(draw, bitmap, matrix, paint);
+    }
+    virtual void drawText(const SkDraw& draw, const void* text, size_t len,
+                          SkScalar x, SkScalar y,
+                          const SkPaint& paint) SK_OVERRIDE {
+        SkBitmap bitmap;
+        if (!GetBitmapFromPaint(paint, &bitmap)) {
+            return;
+        }
+
+        // Math is borrowed from SkBBoxRecord
+        SkRect bounds;
+        paint.measureText(text, len, &bounds);
+        SkPaint::FontMetrics metrics;
+        paint.getFontMetrics(&metrics);
+
+        if (paint.isVerticalText()) {
+            SkScalar h = bounds.fBottom - bounds.fTop;
+            if (paint.getTextAlign() == SkPaint::kCenter_Align) {
+                bounds.fTop -= h / 2;
+                bounds.fBottom -= h / 2;
+            }
+            bounds.fBottom += metrics.fBottom;
+            bounds.fTop += metrics.fTop;
+        } else {
+            SkScalar w = bounds.fRight - bounds.fLeft;
+            if (paint.getTextAlign() == SkPaint::kCenter_Align) {
+                bounds.fLeft -= w / 2;
+                bounds.fRight -= w / 2;
+            } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
+                bounds.fLeft -= w;
+                bounds.fRight -= w;
+            }
+            bounds.fTop = metrics.fTop;
+            bounds.fBottom = metrics.fBottom;
+        }
+
+        SkScalar pad = (metrics.fBottom - metrics.fTop) / 2;
+        bounds.fLeft -= pad;
+        bounds.fRight += pad;
+        bounds.offset(x, y);
+
+        this->drawRect(draw, bounds, paint);
+    }
+    virtual void drawPosText(const SkDraw& draw, const void* text, size_t len,
+                             const SkScalar pos[], SkScalar constY,
+                             int scalarsPerPos, const SkPaint& paint) SK_OVERRIDE {
+        SkBitmap bitmap;
+        if (!GetBitmapFromPaint(paint, &bitmap)) {
+            return;
+        }
+
+        if (0 == len) {
+            return;
+        }
+
+        // Similar to SkDraw asserts.
+        SkASSERT(scalarsPerPos == 1 || scalarsPerPos == 2);
+
+        SkPoint min, max;
+        if (1 == scalarsPerPos) {
+            min.set(pos[0], constY);
+            max.set(pos[0], constY);
+        } else if (2 == scalarsPerPos) {
+            min.set(pos[0], constY + pos[1]);
+            max.set(pos[0], constY + pos[1]);
+        }
+
+        for (size_t i = 1; i < len; ++i) {
+            SkScalar x = pos[i * scalarsPerPos];
+            SkScalar y = constY;
+            if (2 == scalarsPerPos) {
+                y += pos[i * scalarsPerPos + 1];
+            }
+
+            min.set(SkMinScalar(x, min.x()), SkMinScalar(y, min.y()));
+            max.set(SkMaxScalar(x, max.x()), SkMaxScalar(y, max.y()));
+        }
+
+        SkRect bounds = SkRect::MakeLTRB(min.x(), min.y(), max.x(), max.y());
+
+        // Math is borrowed from SkBBoxRecord
+        SkPaint::FontMetrics metrics;
+        paint.getFontMetrics(&metrics);
+
+        bounds.fTop += metrics.fTop;
+        bounds.fBottom += metrics.fBottom;
+
+        SkScalar pad = (metrics.fTop - metrics.fBottom) / 2;
+        bounds.fLeft -= pad;
+        bounds.fRight += pad;
+
+        this->drawRect(draw, bounds, paint);
+    }
+    virtual void drawTextOnPath(const SkDraw& draw, const void* text, size_t len,
+                                const SkPath& path, const SkMatrix* matrix,
+                                const SkPaint& paint) SK_OVERRIDE {
+        SkBitmap bitmap;
+        if (!GetBitmapFromPaint(paint, &bitmap)) {
+            return;
+        }
+
+        // Math is borrowed from SkBBoxRecord
+        SkRect bounds = path.getBounds();
+        SkPaint::FontMetrics metrics;
+        paint.getFontMetrics(&metrics);
+
+        SkScalar pad = metrics.fTop;
+        // TODO: inset?!
+        bounds.fLeft += pad;
+        bounds.fRight -= pad;
+        bounds.fTop += pad;
+        bounds.fBottom -= pad;
+
+        this->drawRect(draw, bounds, paint);
+    }
+    virtual void drawVertices(const SkDraw& draw, SkCanvas::VertexMode, int vertexCount,
+                              const SkPoint verts[], const SkPoint texs[],
+                              const SkColor colors[], SkXfermode* xmode,
+                              const uint16_t indices[], int indexCount,
+                              const SkPaint& paint) SK_OVERRIDE {
+        this->drawPoints(draw, SkCanvas::kPolygon_PointMode, vertexCount, verts, paint);                                    
+    }
+    virtual void drawDevice(const SkDraw&, SkBaseDevice*, int x, int y,
+                            const SkPaint&) SK_OVERRIDE {
+        NothingToDo();
+    }
+    // TODO: allow this call to return failure, or move to SkBitmapDevice only.
+    virtual const SkBitmap& onAccessBitmap() SK_OVERRIDE {
+        return fEmptyBitmap;
+    }
+    virtual bool onReadPixels(const SkBitmap& bitmap,
+                              int x, int y,
+                              SkCanvas::Config8888 config8888) SK_OVERRIDE {
+        NotSupported();
+        return false;
+    }
+    virtual void lockPixels() SK_OVERRIDE { NothingToDo(); }
+    virtual void unlockPixels() SK_OVERRIDE { NothingToDo(); }
+    virtual bool allowImageFilter(SkImageFilter*) SK_OVERRIDE { return false; }
+    virtual bool canHandleImageFilter(SkImageFilter*) SK_OVERRIDE { return false; }
+    virtual bool filterImage(SkImageFilter*, const SkBitmap&, const SkMatrix&,
+                             SkBitmap* result, SkIPoint* offset) SK_OVERRIDE {
+        return false;
+    }
+
+private:
+    SkPictureUtils::SkPixelRefContainer* fPRCont;
+    SkISize                              fSize;
+
+    SkBitmap                             fEmptyBitmap; // legacy -- need to remove
+
+    static bool GetBitmapFromPaint(const SkPaint &paint, SkBitmap* bitmap) {
+        SkShader* shader = paint.getShader();
+        if (NULL != shader) {
+            if (SkShader::kNone_GradientType == shader->asAGradient(NULL)) {
+                return SkShader::kNone_BitmapType != shader->asABitmap(bitmap, NULL, NULL);
+            }
+        }
+        return false;
+    }
+
+    virtual void replaceBitmapBackendForRasterSurface(const SkBitmap&) SK_OVERRIDE {
+        NotSupported();
+    }
+
+    virtual SkBaseDevice* onCreateCompatibleDevice(SkBitmap::Config config,
+                                                   int width, int height,
+                                                   bool isOpaque,
+                                                   Usage usage) SK_OVERRIDE {
+        // we expect to only get called via savelayer, in which case it is fine.
+        SkASSERT(kSaveLayer_Usage == usage);
+        return SkNEW_ARGS(SkGatherPixelRefsAndRectsDevice, (width, height, fPRCont));
+    }
+
+    virtual void flush() SK_OVERRIDE {}
+
+    static void NotSupported() {
+        SkDEBUGFAIL("this method should never be called");
+    }
+
+    static void NothingToDo() {}
+
+    typedef SkBaseDevice INHERITED;
+};
+
+#endif // SkGatherPixelRefsAndRects_DEFINED
+
diff --git a/src/utils/SkPictureUtils.cpp b/src/utils/SkPictureUtils.cpp
index 8795a04..4af8da8 100644
--- a/src/utils/SkPictureUtils.cpp
+++ b/src/utils/SkPictureUtils.cpp
@@ -50,6 +50,8 @@
  */
 class GatherPixelRefDevice : public SkBaseDevice {
 public:
+    SK_DECLARE_INST_COUNT(GatherPixelRefDevice)
+
     GatherPixelRefDevice(int width, int height, PixelRefSet* prset) {
         fSize.set(width, height);
         fEmptyBitmap.setConfig(SkBitmap::kNo_Config, width, height);
diff --git a/tests/PictureTest.cpp b/tests/PictureTest.cpp
index 4e0ad1d..7810ce1 100644
--- a/tests/PictureTest.cpp
+++ b/tests/PictureTest.cpp
@@ -65,35 +65,42 @@
     paint->setShader(shader)->unref();
 }
 
-typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, const SkBitmap&, const SkPoint&);
+typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, 
+                               const SkBitmap&, const SkPoint&,
+                               SkTDArray<SkPixelRef*>* usedPixRefs);
 
 static void drawpaint_proc(SkCanvas* canvas, const SkBitmap& bm,
-                           const SkBitmap& altBM, const SkPoint& pos) {
+                           const SkBitmap& altBM, const SkPoint& pos,
+                           SkTDArray<SkPixelRef*>* usedPixRefs) {
     SkPaint paint;
     init_paint(&paint, bm);
 
     canvas->drawPaint(paint);
+    *usedPixRefs->append() = bm.pixelRef();
 }
 
 static void drawpoints_proc(SkCanvas* canvas, const SkBitmap& bm,
-                            const SkBitmap& altBM, const SkPoint& pos) {
+                            const SkBitmap& altBM, const SkPoint& pos,
+                            SkTDArray<SkPixelRef*>* usedPixRefs) {
     SkPaint paint;
     init_paint(&paint, bm);
 
-    // draw a slightly inset rect
+    // draw a rect
     SkPoint points[5] = {
-        { pos.fX + 1, pos.fY + 1 },
-        { pos.fX + bm.width() - 2, pos.fY + 1 },
-        { pos.fX + bm.width() - 2, pos.fY + bm.height() - 2 },
-        { pos.fX + 1, pos.fY + bm.height() - 2 },
-        { pos.fX + 1, pos.fY + 1 },
+        { pos.fX, pos.fY }, 
+        { pos.fX + bm.width() - 1, pos.fY }, 
+        { pos.fX + bm.width() - 1, pos.fY + bm.height() - 1 }, 
+        { pos.fX, pos.fY + bm.height() - 1 }, 
+        { pos.fX, pos.fY }, 
     };
 
     canvas->drawPoints(SkCanvas::kPolygon_PointMode, 5, points, paint);
+    *usedPixRefs->append() = bm.pixelRef();
 }
 
 static void drawrect_proc(SkCanvas* canvas, const SkBitmap& bm,
-                          const SkBitmap& altBM, const SkPoint& pos) {
+                          const SkBitmap& altBM, const SkPoint& pos,
+                          SkTDArray<SkPixelRef*>* usedPixRefs) {
     SkPaint paint;
     init_paint(&paint, bm);
 
@@ -101,10 +108,12 @@
     r.offset(pos.fX, pos.fY);
 
     canvas->drawRect(r, paint);
+    *usedPixRefs->append() = bm.pixelRef();
 }
 
 static void drawoval_proc(SkCanvas* canvas, const SkBitmap& bm,
-                          const SkBitmap& altBM, const SkPoint& pos) {
+                          const SkBitmap& altBM, const SkPoint& pos,
+                          SkTDArray<SkPixelRef*>* usedPixRefs) {
     SkPaint paint;
     init_paint(&paint, bm);
 
@@ -112,10 +121,12 @@
     r.offset(pos.fX, pos.fY);
 
     canvas->drawOval(r, paint);
+    *usedPixRefs->append() = bm.pixelRef();
 }
 
 static void drawrrect_proc(SkCanvas* canvas, const SkBitmap& bm,
-                           const SkBitmap& altBM, const SkPoint& pos) {
+                           const SkBitmap& altBM, const SkPoint& pos,
+                           SkTDArray<SkPixelRef*>* usedPixRefs) {
     SkPaint paint;
     init_paint(&paint, bm);
 
@@ -125,10 +136,12 @@
     SkRRect rr;
     rr.setRectXY(r, SkIntToScalar(bm.width())/4, SkIntToScalar(bm.height())/4);
     canvas->drawRRect(rr, paint);
+    *usedPixRefs->append() = bm.pixelRef();
 }
 
 static void drawpath_proc(SkCanvas* canvas, const SkBitmap& bm,
-                          const SkBitmap& altBM, const SkPoint& pos) {
+                          const SkBitmap& altBM, const SkPoint& pos,
+                          SkTDArray<SkPixelRef*>* usedPixRefs) {
     SkPaint paint;
     init_paint(&paint, bm);
 
@@ -139,37 +152,46 @@
     path.offset(pos.fX, pos.fY);
 
     canvas->drawPath(path, paint);
+    *usedPixRefs->append() = bm.pixelRef();
 }
 
 static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm,
-                            const SkBitmap& altBM, const SkPoint& pos) {
+                            const SkBitmap& altBM, const SkPoint& pos,
+                            SkTDArray<SkPixelRef*>* usedPixRefs) {
     canvas->drawBitmap(bm, pos.fX, pos.fY, NULL);
+    *usedPixRefs->append() = bm.pixelRef();
 }
 
 static void drawbitmap_withshader_proc(SkCanvas* canvas, const SkBitmap& bm,
-                                       const SkBitmap& altBM, const SkPoint& pos) {
+                                       const SkBitmap& altBM, const SkPoint& pos,
+                                       SkTDArray<SkPixelRef*>* usedPixRefs) {
     SkPaint paint;
     init_paint(&paint, bm);
 
     // The bitmap in the paint is ignored unless we're drawing an A8 bitmap
     canvas->drawBitmap(altBM, pos.fX, pos.fY, &paint);
+    *usedPixRefs->append() = bm.pixelRef();
+    *usedPixRefs->append() = altBM.pixelRef();
 }
 
 static void drawsprite_proc(SkCanvas* canvas, const SkBitmap& bm,
-                            const SkBitmap& altBM, const SkPoint& pos) {
+                            const SkBitmap& altBM, const SkPoint& pos,
+                            SkTDArray<SkPixelRef*>* usedPixRefs) {
     const SkMatrix& ctm = canvas->getTotalMatrix();
 
     SkPoint p(pos);
     ctm.mapPoints(&p, 1);
 
     canvas->drawSprite(bm, (int)p.fX, (int)p.fY, NULL);
+    *usedPixRefs->append() = bm.pixelRef();
 }
 
 #if 0
 // Although specifiable, this case doesn't seem to make sense (i.e., the
 // bitmap in the shader is never used).
 static void drawsprite_withshader_proc(SkCanvas* canvas, const SkBitmap& bm,
-                                       const SkBitmap& altBM, const SkPoint& pos) {
+                                       const SkBitmap& altBM, const SkPoint& pos,
+                                       SkTDArray<SkPixelRef*>* usedPixRefs) {
     SkPaint paint;
     init_paint(&paint, bm);
 
@@ -179,21 +201,26 @@
     ctm.mapPoints(&p, 1);
 
     canvas->drawSprite(altBM, (int)p.fX, (int)p.fY, &paint);
+    *usedPixRefs->append() = bm.pixelRef();
+    *usedPixRefs->append() = altBM.pixelRef();
 }
 #endif
 
 static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm,
-                                const SkBitmap& altBM, const SkPoint& pos) {
+                                const SkBitmap& altBM, const SkPoint& pos,
+                                SkTDArray<SkPixelRef*>* usedPixRefs) {
     SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
 
     r.offset(pos.fX, pos.fY);
     canvas->drawBitmapRectToRect(bm, NULL, r, NULL);
+    *usedPixRefs->append() = bm.pixelRef();
 }
 
 static void drawbitmaprect_withshader_proc(SkCanvas* canvas,
                                            const SkBitmap& bm,
                                            const SkBitmap& altBM,
-                                           const SkPoint& pos) {
+                                           const SkPoint& pos,
+                                           SkTDArray<SkPixelRef*>* usedPixRefs) {
     SkPaint paint;
     init_paint(&paint, bm);
 
@@ -202,29 +229,36 @@
 
     // The bitmap in the paint is ignored unless we're drawing an A8 bitmap
     canvas->drawBitmapRectToRect(altBM, NULL, r, &paint);
+    *usedPixRefs->append() = bm.pixelRef();
+    *usedPixRefs->append() = altBM.pixelRef();
 }
 
 static void drawtext_proc(SkCanvas* canvas, const SkBitmap& bm,
-                          const SkBitmap& altBM, const SkPoint& pos) {
+                          const SkBitmap& altBM, const SkPoint& pos,
+                          SkTDArray<SkPixelRef*>* usedPixRefs) {
     SkPaint paint;
     init_paint(&paint, bm);
     paint.setTextSize(SkIntToScalar(1.5*bm.width()));
 
     canvas->drawText("0", 1, pos.fX, pos.fY+bm.width(), paint);
+    *usedPixRefs->append() = bm.pixelRef();
 }
 
 static void drawpostext_proc(SkCanvas* canvas, const SkBitmap& bm,
-                             const SkBitmap& altBM, const SkPoint& pos) {
+                             const SkBitmap& altBM, const SkPoint& pos,
+                             SkTDArray<SkPixelRef*>* usedPixRefs) {
     SkPaint paint;
     init_paint(&paint, bm);
     paint.setTextSize(SkIntToScalar(1.5*bm.width()));
 
     SkPoint point = { pos.fX, pos.fY + bm.height() };
     canvas->drawPosText("O", 1, &point, paint);
+    *usedPixRefs->append() = bm.pixelRef();
 }
 
 static void drawtextonpath_proc(SkCanvas* canvas, const SkBitmap& bm,
-                                const SkBitmap& altBM, const SkPoint& pos) {
+                                const SkBitmap& altBM, const SkPoint& pos,
+                                SkTDArray<SkPixelRef*>* usedPixRefs) {
     SkPaint paint;
 
     init_paint(&paint, bm);
@@ -235,18 +269,20 @@
     path.offset(pos.fX, pos.fY+bm.height());
 
     canvas->drawTextOnPath("O", 1, path, NULL, paint);
+    *usedPixRefs->append() = bm.pixelRef();
 }
 
 static void drawverts_proc(SkCanvas* canvas, const SkBitmap& bm,
-                           const SkBitmap& altBM, const SkPoint& pos) {
+                           const SkBitmap& altBM, const SkPoint& pos,
+                           SkTDArray<SkPixelRef*>* usedPixRefs) {
     SkPaint paint;
     init_paint(&paint, bm);
 
-    SkPoint verts[4] = {
-        { pos.fX+1, pos.fY+1 },
-        { pos.fX + bm.width()-1, pos.fY+1 },
-        { pos.fX + bm.width()-1, pos.fY + bm.height()-1 },
-        { pos.fX+1, pos.fY + bm.height()-1 }
+    SkPoint verts[4] = { 
+        { pos.fX, pos.fY }, 
+        { pos.fX + bm.width(), pos.fY }, 
+        { pos.fX + bm.width(), pos.fY + bm.height() }, 
+        { pos.fX, pos.fY + bm.height() } 
     };
     SkPoint texs[4] = { { 0, 0 },
                         { SkIntToScalar(bm.width()), 0 },
@@ -256,20 +292,25 @@
 
     canvas->drawVertices(SkCanvas::kTriangles_VertexMode, 4, verts, texs, NULL, NULL,
                          indices, 6, paint);
+    *usedPixRefs->append() = bm.pixelRef();
 }
 
 // Return a picture with the bitmaps drawn at the specified positions.
-static SkPicture* record_bitmaps(const SkBitmap bm[], const SkPoint pos[],
-                                 int count, DrawBitmapProc proc) {
+static SkPicture* record_bitmaps(const SkBitmap bm[], 
+                                 const SkPoint pos[],
+                                 SkTDArray<SkPixelRef*> analytic[],
+                                 int count, 
+                                 DrawBitmapProc proc) {
     SkPicture* pic = new SkPicture;
     SkCanvas* canvas = pic->beginRecording(1000, 1000);
     for (int i = 0; i < count; ++i) {
+        analytic[i].rewind();
         canvas->save();
         SkRect clipRect = SkRect::MakeXYWH(pos[i].fX, pos[i].fY,
                                            SkIntToScalar(bm[i].width()),
                                            SkIntToScalar(bm[i].height()));
         canvas->clipRect(clipRect, SkRegion::kIntersect_Op);
-        proc(canvas, bm[i], bm[count+i], pos[i]);
+        proc(canvas, bm[i], bm[count+i], pos[i], &analytic[i]);
         canvas->restore();
     }
     pic->endRecording();
@@ -363,6 +404,62 @@
     }
 }
 
+void gather_from_analytic(const SkPoint pos[], SkScalar w, SkScalar h,
+                          const SkTDArray<SkPixelRef*> analytic[], 
+                          int count, 
+                          SkTDArray<SkPixelRef*>* result, 
+                          const SkRect& subset) {
+    for (int i = 0; i < count; ++i) {
+        SkRect rect = SkRect::MakeXYWH(pos[i].fX, pos[i].fY, w, h);
+
+        if (SkRect::Intersects(subset, rect)) {
+            result->append(analytic[i].count(), analytic[i].begin());
+        }
+    }
+}
+
+static const DrawBitmapProc gProcs[] = {
+        drawpaint_proc,
+        drawpoints_proc,
+        drawrect_proc,
+        drawoval_proc,
+        drawrrect_proc,
+        drawpath_proc,
+        drawbitmap_proc, 
+        drawbitmap_withshader_proc,
+        drawsprite_proc,
+#if 0
+        drawsprite_withshader_proc,
+#endif
+        drawbitmaprect_proc, 
+        drawbitmaprect_withshader_proc,
+        drawtext_proc,
+        drawpostext_proc,
+        drawtextonpath_proc,
+        drawverts_proc,
+};
+
+static void create_textures(SkBitmap* bm, SkPixelRef** refs, int num, int w, int h) {
+    // Our convention is that the color components contain an encoding of 
+    // the index of their corresponding bitmap/pixelref. (0,0,0,0) is 
+    // reserved for the background
+    for (int i = 0; i < num; ++i) {
+        make_bm(&bm[i], w, h, 
+                SkColorSetARGB(0xFF, 
+                               gColorScale*i+gColorOffset, 
+                               gColorScale*i+gColorOffset, 
+                               gColorScale*i+gColorOffset), 
+                true);
+        refs[i] = bm[i].pixelRef();
+    }
+
+    // The A8 alternate bitmaps are all BW checkerboards
+    for (int i = 0; i < num; ++i) {
+        make_checkerboard(&bm[num+i], w, h, true);
+        refs[num+i] = bm[num+i].pixelRef();
+    }
+}
+
 static void test_gatherpixelrefs(skiatest::Reporter* reporter) {
     const int IW = 32;
     const int IH = IW;
@@ -372,58 +469,21 @@
     static const int N = 4;
     SkBitmap bm[2*N];
     SkPixelRef* refs[2*N];
+    SkTDArray<SkPixelRef*> analytic[N];
 
     const SkPoint pos[N] = {
         { 0, 0 }, { W, 0 }, { 0, H }, { W, H }
     };
 
-    // Our convention is that the color components contain an encoding of
-    // the index of their corresponding bitmap/pixelref. (0,0,0,0) is
-    // reserved for the background
-    for (int i = 0; i < N; ++i) {
-        make_bm(&bm[i], IW, IH,
-                SkColorSetARGB(0xFF,
-                               gColorScale*i+gColorOffset,
-                               gColorScale*i+gColorOffset,
-                               gColorScale*i+gColorOffset),
-                true);
-        refs[i] = bm[i].pixelRef();
-    }
-
-    // The A8 alternate bitmaps are all BW checkerboards
-    for (int i = 0; i < N; ++i) {
-        make_checkerboard(&bm[N+i], IW, IH, true);
-        refs[N+i] = bm[N+i].pixelRef();
-    }
-
-    static const DrawBitmapProc procs[] = {
-            drawpaint_proc,
-            drawpoints_proc,
-            drawrect_proc,
-            drawoval_proc,
-            drawrrect_proc,
-            drawpath_proc,
-            drawbitmap_proc,
-            drawbitmap_withshader_proc,
-            drawsprite_proc,
-#if 0
-            drawsprite_withshader_proc,
-#endif
-            drawbitmaprect_proc,
-            drawbitmaprect_withshader_proc,
-            drawtext_proc,
-            drawpostext_proc,
-            drawtextonpath_proc,
-            drawverts_proc,
-    };
+    create_textures(bm, refs, N, IW, IH);
 
     SkRandom rand;
-    for (size_t k = 0; k < SK_ARRAY_COUNT(procs); ++k) {
-        SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, N, procs[k]));
+    for (size_t k = 0; k < SK_ARRAY_COUNT(gProcs); ++k) {
+        SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, analytic, N, gProcs[k]));
 
         REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0);
         // quick check for a small piece of each quadrant, which should just
-        // contain 1 bitmap.
+        // contain 1 or 2 bitmaps.
         for (size_t  i = 0; i < SK_ARRAY_COUNT(pos); ++i) {
             SkRect r;
             r.set(2, 2, W - 2, H - 2);
@@ -453,7 +513,11 @@
             SkRect r;
             rand_rect(&r, rand, 2*W, 2*H);
 
-            SkTDArray<SkPixelRef*> array;
+            SkTDArray<SkPixelRef*> fromImage;
+            gather_from_image(image, refs, N, &fromImage, r);
+
+            SkTDArray<SkPixelRef*> fromAnalytic;
+            gather_from_analytic(pos, W, H, analytic, N, &fromAnalytic, r);
 
             SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
             size_t dataSize = data ? data->size() : 0;
@@ -462,18 +526,22 @@
             SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL;
             SkAutoDataUnref adu(data);
 
-            gather_from_image(image, refs, N, &array, r);
+            // Everything that we saw drawn should appear in the analytic list
+            // but the analytic list may contain some pixelRefs that were not
+            // seen in the image (e.g., A8 textures used as masks)
+            for (int i = 0; i < fromImage.count(); ++i) {
+                REPORTER_ASSERT(reporter, -1 != fromAnalytic.find(fromImage[i]));
+            }
 
             /*
              *  GatherPixelRefs is conservative, so it can return more bitmaps
-             *  that we actually can see (usually because of conservative bounds
-             *  inflation for antialiasing). Thus our check here is only that
-             *  Gather didn't miss any that we actually saw. Even that isn't
+             *  than are strictly required. Thus our check here is only that
+             *  Gather didn't miss any that we actually needed. Even that isn't
              *  a strict requirement on Gather, which is meant to be quick and
              *  only mostly-correct, but at the moment this test should work.
              */
-            for (int i = 0; i < array.count(); ++i) {
-                bool found = find(gatherRefs, array[i], gatherCount);
+            for (int i = 0; i < fromAnalytic.count(); ++i) {
+                bool found = find(gatherRefs, fromAnalytic[i], gatherCount);
                 REPORTER_ASSERT(reporter, found);
 #if 0
                 // enable this block of code to debug failures, as it will rerun
@@ -488,6 +556,89 @@
     }
 }
 
+static void test_gatherpixelrefsandrects(skiatest::Reporter* reporter) {
+    const int IW = 32;
+    const int IH = IW;
+    const SkScalar W = SkIntToScalar(IW);
+    const SkScalar H = W;
+
+    static const int N = 4;
+    SkBitmap bm[2*N];
+    SkPixelRef* refs[2*N];
+    SkTDArray<SkPixelRef*> analytic[N];
+
+    const SkPoint pos[N] = {
+        { 0, 0 }, { W, 0 }, { 0, H }, { W, H }
+    };
+
+    create_textures(bm, refs, N, IW, IH);
+
+    SkRandom rand;
+    for (size_t k = 0; k < SK_ARRAY_COUNT(gProcs); ++k) {
+        SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, analytic, N, gProcs[k]));
+
+        REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0);
+
+        SkAutoTUnref<SkPictureUtils::SkPixelRefContainer> prCont(
+                                new SkPictureUtils::SkPixelRefsAndRectsList);
+
+        SkPictureUtils::GatherPixelRefsAndRects(pic, prCont);
+
+        // quick check for a small piece of each quadrant, which should just
+        // contain 1 or 2 bitmaps.
+        for (size_t  i = 0; i < SK_ARRAY_COUNT(pos); ++i) {
+            SkRect r;
+            r.set(2, 2, W - 2, H - 2);
+            r.offset(pos[i].fX, pos[i].fY);
+
+            SkTDArray<SkPixelRef*> gatheredRefs;
+            prCont->query(r, &gatheredRefs);
+
+            int count = gatheredRefs.count();
+            REPORTER_ASSERT(reporter, 1 == count || 2 == count);
+            if (1 == count) {
+                REPORTER_ASSERT(reporter, gatheredRefs[0] == refs[i]);
+            } else if (2 == count) {
+                REPORTER_ASSERT(reporter, 
+                    (gatheredRefs[0] == refs[i] && gatheredRefs[1] == refs[i+N]) ||
+                    (gatheredRefs[1] == refs[i] && gatheredRefs[0] == refs[i+N]));
+            }
+        }
+
+        SkBitmap image;
+        draw(pic, 2*IW, 2*IH, &image);
+
+        // Test a bunch of random (mostly) rects, and compare the gather results
+        // with the analytic results and the pixel refs seen in a rendering.
+        for (int j = 0; j < 100; ++j) {
+            SkRect r;
+            rand_rect(&r, rand, 2*W, 2*H);
+
+            SkTDArray<SkPixelRef*> fromImage;
+            gather_from_image(image, refs, N, &fromImage, r);
+
+            SkTDArray<SkPixelRef*> fromAnalytic;
+            gather_from_analytic(pos, W, H, analytic, N, &fromAnalytic, r);
+
+            SkTDArray<SkPixelRef*> gatheredRefs;
+            prCont->query(r, &gatheredRefs);
+
+            // Everything that we saw drawn should appear in the analytic list
+            // but the analytic list may contain some pixelRefs that were not
+            // seen in the image (e.g., A8 textures used as masks)
+            for (int i = 0; i < fromImage.count(); ++i) {
+                REPORTER_ASSERT(reporter, -1 != fromAnalytic.find(fromImage[i]));
+            }
+
+            // Everything in the analytic list should appear in the gathered
+            // list. 
+            for (int i = 0; i < fromAnalytic.count(); ++i) {
+                REPORTER_ASSERT(reporter, -1 != gatheredRefs.find(fromAnalytic[i]));
+            }
+        }
+    }
+}
+
 #ifdef SK_DEBUG
 // Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only
 // run in debug mode.
@@ -875,6 +1026,7 @@
 #endif
     test_peephole();
     test_gatherpixelrefs(reporter);
+    test_gatherpixelrefsandrects(reporter);
     test_bitmap_with_encoded_data(reporter);
     test_clone_empty(reporter);
     test_clip_bound_opt(reporter);