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/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);