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