Revert "Switch SkCodec to use skcms" and follow on change

This reverts commit 81886e8f9461c7049751c6a2c194a1df632dd362 and
f8ae5ce20cf6df2dab4149fb2838d9d8dc6bd1af
("Fix CMYK handling in JPEG codec")

This fixes the Android build, which was failing a CTS test with this
change.

Bug: skia:6839
Bug: skia:8052

TBR=djsollen@google.com
As with the original, no API change

Change-Id: Ic744a610e9f431707f871de44f9f64040bc60d14
Reviewed-on: https://skia-review.googlesource.com/148810
Reviewed-by: Leon Scroggins <scroggo@google.com>
Commit-Queue: Leon Scroggins <scroggo@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 3369a9a..9019cea 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -863,7 +863,6 @@
     "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",
diff --git a/gn/tests.gni b/gn/tests.gni
index e829c70..9731514 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -64,7 +64,6 @@
   "$_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 2e263ea..ecb762e 100644
--- a/include/codec/SkCodec.h
+++ b/include/codec/SkCodec.h
@@ -13,6 +13,7 @@
 #include "../private/SkEncodedInfo.h"
 #include "SkCodecAnimation.h"
 #include "SkColor.h"
+#include "SkColorSpaceXform.h"
 #include "SkEncodedImageFormat.h"
 #include "SkEncodedOrigin.h"
 #include "SkImageInfo.h"
@@ -670,9 +671,21 @@
 protected:
     const SkEncodedInfo& getEncodedInfo() const { return fEncodedInfo; }
 
-    using XformFormat = skcms_PixelFormat;
+    using XformFormat = SkColorSpaceXform::ColorFormat;
 
-    SkCodec(SkEncodedInfo&&,
+    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&,
             XformFormat srcFormat,
             std::unique_ptr<SkStream>,
             SkEncodedOrigin = kTopLeft_SkEncodedOrigin);
@@ -767,14 +780,16 @@
 
     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;
 
-    bool colorXform() const { return fXformTime != kNo_XformTime; }
-    bool xformOnDecode() const { return fXformTime == kDecodeRow_XformTime; }
+    SkColorSpaceXform* colorXform() const { return fColorXform.get(); }
+    bool xformOnDecode() const { return fXformOnDecode; }
 
     virtual int onGetFrameCount() {
         return 1;
@@ -798,16 +813,9 @@
 
     SkImageInfo                        fDstInfo;
     Options                            fOptions;
-
-    enum XformTime {
-        kNo_XformTime,
-        kPalette_XformTime,
-        kDecodeRow_XformTime,
-    };
-    XformTime                          fXformTime;
     XformFormat                        fDstXformFormat; // Based on fDstInfo.
-    skcms_ICCProfile                   fDstProfile;
-    skcms_AlphaFormat                  fDstXformAlphaFormat;
+    std::unique_ptr<SkColorSpaceXform> fColorXform;
+    bool                               fXformOnDecode;
 
     // Only meaningful during scanline decodes.
     int                                fCurrScanline;
@@ -815,15 +823,12 @@
     bool                               fStartedIncrementalDecode;
 
     /**
-     *  Return whether we can convert to dst.
+     *  Return whether {srcColor, srcIsOpaque, srcCS} 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, bool needsColorXform);
-
-    bool initializeColorXform(const SkImageInfo& dstInfo, SkEncodedInfo::Alpha, bool srcIsOpaque);
-
+                                     bool srcIsOpaque, const SkColorSpace* srcCS) const;
     /**
      *  Return whether these dimensions are supported as a scale.
      *
diff --git a/include/private/SkEncodedInfo.h b/include/private/SkEncodedInfo.h
index 5d6dabe..217b859 100644
--- a/include/private/SkEncodedInfo.h
+++ b/include/private/SkEncodedInfo.h
@@ -8,25 +8,12 @@
 #ifndef SkEncodedInfo_DEFINED
 #define SkEncodedInfo_DEFINED
 
-#include "SkData.h"
 #include "SkImageInfo.h"
-#include "../../third_party/skcms/skcms.h"
+
+class SkColorSpace;
 
 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,
@@ -52,20 +39,6 @@
         // 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,
 
@@ -94,18 +67,7 @@
         kYCCK_Color,
     };
 
-    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) {
+    static SkEncodedInfo Make(Color color, Alpha alpha, int bitsPerComponent) {
         SkASSERT(1 == bitsPerComponent ||
                  2 == bitsPerComponent ||
                  4 == bitsPerComponent ||
@@ -143,51 +105,29 @@
                 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(width, height, color, alpha, bitsPerComponent, std::move(profile));
+        return SkEncodedInfo(color, alpha, bitsPerComponent);
     }
 
     /*
-     * Returns a recommended SkImageInfo.
-     *
-     * TODO: Leave this up to the client.
+     * Returns an SkImageInfo with Skia color and alpha types that are the
+     * closest possible match to the encoded info.
      */
-    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      ;
+    SkImageInfo makeImageInfo(int width, int height, sk_sp<SkColorSpace> colorSpace) const {
+        auto ct = kGray_Color == fColor ? kGray_8_SkColorType   :
+                                          kN32_SkColorType      ;
         auto alpha = kOpaque_Alpha == fAlpha ? kOpaque_SkAlphaType
                                              : kUnpremul_SkAlphaType;
-        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));
+        return SkImageInfo::Make(width, height, ct, alpha, std::move(colorSpace));
     }
 
-    int   width() const { return fWidth;  }
-    int  height() const { return fHeight; }
-    Color color() const { return fColor;  }
-    Alpha alpha() const { return fAlpha;  }
+    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; }
 
@@ -195,7 +135,6 @@
         switch (fColor) {
             case kGray_Color:
                 return fBitsPerComponent;
-            case kXAlpha_Color:
             case kGrayAlpha_Color:
                 return 2 * fBitsPerComponent;
             case kPalette_Color:
@@ -203,7 +142,6 @@
             case kRGB_Color:
             case kBGR_Color:
             case kYUV_Color:
-            case k565_Color:
                 return 3 * fBitsPerComponent;
             case kRGBA_Color:
             case kBGRA_Color:
@@ -218,38 +156,17 @@
         }
     }
 
-    SkEncodedInfo(const SkEncodedInfo& orig) = delete;
-    SkEncodedInfo& operator=(const SkEncodedInfo&) = delete;
-
-    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)
+
+    SkEncodedInfo(Color color, Alpha alpha, uint8_t bitsPerComponent)
+        : fColor(color)
         , fAlpha(alpha)
         , fBitsPerComponent(bitsPerComponent)
-        , fProfile(std::move(profile))
     {}
 
-    int                         fWidth;
-    int                         fHeight;
-    Color                       fColor;
-    Alpha                       fAlpha;
-    uint8_t                     fBitsPerComponent;
-    std::unique_ptr<ICCProfile> fProfile;
+    Color   fColor;
+    Alpha   fAlpha;
+    uint8_t fBitsPerComponent;
 };
 
 #endif
diff --git a/resources/images/mandrill_cmyk.jpg b/resources/images/mandrill_cmyk.jpg
deleted file mode 100644
index 0a7f29b..0000000
--- a/resources/images/mandrill_cmyk.jpg
+++ /dev/null
Binary files differ
diff --git a/src/codec/SkBmpBaseCodec.cpp b/src/codec/SkBmpBaseCodec.cpp
index 2b0ed1f..c548514 100644
--- a/src/codec/SkBmpBaseCodec.cpp
+++ b/src/codec/SkBmpBaseCodec.cpp
@@ -9,8 +9,9 @@
 
 SkBmpBaseCodec::~SkBmpBaseCodec() {}
 
-SkBmpBaseCodec::SkBmpBaseCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
+SkBmpBaseCodec::SkBmpBaseCodec(int width, int height, const SkEncodedInfo& info,
+                               std::unique_ptr<SkStream> stream,
                                uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder)
-    : INHERITED(std::move(info), std::move(stream), bitsPerPixel, rowOrder)
+    : INHERITED(width, height, 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 8a076a5..24277fc 100644
--- a/src/codec/SkBmpBaseCodec.h
+++ b/src/codec/SkBmpBaseCodec.h
@@ -25,7 +25,7 @@
     bool didCreateSrcBuffer() const { return fSrcBuffer != nullptr; }
 
 protected:
-    SkBmpBaseCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream>,
+    SkBmpBaseCodec(int width, int height, const 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 02d13dd..7dd49a5 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.
-                auto info = SkEncodedInfo::MakeSRGB(width, height, color, alpha, bitsPerComponent);
-                codecOut->reset(new SkBmpStandardCodec(std::move(info),
+                const SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, bitsPerComponent);
+                codecOut->reset(new SkBmpStandardCodec(width, height, 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;
                 }
-                auto info = SkEncodedInfo::MakeSRGB(width, height, color, alpha, 8);
-                codecOut->reset(new SkBmpMaskCodec(std::move(info),
+                const SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8);
+                codecOut->reset(new SkBmpMaskCodec(width, height, 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.
-                auto info = SkEncodedInfo::MakeSRGB(width, height, SkEncodedInfo::kBGRA_Color,
-                                                SkEncodedInfo::kBinary_Alpha, 8);
-                codecOut->reset(new SkBmpRLECodec(std::move(info),
+                const SkEncodedInfo info = SkEncodedInfo::Make(SkEncodedInfo::kBGRA_Color,
+                        SkEncodedInfo::kBinary_Alpha, 8);
+                codecOut->reset(new SkBmpRLECodec(width, height, info,
                                                   std::unique_ptr<SkStream>(stream), bitsPerPixel,
                                                   numColors, bytesPerColor, offset - bytesRead,
                                                   rowOrder));
@@ -600,12 +600,14 @@
     return kSuccess == *result ? std::move(codec) : nullptr;
 }
 
-SkBmpCodec::SkBmpCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
+SkBmpCodec::SkBmpCodec(int width, int height, const SkEncodedInfo& info,
+                       std::unique_ptr<SkStream> stream,
         uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder)
-    : INHERITED(std::move(info), kXformSrcColorFormat, std::move(stream))
+    : INHERITED(width, height, info, kXformSrcColorFormat, std::move(stream),
+                SkColorSpace::MakeSRGB())
     , fBitsPerPixel(bitsPerPixel)
     , fRowOrder(rowOrder)
-    , fSrcRowBytes(SkAlign4(compute_row_bytes(this->getEncodedInfo().width(), fBitsPerPixel)))
+    , fSrcRowBytes(SkAlign4(compute_row_bytes(width, fBitsPerPixel)))
     , fXformBuffer(nullptr)
 {}
 
diff --git a/src/codec/SkBmpCodec.h b/src/codec/SkBmpCodec.h
index eff1891..3196ae1 100644
--- a/src/codec/SkBmpCodec.h
+++ b/src/codec/SkBmpCodec.h
@@ -38,7 +38,7 @@
 
 protected:
 
-    SkBmpCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream>,
+    SkBmpCodec(int width, int height, const 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 = skcms_PixelFormat_BGRA_8888;
+    static constexpr auto kXformSrcColorFormat = SkColorSpaceXform::kBGRA_8888_ColorFormat;
 
 private:
 
diff --git a/src/codec/SkBmpMaskCodec.cpp b/src/codec/SkBmpMaskCodec.cpp
index 0cb0924..ddcc47b 100644
--- a/src/codec/SkBmpMaskCodec.cpp
+++ b/src/codec/SkBmpMaskCodec.cpp
@@ -12,11 +12,11 @@
 /*
  * Creates an instance of the decoder
  */
-SkBmpMaskCodec::SkBmpMaskCodec(SkEncodedInfo&& info,
+SkBmpMaskCodec::SkBmpMaskCodec(int width, int height, const SkEncodedInfo& info,
                                std::unique_ptr<SkStream> stream,
                                uint16_t bitsPerPixel, SkMasks* masks,
                                SkCodec::SkScanlineOrder rowOrder)
-    : INHERITED(std::move(info), std::move(stream), bitsPerPixel, rowOrder)
+    : INHERITED(width, height, info, std::move(stream), bitsPerPixel, rowOrder)
     , fMasks(masks)
     , fMaskSwizzler(nullptr)
 {}
diff --git a/src/codec/SkBmpMaskCodec.h b/src/codec/SkBmpMaskCodec.h
index 370cddb..4b0dbde 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(SkEncodedInfo&& info, std::unique_ptr<SkStream>,
+    SkBmpMaskCodec(int width, int height, const 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 8a7fb9e8..3fe7a03 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(SkEncodedInfo&& info,
+SkBmpRLECodec::SkBmpRLECodec(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)
-    : INHERITED(std::move(info), std::move(stream), bitsPerPixel, rowOrder)
+    : INHERITED(width, height, 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 dc6f26d..70e97a7 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(SkEncodedInfo&& info, std::unique_ptr<SkStream>,
+    SkBmpRLECodec(int width, int height, const 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 dd71535..153d081 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(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
-                                       uint16_t bitsPerPixel, uint32_t numColors,
-                                       uint32_t bytesPerColor, uint32_t offset,
+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,
                                        SkCodec::SkScanlineOrder rowOrder,
                                        bool isOpaque, bool inIco)
-    : INHERITED(std::move(info), std::move(stream), bitsPerPixel, rowOrder)
+    : INHERITED(width, height, info, std::move(stream), bitsPerPixel, rowOrder)
     , fColorTable(nullptr)
     , fNumColors(numColors)
     , fBytesPerColor(bytesPerColor)
@@ -146,33 +146,20 @@
     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->swizzlerInfo();
+    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);
+        }
+    }
 
     // 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 84a1299..1790692 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(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
-                       uint16_t bitsPerPixel, uint32_t numColors, uint32_t bytesPerColor,
-                       uint32_t offset, SkCodec::SkScanlineOrder rowOrder,
+    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,
                        bool isOpaque, bool inIco);
 
 protected:
@@ -63,8 +63,12 @@
     }
 
 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 d12646a..5564e89 100644
--- a/src/codec/SkCodec.cpp
+++ b/src/codec/SkCodec.cpp
@@ -9,6 +9,7 @@
 #include "SkCodec.h"
 #include "SkCodecPriv.h"
 #include "SkColorSpace.h"
+#include "SkColorSpaceXformPriv.h"
 #include "SkData.h"
 #include "SkFrameHolder.h"
 #include "SkGifCodec.h"
@@ -125,10 +126,26 @@
     return MakeFromStream(SkMemoryStream::Make(std::move(data)), nullptr, reader);
 }
 
-SkCodec::SkCodec(SkEncodedInfo&& info, XformFormat srcFormat, std::unique_ptr<SkStream> stream,
-                 SkEncodedOrigin origin)
-    : fEncodedInfo(std::move(info))
-    , fSrcInfo(fEncodedInfo.makeImageInfo())
+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)
     , fSrcXformFormat(srcFormat)
     , fStream(std::move(stream))
     , fNeedsRewind(false)
@@ -142,7 +159,7 @@
 SkCodec::~SkCodec() {}
 
 bool SkCodec::conversionSupported(const SkImageInfo& dst, SkColorType srcColor,
-                                  bool srcIsOpaque, bool needsColorXform) {
+                                  bool srcIsOpaque, const SkColorSpace* srcCS) const {
     if (!valid_alpha(dst.alphaType(), srcIsOpaque)) {
         return false;
     }
@@ -156,8 +173,8 @@
         case kRGB_565_SkColorType:
             return srcIsOpaque;
         case kGray_8_SkColorType:
-            SkASSERT(!needsColorXform);
-            return kGray_8_SkColorType == srcColor && srcIsOpaque;
+            return kGray_8_SkColorType == srcColor && srcIsOpaque &&
+                   !needs_color_xform(dst, srcCS);
         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.
@@ -165,6 +182,7 @@
         default:
             return false;
     }
+
 }
 
 bool SkCodec::rewindIfNeeded() {
@@ -227,8 +245,13 @@
                                           const Options& options) {
     const int index = options.fFrameIndex;
     if (0 == index) {
-        return this->initializeColorXform(info, fEncodedInfo.alpha(), fEncodedInfo.opaque())
-            ? kSuccess : kInvalidConversion;
+        if (!this->conversionSupported(info, fSrcInfo.colorType(), fEncodedInfo.opaque(),
+                                      fSrcInfo.colorSpace())
+            || !this->initializeColorXform(info, fEncodedInfo.alpha()))
+        {
+            return kInvalidConversion;
+        }
+        return kSuccess;
     }
 
     if (index < 0) {
@@ -251,6 +274,11 @@
     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) {
@@ -296,8 +324,7 @@
         }
     }
 
-    return this->initializeColorXform(info, frame->reportedAlpha(), !frame->hasAlpha())
-        ? kSuccess : kInvalidConversion;
+    return this->initializeColorXform(info, frame->reportedAlpha()) ? kSuccess : kInvalidConversion;
 }
 
 SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
@@ -585,80 +612,63 @@
     }
 }
 
-static inline bool select_xform_format(SkColorType colorType, bool forColorTable,
-                                       skcms_PixelFormat* outFormat) {
-    SkASSERT(outFormat);
-
+static inline SkColorSpaceXform::ColorFormat select_xform_format_ct(SkColorType colorType) {
     switch (colorType) {
         case kRGBA_8888_SkColorType:
-            *outFormat = skcms_PixelFormat_RGBA_8888;
-            break;
+            return SkColorSpaceXform::kRGBA_8888_ColorFormat;
         case kBGRA_8888_SkColorType:
-            *outFormat = skcms_PixelFormat_BGRA_8888;
-            break;
+            return SkColorSpaceXform::kBGRA_8888_ColorFormat;
         case kRGB_565_SkColorType:
-            if (forColorTable) {
 #ifdef SK_PMCOLOR_IS_RGBA
-                *outFormat = skcms_PixelFormat_RGBA_8888;
+            return SkColorSpaceXform::kRGBA_8888_ColorFormat;
 #else
-                *outFormat = skcms_PixelFormat_BGRA_8888;
+            return SkColorSpaceXform::kBGRA_8888_ColorFormat;
 #endif
-                break;
-            }
-            *outFormat = skcms_PixelFormat_BGR_565;
-            break;
-        case kRGBA_F16_SkColorType:
-            *outFormat = skcms_PixelFormat_RGBA_hhhh;
-            break;
         default:
-            return false;
+            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;
 }
 
-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, SkAlphaType at) const {
+    SkASSERT(fColorXform);
+    SkAssertResult(fColorXform->apply(fDstXformFormat, dst,
+                                      fSrcXformFormat, src,
+                                      count, at));
 }
 
 void SkCodec::applyColorXform(void* dst, const void* src, int count) const {
-    const auto* srcProfile = fEncodedInfo.profile();
-    SkASSERT(srcProfile);
-    SkAssertResult(skcms_Transform(src, fSrcXformFormat, skcms_AlphaFormat_Unpremul, srcProfile,
-                                   dst, fDstXformFormat, fDstXformAlphaFormat, &fDstProfile,
-                                   count));
+    auto alphaType = select_xform_alpha(fDstInfo.alphaType(), fSrcInfo.alphaType());
+    this->applyColorXform(dst, src, count, alphaType);
 }
 
 std::vector<SkCodec::FrameInfo> SkCodec::getFrameInfo() {
diff --git a/src/codec/SkCodecPriv.h b/src/codec/SkCodecPriv.h
index 10bd064..d7db89f 100644
--- a/src/codec/SkCodecPriv.h
+++ b/src/codec/SkCodecPriv.h
@@ -9,6 +9,8 @@
 #define SkCodecPriv_DEFINED
 
 #include "SkColorData.h"
+#include "SkColorSpaceXform.h"
+#include "SkColorSpaceXformPriv.h"
 #include "SkColorTable.h"
 #include "SkEncodedInfo.h"
 #include "SkEncodedOrigin.h"
@@ -241,6 +243,30 @@
     }
 }
 
+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 75c5062..63c52b3 100644
--- a/src/codec/SkEncodedInfo.cpp
+++ b/src/codec/SkEncodedInfo.cpp
@@ -5,28 +5,4 @@
  * found in the LICENSE file.
  */
 
-#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))
-{}
+// Dummy file to assist in landing https://skia-review.googlesource.com/c/skia/+/136062
diff --git a/src/codec/SkGifCodec.cpp b/src/codec/SkGifCodec.cpp
index 77160cb..3ac5ed3 100644
--- a/src/codec/SkGifCodec.cpp
+++ b/src/codec/SkGifCodec.cpp
@@ -91,9 +91,18 @@
     // 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.
-    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()));
+    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()));
 }
 
 bool SkGifCodec::onRewind() {
@@ -101,8 +110,9 @@
     return true;
 }
 
-SkGifCodec::SkGifCodec(SkEncodedInfo&& encodedInfo, SkGifImageReader* reader)
-    : INHERITED(std::move(encodedInfo), skcms_PixelFormat_RGBA_8888, nullptr)
+SkGifCodec::SkGifCodec(const SkEncodedInfo& encodedInfo, const SkImageInfo& imageInfo,
+                       SkGifImageReader* reader)
+    : INHERITED(encodedInfo, imageInfo, SkColorSpaceXform::kRGBA_8888_ColorFormat, nullptr)
     , fReader(reader)
     , fTmpBuffer(nullptr)
     , fSwizzler(nullptr)
@@ -147,6 +157,7 @@
 }
 
 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();
@@ -162,8 +173,8 @@
         fCurrColorTable.reset(new SkColorTable(&color, 1));
     } else if (this->colorXform() && !this->xformOnDecode()) {
         SkPMColor dstColors[256];
-        this->applyColorXform(dstColors, currColorTable->readColors(),
-                              currColorTable->count());
+        this->applyColorXform(dstColors, currColorTable->readColors(), currColorTable->count(),
+                              kXformAlphaType);
         fCurrColorTable.reset(new SkColorTable(dstColors, currColorTable->count()));
     } else {
         fCurrColorTable = std::move(currColorTable);
@@ -391,7 +402,7 @@
         fSwizzler->swizzle(fXformBuffer.get(), src);
 
         const int xformWidth = get_scaled_dimension(dstInfo.width(), fSwizzler->sampleX());
-        this->applyColorXform(dst, fXformBuffer.get(), xformWidth);
+        this->applyColorXform(dst, fXformBuffer.get(), xformWidth, kXformAlphaType);
     } else {
         fSwizzler->swizzle(dst, src);
     }
diff --git a/src/codec/SkGifCodec.h b/src/codec/SkGifCodec.h
index 4dd1f0b..21dfd2b 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(SkEncodedInfo&&, SkGifImageReader*);
+    SkGifCodec(const SkEncodedInfo&, const SkImageInfo&, 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 f5785d6..31057a0 100644
--- a/src/codec/SkHeifCodec.cpp
+++ b/src/codec/SkHeifCodec.cpp
@@ -133,37 +133,40 @@
         return nullptr;
     }
 
-    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(
+            SkEncodedInfo::kYUV_Color, SkEncodedInfo::kOpaque_Alpha, 8);
 
-    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(std::move(info), heifDecoder.release(),
-                                                    orientation));
+    return std::unique_ptr<SkCodec>(new SkHeifCodec(frameInfo.mWidth, frameInfo.mHeight,
+            info, heifDecoder.release(), std::move(colorSpace), orientation));
 }
 
-SkHeifCodec::SkHeifCodec(SkEncodedInfo&& info, HeifDecoder* heifDecoder, SkEncodedOrigin origin)
-    : INHERITED(std::move(info), skcms_PixelFormat_RGBA_8888, nullptr, origin)
+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)
     , fHeifDecoder(heifDecoder)
     , fSwizzleSrcRow(nullptr)
     , fColorXformSrcRow(nullptr)
 {}
 
-
-bool SkHeifCodec::conversionSupported(const SkImageInfo& dstInfo, SkColorType /*srcColorType*/,
-                                      bool srcIsOpaque, bool needsColorXform) {
-    SkASSERT(srcIsOpaque);
-
+/*
+ * 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) {
     if (kUnknown_SkAlphaType == dstInfo.alphaType()) {
         return false;
     }
@@ -181,14 +184,14 @@
             return fHeifDecoder->setOutputColor(kHeifColorFormat_BGRA_8888);
 
         case kRGB_565_SkColorType:
-            if (needsColorXform) {
+            if (this->colorXform()) {
                 return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
             } else {
                 return fHeifDecoder->setOutputColor(kHeifColorFormat_RGB565);
             }
 
         case kRGBA_F16_SkColorType:
-            SkASSERT(needsColorXform);
+            SkASSERT(this->colorXform());
             return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
 
         default:
@@ -237,7 +240,7 @@
         }
 
         if (this->colorXform()) {
-            this->applyColorXform(dst, swizzleDst, dstWidth);
+            this->applyColorXform(dst, swizzleDst, dstWidth, kOpaque_SkAlphaType);
             dst = SkTAddOffset<void>(dst, rowBytes);
         }
 
@@ -262,6 +265,11 @@
         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;
     }
@@ -305,13 +313,15 @@
 
 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(this->getEncodedInfo(), nullptr,
+    fSwizzler.reset(SkSwizzler::CreateSwizzler(swizzlerInfo, nullptr,
             swizzlerDstInfo, options, nullptr, true));
     SkASSERT(fSwizzler);
 }
@@ -329,6 +339,11 @@
 
 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 0796805..cdae706 100644
--- a/src/codec/SkHeifCodec.h
+++ b/src/codec/SkHeifCodec.h
@@ -9,6 +9,8 @@
 #define SkHeifCodec_DEFINED
 
 #include "SkCodec.h"
+#include "SkColorSpace.h"
+#include "SkColorSpaceXform.h"
 #include "SkEncodedOrigin.h"
 #include "SkImageInfo.h"
 #include "SkSwizzler.h"
@@ -41,14 +43,27 @@
         return SkEncodedImageFormat::kHEIF;
     }
 
-    bool conversionSupported(const SkImageInfo&, SkColorType, bool, bool) override;
+    bool conversionSupported(const SkImageInfo&, SkColorType, bool,
+                             const SkColorSpace*) const override {
+        // This class checks for conversion after creating colorXform.
+        return true;
+    }
 
 private:
     /*
      * Creates an instance of the decoder
      * Called only by NewFromStream
      */
-    SkHeifCodec(SkEncodedInfo&&, HeifDecoder*, SkEncodedOrigin);
+    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);
 
     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 d838079..8ff4088 100644
--- a/src/codec/SkIcoCodec.cpp
+++ b/src/codec/SkIcoCodec.cpp
@@ -179,19 +179,29 @@
             maxIndex = i;
         }
     }
-
-    auto maxInfo = codecs->operator[](maxIndex)->getEncodedInfo().copy();
+    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();
 
     *result = kSuccess;
     // The original stream is no longer needed, because the embedded codecs own their
     // own streams.
-    return std::unique_ptr<SkCodec>(new SkIcoCodec(std::move(maxInfo), codecs.release()));
+    return std::unique_ptr<SkCodec>(new SkIcoCodec(width, height, info, codecs.release(),
+                                                   sk_ref_sp(colorSpace)));
 }
 
-SkIcoCodec::SkIcoCodec(SkEncodedInfo&& info, SkTArray<std::unique_ptr<SkCodec>, true>* codecs)
-    // The source skcms_PixelFormat will not be used. The embedded
+/*
+ * 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
     // codec's will be used instead.
-    : INHERITED(std::move(info), skcms_PixelFormat(), nullptr)
+    : INHERITED(width, height, info, SkColorSpaceXform::ColorFormat(), nullptr,
+                std::move(colorSpace))
     , fEmbeddedCodecs(codecs)
     , fCurrCodec(nullptr)
 {}
diff --git a/src/codec/SkIcoCodec.h b/src/codec/SkIcoCodec.h
index e733e9f..c43fcf8c 100644
--- a/src/codec/SkIcoCodec.h
+++ b/src/codec/SkIcoCodec.h
@@ -48,7 +48,8 @@
 
     SkScanlineOrder onGetScanlineOrder() const override;
 
-    bool conversionSupported(const SkImageInfo&, SkColorType, bool, bool) override {
+    bool conversionSupported(const SkImageInfo&, SkColorType, bool,
+                             const SkColorSpace*) const override {
         // This will be checked by the embedded codec.
         return true;
     }
@@ -86,7 +87,8 @@
      * Constructor called by NewFromStream
      * @param embeddedCodecs codecs for the embedded images, takes ownership
      */
-    SkIcoCodec(SkEncodedInfo&& info, SkTArray<std::unique_ptr<SkCodec>, true>* embeddedCodecs);
+    SkIcoCodec(int width, int height, const SkEncodedInfo& info,
+            SkTArray<std::unique_ptr<SkCodec>, true>* embeddedCodecs, sk_sp<SkColorSpace> colorSpace);
 
     std::unique_ptr<SkTArray<std::unique_ptr<SkCodec>, true>> fEmbeddedCodecs;
 
diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp
index a0fa009..fd95590 100644
--- a/src/codec/SkJpegCodec.cpp
+++ b/src/codec/SkJpegCodec.cpp
@@ -10,7 +10,6 @@
 #include "SkCodec.h"
 #include "SkCodecPriv.h"
 #include "SkColorData.h"
-#include "SkColorSpace.h"
 #include "SkJpegDecoderMgr.h"
 #include "SkJpegInfo.h"
 #include "SkStream.h"
@@ -132,8 +131,7 @@
  *     (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 std::unique_ptr<SkEncodedInfo::ICCProfile> read_color_profile(jpeg_decompress_struct* dinfo)
-{
+static sk_sp<SkColorSpace> read_color_space(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));
@@ -193,12 +191,11 @@
         dst = SkTAddOffset<void>(dst, bytes);
     }
 
-    return SkEncodedInfo::ICCProfile::Make(std::move(iccData));
+    return SkColorSpace::MakeICC(iccData->data(), iccData->size());
 }
 
 SkCodec::Result SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut,
-        JpegDecoderMgr** decoderMgrOut,
-        std::unique_ptr<SkEncodedInfo::ICCProfile> defaultColorProfile) {
+        JpegDecoderMgr** decoderMgrOut, sk_sp<SkColorSpace> defaultColorSpace) {
 
     // Create a JpegDecoderMgr to own all of the decompress information
     std::unique_ptr<JpegDecoderMgr> decoderMgr(new JpegDecoderMgr(stream));
@@ -211,18 +208,17 @@
 
     // 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(dinfo, kExifMarker, 0xFFFF);
-        jpeg_save_markers(dinfo, kICCMarker, 0xFFFF);
+        jpeg_save_markers(decoderMgr->dinfo(), kExifMarker, 0xFFFF);
+        jpeg_save_markers(decoderMgr->dinfo(), kICCMarker, 0xFFFF);
     }
 
     // Read the jpeg header
-    switch (jpeg_read_header(dinfo, true)) {
+    switch (jpeg_read_header(decoderMgr->dinfo(), true)) {
         case JPEG_HEADER_OK:
             break;
         case JPEG_SUSPENDED:
@@ -238,41 +234,42 @@
             return kInvalidInput;
         }
 
-        SkEncodedOrigin orientation = get_exif_orientation(dinfo);
-        auto profile = read_color_profile(dinfo);
-        if (profile) {
-            auto type = profile->profile()->data_color_space;
+        // 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) {
             switch (decoderMgr->dinfo()->jpeg_color_space) {
                 case JCS_CMYK:
                 case JCS_YCCK:
-                    if (type != skcms_Signature_CMYK) {
-                        profile = nullptr;
+                    if (colorSpace->type() != SkColorSpace::kCMYK_Type) {
+                        colorSpace = nullptr;
                     }
                     break;
                 case JCS_GRAYSCALE:
-                    if (type != skcms_Signature_Gray &&
-                        type != skcms_Signature_RGB)
+                    if (colorSpace->type() != SkColorSpace::kGray_Type &&
+                        colorSpace->type() != SkColorSpace::kRGB_Type)
                     {
-                        profile = nullptr;
+                        colorSpace = nullptr;
                     }
                     break;
                 default:
-                    if (type != skcms_Signature_RGB) {
-                        profile = nullptr;
+                    if (colorSpace->type() != SkColorSpace::kRGB_Type) {
+                        colorSpace = nullptr;
                     }
                     break;
             }
         }
-        if (!profile) {
-            profile = std::move(defaultColorProfile);
+        if (!colorSpace) {
+            colorSpace = defaultColorSpace;
         }
 
-        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);
+        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);
         *codecOut = codec;
     } else {
         SkASSERT(nullptr != decoderMgrOut);
@@ -283,15 +280,14 @@
 
 std::unique_ptr<SkCodec> SkJpegCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
                                                      Result* result) {
-    return SkJpegCodec::MakeFromStream(std::move(stream), result,
-                                       // FIXME: This may not be used. Can we skip creating it?
-                                       SkEncodedInfo::ICCProfile::MakeSRGB());
+    return SkJpegCodec::MakeFromStream(std::move(stream), result, SkColorSpace::MakeSRGB());
 }
 
 std::unique_ptr<SkCodec> SkJpegCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
-        Result* result, std::unique_ptr<SkEncodedInfo::ICCProfile> defaultColorProfile) {
+                                                     Result* result,
+                                    sk_sp<SkColorSpace> defaultColorSpace) {
     SkCodec* codec = nullptr;
-    *result = ReadHeader(stream.get(), &codec, nullptr, std::move(defaultColorProfile));
+    *result = ReadHeader(stream.get(), &codec, nullptr, std::move(defaultColorSpace));
     if (kSuccess == *result) {
         // Codec has taken ownership of the stream, we do not need to delete it
         SkASSERT(codec);
@@ -301,10 +297,11 @@
     return nullptr;
 }
 
-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)
+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)
     , fDecoderMgr(decoderMgr)
     , fReadyState(decoderMgr->dinfo()->global_state)
     , fSwizzleSrcRow(nullptr)
@@ -389,10 +386,12 @@
     return true;
 }
 
-bool SkJpegCodec::conversionSupported(const SkImageInfo& dstInfo, SkColorType srcCT,
-                                      bool srcIsOpaque, bool needsColorXform) {
-    SkASSERT(srcIsOpaque);
-
+/*
+ * 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) {
     if (kUnknown_SkAlphaType == dstInfo.alphaType()) {
         return false;
     }
@@ -410,7 +409,7 @@
             fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA;
             break;
         case kBGRA_8888_SkColorType:
-            if (needsColorXform) {
+            if (this->colorXform()) {
                 // Always using RGBA as the input format for color xforms makes the
                 // implementation a little simpler.
                 fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA;
@@ -419,7 +418,7 @@
             }
             break;
         case kRGB_565_SkColorType:
-            if (needsColorXform) {
+            if (this->colorXform()) {
                 fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA;
             } else {
                 fDecoderMgr->dinfo()->dither_mode = JDITHER_NONE;
@@ -427,15 +426,14 @@
             }
             break;
         case kGray_8_SkColorType:
-            SkASSERT(!needsColorXform);
-            if (JCS_GRAYSCALE != encodedColorType) {
+            if (this->colorXform() || JCS_GRAYSCALE != encodedColorType) {
                 return false;
             }
 
             fDecoderMgr->dinfo()->out_color_space = JCS_GRAYSCALE;
             break;
         case kRGBA_F16_SkColorType:
-            SkASSERT(needsColorXform);
+            SkASSERT(this->colorXform());
             fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA;
             break;
         default:
@@ -541,7 +539,7 @@
         }
 
         if (this->colorXform()) {
-            this->applyColorXform(dst, swizzleDst, dstWidth);
+            this->applyColorXform(dst, swizzleDst, dstWidth, kOpaque_SkAlphaType);
             dst = SkTAddOffset<void>(dst, rowBytes);
         }
 
@@ -555,17 +553,16 @@
 /*
  * This is a bit tricky.  We only need the swizzler to do format conversion if the jpeg is
  * encoded as CMYK.
- * And even then we still may not need it.  If the jpeg has a CMYK color profile and a color
+ * And even then we still may not need it.  If the jpeg has a CMYK color space and a color
  * xform, the color xform will handle the CMYK->RGB conversion.
  */
 static inline bool needs_swizzler_to_convert_from_cmyk(J_COLOR_SPACE jpegColorType,
-                                                       const skcms_ICCProfile* srcProfile,
-                                                       bool hasColorSpaceXform) {
+        const SkImageInfo& srcInfo, bool hasColorSpaceXform) {
     if (JCS_CMYK != jpegColorType) {
         return false;
     }
 
-    bool hasCMYKColorSpace = srcProfile && srcProfile->data_color_space == skcms_Signature_CMYK;
+    bool hasCMYKColorSpace = SkColorSpace::kCMYK_Type ==  srcInfo.colorSpace()->type();
     return !hasCMYKColorSpace || !hasColorSpaceXform;
 }
 
@@ -590,6 +587,11 @@
         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);
     }
@@ -598,8 +600,8 @@
     // If it's not, we want to know because it means our strategy is not optimal.
     SkASSERT(1 == dinfo->rec_outbuf_height);
 
-    if (needs_swizzler_to_convert_from_cmyk(dinfo->out_color_space,
-                                            this->getEncodedInfo().profile(), this->colorXform())) {
+    if (needs_swizzler_to_convert_from_cmyk(dinfo->out_color_space, this->getInfo(),
+            this->colorXform())) {
         this->initializeSwizzler(dstInfo, options, true);
     }
 
@@ -639,16 +641,14 @@
     }
 }
 
-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 = make_info(this->getEncodedInfo(), needsCMYKToRGB);
+    SkEncodedInfo swizzlerInfo = this->getEncodedInfo();
+    if (needsCMYKToRGB) {
+        swizzlerInfo = SkEncodedInfo::Make(SkEncodedInfo::kInvertedCMYK_Color,
+                                           swizzlerInfo.alpha(),
+                                           swizzlerInfo.bitsPerComponent());
+    }
 
     Options swizzlerOptions = options;
     if (options.fSubset) {
@@ -678,8 +678,7 @@
     }
 
     bool needsCMYKToRGB = needs_swizzler_to_convert_from_cmyk(
-            fDecoderMgr->dinfo()->out_color_space, this->getEncodedInfo().profile(),
-            this->colorXform());
+            fDecoderMgr->dinfo()->out_color_space, this->getInfo(), this->colorXform());
     this->initializeSwizzler(this->dstInfo(), this->options(), needsCMYKToRGB);
     this->allocateStorage(this->dstInfo());
     return fSwizzler.get();
@@ -694,14 +693,18 @@
         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;
     }
 
     bool needsCMYKToRGB = needs_swizzler_to_convert_from_cmyk(
-            fDecoderMgr->dinfo()->out_color_space, this->getEncodedInfo().profile(),
-            this->colorXform());
+            fDecoderMgr->dinfo()->out_color_space, this->getInfo(), this->colorXform());
     if (options.fSubset) {
         uint32_t startX = options.fSubset->x();
         uint32_t width = options.fSubset->width();
diff --git a/src/codec/SkJpegCodec.h b/src/codec/SkJpegCodec.h
index e6ec2ce..7fab209 100644
--- a/src/codec/SkJpegCodec.h
+++ b/src/codec/SkJpegCodec.h
@@ -9,13 +9,14 @@
 #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;
 
 /*
  *
@@ -57,14 +58,19 @@
 
     bool onDimensionsSupported(const SkISize&) override;
 
-    bool conversionSupported(const SkImageInfo&, SkColorType, bool, bool) override;
+    bool conversionSupported(const SkImageInfo&, SkColorType, bool,
+                             const SkColorSpace*) const override {
+        // This class checks for conversion after creating colorXform.
+        return true;
+    }
 
 private:
+
     /*
-     * Allows SkRawCodec to communicate the color profile from the exif data.
+     * Allows SkRawCodec to communicate the color space from the exif data.
      */
     static std::unique_ptr<SkCodec> MakeFromStream(std::unique_ptr<SkStream>, Result*,
-            std::unique_ptr<SkEncodedInfo::ICCProfile> defaultColorProfile);
+                                                   sk_sp<SkColorSpace> defaultColorSpace);
 
     /*
      * Read enough of the stream to initialize the SkJpegCodec.
@@ -84,13 +90,12 @@
      * codecOut will take ownership of it in the case where we created a codec.
      * Ownership is unchanged when we set decoderMgrOut.
      *
-     * @param defaultColorProfile
-     * If the jpeg does not have an embedded color profile, the image data should
-     * be tagged with this color profile.
+     * @param defaultColorSpace
+     * If the jpeg does not have an embedded color space, the image data should
+     * be tagged with this color space.
      */
     static Result ReadHeader(SkStream* stream, SkCodec** codecOut,
-            JpegDecoderMgr** decoderMgrOut,
-            std::unique_ptr<SkEncodedInfo::ICCProfile> defaultColorProfile);
+            JpegDecoderMgr** decoderMgrOut, sk_sp<SkColorSpace> defaultColorSpace);
 
     /*
      * Creates an instance of the decoder
@@ -101,8 +106,16 @@
      * @param decoderMgr holds decompress struct, src manager, and error manager
      *                   takes ownership
      */
-    SkJpegCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
-            JpegDecoderMgr* decoderMgr, SkEncodedOrigin origin);
+    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);
 
     void initializeSwizzler(const SkImageInfo& dstInfo, const Options& options,
                             bool needsCMYKToRGB);
diff --git a/src/codec/SkPngCodec.cpp b/src/codec/SkPngCodec.cpp
index 21046d4..e9972cc 100644
--- a/src/codec/SkPngCodec.cpp
+++ b/src/codec/SkPngCodec.cpp
@@ -9,6 +9,7 @@
 #include "SkCodecPriv.h"
 #include "SkColorData.h"
 #include "SkColorSpace.h"
+#include "SkColorSpacePriv.h"
 #include "SkColorTable.h"
 #include "SkMacros.h"
 #include "SkMath.h"
@@ -247,10 +248,6 @@
 
 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) {
 
@@ -268,7 +265,10 @@
     png_bytep alphas;
     int numColorsWithAlpha = 0;
     if (png_get_tRNS(fPng_ptr, fInfo_ptr, &alphas, &numColorsWithAlpha, nullptr)) {
-        bool premultiply = needs_premul(dstInfo.alphaType(), this->getEncodedInfo().alpha());
+        // 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());
 
         // 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,11 +342,13 @@
 
 #endif // LIBPNG >= 1.6
 
-// 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) {
+// 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 (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;
@@ -360,70 +362,74 @@
     int compression;
     if (PNG_INFO_iCCP == png_get_iCCP(png_ptr, info_ptr, &name, &compression, &profile,
             &length)) {
-        auto data = SkData::MakeWithCopy(profile, length);
-        return SkEncodedInfo::ICCProfile::Make(std::move(data));
+        return SkColorSpace::MakeICC(profile, length);
     }
 
     // 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 SkEncodedInfo::ICCProfile::MakeSRGB();
+        return SkColorSpace::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]))
     {
-        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]);
+        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]);
 
-        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.
+        SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
+        if (!primaries.toXYZD50(&toXYZD50)) {
+            toXYZD50.set3x3RowMajorf(gSRGB_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 {
+        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);
+        }
+
         // Default to sRGB gamma if the image has color space information,
         // but does not specify gamma.
-        // Note that Blink would again return nullptr in this case.
-        fn = *skcms_sRGB_TransferFunction();
+        return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, toXYZD50);
     }
 
-    skcms_ICCProfile skcmsProfile;
-    skcms_Init(&skcmsProfile);
-    skcms_SetTransferFunction(&skcmsProfile, &fn);
-    skcms_SetXYZD50(&skcmsProfile, &toXYZD50);
+    // 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);
 
-    return SkEncodedInfo::ICCProfile::Make(skcmsProfile);
-#else // LIBPNG >= 1.6
-    return SkEncodedInfo::ICCProfile::MakeSRGB();
+        // Since there is no cHRM, we will guess sRGB gamut.
+        SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
+        toXYZD50.set3x3RowMajorf(gSRGB_toXYZD50);
+
+        return SkColorSpace::MakeRGB(fn, toXYZD50);
+    }
+
 #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) {
@@ -448,17 +454,17 @@
     }
 }
 
-static skcms_PixelFormat png_select_xform_format(const SkEncodedInfo& info) {
+static SkColorSpaceXform::ColorFormat 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 skcms_PixelFormat_RGBA_16161616;
+            return SkColorSpaceXform::kRGBA_U16_BE_ColorFormat;
         } else if (SkEncodedInfo::kRGB_Color == info.color()) {
-            return skcms_PixelFormat_RGB_161616;
+            return SkColorSpaceXform::kRGB_U16_BE_ColorFormat;
         }
     }
 
-    return skcms_PixelFormat_RGBA_8888;
+    return SkColorSpaceXform::kRGBA_8888_ColorFormat;
 }
 
 void SkPngCodec::applyXformRow(void* dst, const void* src) {
@@ -478,9 +484,10 @@
 
 class SkPngNormalDecoder : public SkPngCodec {
 public:
-    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)
+    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)
         , fRowsWrittenToOutput(0)
         , fDst(nullptr)
         , fRowBytes(0)
@@ -600,10 +607,10 @@
 
 class SkPngInterlacedDecoder : public SkPngCodec {
 public:
-    SkPngInterlacedDecoder(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
-            SkPngChunkReader* reader, png_structp png_ptr,
+    SkPngInterlacedDecoder(const SkEncodedInfo& info, const SkImageInfo& imageInfo,
+            std::unique_ptr<SkStream> stream, SkPngChunkReader* reader, png_structp png_ptr,
             png_infop info_ptr, int bitDepth, int numberPasses)
-        : INHERITED(std::move(info), std::move(stream), reader, png_ptr, info_ptr, bitDepth)
+        : INHERITED(info, imageInfo, std::move(stream), reader, png_ptr, info_ptr, bitDepth)
         , fNumberPasses(numberPasses)
         , fFirstRow(0)
         , fLastRow(0)
@@ -911,33 +918,36 @@
 
     if (fOutCodec) {
         SkASSERT(nullptr == *fOutCodec);
-        auto profile = read_color_profile(fPng_ptr, fInfo_ptr);
-        if (profile) {
-            switch (profile->profile()->data_color_space) {
-                case skcms_Signature_CMYK:
-                    profile = nullptr;
+        sk_sp<SkColorSpace> colorSpace = read_color_space(fPng_ptr, fInfo_ptr);
+        if (colorSpace) {
+            switch (colorSpace->type()) {
+                case SkColorSpace::kCMYK_Type:
+                    colorSpace = nullptr;
                     break;
-                case skcms_Signature_Gray:
+                case SkColorSpace::kGray_Type:
                     if (SkEncodedInfo::kGray_Color != color &&
                         SkEncodedInfo::kGrayAlpha_Color != color)
                     {
-                        profile = nullptr;
+                        colorSpace = nullptr;
                     }
                     break;
-                default:
+                case SkColorSpace::kRGB_Type:
                     break;
             }
         }
-        if (!profile) {
+        if (!colorSpace) {
             // Treat unsupported/invalid color spaces as sRGB.
-            profile = SkEncodedInfo::ICCProfile::MakeSRGB();
+            colorSpace = SkColorSpace::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) {
-                    color = SkEncodedInfo::kXAlpha_Color;
+                    imageInfo = imageInfo.makeColorType(kAlpha_8_SkColorType);
                 }
             }
         } else if (SkEncodedInfo::kOpaque_Alpha == alpha) {
@@ -945,18 +955,16 @@
             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.
-                    color = SkEncodedInfo::k565_Color;
+                    imageInfo = imageInfo.makeColorType(kRGB_565_SkColorType);
                 }
             }
         }
 
-        SkEncodedInfo encodedInfo = SkEncodedInfo::Make(origWidth, origHeight, color, alpha,
-                                                        bitDepth, std::move(profile));
         if (1 == numberPasses) {
-            *fOutCodec = new SkPngNormalDecoder(std::move(encodedInfo),
+            *fOutCodec = new SkPngNormalDecoder(encodedInfo, imageInfo,
                    std::unique_ptr<SkStream>(fStream), fChunkReader, fPng_ptr, fInfo_ptr, bitDepth);
         } else {
-            *fOutCodec = new SkPngInterlacedDecoder(std::move(encodedInfo),
+            *fOutCodec = new SkPngInterlacedDecoder(encodedInfo, imageInfo,
                     std::unique_ptr<SkStream>(fStream), fChunkReader, fPng_ptr, fInfo_ptr, bitDepth,
                     numberPasses);
         }
@@ -968,9 +976,10 @@
     this->releasePngPtrs();
 }
 
-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))
+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))
     , fPngChunkReader(SkSafeRef(chunkReader))
     , fPng_ptr(png_ptr)
     , fInfo_ptr(info_ptr)
diff --git a/src/codec/SkPngCodec.h b/src/codec/SkPngCodec.h
index bf068ea..075181c 100644
--- a/src/codec/SkPngCodec.h
+++ b/src/codec/SkPngCodec.h
@@ -8,6 +8,7 @@
 #define SkPngCodec_DEFINED
 
 #include "SkCodec.h"
+#include "SkColorSpaceXform.h"
 #include "SkColorTable.h"
 #include "SkPngChunkReader.h"
 #include "SkEncodedImageFormat.h"
@@ -44,8 +45,8 @@
         void* fPtr;
     };
 
-    SkPngCodec(SkEncodedInfo&&, std::unique_ptr<SkStream>, SkPngChunkReader*,
-               void* png_ptr, void* info_ptr, int bitDepth);
+    SkPngCodec(const SkEncodedInfo&, const SkImageInfo&, 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 30a3d7b..9eea78c 100644
--- a/src/codec/SkRawCodec.cpp
+++ b/src/codec/SkRawCodec.cpp
@@ -504,6 +504,10 @@
         }
     }
 
+    const SkEncodedInfo& getEncodedInfo() const {
+        return fEncodedInfo;
+    }
+
     int width() const {
         return fWidth;
     }
@@ -598,6 +602,8 @@
 
     SkDngImage(SkRawStream* stream)
         : fStream(stream)
+        , fEncodedInfo(SkEncodedInfo::Make(SkEncodedInfo::kRGB_Color,
+                                           SkEncodedInfo::kOpaque_Alpha, 8))
     {}
 
     dng_memory_allocator fAllocator;
@@ -609,21 +615,11 @@
 
     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,
@@ -648,21 +644,15 @@
             return nullptr;
         }
 
-        std::unique_ptr<SkEncodedInfo::ICCProfile> profile;
+        sk_sp<SkColorSpace> colorSpace;
         switch (imageData.color_space) {
             case ::piex::PreviewImageData::kSrgb:
-                profile = SkEncodedInfo::ICCProfile::MakeSRGB();
+                colorSpace = SkColorSpace::MakeSRGB();
                 break;
-            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);
+            case ::piex::PreviewImageData::kAdobeRgb:
+                colorSpace = SkColorSpace::MakeRGB(g2Dot2_TransferFn,
+                                                   SkColorSpace::kAdobeRGB_Gamut);
                 break;
-            }
         }
 
         //  Theoretically PIEX can return JPEG compressed image or uncompressed RGB image. We only
@@ -680,7 +670,7 @@
                 return nullptr;
             }
             return SkJpegCodec::MakeFromStream(std::move(memoryStream), result,
-                                               std::move(profile));
+                                               std::move(colorSpace));
         }
     }
 
@@ -756,7 +746,7 @@
         if (this->colorXform()) {
             swizzler->swizzle(xformBuffer.get(), &srcRow[0]);
 
-            this->applyColorXform(dstRow, xformBuffer.get(), dstInfo.width());
+            this->applyColorXform(dstRow, xformBuffer.get(), dstInfo.width(), kOpaque_SkAlphaType);
         } else {
             swizzler->swizzle(dstRow, &srcRow[0]);
         }
@@ -806,8 +796,7 @@
 SkRawCodec::~SkRawCodec() {}
 
 SkRawCodec::SkRawCodec(SkDngImage* dngImage)
-    : INHERITED(SkEncodedInfo::MakeSRGB(dngImage->width(), dngImage->height(),
-                                        SkEncodedInfo::kRGB_Color,
-                                        SkEncodedInfo::kOpaque_Alpha, 8),
-                skcms_PixelFormat_RGBA_8888, nullptr)
+    : INHERITED(dngImage->width(), dngImage->height(), dngImage->getEncodedInfo(),
+                SkColorSpaceXform::kRGBA_8888_ColorFormat, nullptr,
+                SkColorSpace::MakeSRGB())
     , fDngImage(dngImage) {}
diff --git a/src/codec/SkSwizzler.cpp b/src/codec/SkSwizzler.cpp
index cca8c41..4b350c4 100644
--- a/src/codec/SkSwizzler.cpp
+++ b/src/codec/SkSwizzler.cpp
@@ -890,7 +890,6 @@
                         return nullptr;
                 }
                 break;
-            case SkEncodedInfo::kXAlpha_Color:
             case SkEncodedInfo::kGrayAlpha_Color:
                 switch (dstInfo.colorType()) {
                     case kRGBA_8888_SkColorType:
@@ -964,10 +963,6 @@
                         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 cc1462e..8d4beb1 100644
--- a/src/codec/SkWbmpCodec.cpp
+++ b/src/codec/SkWbmpCodec.cpp
@@ -97,10 +97,11 @@
     return this->stream()->read(row, fSrcRowBytes) == fSrcRowBytes;
 }
 
-SkWbmpCodec::SkWbmpCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream)
+SkWbmpCodec::SkWbmpCodec(int width, int height, const SkEncodedInfo& info,
+                         std::unique_ptr<SkStream> stream)
     // Wbmp does not need a colorXform, so choose an arbitrary srcFormat.
-    : INHERITED(std::move(info), skcms_PixelFormat(),
-                std::move(stream))
+    : INHERITED(width, height, info, SkColorSpaceXform::ColorFormat(),
+                std::move(stream), SkColorSpace::MakeSRGB())
     , fSrcRowBytes(get_src_row_bytes(this->getInfo().width()))
     , fSwizzler(nullptr)
 {}
@@ -110,7 +111,7 @@
 }
 
 bool SkWbmpCodec::conversionSupported(const SkImageInfo& dst, SkColorType /*srcColor*/,
-                                      bool srcIsOpaque, bool /*needsXform*/) {
+                                      bool srcIsOpaque, const SkColorSpace* srcCS) const {
     return valid_color_type(dst) && valid_alpha(dst.alphaType(), srcIsOpaque);
 }
 
@@ -158,9 +159,10 @@
         return nullptr;
     }
     *result = kSuccess;
-    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)));
+    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)));
 }
 
 int SkWbmpCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) {
diff --git a/src/codec/SkWbmpCodec.h b/src/codec/SkWbmpCodec.h
index b9df0b9..192189d 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, bool needsXform) override;
+                             bool srcIsOpaque, const SkColorSpace* srcCS) const 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(SkEncodedInfo&&, std::unique_ptr<SkStream>);
+    SkWbmpCodec(int width, int height, const SkEncodedInfo&, std::unique_ptr<SkStream>);
 
     const size_t                fSrcRowBytes;
 
diff --git a/src/codec/SkWebpCodec.cpp b/src/codec/SkWebpCodec.cpp
index ac1c9be..a9c0c32 100644
--- a/src/codec/SkWebpCodec.cpp
+++ b/src/codec/SkWebpCodec.cpp
@@ -13,6 +13,7 @@
 #include "SkCodecAnimation.h"
 #include "SkCodecAnimationPriv.h"
 #include "SkCodecPriv.h"
+#include "SkColorSpaceXform.h"
 #include "SkMakeUnique.h"
 #include "SkRasterPipeline.h"
 #include "SkSampler.h"
@@ -88,17 +89,15 @@
         }
     }
 
-    std::unique_ptr<SkEncodedInfo::ICCProfile> profile = nullptr;
+    sk_sp<SkColorSpace> colorSpace = nullptr;
     {
         WebPChunkIterator chunkIterator;
         SkAutoTCallVProc<WebPChunkIterator, WebPDemuxReleaseChunkIterator> autoCI(&chunkIterator);
         if (WebPDemuxGetChunk(demux, "ICCP", 1, &chunkIterator)) {
-            // FIXME: I think this could be MakeWithoutCopy
-            auto chunk = SkData::MakeWithCopy(chunkIterator.chunk.bytes, chunkIterator.chunk.size);
-            profile = SkEncodedInfo::ICCProfile::Make(std::move(chunk));
+            colorSpace = SkColorSpace::MakeICC(chunkIterator.chunk.bytes, chunkIterator.chunk.size);
         }
-        if (!profile || profile->profile()->data_color_space != skcms_Signature_RGB) {
-            profile = SkEncodedInfo::ICCProfile::MakeSRGB();
+        if (!colorSpace || colorSpace->type() != SkColorSpace::kRGB_Type) {
+            colorSpace = SkColorSpace::MakeSRGB();
         }
     }
 
@@ -172,9 +171,10 @@
 
 
     *result = kSuccess;
-    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));
+    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));
 }
 
 SkISize SkWebpCodec::onGetScaledDimensions(float desiredScale) const {
@@ -543,8 +543,35 @@
         webpDst.installPixels(webpInfo, dst, rowBytes);
     }
 
-    config.output.colorspace = webp_decode_mode(webpInfo.colorType(),
-            frame.has_alpha && dstInfo.alphaType() == kPremul_SkAlphaType && !this->colorXform());
+    // 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.is_external_memory = 1;
 
     config.output.u.RGBA.rgba = reinterpret_cast<uint8_t*>(webpDst.getAddr(dstX, dstY));
@@ -593,8 +620,11 @@
             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);
+            this->applyColorXform(xformDst, xformSrc, scaledWidth, xformAlphaType);
             if (blendWithPrevFrame) {
                 blend_line(dstCT, dst, dstCT, xformDst,
                         dstInfo.alphaType(), frame.has_alpha, scaledWidth);
@@ -618,14 +648,14 @@
     return result;
 }
 
-SkWebpCodec::SkWebpCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
+SkWebpCodec::SkWebpCodec(int width, int height, const SkEncodedInfo& info,
+                         sk_sp<SkColorSpace> colorSpace, std::unique_ptr<SkStream> stream,
                          WebPDemuxer* demux, sk_sp<SkData> data, SkEncodedOrigin origin)
-    : INHERITED(std::move(info), skcms_PixelFormat_BGRA_8888, std::move(stream),
-                origin)
+    : INHERITED(width, height, info, SkColorSpaceXform::kBGRA_8888_ColorFormat, std::move(stream),
+                std::move(colorSpace), origin)
     , fDemux(demux)
     , fData(std::move(data))
     , fFailed(false)
 {
-    const auto& eInfo = this->getEncodedInfo();
-    fFrameHolder.setScreenSize(eInfo.width(), eInfo.height());
+    fFrameHolder.setScreenSize(width, height);
 }
diff --git a/src/codec/SkWebpCodec.h b/src/codec/SkWebpCodec.h
index 4de5f38..fdd5422 100644
--- a/src/codec/SkWebpCodec.h
+++ b/src/codec/SkWebpCodec.h
@@ -9,6 +9,7 @@
 #define SkWebpCodec_DEFINED
 
 #include "SkCodec.h"
+#include "SkColorSpace.h"
 #include "SkEncodedImageFormat.h"
 #include "SkFrameHolder.h"
 #include "SkImageInfo.h"
@@ -46,8 +47,8 @@
     }
 
 private:
-    SkWebpCodec(SkEncodedInfo&&, std::unique_ptr<SkStream>, WebPDemuxer*, sk_sp<SkData>,
-                SkEncodedOrigin);
+    SkWebpCodec(int width, int height, const SkEncodedInfo&, sk_sp<SkColorSpace>,
+                std::unique_ptr<SkStream>, WebPDemuxer*, sk_sp<SkData>, SkEncodedOrigin);
 
     SkAutoTCallVProc<WebPDemuxer, WebPDemuxDelete> fDemux;
 
diff --git a/tests/CodecTest.cpp b/tests/CodecTest.cpp
index 7f50a41..9bbadc6 100644
--- a/tests/CodecTest.cpp
+++ b/tests/CodecTest.cpp
@@ -1608,25 +1608,6 @@
     }
 }
 
-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
deleted file mode 100644
index 3149df8..0000000
--- a/tests/EncodedInfoTest.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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));
-}