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