implement A8 destination fast-path for SkPixelInfo::CopyPixels

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

Review-Url: https://codereview.chromium.org/2407093002
diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp
index 2663086..c62f5f3 100644
--- a/src/core/SkBitmap.cpp
+++ b/src/core/SkBitmap.cpp
@@ -8,6 +8,7 @@
 #include "SkAtomics.h"
 #include "SkBitmap.h"
 #include "SkColorPriv.h"
+#include "SkConfig8888.h"
 #include "SkData.h"
 #include "SkFilterQuality.h"
 #include "SkMallocPixelRef.h"
@@ -906,72 +907,21 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static void rect_memset(uint8_t* array, U8CPU value, SkISize size, size_t rowBytes) {
-    for (int y = 0; y < size.height(); ++y) {
-        memset(array, value, size.width());
-        array += rowBytes;
-    }
-}
-
-static void get_bitmap_alpha(const SkPixmap& pmap, uint8_t* SK_RESTRICT alpha, int alphaRowBytes) {
-    SkColorType colorType = pmap.colorType();
-    int         w = pmap.width();
-    int         h = pmap.height();
-    size_t      rb = pmap.rowBytes();
-
-    if (kAlpha_8_SkColorType == colorType && !pmap.isOpaque()) {
-        const uint8_t* s = pmap.addr8(0, 0);
-        while (--h >= 0) {
-            memcpy(alpha, s, w);
-            s += rb;
-            alpha += alphaRowBytes;
-        }
-    } else if (kN32_SkColorType == colorType && !pmap.isOpaque()) {
-        const SkPMColor* SK_RESTRICT s = pmap.addr32(0, 0);
-        while (--h >= 0) {
-            for (int x = 0; x < w; x++) {
-                alpha[x] = SkGetPackedA32(s[x]);
-            }
-            s = (const SkPMColor*)((const char*)s + rb);
-            alpha += alphaRowBytes;
-        }
-    } else if (kARGB_4444_SkColorType == colorType && !pmap.isOpaque()) {
-        const SkPMColor16* SK_RESTRICT s = pmap.addr16(0, 0);
-        while (--h >= 0) {
-            for (int x = 0; x < w; x++) {
-                alpha[x] = SkPacked4444ToA32(s[x]);
-            }
-            s = (const SkPMColor16*)((const char*)s + rb);
-            alpha += alphaRowBytes;
-        }
-    } else if (kIndex_8_SkColorType == colorType && !pmap.isOpaque()) {
-        const SkColorTable* ct = pmap.ctable();
-        if (ct) {
-            const SkPMColor* SK_RESTRICT table = ct->readColors();
-            const uint8_t* SK_RESTRICT s = pmap.addr8(0, 0);
-            while (--h >= 0) {
-                for (int x = 0; x < w; x++) {
-                    alpha[x] = SkGetPackedA32(table[s[x]]);
-                }
-                s += rb;
-                alpha += alphaRowBytes;
-            }
-        }
-    } else {    // src is opaque, so just fill alpha[] with 0xFF
-        rect_memset(alpha, 0xFF, pmap.info().dimensions(), alphaRowBytes);
-    }
-}
-
 static bool GetBitmapAlpha(const SkBitmap& src, uint8_t* SK_RESTRICT alpha, int alphaRowBytes) {
     SkASSERT(alpha != nullptr);
     SkASSERT(alphaRowBytes >= src.width());
 
     SkAutoPixmapUnlock apl;
     if (!src.requestLock(&apl)) {
-        rect_memset(alpha, 0, src.info().dimensions(), alphaRowBytes);
+        for (int y = 0; y < src.height(); ++y) {
+            memset(alpha, 0, src.width());
+            alpha += alphaRowBytes;
+        }
         return false;
     }
-    get_bitmap_alpha(apl.pixmap(), alpha, alphaRowBytes);
+    const SkPixmap& pmap = apl.pixmap();
+    SkPixelInfo::CopyPixels(SkImageInfo::MakeA8(pmap.width(), pmap.height()), alpha, alphaRowBytes,
+                            pmap.info(), pmap.addr(), pmap.rowBytes(), pmap.ctable());
     return true;
 }
 
diff --git a/src/core/SkConfig8888.cpp b/src/core/SkConfig8888.cpp
index 31def9a9..7c3f021 100644
--- a/src/core/SkConfig8888.cpp
+++ b/src/core/SkConfig8888.cpp
@@ -167,6 +167,64 @@
     }
 }
 
+static bool extract_alpha(void* dst, size_t dstRB, const void* src, size_t srcRB,
+                          const SkImageInfo& srcInfo, SkColorTable* ctable) {
+    uint8_t* SK_RESTRICT dst8 = (uint8_t*)dst;
+
+    const int w = srcInfo.width();
+    const int h = srcInfo.height();
+    if (srcInfo.isOpaque()) {
+        // src is opaque, so just fill alpha with 0xFF
+        for (int y = 0; y < h; ++y) {
+           memset(dst8, 0xFF, w);
+           dst8 += dstRB;
+        }
+        return true;
+    }
+    switch (srcInfo.colorType()) {
+        case kN32_SkColorType: {
+            const SkPMColor* SK_RESTRICT src32 = (const SkPMColor*)src;
+            for (int y = 0; y < h; ++y) {
+                for (int x = 0; x < w; ++x) {
+                    dst8[x] = SkGetPackedA32(src32[x]);
+                }
+                dst8 += dstRB;
+                src32 = (const SkPMColor*)((const char*)src32 + srcRB);
+            }
+            break;
+        }
+        case kARGB_4444_SkColorType: {
+            const SkPMColor16* SK_RESTRICT src16 = (const SkPMColor16*)src;
+            for (int y = 0; y < h; ++y) {
+                for (int x = 0; x < w; ++x) {
+                    dst8[x] = SkPacked4444ToA32(src16[x]);
+                }
+                dst8 += dstRB;
+                src16 = (const SkPMColor16*)((const char*)src16 + srcRB);
+            }
+            break;
+        }
+        case kIndex_8_SkColorType: {
+            if (nullptr == ctable) {
+                return false;
+            }
+            const SkPMColor* SK_RESTRICT table = ctable->readColors();
+            const uint8_t* SK_RESTRICT src8 = (const uint8_t*)src;
+            for (int y = 0; y < h; ++y) {
+                for (int x = 0; x < w; ++x) {
+                    dst8[x] = SkGetPackedA32(table[src8[x]]);
+                }
+                dst8 += dstRB;
+                src8 += srcRB;
+            }
+            break;
+        }
+        default:
+            return false;
+    }
+    return true;
+}
+
 bool SkPixelInfo::CopyPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
                              const SkImageInfo& srcInfo, const void* srcPixels, size_t srcRB,
                              SkColorTable* ctable) {
@@ -241,6 +299,11 @@
         return true;
     }
 
+    if (kAlpha_8_SkColorType == dstInfo.colorType() &&
+        extract_alpha(dstPixels, dstRB, srcPixels, srcRB, srcInfo, ctable)) {
+        return true;
+    }
+
     // Can no longer draw directly into 4444, but we can manually whack it for a few combinations
     if (kARGB_4444_SkColorType == dstInfo.colorType() &&
         (kN32_SkColorType == srcInfo.colorType() || kIndex_8_SkColorType == srcInfo.colorType())) {
diff --git a/tests/ReadPixelsTest.cpp b/tests/ReadPixelsTest.cpp
index 008781c..5fa7dd1 100644
--- a/tests/ReadPixelsTest.cpp
+++ b/tests/ReadPixelsTest.cpp
@@ -135,8 +135,14 @@
     intptr_t pixels = reinterpret_cast<intptr_t>(bitmap->getPixels());
     for (int y = 0; y < h; ++y) {
         for (int x = 0; x < w; ++x) {
-            SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bitmap->rowBytes() + x * bitmap->bytesPerPixel());
-            *pixel = get_dst_bmp_init_color(x, y, w);
+            SkPMColor initColor = get_dst_bmp_init_color(x, y, w);
+            if (kAlpha_8_SkColorType == bitmap->colorType()) {
+                uint8_t* alpha = reinterpret_cast<uint8_t*>(pixels + y * bitmap->rowBytes() + x);
+                *alpha = SkGetPackedA32(initColor);
+            } else {
+                SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bitmap->rowBytes() + x * bitmap->bytesPerPixel());
+                *pixel = initColor;
+            }
         }
     }
 }
@@ -168,14 +174,13 @@
                        const SkBitmap& bitmap,
                        int x, int y,
                        bool checkCanvasPixels,
-                       bool checkBitmapPixels) {
-    SkASSERT(4 == bitmap.bytesPerPixel());
+                       bool checkBitmapPixels,
+                       SkColorType ct,
+                       SkAlphaType at) {
+    SkASSERT(ct == bitmap.colorType() && at == bitmap.alphaType());
     SkASSERT(!bitmap.isNull());
     SkASSERT(checkCanvasPixels || checkBitmapPixels);
 
-    const SkColorType ct = bitmap.colorType();
-    const SkAlphaType at = bitmap.alphaType();
-
     int bw = bitmap.width();
     int bh = bitmap.height();
 
@@ -185,6 +190,34 @@
         clippedSrcRect.setEmpty();
     }
     SkAutoLockPixels alp(bitmap);
+    if (kAlpha_8_SkColorType == ct) {
+        for (int by = 0; by < bh; ++by) {
+            for (int bx = 0; bx < bw; ++bx) {
+                int devx = bx + srcRect.fLeft;
+                int devy = by + srcRect.fTop;
+                const uint8_t* alpha = bitmap.getAddr8(bx, by);
+
+                if (clippedSrcRect.contains(devx, devy)) {
+                    if (checkCanvasPixels) {
+                        uint8_t canvasAlpha = SkGetPackedA32(get_src_color(devx, devy));
+                        if (canvasAlpha != *alpha) {
+                            ERRORF(reporter, "Expected readback alpha (%d, %d) value 0x%02x, got 0x%02x. ",
+                                   bx, by, canvasAlpha, *alpha);
+                            return false;
+                        }
+                    }
+                } else if (checkBitmapPixels) {
+                    uint32_t origDstAlpha = SkGetPackedA32(get_dst_bmp_init_color(bx, by, bw));
+                    if (origDstAlpha != *alpha) {
+                        ERRORF(reporter, "Expected clipped out area of readback to be unchanged. "
+                            "Expected 0x%02x, got 0x%02x", origDstAlpha, *alpha);
+                        return false;
+                    }
+                }
+            }
+        }
+        return true;
+    }
     for (int by = 0; by < bh; ++by) {
         for (int bx = 0; bx < bw; ++bx) {
             int devx = bx + srcRect.fLeft;
@@ -249,10 +282,10 @@
         case kTight_BitmapInit:
             break;
         case kRowBytes_BitmapInit:
-            rowBytes = (info.width() + 16) * sizeof(SkPMColor);
+            rowBytes = SkAlign4((info.width() + 16) * info.bytesPerPixel());
             break;
         case kRowBytesOdd_BitmapInit:
-            rowBytes = (info.width() * sizeof(SkPMColor)) + 3;
+            rowBytes = SkAlign4(info.width() * info.bytesPerPixel()) + 3;
             break;
         default:
             SkASSERT(0);
@@ -274,6 +307,7 @@
     { kRGBA_8888_SkColorType,   kUnpremul_SkAlphaType },
     { kBGRA_8888_SkColorType,   kPremul_SkAlphaType },
     { kBGRA_8888_SkColorType,   kUnpremul_SkAlphaType },
+    { kAlpha_8_SkColorType,     kPremul_SkAlphaType },
 };
 const SkIRect gReadPixelsTestRects[] = {
     // entire thing
@@ -354,7 +388,8 @@
 
                 if (success || startsWithPixels) {
                     check_read(reporter, bmp, srcRect.fLeft, srcRect.fTop,
-                               success, startsWithPixels);
+                               success, startsWithPixels,
+                               gReadPixelsConfigs[c].fColorType, gReadPixelsConfigs[c].fAlphaType);
                 } else {
                     // if we had no pixels beforehand and the readPixels
                     // failed then our bitmap should still not have pixels
@@ -371,7 +406,8 @@
                 REPORTER_ASSERT(reporter, kN32_SkColorType == wkbmp.colorType());
                 REPORTER_ASSERT(reporter, kPremul_SkAlphaType == wkbmp.alphaType());
                 check_read(reporter, wkbmp, clippedRect.fLeft,
-                           clippedRect.fTop, true, false);
+                           clippedRect.fTop, true, false,
+                           kN32_SkColorType, kPremul_SkAlphaType);
             } else {
                 REPORTER_ASSERT(reporter, !success);
             }
@@ -427,7 +463,8 @@
                                                        bmp.rowBytes(), flags);
                     bmp.unlockPixels();
                     check_read(reporter, bmp, srcRect.fLeft, srcRect.fTop,
-                               success, true);
+                               success, true,
+                               gReadPixelsConfigs[c].fColorType, gReadPixelsConfigs[c].fAlphaType);
                 }
             }
         }