Switch SkCodec to use skcms

Bug: skia:6839
Bug: skia:8052

Create an skcms_Profile instead of an SkColorSpace when creating an
SkCodec. Eventually we'll move the SkImageInfo (and its SkColorSpace)
entirely to clients (e.g. SkAndroidCodec, SkCodecImageGenerator), but
for now, create it with SkEncodedInfo::makeImageInfo.

Create new SkEncodedInfo::Colors for the special PNG cases that we
want to map to specific SkColorTypes.

SkEncodedInfo:
- Add ICCProfile, which owns an skcms_ICCProfile
 - FIXME: maybe we should have a single instance for
  SRGB like SkColorSpace?
- Add kXAlpha_Color, for kAlpha_8. Since I'm not longer creating
  an SkImageInfo (at least in SkPngCodec), it needs a way to pass
  this info to the caller.
- Add k565_Color, for the same reason. Matt originally had this in
  https://codereview.chromium.org/2212563003/#ps120001, but didn't
  land that version. I like it though. Mike didn't like the bits
  per component for 565, but it seems like a sensible hack, much
  like the existing one for kAlpha_8
- Add width and height. These were removed for redundancy with
  SkImageInfo, but it makes sense to have them here without it.
BUILD.gn:
- Build the new SkEncodedInfo.cpp
SkCodec:
- Remove the constructor with an SkImageInfo. Edit the other one
  to drop width and height (now in SkEncodedInfo) and take a RHS
  reference to SkEncodedInfo
- Create the SkImageInfo from fEncodedInfo (for now)
- Consolidate choosing skcms_AlphaFormat for Transform here
- Call conversionSupported from initializeColorXform, with a new
  parameter for whether there is a color Xform, allowing SkJpegCodec
  and SkHeifCodec to override that method instead of having another
  method.
SkBmpCodec (etc)
- Adapt to the changes above
- Create a new SkEncodedInfo w/o profile for the swizzler.
SkPngCodec:
- use the new SkEncodedInfo::Colors rather than a custom SkImageInfo
SkRawCodec:
- Remove SkEncodedInfo from SkDngImage, which doesn't actually need it.
  This is helpful since we don't know all the info yet.
- Rewrite gAdobeRGB_toXYZD50 as an skcms_Matrix3x3
SkWebpCodec:
- Remove premul_step computation, and simplify to just rely on
  the base class' handling of applying the transform.
SkSwizzler:
- Add cases for the new SkEncodedInfo::Colors

TBR=reed@google.com
No public API changes. Only private/public members of SkCodec.h are
modified.

Change-Id: Ic0d3bb752b03f13be886b80331987aa5a5713fc0
Reviewed-on: https://skia-review.googlesource.com/136062
Commit-Queue: Leon Scroggins <scroggo@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index d689a7e..3369a9a 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -863,6 +863,7 @@
     "src/codec/SkCodec.cpp",
     "src/codec/SkCodecImageGenerator.cpp",
     "src/codec/SkColorTable.cpp",
+    "src/codec/SkEncodedInfo.cpp",
     "src/codec/SkGifCodec.cpp",
     "src/codec/SkMaskSwizzler.cpp",
     "src/codec/SkMasks.cpp",
@@ -2021,8 +2022,8 @@
       "tools/viewer/SlideDir.cpp",
       "tools/viewer/StatsLayer.cpp",
       "tools/viewer/SvgSlide.cpp",
-      "tools/viewer/TouchGesture.h",
       "tools/viewer/TouchGesture.cpp",
+      "tools/viewer/TouchGesture.h",
       "tools/viewer/Viewer.cpp",
     ]
     libs = []
diff --git a/gn/tests.gni b/gn/tests.gni
index 9731514..e829c70 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -64,6 +64,7 @@
   "$_tests/EGLImageTest.cpp",
   "$_tests/EmptyPathTest.cpp",
   "$_tests/EncodeTest.cpp",
+  "$_tests/EncodedInfoTest.cpp",
   "$_tests/ExifTest.cpp",
   "$_tests/F16StagesTest.cpp",
   "$_tests/FillPathTest.cpp",
diff --git a/include/codec/SkCodec.h b/include/codec/SkCodec.h
index ecb762e..2e263ea 100644
--- a/include/codec/SkCodec.h
+++ b/include/codec/SkCodec.h
@@ -13,7 +13,6 @@
 #include "../private/SkEncodedInfo.h"
 #include "SkCodecAnimation.h"
 #include "SkColor.h"
-#include "SkColorSpaceXform.h"
 #include "SkEncodedImageFormat.h"
 #include "SkEncodedOrigin.h"
 #include "SkImageInfo.h"
@@ -671,21 +670,9 @@
 protected:
     const SkEncodedInfo& getEncodedInfo() const { return fEncodedInfo; }
 
-    using XformFormat = SkColorSpaceXform::ColorFormat;
+    using XformFormat = skcms_PixelFormat;
 
-    SkCodec(int width,
-            int height,
-            const SkEncodedInfo&,
-            XformFormat srcFormat,
-            std::unique_ptr<SkStream>,
-            sk_sp<SkColorSpace>,
-            SkEncodedOrigin = kTopLeft_SkEncodedOrigin);
-
-    /**
-     *  Allows the subclass to set the recommended SkImageInfo
-     */
-    SkCodec(const SkEncodedInfo&,
-            const SkImageInfo&,
+    SkCodec(SkEncodedInfo&&,
             XformFormat srcFormat,
             std::unique_ptr<SkStream>,
             SkEncodedOrigin = kTopLeft_SkEncodedOrigin);
@@ -780,16 +767,14 @@
 
     virtual int onOutputScanline(int inputScanline) const;
 
-    bool initializeColorXform(const SkImageInfo& dstInfo, SkEncodedInfo::Alpha);
     // Some classes never need a colorXform e.g.
     // - ICO uses its embedded codec's colorXform
     // - WBMP is just Black/White
     virtual bool usesColorXform() const { return true; }
-    void applyColorXform(void* dst, const void* src, int count, SkAlphaType) const;
     void applyColorXform(void* dst, const void* src, int count) const;
 
-    SkColorSpaceXform* colorXform() const { return fColorXform.get(); }
-    bool xformOnDecode() const { return fXformOnDecode; }
+    bool colorXform() const { return fXformTime != kNo_XformTime; }
+    bool xformOnDecode() const { return fXformTime == kDecodeRow_XformTime; }
 
     virtual int onGetFrameCount() {
         return 1;
@@ -813,9 +798,16 @@
 
     SkImageInfo                        fDstInfo;
     Options                            fOptions;
+
+    enum XformTime {
+        kNo_XformTime,
+        kPalette_XformTime,
+        kDecodeRow_XformTime,
+    };
+    XformTime                          fXformTime;
     XformFormat                        fDstXformFormat; // Based on fDstInfo.
-    std::unique_ptr<SkColorSpaceXform> fColorXform;
-    bool                               fXformOnDecode;
+    skcms_ICCProfile                   fDstProfile;
+    skcms_AlphaFormat                  fDstXformAlphaFormat;
 
     // Only meaningful during scanline decodes.
     int                                fCurrScanline;
@@ -823,12 +815,15 @@
     bool                               fStartedIncrementalDecode;
 
     /**
-     *  Return whether {srcColor, srcIsOpaque, srcCS} can convert to dst.
+     *  Return whether we can convert to dst.
      *
      *  Will be called for the appropriate frame, prior to initializing the colorXform.
      */
     virtual bool conversionSupported(const SkImageInfo& dst, SkColorType srcColor,
-                                     bool srcIsOpaque, const SkColorSpace* srcCS) const;
+                                     bool srcIsOpaque, bool needsColorXform);
+
+    bool initializeColorXform(const SkImageInfo& dstInfo, SkEncodedInfo::Alpha, bool srcIsOpaque);
+
     /**
      *  Return whether these dimensions are supported as a scale.
      *
diff --git a/include/private/SkEncodedInfo.h b/include/private/SkEncodedInfo.h
index 217b859..5d6dabe 100644
--- a/include/private/SkEncodedInfo.h
+++ b/include/private/SkEncodedInfo.h
@@ -8,12 +8,25 @@
 #ifndef SkEncodedInfo_DEFINED
 #define SkEncodedInfo_DEFINED
 
+#include "SkData.h"
 #include "SkImageInfo.h"
-
-class SkColorSpace;
+#include "../../third_party/skcms/skcms.h"
 
 struct SkEncodedInfo {
 public:
+    class ICCProfile {
+    public:
+        static std::unique_ptr<ICCProfile> Make(sk_sp<SkData>);
+        static std::unique_ptr<ICCProfile> MakeSRGB();
+        static std::unique_ptr<ICCProfile> Make(const skcms_ICCProfile&);
+
+        const skcms_ICCProfile* profile() const { return &fProfile; }
+    private:
+        ICCProfile(const skcms_ICCProfile&, sk_sp<SkData> = nullptr);
+
+        skcms_ICCProfile fProfile;
+        sk_sp<SkData>    fData;
+    };
 
     enum Alpha {
         kOpaque_Alpha,
@@ -39,6 +52,20 @@
         // PNG
         kGrayAlpha_Color,
 
+        // PNG with Skia-specific sBIT
+        // Like kGrayAlpha, except this expects to be treated as
+        // kAlpha_8_SkColorType, which ignores the gray component. If
+        // decoded to full color (e.g. kN32), the gray component is respected
+        // (so it can share code with kGrayAlpha).
+        kXAlpha_Color,
+
+        // PNG
+        // 565 images may be encoded to PNG by specifying the number of
+        // significant bits for each channel.  This is a strange 565
+        // representation because the image is still encoded with 8 bits per
+        // component.
+        k565_Color,
+
         // PNG, GIF, BMP
         kPalette_Color,
 
@@ -67,7 +94,18 @@
         kYCCK_Color,
     };
 
-    static SkEncodedInfo Make(Color color, Alpha alpha, int bitsPerComponent) {
+    static SkEncodedInfo Make(int width, int height, Color color, Alpha alpha,
+            int bitsPerComponent) {
+        return Make(width, height, color, alpha, bitsPerComponent, nullptr);
+    }
+
+    static SkEncodedInfo MakeSRGB(int width, int height, Color color, Alpha alpha,
+            int bitsPerComponent) {
+        return Make(width, height, color, alpha, bitsPerComponent, ICCProfile::MakeSRGB());
+    }
+
+    static SkEncodedInfo Make(int width, int height, Color color, Alpha alpha,
+            int bitsPerComponent, std::unique_ptr<ICCProfile> profile) {
         SkASSERT(1 == bitsPerComponent ||
                  2 == bitsPerComponent ||
                  4 == bitsPerComponent ||
@@ -105,29 +143,51 @@
                 SkASSERT(kOpaque_Alpha != alpha);
                 SkASSERT(8 == bitsPerComponent);
                 break;
+            case kXAlpha_Color:
+                SkASSERT(kUnpremul_Alpha == alpha);
+                SkASSERT(8 == bitsPerComponent);
+                break;
+            case k565_Color:
+                SkASSERT(kOpaque_Alpha == alpha);
+                SkASSERT(8 == bitsPerComponent);
+                break;
             default:
                 SkASSERT(false);
                 break;
         }
 
-        return SkEncodedInfo(color, alpha, bitsPerComponent);
+        return SkEncodedInfo(width, height, color, alpha, bitsPerComponent, std::move(profile));
     }
 
     /*
-     * Returns an SkImageInfo with Skia color and alpha types that are the
-     * closest possible match to the encoded info.
+     * Returns a recommended SkImageInfo.
+     *
+     * TODO: Leave this up to the client.
      */
-    SkImageInfo makeImageInfo(int width, int height, sk_sp<SkColorSpace> colorSpace) const {
-        auto ct = kGray_Color == fColor ? kGray_8_SkColorType   :
-                                          kN32_SkColorType      ;
+    SkImageInfo makeImageInfo() const {
+        auto ct =  kGray_Color == fColor ? kGray_8_SkColorType   :
+                 kXAlpha_Color == fColor ? kAlpha_8_SkColorType  :
+                    k565_Color == fColor ? kRGB_565_SkColorType  :
+                                           kN32_SkColorType      ;
         auto alpha = kOpaque_Alpha == fAlpha ? kOpaque_SkAlphaType
                                              : kUnpremul_SkAlphaType;
-        return SkImageInfo::Make(width, height, ct, alpha, std::move(colorSpace));
+        sk_sp<SkColorSpace> cs = fProfile ? SkColorSpace::Make(*fProfile->profile())
+                                          : nullptr;
+        if (!cs) {
+            cs = SkColorSpace::MakeSRGB();
+        }
+        return SkImageInfo::Make(fWidth, fHeight, ct, alpha, std::move(cs));
     }
 
-    Color color() const { return fColor; }
-    Alpha alpha() const { return fAlpha; }
+    int   width() const { return fWidth;  }
+    int  height() const { return fHeight; }
+    Color color() const { return fColor;  }
+    Alpha alpha() const { return fAlpha;  }
     bool opaque() const { return fAlpha == kOpaque_Alpha; }
+    const skcms_ICCProfile* profile() const {
+        if (!fProfile) return nullptr;
+        return fProfile->profile();
+    }
 
     uint8_t bitsPerComponent() const { return fBitsPerComponent; }
 
@@ -135,6 +195,7 @@
         switch (fColor) {
             case kGray_Color:
                 return fBitsPerComponent;
+            case kXAlpha_Color:
             case kGrayAlpha_Color:
                 return 2 * fBitsPerComponent;
             case kPalette_Color:
@@ -142,6 +203,7 @@
             case kRGB_Color:
             case kBGR_Color:
             case kYUV_Color:
+            case k565_Color:
                 return 3 * fBitsPerComponent;
             case kRGBA_Color:
             case kBGRA_Color:
@@ -156,17 +218,38 @@
         }
     }
 
-private:
+    SkEncodedInfo(const SkEncodedInfo& orig) = delete;
+    SkEncodedInfo& operator=(const SkEncodedInfo&) = delete;
 
-    SkEncodedInfo(Color color, Alpha alpha, uint8_t bitsPerComponent)
-        : fColor(color)
+    SkEncodedInfo(SkEncodedInfo&& orig) = default;
+    SkEncodedInfo& operator=(SkEncodedInfo&&) = default;
+
+    // Explicit copy method, to avoid accidental copying.
+    SkEncodedInfo copy() const {
+        auto copy = SkEncodedInfo::Make(fWidth, fHeight, fColor, fAlpha, fBitsPerComponent);
+        if (fProfile) {
+            copy.fProfile.reset(new ICCProfile(*fProfile.get()));
+        }
+        return copy;
+    }
+
+private:
+    SkEncodedInfo(int width, int height, Color color, Alpha alpha,
+            uint8_t bitsPerComponent, std::unique_ptr<ICCProfile> profile)
+        : fWidth(width)
+        , fHeight(height)
+        , fColor(color)
         , fAlpha(alpha)
         , fBitsPerComponent(bitsPerComponent)
+        , fProfile(std::move(profile))
     {}
 
-    Color   fColor;
-    Alpha   fAlpha;
-    uint8_t fBitsPerComponent;
+    int                         fWidth;
+    int                         fHeight;
+    Color                       fColor;
+    Alpha                       fAlpha;
+    uint8_t                     fBitsPerComponent;
+    std::unique_ptr<ICCProfile> fProfile;
 };
 
 #endif
diff --git a/resources/images/mandrill_cmyk.jpg b/resources/images/mandrill_cmyk.jpg
new file mode 100644
index 0000000..0a7f29b
--- /dev/null
+++ b/resources/images/mandrill_cmyk.jpg
Binary files differ
diff --git a/src/codec/SkBmpBaseCodec.cpp b/src/codec/SkBmpBaseCodec.cpp
index c548514..2b0ed1f 100644
--- a/src/codec/SkBmpBaseCodec.cpp
+++ b/src/codec/SkBmpBaseCodec.cpp
@@ -9,9 +9,8 @@
 
 SkBmpBaseCodec::~SkBmpBaseCodec() {}
 
-SkBmpBaseCodec::SkBmpBaseCodec(int width, int height, const SkEncodedInfo& info,
-                               std::unique_ptr<SkStream> stream,
+SkBmpBaseCodec::SkBmpBaseCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
                                uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder)
-    : INHERITED(width, height, info, std::move(stream), bitsPerPixel, rowOrder)
+    : INHERITED(std::move(info), std::move(stream), bitsPerPixel, rowOrder)
     , fSrcBuffer(sk_malloc_canfail(this->srcRowBytes()))
 {}
diff --git a/src/codec/SkBmpBaseCodec.h b/src/codec/SkBmpBaseCodec.h
index 24277fc..8a076a5 100644
--- a/src/codec/SkBmpBaseCodec.h
+++ b/src/codec/SkBmpBaseCodec.h
@@ -25,7 +25,7 @@
     bool didCreateSrcBuffer() const { return fSrcBuffer != nullptr; }
 
 protected:
-    SkBmpBaseCodec(int width, int height, const SkEncodedInfo& info, std::unique_ptr<SkStream>,
+    SkBmpBaseCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream>,
                    uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder);
 
     uint8_t* srcBuffer() { return reinterpret_cast<uint8_t*>(fSrcBuffer.get()); }
diff --git a/src/codec/SkBmpCodec.cpp b/src/codec/SkBmpCodec.cpp
index 7dd49a5..02d13dd 100644
--- a/src/codec/SkBmpCodec.cpp
+++ b/src/codec/SkBmpCodec.cpp
@@ -481,8 +481,8 @@
                 SkASSERT(!inIco || nullptr != stream->getMemoryBase());
 
                 // Set the image info and create a codec.
-                const SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, bitsPerComponent);
-                codecOut->reset(new SkBmpStandardCodec(width, height, info,
+                auto info = SkEncodedInfo::MakeSRGB(width, height, color, alpha, bitsPerComponent);
+                codecOut->reset(new SkBmpStandardCodec(std::move(info),
                                                        std::unique_ptr<SkStream>(stream),
                                                        bitsPerPixel, numColors, bytesPerColor,
                                                        offset - bytesRead, rowOrder, isOpaque,
@@ -539,8 +539,8 @@
                     color = SkEncodedInfo::kBGR_Color;
                     alpha = SkEncodedInfo::kOpaque_Alpha;
                 }
-                const SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8);
-                codecOut->reset(new SkBmpMaskCodec(width, height, info,
+                auto info = SkEncodedInfo::MakeSRGB(width, height, color, alpha, 8);
+                codecOut->reset(new SkBmpMaskCodec(std::move(info),
                                                    std::unique_ptr<SkStream>(stream), bitsPerPixel,
                                                    masks.release(), rowOrder));
                 return static_cast<SkBmpMaskCodec*>(codecOut->get())->didCreateSrcBuffer()
@@ -570,9 +570,9 @@
                 // is uncommon, but we cannot be certain that an RLE bmp will be
                 // opaque or that we will be able to represent it with a palette.
                 // For that reason, we always indicate that we are kBGRA.
-                const SkEncodedInfo info = SkEncodedInfo::Make(SkEncodedInfo::kBGRA_Color,
-                        SkEncodedInfo::kBinary_Alpha, 8);
-                codecOut->reset(new SkBmpRLECodec(width, height, info,
+                auto info = SkEncodedInfo::MakeSRGB(width, height, SkEncodedInfo::kBGRA_Color,
+                                                SkEncodedInfo::kBinary_Alpha, 8);
+                codecOut->reset(new SkBmpRLECodec(std::move(info),
                                                   std::unique_ptr<SkStream>(stream), bitsPerPixel,
                                                   numColors, bytesPerColor, offset - bytesRead,
                                                   rowOrder));
@@ -600,14 +600,12 @@
     return kSuccess == *result ? std::move(codec) : nullptr;
 }
 
-SkBmpCodec::SkBmpCodec(int width, int height, const SkEncodedInfo& info,
-                       std::unique_ptr<SkStream> stream,
+SkBmpCodec::SkBmpCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
         uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder)
-    : INHERITED(width, height, info, kXformSrcColorFormat, std::move(stream),
-                SkColorSpace::MakeSRGB())
+    : INHERITED(std::move(info), kXformSrcColorFormat, std::move(stream))
     , fBitsPerPixel(bitsPerPixel)
     , fRowOrder(rowOrder)
-    , fSrcRowBytes(SkAlign4(compute_row_bytes(width, fBitsPerPixel)))
+    , fSrcRowBytes(SkAlign4(compute_row_bytes(this->getEncodedInfo().width(), fBitsPerPixel)))
     , fXformBuffer(nullptr)
 {}
 
diff --git a/src/codec/SkBmpCodec.h b/src/codec/SkBmpCodec.h
index 3196ae1..eff1891 100644
--- a/src/codec/SkBmpCodec.h
+++ b/src/codec/SkBmpCodec.h
@@ -38,7 +38,7 @@
 
 protected:
 
-    SkBmpCodec(int width, int height, const SkEncodedInfo& info, std::unique_ptr<SkStream>,
+    SkBmpCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream>,
             uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder);
 
     SkEncodedImageFormat onGetEncodedFormat() const override { return SkEncodedImageFormat::kBMP; }
@@ -103,7 +103,7 @@
      * than RGBA.
      */
     static constexpr SkColorType kXformSrcColorType = kBGRA_8888_SkColorType;
-    static constexpr auto kXformSrcColorFormat = SkColorSpaceXform::kBGRA_8888_ColorFormat;
+    static constexpr auto kXformSrcColorFormat = skcms_PixelFormat_BGRA_8888;
 
 private:
 
diff --git a/src/codec/SkBmpMaskCodec.cpp b/src/codec/SkBmpMaskCodec.cpp
index ddcc47b..0cb0924 100644
--- a/src/codec/SkBmpMaskCodec.cpp
+++ b/src/codec/SkBmpMaskCodec.cpp
@@ -12,11 +12,11 @@
 /*
  * Creates an instance of the decoder
  */
-SkBmpMaskCodec::SkBmpMaskCodec(int width, int height, const SkEncodedInfo& info,
+SkBmpMaskCodec::SkBmpMaskCodec(SkEncodedInfo&& info,
                                std::unique_ptr<SkStream> stream,
                                uint16_t bitsPerPixel, SkMasks* masks,
                                SkCodec::SkScanlineOrder rowOrder)
-    : INHERITED(width, height, info, std::move(stream), bitsPerPixel, rowOrder)
+    : INHERITED(std::move(info), std::move(stream), bitsPerPixel, rowOrder)
     , fMasks(masks)
     , fMaskSwizzler(nullptr)
 {}
diff --git a/src/codec/SkBmpMaskCodec.h b/src/codec/SkBmpMaskCodec.h
index 4b0dbde..370cddb 100644
--- a/src/codec/SkBmpMaskCodec.h
+++ b/src/codec/SkBmpMaskCodec.h
@@ -31,7 +31,7 @@
      * @param masks color masks for certain bmp formats
      * @param rowOrder indicates whether rows are ordered top-down or bottom-up
      */
-    SkBmpMaskCodec(int width, int height, const SkEncodedInfo& info, std::unique_ptr<SkStream>,
+    SkBmpMaskCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream>,
             uint16_t bitsPerPixel, SkMasks* masks,
             SkCodec::SkScanlineOrder rowOrder);
 
diff --git a/src/codec/SkBmpRLECodec.cpp b/src/codec/SkBmpRLECodec.cpp
index 3fe7a03..8a7fb9e8 100644
--- a/src/codec/SkBmpRLECodec.cpp
+++ b/src/codec/SkBmpRLECodec.cpp
@@ -14,12 +14,12 @@
  * Creates an instance of the decoder
  * Called only by NewFromStream
  */
-SkBmpRLECodec::SkBmpRLECodec(int width, int height, const SkEncodedInfo& info,
+SkBmpRLECodec::SkBmpRLECodec(SkEncodedInfo&& info,
                              std::unique_ptr<SkStream> stream,
                              uint16_t bitsPerPixel, uint32_t numColors,
                              uint32_t bytesPerColor, uint32_t offset,
                              SkCodec::SkScanlineOrder rowOrder)
-    : INHERITED(width, height, info, std::move(stream), bitsPerPixel, rowOrder)
+    : INHERITED(std::move(info), std::move(stream), bitsPerPixel, rowOrder)
     , fColorTable(nullptr)
     , fNumColors(numColors)
     , fBytesPerColor(bytesPerColor)
diff --git a/src/codec/SkBmpRLECodec.h b/src/codec/SkBmpRLECodec.h
index 70e97a7..dc6f26d 100644
--- a/src/codec/SkBmpRLECodec.h
+++ b/src/codec/SkBmpRLECodec.h
@@ -35,7 +35,7 @@
      *               headers
      * @param rowOrder indicates whether rows are ordered top-down or bottom-up
      */
-    SkBmpRLECodec(int width, int height, const SkEncodedInfo& info, std::unique_ptr<SkStream>,
+    SkBmpRLECodec(SkEncodedInfo&& info, std::unique_ptr<SkStream>,
             uint16_t bitsPerPixel, uint32_t numColors, uint32_t bytesPerColor,
             uint32_t offset, SkCodec::SkScanlineOrder rowOrder);
 
diff --git a/src/codec/SkBmpStandardCodec.cpp b/src/codec/SkBmpStandardCodec.cpp
index 153d081..dd71535 100644
--- a/src/codec/SkBmpStandardCodec.cpp
+++ b/src/codec/SkBmpStandardCodec.cpp
@@ -14,12 +14,12 @@
  * Creates an instance of the decoder
  * Called only by NewFromStream
  */
-SkBmpStandardCodec::SkBmpStandardCodec(int width, int height, const SkEncodedInfo& info,
-                                       std::unique_ptr<SkStream> stream, uint16_t bitsPerPixel,
-                                       uint32_t numColors, uint32_t bytesPerColor, uint32_t offset,
+SkBmpStandardCodec::SkBmpStandardCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
+                                       uint16_t bitsPerPixel, uint32_t numColors,
+                                       uint32_t bytesPerColor, uint32_t offset,
                                        SkCodec::SkScanlineOrder rowOrder,
                                        bool isOpaque, bool inIco)
-    : INHERITED(width, height, info, std::move(stream), bitsPerPixel, rowOrder)
+    : INHERITED(std::move(info), std::move(stream), bitsPerPixel, rowOrder)
     , fColorTable(nullptr)
     , fNumColors(numColors)
     , fBytesPerColor(bytesPerColor)
@@ -146,20 +146,33 @@
     return true;
 }
 
+static SkEncodedInfo make_info(SkEncodedInfo::Color color,
+                               SkEncodedInfo::Alpha alpha, int bitsPerPixel) {
+    // This is just used for the swizzler, which does not need the width or height.
+    return SkEncodedInfo::Make(0, 0, color, alpha, bitsPerPixel);
+}
+
+SkEncodedInfo SkBmpStandardCodec::swizzlerInfo() const {
+    const auto& info = this->getEncodedInfo();
+    if (fInIco) {
+        if (this->bitsPerPixel() <= 8) {
+            return make_info(SkEncodedInfo::kPalette_Color,
+                             info.alpha(), this->bitsPerPixel());
+        }
+        if (this->bitsPerPixel() == 24) {
+            return make_info(SkEncodedInfo::kBGR_Color,
+                             SkEncodedInfo::kOpaque_Alpha, 8);
+        }
+    }
+
+    return make_info(info.color(), info.alpha(), info.bitsPerComponent());
+}
+
 void SkBmpStandardCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts) {
     // In the case of bmp-in-icos, we will report BGRA to the client,
     // since we may be required to apply an alpha mask after the decode.
     // However, the swizzler needs to know the actual format of the bmp.
-    SkEncodedInfo encodedInfo = this->getEncodedInfo();
-    if (fInIco) {
-        if (this->bitsPerPixel() <= 8) {
-            encodedInfo = SkEncodedInfo::Make(SkEncodedInfo::kPalette_Color,
-                    encodedInfo.alpha(), this->bitsPerPixel());
-        } else if (this->bitsPerPixel() == 24) {
-            encodedInfo = SkEncodedInfo::Make(SkEncodedInfo::kBGR_Color,
-                    SkEncodedInfo::kOpaque_Alpha, 8);
-        }
-    }
+    SkEncodedInfo encodedInfo = this->swizzlerInfo();
 
     // Get a pointer to the color table if it exists
     const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
diff --git a/src/codec/SkBmpStandardCodec.h b/src/codec/SkBmpStandardCodec.h
index 1790692..84a1299 100644
--- a/src/codec/SkBmpStandardCodec.h
+++ b/src/codec/SkBmpStandardCodec.h
@@ -39,9 +39,9 @@
      *                 the icp mask, if there is one)
      * @param inIco    indicates if the bmp is embedded in an ico file
      */
-    SkBmpStandardCodec(int width, int height, const SkEncodedInfo& info,
-                       std::unique_ptr<SkStream> stream, uint16_t bitsPerPixel, uint32_t numColors,
-                       uint32_t bytesPerColor, uint32_t offset, SkCodec::SkScanlineOrder rowOrder,
+    SkBmpStandardCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
+                       uint16_t bitsPerPixel, uint32_t numColors, uint32_t bytesPerColor,
+                       uint32_t offset, SkCodec::SkScanlineOrder rowOrder,
                        bool isOpaque, bool inIco);
 
 protected:
@@ -63,12 +63,8 @@
     }
 
 private:
-
-    /*
-     * Creates the color table
-     */
     bool createColorTable(SkColorType colorType, SkAlphaType alphaType);
-
+    SkEncodedInfo swizzlerInfo() const;
     void initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts);
 
     int decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes,
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp
index 5564e89..d12646a 100644
--- a/src/codec/SkCodec.cpp
+++ b/src/codec/SkCodec.cpp
@@ -9,7 +9,6 @@
 #include "SkCodec.h"
 #include "SkCodecPriv.h"
 #include "SkColorSpace.h"
-#include "SkColorSpaceXformPriv.h"
 #include "SkData.h"
 #include "SkFrameHolder.h"
 #include "SkGifCodec.h"
@@ -126,26 +125,10 @@
     return MakeFromStream(SkMemoryStream::Make(std::move(data)), nullptr, reader);
 }
 
-SkCodec::SkCodec(int width, int height, const SkEncodedInfo& info,
-        XformFormat srcFormat, std::unique_ptr<SkStream> stream,
-        sk_sp<SkColorSpace> colorSpace, SkEncodedOrigin origin)
-    : fEncodedInfo(info)
-    , fSrcInfo(info.makeImageInfo(width, height, std::move(colorSpace)))
-    , fSrcXformFormat(srcFormat)
-    , fStream(std::move(stream))
-    , fNeedsRewind(false)
-    , fOrigin(origin)
-    , fDstInfo()
-    , fOptions()
-    , fCurrScanline(-1)
-    , fStartedIncrementalDecode(false)
-{}
-
-SkCodec::SkCodec(const SkEncodedInfo& info, const SkImageInfo& imageInfo,
-        XformFormat srcFormat, std::unique_ptr<SkStream> stream,
-        SkEncodedOrigin origin)
-    : fEncodedInfo(info)
-    , fSrcInfo(imageInfo)
+SkCodec::SkCodec(SkEncodedInfo&& info, XformFormat srcFormat, std::unique_ptr<SkStream> stream,
+                 SkEncodedOrigin origin)
+    : fEncodedInfo(std::move(info))
+    , fSrcInfo(fEncodedInfo.makeImageInfo())
     , fSrcXformFormat(srcFormat)
     , fStream(std::move(stream))
     , fNeedsRewind(false)
@@ -159,7 +142,7 @@
 SkCodec::~SkCodec() {}
 
 bool SkCodec::conversionSupported(const SkImageInfo& dst, SkColorType srcColor,
-                                  bool srcIsOpaque, const SkColorSpace* srcCS) const {
+                                  bool srcIsOpaque, bool needsColorXform) {
     if (!valid_alpha(dst.alphaType(), srcIsOpaque)) {
         return false;
     }
@@ -173,8 +156,8 @@
         case kRGB_565_SkColorType:
             return srcIsOpaque;
         case kGray_8_SkColorType:
-            return kGray_8_SkColorType == srcColor && srcIsOpaque &&
-                   !needs_color_xform(dst, srcCS);
+            SkASSERT(!needsColorXform);
+            return kGray_8_SkColorType == srcColor && srcIsOpaque;
         case kAlpha_8_SkColorType:
             // conceptually we can convert anything into alpha_8, but we haven't actually coded
             // all of those other conversions yet, so only return true for the case we have codec.
@@ -182,7 +165,6 @@
         default:
             return false;
     }
-
 }
 
 bool SkCodec::rewindIfNeeded() {
@@ -245,13 +227,8 @@
                                           const Options& options) {
     const int index = options.fFrameIndex;
     if (0 == index) {
-        if (!this->conversionSupported(info, fSrcInfo.colorType(), fEncodedInfo.opaque(),
-                                      fSrcInfo.colorSpace())
-            || !this->initializeColorXform(info, fEncodedInfo.alpha()))
-        {
-            return kInvalidConversion;
-        }
-        return kSuccess;
+        return this->initializeColorXform(info, fEncodedInfo.alpha(), fEncodedInfo.opaque())
+            ? kSuccess : kInvalidConversion;
     }
 
     if (index < 0) {
@@ -274,11 +251,6 @@
     const auto* frame = frameHolder->getFrame(index);
     SkASSERT(frame);
 
-    if (!this->conversionSupported(info, fSrcInfo.colorType(), !frame->hasAlpha(),
-                                   fSrcInfo.colorSpace())) {
-        return kInvalidConversion;
-    }
-
     const int requiredFrame = frame->getRequiredFrame();
     if (requiredFrame != kNoFrame) {
         if (options.fPriorFrame != kNoFrame) {
@@ -324,7 +296,8 @@
         }
     }
 
-    return this->initializeColorXform(info, frame->reportedAlpha()) ? kSuccess : kInvalidConversion;
+    return this->initializeColorXform(info, frame->reportedAlpha(), !frame->hasAlpha())
+        ? kSuccess : kInvalidConversion;
 }
 
 SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
@@ -612,63 +585,80 @@
     }
 }
 
-static inline SkColorSpaceXform::ColorFormat select_xform_format_ct(SkColorType colorType) {
+static inline bool select_xform_format(SkColorType colorType, bool forColorTable,
+                                       skcms_PixelFormat* outFormat) {
+    SkASSERT(outFormat);
+
     switch (colorType) {
         case kRGBA_8888_SkColorType:
-            return SkColorSpaceXform::kRGBA_8888_ColorFormat;
+            *outFormat = skcms_PixelFormat_RGBA_8888;
+            break;
         case kBGRA_8888_SkColorType:
-            return SkColorSpaceXform::kBGRA_8888_ColorFormat;
+            *outFormat = skcms_PixelFormat_BGRA_8888;
+            break;
         case kRGB_565_SkColorType:
+            if (forColorTable) {
 #ifdef SK_PMCOLOR_IS_RGBA
-            return SkColorSpaceXform::kRGBA_8888_ColorFormat;
+                *outFormat = skcms_PixelFormat_RGBA_8888;
 #else
-            return SkColorSpaceXform::kBGRA_8888_ColorFormat;
+                *outFormat = skcms_PixelFormat_BGRA_8888;
 #endif
+                break;
+            }
+            *outFormat = skcms_PixelFormat_BGR_565;
+            break;
+        case kRGBA_F16_SkColorType:
+            *outFormat = skcms_PixelFormat_RGBA_hhhh;
+            break;
         default:
-            SkASSERT(false);
-            return SkColorSpaceXform::kRGBA_8888_ColorFormat;
-    }
-}
-
-bool SkCodec::initializeColorXform(const SkImageInfo& dstInfo, SkEncodedInfo::Alpha encodedAlpha) {
-    fColorXform = nullptr;
-    fXformOnDecode = false;
-    if (!this->usesColorXform()) {
-        return true;
-    }
-    // FIXME: In SkWebpCodec, if a frame is blending with a prior frame, we don't need
-    // a colorXform to do a color correct premul, since the blend step will handle
-    // premultiplication. But there is no way to know whether we need to blend from
-    // inside this call.
-    if (needs_color_xform(dstInfo, fSrcInfo.colorSpace())) {
-        fColorXform = SkMakeColorSpaceXform(fSrcInfo.colorSpace(), dstInfo.colorSpace());
-        if (!fColorXform) {
             return false;
-        }
-
-        // We will apply the color xform when reading the color table unless F16 is requested.
-        fXformOnDecode = SkEncodedInfo::kPalette_Color != fEncodedInfo.color()
-            || kRGBA_F16_SkColorType == dstInfo.colorType();
-        if (fXformOnDecode) {
-            fDstXformFormat = select_xform_format(dstInfo.colorType());
-        } else {
-            fDstXformFormat = select_xform_format_ct(dstInfo.colorType());
-        }
     }
-
     return true;
 }
 
-void SkCodec::applyColorXform(void* dst, const void* src, int count, SkAlphaType at) const {
-    SkASSERT(fColorXform);
-    SkAssertResult(fColorXform->apply(fDstXformFormat, dst,
-                                      fSrcXformFormat, src,
-                                      count, at));
+bool SkCodec::initializeColorXform(const SkImageInfo& dstInfo, SkEncodedInfo::Alpha encodedAlpha,
+                                   bool srcIsOpaque) {
+    fXformTime = kNo_XformTime;
+    bool needsColorXform = false;
+    if (this->usesColorXform() && dstInfo.colorSpace()) {
+        dstInfo.colorSpace()->toProfile(&fDstProfile);
+        if (kRGBA_F16_SkColorType == dstInfo.colorType()
+                || !skcms_ApproximatelyEqualProfiles(fEncodedInfo.profile(), &fDstProfile) ) {
+            needsColorXform = true;
+            if (kGray_8_SkColorType == dstInfo.colorType()) {
+                return false;
+            }
+        }
+    }
+
+    if (!this->conversionSupported(dstInfo, fSrcInfo.colorType(), srcIsOpaque, needsColorXform)) {
+        return false;
+    }
+
+    if (needsColorXform) {
+        fXformTime = SkEncodedInfo::kPalette_Color != fEncodedInfo.color()
+                          || kRGBA_F16_SkColorType == dstInfo.colorType()
+                ? kDecodeRow_XformTime : kPalette_XformTime;
+        if (!select_xform_format(dstInfo.colorType(), fXformTime == kPalette_XformTime,
+                                 &fDstXformFormat)) {
+            return false;
+        }
+        if (encodedAlpha == SkEncodedInfo::kUnpremul_Alpha
+                && dstInfo.alphaType() == kPremul_SkAlphaType) {
+            fDstXformAlphaFormat = skcms_AlphaFormat_PremulAsEncoded;
+        } else {
+            fDstXformAlphaFormat = skcms_AlphaFormat_Unpremul;
+        }
+    }
+    return true;
 }
 
 void SkCodec::applyColorXform(void* dst, const void* src, int count) const {
-    auto alphaType = select_xform_alpha(fDstInfo.alphaType(), fSrcInfo.alphaType());
-    this->applyColorXform(dst, src, count, alphaType);
+    const auto* srcProfile = fEncodedInfo.profile();
+    SkASSERT(srcProfile);
+    SkAssertResult(skcms_Transform(src, fSrcXformFormat, skcms_AlphaFormat_Unpremul, srcProfile,
+                                   dst, fDstXformFormat, fDstXformAlphaFormat, &fDstProfile,
+                                   count));
 }
 
 std::vector<SkCodec::FrameInfo> SkCodec::getFrameInfo() {
diff --git a/src/codec/SkCodecPriv.h b/src/codec/SkCodecPriv.h
index d7db89f..10bd064 100644
--- a/src/codec/SkCodecPriv.h
+++ b/src/codec/SkCodecPriv.h
@@ -9,8 +9,6 @@
 #define SkCodecPriv_DEFINED
 
 #include "SkColorData.h"
-#include "SkColorSpaceXform.h"
-#include "SkColorSpaceXformPriv.h"
 #include "SkColorTable.h"
 #include "SkEncodedInfo.h"
 #include "SkEncodedOrigin.h"
@@ -243,30 +241,6 @@
     }
 }
 
-static inline bool needs_premul(SkAlphaType dstAT, SkEncodedInfo::Alpha encodedAlpha) {
-    return kPremul_SkAlphaType == dstAT && SkEncodedInfo::kUnpremul_Alpha == encodedAlpha;
-}
-
-static inline bool needs_color_xform(const SkImageInfo& dstInfo, const SkColorSpace* srcCS) {
-    // We never perform a color xform in legacy mode.
-    if (!dstInfo.colorSpace()) {
-        return false;
-    }
-
-    // None of the codecs we have output F16 natively, so if we're trying to decode to F16,
-    // we'll have to use SkColorSpaceXform to get there.
-    bool isF16 = kRGBA_F16_SkColorType == dstInfo.colorType();
-
-    // Need a color xform when dst space does not match the src.
-    bool srcDstNotEqual = !SkColorSpace::Equals(srcCS, dstInfo.colorSpace());
-
-    return isF16 || srcDstNotEqual;
-}
-
-static inline SkAlphaType select_xform_alpha(SkAlphaType dstAlphaType, SkAlphaType srcAlphaType) {
-    return (kOpaque_SkAlphaType == srcAlphaType) ? kOpaque_SkAlphaType : dstAlphaType;
-}
-
 bool is_orientation_marker(const uint8_t* data, size_t data_length, SkEncodedOrigin* orientation);
 
 #endif // SkCodecPriv_DEFINED
diff --git a/src/codec/SkEncodedInfo.cpp b/src/codec/SkEncodedInfo.cpp
index 63c52b3..75c5062 100644
--- a/src/codec/SkEncodedInfo.cpp
+++ b/src/codec/SkEncodedInfo.cpp
@@ -5,4 +5,28 @@
  * found in the LICENSE file.
  */
 
-// Dummy file to assist in landing https://skia-review.googlesource.com/c/skia/+/136062
+#include "SkEncodedInfo.h"
+
+std::unique_ptr<SkEncodedInfo::ICCProfile> SkEncodedInfo::ICCProfile::Make(sk_sp<SkData> data) {
+    if (data) {
+        skcms_ICCProfile profile;
+        if (skcms_Parse(data->data(), data->size(), &profile)) {
+            return std::unique_ptr<ICCProfile>(new ICCProfile(profile, std::move(data)));
+        }
+    }
+    return nullptr;
+}
+
+std::unique_ptr<SkEncodedInfo::ICCProfile> SkEncodedInfo::ICCProfile::MakeSRGB() {
+    return std::unique_ptr<ICCProfile>(new ICCProfile(*skcms_sRGB_profile()));
+}
+
+std::unique_ptr<SkEncodedInfo::ICCProfile> SkEncodedInfo::ICCProfile::Make(
+        const skcms_ICCProfile& profile) {
+    return std::unique_ptr<ICCProfile>(new ICCProfile(profile));
+}
+
+SkEncodedInfo::ICCProfile::ICCProfile(const skcms_ICCProfile& profile, sk_sp<SkData> data)
+    : fProfile(profile)
+    , fData(std::move(data))
+{}
diff --git a/src/codec/SkGifCodec.cpp b/src/codec/SkGifCodec.cpp
index 3ac5ed3..77160cb 100644
--- a/src/codec/SkGifCodec.cpp
+++ b/src/codec/SkGifCodec.cpp
@@ -91,18 +91,9 @@
     // Use kPalette since Gifs are encoded with a color table.
     // FIXME: Gifs can actually be encoded with 4-bits per pixel. Using 8 works, but we could skip
     //        expanding to 8 bits and take advantage of the SkSwizzler to work from 4.
-    const auto encodedInfo = SkEncodedInfo::Make(SkEncodedInfo::kPalette_Color, alpha, 8);
-
-    // The choice of unpremul versus premul is arbitrary, since all colors are either fully
-    // opaque or fully transparent (i.e. kBinary), but we stored the transparent colors as all
-    // zeroes, which is arguably premultiplied.
-    const auto alphaType = reader->firstFrameHasAlpha() ? kUnpremul_SkAlphaType
-                                                        : kOpaque_SkAlphaType;
-
-    const auto imageInfo = SkImageInfo::Make(reader->screenWidth(), reader->screenHeight(),
-                                             kN32_SkColorType, alphaType,
-                                             SkColorSpace::MakeSRGB());
-    return std::unique_ptr<SkCodec>(new SkGifCodec(encodedInfo, imageInfo, reader.release()));
+    auto encodedInfo = SkEncodedInfo::MakeSRGB(reader->screenWidth(), reader->screenHeight(),
+                                               SkEncodedInfo::kPalette_Color, alpha, 8);
+    return std::unique_ptr<SkCodec>(new SkGifCodec(std::move(encodedInfo), reader.release()));
 }
 
 bool SkGifCodec::onRewind() {
@@ -110,9 +101,8 @@
     return true;
 }
 
-SkGifCodec::SkGifCodec(const SkEncodedInfo& encodedInfo, const SkImageInfo& imageInfo,
-                       SkGifImageReader* reader)
-    : INHERITED(encodedInfo, imageInfo, SkColorSpaceXform::kRGBA_8888_ColorFormat, nullptr)
+SkGifCodec::SkGifCodec(SkEncodedInfo&& encodedInfo, SkGifImageReader* reader)
+    : INHERITED(std::move(encodedInfo), skcms_PixelFormat_RGBA_8888, nullptr)
     , fReader(reader)
     , fTmpBuffer(nullptr)
     , fSwizzler(nullptr)
@@ -157,7 +147,6 @@
 }
 
 static constexpr SkColorType kXformSrcColorType = kRGBA_8888_SkColorType;
-static constexpr SkAlphaType kXformAlphaType    = kUnpremul_SkAlphaType;
 
 void SkGifCodec::initializeColorTable(const SkImageInfo& dstInfo, int frameIndex) {
     SkColorType colorTableColorType = dstInfo.colorType();
@@ -173,8 +162,8 @@
         fCurrColorTable.reset(new SkColorTable(&color, 1));
     } else if (this->colorXform() && !this->xformOnDecode()) {
         SkPMColor dstColors[256];
-        this->applyColorXform(dstColors, currColorTable->readColors(), currColorTable->count(),
-                              kXformAlphaType);
+        this->applyColorXform(dstColors, currColorTable->readColors(),
+                              currColorTable->count());
         fCurrColorTable.reset(new SkColorTable(dstColors, currColorTable->count()));
     } else {
         fCurrColorTable = std::move(currColorTable);
@@ -402,7 +391,7 @@
         fSwizzler->swizzle(fXformBuffer.get(), src);
 
         const int xformWidth = get_scaled_dimension(dstInfo.width(), fSwizzler->sampleX());
-        this->applyColorXform(dst, fXformBuffer.get(), xformWidth, kXformAlphaType);
+        this->applyColorXform(dst, fXformBuffer.get(), xformWidth);
     } else {
         fSwizzler->swizzle(dst, src);
     }
diff --git a/src/codec/SkGifCodec.h b/src/codec/SkGifCodec.h
index 21dfd2b..4dd1f0b 100644
--- a/src/codec/SkGifCodec.h
+++ b/src/codec/SkGifCodec.h
@@ -126,7 +126,7 @@
      * Called only by NewFromStream
      * Takes ownership of the SkGifImageReader
      */
-    SkGifCodec(const SkEncodedInfo&, const SkImageInfo&, SkGifImageReader*);
+    SkGifCodec(SkEncodedInfo&&, SkGifImageReader*);
 
     std::unique_ptr<SkGifImageReader>   fReader;
     std::unique_ptr<uint8_t[]>          fTmpBuffer;
diff --git a/src/codec/SkHeifCodec.cpp b/src/codec/SkHeifCodec.cpp
index 31057a0..f5785d6 100644
--- a/src/codec/SkHeifCodec.cpp
+++ b/src/codec/SkHeifCodec.cpp
@@ -133,40 +133,37 @@
         return nullptr;
     }
 
-    SkEncodedInfo info = SkEncodedInfo::Make(
-            SkEncodedInfo::kYUV_Color, SkEncodedInfo::kOpaque_Alpha, 8);
+    std::unique_ptr<SkEncodedInfo::ICCProfile> profile = nullptr;
+    if ((frameInfo.mIccSize > 0) && (frameInfo.mIccData != nullptr)) {
+        // FIXME: Would it be possible to use MakeWithoutCopy?
+        auto icc = SkData::MakeWithCopy(frameInfo.mIccData.get(), frameInfo.mIccSize);
+        profile = SkEncodedInfo::ICCProfile::Make(std::move(icc));
+    }
+    if (!profile || profile->profile()->data_color_space != skcms_Signature_RGB) {
+        profile = SkEncodedInfo::ICCProfile::MakeSRGB();
+    }
 
+    SkEncodedInfo info = SkEncodedInfo::Make(frameInfo.mWidth, frameInfo.mHeight,
+            SkEncodedInfo::kYUV_Color, SkEncodedInfo::kOpaque_Alpha, 8, std::move(profile));
     SkEncodedOrigin orientation = get_orientation(frameInfo);
 
-    sk_sp<SkColorSpace> colorSpace = nullptr;
-    if ((frameInfo.mIccSize > 0) && (frameInfo.mIccData != nullptr)) {
-        colorSpace = SkColorSpace::MakeICC(frameInfo.mIccData.get(),
-                                           frameInfo.mIccSize);
-    }
-    if (!colorSpace || colorSpace->type() != SkColorSpace::kRGB_Type) {
-        colorSpace = SkColorSpace::MakeSRGB();
-    }
-
     *result = kSuccess;
-    return std::unique_ptr<SkCodec>(new SkHeifCodec(frameInfo.mWidth, frameInfo.mHeight,
-            info, heifDecoder.release(), std::move(colorSpace), orientation));
+    return std::unique_ptr<SkCodec>(new SkHeifCodec(std::move(info), heifDecoder.release(),
+                                                    orientation));
 }
 
-SkHeifCodec::SkHeifCodec(int width, int height, const SkEncodedInfo& info,
-        HeifDecoder* heifDecoder, sk_sp<SkColorSpace> colorSpace, SkEncodedOrigin origin)
-    : INHERITED(width, height, info, SkColorSpaceXform::kRGBA_8888_ColorFormat,
-            nullptr, std::move(colorSpace), origin)
+SkHeifCodec::SkHeifCodec(SkEncodedInfo&& info, HeifDecoder* heifDecoder, SkEncodedOrigin origin)
+    : INHERITED(std::move(info), skcms_PixelFormat_RGBA_8888, nullptr, origin)
     , fHeifDecoder(heifDecoder)
     , fSwizzleSrcRow(nullptr)
     , fColorXformSrcRow(nullptr)
 {}
 
-/*
- * Checks if the conversion between the input image and the requested output
- * image has been implemented
- * Sets the output color format
- */
-bool SkHeifCodec::setOutputColorFormat(const SkImageInfo& dstInfo) {
+
+bool SkHeifCodec::conversionSupported(const SkImageInfo& dstInfo, SkColorType /*srcColorType*/,
+                                      bool srcIsOpaque, bool needsColorXform) {
+    SkASSERT(srcIsOpaque);
+
     if (kUnknown_SkAlphaType == dstInfo.alphaType()) {
         return false;
     }
@@ -184,14 +181,14 @@
             return fHeifDecoder->setOutputColor(kHeifColorFormat_BGRA_8888);
 
         case kRGB_565_SkColorType:
-            if (this->colorXform()) {
+            if (needsColorXform) {
                 return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
             } else {
                 return fHeifDecoder->setOutputColor(kHeifColorFormat_RGB565);
             }
 
         case kRGBA_F16_SkColorType:
-            SkASSERT(this->colorXform());
+            SkASSERT(needsColorXform);
             return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
 
         default:
@@ -240,7 +237,7 @@
         }
 
         if (this->colorXform()) {
-            this->applyColorXform(dst, swizzleDst, dstWidth, kOpaque_SkAlphaType);
+            this->applyColorXform(dst, swizzleDst, dstWidth);
             dst = SkTAddOffset<void>(dst, rowBytes);
         }
 
@@ -265,11 +262,6 @@
         return kUnimplemented;
     }
 
-    // Check if we can decode to the requested destination and set the output color space
-    if (!this->setOutputColorFormat(dstInfo)) {
-        return kInvalidConversion;
-    }
-
     if (!fHeifDecoder->decode(&fFrameInfo)) {
         return kInvalidInput;
     }
@@ -313,15 +305,13 @@
 
 void SkHeifCodec::initializeSwizzler(
         const SkImageInfo& dstInfo, const Options& options) {
-    SkEncodedInfo swizzlerInfo = this->getEncodedInfo();
-
     SkImageInfo swizzlerDstInfo = dstInfo;
     if (this->colorXform()) {
         // The color xform will be expecting RGBA 8888 input.
         swizzlerDstInfo = swizzlerDstInfo.makeColorType(kRGBA_8888_SkColorType);
     }
 
-    fSwizzler.reset(SkSwizzler::CreateSwizzler(swizzlerInfo, nullptr,
+    fSwizzler.reset(SkSwizzler::CreateSwizzler(this->getEncodedInfo(), nullptr,
             swizzlerDstInfo, options, nullptr, true));
     SkASSERT(fSwizzler);
 }
@@ -339,11 +329,6 @@
 
 SkCodec::Result SkHeifCodec::onStartScanlineDecode(
         const SkImageInfo& dstInfo, const Options& options) {
-    // Check if we can decode to the requested destination and set the output color space
-    if (!this->setOutputColorFormat(dstInfo)) {
-        return kInvalidConversion;
-    }
-
     // TODO: For now, just decode the whole thing even when there is a subset.
     // If the heif image has tiles, we could potentially do this much faster,
     // but the tile configuration needs to be retrieved from the metadata.
diff --git a/src/codec/SkHeifCodec.h b/src/codec/SkHeifCodec.h
index cdae706..0796805 100644
--- a/src/codec/SkHeifCodec.h
+++ b/src/codec/SkHeifCodec.h
@@ -9,8 +9,6 @@
 #define SkHeifCodec_DEFINED
 
 #include "SkCodec.h"
-#include "SkColorSpace.h"
-#include "SkColorSpaceXform.h"
 #include "SkEncodedOrigin.h"
 #include "SkImageInfo.h"
 #include "SkSwizzler.h"
@@ -43,27 +41,14 @@
         return SkEncodedImageFormat::kHEIF;
     }
 
-    bool conversionSupported(const SkImageInfo&, SkColorType, bool,
-                             const SkColorSpace*) const override {
-        // This class checks for conversion after creating colorXform.
-        return true;
-    }
+    bool conversionSupported(const SkImageInfo&, SkColorType, bool, bool) override;
 
 private:
     /*
      * Creates an instance of the decoder
      * Called only by NewFromStream
      */
-    SkHeifCodec(int width, int height, const SkEncodedInfo&,
-            HeifDecoder*, sk_sp<SkColorSpace>, SkEncodedOrigin);
-
-    /*
-     * Checks if the conversion between the input image and the requested output
-     * image has been implemented.
-     *
-     * Sets the output color format.
-     */
-    bool setOutputColorFormat(const SkImageInfo& dst);
+    SkHeifCodec(SkEncodedInfo&&, HeifDecoder*, SkEncodedOrigin);
 
     void initializeSwizzler(const SkImageInfo& dstInfo, const Options& options);
     void allocateStorage(const SkImageInfo& dstInfo);
diff --git a/src/codec/SkIcoCodec.cpp b/src/codec/SkIcoCodec.cpp
index 8ff4088..d838079 100644
--- a/src/codec/SkIcoCodec.cpp
+++ b/src/codec/SkIcoCodec.cpp
@@ -179,29 +179,19 @@
             maxIndex = i;
         }
     }
-    int width = codecs->operator[](maxIndex)->getInfo().width();
-    int height = codecs->operator[](maxIndex)->getInfo().height();
-    SkEncodedInfo info = codecs->operator[](maxIndex)->getEncodedInfo();
-    SkColorSpace* colorSpace = codecs->operator[](maxIndex)->getInfo().colorSpace();
+
+    auto maxInfo = codecs->operator[](maxIndex)->getEncodedInfo().copy();
 
     *result = kSuccess;
     // The original stream is no longer needed, because the embedded codecs own their
     // own streams.
-    return std::unique_ptr<SkCodec>(new SkIcoCodec(width, height, info, codecs.release(),
-                                                   sk_ref_sp(colorSpace)));
+    return std::unique_ptr<SkCodec>(new SkIcoCodec(std::move(maxInfo), codecs.release()));
 }
 
-/*
- * Creates an instance of the decoder
- * Called only by NewFromStream
- */
-SkIcoCodec::SkIcoCodec(int width, int height, const SkEncodedInfo& info,
-                       SkTArray<std::unique_ptr<SkCodec>, true>* codecs,
-                       sk_sp<SkColorSpace> colorSpace)
-    // The source SkColorSpaceXform::ColorFormat will not be used. The embedded
+SkIcoCodec::SkIcoCodec(SkEncodedInfo&& info, SkTArray<std::unique_ptr<SkCodec>, true>* codecs)
+    // The source skcms_PixelFormat will not be used. The embedded
     // codec's will be used instead.
-    : INHERITED(width, height, info, SkColorSpaceXform::ColorFormat(), nullptr,
-                std::move(colorSpace))
+    : INHERITED(std::move(info), skcms_PixelFormat(), nullptr)
     , fEmbeddedCodecs(codecs)
     , fCurrCodec(nullptr)
 {}
diff --git a/src/codec/SkIcoCodec.h b/src/codec/SkIcoCodec.h
index c43fcf8c..e733e9f 100644
--- a/src/codec/SkIcoCodec.h
+++ b/src/codec/SkIcoCodec.h
@@ -48,8 +48,7 @@
 
     SkScanlineOrder onGetScanlineOrder() const override;
 
-    bool conversionSupported(const SkImageInfo&, SkColorType, bool,
-                             const SkColorSpace*) const override {
+    bool conversionSupported(const SkImageInfo&, SkColorType, bool, bool) override {
         // This will be checked by the embedded codec.
         return true;
     }
@@ -87,8 +86,7 @@
      * Constructor called by NewFromStream
      * @param embeddedCodecs codecs for the embedded images, takes ownership
      */
-    SkIcoCodec(int width, int height, const SkEncodedInfo& info,
-            SkTArray<std::unique_ptr<SkCodec>, true>* embeddedCodecs, sk_sp<SkColorSpace> colorSpace);
+    SkIcoCodec(SkEncodedInfo&& info, SkTArray<std::unique_ptr<SkCodec>, true>* embeddedCodecs);
 
     std::unique_ptr<SkTArray<std::unique_ptr<SkCodec>, true>> fEmbeddedCodecs;
 
diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp
index fd95590..9a90e89 100644
--- a/src/codec/SkJpegCodec.cpp
+++ b/src/codec/SkJpegCodec.cpp
@@ -10,6 +10,7 @@
 #include "SkCodec.h"
 #include "SkCodecPriv.h"
 #include "SkColorData.h"
+#include "SkColorSpace.h"
 #include "SkJpegDecoderMgr.h"
 #include "SkJpegInfo.h"
 #include "SkStream.h"
@@ -131,7 +132,8 @@
  *     (1) Discover all ICC profile markers and verify that they are numbered properly.
  *     (2) Copy the data from each marker into a contiguous ICC profile.
  */
-static sk_sp<SkColorSpace> read_color_space(jpeg_decompress_struct* dinfo) {
+static std::unique_ptr<SkEncodedInfo::ICCProfile> read_color_profile(jpeg_decompress_struct* dinfo)
+{
     // Note that 256 will be enough storage space since each markerIndex is stored in 8-bits.
     jpeg_marker_struct* markerSequence[256];
     memset(markerSequence, 0, sizeof(markerSequence));
@@ -191,11 +193,12 @@
         dst = SkTAddOffset<void>(dst, bytes);
     }
 
-    return SkColorSpace::MakeICC(iccData->data(), iccData->size());
+    return SkEncodedInfo::ICCProfile::Make(std::move(iccData));
 }
 
 SkCodec::Result SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut,
-        JpegDecoderMgr** decoderMgrOut, sk_sp<SkColorSpace> defaultColorSpace) {
+        JpegDecoderMgr** decoderMgrOut,
+        std::unique_ptr<SkEncodedInfo::ICCProfile> defaultColorProfile) {
 
     // Create a JpegDecoderMgr to own all of the decompress information
     std::unique_ptr<JpegDecoderMgr> decoderMgr(new JpegDecoderMgr(stream));
@@ -208,17 +211,18 @@
 
     // Initialize the decompress info and the source manager
     decoderMgr->init();
+    auto* dinfo = decoderMgr->dinfo();
 
     // Instruct jpeg library to save the markers that we care about.  Since
     // the orientation and color profile will not change, we can skip this
     // step on rewinds.
     if (codecOut) {
-        jpeg_save_markers(decoderMgr->dinfo(), kExifMarker, 0xFFFF);
-        jpeg_save_markers(decoderMgr->dinfo(), kICCMarker, 0xFFFF);
+        jpeg_save_markers(dinfo, kExifMarker, 0xFFFF);
+        jpeg_save_markers(dinfo, kICCMarker, 0xFFFF);
     }
 
     // Read the jpeg header
-    switch (jpeg_read_header(decoderMgr->dinfo(), true)) {
+    switch (jpeg_read_header(dinfo, true)) {
         case JPEG_HEADER_OK:
             break;
         case JPEG_SUSPENDED:
@@ -234,42 +238,41 @@
             return kInvalidInput;
         }
 
-        // Create image info object and the codec
-        SkEncodedInfo info = SkEncodedInfo::Make(color, SkEncodedInfo::kOpaque_Alpha, 8);
-
-        SkEncodedOrigin orientation = get_exif_orientation(decoderMgr->dinfo());
-        sk_sp<SkColorSpace> colorSpace = read_color_space(decoderMgr->dinfo());
-        if (colorSpace) {
+        SkEncodedOrigin orientation = get_exif_orientation(dinfo);
+        auto profile = read_color_profile(dinfo);
+        if (profile) {
+            auto type = profile->profile()->data_color_space;
             switch (decoderMgr->dinfo()->jpeg_color_space) {
                 case JCS_CMYK:
                 case JCS_YCCK:
-                    if (colorSpace->type() != SkColorSpace::kCMYK_Type) {
-                        colorSpace = nullptr;
+                    if (type != skcms_Signature_CMYK) {
+                        profile = nullptr;
                     }
                     break;
                 case JCS_GRAYSCALE:
-                    if (colorSpace->type() != SkColorSpace::kGray_Type &&
-                        colorSpace->type() != SkColorSpace::kRGB_Type)
+                    if (type != skcms_Signature_Gray &&
+                        type != skcms_Signature_RGB)
                     {
-                        colorSpace = nullptr;
+                        profile = nullptr;
                     }
                     break;
                 default:
-                    if (colorSpace->type() != SkColorSpace::kRGB_Type) {
-                        colorSpace = nullptr;
+                    if (type != skcms_Signature_RGB) {
+                        profile = nullptr;
                     }
                     break;
             }
         }
-        if (!colorSpace) {
-            colorSpace = defaultColorSpace;
+        if (!profile) {
+            profile = std::move(defaultColorProfile);
         }
 
-        const int width = decoderMgr->dinfo()->image_width;
-        const int height = decoderMgr->dinfo()->image_height;
-        SkJpegCodec* codec = new SkJpegCodec(width, height, info, std::unique_ptr<SkStream>(stream),
-                                             decoderMgr.release(), std::move(colorSpace),
-                                             orientation);
+        SkEncodedInfo info = SkEncodedInfo::Make(dinfo->image_width, dinfo->image_height,
+                                                 color, SkEncodedInfo::kOpaque_Alpha, 8,
+                                                 std::move(profile));
+
+        SkJpegCodec* codec = new SkJpegCodec(std::move(info), std::unique_ptr<SkStream>(stream),
+                                             decoderMgr.release(), orientation);
         *codecOut = codec;
     } else {
         SkASSERT(nullptr != decoderMgrOut);
@@ -280,14 +283,15 @@
 
 std::unique_ptr<SkCodec> SkJpegCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
                                                      Result* result) {
-    return SkJpegCodec::MakeFromStream(std::move(stream), result, SkColorSpace::MakeSRGB());
+    return SkJpegCodec::MakeFromStream(std::move(stream), result,
+                                       // FIXME: This may not be used. Can we skip creating it?
+                                       SkEncodedInfo::ICCProfile::MakeSRGB());
 }
 
 std::unique_ptr<SkCodec> SkJpegCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
-                                                     Result* result,
-                                    sk_sp<SkColorSpace> defaultColorSpace) {
+        Result* result, std::unique_ptr<SkEncodedInfo::ICCProfile> defaultColorProfile) {
     SkCodec* codec = nullptr;
-    *result = ReadHeader(stream.get(), &codec, nullptr, std::move(defaultColorSpace));
+    *result = ReadHeader(stream.get(), &codec, nullptr, std::move(defaultColorProfile));
     if (kSuccess == *result) {
         // Codec has taken ownership of the stream, we do not need to delete it
         SkASSERT(codec);
@@ -297,11 +301,10 @@
     return nullptr;
 }
 
-SkJpegCodec::SkJpegCodec(int width, int height, const SkEncodedInfo& info,
-                         std::unique_ptr<SkStream> stream, JpegDecoderMgr* decoderMgr,
-                         sk_sp<SkColorSpace> colorSpace, SkEncodedOrigin origin)
-    : INHERITED(width, height, info, SkColorSpaceXform::kRGBA_8888_ColorFormat, std::move(stream),
-                std::move(colorSpace), origin)
+SkJpegCodec::SkJpegCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
+                         JpegDecoderMgr* decoderMgr, SkEncodedOrigin origin)
+    : INHERITED(std::move(info), skcms_PixelFormat_RGBA_8888, std::move(stream),
+                origin)
     , fDecoderMgr(decoderMgr)
     , fReadyState(decoderMgr->dinfo()->global_state)
     , fSwizzleSrcRow(nullptr)
@@ -386,12 +389,10 @@
     return true;
 }
 
-/*
- * Checks if the conversion between the input image and the requested output
- * image has been implemented
- * Sets the output color space
- */
-bool SkJpegCodec::setOutputColorSpace(const SkImageInfo& dstInfo) {
+bool SkJpegCodec::conversionSupported(const SkImageInfo& dstInfo, SkColorType srcCT,
+                                      bool srcIsOpaque, bool needsColorXform) {
+    SkASSERT(srcIsOpaque);
+
     if (kUnknown_SkAlphaType == dstInfo.alphaType()) {
         return false;
     }
@@ -409,7 +410,7 @@
             fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA;
             break;
         case kBGRA_8888_SkColorType:
-            if (this->colorXform()) {
+            if (needsColorXform) {
                 // Always using RGBA as the input format for color xforms makes the
                 // implementation a little simpler.
                 fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA;
@@ -418,7 +419,7 @@
             }
             break;
         case kRGB_565_SkColorType:
-            if (this->colorXform()) {
+            if (needsColorXform) {
                 fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA;
             } else {
                 fDecoderMgr->dinfo()->dither_mode = JDITHER_NONE;
@@ -426,14 +427,15 @@
             }
             break;
         case kGray_8_SkColorType:
-            if (this->colorXform() || JCS_GRAYSCALE != encodedColorType) {
+            SkASSERT(!needsColorXform);
+            if (JCS_GRAYSCALE != encodedColorType) {
                 return false;
             }
 
             fDecoderMgr->dinfo()->out_color_space = JCS_GRAYSCALE;
             break;
         case kRGBA_F16_SkColorType:
-            SkASSERT(this->colorXform());
+            SkASSERT(needsColorXform);
             fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA;
             break;
         default:
@@ -539,7 +541,7 @@
         }
 
         if (this->colorXform()) {
-            this->applyColorXform(dst, swizzleDst, dstWidth, kOpaque_SkAlphaType);
+            this->applyColorXform(dst, swizzleDst, dstWidth);
             dst = SkTAddOffset<void>(dst, rowBytes);
         }
 
@@ -587,11 +589,6 @@
         return fDecoderMgr->returnFailure("setjmp", kInvalidInput);
     }
 
-    // Check if we can decode to the requested destination and set the output color space
-    if (!this->setOutputColorSpace(dstInfo)) {
-        return fDecoderMgr->returnFailure("setOutputColorSpace", kInvalidConversion);
-    }
-
     if (!jpeg_start_decompress(dinfo)) {
         return fDecoderMgr->returnFailure("startDecompress", kInvalidInput);
     }
@@ -641,14 +638,16 @@
     }
 }
 
+static SkEncodedInfo make_info(const SkEncodedInfo& orig, bool needsCMYKToRGB) {
+    auto color = needsCMYKToRGB ? SkEncodedInfo::kInvertedCMYK_Color
+                                : orig.color();
+    // The swizzler does not need the width or height
+    return SkEncodedInfo::Make(0, 0, color, orig.alpha(), orig.bitsPerComponent());
+}
+
 void SkJpegCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& options,
         bool needsCMYKToRGB) {
-    SkEncodedInfo swizzlerInfo = this->getEncodedInfo();
-    if (needsCMYKToRGB) {
-        swizzlerInfo = SkEncodedInfo::Make(SkEncodedInfo::kInvertedCMYK_Color,
-                                           swizzlerInfo.alpha(),
-                                           swizzlerInfo.bitsPerComponent());
-    }
+    SkEncodedInfo swizzlerInfo = make_info(this->getEncodedInfo(), needsCMYKToRGB);
 
     Options swizzlerOptions = options;
     if (options.fSubset) {
@@ -693,11 +692,6 @@
         return kInvalidInput;
     }
 
-    // Check if we can decode to the requested destination and set the output color space
-    if (!this->setOutputColorSpace(dstInfo)) {
-        return fDecoderMgr->returnFailure("setOutputColorSpace", kInvalidConversion);
-    }
-
     if (!jpeg_start_decompress(fDecoderMgr->dinfo())) {
         SkCodecPrintf("start decompress failed\n");
         return kInvalidInput;
diff --git a/src/codec/SkJpegCodec.h b/src/codec/SkJpegCodec.h
index 7fab209..e6ec2ce 100644
--- a/src/codec/SkJpegCodec.h
+++ b/src/codec/SkJpegCodec.h
@@ -9,14 +9,13 @@
 #define SkJpegCodec_DEFINED
 
 #include "SkCodec.h"
-#include "SkColorSpace.h"
-#include "SkColorSpaceXform.h"
 #include "SkImageInfo.h"
 #include "SkSwizzler.h"
 #include "SkStream.h"
 #include "SkTemplates.h"
 
 class JpegDecoderMgr;
+class SkColorSpace;
 
 /*
  *
@@ -58,19 +57,14 @@
 
     bool onDimensionsSupported(const SkISize&) override;
 
-    bool conversionSupported(const SkImageInfo&, SkColorType, bool,
-                             const SkColorSpace*) const override {
-        // This class checks for conversion after creating colorXform.
-        return true;
-    }
+    bool conversionSupported(const SkImageInfo&, SkColorType, bool, bool) override;
 
 private:
-
     /*
-     * Allows SkRawCodec to communicate the color space from the exif data.
+     * Allows SkRawCodec to communicate the color profile from the exif data.
      */
     static std::unique_ptr<SkCodec> MakeFromStream(std::unique_ptr<SkStream>, Result*,
-                                                   sk_sp<SkColorSpace> defaultColorSpace);
+            std::unique_ptr<SkEncodedInfo::ICCProfile> defaultColorProfile);
 
     /*
      * Read enough of the stream to initialize the SkJpegCodec.
@@ -90,12 +84,13 @@
      * codecOut will take ownership of it in the case where we created a codec.
      * Ownership is unchanged when we set decoderMgrOut.
      *
-     * @param defaultColorSpace
-     * If the jpeg does not have an embedded color space, the image data should
-     * be tagged with this color space.
+     * @param defaultColorProfile
+     * If the jpeg does not have an embedded color profile, the image data should
+     * be tagged with this color profile.
      */
     static Result ReadHeader(SkStream* stream, SkCodec** codecOut,
-            JpegDecoderMgr** decoderMgrOut, sk_sp<SkColorSpace> defaultColorSpace);
+            JpegDecoderMgr** decoderMgrOut,
+            std::unique_ptr<SkEncodedInfo::ICCProfile> defaultColorProfile);
 
     /*
      * Creates an instance of the decoder
@@ -106,16 +101,8 @@
      * @param decoderMgr holds decompress struct, src manager, and error manager
      *                   takes ownership
      */
-    SkJpegCodec(int width, int height, const SkEncodedInfo& info, std::unique_ptr<SkStream> stream,
-            JpegDecoderMgr* decoderMgr, sk_sp<SkColorSpace> colorSpace, SkEncodedOrigin origin);
-
-    /*
-     * Checks if the conversion between the input image and the requested output
-     * image has been implemented.
-     *
-     * Sets the output color space.
-     */
-    bool setOutputColorSpace(const SkImageInfo& dst);
+    SkJpegCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
+            JpegDecoderMgr* decoderMgr, SkEncodedOrigin origin);
 
     void initializeSwizzler(const SkImageInfo& dstInfo, const Options& options,
                             bool needsCMYKToRGB);
diff --git a/src/codec/SkPngCodec.cpp b/src/codec/SkPngCodec.cpp
index e9972cc..21046d4 100644
--- a/src/codec/SkPngCodec.cpp
+++ b/src/codec/SkPngCodec.cpp
@@ -9,7 +9,6 @@
 #include "SkCodecPriv.h"
 #include "SkColorData.h"
 #include "SkColorSpace.h"
-#include "SkColorSpacePriv.h"
 #include "SkColorTable.h"
 #include "SkMacros.h"
 #include "SkMath.h"
@@ -248,6 +247,10 @@
 
 static constexpr SkColorType kXformSrcColorType = kRGBA_8888_SkColorType;
 
+static inline bool needs_premul(SkAlphaType dstAT, SkEncodedInfo::Alpha encodedAlpha) {
+    return kPremul_SkAlphaType == dstAT && SkEncodedInfo::kUnpremul_Alpha == encodedAlpha;
+}
+
 // Note: SkColorTable claims to store SkPMColors, which is not necessarily the case here.
 bool SkPngCodec::createColorTable(const SkImageInfo& dstInfo) {
 
@@ -265,10 +268,7 @@
     png_bytep alphas;
     int numColorsWithAlpha = 0;
     if (png_get_tRNS(fPng_ptr, fInfo_ptr, &alphas, &numColorsWithAlpha, nullptr)) {
-        // If we are performing a color xform, it will handle the premultiply.  Otherwise,
-        // we'll do it here.
-        bool premultiply = !this->colorXform() && needs_premul(dstInfo.alphaType(),
-                                                               this->getEncodedInfo().alpha());
+        bool premultiply = needs_premul(dstInfo.alphaType(), this->getEncodedInfo().alpha());
 
         // Choose which function to use to create the color table. If the final destination's
         // colortype is unpremultiplied, the color table will store unpremultiplied colors.
@@ -342,13 +342,11 @@
 
 #endif // LIBPNG >= 1.6
 
-// Returns a colorSpace object that represents any color space information in
-// the encoded data.  If the encoded data contains an invalid/unsupported color space,
-// this will return NULL. If there is no color space information, it will guess sRGB
-sk_sp<SkColorSpace> read_color_space(png_structp png_ptr, png_infop info_ptr) {
+// If there is no color profile information, it will use sRGB.
+std::unique_ptr<SkEncodedInfo::ICCProfile> read_color_profile(png_structp png_ptr,
+                                                              png_infop info_ptr) {
 
 #if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 6)
-
     // First check for an ICC profile
     png_bytep profile;
     png_uint_32 length;
@@ -362,74 +360,70 @@
     int compression;
     if (PNG_INFO_iCCP == png_get_iCCP(png_ptr, info_ptr, &name, &compression, &profile,
             &length)) {
-        return SkColorSpace::MakeICC(profile, length);
+        auto data = SkData::MakeWithCopy(profile, length);
+        return SkEncodedInfo::ICCProfile::Make(std::move(data));
     }
 
     // Second, check for sRGB.
+    // Note that Blink does this first. This code checks ICC first, with the thinking that
+    // an image has both truly wants the potentially more specific ICC chunk, with sRGB as a
+    // backup in case the decoder does not support full color management.
     if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
-
         // sRGB chunks also store a rendering intent: Absolute, Relative,
         // Perceptual, and Saturation.
         // FIXME (msarett): Extract this information from the sRGB chunk once
         //                  we are able to handle this information in
         //                  SkColorSpace.
-        return SkColorSpace::MakeSRGB();
+        return SkEncodedInfo::ICCProfile::MakeSRGB();
     }
 
+    // Default to SRGB gamut.
+    skcms_Matrix3x3 toXYZD50 = skcms_sRGB_profile()->toXYZD50;
     // Next, check for chromaticities.
     png_fixed_point chrm[8];
     png_fixed_point gamma;
     if (png_get_cHRM_fixed(png_ptr, info_ptr, &chrm[0], &chrm[1], &chrm[2], &chrm[3], &chrm[4],
                            &chrm[5], &chrm[6], &chrm[7]))
     {
-        SkColorSpacePrimaries primaries;
-        primaries.fRX = png_fixed_point_to_float(chrm[2]);
-        primaries.fRY = png_fixed_point_to_float(chrm[3]);
-        primaries.fGX = png_fixed_point_to_float(chrm[4]);
-        primaries.fGY = png_fixed_point_to_float(chrm[5]);
-        primaries.fBX = png_fixed_point_to_float(chrm[6]);
-        primaries.fBY = png_fixed_point_to_float(chrm[7]);
-        primaries.fWX = png_fixed_point_to_float(chrm[0]);
-        primaries.fWY = png_fixed_point_to_float(chrm[1]);
+        float rx = png_fixed_point_to_float(chrm[2]);
+        float ry = png_fixed_point_to_float(chrm[3]);
+        float gx = png_fixed_point_to_float(chrm[4]);
+        float gy = png_fixed_point_to_float(chrm[5]);
+        float bx = png_fixed_point_to_float(chrm[6]);
+        float by = png_fixed_point_to_float(chrm[7]);
+        float wx = png_fixed_point_to_float(chrm[0]);
+        float wy = png_fixed_point_to_float(chrm[1]);
 
-        SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
-        if (!primaries.toXYZD50(&toXYZD50)) {
-            toXYZD50.set3x3RowMajorf(gSRGB_toXYZD50);
+        skcms_Matrix3x3 tmp;
+        if (skcms_PrimariesToXYZD50(rx, ry, gx, gy, bx, by, wx, wy, &tmp)) {
+            toXYZD50 = tmp;
+        } else {
+            // Note that Blink simply returns nullptr in this case. We'll fall
+            // back to srgb.
         }
+    }
 
-        if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) {
-            SkColorSpaceTransferFn fn;
-            fn.fA = 1.0f;
-            fn.fB = fn.fC = fn.fD = fn.fE = fn.fF = 0.0f;
-            fn.fG = png_inverted_fixed_point_to_float(gamma);
-
-            return SkColorSpace::MakeRGB(fn, toXYZD50);
-        }
-
+    skcms_TransferFunction fn;
+    if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) {
+        fn.a = 1.0f;
+        fn.b = fn.c = fn.d = fn.e = fn.f = 0.0f;
+        fn.g = png_inverted_fixed_point_to_float(gamma);
+    } else {
         // Default to sRGB gamma if the image has color space information,
         // but does not specify gamma.
-        return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, toXYZD50);
+        // Note that Blink would again return nullptr in this case.
+        fn = *skcms_sRGB_TransferFunction();
     }
 
-    // Last, check for gamma.
-    if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) {
-        SkColorSpaceTransferFn fn;
-        fn.fA = 1.0f;
-        fn.fB = fn.fC = fn.fD = fn.fE = fn.fF = 0.0f;
-        fn.fG = png_inverted_fixed_point_to_float(gamma);
+    skcms_ICCProfile skcmsProfile;
+    skcms_Init(&skcmsProfile);
+    skcms_SetTransferFunction(&skcmsProfile, &fn);
+    skcms_SetXYZD50(&skcmsProfile, &toXYZD50);
 
-        // Since there is no cHRM, we will guess sRGB gamut.
-        SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
-        toXYZD50.set3x3RowMajorf(gSRGB_toXYZD50);
-
-        return SkColorSpace::MakeRGB(fn, toXYZD50);
-    }
-
+    return SkEncodedInfo::ICCProfile::Make(skcmsProfile);
+#else // LIBPNG >= 1.6
+    return SkEncodedInfo::ICCProfile::MakeSRGB();
 #endif // LIBPNG >= 1.6
-
-    // Report that there is no color space information in the PNG.
-    // Guess sRGB in this case.
-    return SkColorSpace::MakeSRGB();
 }
 
 void SkPngCodec::allocateStorage(const SkImageInfo& dstInfo) {
@@ -454,17 +448,17 @@
     }
 }
 
-static SkColorSpaceXform::ColorFormat png_select_xform_format(const SkEncodedInfo& info) {
+static skcms_PixelFormat png_select_xform_format(const SkEncodedInfo& info) {
     // We use kRGB and kRGBA formats because color PNGs are always RGB or RGBA.
     if (16 == info.bitsPerComponent()) {
         if (SkEncodedInfo::kRGBA_Color == info.color()) {
-            return SkColorSpaceXform::kRGBA_U16_BE_ColorFormat;
+            return skcms_PixelFormat_RGBA_16161616;
         } else if (SkEncodedInfo::kRGB_Color == info.color()) {
-            return SkColorSpaceXform::kRGB_U16_BE_ColorFormat;
+            return skcms_PixelFormat_RGB_161616;
         }
     }
 
-    return SkColorSpaceXform::kRGBA_8888_ColorFormat;
+    return skcms_PixelFormat_RGBA_8888;
 }
 
 void SkPngCodec::applyXformRow(void* dst, const void* src) {
@@ -484,10 +478,9 @@
 
 class SkPngNormalDecoder : public SkPngCodec {
 public:
-    SkPngNormalDecoder(const SkEncodedInfo& info, const SkImageInfo& imageInfo,
-                       std::unique_ptr<SkStream> stream, SkPngChunkReader* reader,
-                       png_structp png_ptr, png_infop info_ptr, int bitDepth)
-        : INHERITED(info, imageInfo, std::move(stream), reader, png_ptr, info_ptr, bitDepth)
+    SkPngNormalDecoder(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
+            SkPngChunkReader* reader, png_structp png_ptr, png_infop info_ptr, int bitDepth)
+        : INHERITED(std::move(info), std::move(stream), reader, png_ptr, info_ptr, bitDepth)
         , fRowsWrittenToOutput(0)
         , fDst(nullptr)
         , fRowBytes(0)
@@ -607,10 +600,10 @@
 
 class SkPngInterlacedDecoder : public SkPngCodec {
 public:
-    SkPngInterlacedDecoder(const SkEncodedInfo& info, const SkImageInfo& imageInfo,
-            std::unique_ptr<SkStream> stream, SkPngChunkReader* reader, png_structp png_ptr,
+    SkPngInterlacedDecoder(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
+            SkPngChunkReader* reader, png_structp png_ptr,
             png_infop info_ptr, int bitDepth, int numberPasses)
-        : INHERITED(info, imageInfo, std::move(stream), reader, png_ptr, info_ptr, bitDepth)
+        : INHERITED(std::move(info), std::move(stream), reader, png_ptr, info_ptr, bitDepth)
         , fNumberPasses(numberPasses)
         , fFirstRow(0)
         , fLastRow(0)
@@ -918,36 +911,33 @@
 
     if (fOutCodec) {
         SkASSERT(nullptr == *fOutCodec);
-        sk_sp<SkColorSpace> colorSpace = read_color_space(fPng_ptr, fInfo_ptr);
-        if (colorSpace) {
-            switch (colorSpace->type()) {
-                case SkColorSpace::kCMYK_Type:
-                    colorSpace = nullptr;
+        auto profile = read_color_profile(fPng_ptr, fInfo_ptr);
+        if (profile) {
+            switch (profile->profile()->data_color_space) {
+                case skcms_Signature_CMYK:
+                    profile = nullptr;
                     break;
-                case SkColorSpace::kGray_Type:
+                case skcms_Signature_Gray:
                     if (SkEncodedInfo::kGray_Color != color &&
                         SkEncodedInfo::kGrayAlpha_Color != color)
                     {
-                        colorSpace = nullptr;
+                        profile = nullptr;
                     }
                     break;
-                case SkColorSpace::kRGB_Type:
+                default:
                     break;
             }
         }
-        if (!colorSpace) {
+        if (!profile) {
             // Treat unsupported/invalid color spaces as sRGB.
-            colorSpace = SkColorSpace::MakeSRGB();
+            profile = SkEncodedInfo::ICCProfile::MakeSRGB();
         }
 
-        SkEncodedInfo encodedInfo = SkEncodedInfo::Make(color, alpha, bitDepth);
-        SkImageInfo imageInfo = encodedInfo.makeImageInfo(origWidth, origHeight, colorSpace);
-
         if (encodedColorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
             png_color_8p sigBits;
             if (png_get_sBIT(fPng_ptr, fInfo_ptr, &sigBits)) {
                 if (8 == sigBits->alpha && kGraySigBit_GrayAlphaIsJustAlpha == sigBits->gray) {
-                    imageInfo = imageInfo.makeColorType(kAlpha_8_SkColorType);
+                    color = SkEncodedInfo::kXAlpha_Color;
                 }
             }
         } else if (SkEncodedInfo::kOpaque_Alpha == alpha) {
@@ -955,16 +945,18 @@
             if (png_get_sBIT(fPng_ptr, fInfo_ptr, &sigBits)) {
                 if (5 == sigBits->red && 6 == sigBits->green && 5 == sigBits->blue) {
                     // Recommend a decode to 565 if the sBIT indicates 565.
-                    imageInfo = imageInfo.makeColorType(kRGB_565_SkColorType);
+                    color = SkEncodedInfo::k565_Color;
                 }
             }
         }
 
+        SkEncodedInfo encodedInfo = SkEncodedInfo::Make(origWidth, origHeight, color, alpha,
+                                                        bitDepth, std::move(profile));
         if (1 == numberPasses) {
-            *fOutCodec = new SkPngNormalDecoder(encodedInfo, imageInfo,
+            *fOutCodec = new SkPngNormalDecoder(std::move(encodedInfo),
                    std::unique_ptr<SkStream>(fStream), fChunkReader, fPng_ptr, fInfo_ptr, bitDepth);
         } else {
-            *fOutCodec = new SkPngInterlacedDecoder(encodedInfo, imageInfo,
+            *fOutCodec = new SkPngInterlacedDecoder(std::move(encodedInfo),
                     std::unique_ptr<SkStream>(fStream), fChunkReader, fPng_ptr, fInfo_ptr, bitDepth,
                     numberPasses);
         }
@@ -976,10 +968,9 @@
     this->releasePngPtrs();
 }
 
-SkPngCodec::SkPngCodec(const SkEncodedInfo& encodedInfo, const SkImageInfo& imageInfo,
-                       std::unique_ptr<SkStream> stream, SkPngChunkReader* chunkReader,
-                       void* png_ptr, void* info_ptr, int bitDepth)
-    : INHERITED(encodedInfo, imageInfo, png_select_xform_format(encodedInfo), std::move(stream))
+SkPngCodec::SkPngCodec(SkEncodedInfo&& encodedInfo, std::unique_ptr<SkStream> stream,
+                       SkPngChunkReader* chunkReader, void* png_ptr, void* info_ptr, int bitDepth)
+    : INHERITED(std::move(encodedInfo), png_select_xform_format(encodedInfo), std::move(stream))
     , fPngChunkReader(SkSafeRef(chunkReader))
     , fPng_ptr(png_ptr)
     , fInfo_ptr(info_ptr)
diff --git a/src/codec/SkPngCodec.h b/src/codec/SkPngCodec.h
index 075181c..bf068ea 100644
--- a/src/codec/SkPngCodec.h
+++ b/src/codec/SkPngCodec.h
@@ -8,7 +8,6 @@
 #define SkPngCodec_DEFINED
 
 #include "SkCodec.h"
-#include "SkColorSpaceXform.h"
 #include "SkColorTable.h"
 #include "SkPngChunkReader.h"
 #include "SkEncodedImageFormat.h"
@@ -45,8 +44,8 @@
         void* fPtr;
     };
 
-    SkPngCodec(const SkEncodedInfo&, const SkImageInfo&, std::unique_ptr<SkStream>,
-               SkPngChunkReader*, void* png_ptr, void* info_ptr, int bitDepth);
+    SkPngCodec(SkEncodedInfo&&, std::unique_ptr<SkStream>, SkPngChunkReader*,
+               void* png_ptr, void* info_ptr, int bitDepth);
 
     Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, int*)
             override;
diff --git a/src/codec/SkRawCodec.cpp b/src/codec/SkRawCodec.cpp
index 9eea78c..30a3d7b 100644
--- a/src/codec/SkRawCodec.cpp
+++ b/src/codec/SkRawCodec.cpp
@@ -504,10 +504,6 @@
         }
     }
 
-    const SkEncodedInfo& getEncodedInfo() const {
-        return fEncodedInfo;
-    }
-
     int width() const {
         return fWidth;
     }
@@ -602,8 +598,6 @@
 
     SkDngImage(SkRawStream* stream)
         : fStream(stream)
-        , fEncodedInfo(SkEncodedInfo::Make(SkEncodedInfo::kRGB_Color,
-                                           SkEncodedInfo::kOpaque_Alpha, 8))
     {}
 
     dng_memory_allocator fAllocator;
@@ -615,11 +609,21 @@
 
     int fWidth;
     int fHeight;
-    SkEncodedInfo fEncodedInfo;
     bool fIsScalable;
     bool fIsXtransImage;
 };
 
+static constexpr skcms_Matrix3x3 gAdobe_RGB_to_XYZD50 = {{
+    // ICC fixed-point (16.16) repesentation of:
+    // 0.60974, 0.20528, 0.14919,
+    // 0.31111, 0.62567, 0.06322,
+    // 0.01947, 0.06087, 0.74457,
+    { SkFixedToFloat(0x9c18), SkFixedToFloat(0x348d), SkFixedToFloat(0x2631) }, // Rx, Gx, Bx
+    { SkFixedToFloat(0x4fa5), SkFixedToFloat(0xa02c), SkFixedToFloat(0x102f) }, // Ry, Gy, By
+    { SkFixedToFloat(0x04fc), SkFixedToFloat(0x0f95), SkFixedToFloat(0xbe9c) }, // Rz, Gz, Bz
+}};
+
+
 /*
  * Tries to handle the image with PIEX. If PIEX returns kOk and finds the preview image, create a
  * SkJpegCodec. If PIEX returns kFail, then the file is invalid, return nullptr. In other cases,
@@ -644,15 +648,21 @@
             return nullptr;
         }
 
-        sk_sp<SkColorSpace> colorSpace;
+        std::unique_ptr<SkEncodedInfo::ICCProfile> profile;
         switch (imageData.color_space) {
             case ::piex::PreviewImageData::kSrgb:
-                colorSpace = SkColorSpace::MakeSRGB();
+                profile = SkEncodedInfo::ICCProfile::MakeSRGB();
                 break;
-            case ::piex::PreviewImageData::kAdobeRgb:
-                colorSpace = SkColorSpace::MakeRGB(g2Dot2_TransferFn,
-                                                   SkColorSpace::kAdobeRGB_Gamut);
+            case ::piex::PreviewImageData::kAdobeRgb: {
+                constexpr skcms_TransferFunction twoDotTwo =
+                        { 2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+                skcms_ICCProfile skcmsProfile;
+                skcms_Init(&skcmsProfile);
+                skcms_SetTransferFunction(&skcmsProfile, &twoDotTwo);
+                skcms_SetXYZD50(&skcmsProfile, &gAdobe_RGB_to_XYZD50);
+                profile = SkEncodedInfo::ICCProfile::Make(skcmsProfile);
                 break;
+            }
         }
 
         //  Theoretically PIEX can return JPEG compressed image or uncompressed RGB image. We only
@@ -670,7 +680,7 @@
                 return nullptr;
             }
             return SkJpegCodec::MakeFromStream(std::move(memoryStream), result,
-                                               std::move(colorSpace));
+                                               std::move(profile));
         }
     }
 
@@ -746,7 +756,7 @@
         if (this->colorXform()) {
             swizzler->swizzle(xformBuffer.get(), &srcRow[0]);
 
-            this->applyColorXform(dstRow, xformBuffer.get(), dstInfo.width(), kOpaque_SkAlphaType);
+            this->applyColorXform(dstRow, xformBuffer.get(), dstInfo.width());
         } else {
             swizzler->swizzle(dstRow, &srcRow[0]);
         }
@@ -796,7 +806,8 @@
 SkRawCodec::~SkRawCodec() {}
 
 SkRawCodec::SkRawCodec(SkDngImage* dngImage)
-    : INHERITED(dngImage->width(), dngImage->height(), dngImage->getEncodedInfo(),
-                SkColorSpaceXform::kRGBA_8888_ColorFormat, nullptr,
-                SkColorSpace::MakeSRGB())
+    : INHERITED(SkEncodedInfo::MakeSRGB(dngImage->width(), dngImage->height(),
+                                        SkEncodedInfo::kRGB_Color,
+                                        SkEncodedInfo::kOpaque_Alpha, 8),
+                skcms_PixelFormat_RGBA_8888, nullptr)
     , fDngImage(dngImage) {}
diff --git a/src/codec/SkSwizzler.cpp b/src/codec/SkSwizzler.cpp
index 4b350c4..cca8c41 100644
--- a/src/codec/SkSwizzler.cpp
+++ b/src/codec/SkSwizzler.cpp
@@ -890,6 +890,7 @@
                         return nullptr;
                 }
                 break;
+            case SkEncodedInfo::kXAlpha_Color:
             case SkEncodedInfo::kGrayAlpha_Color:
                 switch (dstInfo.colorType()) {
                     case kRGBA_8888_SkColorType:
@@ -963,6 +964,10 @@
                         return nullptr;
                 }
                 break;
+            case SkEncodedInfo::k565_Color:
+                // Treat 565 exactly like RGB (since it's still encoded as 8 bits per component).
+                // We just mark as 565 when we have a hint that there are only 5/6/5 "significant"
+                // bits in each channel.
             case SkEncodedInfo::kRGB_Color:
                 switch (dstInfo.colorType()) {
                     case kRGBA_8888_SkColorType:
diff --git a/src/codec/SkWbmpCodec.cpp b/src/codec/SkWbmpCodec.cpp
index 8d4beb1..cc1462e 100644
--- a/src/codec/SkWbmpCodec.cpp
+++ b/src/codec/SkWbmpCodec.cpp
@@ -97,11 +97,10 @@
     return this->stream()->read(row, fSrcRowBytes) == fSrcRowBytes;
 }
 
-SkWbmpCodec::SkWbmpCodec(int width, int height, const SkEncodedInfo& info,
-                         std::unique_ptr<SkStream> stream)
+SkWbmpCodec::SkWbmpCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream)
     // Wbmp does not need a colorXform, so choose an arbitrary srcFormat.
-    : INHERITED(width, height, info, SkColorSpaceXform::ColorFormat(),
-                std::move(stream), SkColorSpace::MakeSRGB())
+    : INHERITED(std::move(info), skcms_PixelFormat(),
+                std::move(stream))
     , fSrcRowBytes(get_src_row_bytes(this->getInfo().width()))
     , fSwizzler(nullptr)
 {}
@@ -111,7 +110,7 @@
 }
 
 bool SkWbmpCodec::conversionSupported(const SkImageInfo& dst, SkColorType /*srcColor*/,
-                                      bool srcIsOpaque, const SkColorSpace* srcCS) const {
+                                      bool srcIsOpaque, bool /*needsXform*/) {
     return valid_color_type(dst) && valid_alpha(dst.alphaType(), srcIsOpaque);
 }
 
@@ -159,10 +158,9 @@
         return nullptr;
     }
     *result = kSuccess;
-    SkEncodedInfo info = SkEncodedInfo::Make(SkEncodedInfo::kGray_Color,
-            SkEncodedInfo::kOpaque_Alpha, 1);
-    return std::unique_ptr<SkCodec>(new SkWbmpCodec(size.width(), size.height(), info,
-                                                    std::move(stream)));
+    auto info = SkEncodedInfo::Make(size.width(), size.height(), SkEncodedInfo::kGray_Color,
+                                    SkEncodedInfo::kOpaque_Alpha, 1);
+    return std::unique_ptr<SkCodec>(new SkWbmpCodec(std::move(info), std::move(stream)));
 }
 
 int SkWbmpCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) {
diff --git a/src/codec/SkWbmpCodec.h b/src/codec/SkWbmpCodec.h
index 192189d..b9df0b9 100644
--- a/src/codec/SkWbmpCodec.h
+++ b/src/codec/SkWbmpCodec.h
@@ -29,7 +29,7 @@
                        const Options&, int*) override;
     bool onRewind() override;
     bool conversionSupported(const SkImageInfo& dst, SkColorType srcColor,
-                             bool srcIsOpaque, const SkColorSpace* srcCS) const override;
+                             bool srcIsOpaque, bool needsXform) override;
     // No need to Xform; all pixels are either black or white.
     bool usesColorXform() const override { return false; }
 private:
@@ -48,7 +48,7 @@
      */
     bool readRow(uint8_t* row);
 
-    SkWbmpCodec(int width, int height, const SkEncodedInfo&, std::unique_ptr<SkStream>);
+    SkWbmpCodec(SkEncodedInfo&&, std::unique_ptr<SkStream>);
 
     const size_t                fSrcRowBytes;
 
diff --git a/src/codec/SkWebpCodec.cpp b/src/codec/SkWebpCodec.cpp
index a9c0c32..ac1c9be 100644
--- a/src/codec/SkWebpCodec.cpp
+++ b/src/codec/SkWebpCodec.cpp
@@ -13,7 +13,6 @@
 #include "SkCodecAnimation.h"
 #include "SkCodecAnimationPriv.h"
 #include "SkCodecPriv.h"
-#include "SkColorSpaceXform.h"
 #include "SkMakeUnique.h"
 #include "SkRasterPipeline.h"
 #include "SkSampler.h"
@@ -89,15 +88,17 @@
         }
     }
 
-    sk_sp<SkColorSpace> colorSpace = nullptr;
+    std::unique_ptr<SkEncodedInfo::ICCProfile> profile = nullptr;
     {
         WebPChunkIterator chunkIterator;
         SkAutoTCallVProc<WebPChunkIterator, WebPDemuxReleaseChunkIterator> autoCI(&chunkIterator);
         if (WebPDemuxGetChunk(demux, "ICCP", 1, &chunkIterator)) {
-            colorSpace = SkColorSpace::MakeICC(chunkIterator.chunk.bytes, chunkIterator.chunk.size);
+            // FIXME: I think this could be MakeWithoutCopy
+            auto chunk = SkData::MakeWithCopy(chunkIterator.chunk.bytes, chunkIterator.chunk.size);
+            profile = SkEncodedInfo::ICCProfile::Make(std::move(chunk));
         }
-        if (!colorSpace || colorSpace->type() != SkColorSpace::kRGB_Type) {
-            colorSpace = SkColorSpace::MakeSRGB();
+        if (!profile || profile->profile()->data_color_space != skcms_Signature_RGB) {
+            profile = SkEncodedInfo::ICCProfile::MakeSRGB();
         }
     }
 
@@ -171,10 +172,9 @@
 
 
     *result = kSuccess;
-    SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8);
-    return std::unique_ptr<SkCodec>(new SkWebpCodec(width, height, info, std::move(colorSpace),
-                                           std::move(stream), demux.release(), std::move(data),
-                                           origin));
+    SkEncodedInfo info = SkEncodedInfo::Make(width, height, color, alpha, 8, std::move(profile));
+    return std::unique_ptr<SkCodec>(new SkWebpCodec(std::move(info), std::move(stream),
+                                                    demux.release(), std::move(data), origin));
 }
 
 SkISize SkWebpCodec::onGetScaledDimensions(float desiredScale) const {
@@ -543,35 +543,8 @@
         webpDst.installPixels(webpInfo, dst, rowBytes);
     }
 
-    // Choose the step when we will perform premultiplication.
-    enum {
-        kNoPremul,
-        kBlendLine,
-        kColorXform,
-        kLibwebp,
-    };
-    auto choose_premul_step = [&]() {
-        if (!frame.has_alpha) {
-            // None necessary.
-            return kNoPremul;
-        }
-        if (blendWithPrevFrame) {
-            // Premultiply in blend_line, in a linear space.
-            return kBlendLine;
-        }
-        if (dstInfo.alphaType() != kPremul_SkAlphaType) {
-            // No blending is necessary, so we only need to premultiply if the
-            // client requested it.
-            return kNoPremul;
-        }
-        if (this->colorXform()) {
-            // Premultiply in the colorXform, in a linear space.
-            return kColorXform;
-        }
-        return kLibwebp;
-    };
-    const auto premulStep = choose_premul_step();
-    config.output.colorspace = webp_decode_mode(webpInfo.colorType(), premulStep == kLibwebp);
+    config.output.colorspace = webp_decode_mode(webpInfo.colorType(),
+            frame.has_alpha && dstInfo.alphaType() == kPremul_SkAlphaType && !this->colorXform());
     config.output.is_external_memory = 1;
 
     config.output.u.RGBA.rgba = reinterpret_cast<uint8_t*>(webpDst.getAddr(dstX, dstY));
@@ -620,11 +593,8 @@
             xformDst = dst;
         }
 
-        const auto xformAlphaType = (premulStep == kColorXform) ? kPremul_SkAlphaType   :
-                                    (          frame.has_alpha) ? kUnpremul_SkAlphaType :
-                                                                  kOpaque_SkAlphaType   ;
         for (int y = 0; y < rowsDecoded; y++) {
-            this->applyColorXform(xformDst, xformSrc, scaledWidth, xformAlphaType);
+            this->applyColorXform(xformDst, xformSrc, scaledWidth);
             if (blendWithPrevFrame) {
                 blend_line(dstCT, dst, dstCT, xformDst,
                         dstInfo.alphaType(), frame.has_alpha, scaledWidth);
@@ -648,14 +618,14 @@
     return result;
 }
 
-SkWebpCodec::SkWebpCodec(int width, int height, const SkEncodedInfo& info,
-                         sk_sp<SkColorSpace> colorSpace, std::unique_ptr<SkStream> stream,
+SkWebpCodec::SkWebpCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
                          WebPDemuxer* demux, sk_sp<SkData> data, SkEncodedOrigin origin)
-    : INHERITED(width, height, info, SkColorSpaceXform::kBGRA_8888_ColorFormat, std::move(stream),
-                std::move(colorSpace), origin)
+    : INHERITED(std::move(info), skcms_PixelFormat_BGRA_8888, std::move(stream),
+                origin)
     , fDemux(demux)
     , fData(std::move(data))
     , fFailed(false)
 {
-    fFrameHolder.setScreenSize(width, height);
+    const auto& eInfo = this->getEncodedInfo();
+    fFrameHolder.setScreenSize(eInfo.width(), eInfo.height());
 }
diff --git a/src/codec/SkWebpCodec.h b/src/codec/SkWebpCodec.h
index fdd5422..4de5f38 100644
--- a/src/codec/SkWebpCodec.h
+++ b/src/codec/SkWebpCodec.h
@@ -9,7 +9,6 @@
 #define SkWebpCodec_DEFINED
 
 #include "SkCodec.h"
-#include "SkColorSpace.h"
 #include "SkEncodedImageFormat.h"
 #include "SkFrameHolder.h"
 #include "SkImageInfo.h"
@@ -47,8 +46,8 @@
     }
 
 private:
-    SkWebpCodec(int width, int height, const SkEncodedInfo&, sk_sp<SkColorSpace>,
-                std::unique_ptr<SkStream>, WebPDemuxer*, sk_sp<SkData>, SkEncodedOrigin);
+    SkWebpCodec(SkEncodedInfo&&, std::unique_ptr<SkStream>, WebPDemuxer*, sk_sp<SkData>,
+                SkEncodedOrigin);
 
     SkAutoTCallVProc<WebPDemuxer, WebPDemuxDelete> fDemux;
 
diff --git a/tests/CodecTest.cpp b/tests/CodecTest.cpp
index 9bbadc6..7f50a41 100644
--- a/tests/CodecTest.cpp
+++ b/tests/CodecTest.cpp
@@ -1608,6 +1608,25 @@
     }
 }
 
+DEF_TEST(Codec_A8, r) {
+    if (GetResourcePath().isEmpty()) {
+        return;
+    }
+
+    const char* file = "images/mandrill_cmyk.jpg";
+    auto data = GetResourceAsData(file);
+    if (!data) {
+        ERRORF(r, "missing %s", file);
+        return;
+    }
+
+    auto codec = SkCodec::MakeFromData(std::move(data));
+    auto info = codec->getInfo().makeColorType(kAlpha_8_SkColorType);
+    SkBitmap bm;
+    bm.allocPixels(info);
+    REPORTER_ASSERT(r, codec->getPixels(bm.pixmap()) == SkCodec::kInvalidConversion);
+}
+
 DEF_TEST(Codec_crbug807324, r) {
     if (GetResourcePath().isEmpty()) {
         return;
diff --git a/tests/EncodedInfoTest.cpp b/tests/EncodedInfoTest.cpp
new file mode 100644
index 0000000..3149df8
--- /dev/null
+++ b/tests/EncodedInfoTest.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Resources.h"
+#include "Test.h"
+#include "sk_tool_utils.h"
+
+#include "SkBitmap.h"
+#include "SkCodec.h"
+#include "SkData.h"
+#include "SkEncodedImageFormat.h"
+#include "SkImageInfo.h"
+#include "SkImageEncoder.h"
+
+DEF_TEST(AlphaEncodedInfo, r) {
+    auto codec = SkCodec::MakeFromStream(GetResourceAsStream("images/grayscale.jpg"));
+    REPORTER_ASSERT(r, codec->getInfo().colorType() == kGray_8_SkColorType);
+
+    SkBitmap bm;
+    bm.allocPixels(codec->getInfo().makeColorType(kAlpha_8_SkColorType).makeColorSpace(nullptr));
+    auto result = codec->getPixels(codec->getInfo(), bm.getPixels(), bm.rowBytes());
+    REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+
+    auto data = SkEncodeBitmap(bm, SkEncodedImageFormat::kPNG, 100);
+    REPORTER_ASSERT(r, data);
+
+    codec = SkCodec::MakeFromData(std::move(data));
+    REPORTER_ASSERT(r, codec);
+    // TODO: Make SkEncodedInfo public and compare to its version of kAlpha_8.
+    REPORTER_ASSERT(r, codec->getInfo().colorType() == kAlpha_8_SkColorType);
+
+    SkBitmap bm2;
+    bm2.allocPixels(codec->getInfo().makeColorSpace(nullptr));
+    result = codec->getPixels(bm2.pixmap());
+    REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+
+    REPORTER_ASSERT(r, sk_tool_utils::equal_pixels(bm.pixmap(), bm2.pixmap(), 0, true));
+}