aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gyp/utils.gyp1
-rw-r--r--include/utils/SkPictureUtils.h31
-rw-r--r--src/utils/SkPictureUtils.cpp213
-rw-r--r--tests/PictureTest.cpp222
-rw-r--r--tools/PictureRenderer.cpp25
-rw-r--r--tools/PictureRenderer.h2
-rw-r--r--tools/bench_pictures_main.cpp2
7 files changed, 496 insertions, 0 deletions
diff --git a/gyp/utils.gyp b/gyp/utils.gyp
index 706b851903..7bbe67cf90 100644
--- a/gyp/utils.gyp
+++ b/gyp/utils.gyp
@@ -70,6 +70,7 @@
'../src/utils/SkParse.cpp',
'../src/utils/SkParseColor.cpp',
'../src/utils/SkParsePath.cpp',
+ '../src/utils/SkPictureUtils.cpp',
'../src/utils/SkProxyCanvas.cpp',
'../src/utils/SkThreadUtils.h',
'../src/utils/SkThreadUtils_pthread.cpp',
diff --git a/include/utils/SkPictureUtils.h b/include/utils/SkPictureUtils.h
new file mode 100644
index 0000000000..56318cdb70
--- /dev/null
+++ b/include/utils/SkPictureUtils.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkPictureUtils_DEFINED
+#define SkPictureUtils_DEFINED
+
+#include "SkPicture.h"
+
+class SkData;
+struct SkRect;
+
+class SkPictureUtils {
+public:
+ /**
+ * Given a rectangular visible "window" into the picture, return an array
+ * of SkPixelRefs that might intersect that area. To keep the call fast,
+ * the returned list is not guaranteed to be exact, so it may miss some,
+ * and it may return false positives.
+ *
+ * The pixelrefs returned in the SkData are already owned by the picture,
+ * so the returned pointers are only valid while the picture is in scope
+ * and remains unchanged.
+ */
+ static SkData* GatherPixelRefs(SkPicture* pict, const SkRect& area);
+};
+
+#endif
diff --git a/src/utils/SkPictureUtils.cpp b/src/utils/SkPictureUtils.cpp
new file mode 100644
index 0000000000..16cf11d2e0
--- /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;
+}
+
diff --git a/tests/PictureTest.cpp b/tests/PictureTest.cpp
index 44cf59a889..567a8d2006 100644
--- a/tests/PictureTest.cpp
+++ b/tests/PictureTest.cpp
@@ -6,11 +6,232 @@
*/
#include "Test.h"
#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkData.h"
#include "SkPaint.h"
#include "SkPicture.h"
#include "SkRandom.h"
+#include "SkShader.h"
#include "SkStream.h"
+#include "SkPictureUtils.h"
+
+static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
+ bm->setConfig(SkBitmap::kARGB_8888_Config, w, h);
+ bm->allocPixels();
+ bm->eraseColor(color);
+ if (immutable) {
+ bm->setImmutable();
+ }
+}
+
+typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, const SkPoint&);
+
+static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm,
+ const SkPoint& pos) {
+ canvas->drawBitmap(bm, pos.fX, pos.fY, NULL);
+}
+
+static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm,
+ const SkPoint& pos) {
+ SkRect r = {
+ 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())
+ };
+ r.offset(pos.fX, pos.fY);
+ canvas->drawBitmapRectToRect(bm, NULL, r, NULL);
+}
+
+static void drawshader_proc(SkCanvas* canvas, const SkBitmap& bm,
+ const SkPoint& pos) {
+ SkRect r = {
+ 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())
+ };
+ r.offset(pos.fX, pos.fY);
+
+ SkShader* s = SkShader::CreateBitmapShader(bm,
+ SkShader::kClamp_TileMode,
+ SkShader::kClamp_TileMode);
+ SkPaint paint;
+ paint.setShader(s)->unref();
+ canvas->drawRect(r, paint);
+}
+
+// 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) {
+ SkPicture* pic = new SkPicture;
+ SkCanvas* canvas = pic->beginRecording(1000, 1000);
+ for (int i = 0; i < count; ++i) {
+ proc(canvas, bm[i], pos[i]);
+ }
+ pic->endRecording();
+ return pic;
+}
+
+static void rand_rect(SkRect* rect, SkRandom& rand, SkScalar W, SkScalar H) {
+ rect->fLeft = rand.nextRangeScalar(-W, 2*W);
+ rect->fTop = rand.nextRangeScalar(-H, 2*H);
+ rect->fRight = rect->fLeft + rand.nextRangeScalar(0, W);
+ rect->fBottom = rect->fTop + rand.nextRangeScalar(0, H);
+
+ // we integralize rect to make our tests more predictable, since Gather is
+ // a little sloppy.
+ SkIRect ir;
+ rect->round(&ir);
+ rect->set(ir);
+}
+
+// Allocate result to be large enough to hold subset, and then draw the picture
+// into it, offsetting by subset's top/left corner.
+static void draw(SkPicture* pic, const SkRect& subset, SkBitmap* result) {
+ SkIRect ir;
+ subset.roundOut(&ir);
+ int w = ir.width();
+ int h = ir.height();
+ make_bm(result, w, h, 0, false);
+
+ SkCanvas canvas(*result);
+ canvas.translate(-SkIntToScalar(ir.left()), -SkIntToScalar(ir.top()));
+ canvas.drawPicture(*pic);
+}
+
+template <typename T> int find_index(const T* array, T elem, int count) {
+ for (int i = 0; i < count; ++i) {
+ if (array[i] == elem) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+// Return true if 'ref' is found in array[]
+static bool find(SkPixelRef const * const * array, SkPixelRef const * ref, int count) {
+ return find_index<const SkPixelRef*>(array, ref, count) >= 0;
+}
+
+// Look at each pixel in bm, and if its color appears in colors[], find the
+// corresponding value in refs[] and append that ref into array, skipping
+// duplicates of the same value.
+static void gather_from_colors(const SkBitmap& bm, SkPixelRef* const refs[],
+ int count, SkTDArray<SkPixelRef*>* array) {
+ // Since we only want to return unique values in array, when we scan we just
+ // set a bit for each index'd color found. In practice we only have a few
+ // distinct colors, so we just use an int's bits as our array. Hence the
+ // assert that count <= number-of-bits-in-our-int.
+ SkASSERT((unsigned)count <= 32);
+ uint32_t bitarray = 0;
+
+ SkAutoLockPixels alp(bm);
+
+ for (int y = 0; y < bm.height(); ++y) {
+ for (int x = 0; x < bm.width(); ++x) {
+ SkPMColor pmc = *bm.getAddr32(x, y);
+ // the only good case where the color is not found would be if
+ // the color is transparent, meaning no bitmap was drawn in that
+ // pixel.
+ if (pmc) {
+ int index = SkGetPackedR32(pmc);
+ SkASSERT(SkGetPackedG32(pmc) == index);
+ SkASSERT(SkGetPackedB32(pmc) == index);
+ SkASSERT(index < count);
+ bitarray |= 1 << index;
+ }
+ }
+ }
+
+ for (int i = 0; i < count; ++i) {
+ if (bitarray & (1 << i)) {
+ *array->append() = refs[i];
+ }
+ }
+}
+
+static void test_gatherpixelrefs(skiatest::Reporter* reporter) {
+ const int IW = 8;
+ const int IH = IW;
+ const SkScalar W = SkIntToScalar(IW);
+ const SkScalar H = W;
+
+ static const int N = 4;
+ SkBitmap bm[N];
+ SkPMColor pmcolors[N];
+ SkPixelRef* refs[N];
+
+ const SkPoint pos[] = {
+ { 0, 0 }, { W, 0 }, { 0, H }, { W, H }
+ };
+
+ // Our convention is that the color components contain the index of their
+ // corresponding bitmap/pixelref
+ for (int i = 0; i < N; ++i) {
+ make_bm(&bm[i], IW, IH, SkColorSetARGB(0xFF, i, i, i), true);
+ refs[i] = bm[i].pixelRef();
+ }
+
+ static const DrawBitmapProc procs[] = {
+ drawbitmap_proc, drawbitmaprect_proc, drawshader_proc
+ };
+
+ SkRandom rand;
+ for (size_t k = 0; k < SK_ARRAY_COUNT(procs); ++k) {
+ SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, N, procs[k]));
+
+ // quick check for a small piece of each quadrant, which should just
+ // contain 1 bitmap.
+ 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);
+ SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r));
+ REPORTER_ASSERT(reporter, data);
+ int count = data->size() / sizeof(SkPixelRef*);
+ REPORTER_ASSERT(reporter, 1 == count);
+ REPORTER_ASSERT(reporter, *(SkPixelRef**)data->data() == refs[i]);
+ }
+
+ // Test a bunch of random (mostly) rects, and compare the gather results
+ // with a deduced list of refs by looking at the colors drawn.
+ for (int j = 0; j < 100; ++j) {
+ SkRect r;
+ rand_rect(&r, rand, 2*W, 2*H);
+
+ SkBitmap result;
+ draw(pic, r, &result);
+ SkTDArray<SkPixelRef*> array;
+
+ SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
+ size_t dataSize = data ? data->size() : 0;
+ int gatherCount = dataSize / sizeof(SkPixelRef*);
+ SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize);
+ SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL;
+ SkAutoDataUnref adu(data);
+
+ gather_from_colors(result, refs, N, &array);
+
+ /*
+ * 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
+ * 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);
+ REPORTER_ASSERT(reporter, found);
+#if 0
+ // enable this block of code to debug failures, as it will rerun
+ // the case that failed.
+ if (!found) {
+ SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
+ size_t dataSize = data ? data->size() : 0;
+ }
+#endif
+ }
+ }
+ }
+}
+
#ifdef SK_DEBUG
// Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only
// run in debug mode.
@@ -91,6 +312,7 @@ static void TestPicture(skiatest::Reporter* reporter) {
test_serializing_empty_picture();
#endif
test_peephole(reporter);
+ test_gatherpixelrefs(reporter);
}
#include "TestClassDef.h"
diff --git a/tools/PictureRenderer.cpp b/tools/PictureRenderer.cpp
index 132f88d852..69ce6f86fa 100644
--- a/tools/PictureRenderer.cpp
+++ b/tools/PictureRenderer.cpp
@@ -28,6 +28,8 @@
#include "SkTDArray.h"
#include "SkThreadUtils.h"
#include "SkTypes.h"
+#include "SkData.h"
+#include "SkPictureUtils.h"
namespace sk_tools {
@@ -658,4 +660,27 @@ SkPicture* PictureRenderer::createPicture() {
return NULL;
}
+///////////////////////////////////////////////////////////////////////////////
+
+class GatherRenderer : public PictureRenderer {
+public:
+ virtual bool render(const SkString* path) SK_OVERRIDE {
+ SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
+ SkIntToScalar(fPicture->height()));
+ SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
+ SkSafeUnref(data);
+
+ return NULL == path; // we don't have anything to write
+ }
+
+private:
+ virtual SkString getConfigNameInternal() SK_OVERRIDE {
+ return SkString("gather_pixelrefs");
+ }
+};
+
+PictureRenderer* CreateGatherPixelRefsRenderer() {
+ return SkNEW(GatherRenderer);
+}
+
} // namespace sk_tools
diff --git a/tools/PictureRenderer.h b/tools/PictureRenderer.h
index 8d9a6bd978..8bce978bf9 100644
--- a/tools/PictureRenderer.h
+++ b/tools/PictureRenderer.h
@@ -367,6 +367,8 @@ private:
typedef PictureRenderer INHERITED;
};
+extern PictureRenderer* CreateGatherPixelRefsRenderer();
+
}
#endif // PictureRenderer_DEFINED
diff --git a/tools/bench_pictures_main.cpp b/tools/bench_pictures_main.cpp
index 409439e768..c1d0e1c0b3 100644
--- a/tools/bench_pictures_main.cpp
+++ b/tools/bench_pictures_main.cpp
@@ -413,6 +413,8 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
} else if (0 == strcmp(*argv, "playbackCreation")) {
renderer.reset(SkNEW(sk_tools::PlaybackCreationRenderer));
gridSupported = true;
+ } else if (0 == strcmp(*argv, "gatherPixelRefs")) {
+ renderer.reset(sk_tools::CreateGatherPixelRefsRenderer());
} else {
SkString err;
err.printf("%s is not a valid mode for --mode\n", *argv);