Add SkOverdrawCanvas to detect overdraw

This is the first part of a multi-part change to detect and
display gpu overdraw on Android.

BUG:32370375

GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=4907

Change-Id: Ibba9d7343f2fd57397fa1168a5a5b1ef6ef91287
Reviewed-on: https://skia-review.googlesource.com/4907
Reviewed-by: Ben Wagner <bungeman@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Matt Sarett <msarett@google.com>
diff --git a/gn/core.gni b/gn/core.gni
index fcc8de1..d88ac5a 100644
--- a/gn/core.gni
+++ b/gn/core.gni
@@ -213,6 +213,8 @@
   "$_src/core/SkOpts.cpp",
   "$_src/core/SkOpts.h",
   "$_src/core/SkOrderedReadBuffer.h",
+  "$_src/core/SkOverdrawCanvas.cpp",
+  "$_src/core/SkOverdrawCanvas.h",
   "$_src/core/SkPaint.cpp",
   "$_src/core/SkPaintDefaults.h",
   "$_src/core/SkPaintPriv.cpp",
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index 95667e4..aabc7d8 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -1628,6 +1628,7 @@
     friend class SkPicturePlayback; // SaveFlagsToSaveLayerFlags
     friend class SkPipeCanvas;      // InitFlags
     friend class SkDeferredCanvas;  // For use of resetForNextPicture
+    friend class SkOverdrawCanvas;
 
     enum InitFlags {
         kDefault_InitFlags                  = 0,
diff --git a/src/core/SkOverdrawCanvas.cpp b/src/core/SkOverdrawCanvas.cpp
new file mode 100644
index 0000000..b8141c5
--- /dev/null
+++ b/src/core/SkOverdrawCanvas.cpp
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkColorFilter.h"
+#include "SkFindAndPlaceGlyph.h"
+#include "SkOverdrawCanvas.h"
+#include "SkPatchUtils.h"
+#include "SkPath.h"
+#include "SkRRect.h"
+#include "SkRSXform.h"
+#include "SkTextBlob.h"
+#include "SkTextBlobRunIterator.h"
+
+namespace {
+class ProcessOneGlyphBounds {
+public:
+    ProcessOneGlyphBounds(SkOverdrawCanvas* canvas)
+        : fCanvas(canvas)
+    {}
+
+    void operator()(const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
+        int left = SkScalarFloorToInt(position.fX) + glyph.fLeft;
+        int top = SkScalarFloorToInt(position.fY) + glyph.fTop;
+        int right = left + glyph.fWidth;
+        int bottom = top + glyph.fHeight;
+        fCanvas->onDrawRect(SkRect::MakeLTRB(left, top, right, bottom), SkPaint());
+    }
+
+private:
+    SkOverdrawCanvas* fCanvas;
+};
+};
+
+SkOverdrawCanvas::SkOverdrawCanvas(SkCanvas* canvas)
+    : INHERITED(canvas->onImageInfo().width(), canvas->onImageInfo().height())
+    , fCanvas(canvas)
+{
+    static constexpr float kIncrementAlpha[] = {
+            0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+            0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+            0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+            0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
+    };
+
+    fPaint.setAntiAlias(false);
+    fPaint.setBlendMode(SkBlendMode::kPlus);
+    fPaint.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(kIncrementAlpha));
+}
+
+void SkOverdrawCanvas::onDrawDRRect(const SkRRect& outer , const SkRRect&, const SkPaint&) {
+    fCanvas->onDrawRect(outer.getBounds(), fPaint);
+}
+
+void SkOverdrawCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
+                                  const SkPaint& paint) {
+    ProcessOneGlyphBounds processBounds(this);
+    SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
+    this->getProps(&props);
+    SkAutoGlyphCache cache(paint, &props, 0, &this->getTotalMatrix());
+    SkFindAndPlaceGlyph::ProcessText(paint.getTextEncoding(), (const char*) text, byteLength,
+                                     SkPoint::Make(x, y), SkMatrix(), paint.getTextAlign(),
+                                     cache.get(), processBounds);
+}
+
+void SkOverdrawCanvas::drawPosTextCommon(const void* text, size_t byteLength, const SkScalar pos[],
+                                         int scalarsPerPos, const SkPoint& offset,
+                                         const SkPaint& paint) {
+    ProcessOneGlyphBounds processBounds(this);
+    SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
+    this->getProps(&props);
+    SkAutoGlyphCache cache(paint, &props, 0, &this->getTotalMatrix());
+    SkFindAndPlaceGlyph::ProcessPosText(paint.getTextEncoding(), (const char*) text, byteLength,
+                                        SkPoint::Make(0, 0), SkMatrix(), (const SkScalar*) pos, 2,
+                                        paint.getTextAlign(), cache.get(), processBounds);
+}
+
+void SkOverdrawCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
+                                     const SkPaint& paint) {
+    this->drawPosTextCommon(text, byteLength, (SkScalar*) pos, 2, SkPoint::Make(0, 0), paint);
+}
+
+void SkOverdrawCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xs[],
+                                      SkScalar y, const SkPaint& paint) {
+    this->drawPosTextCommon(text, byteLength, (SkScalar*) xs, 1, SkPoint::Make(0, y), paint);
+}
+
+void SkOverdrawCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
+                                        const SkMatrix* matrix, const SkPaint& paint) {
+    SkASSERT(false);
+    return;
+}
+
+typedef int (*CountTextProc)(const char* text);
+static int count_utf16(const char* text) {
+    const uint16_t* prev = (uint16_t*)text;
+    (void)SkUTF16_NextUnichar(&prev);
+    return SkToInt((const char*)prev - text);
+}
+static int return_4(const char* text) { return 4; }
+static int return_2(const char* text) { return 2; }
+
+void SkOverdrawCanvas::onDrawTextRSXform(const void* text, size_t byteLength,
+                                         const SkRSXform xform[], const SkRect*,
+                                         const SkPaint& paint) {
+    CountTextProc proc = nullptr;
+    switch (paint.getTextEncoding()) {
+        case SkPaint::kUTF8_TextEncoding:
+            proc = SkUTF8_CountUTF8Bytes;
+            break;
+        case SkPaint::kUTF16_TextEncoding:
+            proc = count_utf16;
+            break;
+        case SkPaint::kUTF32_TextEncoding:
+            proc = return_4;
+            break;
+        case SkPaint::kGlyphID_TextEncoding:
+            proc = return_2;
+            break;
+    }
+    SkASSERT(proc);
+
+    SkMatrix matrix;
+    const void* stopText = (const char*)text + byteLength;
+    while ((const char*)text < (const char*)stopText) {
+        matrix.setRSXform(*xform++);
+        matrix.setConcat(this->getTotalMatrix(), matrix);
+        int subLen = proc((const char*)text);
+
+        this->save();
+        this->concat(matrix);
+        this->drawText(text, subLen, 0, 0, paint);
+        this->restore();
+
+        text = (const char*)text + subLen;
+    }
+}
+
+void SkOverdrawCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
+                                      const SkPaint& paint) {
+    SkPaint runPaint = paint;
+    SkTextBlobRunIterator it(blob);
+    for (;!it.done(); it.next()) {
+        size_t textLen = it.glyphCount() * sizeof(uint16_t);
+        const SkPoint& offset = it.offset();
+        it.applyFontToPaint(&runPaint);
+        switch (it.positioning()) {
+            case SkTextBlob::kDefault_Positioning:
+                this->onDrawText(it.glyphs(), textLen, x + offset.x(), y + offset.y(), runPaint);
+                break;
+            case SkTextBlob::kHorizontal_Positioning:
+                this->drawPosTextCommon(it.glyphs(), textLen, it.pos(), 1,
+                                        SkPoint::Make(x, y + offset.y()), runPaint);
+                break;
+            case SkTextBlob::kFull_Positioning:
+                this->drawPosTextCommon(it.glyphs(), textLen, it.pos(), 2, SkPoint::Make(x, y),
+                                        runPaint);
+                break;
+            default:
+                SkASSERT(false);
+                break;
+        }
+    }
+}
+
+void SkOverdrawCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
+                                   const SkPoint texCoords[4], SkBlendMode blendMode,
+                                   const SkPaint&) {
+    fCanvas->onDrawPatch(cubics, colors, texCoords, blendMode, fPaint);
+}
+
+void SkOverdrawCanvas::onDrawPaint(const SkPaint&) {
+    fCanvas->onDrawPaint(fPaint);
+}
+
+void SkOverdrawCanvas::onDrawRect(const SkRect& rect, const SkPaint&) {
+    fCanvas->onDrawRect(rect, fPaint);
+}
+
+void SkOverdrawCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
+    fCanvas->onDrawRegion(region, fPaint);
+}
+
+void SkOverdrawCanvas::onDrawOval(const SkRect& oval, const SkPaint&) {
+    fCanvas->onDrawOval(oval, fPaint);
+}
+
+void SkOverdrawCanvas::onDrawArc(const SkRect& arc, SkScalar startAngle, SkScalar sweepAngle,
+                                 bool useCenter, const SkPaint&) {
+    fCanvas->onDrawArc(arc, startAngle, sweepAngle, useCenter, fPaint);
+}
+
+void SkOverdrawCanvas::onDrawRRect(const SkRRect& rect, const SkPaint&) {
+    fCanvas->onDrawRRect(rect, fPaint);
+}
+
+void SkOverdrawCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint points[],
+                                    const SkPaint&) {
+    fCanvas->onDrawPoints(mode, count, points, fPaint);
+}
+
+void SkOverdrawCanvas::onDrawVertices(VertexMode vertexMode, int vertexCount,
+                                      const SkPoint vertices[], const SkPoint texs[],
+                                      const SkColor colors[], SkBlendMode blendMode,
+                                      const uint16_t indices[], int indexCount, const SkPaint&) {
+    fCanvas->onDrawVertices(vertexMode, vertexCount, vertices, texs, colors, blendMode, indices,
+                              indexCount, fPaint);
+}
+
+void SkOverdrawCanvas::onDrawAtlas(const SkImage* image, const SkRSXform xform[],
+                                   const SkRect texs[], const SkColor colors[], int count,
+                                   SkBlendMode mode, const SkRect* cull, const SkPaint*) {
+    fCanvas->onDrawAtlas(image, xform, texs, colors, count, mode, cull, &fPaint);
+}
+
+void SkOverdrawCanvas::onDrawPath(const SkPath& path, const SkPaint&) {
+    fCanvas->onDrawPath(path, fPaint);
+}
+
+void SkOverdrawCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint*) {
+    fCanvas->onDrawRect(SkRect::MakeXYWH(x, y, image->width(), image->height()), fPaint);
+}
+
+void SkOverdrawCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
+                                       const SkPaint*, SrcRectConstraint) {
+    fCanvas->onDrawRect(dst, fPaint);
+}
+
+void SkOverdrawCanvas::onDrawImageNine(const SkImage*, const SkIRect&, const SkRect& dst,
+                                       const SkPaint*) {
+    fCanvas->onDrawRect(dst, fPaint);
+}
+
+void SkOverdrawCanvas::onDrawImageLattice(const SkImage*, const Lattice&, const SkRect& dst,
+                                          const SkPaint*) {
+    fCanvas->onDrawRect(dst, fPaint);
+}
+
+void SkOverdrawCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
+                                    const SkPaint*) {
+    fCanvas->onDrawRect(SkRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()), fPaint);
+}
+
+void SkOverdrawCanvas::onDrawBitmapRect(const SkBitmap&, const SkRect*, const SkRect& dst,
+                                        const SkPaint*, SrcRectConstraint) {
+    fCanvas->onDrawRect(dst, fPaint);
+}
+
+void SkOverdrawCanvas::onDrawBitmapNine(const SkBitmap&, const SkIRect&, const SkRect& dst,
+                                        const SkPaint*) {
+    fCanvas->onDrawRect(dst, fPaint);
+}
+
+void SkOverdrawCanvas::onDrawBitmapLattice(const SkBitmap&, const Lattice&, const SkRect& dst,
+                                           const SkPaint*) {
+    fCanvas->onDrawRect(dst, fPaint);
+}
+
+void SkOverdrawCanvas::onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) {
+    SkASSERT(false);
+    return;
+}
diff --git a/src/core/SkOverdrawCanvas.h b/src/core/SkOverdrawCanvas.h
new file mode 100644
index 0000000..65eabbc
--- /dev/null
+++ b/src/core/SkOverdrawCanvas.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkOverdrawCanvas_DEFINED
+#define SkOverdrawCanvas_DEFINED
+
+#include "SkCanvas.h"
+
+/**
+ *  Captures all drawing commands.  Rather than draw the actual content, this device
+ *  increments the alpha channel of each pixel every time it would have been touched
+ *  by a draw call.  This is useful for detecting overdraw.
+ */
+class SkOverdrawCanvas : public SkCanvas {
+public:
+    /* Does not take ownership of canvas */
+    SkOverdrawCanvas(SkCanvas*);
+
+    void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
+    void onDrawText(const void*, size_t, SkScalar, SkScalar, const SkPaint&) override;
+    void onDrawPosText(const void*, size_t, const SkPoint[], const SkPaint&) override;
+    void onDrawPosTextH(const void*, size_t, const SkScalar[], SkScalar, const SkPaint&) override;
+    void onDrawTextOnPath(const void*, size_t, const SkPath&, const SkMatrix*,
+                          const SkPaint&) override;
+    void onDrawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*,
+                           const SkPaint&) override;
+    void onDrawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&) override;
+    void onDrawPatch(const SkPoint[12], const SkColor[4], const SkPoint[4], SkBlendMode,
+                     const SkPaint&) override;
+    void onDrawPaint(const SkPaint&) override;
+    void onDrawRect(const SkRect&, const SkPaint&) override;
+    void onDrawRegion(const SkRegion&, const SkPaint&) override;
+    void onDrawOval(const SkRect&, const SkPaint&) override;
+    void onDrawArc(const SkRect&, SkScalar, SkScalar, bool, const SkPaint&) override;
+    void onDrawRRect(const SkRRect&, const SkPaint&) override;
+    void onDrawPoints(PointMode, size_t, const SkPoint[], const SkPaint&) override;
+    void onDrawVertices(VertexMode, int, const SkPoint[], const SkPoint[], const SkColor[],
+                        SkBlendMode, const uint16_t[], int, const SkPaint&) override;
+    void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[],
+                     int, SkBlendMode, const SkRect*, const SkPaint*) override;
+    void onDrawPath(const SkPath&, const SkPaint&) override;
+    void onDrawImage(const SkImage*, SkScalar, SkScalar, const SkPaint*) override;
+    void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*,
+                         SrcRectConstraint) override;
+    void onDrawImageNine(const SkImage*, const SkIRect&, const SkRect&, const SkPaint*) override;
+    void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&, const SkPaint*) override;
+    void onDrawBitmap(const SkBitmap&, SkScalar, SkScalar, const SkPaint*) override;
+    void onDrawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint*,
+                          SrcRectConstraint) override;
+    void onDrawBitmapNine(const SkBitmap&, const SkIRect&, const SkRect&, const SkPaint*) override;
+    void onDrawBitmapLattice(const SkBitmap&, const Lattice&, const SkRect&,
+                             const SkPaint*) override;
+    void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
+
+private:
+    void drawPosTextCommon(const void*, size_t, const SkScalar[], int, const SkPoint&,
+                           const SkPaint&);
+
+    SkCanvas* fCanvas;
+    SkPaint   fPaint;
+
+    typedef SkCanvas INHERITED;
+};
+
+#endif
diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp
index 3d6670f..47583a1 100644
--- a/src/image/SkSurface.cpp
+++ b/src/image/SkSurface.cpp
@@ -9,6 +9,7 @@
 #include "SkSurface_Base.h"
 #include "SkImagePriv.h"
 #include "SkCanvas.h"
+#include "SkOverdrawCanvas.h"
 
 #include "SkFontLCDConfig.h"
 static SkPixelGeometry compute_default_geometry() {
@@ -117,6 +118,10 @@
     }
 }
 
+std::unique_ptr<SkCanvas> SkSurface_Base::onMakeOverdrawCanvas() {
+    return std::unique_ptr<SkCanvas>(new SkOverdrawCanvas(this->getCachedCanvas()));
+}
+
 uint32_t SkSurface_Base::newGenerationID() {
     SkASSERT(!fCachedCanvas || fCachedCanvas->getSurfaceBase() == this);
     static int32_t gID;
diff --git a/src/image/SkSurface_Base.h b/src/image/SkSurface_Base.h
index a8c1d8f..f824bbb 100644
--- a/src/image/SkSurface_Base.h
+++ b/src/image/SkSurface_Base.h
@@ -80,6 +80,13 @@
      */
     virtual void onPrepareForExternalIO() {}
 
+    /**
+     *  Creates an overdraw debugging canvas.
+     *  Draws to this canvas will not actually draw any content.  Instead, they will
+     *  increment the alpha channel each time a pixel would have been touched.
+     *  It may be efficient to use kAlpha8 as the color type on the surface.
+     */
+    std::unique_ptr<SkCanvas> onMakeOverdrawCanvas();
     inline SkCanvas* getCachedCanvas();
     inline sk_sp<SkImage> refCachedImage(SkBudgeted, ForceUnique);
 
diff --git a/tests/SurfaceTest.cpp b/tests/SurfaceTest.cpp
index 2ac8d29..e613e1d 100644
--- a/tests/SurfaceTest.cpp
+++ b/tests/SurfaceTest.cpp
@@ -1021,3 +1021,31 @@
     }
 }
 #endif
+
+static void test_overdraw_surface(skiatest::Reporter* r, SkSurface* surface) {
+    std::unique_ptr<SkCanvas> canvas = ((SkSurface_Base*) surface)->onMakeOverdrawCanvas();
+    canvas->drawPaint(SkPaint());
+    sk_sp<SkImage> image = surface->makeImageSnapshot();
+
+    SkBitmap bitmap;
+    image->asLegacyBitmap(&bitmap, SkImage::kRO_LegacyBitmapMode);
+    bitmap.lockPixels();
+    for (int y = 0; y < 10; y++) {
+        for (int x = 0; x < 10; x++) {
+            REPORTER_ASSERT(r, 1 == SkGetPackedA32(*bitmap.getAddr32(x, y)));
+        }
+    }
+}
+
+DEF_TEST(OverdrawSurface_Raster, r) {
+    sk_sp<SkSurface> surface = create_surface();
+    test_overdraw_surface(r, surface.get());
+}
+
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(OverdrawSurface_Gpu, r, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    sk_sp<SkSurface> surface = create_gpu_surface(context);
+    test_overdraw_surface(r, surface.get());
+}
+#endif