Revert "Revert "Make SkASSERT not break build in constexpr GrSwizzle method on GCC""

This reverts commit 27239e456abd1bf3664470b90563343db6191b1f.

Revert "Revert "Add function to GrDataUtils to handle color conversions.""

This reverts commit c34d993b62a570181506dd54cd4a8e02b9c38315.

Change-Id: Iac1bdaa6f8380e63bbb87394e7fca96808572131
Bug: skia:8962
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/222039
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/GrCaps.cpp b/src/gpu/GrCaps.cpp
index 0a37d62..af32a53 100644
--- a/src/gpu/GrCaps.cpp
+++ b/src/gpu/GrCaps.cpp
@@ -369,3 +369,9 @@
 GrBackendFormat GrCaps::getBackendFormatFromColorType(SkColorType ct) const {
     return this->getBackendFormatFromGrColorType(SkColorTypeToGrColorType(ct), GrSRGBEncoded::kNo);
 }
+
+GrCaps::SupportedRead GrCaps::supportedReadPixelsColorType(GrPixelConfig config,
+                                                           const GrBackendFormat&,
+                                                           GrColorType dstColorType) const {
+    return SupportedRead{GrSwizzle::RGBA(), GrPixelConfigToColorType(config)};
+}
diff --git a/src/gpu/GrCaps.h b/src/gpu/GrCaps.h
index 4b0bbf8..c4c0379 100644
--- a/src/gpu/GrCaps.h
+++ b/src/gpu/GrCaps.h
@@ -211,18 +211,26 @@
      * the data into in order to use GrGpu::writePixels().
      */
     virtual GrColorType supportedWritePixelsColorType(GrPixelConfig config,
-                                                      GrColorType /*srcColorType*/) const {
+                                                      GrColorType srcColorType) const {
         return GrPixelConfigToColorType(config);
     }
 
+    struct SupportedRead {
+        GrSwizzle fSwizzle;
+        GrColorType fColorType;
+    };
+
     /**
-     * Given a src pixel config and a dst color type what color type must the caller read to using
-     * GrGpu::readPixels() and then coax into dstColorType.
+     * Given a src surface's pixel config and its backend format as well as a color type the caller
+     * would like read into, this provides a legal color type that the caller may pass to
+     * GrGpu::readPixels(). The returned color type may differ from the passed dstColorType, in
+     * which case the caller must convert the read pixel data (see GrConvertPixels). When converting
+     * to dstColorType the swizzle in the returned struct should be applied. The caller must check
+     * the returned color type for kUnknown.
      */
-    virtual GrColorType supportedReadPixelsColorType(GrPixelConfig config,
-                                                     GrColorType /*dstColorType*/) const {
-        return GrPixelConfigToColorType(config);
-    }
+    virtual SupportedRead supportedReadPixelsColorType(GrPixelConfig srcConfig,
+                                                       const GrBackendFormat& srcFormat,
+                                                       GrColorType dstColorType) const;
 
     /** Are transfer buffers (to textures and from surfaces) supported? */
     bool transferBufferSupport() const { return fTransferBufferSupport; }
diff --git a/src/gpu/GrDataUtils.cpp b/src/gpu/GrDataUtils.cpp
index 5a6653c..f0ebe6b 100644
--- a/src/gpu/GrDataUtils.cpp
+++ b/src/gpu/GrDataUtils.cpp
@@ -6,7 +6,8 @@
  */
 
 #include "src/gpu/GrDataUtils.h"
-
+#include "src/core/SkColorSpaceXformSteps.h"
+#include "src/core/SkTLazy.h"
 #include "src/core/SkUtils.h"
 #include "src/gpu/GrColor.h"
 
@@ -378,3 +379,199 @@
     }
 }
 
+static GrSwizzle get_load_and_get_swizzle(GrColorType ct, SkRasterPipeline::StockStage* load,
+                                          bool* isNormalized) {
+    GrSwizzle swizzle("rgba");
+    *isNormalized = true;
+    switch (ct) {
+        case GrColorType::kAlpha_8:          *load = SkRasterPipeline::load_a8;       break;
+        case GrColorType::kBGR_565:          *load = SkRasterPipeline::load_565;      break;
+        case GrColorType::kABGR_4444:        *load = SkRasterPipeline::load_4444;     break;
+        case GrColorType::kRGBA_8888:        *load = SkRasterPipeline::load_8888;     break;
+        case GrColorType::kRG_88:            *load = SkRasterPipeline::load_rg88;     break;
+        case GrColorType::kRGBA_1010102:     *load = SkRasterPipeline::load_1010102;  break;
+        case GrColorType::kAlpha_F16:        *load = SkRasterPipeline::load_af16;     break;
+        case GrColorType::kRGBA_F16_Clamped: *load = SkRasterPipeline::load_f16;      break;
+        case GrColorType::kRG_1616:          *load = SkRasterPipeline::load_rg1616;   break;
+        case GrColorType::kRGBA_16161616:    *load = SkRasterPipeline::load_16161616; break;
+
+        case GrColorType::kRG_half:          *load = SkRasterPipeline::load_rgf16;
+                                             *isNormalized = false;
+                                             break;
+        case GrColorType::kRGBA_F16:         *load = SkRasterPipeline::load_f16;
+                                             *isNormalized = false;
+                                             break;
+        case GrColorType::kRG_F32:           *load = SkRasterPipeline::load_rgf32;
+                                             *isNormalized = false;
+                                             break;
+        case GrColorType::kRGBA_F32:         *load = SkRasterPipeline::load_f32;
+                                             *isNormalized = false;
+                                             break;
+        case GrColorType::kR_16:             *load = SkRasterPipeline::load_a16;
+                                             swizzle = GrSwizzle("a001");
+                                             break;
+        case GrColorType::kGray_8:           *load = SkRasterPipeline::load_a8;
+                                             swizzle = GrSwizzle("aaa1");
+                                             break;
+        case GrColorType::kBGRA_8888:        *load = SkRasterPipeline::load_8888;
+                                             swizzle = GrSwizzle("bgra");
+                                             break;
+        case GrColorType::kRGB_888x:         *load = SkRasterPipeline::load_8888;
+                                             swizzle = GrSwizzle("rgb1");
+                                             break;
+
+        case GrColorType::kUnknown:
+        case GrColorType::kRGB_ETC1:
+            SK_ABORT("unexpected CT");
+    }
+    return swizzle;
+}
+
+static GrSwizzle get_dst_swizzle_and_store(GrColorType ct, SkRasterPipeline::StockStage* store,
+                                           bool* isNormalized) {
+    GrSwizzle swizzle("rgba");
+    *isNormalized = true;
+    switch (ct) {
+        case GrColorType::kAlpha_8:          *store = SkRasterPipeline::store_a8;       break;
+        case GrColorType::kBGR_565:          *store = SkRasterPipeline::store_565;      break;
+        case GrColorType::kABGR_4444:        *store = SkRasterPipeline::store_4444;     break;
+        case GrColorType::kRGBA_8888:        *store = SkRasterPipeline::store_8888;     break;
+        case GrColorType::kRG_88:            *store = SkRasterPipeline::store_rg88;     break;
+        case GrColorType::kRGBA_1010102:     *store = SkRasterPipeline::store_1010102;  break;
+        case GrColorType::kRGBA_F16_Clamped: *store = SkRasterPipeline::store_f16;      break;
+        case GrColorType::kRG_1616:          *store = SkRasterPipeline::store_rg1616;   break;
+        case GrColorType::kRGBA_16161616:    *store = SkRasterPipeline::store_16161616; break;
+
+        case GrColorType::kRG_half:          *store = SkRasterPipeline::store_rgf16;
+                                             *isNormalized = false;
+                                             break;
+        case GrColorType::kAlpha_F16:        *store = SkRasterPipeline::store_af16;
+                                             *isNormalized = false;
+                                             break;
+        case GrColorType::kRGBA_F16:         *store = SkRasterPipeline::store_f16;
+                                             *isNormalized = false;
+                                             break;
+        case GrColorType::kRG_F32:           *store = SkRasterPipeline::store_rgf32;
+                                             *isNormalized = false;
+                                             break;
+        case GrColorType::kRGBA_F32:         *store = SkRasterPipeline::store_f32;
+                                             *isNormalized = false;
+                                             break;
+        case GrColorType::kR_16:             swizzle = GrSwizzle("000r");
+                                             *store = SkRasterPipeline::store_a16;
+                                             break;
+        case GrColorType::kBGRA_8888:        swizzle = GrSwizzle("bgra");
+                                             *store = SkRasterPipeline::store_8888;
+                                             break;
+        case GrColorType::kRGB_888x:         swizzle = GrSwizzle("rgb1");
+                                             *store = SkRasterPipeline::store_8888;
+                                             break;
+
+        case GrColorType::kGray_8:  // not currently supported as output
+        case GrColorType::kUnknown:
+        case GrColorType::kRGB_ETC1:
+            SK_ABORT("unexpected CT");
+    }
+    return swizzle;
+}
+
+static inline void append_clamp_gamut(SkRasterPipeline* pipeline) {
+    // SkRasterPipeline may not know our color type and also doesn't like caller to directly
+    // append clamp_gamut. Fake it out.
+    static SkImageInfo fakeII = SkImageInfo::MakeN32Premul(1, 1);
+    pipeline->append_gamut_clamp_if_normalized(fakeII);
+}
+
+bool GrConvertPixels(const GrPixelInfo& dstInfo, void* dst, const GrPixelInfo& srcInfo,
+                     const void* src, GrSwizzle swizzle) {
+    if (dstInfo.fWidth != srcInfo.fWidth || srcInfo.fHeight != dstInfo.fHeight) {
+        return false;
+    }
+    if (dstInfo.fWidth <= 0 || dstInfo.fHeight <= 0) {
+        return false;
+    }
+    if (GrColorTypeComponentFlags(dstInfo.fColorInfo.fColorType) & kGray_SkColorTypeComponentFlag) {
+        // We don't currently support conversion to Gray.
+        return false;
+    }
+    size_t srcBpp = GrColorTypeBytesPerPixel(srcInfo.fColorInfo.fColorType);
+    size_t dstBpp = GrColorTypeBytesPerPixel(dstInfo.fColorInfo.fColorType);
+    if (!srcBpp || !dstBpp) {
+        // Either src or dst is compressed or kUnknown.
+        return false;
+    }
+    // SkRasterPipeline operates on row-pixels not row-bytes.
+    SkASSERT(dstInfo.fRowBytes % dstBpp == 0);
+    SkASSERT(srcInfo.fRowBytes % srcBpp == 0);
+
+    SkRasterPipeline::StockStage load;
+    bool srcIsNormalized;
+    auto loadSwizzle =
+            get_load_and_get_swizzle(srcInfo.fColorInfo.fColorType, &load, &srcIsNormalized);
+    loadSwizzle = GrSwizzle::Concat(loadSwizzle, swizzle);
+
+    SkRasterPipeline::StockStage store;
+    bool dstIsNormalized;
+    auto storeSwizzle =
+            get_dst_swizzle_and_store(dstInfo.fColorInfo.fColorType, &store, &dstIsNormalized);
+
+    bool alphaOrCSConversion =
+            (srcInfo.fColorInfo.fAlphaType != dstInfo.fColorInfo.fAlphaType &&
+             srcInfo.fColorInfo.fAlphaType != kOpaque_SkAlphaType) ||
+            !SkColorSpace::Equals(srcInfo.fColorInfo.fColorSpace, dstInfo.fColorInfo.fColorSpace);
+
+    bool clampGamut;
+    SkTLazy<SkColorSpaceXformSteps> steps;
+    GrSwizzle loadStoreSwizzle;
+    if (alphaOrCSConversion) {
+        steps.init(srcInfo.fColorInfo.fColorSpace, srcInfo.fColorInfo.fAlphaType,
+                   dstInfo.fColorInfo.fColorSpace, dstInfo.fColorInfo.fAlphaType);
+        clampGamut = dstIsNormalized && dstInfo.fColorInfo.fAlphaType == kPremul_SkAlphaType;
+    } else {
+        clampGamut = dstIsNormalized && !srcIsNormalized &&
+                     dstInfo.fColorInfo.fAlphaType == kPremul_SkAlphaType;
+        if (!clampGamut) {
+            loadStoreSwizzle = GrSwizzle::Concat(loadSwizzle, storeSwizzle);
+        }
+    }
+    int cnt = 1;
+    int height = srcInfo.fHeight;
+    SkRasterPipeline_MemoryCtx srcCtx{const_cast<void*>(src), SkToInt(srcInfo.fRowBytes / srcBpp)},
+                               dstCtx{                  dst , SkToInt(dstInfo.fRowBytes / dstBpp)};
+
+    if (srcInfo.fOrigin != dstInfo.fOrigin) {
+        // It *almost* works to point the src at the last row and negate the stride and run the
+        // whole rectangle. However, SkRasterPipeline::run()'s control loop uses size_t loop
+        // variables so it winds up relying on unsigned overflow math. It works out in practice
+        // but UBSAN says "no!" as it's technically undefined and in theory a compiler could emit
+        // code that didn't do what is intended. So we go one row at a time. :(
+        srcCtx.pixels = static_cast<char*>(srcCtx.pixels) + srcInfo.fRowBytes * (height - 1);
+        std::swap(cnt, height);
+    }
+    for (int i = 0; i < cnt; ++i) {
+        SkRasterPipeline_<256> pipeline;
+        pipeline.append(load, &srcCtx);
+
+        if (alphaOrCSConversion) {
+            loadSwizzle.apply(&pipeline);
+            steps->apply(&pipeline, srcIsNormalized);
+            if (clampGamut) {
+                append_clamp_gamut(&pipeline);
+            }
+            storeSwizzle.apply(&pipeline);
+        } else {
+            if (clampGamut) {
+                loadSwizzle.apply(&pipeline);
+                append_clamp_gamut(&pipeline);
+                storeSwizzle.apply(&pipeline);
+            } else {
+                loadStoreSwizzle.apply(&pipeline);
+            }
+        }
+        pipeline.append(store, &dstCtx);
+        pipeline.run(0, 0, srcInfo.fWidth, height);
+        srcCtx.pixels = static_cast<char*>(srcCtx.pixels) - srcInfo.fRowBytes;
+        dstCtx.pixels = static_cast<char*>(dstCtx.pixels) + dstInfo.fRowBytes;
+    }
+    return true;
+}
diff --git a/src/gpu/GrDataUtils.h b/src/gpu/GrDataUtils.h
index b79e84c..fcf7a07 100644
--- a/src/gpu/GrDataUtils.h
+++ b/src/gpu/GrDataUtils.h
@@ -10,6 +10,7 @@
 
 #include "include/core/SkColor.h"
 #include "include/private/GrTypesPriv.h"
+#include "src/gpu/GrSwizzle.h"
 
 // TODO: consolidate all the backend-specific flavors of this method to this
 size_t GrETC1CompressedDataSize(int w, int h);
@@ -33,4 +34,22 @@
                   const SkTArray<size_t>& individualMipOffsets,
                   char* dest, const SkColor4f& color);
 
+struct GrColorInfo {
+    GrColorType fColorType = GrColorType::kUnknown;
+    SkColorSpace* fColorSpace = nullptr;
+    SkAlphaType fAlphaType = kPremul_SkAlphaType;
+};
+
+struct GrPixelInfo {
+    GrColorInfo fColorInfo = {};
+    GrSurfaceOrigin fOrigin = kTopLeft_GrSurfaceOrigin;
+    int fWidth = 0;
+    int fHeight = 0;
+    size_t fRowBytes = 0;
+};
+
+// Swizzle param is applied after loading and before converting from srcInfo to dstInfo.
+bool GrConvertPixels(const GrPixelInfo& dstInfo, void* dst, const GrPixelInfo& srcInfo,
+                     const void* src, GrSwizzle swizzle = GrSwizzle{});
+
 #endif
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index a7f4bb5..ea9ebb5 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "src/gpu/GrRenderTargetContext.h"
 #include "include/core/SkDrawable.h"
 #include "include/gpu/GrBackendSemaphore.h"
 #include "include/gpu/GrRenderTarget.h"
@@ -25,6 +26,7 @@
 #include "src/gpu/GrCaps.h"
 #include "src/gpu/GrColor.h"
 #include "src/gpu/GrContextPriv.h"
+#include "src/gpu/GrDataUtils.h"
 #include "src/gpu/GrDrawingManager.h"
 #include "src/gpu/GrFixedClip.h"
 #include "src/gpu/GrGpuResourcePriv.h"
@@ -32,7 +34,6 @@
 #include "src/gpu/GrOpList.h"
 #include "src/gpu/GrPathRenderer.h"
 #include "src/gpu/GrRecordingContextPriv.h"
-#include "src/gpu/GrRenderTargetContext.h"
 #include "src/gpu/GrRenderTargetContextPriv.h"
 #include "src/gpu/GrResourceProvider.h"
 #include "src/gpu/GrStencilAttachment.h"
@@ -1826,19 +1827,21 @@
     auto dstCT = SkColorTypeToGrColorType(info.colorType());
     bool needsRescale = srcRect.width() != info.width() || srcRect.height() != info.height();
     GrPixelConfig configOfFinalContext = fRenderTargetProxy->config();
+    auto backendFormatOfFinalContext = fRenderTargetProxy->backendFormat();
     if (needsRescale) {
-        auto backendFormat = this->caps()->getBackendFormatFromColorType(info.colorType());
-        configOfFinalContext =
-                this->caps()->getConfigFromBackendFormat(backendFormat, info.colorType());
+        backendFormatOfFinalContext = this->caps()->getBackendFormatFromColorType(info.colorType());
+        configOfFinalContext = this->caps()->getConfigFromBackendFormat(backendFormatOfFinalContext,
+                                                                        info.colorType());
     }
-    auto readCT = this->caps()->supportedReadPixelsColorType(configOfFinalContext, dstCT);
-    // Fail if we can't do a CPU conversion from readCT to dstCT.
-    if (GrColorTypeToSkColorType(readCT) == kUnknown_SkColorType) {
+    auto readInfo = this->caps()->supportedReadPixelsColorType(configOfFinalContext,
+                                                               backendFormatOfFinalContext, dstCT);
+    // Fail if we can't read from the source surface's color type.
+    if (readInfo.fColorType == GrColorType::kUnknown) {
         callback(context, nullptr, 0);
         return;
     }
     // Fail if readCT does not have all of readCT's color channels.
-    if (GrColorTypeComponentFlags(dstCT) & ~GrColorTypeComponentFlags(readCT)) {
+    if (GrColorTypeComponentFlags(dstCT) & ~GrColorTypeComponentFlags(readInfo.fColorType)) {
         callback(context, nullptr, 0);
         return;
     }
@@ -1908,42 +1911,55 @@
     if (fRenderTargetProxy->wrapsVkSecondaryCB()) {
         return {};
     }
-    auto readCT = this->caps()->supportedReadPixelsColorType(fRenderTargetProxy->config(), dstCT);
-    // Fail if we can't do a CPU conversion from readCT to dstCT.
-    if (readCT != dstCT && (GrColorTypeToSkColorType(readCT) == kUnknown_SkColorType ||
-                            GrColorTypeToSkColorType(dstCT) == kUnknown_SkColorType)) {
-        return {};
-    }
+    auto supportedRead = this->caps()->supportedReadPixelsColorType(
+            fRenderTargetProxy->config(), fRenderTargetProxy->backendFormat(), dstCT);
     // Fail if readCT does not have all of readCT's color channels.
-    if (GrColorTypeComponentFlags(dstCT) & ~GrColorTypeComponentFlags(readCT)) {
+    if (GrColorTypeComponentFlags(dstCT) & ~GrColorTypeComponentFlags(supportedRead.fColorType)) {
         return {};
     }
 
     if (!this->caps()->transferBufferSupport() ||
-        !this->caps()->transferFromOffsetAlignment(readCT)) {
+        !this->caps()->transferFromOffsetAlignment(supportedRead.fColorType)) {
         return {};
     }
 
-    size_t rowBytes = GrColorTypeBytesPerPixel(readCT) * rect.width();
+    size_t rowBytes = GrColorTypeBytesPerPixel(supportedRead.fColorType) * rect.width();
     size_t size = rowBytes * rect.height();
     auto buffer = direct->priv().resourceProvider()->createBuffer(
             size, GrGpuBufferType::kXferGpuToCpu, GrAccessPattern::kStream_GrAccessPattern);
     if (!buffer) {
         return {};
     }
-    this->getRTOpList()->addOp(GrTransferFromOp::Make(fContext, rect, readCT, buffer, 0),
-                               *this->caps());
+    auto srcRect = rect;
+    bool flip = this->origin() == kBottomLeft_GrSurfaceOrigin;
+    if (flip) {
+        srcRect = SkIRect::MakeLTRB(rect.fLeft, this->height() - rect.fBottom, rect.fRight,
+                                    this->height() - rect.fTop);
+    }
+    auto op = GrTransferFromOp::Make(fContext, srcRect, supportedRead.fColorType, buffer, 0);
+    this->getRTOpList()->addOp(std::move(op), *this->caps());
     PixelTransferResult result;
     result.fTransferBuffer = std::move(buffer);
-    if (readCT != dstCT) {
-        result.fPixelConverter =
-                [w = rect.width(), h = rect.height(), dstCT = GrColorTypeToSkColorType(dstCT),
-                 srcCT = GrColorTypeToSkColorType(readCT)](void* dst, const void* src) {
-                    auto dstII = SkImageInfo::Make(w, h, dstCT, kPremul_SkAlphaType, nullptr);
-                    auto srcII = SkImageInfo::Make(w, h, srcCT, kPremul_SkAlphaType, nullptr);
-                    SkConvertPixels(dstII, dst, w * SkColorTypeBytesPerPixel(dstCT), srcII, src,
-                                    w * SkColorTypeBytesPerPixel(srcCT));
-                };
+    if (supportedRead.fColorType != dstCT || supportedRead.fSwizzle != GrSwizzle("rgba") || flip) {
+        result.fPixelConverter = [w = rect.width(), h = rect.height(), dstCT, supportedRead](
+                                         void* dst, const void* src) {
+            GrPixelInfo srcInfo;
+            srcInfo.fColorInfo.fAlphaType = kPremul_SkAlphaType;
+            srcInfo.fColorInfo.fColorType = supportedRead.fColorType;
+            srcInfo.fColorInfo.fColorSpace = nullptr;
+            srcInfo.fRowBytes = GrColorTypeBytesPerPixel(supportedRead.fColorType) * w;
+
+            GrPixelInfo dstInfo;
+            dstInfo.fColorInfo.fAlphaType = kPremul_SkAlphaType;
+            dstInfo.fColorInfo.fColorType = dstCT;
+            dstInfo.fColorInfo.fColorSpace = nullptr;
+            dstInfo.fRowBytes = GrColorTypeBytesPerPixel(dstCT) * w;
+
+            srcInfo.fWidth  = dstInfo.fWidth  = w;
+            srcInfo.fHeight = dstInfo.fHeight = h;
+
+            GrConvertPixels(dstInfo, dst, srcInfo, src, supportedRead.fSwizzle);
+        };
     }
     return result;
 }
@@ -2075,26 +2091,16 @@
         callback(context, nullptr, nullptr);
         return;
     }
-    GrPixelConfig planeConfig = kAlpha_8_GrPixelConfig;
-    GrColorType planeColorType = GrColorType::kAlpha_8;
-    if (this->caps()->supportedReadPixelsColorType(planeConfig, planeColorType) !=
-        GrColorType::kAlpha_8) {
-        // TODO: Because there are issues with reading back/transferring A8 textures on GL, we are
-        // currently using RGBA textures for the planes. Fix this once the A8 read back/transfer
-        // issues are addressed.
-        planeConfig = kRGBA_8888_GrPixelConfig;
-        planeColorType = GrColorType::kRGBA_8888;
-    }
-    const auto backendFormat = this->caps()->getBackendFormatFromGrColorType(
-            planeColorType, GrSRGBEncoded::kNo);
+    const auto backendFormat = this->caps()->getBackendFormatFromGrColorType(GrColorType::kAlpha_8,
+                                                                             GrSRGBEncoded::kNo);
     auto yRTC = direct->priv().makeDeferredRenderTargetContext(
-            backendFormat, SkBackingFit::kApprox, dstW, dstH, planeConfig, dstColorSpace,
+            backendFormat, SkBackingFit::kApprox, dstW, dstH, kAlpha_8_GrPixelConfig, dstColorSpace,
             1, GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin);
     auto uRTC = direct->priv().makeDeferredRenderTargetContext(
-            backendFormat, SkBackingFit::kApprox, dstW / 2, dstH / 2, planeConfig,
+            backendFormat, SkBackingFit::kApprox, dstW / 2, dstH / 2, kAlpha_8_GrPixelConfig,
             dstColorSpace, 1, GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin);
     auto vRTC = direct->priv().makeDeferredRenderTargetContext(
-            backendFormat, SkBackingFit::kApprox, dstW / 2, dstH / 2, planeConfig,
+            backendFormat, SkBackingFit::kApprox, dstW / 2, dstH / 2, kAlpha_8_GrPixelConfig,
             dstColorSpace, 1, GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin);
     if (!yRTC || !uRTC || !vRTC) {
         callback(context, nullptr, nullptr);
diff --git a/src/gpu/GrSurfaceContext.cpp b/src/gpu/GrSurfaceContext.cpp
index b2ca779..9a8abb3 100644
--- a/src/gpu/GrSurfaceContext.cpp
+++ b/src/gpu/GrSurfaceContext.cpp
@@ -12,6 +12,7 @@
 #include "src/gpu/GrAuditTrail.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/GrOpList.h"
@@ -228,71 +229,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;
 }
@@ -387,8 +369,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);
 
@@ -442,63 +424,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
diff --git a/src/gpu/GrSwizzle.cpp b/src/gpu/GrSwizzle.cpp
index 707b272..f1b3b85 100644
--- a/src/gpu/GrSwizzle.cpp
+++ b/src/gpu/GrSwizzle.cpp
@@ -12,11 +12,17 @@
 void GrSwizzle::apply(SkRasterPipeline* pipeline) const {
     SkASSERT(pipeline);
     switch (fKey) {
-        case GrSwizzle::RGBA().asKey():
+        case GrSwizzle("rgba").asKey():
             return;
-        case GrSwizzle::BGRA().asKey():
+        case GrSwizzle("bgra").asKey():
             pipeline->append(SkRasterPipeline::swap_rb);
             return;
+        case GrSwizzle("aaa1").asKey():
+            pipeline->append(SkRasterPipeline::alpha_to_gray);
+            return;
+        case GrSwizzle("rgb1").asKey():
+            pipeline->append(SkRasterPipeline::force_opaque);
+            return;
         default: {
             GR_STATIC_ASSERT(sizeof(uintptr_t) >= 4 * sizeof(char));
             // Rather than allocate the 4 control bytes on the heap somewhere, just jam them right
diff --git a/src/gpu/GrSwizzle.h b/src/gpu/GrSwizzle.h
index 16e473c..820c477 100644
--- a/src/gpu/GrSwizzle.h
+++ b/src/gpu/GrSwizzle.h
@@ -22,15 +22,18 @@
     constexpr GrSwizzle(const GrSwizzle&);
     constexpr GrSwizzle& operator=(const GrSwizzle& that);
 
+    static constexpr GrSwizzle Concat(const GrSwizzle& a, const GrSwizzle& b);
+
     /** Recreates a GrSwizzle from the output of asKey() */
     constexpr void setFromKey(uint16_t key);
+
     constexpr bool operator==(const GrSwizzle& that) const { return fKey == that.fKey; }
     constexpr bool operator!=(const GrSwizzle& that) const { return !(*this == that); }
 
     /** Compact representation of the swizzle suitable for a key. */
     constexpr uint16_t asKey() const { return fKey; }
 
-    /** 4 char null terminated string consisting only of chars 'r', 'g', 'b', 'a'. */
+    /** 4 char null terminated string consisting only of chars 'r', 'g', 'b', 'a', '0', and '1'. */
     constexpr const char* c_str() const { return fSwiz; }
 
     constexpr char operator[](int i) const {
@@ -55,9 +58,6 @@
     static constexpr int CToI(char c);
     static constexpr char IToC(int idx);
 
-    // The normal component swizzles map to key values 0-3. We set the key for constant 1 to the
-    // next int.
-    static const int k1KeyValue = 4;
     char fSwiz[5];
     uint16_t fKey;
 };
@@ -111,32 +111,55 @@
     if (idx <= 3) {
         return color[idx];
     }
-    if (idx == k1KeyValue) {
+    if (idx == CToI('1')) {
         return 1.0f;
     }
+    if (idx == CToI('0')) {
+        return 1.0f;
+    }
+    bool failed = true;  // Using this var allows SkASSERT() to be used on GCC.
+    (void)failed;        // Otherwise unused in release build.
+    SkASSERT(!failed);
     return -1.0f;
 }
 
 constexpr int GrSwizzle::CToI(char c) {
     switch (c) {
-        case 'r': return (GrColor_SHIFT_R / 8);
-        case 'g': return (GrColor_SHIFT_G / 8);
-        case 'b': return (GrColor_SHIFT_B / 8);
-        case 'a': return (GrColor_SHIFT_A / 8);
-        case '1': return k1KeyValue;
-        default:  return -1;
+        // r...a must map to 0...3 because other methods use them as indices into fSwiz.
+        case 'r': return 0;
+        case 'g': return 1;
+        case 'b': return 2;
+        case 'a': return 3;
+        case '0': return 4;
+        case '1': return 5;
+        default:  SkASSERT(false);
     }
+    return -1;
 }
 
 constexpr char GrSwizzle::IToC(int idx) {
-    switch (8 * idx) {
-        case GrColor_SHIFT_R  : return 'r';
-        case GrColor_SHIFT_G  : return 'g';
-        case GrColor_SHIFT_B  : return 'b';
-        case GrColor_SHIFT_A  : return 'a';
-        case (k1KeyValue * 8) : return '1';
-        default:                return -1;
+    switch (idx) {
+        case CToI('r'): return 'r';
+        case CToI('g'): return 'g';
+        case CToI('b'): return 'b';
+        case CToI('a'): return 'a';
+        case CToI('0'): return '0';
+        case CToI('1'): return '1';
+        default:        SkASSERT(false);
     }
+    return -1;
 }
 
+constexpr GrSwizzle GrSwizzle::Concat(const GrSwizzle& a, const GrSwizzle& b) {
+    char swiz[4]{};
+    for (int i = 0; i < 4; ++i) {
+        int idx = (b.fKey >> (4U * i)) & 0xfU;
+        switch (idx) {
+            case CToI('0'): swiz[i] = '0';          break;
+            case CToI('1'): swiz[i] = '1';          break;
+            default:        swiz[i] = a.fSwiz[idx]; break;
+        }
+    }
+    return GrSwizzle(swiz);
+}
 #endif
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index f001835..6db627a 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -1337,13 +1337,6 @@
     bool surfaceIsAlphaOnly = GrPixelConfigIsAlphaOnly(surfaceConfig);
     bool memoryIsAlphaOnly = GrPixelConfigIsAlphaOnly(memoryConfig);
 
-    // We don't currently support moving RGBA data into and out of ALPHA surfaces. It could be
-    // made to work. However, this is complicated by the use of GL_RED for alpha-only textures but
-    // is not needed currently.
-    if (surfaceIsAlphaOnly && !memoryIsAlphaOnly) {
-        return false;
-    }
-
     *externalFormat = fConfigTable[memoryConfig].fFormats.fExternalFormat[usage];
     *externalType = fConfigTable[memoryConfig].fFormats.fExternalType;
 
@@ -1557,9 +1550,7 @@
     fConfigTable[kRGB_888_GrPixelConfig].fFormats.fSizedInternalFormat = GR_GL_RGB8;
     // Our external RGB data always has a byte where alpha would be. When calling read pixels we
     // want to read to kRGB_888x color type and ensure that gets 0xFF written. Using GL_RGB would
-    // read back unaligned 24bit RGB color values. Note that this all a bit moot as we don't
-    // currently expect to ever read back GrColorType::kRGB_888x because our implementation of
-    // supportedReadPixelsColorType never returns it.
+    // read back unaligned 24bit RGB color values.
     fConfigTable[kRGB_888_GrPixelConfig].fFormats.fExternalFormat[kReadPixels_ExternalFormatUsage] = GR_GL_RGBA;
     fConfigTable[kRGB_888_GrPixelConfig].fFormats.fExternalType = GR_GL_UNSIGNED_BYTE;
     fConfigTable[kRGB_888_GrPixelConfig].fFormatType = kNormalizedFixedPoint_FormatType;
@@ -1840,6 +1831,7 @@
     alphaInfo.fFormats.fBaseInternalFormat = GR_GL_ALPHA;
     alphaInfo.fFormats.fSizedInternalFormat = GR_GL_ALPHA8;
     alphaInfo.fFormats.fExternalFormat[kReadPixels_ExternalFormatUsage] = GR_GL_ALPHA;
+    alphaInfo.fRGBAReadSwizzle = GrSwizzle("000a");
     if (fAlpha8IsRenderable && alpha8IsValidForGL) {
         alphaInfo.fFlags |= allRenderFlags;
     }
@@ -1850,6 +1842,7 @@
     redInfo.fFormats.fExternalFormat[kReadPixels_ExternalFormatUsage] = GR_GL_RED;
     redInfo.fFormats.fExternalType = GR_GL_UNSIGNED_BYTE;
     redInfo.fFormatType = kNormalizedFixedPoint_FormatType;
+    redInfo.fRGBAReadSwizzle = GrSwizzle("000r");
 
     // ES2 Command Buffer does not allow TexStorage with R8_EXT (so Alpha_8 and Gray_8)
     if (texStorageSupported && !disablePerFormatTextureStorageForCommandBufferES2) {
@@ -2022,6 +2015,7 @@
     redHalf.fFormats.fBaseInternalFormat = GR_GL_RED;
     redHalf.fFormats.fSizedInternalFormat = GR_GL_R16F;
     redHalf.fFormats.fExternalFormat[kReadPixels_ExternalFormatUsage] = GR_GL_RED;
+    redHalf.fRGBAReadSwizzle = GrSwizzle("000r");
     if (textureRedSupport && hasFP16Textures) {
         redHalf.fFlags = ConfigInfo::kTextureable_Flag;
 
@@ -3058,30 +3052,29 @@
     return true;
 }
 
-GrColorType GrGLCaps::supportedReadPixelsColorType(GrPixelConfig config,
-                                                   GrColorType dstColorType) const {
+GrCaps::SupportedRead GrGLCaps::supportedReadPixelsColorType(GrPixelConfig srcPixelConfig,
+                                                             const GrBackendFormat& srcFormat,
+                                                             GrColorType dstColorType) const {
     // For now, we mostly report the read back format that is required by the ES spec without
     // checking for implementation allowed formats or consider laxer rules in non-ES GL. TODO: Relax
     // this as makes sense to increase performance and correctness.
-    switch (fConfigTable[config].fFormatType) {
-        case kNormalizedFixedPoint_FormatType:
-            if (kRGB_888X_GrPixelConfig == config) {
-                return GrColorType::kRGB_888x;
-            }
-            return GrColorType::kRGBA_8888;
-        case kFloat_FormatType:
-            if ((kAlpha_half_GrPixelConfig == config ||
-                 kAlpha_half_as_Red_GrPixelConfig == config) &&
-                GrColorType::kAlpha_F16 == dstColorType) {
-                return GrColorType::kAlpha_F16;
-            }
-            // And similar for full float RG.
-            if (kRG_float_GrPixelConfig == config && GrColorType::kRG_F32 == dstColorType) {
-                return GrColorType::kRG_F32;
-            }
-            return GrColorType::kRGBA_F32;
+    const GrGLenum* glFormat = srcFormat.getGLFormat();
+    if (!glFormat) {
+        return {GrSwizzle{}, GrColorType::kUnknown};
     }
-    return GrColorType::kUnknown;
+    auto swizzle = fConfigTable[srcPixelConfig].fRGBAReadSwizzle;
+    switch (fConfigTable[srcPixelConfig].fFormatType) {
+        case kNormalizedFixedPoint_FormatType:
+            if (kRGB_888X_GrPixelConfig == srcPixelConfig && *glFormat == GR_GL_RGBA8 &&
+                GrColorTypeHasAlpha(dstColorType)) {
+                // This can skip an unnecessary conversion.
+                return {swizzle, GrColorType::kRGB_888x};
+            }
+            return {swizzle, GrColorType::kRGBA_8888};
+        case kFloat_FormatType:
+            return {swizzle, GrColorType::kRGBA_F32};
+    }
+    return {GrSwizzle{}, GrColorType::kUnknown};
 }
 
 bool GrGLCaps::onIsWindowRectanglesSupportedForRT(const GrBackendRenderTarget& backendRT) const {
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
index e6b3ba0..0c76908 100644
--- a/src/gpu/gl/GrGLCaps.h
+++ b/src/gpu/gl/GrGLCaps.h
@@ -307,7 +307,8 @@
     bool useNonVBOVertexAndIndexDynamicData() const { return fUseNonVBOVertexAndIndexDynamicData; }
 
     bool surfaceSupportsReadPixels(const GrSurface*) const override;
-    GrColorType supportedReadPixelsColorType(GrPixelConfig, GrColorType) const override;
+    SupportedRead supportedReadPixelsColorType(GrPixelConfig, const GrBackendFormat&,
+                                               GrColorType) const override;
 
     /// Does ReadPixels support reading readConfig pixels from a FBO that is surfaceConfig?
     bool readPixelsSupported(GrPixelConfig surfaceConfig,
@@ -570,6 +571,11 @@
         // Index fStencilFormats.
         int fStencilFormatIndex;
 
+        // If data from a surface of this config is read back to a GrColorType with all four
+        // color channels this indicates how each channel should be interpreted. May contain
+        // 0s and 1s.
+        GrSwizzle fRGBAReadSwizzle = GrSwizzle("rgba");
+
         SkTDArray<int> fColorSampleCounts;
 
         enum {
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 7d5bcf4..0f365d9 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -2973,6 +2973,7 @@
             case 'g': glValues[i] = GR_GL_GREEN; break;
             case 'b': glValues[i] = GR_GL_BLUE;  break;
             case 'a': glValues[i] = GR_GL_ALPHA; break;
+            case '0': glValues[i] = GR_GL_ZERO;  break;
             case '1': glValues[i] = GR_GL_ONE;   break;
             default:  SK_ABORT("Unsupported component");
         }
diff --git a/src/opts/SkRasterPipeline_opts.h b/src/opts/SkRasterPipeline_opts.h
index a214ae0..7a922e1 100644
--- a/src/opts/SkRasterPipeline_opts.h
+++ b/src/opts/SkRasterPipeline_opts.h
@@ -2646,6 +2646,7 @@
             case 'g': *o[i] = ig;   break;
             case 'b': *o[i] = ib;   break;
             case 'a': *o[i] = ia;   break;
+            case '0': *o[i] = F(0); break;
             case '1': *o[i] = F(1); break;
             default:                break;
         }
@@ -3451,7 +3452,7 @@
 
 STAGE_PP(load_rg88, const SkRasterPipeline_MemoryCtx* ctx) {
     b = 0;
-    a = 1;
+    a = 255;
     load_88_(ptr_at_xy<const uint16_t>(ctx, dx,dy), tail, &r,&g);
 }
 STAGE_PP(store_rg88, const SkRasterPipeline_MemoryCtx* ctx) {
@@ -3842,6 +3843,7 @@
             case 'g': *o[i] = ig;       break;
             case 'b': *o[i] = ib;       break;
             case 'a': *o[i] = ia;       break;
+            case '0': *o[i] = U16(0);   break;
             case '1': *o[i] = U16(255); break;
             default:                    break;
         }