Reland "Add sRGB 8888 colortype"

This is a reland of 0f7c10ef5681d5c739bcd8862c58d856fd663a0c

Original change's description:
> Add sRGB 8888 colortype
>
> A color type that linearizes just after loading, and re-encodes to sRGB
> just before storing, mimicking the GPU formats that work the same way.
>
> Notes:
>   - No mipmap support
>   - No SkPngEncoder support (HashAndEncode's .pngs are ok, though?)
>   - Needs better testing
>
> This is a re-creation of reviews.skia.org/392990
>
> Change-Id: I4739c2280211e7176aae98ba0a8476a7fe5efa72
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/438219
> Commit-Queue: Brian Osman <brianosman@google.com>
> Reviewed-by: Brian Salomon <bsalomon@google.com>

Change-Id: I5b6bb28c4c1faa6c97fcad7552d12c331535714d
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/441402
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/bench/nanobench.cpp b/bench/nanobench.cpp
index c16eeb0..65a2d87 100644
--- a/bench/nanobench.cpp
+++ b/bench/nanobench.cpp
@@ -530,12 +530,13 @@
 
     CPU_CONFIG("nonrendering", kNonRendering_Backend, kUnknown_SkColorType, kUnpremul_SkAlphaType)
 
-    CPU_CONFIG("a8",   kRaster_Backend,   kAlpha_8_SkColorType, kPremul_SkAlphaType)
-    CPU_CONFIG("565",  kRaster_Backend,   kRGB_565_SkColorType, kOpaque_SkAlphaType)
-    CPU_CONFIG("8888", kRaster_Backend,       kN32_SkColorType, kPremul_SkAlphaType)
-    CPU_CONFIG("rgba", kRaster_Backend, kRGBA_8888_SkColorType, kPremul_SkAlphaType)
-    CPU_CONFIG("bgra", kRaster_Backend, kBGRA_8888_SkColorType, kPremul_SkAlphaType)
-    CPU_CONFIG("f16",  kRaster_Backend,  kRGBA_F16_SkColorType, kPremul_SkAlphaType)
+    CPU_CONFIG("a8",    kRaster_Backend,    kAlpha_8_SkColorType, kPremul_SkAlphaType)
+    CPU_CONFIG("565",   kRaster_Backend,    kRGB_565_SkColorType, kOpaque_SkAlphaType)
+    CPU_CONFIG("8888",  kRaster_Backend,        kN32_SkColorType, kPremul_SkAlphaType)
+    CPU_CONFIG("rgba",  kRaster_Backend,  kRGBA_8888_SkColorType, kPremul_SkAlphaType)
+    CPU_CONFIG("bgra",  kRaster_Backend,  kBGRA_8888_SkColorType, kPremul_SkAlphaType)
+    CPU_CONFIG("f16",   kRaster_Backend,   kRGBA_F16_SkColorType, kPremul_SkAlphaType)
+    CPU_CONFIG("srgba", kRaster_Backend, kSRGBA_8888_SkColorType, kPremul_SkAlphaType)
 
 #undef CPU_CONFIG
 
diff --git a/dm/DM.cpp b/dm/DM.cpp
index c179a83..10cd5db 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -982,6 +982,7 @@
         SINK("f16",         RasterSink, kRGBA_F16_SkColorType);
         SINK("f16norm",     RasterSink, kRGBA_F16Norm_SkColorType);
         SINK("f32",         RasterSink, kRGBA_F32_SkColorType);
+        SINK("srgba",       RasterSink, kSRGBA_8888_SkColorType);
 
         SINK("pdf",         PDFSink, false, SK_ScalarDefaultRasterDPI);
         SINK("skp",         SKPSink);
diff --git a/docs/examples/unexpected_setAlphaType.cpp b/docs/examples/unexpected_setAlphaType.cpp
index 1ec1d9f..8c3daf2 100644
--- a/docs/examples/unexpected_setAlphaType.cpp
+++ b/docs/examples/unexpected_setAlphaType.cpp
@@ -21,6 +21,7 @@
         case kRGB_565_SkColorType:            return "RGB_565";
         case kARGB_4444_SkColorType:          return "ARGB_4444";
         case kRGBA_8888_SkColorType:          return "RGBA_8888";
+        case kSRGBA_8888_SkColorType:         return "SRGBA_8888";
         case kRGB_888x_SkColorType:           return "RGB_888x";
         case kBGRA_8888_SkColorType:          return "BGRA_8888";
         case kRGBA_1010102_SkColorType:       return "RGBA_1010102";
diff --git a/gm/bitmapcopy.cpp b/gm/bitmapcopy.cpp
index 3f83da6..f3f3820 100644
--- a/gm/bitmapcopy.cpp
+++ b/gm/bitmapcopy.cpp
@@ -48,6 +48,7 @@
         case kA16_float_SkColorType:          return "A16_float";
         case kR16G16_float_SkColorType:       return "R16G16_float";
         case kR16G16B16A16_unorm_SkColorType: return "R16G16B16A16_unorm";
+        case kSRGBA_8888_SkColorType:         return "SRGBA_8888";
     }
     return "";
 }
diff --git a/include/core/SkImageInfo.h b/include/core/SkImageInfo.h
index 426c234..8deda88 100644
--- a/include/core/SkImageInfo.h
+++ b/include/core/SkImageInfo.h
@@ -16,6 +16,9 @@
 #include "include/private/SkTFitsIn.h"
 #include "include/private/SkTo.h"
 
+// Temporary guard for Chromium
+#define SK_HAS_SRGBA_COLOR_TYPE
+
 class SkReadBuffer;
 class SkWriteBuffer;
 
@@ -91,7 +94,9 @@
     kR16G16B16A16_unorm_SkColorType, //!< pixel with a little endian uint16_t for red, green, blue
                                      //   and alpha
 
-    kLastEnum_SkColorType     = kR16G16B16A16_unorm_SkColorType, //!< last valid value
+    kSRGBA_8888_SkColorType,
+
+    kLastEnum_SkColorType     = kSRGBA_8888_SkColorType, //!< last valid value
 
 #if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
     kN32_SkColorType          = kBGRA_8888_SkColorType,//!< native 32-bit BGRA encoding
diff --git a/include/private/GrTypesPriv.h b/include/private/GrTypesPriv.h
index 6677836..5a8a0fa 100644
--- a/include/private/GrTypesPriv.h
+++ b/include/private/GrTypesPriv.h
@@ -940,8 +940,7 @@
         case GrColorType::kBGR_565:          return kRGB_565_SkColorType;
         case GrColorType::kABGR_4444:        return kARGB_4444_SkColorType;
         case GrColorType::kRGBA_8888:        return kRGBA_8888_SkColorType;
-        // Once we add kRGBA_8888_SRGB_SkColorType we should return that here.
-        case GrColorType::kRGBA_8888_SRGB:   return kRGBA_8888_SkColorType;
+        case GrColorType::kRGBA_8888_SRGB:   return kSRGBA_8888_SkColorType;
         case GrColorType::kRGB_888x:         return kRGB_888x_SkColorType;
         case GrColorType::kRG_88:            return kR8G8_unorm_SkColorType;
         case GrColorType::kBGRA_8888:        return kBGRA_8888_SkColorType;
@@ -978,6 +977,7 @@
         case kRGB_565_SkColorType:            return GrColorType::kBGR_565;
         case kARGB_4444_SkColorType:          return GrColorType::kABGR_4444;
         case kRGBA_8888_SkColorType:          return GrColorType::kRGBA_8888;
+        case kSRGBA_8888_SkColorType:         return GrColorType::kRGBA_8888_SRGB;
         case kRGB_888x_SkColorType:           return GrColorType::kRGB_888x;
         case kBGRA_8888_SkColorType:          return GrColorType::kBGRA_8888;
         case kGray_8_SkColorType:             return GrColorType::kGray_8;
@@ -998,12 +998,6 @@
     SkUNREACHABLE;
 }
 
-// This is a temporary means of mapping an SkColorType and format to a
-// GrColorType::kRGBA_8888_SRGB. Once we have an SRGB SkColorType this can go away.
-GrColorType SkColorTypeAndFormatToGrColorType(const GrCaps* caps,
-                                              SkColorType skCT,
-                                              const GrBackendFormat& format);
-
 static constexpr uint32_t GrColorTypeChannelFlags(GrColorType ct) {
     switch (ct) {
         case GrColorType::kUnknown:          return 0;
diff --git a/include/private/SkImageInfoPriv.h b/include/private/SkImageInfoPriv.h
index 0d86130..5e4abb8 100644
--- a/include/private/SkImageInfoPriv.h
+++ b/include/private/SkImageInfoPriv.h
@@ -34,6 +34,7 @@
         case kA16_float_SkColorType:          return kAlpha_SkColorChannelFlag;
         case kR16G16_float_SkColorType:       return kRG_SkColorChannelFlags;
         case kR16G16B16A16_unorm_SkColorType: return kRGBA_SkColorChannelFlags;
+        case kSRGBA_8888_SkColorType:         return kRGBA_SkColorChannelFlags;
     }
     SkUNREACHABLE;
 }
@@ -69,6 +70,7 @@
         case kA16_float_SkColorType:          return 1;
         case kR16G16_float_SkColorType:       return 2;
         case kR16G16B16A16_unorm_SkColorType: return 3;
+        case kSRGBA_8888_SkColorType:         return 2;
     }
     SkUNREACHABLE;
 }
@@ -107,7 +109,8 @@
         case kA16_unorm_SkColorType:
         case kA16_float_SkColorType:          /*subtle... alpha is always [0,1]*/
         case kR16G16_unorm_SkColorType:
-        case kR16G16B16A16_unorm_SkColorType: return true;
+        case kR16G16B16A16_unorm_SkColorType:
+        case kSRGBA_8888_SkColorType: return true;
 
         case kRGBA_F16_SkColorType:
         case kRGBA_F32_SkColorType:
@@ -133,6 +136,7 @@
         case kBGRA_8888_SkColorType:
         case kGray_8_SkColorType:
         case kR8G8_unorm_SkColorType:
+        case kSRGBA_8888_SkColorType:
             return 8;
 
         case kRGBA_1010102_SkColorType:
diff --git a/src/core/SkColorSpacePriv.h b/src/core/SkColorSpacePriv.h
index 9b4fad9..d4e3f4d 100644
--- a/src/core/SkColorSpacePriv.h
+++ b/src/core/SkColorSpacePriv.h
@@ -101,6 +101,10 @@
     return linearExp || linearFn;
 }
 
+skvm::F32 sk_program_transfer_fn(
+    skvm::F32 v, TFKind,
+    skvm::F32 G, skvm::F32 A, skvm::F32 B, skvm::F32 C, skvm::F32 D, skvm::F32 E, skvm::F32 F);
+
 skvm::Color sk_program_transfer_fn(skvm::Builder*, skvm::Uniforms*,
                                    const skcms_TransferFunction&, skvm::Color);
 
diff --git a/src/core/SkColorSpaceXformSteps.cpp b/src/core/SkColorSpaceXformSteps.cpp
index c091d6a..501f8b4 100644
--- a/src/core/SkColorSpaceXformSteps.cpp
+++ b/src/core/SkColorSpaceXformSteps.cpp
@@ -137,6 +137,47 @@
     if (flags.premul)          { p->append(SkRasterPipeline::premul); }
 }
 
+skvm::F32 sk_program_transfer_fn(
+    skvm::F32 v, TFKind tf_kind,
+    skvm::F32 G, skvm::F32 A, skvm::F32 B, skvm::F32 C, skvm::F32 D, skvm::F32 E, skvm::F32 F)
+{
+    // Strip off the sign bit and save it for later.
+    skvm::I32 bits = pun_to_I32(v),
+              sign = bits & 0x80000000;
+    v = pun_to_F32(bits ^ sign);
+
+    switch (tf_kind) {
+        case Bad_TF: SkASSERT(false); break;
+
+        case sRGBish_TF: {
+            v = select(v <= D, C*v + F
+                             , approx_powf(A*v + B, G) + E);
+        } break;
+
+        case PQish_TF: {
+            skvm::F32 vC = approx_powf(v, C);
+            v = approx_powf(max(B * vC + A, 0.0f) / (E * vC + D), F);
+        } break;
+
+        case HLGish_TF: {
+            skvm::F32 vA = v*A,
+                       K = F + 1.0f;
+            v = K*select(vA <= 1.0f, approx_powf(vA, B)
+                                   , approx_exp((v-E) * C + D));
+        } break;
+
+        case HLGinvish_TF: {
+            skvm::F32 K = F + 1.0f;
+            v /= K;
+            v = select(v <= 1.0f, A * approx_powf(v, B)
+                                , C * approx_log(v-D) + E);
+        } break;
+    }
+
+    // Re-apply the original sign bit on our way out the door.
+    return pun_to_F32(sign | pun_to_I32(v));
+}
+
 skvm::Color sk_program_transfer_fn(skvm::Builder* p, skvm::Uniforms* uniforms,
                                    const skcms_TransferFunction& tf, skvm::Color c) {
     skvm::F32 G = p->uniformF(uniforms->pushF(tf.g)),
@@ -146,46 +187,13 @@
               D = p->uniformF(uniforms->pushF(tf.d)),
               E = p->uniformF(uniforms->pushF(tf.e)),
               F = p->uniformF(uniforms->pushF(tf.f));
-
-    auto apply = [&](skvm::F32 v) -> skvm::F32 {
-        // Strip off the sign bit and save it for later.
-        skvm::I32 bits = pun_to_I32(v),
-                  sign = bits & 0x80000000;
-        v = pun_to_F32(bits ^ sign);
-
-        switch (classify_transfer_fn(tf)) {
-            case Bad_TF: SkASSERT(false); break;
-
-            case sRGBish_TF:
-                v = select(v <= D, C*v + F
-                                 , approx_powf(A*v + B, G) + E);
-                break;
-
-            case PQish_TF: {
-                skvm::F32 vC = approx_powf(v, C);
-                v = approx_powf(max(B * vC + A, 0.0f) / (E * vC + D), F);
-            } break;
-
-            case HLGish_TF: {
-                skvm::F32 vA = v*A,
-                           K = F + 1.0f;
-                v = K*select(vA <= 1.0f, approx_powf(vA, B)
-                                       , approx_exp((v-E) * C + D));
-            } break;
-
-            case HLGinvish_TF:
-                skvm::F32 K = F + 1.0f;
-                v /= K;
-                v = select(v <= 1.0f, A * approx_powf(v, B)
-                                    , C * approx_log(v-D) + E);
-                break;
-        }
-
-        // Re-apply the original sign bit on our way out the door.
-        return pun_to_F32(sign | pun_to_I32(v));
+    TFKind tf_kind = classify_transfer_fn(tf);
+    return {
+        sk_program_transfer_fn(c.r, tf_kind, G,A,B,C,D,E,F),
+        sk_program_transfer_fn(c.g, tf_kind, G,A,B,C,D,E,F),
+        sk_program_transfer_fn(c.b, tf_kind, G,A,B,C,D,E,F),
+        c.a,
     };
-
-    return {apply(c.r), apply(c.g), apply(c.b), c.a};
 }
 
 skvm::Color SkColorSpaceXformSteps::program(skvm::Builder* p, skvm::Uniforms* uniforms,
diff --git a/src/core/SkConvertPixels.cpp b/src/core/SkConvertPixels.cpp
index 42b2aa0..f00042b 100644
--- a/src/core/SkConvertPixels.cpp
+++ b/src/core/SkConvertPixels.cpp
@@ -124,7 +124,8 @@
         }
 
         case kBGRA_8888_SkColorType:
-        case kRGBA_8888_SkColorType: {
+        case kRGBA_8888_SkColorType:
+        case kSRGBA_8888_SkColorType: {
             auto src32 = (const uint32_t*) src;
             for (int y = 0; y < srcInfo.height(); y++) {
                 for (int x = 0; x < srcInfo.width(); x++) {
diff --git a/src/core/SkImageInfo.cpp b/src/core/SkImageInfo.cpp
index 81994fb..dc045ec 100644
--- a/src/core/SkImageInfo.cpp
+++ b/src/core/SkImageInfo.cpp
@@ -33,6 +33,7 @@
         case kA16_float_SkColorType:          return 2;
         case kR16G16_float_SkColorType:       return 4;
         case kR16G16B16A16_unorm_SkColorType: return 8;
+        case kSRGBA_8888_SkColorType:         return 4;
     }
     SkUNREACHABLE;
 }
@@ -93,6 +94,7 @@
             [[fallthrough]];
         case kARGB_4444_SkColorType:
         case kRGBA_8888_SkColorType:
+        case kSRGBA_8888_SkColorType:
         case kBGRA_8888_SkColorType:
         case kRGBA_1010102_SkColorType:
         case kBGRA_1010102_SkColorType:
diff --git a/src/core/SkMipmap.cpp b/src/core/SkMipmap.cpp
index eef99dc..5074a00 100644
--- a/src/core/SkMipmap.cpp
+++ b/src/core/SkMipmap.cpp
@@ -543,6 +543,9 @@
         case kBGR_101010x_SkColorType:  // TODO: use 1010102?
         case kRGBA_F32_SkColorType:
             return nullptr;
+
+        case kSRGBA_8888_SkColorType:  // TODO: needs careful handling
+            return nullptr;
     }
 
     if (src.width() <= 1 && src.height() <= 1) {
diff --git a/src/core/SkPixmap.cpp b/src/core/SkPixmap.cpp
index 2545c62..47a9974 100644
--- a/src/core/SkPixmap.cpp
+++ b/src/core/SkPixmap.cpp
@@ -128,6 +128,7 @@
         }
         case kRGBA_8888_SkColorType:
         case kBGRA_8888_SkColorType:
+        case kSRGBA_8888_SkColorType:
             value = static_cast<const uint8_t*>(srcPtr)[3] * (1.0f/255);
             break;
         case kRGBA_1010102_SkColorType:
@@ -326,6 +327,30 @@
             SkPMColor c = SkSwizzle_RGBA_to_PMColor(value);
             return toColor(c);
         }
+        case kSRGBA_8888_SkColorType: {
+            auto srgb_to_linear = [](float x) {
+                return (x <= 0.04045f) ? x * (1 / 12.92f)
+                                       : sk_float_pow(x * (1 / 1.055f) + (0.055f / 1.055f), 2.4f);
+            };
+
+            uint32_t value = *this->addr32(x, y);
+            float r = ((value >>  0) & 0xff) * (1/255.0f),
+                  g = ((value >>  8) & 0xff) * (1/255.0f),
+                  b = ((value >> 16) & 0xff) * (1/255.0f),
+                  a = ((value >> 24) & 0xff) * (1/255.0f);
+            r = srgb_to_linear(r);
+            g = srgb_to_linear(g);
+            b = srgb_to_linear(b);
+            if (a != 0 && needsUnpremul) {
+                r = SkTPin(r/a, 0.0f, 1.0f);
+                g = SkTPin(g/a, 0.0f, 1.0f);
+                b = SkTPin(b/a, 0.0f, 1.0f);
+            }
+            return (uint32_t)( r * 255.0f ) << 16
+                 | (uint32_t)( g * 255.0f ) <<  8
+                 | (uint32_t)( b * 255.0f ) <<  0
+                 | (uint32_t)( a * 255.0f ) << 24;
+        }
         case kRGB_101010x_SkColorType: {
             uint32_t value = *this->addr32(x, y);
             // Convert 10-bit rgb to 8-bit bgr, and mask in 0xff alpha at the top.
@@ -481,7 +506,8 @@
             return true;
         }
         case kBGRA_8888_SkColorType:
-        case kRGBA_8888_SkColorType: {
+        case kRGBA_8888_SkColorType:
+        case kSRGBA_8888_SkColorType: {
             SkPMColor c = (SkPMColor)~0;
             for (int y = 0; y < height; ++y) {
                 const SkPMColor* row = this->addr32(0, y);
diff --git a/src/core/SkRasterPipeline.cpp b/src/core/SkRasterPipeline.cpp
index b5d30be..93ad3a9 100644
--- a/src/core/SkRasterPipeline.cpp
+++ b/src/core/SkRasterPipeline.cpp
@@ -210,6 +210,11 @@
         case kBGRA_8888_SkColorType:         this->append(load_8888, ctx);
                                              this->append(swap_rb);
                                              break;
+
+        case kSRGBA_8888_SkColorType:
+            this->append(load_8888, ctx);
+            this->append_transfer_function(*skcms_sRGB_TransferFunction());
+            break;
     }
 }
 
@@ -256,6 +261,14 @@
         case kBGRA_8888_SkColorType:          this->append(load_8888_dst, ctx);
                                               this->append(swap_rb_dst);
                                               break;
+
+        case kSRGBA_8888_SkColorType:
+            // TODO: We could remove the double-swap if we had _dst versions of all the TF stages
+            this->append(load_8888_dst, ctx);
+            this->append(swap_src_dst);
+            this->append_transfer_function(*skcms_sRGB_TransferFunction());
+            this->append(swap_src_dst);
+            break;
     }
 }
 
@@ -302,6 +315,11 @@
         case kBGRA_8888_SkColorType:          this->append(swap_rb);
                                               this->append(store_8888, ctx);
                                               break;
+
+        case kSRGBA_8888_SkColorType:
+            this->append_transfer_function(*skcms_sRGB_Inverse_TransferFunction());
+            this->append(store_8888, ctx);
+            break;
     }
 }
 
diff --git a/src/core/SkRasterPipeline.h b/src/core/SkRasterPipeline.h
index 6e8b4be..3b03020 100644
--- a/src/core/SkRasterPipeline.h
+++ b/src/core/SkRasterPipeline.h
@@ -38,7 +38,7 @@
 
 #define SK_RASTER_PIPELINE_STAGES(M)                               \
     M(callback)                                                    \
-    M(move_src_dst) M(move_dst_src)                                \
+    M(move_src_dst) M(move_dst_src) M(swap_src_dst)                \
     M(clamp_0) M(clamp_1) M(clamp_a) M(clamp_gamut)                \
     M(unpremul) M(premul) M(premul_dst)                            \
     M(force_opaque) M(force_opaque_dst)                            \
diff --git a/src/core/SkRasterPipelineBlitter.cpp b/src/core/SkRasterPipelineBlitter.cpp
index fd3fc5f..b1313de 100644
--- a/src/core/SkRasterPipelineBlitter.cpp
+++ b/src/core/SkRasterPipelineBlitter.cpp
@@ -208,7 +208,8 @@
             case    kGray_8_SkColorType:
             case  kRGB_888x_SkColorType:
             case kRGBA_8888_SkColorType:
-            case kBGRA_8888_SkColorType:    blitter->fDitherRate =  1/255.0f; break;
+            case kBGRA_8888_SkColorType:
+            case kSRGBA_8888_SkColorType:   blitter->fDitherRate =  1/255.0f; break;
             case kRGB_101010x_SkColorType:
             case kRGBA_1010102_SkColorType:
             case kBGR_101010x_SkColorType:
diff --git a/src/core/SkVM.cpp b/src/core/SkVM.cpp
index 8c760d5..967b593 100644
--- a/src/core/SkVM.cpp
+++ b/src/core/SkVM.cpp
@@ -13,6 +13,7 @@
 #include "include/private/SkTFitsIn.h"
 #include "include/private/SkThreadID.h"
 #include "include/private/SkVx.h"
+#include "src/core/SkColorSpacePriv.h"
 #include "src/core/SkColorSpaceXformSteps.h"
 #include "src/core/SkCpu.h"
 #include "src/core/SkEnumerate.h"
@@ -112,7 +113,20 @@
     #endif
 #endif
 
+#if defined(SKSL_STANDALONE)
+    // skslc needs to link against this module (for the VM code generator). This module pulls in
+    // color-space code, but attempting to add those transitive dependencies to skslc gets out of
+    // hand. So we terminate the chain here with stub functions. Note that skslc's usage of SkVM
+    // never cares about color management.
+    skvm::F32 sk_program_transfer_fn(
+        skvm::F32 v, TFKind tf_kind,
+        skvm::F32 G, skvm::F32 A, skvm::F32 B, skvm::F32 C, skvm::F32 D, skvm::F32 E, skvm::F32 F) {
+            return v;
+    }
 
+    const skcms_TransferFunction* skcms_sRGB_TransferFunction() { return nullptr; }
+    const skcms_TransferFunction* skcms_sRGB_Inverse_TransferFunction() { return nullptr; }
+#endif
 
 namespace skvm {
 
@@ -1043,6 +1057,7 @@
 
     PixelFormat SkColorType_to_PixelFormat(SkColorType ct) {
         auto UNORM = PixelFormat::UNORM,
+             SRGB  = PixelFormat::SRGB,
              FLOAT = PixelFormat::FLOAT;
         switch (ct) {
             case kUnknown_SkColorType: break;
@@ -1065,6 +1080,7 @@
             case kRGBA_8888_SkColorType:  return {UNORM, 8,8,8,8,  0,8,16,24};
             case kRGB_888x_SkColorType:   return {UNORM, 8,8,8,0,  0,8,16,32};  // 32-bit
             case kBGRA_8888_SkColorType:  return {UNORM, 8,8,8,8, 16,8, 0,24};
+            case kSRGBA_8888_SkColorType: return { SRGB, 8,8,8,8,  0,8,16,24};
 
             case kRGBA_1010102_SkColorType: return {UNORM, 10,10,10,2,  0,10,20,30};
             case kBGRA_1010102_SkColorType: return {UNORM, 10,10,10,2, 20,10, 0,30};
@@ -1091,19 +1107,43 @@
 
     static Color unpack(PixelFormat f, I32 x) {
         SkASSERT(byte_size(f) <= 4);
-        auto unpack_channel = [=](int bits, int shift) {
+
+        auto from_srgb = [](int bits, I32 channel) -> F32 {
+            const skcms_TransferFunction* tf = skcms_sRGB_TransferFunction();
+            F32 v = from_unorm(bits, channel);
+            return sk_program_transfer_fn(v, sRGBish_TF,
+                                          v->splat(tf->g),
+                                          v->splat(tf->a),
+                                          v->splat(tf->b),
+                                          v->splat(tf->c),
+                                          v->splat(tf->d),
+                                          v->splat(tf->e),
+                                          v->splat(tf->f));
+        };
+
+        auto unpack_rgb = [=](int bits, int shift) -> F32 {
             I32 channel = extract(x, shift, (1<<bits)-1);
             switch (f.encoding) {
                 case PixelFormat::UNORM: return from_unorm(bits, channel);
+                case PixelFormat:: SRGB: return from_srgb (bits, channel);
+                case PixelFormat::FLOAT: return from_fp16 (      channel);
+            }
+            SkUNREACHABLE;
+        };
+        auto unpack_alpha = [=](int bits, int shift) -> F32 {
+            I32 channel = extract(x, shift, (1<<bits)-1);
+            switch (f.encoding) {
+                case PixelFormat::UNORM:
+                case PixelFormat:: SRGB: return from_unorm(bits, channel);
                 case PixelFormat::FLOAT: return from_fp16 (      channel);
             }
             SkUNREACHABLE;
         };
         return {
-            f.r_bits ? unpack_channel(f.r_bits, f.r_shift) : x->splat(0.0f),
-            f.g_bits ? unpack_channel(f.g_bits, f.g_shift) : x->splat(0.0f),
-            f.b_bits ? unpack_channel(f.b_bits, f.b_shift) : x->splat(0.0f),
-            f.a_bits ? unpack_channel(f.a_bits, f.a_shift) : x->splat(1.0f),
+            f.r_bits ? unpack_rgb  (f.r_bits, f.r_shift) : x->splat(0.0f),
+            f.g_bits ? unpack_rgb  (f.g_bits, f.g_shift) : x->splat(0.0f),
+            f.b_bits ? unpack_rgb  (f.b_bits, f.b_shift) : x->splat(0.0f),
+            f.a_bits ? unpack_alpha(f.a_bits, f.a_shift) : x->splat(1.0f),
         };
     }
 
@@ -1211,19 +1251,42 @@
 
     static I32 pack32(PixelFormat f, Color c) {
         SkASSERT(byte_size(f) <= 4);
+
+        auto to_srgb = [](int bits, F32 v) {
+            const skcms_TransferFunction* tf = skcms_sRGB_Inverse_TransferFunction();
+            return to_unorm(bits, sk_program_transfer_fn(v, sRGBish_TF,
+                                                         v->splat(tf->g),
+                                                         v->splat(tf->a),
+                                                         v->splat(tf->b),
+                                                         v->splat(tf->c),
+                                                         v->splat(tf->d),
+                                                         v->splat(tf->e),
+                                                         v->splat(tf->f)));
+        };
+
         I32 packed = c->splat(0);
-        auto pack_channel = [&](F32 channel, int bits, int shift) {
+        auto pack_rgb = [&](F32 channel, int bits, int shift) {
             I32 encoded;
             switch (f.encoding) {
                 case PixelFormat::UNORM: encoded = to_unorm(bits, channel); break;
+                case PixelFormat:: SRGB: encoded = to_srgb (bits, channel); break;
                 case PixelFormat::FLOAT: encoded = to_fp16 (      channel); break;
             }
             packed = pack(packed, encoded, shift);
         };
-        if (f.r_bits) { pack_channel(c.r, f.r_bits, f.r_shift); }
-        if (f.g_bits) { pack_channel(c.g, f.g_bits, f.g_shift); }
-        if (f.b_bits) { pack_channel(c.b, f.b_bits, f.b_shift); }
-        if (f.a_bits) { pack_channel(c.a, f.a_bits, f.a_shift); }
+        auto pack_alpha = [&](F32 channel, int bits, int shift) {
+            I32 encoded;
+            switch (f.encoding) {
+                case PixelFormat::UNORM:
+                case PixelFormat:: SRGB: encoded = to_unorm(bits, channel); break;
+                case PixelFormat::FLOAT: encoded = to_fp16 (      channel); break;
+            }
+            packed = pack(packed, encoded, shift);
+        };
+        if (f.r_bits) { pack_rgb  (c.r, f.r_bits, f.r_shift); }
+        if (f.g_bits) { pack_rgb  (c.g, f.g_bits, f.g_shift); }
+        if (f.b_bits) { pack_rgb  (c.b, f.b_bits, f.b_shift); }
+        if (f.a_bits) { pack_alpha(c.a, f.a_bits, f.a_shift); }
         return packed;
     }
 
diff --git a/src/core/SkVM.h b/src/core/SkVM.h
index 670e1cd..60277e8 100644
--- a/src/core/SkVM.h
+++ b/src/core/SkVM.h
@@ -560,7 +560,7 @@
     };
 
     struct PixelFormat {
-        enum { UNORM, FLOAT} encoding;
+        enum { UNORM, SRGB, FLOAT} encoding;
         int r_bits,  g_bits,  b_bits,  a_bits,
             r_shift, g_shift, b_shift, a_shift;
     };
diff --git a/src/core/SkVMBlitter.cpp b/src/core/SkVMBlitter.cpp
index 76415cf..3be35ee 100644
--- a/src/core/SkVMBlitter.cpp
+++ b/src/core/SkVMBlitter.cpp
@@ -114,7 +114,8 @@
                 case    kGray_8_SkColorType:
                 case  kRGB_888x_SkColorType:
                 case kRGBA_8888_SkColorType:
-                case kBGRA_8888_SkColorType:    rate =  1/255.0f; break;
+                case kBGRA_8888_SkColorType:
+                case kSRGBA_8888_SkColorType:   rate =  1/255.0f; break;
                 case kRGB_101010x_SkColorType:
                 case kRGBA_1010102_SkColorType:
                 case kBGR_101010x_SkColorType:
diff --git a/src/gpu/GrDataUtils.cpp b/src/gpu/GrDataUtils.cpp
index 0d449d6..d14c466 100644
--- a/src/gpu/GrDataUtils.cpp
+++ b/src/gpu/GrDataUtils.cpp
@@ -747,18 +747,3 @@
 
     return true;
 }
-
-GrColorType SkColorTypeAndFormatToGrColorType(const GrCaps* caps,
-                                              SkColorType skCT,
-                                              const GrBackendFormat& format) {
-    GrColorType grCT = SkColorTypeToGrColorType(skCT);
-    // Until we support SRGB in the SkColorType we have to do this manual check here to make sure
-    // we use the correct GrColorType.
-    if (caps->isFormatSRGB(format)) {
-        if (grCT != GrColorType::kRGBA_8888) {
-            return GrColorType::kUnknown;
-        }
-        grCT = GrColorType::kRGBA_8888_SRGB;
-    }
-    return grCT;
-}
diff --git a/src/gpu/GrDirectContext.cpp b/src/gpu/GrDirectContext.cpp
index cb9edf0..4ada100 100644
--- a/src/gpu/GrDirectContext.cpp
+++ b/src/gpu/GrDirectContext.cpp
@@ -683,7 +683,7 @@
     }
 
     GrBackendFormat format = backendTexture.getBackendFormat();
-    GrColorType grColorType = SkColorTypeAndFormatToGrColorType(this->caps(), skColorType, format);
+    GrColorType grColorType = SkColorTypeToGrColorType(skColorType);
 
     if (!this->caps()->areColorTypeAndFormatCompatible(grColorType, format)) {
         return false;
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index 9468369..b8a8971 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -154,7 +154,7 @@
 #ifdef SK_DEBUG
     const GrBackendFormat& format = fChooser.backendFormat();
     const GrCaps* caps = this->context()->priv().caps();
-    GrColorType grCT = SkColorTypeAndFormatToGrColorType(caps, this->colorType(), format);
+    GrColorType grCT = SkColorTypeToGrColorType(this->colorType());
     SkASSERT(caps->isFormatCompressed(format) ||
              caps->areColorTypeAndFormatCompatible(grCT, format));
 #endif
@@ -179,7 +179,7 @@
 #ifdef SK_DEBUG
     const GrBackendFormat& format = fChooser.backendFormat();
     const GrCaps* caps = this->context()->priv().caps();
-    GrColorType grCT = SkColorTypeAndFormatToGrColorType(caps, this->colorType(), format);
+    GrColorType grCT = SkColorTypeToGrColorType(this->colorType());
     SkASSERT(caps->isFormatCompressed(format) ||
              caps->areColorTypeAndFormatCompatible(grCT, format));
 #endif
@@ -457,7 +457,7 @@
 
     const GrCaps* caps = rContext->priv().caps();
 
-    GrColorType grColorType = SkColorTypeAndFormatToGrColorType(caps, ct, tex.getBackendFormat());
+    GrColorType grColorType = SkColorTypeToGrColorType(ct);
     if (GrColorType::kUnknown == grColorType) {
         return nullptr;
     }
@@ -482,7 +482,7 @@
 
     const GrCaps* caps = dContext->priv().caps();
 
-    GrColorType grColorType = SkColorTypeAndFormatToGrColorType(caps, ct, tex.getBackendFormat());
+    GrColorType grColorType = SkColorTypeToGrColorType(ct);
     if (GrColorType::kUnknown == grColorType) {
         return nullptr;
     }
@@ -595,9 +595,7 @@
         return nullptr;
     }
 
-    GrColorType grColorType = SkColorTypeAndFormatToGrColorType(threadSafeProxy->priv().caps(),
-                                                                colorType,
-                                                                backendFormat);
+    GrColorType grColorType = SkColorTypeToGrColorType(colorType);
     if (GrColorType::kUnknown == grColorType) {
         return nullptr;
     }
@@ -834,9 +832,7 @@
                 SkColorTypeToGrColorType(this->colorType())};
     }
     GrSurfaceProxyView view = this->makeView(recordingContext);
-    GrColorType ct = SkColorTypeAndFormatToGrColorType(recordingContext->priv().caps(),
-                                                       this->colorType(),
-                                                       view.proxy()->backendFormat());
+    GrColorType ct = SkColorTypeToGrColorType(this->colorType());
     if (mipmapped == GrMipmapped::kYes) {
         view = FindOrMakeCachedMipmappedView(recordingContext, std::move(view), this->uniqueID());
     }
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index 873b431..6a7662e 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -489,8 +489,7 @@
     }
     sampleCnt = std::max(1, sampleCnt);
 
-    GrColorType grColorType = SkColorTypeAndFormatToGrColorType(rContext->priv().caps(), colorType,
-                                                                tex.getBackendFormat());
+    GrColorType grColorType = SkColorTypeToGrColorType(colorType);
     if (grColorType == GrColorType::kUnknown) {
         return nullptr;
     }
@@ -607,8 +606,7 @@
         return nullptr;
     }
 
-    GrColorType grColorType = SkColorTypeAndFormatToGrColorType(rContext->priv().caps(), colorType,
-                                                                rt.getBackendFormat());
+    GrColorType grColorType = SkColorTypeToGrColorType(colorType);
     if (grColorType == GrColorType::kUnknown) {
         return nullptr;
     }
diff --git a/src/images/SkPngEncoder.cpp b/src/images/SkPngEncoder.cpp
index d3957f7..2cf3ba4 100644
--- a/src/images/SkPngEncoder.cpp
+++ b/src/images/SkPngEncoder.cpp
@@ -237,6 +237,11 @@
         case kUnknown_SkColorType:
             break;
 
+        // TODO: I don't think this can just use kRGBA's procs.
+        // kPremul is especially tricky here, since it's presumably TF⁻¹(rgb * a),
+        // so to get at unpremul rgb we'd need to undo the transfer function first.
+        case kSRGBA_8888_SkColorType: return nullptr;
+
         case kRGBA_8888_SkColorType:
             switch (info.alphaType()) {
                 case kOpaque_SkAlphaType:
diff --git a/src/opts/SkRasterPipeline_opts.h b/src/opts/SkRasterPipeline_opts.h
index ff0d8c1..33a067f 100644
--- a/src/opts/SkRasterPipeline_opts.h
+++ b/src/opts/SkRasterPipeline_opts.h
@@ -1644,6 +1644,12 @@
     b = db;
     a = da;
 }
+STAGE(swap_src_dst, Ctx::None) {
+    std::swap(r, dr);
+    std::swap(g, dg);
+    std::swap(b, db);
+    std::swap(a, da);
+}
 
 STAGE(premul, Ctx::None) {
     r = r * a;
@@ -3288,6 +3294,13 @@
     a = da;
 }
 
+STAGE_PP(swap_src_dst, Ctx::None) {
+    std::swap(r, dr);
+    std::swap(g, dg);
+    std::swap(b, db);
+    std::swap(a, da);
+}
+
 // ~~~~~~ Blend modes ~~~~~~ //
 
 // The same logic applied to all 4 channels.
diff --git a/src/shaders/SkImageShader.cpp b/src/shaders/SkImageShader.cpp
index d126df0..2e44dd0 100755
--- a/src/shaders/SkImageShader.cpp
+++ b/src/shaders/SkImageShader.cpp
@@ -539,6 +539,11 @@
             case kBGRA_8888_SkColorType:    p->append(SkRasterPipeline::gather_8888,    ctx);
                                             p->append(SkRasterPipeline::swap_rb            ); break;
 
+            case kSRGBA_8888_SkColorType:
+                p->append(SkRasterPipeline::gather_8888, ctx);
+                p->append_transfer_function(*skcms_sRGB_TransferFunction());
+                break;
+
             case kUnknown_SkColorType: SkASSERT(false);
         }
         if (decal_ctx) {
diff --git a/tests/BackendAllocationTest.cpp b/tests/BackendAllocationTest.cpp
index 5800e6e..6c883a4 100644
--- a/tests/BackendAllocationTest.cpp
+++ b/tests/BackendAllocationTest.cpp
@@ -573,6 +573,7 @@
         { kRGB_565_SkColorType,           SkColors::kRed           },
         { kARGB_4444_SkColorType,         SkColors::kGreen         },
         { kRGBA_8888_SkColorType,         SkColors::kBlue          },
+        { kSRGBA_8888_SkColorType,        { 0.25f, 0.5f, 0.75f, 1.0f}},
         { kRGB_888x_SkColorType,          SkColors::kCyan          },
         // TODO: readback is busted when alpha = 0.5f (perhaps premul vs. unpremul)
         { kBGRA_8888_SkColorType,         { 1, 0, 0, 1.0f }        },
diff --git a/tests/ReadWritePixelsGpuTest.cpp b/tests/ReadWritePixelsGpuTest.cpp
index 3b546eb..8fd263a 100644
--- a/tests/ReadWritePixelsGpuTest.cpp
+++ b/tests/ReadWritePixelsGpuTest.cpp
@@ -39,6 +39,7 @@
         case kR16G16_unorm_SkColorType:       return 16;
         case kR16G16_float_SkColorType:       return 16;
         case kRGBA_8888_SkColorType:          return 8;
+        case kSRGBA_8888_SkColorType:         return 8;
         case kRGB_888x_SkColorType:           return 8;
         case kBGRA_8888_SkColorType:          return 8;
         case kRGBA_1010102_SkColorType:       return 10;
@@ -66,6 +67,7 @@
         case kR16G16_unorm_SkColorType:       return 0;
         case kR16G16_float_SkColorType:       return 0;
         case kRGBA_8888_SkColorType:          return 8;
+        case kSRGBA_8888_SkColorType:         return 8;
         case kRGB_888x_SkColorType:           return 0;
         case kBGRA_8888_SkColorType:          return 8;
         case kRGBA_1010102_SkColorType:       return 2;
diff --git a/tools/HashAndEncode.cpp b/tools/HashAndEncode.cpp
index 0f5841b..9e7a8a1 100644
--- a/tools/HashAndEncode.cpp
+++ b/tools/HashAndEncode.cpp
@@ -29,6 +29,7 @@
         case kARGB_4444_SkColorType:          srcFmt = skcms_PixelFormat_ABGR_4444;       break;
         case kRGBA_8888_SkColorType:          srcFmt = skcms_PixelFormat_RGBA_8888;       break;
         case kBGRA_8888_SkColorType:          srcFmt = skcms_PixelFormat_BGRA_8888;       break;
+        case kSRGBA_8888_SkColorType:         srcFmt = skcms_PixelFormat_RGBA_8888_sRGB;  break;
         case kRGBA_1010102_SkColorType:       srcFmt = skcms_PixelFormat_RGBA_1010102;    break;
         case kBGRA_1010102_SkColorType:       srcFmt = skcms_PixelFormat_BGRA_1010102;    break;
         case kGray_8_SkColorType:             srcFmt = skcms_PixelFormat_G_8;             break;
diff --git a/tools/ToolUtils.cpp b/tools/ToolUtils.cpp
index 1eed400..681ac22 100644
--- a/tools/ToolUtils.cpp
+++ b/tools/ToolUtils.cpp
@@ -55,6 +55,7 @@
         case kRGB_565_SkColorType:            return "RGB_565";
         case kARGB_4444_SkColorType:          return "ARGB_4444";
         case kRGBA_8888_SkColorType:          return "RGBA_8888";
+        case kSRGBA_8888_SkColorType:         return "SRGBA_8888";
         case kRGB_888x_SkColorType:           return "RGB_888x";
         case kBGRA_8888_SkColorType:          return "BGRA_8888";
         case kRGBA_1010102_SkColorType:       return "RGBA_1010102";
@@ -83,6 +84,7 @@
         case kRGB_565_SkColorType:            return "565";
         case kARGB_4444_SkColorType:          return "4444";
         case kRGBA_8888_SkColorType:          return "8888";
+        case kSRGBA_8888_SkColorType:         return "8888";
         case kRGB_888x_SkColorType:           return "888";
         case kBGRA_8888_SkColorType:          return "8888";
         case kRGBA_1010102_SkColorType:       return "1010102";
diff --git a/tools/flags/CommonFlagsConfig.cpp b/tools/flags/CommonFlagsConfig.cpp
index d3adc4e..d4dfd82 100644
--- a/tools/flags/CommonFlagsConfig.cpp
+++ b/tools/flags/CommonFlagsConfig.cpp
@@ -61,7 +61,9 @@
     { "gles1010102",           "gpu", "api=gles,color=1010102" },
     { "glf16",                 "gpu", "api=gl,color=f16" },
     { "glf16norm",             "gpu", "api=gl,color=f16norm" },
+    { "glsrgba",               "gpu", "api=gl,color=srgba" },
     { "glesf16",               "gpu", "api=gles,color=f16" },
+    { "glessrgba",             "gpu", "api=gles,color=srgba" },
     { "glnostencils",          "gpu", "api=gl,stencils=false" },
     { "gldft",                 "gpu", "api=gl,dit=true" },
     { "glesdft",               "gpu", "api=gles,dit=true" },
@@ -339,9 +341,9 @@
     return false;
 }
 
-static bool parse_option_gpu_color(const SkString&      value,
-                                   SkColorType*         outColorType,
-                                   SkAlphaType*         alphaType) {
+static bool parse_option_gpu_color(const SkString& value,
+                                   SkColorType*    outColorType,
+                                   SkAlphaType*    alphaType) {
     // We always use premul unless the color type is 565.
     *alphaType = kPremul_SkAlphaType;
 
@@ -362,6 +364,8 @@
         *outColorType  = kRGBA_F16_SkColorType;
     } else if (value.equals("f16norm")) {
         *outColorType  = kRGBA_F16Norm_SkColorType;
+    } else if (value.equals("srgba")) {
+        *outColorType = kSRGBA_8888_SkColorType;
     } else {
         return false;
     }
diff --git a/tools/fm/fm.cpp b/tools/fm/fm.cpp
index 9a26f6a..67624eb 100644
--- a/tools/fm/fm.cpp
+++ b/tools/fm/fm.cpp
@@ -520,6 +520,7 @@
         { "f32",                kRGBA_F32_SkColorType },
         { "rgba",              kRGBA_8888_SkColorType },
         { "bgra",              kBGRA_8888_SkColorType },
+        { "srgba",            kSRGBA_8888_SkColorType },
         { "16161616", kR16G16B16A16_unorm_SkColorType },
     };
     const FlagOption<SkAlphaType> kAlphaTypes[] = {