Add function to GrDataUtils to handle color conversions.

Like SkConvertPixels but knows about all GrColorTypes, origin, and can
apply an arbitrary GrSwizzle.

Use in GrSurfaceContext read/write pixels methods.

Add support for '0' to GrSwizzle.


Change-Id: Ib9dd215fcb0ee8b33c4020893c22b4ab7ce1f40b
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/220761
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
diff --git a/src/gpu/GrSurfaceContext.cpp b/src/gpu/GrSurfaceContext.cpp
index 71113b4..4b29ce0 100644
--- a/src/gpu/GrSurfaceContext.cpp
+++ b/src/gpu/GrSurfaceContext.cpp
@@ -13,6 +13,7 @@
 #include "src/core/SkAutoPixmapStorage.h"
 #include "src/gpu/GrClip.h"
 #include "src/gpu/GrContextPriv.h"
+#include "src/gpu/GrDataUtils.h"
 #include "src/gpu/GrDrawingManager.h"
 #include "src/gpu/GrGpu.h"
 #include "src/gpu/GrRecordingContextPriv.h"
@@ -224,71 +225,52 @@
                                        buffer, rowBytes, flags);
     }
 
-    bool convert = unpremul || needColorConversion;
-
     bool flip = srcProxy->origin() == kBottomLeft_GrSurfaceOrigin;
-    if (flip) {
-        top = srcSurface->height() - top - height;
-    }
+    auto supportedRead = caps->supportedReadPixelsColorType(
+            srcProxy->config(), srcProxy->backendFormat(), dstColorType);
 
-    GrColorType allowedColorType = caps->supportedReadPixelsColorType(srcProxy->config(),
-                                                                      dstColorType);
-    convert = convert || (dstColorType != allowedColorType);
+    bool convert = unpremul || needColorConversion || flip ||
+                   (dstColorType != supportedRead.fColorType) ||
+                   supportedRead.fSwizzle != GrSwizzle::RGBA();
 
-    SkAutoPixmapStorage tempPixmap;
-    SkPixmap finalPixmap;
+    std::unique_ptr<char[]> tmpPixels;
+    GrPixelInfo tmpInfo;
+    GrPixelInfo dstInfo;
+    void* readDst = buffer;
     if (convert) {
-        SkColorType srcSkColorType = GrColorTypeToSkColorType(allowedColorType);
-        SkColorType dstSkColorType = GrColorTypeToSkColorType(dstColorType);
-        bool srcAlwaysOpaque = SkColorTypeIsAlwaysOpaque(srcSkColorType);
-        bool dstAlwaysOpaque = SkColorTypeIsAlwaysOpaque(dstSkColorType);
-        if (kUnknown_SkColorType == srcSkColorType || kUnknown_SkColorType == dstSkColorType) {
-            return false;
-        }
-        auto tempAT = srcAlwaysOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
-        auto tempII = SkImageInfo::Make(width, height, srcSkColorType, tempAT,
-                                        this->colorSpaceInfo().refColorSpace());
-        SkASSERT(!unpremul || !dstAlwaysOpaque);
-        auto finalAT = (srcAlwaysOpaque || dstAlwaysOpaque)
-                               ? kOpaque_SkAlphaType
-                               : unpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType;
-        auto finalII =
-                SkImageInfo::Make(width, height, dstSkColorType, finalAT, sk_ref_sp(dstColorSpace));
-        if (!SkImageInfoValidConversion(finalII, tempII)) {
-            return false;
-        }
-        if (!tempPixmap.tryAlloc(tempII)) {
-            return false;
-        }
-        finalPixmap.reset(finalII, buffer, rowBytes);
-        buffer = tempPixmap.writable_addr();
-        rowBytes = tempPixmap.rowBytes();
-        // Chrome msan bots require this.
-        sk_bzero(buffer, tempPixmap.computeByteSize());
+        tmpInfo.fColorInfo.fColorType = supportedRead.fColorType;
+        tmpInfo.fColorInfo.fAlphaType = kPremul_SkAlphaType;
+        tmpInfo.fColorInfo.fColorSpace = this->colorSpaceInfo().colorSpace();
+        tmpInfo.fOrigin = srcProxy->origin();
+        tmpInfo.fRowBytes = GrColorTypeBytesPerPixel(supportedRead.fColorType) * width;
+
+        dstInfo.fColorInfo.fColorType = dstColorType;
+        dstInfo.fColorInfo.fAlphaType = unpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType;
+        dstInfo.fColorInfo.fColorSpace = dstColorSpace;
+        dstInfo.fOrigin = kTopLeft_GrSurfaceOrigin;
+        dstInfo.fRowBytes = rowBytes;
+
+        dstInfo.fWidth = tmpInfo.fWidth = width;
+        dstInfo.fHeight = tmpInfo.fHeight = height;
+
+        size_t size = tmpInfo.fRowBytes * height;
+        tmpPixels.reset(new char[size]);
+        // Chrome MSAN bots require this.
+        sk_bzero(tmpPixels.get(), size);
+        readDst = tmpPixels.get();
+        rowBytes = tmpInfo.fRowBytes;
+        top = flip ? srcSurface->height() - top - height : top;
     }
 
     direct->priv().flushSurface(srcProxy);
 
-    if (!direct->priv().getGpu()->readPixels(srcSurface, left, top, width, height, allowedColorType,
-                                             buffer, rowBytes)) {
+    if (!direct->priv().getGpu()->readPixels(srcSurface, left, top, width, height,
+                                             supportedRead.fColorType, readDst, rowBytes)) {
         return false;
     }
 
-    if (flip) {
-        size_t trimRowBytes = GrColorTypeBytesPerPixel(allowedColorType) * width;
-        std::unique_ptr<char[]> row(new char[trimRowBytes]);
-        char* upper = reinterpret_cast<char*>(buffer);
-        char* lower = reinterpret_cast<char*>(buffer) + (height - 1) * rowBytes;
-        for (int y = 0; y < height / 2; ++y, upper += rowBytes, lower -= rowBytes) {
-            memcpy(row.get(), upper, trimRowBytes);
-            memcpy(upper, lower, trimRowBytes);
-            memcpy(lower, row.get(), trimRowBytes);
-        }
-    }
     if (convert) {
-        if (!tempPixmap.readPixels(finalPixmap)) {
-            return false;
-        }
+        return GrConvertPixels(dstInfo, buffer, tmpInfo, tmpPixels.get(), supportedRead.fSwizzle);
     }
     return true;
 }
@@ -383,8 +365,8 @@
         // data into it which requires the origins to match. If the final proxy is a render target
         // we can use a draw instead which doesn't have this origin restriction. Thus for render
         // targets we will use top left and otherwise we will make the origins match.
-        GrSurfaceOrigin tempOrigin = this->asRenderTargetContext() ? kTopLeft_GrSurfaceOrigin :
-                                                                     dstProxy->origin();
+        GrSurfaceOrigin tempOrigin =
+                this->asRenderTargetContext() ? kTopLeft_GrSurfaceOrigin : dstProxy->origin();
         auto tempProxy = direct->priv().proxyProvider()->createProxy(
                 format, desc, tempOrigin, SkBackingFit::kApprox, SkBudgeted::kYes);
 
@@ -438,63 +420,44 @@
         return true;
     }
 
-    bool convert = premul || needColorConversion;
-
     if (!valid_pixel_conversion(srcColorType, dstProxy->config(), premul)) {
         return false;
     }
 
     GrColorType allowedColorType = caps->supportedWritePixelsColorType(dstProxy->config(),
                                                                        srcColorType);
-    convert = convert || (srcColorType != allowedColorType);
+    bool convert = premul || needColorConversion || (srcColorType != allowedColorType) ||
+                   dstProxy->origin() == kBottomLeft_GrSurfaceOrigin;
 
-    std::unique_ptr<char[]> tempBuffer;
+    std::unique_ptr<char[]> tmpPixels;
     if (convert) {
-        auto srcSkColorType = GrColorTypeToSkColorType(srcColorType);
-        auto dstSkColorType = GrColorTypeToSkColorType(allowedColorType);
-        if (kUnknown_SkColorType == srcSkColorType || kUnknown_SkColorType == dstSkColorType) {
-            return false;
-        }
-        auto srcAlphaType = SkColorTypeIsAlwaysOpaque(srcSkColorType)
-                ? kOpaque_SkAlphaType
-                : (premul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType);
-        SkPixmap src(SkImageInfo::Make(width, height, srcSkColorType, srcAlphaType,
-                                       sk_ref_sp(srcColorSpace)),
-                     srcBuffer, srcRowBytes);
-        auto tempSrcII = SkImageInfo::Make(width, height, dstSkColorType, kPremul_SkAlphaType,
-                                           this->colorSpaceInfo().refColorSpace());
-        auto size = tempSrcII.computeMinByteSize();
-        if (!size) {
-            return false;
-        }
-        tempBuffer.reset(new char[size]);
-        SkPixmap tempSrc(tempSrcII, tempBuffer.get(), tempSrcII.minRowBytes());
-        if (!src.readPixels(tempSrc)) {
-            return false;
-        }
-        srcColorType = allowedColorType;
-        srcBuffer = tempSrc.addr();
-        srcRowBytes = tempSrc.rowBytes();
+        GrPixelInfo srcInfo;
+        srcInfo.fColorInfo.fAlphaType = (premul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType);
+        srcInfo.fColorInfo.fColorType = srcColorType;
+        srcInfo.fColorInfo.fColorSpace = srcColorSpace;
+        srcInfo.fRowBytes = srcRowBytes;
+        srcInfo.fOrigin = kTopLeft_GrSurfaceOrigin;
+
+        GrPixelInfo tmpInfo;
+        tmpInfo.fColorInfo.fAlphaType = kPremul_SkAlphaType;
+        tmpInfo.fColorInfo.fColorType = allowedColorType;
+        tmpInfo.fColorInfo.fColorSpace = this->colorSpaceInfo().colorSpace();
+        tmpInfo.fRowBytes = GrColorTypeBytesPerPixel(allowedColorType) * width;
+        tmpInfo.fOrigin = dstProxy->origin();
+
+        srcInfo.fWidth  = tmpInfo.fWidth  = width;
+        srcInfo.fHeight = tmpInfo.fHeight = height;
+
+        tmpPixels.reset(new char[tmpInfo.fRowBytes * height]);
+
+        GrConvertPixels(tmpInfo, tmpPixels.get(), srcInfo, srcBuffer);
+
+        srcColorType = tmpInfo.fColorInfo.fColorType;
+        srcBuffer = tmpPixels.get();
+        srcRowBytes = tmpInfo.fRowBytes;
         if (dstProxy->origin() == kBottomLeft_GrSurfaceOrigin) {
-            std::unique_ptr<char[]> row(new char[srcRowBytes]);
-            for (int y = 0; y < height / 2; ++y) {
-                memcpy(row.get(), tempSrc.addr(0, y), srcRowBytes);
-                memcpy(tempSrc.writable_addr(0, y), tempSrc.addr(0, height - 1 - y), srcRowBytes);
-                memcpy(tempSrc.writable_addr(0, height - 1 - y), row.get(), srcRowBytes);
-            }
             top = dstSurface->height() - top - height;
         }
-    } else if (dstProxy->origin() == kBottomLeft_GrSurfaceOrigin) {
-        size_t trimRowBytes = GrColorTypeBytesPerPixel(srcColorType) * width;
-        tempBuffer.reset(new char[trimRowBytes * height]);
-        char* dst = reinterpret_cast<char*>(tempBuffer.get()) + trimRowBytes * (height - 1);
-        const char* src = reinterpret_cast<const char*>(srcBuffer);
-        for (int i = 0; i < height; ++i, src += srcRowBytes, dst -= trimRowBytes) {
-            memcpy(dst, src, trimRowBytes);
-        }
-        srcBuffer = tempBuffer.get();
-        srcRowBytes = trimRowBytes;
-        top = dstSurface->height() - top - height;
     }
 
     // On platforms that prefer flushes over VRAM use (i.e., ANGLE) we're better off forcing a