add SkPictureUtils::GatherPixelRefs()
Review URL: https://codereview.appspot.com/6845106

git-svn-id: http://skia.googlecode.com/svn/trunk@6615 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/utils/SkPictureUtils.cpp b/src/utils/SkPictureUtils.cpp
new file mode 100644
index 0000000..16cf11d
--- /dev/null
+++ b/src/utils/SkPictureUtils.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkPictureUtils.h"
+#include "SkCanvas.h"
+#include "SkData.h"
+#include "SkDevice.h"
+#include "SkPixelRef.h"
+#include "SkShader.h"
+
+class PixelRefSet {
+public:
+    PixelRefSet(SkTDArray<SkPixelRef*>* array) : fArray(array) {}
+
+    // This does a linear search on existing pixelrefs, so if this list gets big
+    // we should use a more complex sorted/hashy thing.
+    //
+    void add(SkPixelRef* pr) {
+        uint32_t genID = pr->getGenerationID();
+        if (fGenID.find(genID) < 0) {
+            *fArray->append() = pr;
+            *fGenID.append() = genID;
+//            SkDebugf("--- adding [%d] %x %d\n", fArray->count() - 1, pr, genID);
+        } else {
+//            SkDebugf("--- already have %x %d\n", pr, genID);
+        }
+    }
+
+private:
+    SkTDArray<SkPixelRef*>* fArray;
+    SkTDArray<uint32_t>     fGenID;
+};
+
+static void not_supported() {
+    SkASSERT(!"this method should never be called");
+}
+
+static void nothing_to_do() {}
+
+/**
+ *  This device will route all bitmaps (primitives and in shaders) to its PRSet.
+ *  It should never actually draw anything, so there need not be any pixels
+ *  behind its device-bitmap.
+ */
+class GatherPixelRefDevice : public SkDevice {
+private:
+    PixelRefSet*  fPRSet;
+
+    void addBitmap(const SkBitmap& bm) {
+        fPRSet->add(bm.pixelRef());
+    }
+
+    void addBitmapFromPaint(const SkPaint& paint) {
+        SkShader* shader = paint.getShader();
+        if (shader) {
+            SkBitmap bm;
+            if (shader->asABitmap(&bm, NULL, NULL)) {
+                fPRSet->add(bm.pixelRef());
+            }
+        }
+    }
+
+public:
+    GatherPixelRefDevice(const SkBitmap& bm, PixelRefSet* prset) : SkDevice(bm) {
+        fPRSet = prset;
+    }
+
+    virtual void clear(SkColor color) SK_OVERRIDE {
+        nothing_to_do();
+    }
+    virtual void writePixels(const SkBitmap& bitmap, int x, int y,
+                             SkCanvas::Config8888 config8888) SK_OVERRIDE {
+        not_supported();
+    }
+    
+    virtual void drawPaint(const SkDraw&, const SkPaint& paint) SK_OVERRIDE {
+        this->addBitmapFromPaint(paint);
+    }
+    virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count,
+                            const SkPoint[], const SkPaint& paint) SK_OVERRIDE {
+        this->addBitmapFromPaint(paint);
+    }
+    virtual void drawRect(const SkDraw&, const SkRect& r,
+                          const SkPaint& paint) SK_OVERRIDE {
+        this->addBitmapFromPaint(paint);
+    }
+    virtual void drawPath(const SkDraw&, const SkPath& path,
+                          const SkPaint& paint, const SkMatrix* prePathMatrix,
+                          bool pathIsMutable) SK_OVERRIDE {
+        this->addBitmapFromPaint(paint);
+    }
+    virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
+                            const SkIRect* srcRectOrNull,
+                            const SkMatrix&, const SkPaint&) SK_OVERRIDE {
+        this->addBitmap(bitmap);
+    }
+    virtual void drawBitmapRect(const SkDraw&, const SkBitmap& bitmap,
+                                const SkRect* srcOrNull, const SkRect& dst,
+                                const SkPaint&) SK_OVERRIDE {
+        this->addBitmap(bitmap);
+    }
+    virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
+                            int x, int y, const SkPaint& paint) SK_OVERRIDE {
+        this->addBitmap(bitmap);
+    }
+    virtual void drawText(const SkDraw&, const void* text, size_t len,
+                          SkScalar x, SkScalar y,
+                          const SkPaint& paint) SK_OVERRIDE {
+        this->addBitmapFromPaint(paint);
+    }
+    virtual void drawPosText(const SkDraw&, const void* text, size_t len,
+                             const SkScalar pos[], SkScalar constY,
+                             int, const SkPaint& paint) SK_OVERRIDE {
+        this->addBitmapFromPaint(paint);
+    }
+    virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len,
+                                const SkPath& path, const SkMatrix* matrix,
+                                const SkPaint& paint) SK_OVERRIDE {
+        this->addBitmapFromPaint(paint);
+    }
+    virtual void drawVertices(const SkDraw&, 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->addBitmapFromPaint(paint);
+    }
+    virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
+                            const SkPaint&) SK_OVERRIDE {
+        nothing_to_do();
+    }
+
+protected:
+    virtual bool onReadPixels(const SkBitmap& bitmap,
+                              int x, int y,
+                              SkCanvas::Config8888 config8888) SK_OVERRIDE {
+        not_supported();
+        return false;
+    }
+};
+
+class NoSaveLayerCanvas : public SkCanvas {
+public:
+    NoSaveLayerCanvas(SkDevice* device) : INHERITED(device) {}
+    
+    // turn saveLayer() into save() for speed, should not affect correctness.
+    virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
+                          SaveFlags flags) SK_OVERRIDE {
+
+        // Like SkPictureRecord, we don't want to create layers, but we do need
+        // to respect the save and (possibly) its rect-clip.
+
+        int count = this->INHERITED::save(flags);
+        if (bounds) {
+            this->INHERITED::clipRectBounds(bounds, flags, NULL);
+        }
+        return count;
+    }
+
+    // disable aa for speed
+    virtual bool clipRect(const SkRect& rect, SkRegion::Op op,
+                          bool doAA) SK_OVERRIDE {
+        return this->INHERITED::clipRect(rect, op, false);
+    }
+
+    // for speed, just respect the bounds, and disable AA. May give us a few
+    // false positives and negatives.
+    virtual bool clipPath(const SkPath& path, SkRegion::Op op,
+                          bool doAA) SK_OVERRIDE {
+        return this->INHERITED::clipRect(path.getBounds(), op, false);
+    }
+
+private:
+    typedef SkCanvas INHERITED;
+};
+
+SkData* SkPictureUtils::GatherPixelRefs(SkPicture* pict, const SkRect& area) {
+    if (NULL == pict) {
+        return NULL;
+    }
+
+    // this test also handles if either area or pict's width/height are empty
+    if (!SkRect::Intersects(area,
+                            SkRect::MakeWH(SkIntToScalar(pict->width()),
+                                           SkIntToScalar(pict->height())))) {
+        return NULL;
+    }
+
+    SkTDArray<SkPixelRef*> array;
+    PixelRefSet prset(&array);
+
+    SkBitmap emptyBitmap;
+    emptyBitmap.setConfig(SkBitmap::kARGB_8888_Config, pict->width(), pict->height());
+    // note: we do not set any pixels (shouldn't need to)
+
+    GatherPixelRefDevice device(emptyBitmap, &prset);
+    NoSaveLayerCanvas canvas(&device);
+
+    canvas.clipRect(area, SkRegion::kIntersect_Op, false);
+    canvas.drawPicture(*pict);
+
+    SkData* data = NULL;
+    int count = array.count();
+    if (count > 0) {
+        data = SkData::NewFromMalloc(array.detach(), count * sizeof(SkPixelRef*));
+    }
+    return data;
+}
+