Switch SkCodec to use skcms

Bug: skia:6839
Bug: skia:8052

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

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

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

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

Change-Id: Ic0d3bb752b03f13be886b80331987aa5a5713fc0
Reviewed-on: https://skia-review.googlesource.com/136062
Commit-Queue: Leon Scroggins <scroggo@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
Leon Scroggins III 2018-08-22 11:18:08 -04:00 committed by Skia Commit-Bot
parent 82cf64a0d3
commit 81886e8f94
36 changed files with 577 additions and 541 deletions

View File

@ -863,6 +863,7 @@ component("skia") {
"src/codec/SkCodec.cpp", "src/codec/SkCodec.cpp",
"src/codec/SkCodecImageGenerator.cpp", "src/codec/SkCodecImageGenerator.cpp",
"src/codec/SkColorTable.cpp", "src/codec/SkColorTable.cpp",
"src/codec/SkEncodedInfo.cpp",
"src/codec/SkGifCodec.cpp", "src/codec/SkGifCodec.cpp",
"src/codec/SkMaskSwizzler.cpp", "src/codec/SkMaskSwizzler.cpp",
"src/codec/SkMasks.cpp", "src/codec/SkMasks.cpp",
@ -2021,8 +2022,8 @@ if (skia_enable_tools) {
"tools/viewer/SlideDir.cpp", "tools/viewer/SlideDir.cpp",
"tools/viewer/StatsLayer.cpp", "tools/viewer/StatsLayer.cpp",
"tools/viewer/SvgSlide.cpp", "tools/viewer/SvgSlide.cpp",
"tools/viewer/TouchGesture.h",
"tools/viewer/TouchGesture.cpp", "tools/viewer/TouchGesture.cpp",
"tools/viewer/TouchGesture.h",
"tools/viewer/Viewer.cpp", "tools/viewer/Viewer.cpp",
] ]
libs = [] libs = []

View File

@ -64,6 +64,7 @@ tests_sources = [
"$_tests/EGLImageTest.cpp", "$_tests/EGLImageTest.cpp",
"$_tests/EmptyPathTest.cpp", "$_tests/EmptyPathTest.cpp",
"$_tests/EncodeTest.cpp", "$_tests/EncodeTest.cpp",
"$_tests/EncodedInfoTest.cpp",
"$_tests/ExifTest.cpp", "$_tests/ExifTest.cpp",
"$_tests/F16StagesTest.cpp", "$_tests/F16StagesTest.cpp",
"$_tests/FillPathTest.cpp", "$_tests/FillPathTest.cpp",

View File

@ -13,7 +13,6 @@
#include "../private/SkEncodedInfo.h" #include "../private/SkEncodedInfo.h"
#include "SkCodecAnimation.h" #include "SkCodecAnimation.h"
#include "SkColor.h" #include "SkColor.h"
#include "SkColorSpaceXform.h"
#include "SkEncodedImageFormat.h" #include "SkEncodedImageFormat.h"
#include "SkEncodedOrigin.h" #include "SkEncodedOrigin.h"
#include "SkImageInfo.h" #include "SkImageInfo.h"
@ -671,21 +670,9 @@ public:
protected: protected:
const SkEncodedInfo& getEncodedInfo() const { return fEncodedInfo; } const SkEncodedInfo& getEncodedInfo() const { return fEncodedInfo; }
using XformFormat = SkColorSpaceXform::ColorFormat; using XformFormat = skcms_PixelFormat;
SkCodec(int width, SkCodec(SkEncodedInfo&&,
int height,
const SkEncodedInfo&,
XformFormat srcFormat,
std::unique_ptr<SkStream>,
sk_sp<SkColorSpace>,
SkEncodedOrigin = kTopLeft_SkEncodedOrigin);
/**
* Allows the subclass to set the recommended SkImageInfo
*/
SkCodec(const SkEncodedInfo&,
const SkImageInfo&,
XformFormat srcFormat, XformFormat srcFormat,
std::unique_ptr<SkStream>, std::unique_ptr<SkStream>,
SkEncodedOrigin = kTopLeft_SkEncodedOrigin); SkEncodedOrigin = kTopLeft_SkEncodedOrigin);
@ -780,16 +767,14 @@ protected:
virtual int onOutputScanline(int inputScanline) const; virtual int onOutputScanline(int inputScanline) const;
bool initializeColorXform(const SkImageInfo& dstInfo, SkEncodedInfo::Alpha);
// Some classes never need a colorXform e.g. // Some classes never need a colorXform e.g.
// - ICO uses its embedded codec's colorXform // - ICO uses its embedded codec's colorXform
// - WBMP is just Black/White // - WBMP is just Black/White
virtual bool usesColorXform() const { return true; } 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; void applyColorXform(void* dst, const void* src, int count) const;
SkColorSpaceXform* colorXform() const { return fColorXform.get(); } bool colorXform() const { return fXformTime != kNo_XformTime; }
bool xformOnDecode() const { return fXformOnDecode; } bool xformOnDecode() const { return fXformTime == kDecodeRow_XformTime; }
virtual int onGetFrameCount() { virtual int onGetFrameCount() {
return 1; return 1;
@ -813,9 +798,16 @@ private:
SkImageInfo fDstInfo; SkImageInfo fDstInfo;
Options fOptions; Options fOptions;
enum XformTime {
kNo_XformTime,
kPalette_XformTime,
kDecodeRow_XformTime,
};
XformTime fXformTime;
XformFormat fDstXformFormat; // Based on fDstInfo. XformFormat fDstXformFormat; // Based on fDstInfo.
std::unique_ptr<SkColorSpaceXform> fColorXform; skcms_ICCProfile fDstProfile;
bool fXformOnDecode; skcms_AlphaFormat fDstXformAlphaFormat;
// Only meaningful during scanline decodes. // Only meaningful during scanline decodes.
int fCurrScanline; int fCurrScanline;
@ -823,12 +815,15 @@ private:
bool fStartedIncrementalDecode; 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. * Will be called for the appropriate frame, prior to initializing the colorXform.
*/ */
virtual bool conversionSupported(const SkImageInfo& dst, SkColorType srcColor, 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. * Return whether these dimensions are supported as a scale.
* *

View File

@ -8,12 +8,25 @@
#ifndef SkEncodedInfo_DEFINED #ifndef SkEncodedInfo_DEFINED
#define SkEncodedInfo_DEFINED #define SkEncodedInfo_DEFINED
#include "SkData.h"
#include "SkImageInfo.h" #include "SkImageInfo.h"
#include "../../third_party/skcms/skcms.h"
class SkColorSpace;
struct SkEncodedInfo { struct SkEncodedInfo {
public: 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 { enum Alpha {
kOpaque_Alpha, kOpaque_Alpha,
@ -39,6 +52,20 @@ public:
// PNG // PNG
kGrayAlpha_Color, 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 // PNG, GIF, BMP
kPalette_Color, kPalette_Color,
@ -67,7 +94,18 @@ public:
kYCCK_Color, 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 || SkASSERT(1 == bitsPerComponent ||
2 == bitsPerComponent || 2 == bitsPerComponent ||
4 == bitsPerComponent || 4 == bitsPerComponent ||
@ -105,29 +143,51 @@ public:
SkASSERT(kOpaque_Alpha != alpha); SkASSERT(kOpaque_Alpha != alpha);
SkASSERT(8 == bitsPerComponent); SkASSERT(8 == bitsPerComponent);
break; break;
case kXAlpha_Color:
SkASSERT(kUnpremul_Alpha == alpha);
SkASSERT(8 == bitsPerComponent);
break;
case k565_Color:
SkASSERT(kOpaque_Alpha == alpha);
SkASSERT(8 == bitsPerComponent);
break;
default: default:
SkASSERT(false); SkASSERT(false);
break; 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 * Returns a recommended SkImageInfo.
* closest possible match to the encoded info. *
* TODO: Leave this up to the client.
*/ */
SkImageInfo makeImageInfo(int width, int height, sk_sp<SkColorSpace> colorSpace) const { SkImageInfo makeImageInfo() const {
auto ct = kGray_Color == fColor ? kGray_8_SkColorType : auto ct = kGray_Color == fColor ? kGray_8_SkColorType :
kXAlpha_Color == fColor ? kAlpha_8_SkColorType :
k565_Color == fColor ? kRGB_565_SkColorType :
kN32_SkColorType ; kN32_SkColorType ;
auto alpha = kOpaque_Alpha == fAlpha ? kOpaque_SkAlphaType auto alpha = kOpaque_Alpha == fAlpha ? kOpaque_SkAlphaType
: kUnpremul_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));
} }
int width() const { return fWidth; }
int height() const { return fHeight; }
Color color() const { return fColor; } Color color() const { return fColor; }
Alpha alpha() const { return fAlpha; } Alpha alpha() const { return fAlpha; }
bool opaque() const { return fAlpha == kOpaque_Alpha; } 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; } uint8_t bitsPerComponent() const { return fBitsPerComponent; }
@ -135,6 +195,7 @@ public:
switch (fColor) { switch (fColor) {
case kGray_Color: case kGray_Color:
return fBitsPerComponent; return fBitsPerComponent;
case kXAlpha_Color:
case kGrayAlpha_Color: case kGrayAlpha_Color:
return 2 * fBitsPerComponent; return 2 * fBitsPerComponent;
case kPalette_Color: case kPalette_Color:
@ -142,6 +203,7 @@ public:
case kRGB_Color: case kRGB_Color:
case kBGR_Color: case kBGR_Color:
case kYUV_Color: case kYUV_Color:
case k565_Color:
return 3 * fBitsPerComponent; return 3 * fBitsPerComponent;
case kRGBA_Color: case kRGBA_Color:
case kBGRA_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) SkEncodedInfo(SkEncodedInfo&& orig) = default;
: fColor(color) 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) , fAlpha(alpha)
, fBitsPerComponent(bitsPerComponent) , fBitsPerComponent(bitsPerComponent)
, fProfile(std::move(profile))
{} {}
int fWidth;
int fHeight;
Color fColor; Color fColor;
Alpha fAlpha; Alpha fAlpha;
uint8_t fBitsPerComponent; uint8_t fBitsPerComponent;
std::unique_ptr<ICCProfile> fProfile;
}; };
#endif #endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 KiB

View File

@ -9,9 +9,8 @@
SkBmpBaseCodec::~SkBmpBaseCodec() {} SkBmpBaseCodec::~SkBmpBaseCodec() {}
SkBmpBaseCodec::SkBmpBaseCodec(int width, int height, const SkEncodedInfo& info, SkBmpBaseCodec::SkBmpBaseCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
std::unique_ptr<SkStream> stream,
uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder) 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())) , fSrcBuffer(sk_malloc_canfail(this->srcRowBytes()))
{} {}

View File

@ -25,7 +25,7 @@ public:
bool didCreateSrcBuffer() const { return fSrcBuffer != nullptr; } bool didCreateSrcBuffer() const { return fSrcBuffer != nullptr; }
protected: 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); uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder);
uint8_t* srcBuffer() { return reinterpret_cast<uint8_t*>(fSrcBuffer.get()); } uint8_t* srcBuffer() { return reinterpret_cast<uint8_t*>(fSrcBuffer.get()); }

View File

@ -481,8 +481,8 @@ SkCodec::Result SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
SkASSERT(!inIco || nullptr != stream->getMemoryBase()); SkASSERT(!inIco || nullptr != stream->getMemoryBase());
// Set the image info and create a codec. // Set the image info and create a codec.
const SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, bitsPerComponent); auto info = SkEncodedInfo::MakeSRGB(width, height, color, alpha, bitsPerComponent);
codecOut->reset(new SkBmpStandardCodec(width, height, info, codecOut->reset(new SkBmpStandardCodec(std::move(info),
std::unique_ptr<SkStream>(stream), std::unique_ptr<SkStream>(stream),
bitsPerPixel, numColors, bytesPerColor, bitsPerPixel, numColors, bytesPerColor,
offset - bytesRead, rowOrder, isOpaque, offset - bytesRead, rowOrder, isOpaque,
@ -539,8 +539,8 @@ SkCodec::Result SkBmpCodec::ReadHeader(SkStream* stream, bool inIco,
color = SkEncodedInfo::kBGR_Color; color = SkEncodedInfo::kBGR_Color;
alpha = SkEncodedInfo::kOpaque_Alpha; alpha = SkEncodedInfo::kOpaque_Alpha;
} }
const SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8); auto info = SkEncodedInfo::MakeSRGB(width, height, color, alpha, 8);
codecOut->reset(new SkBmpMaskCodec(width, height, info, codecOut->reset(new SkBmpMaskCodec(std::move(info),
std::unique_ptr<SkStream>(stream), bitsPerPixel, std::unique_ptr<SkStream>(stream), bitsPerPixel,
masks.release(), rowOrder)); masks.release(), rowOrder));
return static_cast<SkBmpMaskCodec*>(codecOut->get())->didCreateSrcBuffer() return static_cast<SkBmpMaskCodec*>(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 // 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. // opaque or that we will be able to represent it with a palette.
// For that reason, we always indicate that we are kBGRA. // For that reason, we always indicate that we are kBGRA.
const SkEncodedInfo info = SkEncodedInfo::Make(SkEncodedInfo::kBGRA_Color, auto info = SkEncodedInfo::MakeSRGB(width, height, SkEncodedInfo::kBGRA_Color,
SkEncodedInfo::kBinary_Alpha, 8); SkEncodedInfo::kBinary_Alpha, 8);
codecOut->reset(new SkBmpRLECodec(width, height, info, codecOut->reset(new SkBmpRLECodec(std::move(info),
std::unique_ptr<SkStream>(stream), bitsPerPixel, std::unique_ptr<SkStream>(stream), bitsPerPixel,
numColors, bytesPerColor, offset - bytesRead, numColors, bytesPerColor, offset - bytesRead,
rowOrder)); rowOrder));
@ -600,14 +600,12 @@ std::unique_ptr<SkCodec> SkBmpCodec::MakeFromStream(std::unique_ptr<SkStream> st
return kSuccess == *result ? std::move(codec) : nullptr; return kSuccess == *result ? std::move(codec) : nullptr;
} }
SkBmpCodec::SkBmpCodec(int width, int height, const SkEncodedInfo& info, SkBmpCodec::SkBmpCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
std::unique_ptr<SkStream> stream,
uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder) uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder)
: INHERITED(width, height, info, kXformSrcColorFormat, std::move(stream), : INHERITED(std::move(info), kXformSrcColorFormat, std::move(stream))
SkColorSpace::MakeSRGB())
, fBitsPerPixel(bitsPerPixel) , fBitsPerPixel(bitsPerPixel)
, fRowOrder(rowOrder) , fRowOrder(rowOrder)
, fSrcRowBytes(SkAlign4(compute_row_bytes(width, fBitsPerPixel))) , fSrcRowBytes(SkAlign4(compute_row_bytes(this->getEncodedInfo().width(), fBitsPerPixel)))
, fXformBuffer(nullptr) , fXformBuffer(nullptr)
{} {}

View File

@ -38,7 +38,7 @@ public:
protected: 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); uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder);
SkEncodedImageFormat onGetEncodedFormat() const override { return SkEncodedImageFormat::kBMP; } SkEncodedImageFormat onGetEncodedFormat() const override { return SkEncodedImageFormat::kBMP; }
@ -103,7 +103,7 @@ protected:
* than RGBA. * than RGBA.
*/ */
static constexpr SkColorType kXformSrcColorType = kBGRA_8888_SkColorType; static constexpr SkColorType kXformSrcColorType = kBGRA_8888_SkColorType;
static constexpr auto kXformSrcColorFormat = SkColorSpaceXform::kBGRA_8888_ColorFormat; static constexpr auto kXformSrcColorFormat = skcms_PixelFormat_BGRA_8888;
private: private:

View File

@ -12,11 +12,11 @@
/* /*
* Creates an instance of the decoder * Creates an instance of the decoder
*/ */
SkBmpMaskCodec::SkBmpMaskCodec(int width, int height, const SkEncodedInfo& info, SkBmpMaskCodec::SkBmpMaskCodec(SkEncodedInfo&& info,
std::unique_ptr<SkStream> stream, std::unique_ptr<SkStream> stream,
uint16_t bitsPerPixel, SkMasks* masks, uint16_t bitsPerPixel, SkMasks* masks,
SkCodec::SkScanlineOrder rowOrder) SkCodec::SkScanlineOrder rowOrder)
: INHERITED(width, height, info, std::move(stream), bitsPerPixel, rowOrder) : INHERITED(std::move(info), std::move(stream), bitsPerPixel, rowOrder)
, fMasks(masks) , fMasks(masks)
, fMaskSwizzler(nullptr) , fMaskSwizzler(nullptr)
{} {}

View File

@ -31,7 +31,7 @@ public:
* @param masks color masks for certain bmp formats * @param masks color masks for certain bmp formats
* @param rowOrder indicates whether rows are ordered top-down or bottom-up * @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, uint16_t bitsPerPixel, SkMasks* masks,
SkCodec::SkScanlineOrder rowOrder); SkCodec::SkScanlineOrder rowOrder);

View File

@ -14,12 +14,12 @@
* Creates an instance of the decoder * Creates an instance of the decoder
* Called only by NewFromStream * Called only by NewFromStream
*/ */
SkBmpRLECodec::SkBmpRLECodec(int width, int height, const SkEncodedInfo& info, SkBmpRLECodec::SkBmpRLECodec(SkEncodedInfo&& info,
std::unique_ptr<SkStream> stream, std::unique_ptr<SkStream> stream,
uint16_t bitsPerPixel, uint32_t numColors, uint16_t bitsPerPixel, uint32_t numColors,
uint32_t bytesPerColor, uint32_t offset, uint32_t bytesPerColor, uint32_t offset,
SkCodec::SkScanlineOrder rowOrder) SkCodec::SkScanlineOrder rowOrder)
: INHERITED(width, height, info, std::move(stream), bitsPerPixel, rowOrder) : INHERITED(std::move(info), std::move(stream), bitsPerPixel, rowOrder)
, fColorTable(nullptr) , fColorTable(nullptr)
, fNumColors(numColors) , fNumColors(numColors)
, fBytesPerColor(bytesPerColor) , fBytesPerColor(bytesPerColor)

View File

@ -35,7 +35,7 @@ public:
* headers * headers
* @param rowOrder indicates whether rows are ordered top-down or bottom-up * @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, uint16_t bitsPerPixel, uint32_t numColors, uint32_t bytesPerColor,
uint32_t offset, SkCodec::SkScanlineOrder rowOrder); uint32_t offset, SkCodec::SkScanlineOrder rowOrder);

View File

@ -14,12 +14,12 @@
* Creates an instance of the decoder * Creates an instance of the decoder
* Called only by NewFromStream * Called only by NewFromStream
*/ */
SkBmpStandardCodec::SkBmpStandardCodec(int width, int height, const SkEncodedInfo& info, SkBmpStandardCodec::SkBmpStandardCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
std::unique_ptr<SkStream> stream, uint16_t bitsPerPixel, uint16_t bitsPerPixel, uint32_t numColors,
uint32_t numColors, uint32_t bytesPerColor, uint32_t offset, uint32_t bytesPerColor, uint32_t offset,
SkCodec::SkScanlineOrder rowOrder, SkCodec::SkScanlineOrder rowOrder,
bool isOpaque, bool inIco) bool isOpaque, bool inIco)
: INHERITED(width, height, info, std::move(stream), bitsPerPixel, rowOrder) : INHERITED(std::move(info), std::move(stream), bitsPerPixel, rowOrder)
, fColorTable(nullptr) , fColorTable(nullptr)
, fNumColors(numColors) , fNumColors(numColors)
, fBytesPerColor(bytesPerColor) , fBytesPerColor(bytesPerColor)
@ -146,20 +146,33 @@ SkCodec::Result SkBmpStandardCodec::onGetPixels(const SkImageInfo& dstInfo,
return true; 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) { void SkBmpStandardCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts) {
// In the case of bmp-in-icos, we will report BGRA to the client, // 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. // 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. // However, the swizzler needs to know the actual format of the bmp.
SkEncodedInfo encodedInfo = this->getEncodedInfo(); SkEncodedInfo encodedInfo = this->swizzlerInfo();
if (fInIco) {
if (this->bitsPerPixel() <= 8) {
encodedInfo = SkEncodedInfo::Make(SkEncodedInfo::kPalette_Color,
encodedInfo.alpha(), this->bitsPerPixel());
} else if (this->bitsPerPixel() == 24) {
encodedInfo = SkEncodedInfo::Make(SkEncodedInfo::kBGR_Color,
SkEncodedInfo::kOpaque_Alpha, 8);
}
}
// Get a pointer to the color table if it exists // Get a pointer to the color table if it exists
const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());

View File

@ -39,9 +39,9 @@ public:
* the icp mask, if there is one) * the icp mask, if there is one)
* @param inIco indicates if the bmp is embedded in an ico file * @param inIco indicates if the bmp is embedded in an ico file
*/ */
SkBmpStandardCodec(int width, int height, const SkEncodedInfo& info, SkBmpStandardCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
std::unique_ptr<SkStream> stream, uint16_t bitsPerPixel, uint32_t numColors, uint16_t bitsPerPixel, uint32_t numColors, uint32_t bytesPerColor,
uint32_t bytesPerColor, uint32_t offset, SkCodec::SkScanlineOrder rowOrder, uint32_t offset, SkCodec::SkScanlineOrder rowOrder,
bool isOpaque, bool inIco); bool isOpaque, bool inIco);
protected: protected:
@ -63,12 +63,8 @@ protected:
} }
private: private:
/*
* Creates the color table
*/
bool createColorTable(SkColorType colorType, SkAlphaType alphaType); bool createColorTable(SkColorType colorType, SkAlphaType alphaType);
SkEncodedInfo swizzlerInfo() const;
void initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts); void initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts);
int decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, int decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes,

View File

@ -9,7 +9,6 @@
#include "SkCodec.h" #include "SkCodec.h"
#include "SkCodecPriv.h" #include "SkCodecPriv.h"
#include "SkColorSpace.h" #include "SkColorSpace.h"
#include "SkColorSpaceXformPriv.h"
#include "SkData.h" #include "SkData.h"
#include "SkFrameHolder.h" #include "SkFrameHolder.h"
#include "SkGifCodec.h" #include "SkGifCodec.h"
@ -126,26 +125,10 @@ std::unique_ptr<SkCodec> SkCodec::MakeFromData(sk_sp<SkData> data, SkPngChunkRea
return MakeFromStream(SkMemoryStream::Make(std::move(data)), nullptr, reader); return MakeFromStream(SkMemoryStream::Make(std::move(data)), nullptr, reader);
} }
SkCodec::SkCodec(int width, int height, const SkEncodedInfo& info, SkCodec::SkCodec(SkEncodedInfo&& info, XformFormat srcFormat, std::unique_ptr<SkStream> stream,
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) SkEncodedOrigin origin)
: fEncodedInfo(info) : fEncodedInfo(std::move(info))
, fSrcInfo(imageInfo) , fSrcInfo(fEncodedInfo.makeImageInfo())
, fSrcXformFormat(srcFormat) , fSrcXformFormat(srcFormat)
, fStream(std::move(stream)) , fStream(std::move(stream))
, fNeedsRewind(false) , fNeedsRewind(false)
@ -159,7 +142,7 @@ SkCodec::SkCodec(const SkEncodedInfo& info, const SkImageInfo& imageInfo,
SkCodec::~SkCodec() {} SkCodec::~SkCodec() {}
bool SkCodec::conversionSupported(const SkImageInfo& dst, SkColorType srcColor, bool SkCodec::conversionSupported(const SkImageInfo& dst, SkColorType srcColor,
bool srcIsOpaque, const SkColorSpace* srcCS) const { bool srcIsOpaque, bool needsColorXform) {
if (!valid_alpha(dst.alphaType(), srcIsOpaque)) { if (!valid_alpha(dst.alphaType(), srcIsOpaque)) {
return false; return false;
} }
@ -173,8 +156,8 @@ bool SkCodec::conversionSupported(const SkImageInfo& dst, SkColorType srcColor,
case kRGB_565_SkColorType: case kRGB_565_SkColorType:
return srcIsOpaque; return srcIsOpaque;
case kGray_8_SkColorType: case kGray_8_SkColorType:
return kGray_8_SkColorType == srcColor && srcIsOpaque && SkASSERT(!needsColorXform);
!needs_color_xform(dst, srcCS); return kGray_8_SkColorType == srcColor && srcIsOpaque;
case kAlpha_8_SkColorType: case kAlpha_8_SkColorType:
// conceptually we can convert anything into alpha_8, but we haven't actually coded // 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. // 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: default:
return false; return false;
} }
} }
bool SkCodec::rewindIfNeeded() { bool SkCodec::rewindIfNeeded() {
@ -245,13 +227,8 @@ SkCodec::Result SkCodec::handleFrameIndex(const SkImageInfo& info, void* pixels,
const Options& options) { const Options& options) {
const int index = options.fFrameIndex; const int index = options.fFrameIndex;
if (0 == index) { if (0 == index) {
if (!this->conversionSupported(info, fSrcInfo.colorType(), fEncodedInfo.opaque(), return this->initializeColorXform(info, fEncodedInfo.alpha(), fEncodedInfo.opaque())
fSrcInfo.colorSpace()) ? kSuccess : kInvalidConversion;
|| !this->initializeColorXform(info, fEncodedInfo.alpha()))
{
return kInvalidConversion;
}
return kSuccess;
} }
if (index < 0) { if (index < 0) {
@ -274,11 +251,6 @@ SkCodec::Result SkCodec::handleFrameIndex(const SkImageInfo& info, void* pixels,
const auto* frame = frameHolder->getFrame(index); const auto* frame = frameHolder->getFrame(index);
SkASSERT(frame); SkASSERT(frame);
if (!this->conversionSupported(info, fSrcInfo.colorType(), !frame->hasAlpha(),
fSrcInfo.colorSpace())) {
return kInvalidConversion;
}
const int requiredFrame = frame->getRequiredFrame(); const int requiredFrame = frame->getRequiredFrame();
if (requiredFrame != kNoFrame) { if (requiredFrame != kNoFrame) {
if (options.fPriorFrame != 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, 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) { switch (colorType) {
case kRGBA_8888_SkColorType: case kRGBA_8888_SkColorType:
return SkColorSpaceXform::kRGBA_8888_ColorFormat; *outFormat = skcms_PixelFormat_RGBA_8888;
break;
case kBGRA_8888_SkColorType: case kBGRA_8888_SkColorType:
return SkColorSpaceXform::kBGRA_8888_ColorFormat; *outFormat = skcms_PixelFormat_BGRA_8888;
break;
case kRGB_565_SkColorType: case kRGB_565_SkColorType:
if (forColorTable) {
#ifdef SK_PMCOLOR_IS_RGBA #ifdef SK_PMCOLOR_IS_RGBA
return SkColorSpaceXform::kRGBA_8888_ColorFormat; *outFormat = skcms_PixelFormat_RGBA_8888;
#else #else
return SkColorSpaceXform::kBGRA_8888_ColorFormat; *outFormat = skcms_PixelFormat_BGRA_8888;
#endif #endif
break;
}
*outFormat = skcms_PixelFormat_BGR_565;
break;
case kRGBA_F16_SkColorType:
*outFormat = skcms_PixelFormat_RGBA_hhhh;
break;
default: default:
SkASSERT(false); return false;
return SkColorSpaceXform::kRGBA_8888_ColorFormat; }
return true;
}
bool SkCodec::initializeColorXform(const SkImageInfo& dstInfo, SkEncodedInfo::Alpha encodedAlpha,
bool srcIsOpaque) {
fXformTime = kNo_XformTime;
bool needsColorXform = false;
if (this->usesColorXform() && dstInfo.colorSpace()) {
dstInfo.colorSpace()->toProfile(&fDstProfile);
if (kRGBA_F16_SkColorType == dstInfo.colorType()
|| !skcms_ApproximatelyEqualProfiles(fEncodedInfo.profile(), &fDstProfile) ) {
needsColorXform = true;
if (kGray_8_SkColorType == dstInfo.colorType()) {
return false;
}
} }
} }
bool SkCodec::initializeColorXform(const SkImageInfo& dstInfo, SkEncodedInfo::Alpha encodedAlpha) { if (!this->conversionSupported(dstInfo, fSrcInfo.colorType(), srcIsOpaque, needsColorXform)) {
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; return false;
} }
// We will apply the color xform when reading the color table unless F16 is requested. if (needsColorXform) {
fXformOnDecode = SkEncodedInfo::kPalette_Color != fEncodedInfo.color() fXformTime = SkEncodedInfo::kPalette_Color != fEncodedInfo.color()
|| kRGBA_F16_SkColorType == dstInfo.colorType(); || kRGBA_F16_SkColorType == dstInfo.colorType()
if (fXformOnDecode) { ? kDecodeRow_XformTime : kPalette_XformTime;
fDstXformFormat = select_xform_format(dstInfo.colorType()); 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 { } else {
fDstXformFormat = select_xform_format_ct(dstInfo.colorType()); fDstXformAlphaFormat = skcms_AlphaFormat_Unpremul;
} }
} }
return true; return true;
} }
void SkCodec::applyColorXform(void* dst, const void* src, int count, SkAlphaType at) const {
SkASSERT(fColorXform);
SkAssertResult(fColorXform->apply(fDstXformFormat, dst,
fSrcXformFormat, src,
count, at));
}
void SkCodec::applyColorXform(void* dst, const void* src, int count) const { void SkCodec::applyColorXform(void* dst, const void* src, int count) const {
auto alphaType = select_xform_alpha(fDstInfo.alphaType(), fSrcInfo.alphaType()); const auto* srcProfile = fEncodedInfo.profile();
this->applyColorXform(dst, src, count, alphaType); SkASSERT(srcProfile);
SkAssertResult(skcms_Transform(src, fSrcXformFormat, skcms_AlphaFormat_Unpremul, srcProfile,
dst, fDstXformFormat, fDstXformAlphaFormat, &fDstProfile,
count));
} }
std::vector<SkCodec::FrameInfo> SkCodec::getFrameInfo() { std::vector<SkCodec::FrameInfo> SkCodec::getFrameInfo() {

View File

@ -9,8 +9,6 @@
#define SkCodecPriv_DEFINED #define SkCodecPriv_DEFINED
#include "SkColorData.h" #include "SkColorData.h"
#include "SkColorSpaceXform.h"
#include "SkColorSpaceXformPriv.h"
#include "SkColorTable.h" #include "SkColorTable.h"
#include "SkEncodedInfo.h" #include "SkEncodedInfo.h"
#include "SkEncodedOrigin.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); bool is_orientation_marker(const uint8_t* data, size_t data_length, SkEncodedOrigin* orientation);
#endif // SkCodecPriv_DEFINED #endif // SkCodecPriv_DEFINED

View File

@ -5,4 +5,28 @@
* found in the LICENSE file. * 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))
{}

View File

@ -91,18 +91,9 @@ std::unique_ptr<SkCodec> SkGifCodec::MakeFromStream(std::unique_ptr<SkStream> st
// Use kPalette since Gifs are encoded with a color table. // 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 // 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. // expanding to 8 bits and take advantage of the SkSwizzler to work from 4.
const auto encodedInfo = SkEncodedInfo::Make(SkEncodedInfo::kPalette_Color, alpha, 8); auto encodedInfo = SkEncodedInfo::MakeSRGB(reader->screenWidth(), reader->screenHeight(),
SkEncodedInfo::kPalette_Color, alpha, 8);
// The choice of unpremul versus premul is arbitrary, since all colors are either fully return std::unique_ptr<SkCodec>(new SkGifCodec(std::move(encodedInfo), reader.release()));
// opaque or fully transparent (i.e. kBinary), but we stored the transparent colors as all
// zeroes, which is arguably premultiplied.
const auto alphaType = reader->firstFrameHasAlpha() ? kUnpremul_SkAlphaType
: kOpaque_SkAlphaType;
const auto imageInfo = SkImageInfo::Make(reader->screenWidth(), reader->screenHeight(),
kN32_SkColorType, alphaType,
SkColorSpace::MakeSRGB());
return std::unique_ptr<SkCodec>(new SkGifCodec(encodedInfo, imageInfo, reader.release()));
} }
bool SkGifCodec::onRewind() { bool SkGifCodec::onRewind() {
@ -110,9 +101,8 @@ bool SkGifCodec::onRewind() {
return true; return true;
} }
SkGifCodec::SkGifCodec(const SkEncodedInfo& encodedInfo, const SkImageInfo& imageInfo, SkGifCodec::SkGifCodec(SkEncodedInfo&& encodedInfo, SkGifImageReader* reader)
SkGifImageReader* reader) : INHERITED(std::move(encodedInfo), skcms_PixelFormat_RGBA_8888, nullptr)
: INHERITED(encodedInfo, imageInfo, SkColorSpaceXform::kRGBA_8888_ColorFormat, nullptr)
, fReader(reader) , fReader(reader)
, fTmpBuffer(nullptr) , fTmpBuffer(nullptr)
, fSwizzler(nullptr) , fSwizzler(nullptr)
@ -157,7 +147,6 @@ int SkGifCodec::onGetRepetitionCount() {
} }
static constexpr SkColorType kXformSrcColorType = kRGBA_8888_SkColorType; static constexpr SkColorType kXformSrcColorType = kRGBA_8888_SkColorType;
static constexpr SkAlphaType kXformAlphaType = kUnpremul_SkAlphaType;
void SkGifCodec::initializeColorTable(const SkImageInfo& dstInfo, int frameIndex) { void SkGifCodec::initializeColorTable(const SkImageInfo& dstInfo, int frameIndex) {
SkColorType colorTableColorType = dstInfo.colorType(); SkColorType colorTableColorType = dstInfo.colorType();
@ -173,8 +162,8 @@ void SkGifCodec::initializeColorTable(const SkImageInfo& dstInfo, int frameIndex
fCurrColorTable.reset(new SkColorTable(&color, 1)); fCurrColorTable.reset(new SkColorTable(&color, 1));
} else if (this->colorXform() && !this->xformOnDecode()) { } else if (this->colorXform() && !this->xformOnDecode()) {
SkPMColor dstColors[256]; SkPMColor dstColors[256];
this->applyColorXform(dstColors, currColorTable->readColors(), currColorTable->count(), this->applyColorXform(dstColors, currColorTable->readColors(),
kXformAlphaType); currColorTable->count());
fCurrColorTable.reset(new SkColorTable(dstColors, currColorTable->count())); fCurrColorTable.reset(new SkColorTable(dstColors, currColorTable->count()));
} else { } else {
fCurrColorTable = std::move(currColorTable); fCurrColorTable = std::move(currColorTable);
@ -402,7 +391,7 @@ void SkGifCodec::applyXformRow(const SkImageInfo& dstInfo, void* dst, const uint
fSwizzler->swizzle(fXformBuffer.get(), src); fSwizzler->swizzle(fXformBuffer.get(), src);
const int xformWidth = get_scaled_dimension(dstInfo.width(), fSwizzler->sampleX()); const int xformWidth = get_scaled_dimension(dstInfo.width(), fSwizzler->sampleX());
this->applyColorXform(dst, fXformBuffer.get(), xformWidth, kXformAlphaType); this->applyColorXform(dst, fXformBuffer.get(), xformWidth);
} else { } else {
fSwizzler->swizzle(dst, src); fSwizzler->swizzle(dst, src);
} }

View File

@ -126,7 +126,7 @@ private:
* Called only by NewFromStream * Called only by NewFromStream
* Takes ownership of the SkGifImageReader * Takes ownership of the SkGifImageReader
*/ */
SkGifCodec(const SkEncodedInfo&, const SkImageInfo&, SkGifImageReader*); SkGifCodec(SkEncodedInfo&&, SkGifImageReader*);
std::unique_ptr<SkGifImageReader> fReader; std::unique_ptr<SkGifImageReader> fReader;
std::unique_ptr<uint8_t[]> fTmpBuffer; std::unique_ptr<uint8_t[]> fTmpBuffer;

View File

@ -133,40 +133,37 @@ std::unique_ptr<SkCodec> SkHeifCodec::MakeFromStream(
return nullptr; return nullptr;
} }
SkEncodedInfo info = SkEncodedInfo::Make( std::unique_ptr<SkEncodedInfo::ICCProfile> profile = nullptr;
SkEncodedInfo::kYUV_Color, SkEncodedInfo::kOpaque_Alpha, 8); 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); 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; *result = kSuccess;
return std::unique_ptr<SkCodec>(new SkHeifCodec(frameInfo.mWidth, frameInfo.mHeight, return std::unique_ptr<SkCodec>(new SkHeifCodec(std::move(info), heifDecoder.release(),
info, heifDecoder.release(), std::move(colorSpace), orientation)); orientation));
} }
SkHeifCodec::SkHeifCodec(int width, int height, const SkEncodedInfo& info, SkHeifCodec::SkHeifCodec(SkEncodedInfo&& info, HeifDecoder* heifDecoder, SkEncodedOrigin origin)
HeifDecoder* heifDecoder, sk_sp<SkColorSpace> colorSpace, SkEncodedOrigin origin) : INHERITED(std::move(info), skcms_PixelFormat_RGBA_8888, nullptr, origin)
: INHERITED(width, height, info, SkColorSpaceXform::kRGBA_8888_ColorFormat,
nullptr, std::move(colorSpace), origin)
, fHeifDecoder(heifDecoder) , fHeifDecoder(heifDecoder)
, fSwizzleSrcRow(nullptr) , fSwizzleSrcRow(nullptr)
, fColorXformSrcRow(nullptr) , fColorXformSrcRow(nullptr)
{} {}
/*
* Checks if the conversion between the input image and the requested output bool SkHeifCodec::conversionSupported(const SkImageInfo& dstInfo, SkColorType /*srcColorType*/,
* image has been implemented bool srcIsOpaque, bool needsColorXform) {
* Sets the output color format SkASSERT(srcIsOpaque);
*/
bool SkHeifCodec::setOutputColorFormat(const SkImageInfo& dstInfo) {
if (kUnknown_SkAlphaType == dstInfo.alphaType()) { if (kUnknown_SkAlphaType == dstInfo.alphaType()) {
return false; return false;
} }
@ -184,14 +181,14 @@ bool SkHeifCodec::setOutputColorFormat(const SkImageInfo& dstInfo) {
return fHeifDecoder->setOutputColor(kHeifColorFormat_BGRA_8888); return fHeifDecoder->setOutputColor(kHeifColorFormat_BGRA_8888);
case kRGB_565_SkColorType: case kRGB_565_SkColorType:
if (this->colorXform()) { if (needsColorXform) {
return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888); return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
} else { } else {
return fHeifDecoder->setOutputColor(kHeifColorFormat_RGB565); return fHeifDecoder->setOutputColor(kHeifColorFormat_RGB565);
} }
case kRGBA_F16_SkColorType: case kRGBA_F16_SkColorType:
SkASSERT(this->colorXform()); SkASSERT(needsColorXform);
return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888); return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
default: default:
@ -240,7 +237,7 @@ int SkHeifCodec::readRows(const SkImageInfo& dstInfo, void* dst, size_t rowBytes
} }
if (this->colorXform()) { if (this->colorXform()) {
this->applyColorXform(dst, swizzleDst, dstWidth, kOpaque_SkAlphaType); this->applyColorXform(dst, swizzleDst, dstWidth);
dst = SkTAddOffset<void>(dst, rowBytes); dst = SkTAddOffset<void>(dst, rowBytes);
} }
@ -265,11 +262,6 @@ SkCodec::Result SkHeifCodec::onGetPixels(const SkImageInfo& dstInfo,
return kUnimplemented; 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)) { if (!fHeifDecoder->decode(&fFrameInfo)) {
return kInvalidInput; return kInvalidInput;
} }
@ -313,15 +305,13 @@ void SkHeifCodec::allocateStorage(const SkImageInfo& dstInfo) {
void SkHeifCodec::initializeSwizzler( void SkHeifCodec::initializeSwizzler(
const SkImageInfo& dstInfo, const Options& options) { const SkImageInfo& dstInfo, const Options& options) {
SkEncodedInfo swizzlerInfo = this->getEncodedInfo();
SkImageInfo swizzlerDstInfo = dstInfo; SkImageInfo swizzlerDstInfo = dstInfo;
if (this->colorXform()) { if (this->colorXform()) {
// The color xform will be expecting RGBA 8888 input. // The color xform will be expecting RGBA 8888 input.
swizzlerDstInfo = swizzlerDstInfo.makeColorType(kRGBA_8888_SkColorType); swizzlerDstInfo = swizzlerDstInfo.makeColorType(kRGBA_8888_SkColorType);
} }
fSwizzler.reset(SkSwizzler::CreateSwizzler(swizzlerInfo, nullptr, fSwizzler.reset(SkSwizzler::CreateSwizzler(this->getEncodedInfo(), nullptr,
swizzlerDstInfo, options, nullptr, true)); swizzlerDstInfo, options, nullptr, true));
SkASSERT(fSwizzler); SkASSERT(fSwizzler);
} }
@ -339,11 +329,6 @@ SkSampler* SkHeifCodec::getSampler(bool createIfNecessary) {
SkCodec::Result SkHeifCodec::onStartScanlineDecode( SkCodec::Result SkHeifCodec::onStartScanlineDecode(
const SkImageInfo& dstInfo, const Options& options) { 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. // 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, // If the heif image has tiles, we could potentially do this much faster,
// but the tile configuration needs to be retrieved from the metadata. // but the tile configuration needs to be retrieved from the metadata.

View File

@ -9,8 +9,6 @@
#define SkHeifCodec_DEFINED #define SkHeifCodec_DEFINED
#include "SkCodec.h" #include "SkCodec.h"
#include "SkColorSpace.h"
#include "SkColorSpaceXform.h"
#include "SkEncodedOrigin.h" #include "SkEncodedOrigin.h"
#include "SkImageInfo.h" #include "SkImageInfo.h"
#include "SkSwizzler.h" #include "SkSwizzler.h"
@ -43,27 +41,14 @@ protected:
return SkEncodedImageFormat::kHEIF; return SkEncodedImageFormat::kHEIF;
} }
bool conversionSupported(const SkImageInfo&, SkColorType, bool, bool conversionSupported(const SkImageInfo&, SkColorType, bool, bool) override;
const SkColorSpace*) const override {
// This class checks for conversion after creating colorXform.
return true;
}
private: private:
/* /*
* Creates an instance of the decoder * Creates an instance of the decoder
* Called only by NewFromStream * Called only by NewFromStream
*/ */
SkHeifCodec(int width, int height, const SkEncodedInfo&, SkHeifCodec(SkEncodedInfo&&, HeifDecoder*, SkEncodedOrigin);
HeifDecoder*, sk_sp<SkColorSpace>, SkEncodedOrigin);
/*
* Checks if the conversion between the input image and the requested output
* image has been implemented.
*
* Sets the output color format.
*/
bool setOutputColorFormat(const SkImageInfo& dst);
void initializeSwizzler(const SkImageInfo& dstInfo, const Options& options); void initializeSwizzler(const SkImageInfo& dstInfo, const Options& options);
void allocateStorage(const SkImageInfo& dstInfo); void allocateStorage(const SkImageInfo& dstInfo);

View File

@ -179,29 +179,19 @@ std::unique_ptr<SkCodec> SkIcoCodec::MakeFromStream(std::unique_ptr<SkStream> st
maxIndex = i; maxIndex = i;
} }
} }
int width = codecs->operator[](maxIndex)->getInfo().width();
int height = codecs->operator[](maxIndex)->getInfo().height(); auto maxInfo = codecs->operator[](maxIndex)->getEncodedInfo().copy();
SkEncodedInfo info = codecs->operator[](maxIndex)->getEncodedInfo();
SkColorSpace* colorSpace = codecs->operator[](maxIndex)->getInfo().colorSpace();
*result = kSuccess; *result = kSuccess;
// The original stream is no longer needed, because the embedded codecs own their // The original stream is no longer needed, because the embedded codecs own their
// own streams. // own streams.
return std::unique_ptr<SkCodec>(new SkIcoCodec(width, height, info, codecs.release(), return std::unique_ptr<SkCodec>(new SkIcoCodec(std::move(maxInfo), codecs.release()));
sk_ref_sp(colorSpace)));
} }
/* SkIcoCodec::SkIcoCodec(SkEncodedInfo&& info, SkTArray<std::unique_ptr<SkCodec>, true>* codecs)
* Creates an instance of the decoder // The source skcms_PixelFormat will not be used. The embedded
* Called only by NewFromStream
*/
SkIcoCodec::SkIcoCodec(int width, int height, const SkEncodedInfo& info,
SkTArray<std::unique_ptr<SkCodec>, true>* codecs,
sk_sp<SkColorSpace> colorSpace)
// The source SkColorSpaceXform::ColorFormat will not be used. The embedded
// codec's will be used instead. // codec's will be used instead.
: INHERITED(width, height, info, SkColorSpaceXform::ColorFormat(), nullptr, : INHERITED(std::move(info), skcms_PixelFormat(), nullptr)
std::move(colorSpace))
, fEmbeddedCodecs(codecs) , fEmbeddedCodecs(codecs)
, fCurrCodec(nullptr) , fCurrCodec(nullptr)
{} {}

View File

@ -48,8 +48,7 @@ protected:
SkScanlineOrder onGetScanlineOrder() const override; SkScanlineOrder onGetScanlineOrder() const override;
bool conversionSupported(const SkImageInfo&, SkColorType, bool, bool conversionSupported(const SkImageInfo&, SkColorType, bool, bool) override {
const SkColorSpace*) const override {
// This will be checked by the embedded codec. // This will be checked by the embedded codec.
return true; return true;
} }
@ -87,8 +86,7 @@ private:
* Constructor called by NewFromStream * Constructor called by NewFromStream
* @param embeddedCodecs codecs for the embedded images, takes ownership * @param embeddedCodecs codecs for the embedded images, takes ownership
*/ */
SkIcoCodec(int width, int height, const SkEncodedInfo& info, SkIcoCodec(SkEncodedInfo&& info, SkTArray<std::unique_ptr<SkCodec>, true>* embeddedCodecs);
SkTArray<std::unique_ptr<SkCodec>, true>* embeddedCodecs, sk_sp<SkColorSpace> colorSpace);
std::unique_ptr<SkTArray<std::unique_ptr<SkCodec>, true>> fEmbeddedCodecs; std::unique_ptr<SkTArray<std::unique_ptr<SkCodec>, true>> fEmbeddedCodecs;

View File

@ -10,6 +10,7 @@
#include "SkCodec.h" #include "SkCodec.h"
#include "SkCodecPriv.h" #include "SkCodecPriv.h"
#include "SkColorData.h" #include "SkColorData.h"
#include "SkColorSpace.h"
#include "SkJpegDecoderMgr.h" #include "SkJpegDecoderMgr.h"
#include "SkJpegInfo.h" #include "SkJpegInfo.h"
#include "SkStream.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. * (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. * (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. // Note that 256 will be enough storage space since each markerIndex is stored in 8-bits.
jpeg_marker_struct* markerSequence[256]; jpeg_marker_struct* markerSequence[256];
memset(markerSequence, 0, sizeof(markerSequence)); memset(markerSequence, 0, sizeof(markerSequence));
@ -191,11 +193,12 @@ static sk_sp<SkColorSpace> read_color_space(jpeg_decompress_struct* dinfo) {
dst = SkTAddOffset<void>(dst, bytes); 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, 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 // Create a JpegDecoderMgr to own all of the decompress information
std::unique_ptr<JpegDecoderMgr> decoderMgr(new JpegDecoderMgr(stream)); std::unique_ptr<JpegDecoderMgr> decoderMgr(new JpegDecoderMgr(stream));
@ -208,17 +211,18 @@ SkCodec::Result SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut,
// Initialize the decompress info and the source manager // Initialize the decompress info and the source manager
decoderMgr->init(); decoderMgr->init();
auto* dinfo = decoderMgr->dinfo();
// Instruct jpeg library to save the markers that we care about. Since // Instruct jpeg library to save the markers that we care about. Since
// the orientation and color profile will not change, we can skip this // the orientation and color profile will not change, we can skip this
// step on rewinds. // step on rewinds.
if (codecOut) { if (codecOut) {
jpeg_save_markers(decoderMgr->dinfo(), kExifMarker, 0xFFFF); jpeg_save_markers(dinfo, kExifMarker, 0xFFFF);
jpeg_save_markers(decoderMgr->dinfo(), kICCMarker, 0xFFFF); jpeg_save_markers(dinfo, kICCMarker, 0xFFFF);
} }
// Read the jpeg header // Read the jpeg header
switch (jpeg_read_header(decoderMgr->dinfo(), true)) { switch (jpeg_read_header(dinfo, true)) {
case JPEG_HEADER_OK: case JPEG_HEADER_OK:
break; break;
case JPEG_SUSPENDED: case JPEG_SUSPENDED:
@ -234,42 +238,41 @@ SkCodec::Result SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut,
return kInvalidInput; return kInvalidInput;
} }
// Create image info object and the codec SkEncodedOrigin orientation = get_exif_orientation(dinfo);
SkEncodedInfo info = SkEncodedInfo::Make(color, SkEncodedInfo::kOpaque_Alpha, 8); auto profile = read_color_profile(dinfo);
if (profile) {
SkEncodedOrigin orientation = get_exif_orientation(decoderMgr->dinfo()); auto type = profile->profile()->data_color_space;
sk_sp<SkColorSpace> colorSpace = read_color_space(decoderMgr->dinfo());
if (colorSpace) {
switch (decoderMgr->dinfo()->jpeg_color_space) { switch (decoderMgr->dinfo()->jpeg_color_space) {
case JCS_CMYK: case JCS_CMYK:
case JCS_YCCK: case JCS_YCCK:
if (colorSpace->type() != SkColorSpace::kCMYK_Type) { if (type != skcms_Signature_CMYK) {
colorSpace = nullptr; profile = nullptr;
} }
break; break;
case JCS_GRAYSCALE: case JCS_GRAYSCALE:
if (colorSpace->type() != SkColorSpace::kGray_Type && if (type != skcms_Signature_Gray &&
colorSpace->type() != SkColorSpace::kRGB_Type) type != skcms_Signature_RGB)
{ {
colorSpace = nullptr; profile = nullptr;
} }
break; break;
default: default:
if (colorSpace->type() != SkColorSpace::kRGB_Type) { if (type != skcms_Signature_RGB) {
colorSpace = nullptr; profile = nullptr;
} }
break; break;
} }
} }
if (!colorSpace) { if (!profile) {
colorSpace = defaultColorSpace; profile = std::move(defaultColorProfile);
} }
const int width = decoderMgr->dinfo()->image_width; SkEncodedInfo info = SkEncodedInfo::Make(dinfo->image_width, dinfo->image_height,
const int height = decoderMgr->dinfo()->image_height; color, SkEncodedInfo::kOpaque_Alpha, 8,
SkJpegCodec* codec = new SkJpegCodec(width, height, info, std::unique_ptr<SkStream>(stream), std::move(profile));
decoderMgr.release(), std::move(colorSpace),
orientation); SkJpegCodec* codec = new SkJpegCodec(std::move(info), std::unique_ptr<SkStream>(stream),
decoderMgr.release(), orientation);
*codecOut = codec; *codecOut = codec;
} else { } else {
SkASSERT(nullptr != decoderMgrOut); SkASSERT(nullptr != decoderMgrOut);
@ -280,14 +283,15 @@ SkCodec::Result SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut,
std::unique_ptr<SkCodec> SkJpegCodec::MakeFromStream(std::unique_ptr<SkStream> stream, std::unique_ptr<SkCodec> SkJpegCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
Result* result) { 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, std::unique_ptr<SkCodec> SkJpegCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
Result* result, Result* result, std::unique_ptr<SkEncodedInfo::ICCProfile> defaultColorProfile) {
sk_sp<SkColorSpace> defaultColorSpace) {
SkCodec* codec = nullptr; SkCodec* codec = nullptr;
*result = ReadHeader(stream.get(), &codec, nullptr, std::move(defaultColorSpace)); *result = ReadHeader(stream.get(), &codec, nullptr, std::move(defaultColorProfile));
if (kSuccess == *result) { if (kSuccess == *result) {
// Codec has taken ownership of the stream, we do not need to delete it // Codec has taken ownership of the stream, we do not need to delete it
SkASSERT(codec); SkASSERT(codec);
@ -297,11 +301,10 @@ std::unique_ptr<SkCodec> SkJpegCodec::MakeFromStream(std::unique_ptr<SkStream> s
return nullptr; return nullptr;
} }
SkJpegCodec::SkJpegCodec(int width, int height, const SkEncodedInfo& info, SkJpegCodec::SkJpegCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
std::unique_ptr<SkStream> stream, JpegDecoderMgr* decoderMgr, JpegDecoderMgr* decoderMgr, SkEncodedOrigin origin)
sk_sp<SkColorSpace> colorSpace, SkEncodedOrigin origin) : INHERITED(std::move(info), skcms_PixelFormat_RGBA_8888, std::move(stream),
: INHERITED(width, height, info, SkColorSpaceXform::kRGBA_8888_ColorFormat, std::move(stream), origin)
std::move(colorSpace), origin)
, fDecoderMgr(decoderMgr) , fDecoderMgr(decoderMgr)
, fReadyState(decoderMgr->dinfo()->global_state) , fReadyState(decoderMgr->dinfo()->global_state)
, fSwizzleSrcRow(nullptr) , fSwizzleSrcRow(nullptr)
@ -386,12 +389,10 @@ bool SkJpegCodec::onRewind() {
return true; return true;
} }
/* bool SkJpegCodec::conversionSupported(const SkImageInfo& dstInfo, SkColorType srcCT,
* Checks if the conversion between the input image and the requested output bool srcIsOpaque, bool needsColorXform) {
* image has been implemented SkASSERT(srcIsOpaque);
* Sets the output color space
*/
bool SkJpegCodec::setOutputColorSpace(const SkImageInfo& dstInfo) {
if (kUnknown_SkAlphaType == dstInfo.alphaType()) { if (kUnknown_SkAlphaType == dstInfo.alphaType()) {
return false; return false;
} }
@ -409,7 +410,7 @@ bool SkJpegCodec::setOutputColorSpace(const SkImageInfo& dstInfo) {
fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA; fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA;
break; break;
case kBGRA_8888_SkColorType: case kBGRA_8888_SkColorType:
if (this->colorXform()) { if (needsColorXform) {
// Always using RGBA as the input format for color xforms makes the // Always using RGBA as the input format for color xforms makes the
// implementation a little simpler. // implementation a little simpler.
fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA; fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA;
@ -418,7 +419,7 @@ bool SkJpegCodec::setOutputColorSpace(const SkImageInfo& dstInfo) {
} }
break; break;
case kRGB_565_SkColorType: case kRGB_565_SkColorType:
if (this->colorXform()) { if (needsColorXform) {
fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA; fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA;
} else { } else {
fDecoderMgr->dinfo()->dither_mode = JDITHER_NONE; fDecoderMgr->dinfo()->dither_mode = JDITHER_NONE;
@ -426,14 +427,15 @@ bool SkJpegCodec::setOutputColorSpace(const SkImageInfo& dstInfo) {
} }
break; break;
case kGray_8_SkColorType: case kGray_8_SkColorType:
if (this->colorXform() || JCS_GRAYSCALE != encodedColorType) { SkASSERT(!needsColorXform);
if (JCS_GRAYSCALE != encodedColorType) {
return false; return false;
} }
fDecoderMgr->dinfo()->out_color_space = JCS_GRAYSCALE; fDecoderMgr->dinfo()->out_color_space = JCS_GRAYSCALE;
break; break;
case kRGBA_F16_SkColorType: case kRGBA_F16_SkColorType:
SkASSERT(this->colorXform()); SkASSERT(needsColorXform);
fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA; fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA;
break; break;
default: default:
@ -539,7 +541,7 @@ int SkJpegCodec::readRows(const SkImageInfo& dstInfo, void* dst, size_t rowBytes
} }
if (this->colorXform()) { if (this->colorXform()) {
this->applyColorXform(dst, swizzleDst, dstWidth, kOpaque_SkAlphaType); this->applyColorXform(dst, swizzleDst, dstWidth);
dst = SkTAddOffset<void>(dst, rowBytes); dst = SkTAddOffset<void>(dst, rowBytes);
} }
@ -587,11 +589,6 @@ SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo,
return fDecoderMgr->returnFailure("setjmp", kInvalidInput); 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)) { if (!jpeg_start_decompress(dinfo)) {
return fDecoderMgr->returnFailure("startDecompress", kInvalidInput); 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, void SkJpegCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& options,
bool needsCMYKToRGB) { bool needsCMYKToRGB) {
SkEncodedInfo swizzlerInfo = this->getEncodedInfo(); SkEncodedInfo swizzlerInfo = make_info(this->getEncodedInfo(), needsCMYKToRGB);
if (needsCMYKToRGB) {
swizzlerInfo = SkEncodedInfo::Make(SkEncodedInfo::kInvertedCMYK_Color,
swizzlerInfo.alpha(),
swizzlerInfo.bitsPerComponent());
}
Options swizzlerOptions = options; Options swizzlerOptions = options;
if (options.fSubset) { if (options.fSubset) {
@ -693,11 +692,6 @@ SkCodec::Result SkJpegCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
return kInvalidInput; 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())) { if (!jpeg_start_decompress(fDecoderMgr->dinfo())) {
SkCodecPrintf("start decompress failed\n"); SkCodecPrintf("start decompress failed\n");
return kInvalidInput; return kInvalidInput;

View File

@ -9,14 +9,13 @@
#define SkJpegCodec_DEFINED #define SkJpegCodec_DEFINED
#include "SkCodec.h" #include "SkCodec.h"
#include "SkColorSpace.h"
#include "SkColorSpaceXform.h"
#include "SkImageInfo.h" #include "SkImageInfo.h"
#include "SkSwizzler.h" #include "SkSwizzler.h"
#include "SkStream.h" #include "SkStream.h"
#include "SkTemplates.h" #include "SkTemplates.h"
class JpegDecoderMgr; class JpegDecoderMgr;
class SkColorSpace;
/* /*
* *
@ -58,19 +57,14 @@ protected:
bool onDimensionsSupported(const SkISize&) override; bool onDimensionsSupported(const SkISize&) override;
bool conversionSupported(const SkImageInfo&, SkColorType, bool, bool conversionSupported(const SkImageInfo&, SkColorType, bool, bool) override;
const SkColorSpace*) const override {
// This class checks for conversion after creating colorXform.
return true;
}
private: 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*, 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. * 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. * codecOut will take ownership of it in the case where we created a codec.
* Ownership is unchanged when we set decoderMgrOut. * Ownership is unchanged when we set decoderMgrOut.
* *
* @param defaultColorSpace * @param defaultColorProfile
* If the jpeg does not have an embedded color space, the image data should * If the jpeg does not have an embedded color profile, the image data should
* be tagged with this color space. * be tagged with this color profile.
*/ */
static Result ReadHeader(SkStream* stream, SkCodec** codecOut, 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 * Creates an instance of the decoder
@ -106,16 +101,8 @@ private:
* @param decoderMgr holds decompress struct, src manager, and error manager * @param decoderMgr holds decompress struct, src manager, and error manager
* takes ownership * takes ownership
*/ */
SkJpegCodec(int width, int height, const SkEncodedInfo& info, std::unique_ptr<SkStream> stream, SkJpegCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
JpegDecoderMgr* decoderMgr, sk_sp<SkColorSpace> colorSpace, SkEncodedOrigin origin); JpegDecoderMgr* decoderMgr, SkEncodedOrigin origin);
/*
* Checks if the conversion between the input image and the requested output
* image has been implemented.
*
* Sets the output color space.
*/
bool setOutputColorSpace(const SkImageInfo& dst);
void initializeSwizzler(const SkImageInfo& dstInfo, const Options& options, void initializeSwizzler(const SkImageInfo& dstInfo, const Options& options,
bool needsCMYKToRGB); bool needsCMYKToRGB);

View File

@ -9,7 +9,6 @@
#include "SkCodecPriv.h" #include "SkCodecPriv.h"
#include "SkColorData.h" #include "SkColorData.h"
#include "SkColorSpace.h" #include "SkColorSpace.h"
#include "SkColorSpacePriv.h"
#include "SkColorTable.h" #include "SkColorTable.h"
#include "SkMacros.h" #include "SkMacros.h"
#include "SkMath.h" #include "SkMath.h"
@ -248,6 +247,10 @@ bool SkPngCodec::processData() {
static constexpr SkColorType kXformSrcColorType = kRGBA_8888_SkColorType; 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. // Note: SkColorTable claims to store SkPMColors, which is not necessarily the case here.
bool SkPngCodec::createColorTable(const SkImageInfo& dstInfo) { bool SkPngCodec::createColorTable(const SkImageInfo& dstInfo) {
@ -265,10 +268,7 @@ bool SkPngCodec::createColorTable(const SkImageInfo& dstInfo) {
png_bytep alphas; png_bytep alphas;
int numColorsWithAlpha = 0; int numColorsWithAlpha = 0;
if (png_get_tRNS(fPng_ptr, fInfo_ptr, &alphas, &numColorsWithAlpha, nullptr)) { if (png_get_tRNS(fPng_ptr, fInfo_ptr, &alphas, &numColorsWithAlpha, nullptr)) {
// If we are performing a color xform, it will handle the premultiply. Otherwise, bool premultiply = needs_premul(dstInfo.alphaType(), this->getEncodedInfo().alpha());
// we'll do it here.
bool premultiply = !this->colorXform() && needs_premul(dstInfo.alphaType(),
this->getEncodedInfo().alpha());
// Choose which function to use to create the color table. If the final destination's // 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. // 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 #endif // LIBPNG >= 1.6
// Returns a colorSpace object that represents any color space information in // If there is no color profile information, it will use sRGB.
// the encoded data. If the encoded data contains an invalid/unsupported color space, std::unique_ptr<SkEncodedInfo::ICCProfile> read_color_profile(png_structp png_ptr,
// this will return NULL. If there is no color space information, it will guess sRGB png_infop info_ptr) {
sk_sp<SkColorSpace> read_color_space(png_structp png_ptr, png_infop info_ptr) {
#if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 6) #if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 6)
// First check for an ICC profile // First check for an ICC profile
png_bytep profile; png_bytep profile;
png_uint_32 length; png_uint_32 length;
@ -362,74 +360,70 @@ sk_sp<SkColorSpace> read_color_space(png_structp png_ptr, png_infop info_ptr) {
int compression; int compression;
if (PNG_INFO_iCCP == png_get_iCCP(png_ptr, info_ptr, &name, &compression, &profile, if (PNG_INFO_iCCP == png_get_iCCP(png_ptr, info_ptr, &name, &compression, &profile,
&length)) { &length)) {
return SkColorSpace::MakeICC(profile, length); auto data = SkData::MakeWithCopy(profile, length);
return SkEncodedInfo::ICCProfile::Make(std::move(data));
} }
// Second, check for sRGB. // 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)) { if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
// sRGB chunks also store a rendering intent: Absolute, Relative, // sRGB chunks also store a rendering intent: Absolute, Relative,
// Perceptual, and Saturation. // Perceptual, and Saturation.
// FIXME (msarett): Extract this information from the sRGB chunk once // FIXME (msarett): Extract this information from the sRGB chunk once
// we are able to handle this information in // we are able to handle this information in
// SkColorSpace. // SkColorSpace.
return SkColorSpace::MakeSRGB(); return SkEncodedInfo::ICCProfile::MakeSRGB();
} }
// Default to SRGB gamut.
skcms_Matrix3x3 toXYZD50 = skcms_sRGB_profile()->toXYZD50;
// Next, check for chromaticities. // Next, check for chromaticities.
png_fixed_point chrm[8]; png_fixed_point chrm[8];
png_fixed_point gamma; png_fixed_point gamma;
if (png_get_cHRM_fixed(png_ptr, info_ptr, &chrm[0], &chrm[1], &chrm[2], &chrm[3], &chrm[4], 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])) &chrm[5], &chrm[6], &chrm[7]))
{ {
SkColorSpacePrimaries primaries; float rx = png_fixed_point_to_float(chrm[2]);
primaries.fRX = png_fixed_point_to_float(chrm[2]); float ry = png_fixed_point_to_float(chrm[3]);
primaries.fRY = png_fixed_point_to_float(chrm[3]); float gx = png_fixed_point_to_float(chrm[4]);
primaries.fGX = png_fixed_point_to_float(chrm[4]); float gy = png_fixed_point_to_float(chrm[5]);
primaries.fGY = png_fixed_point_to_float(chrm[5]); float bx = png_fixed_point_to_float(chrm[6]);
primaries.fBX = png_fixed_point_to_float(chrm[6]); float by = png_fixed_point_to_float(chrm[7]);
primaries.fBY = png_fixed_point_to_float(chrm[7]); float wx = png_fixed_point_to_float(chrm[0]);
primaries.fWX = png_fixed_point_to_float(chrm[0]); float wy = png_fixed_point_to_float(chrm[1]);
primaries.fWY = png_fixed_point_to_float(chrm[1]);
SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor); skcms_Matrix3x3 tmp;
if (!primaries.toXYZD50(&toXYZD50)) { if (skcms_PrimariesToXYZD50(rx, ry, gx, gy, bx, by, wx, wy, &tmp)) {
toXYZD50.set3x3RowMajorf(gSRGB_toXYZD50); 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)) { if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) {
SkColorSpaceTransferFn fn; fn.a = 1.0f;
fn.fA = 1.0f; fn.b = fn.c = fn.d = fn.e = fn.f = 0.0f;
fn.fB = fn.fC = fn.fD = fn.fE = fn.fF = 0.0f; fn.g = png_inverted_fixed_point_to_float(gamma);
fn.fG = png_inverted_fixed_point_to_float(gamma); } else {
return SkColorSpace::MakeRGB(fn, toXYZD50);
}
// Default to sRGB gamma if the image has color space information, // Default to sRGB gamma if the image has color space information,
// but does not specify gamma. // 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. skcms_ICCProfile skcmsProfile;
if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) { skcms_Init(&skcmsProfile);
SkColorSpaceTransferFn fn; skcms_SetTransferFunction(&skcmsProfile, &fn);
fn.fA = 1.0f; skcms_SetXYZD50(&skcmsProfile, &toXYZD50);
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);
}
return SkEncodedInfo::ICCProfile::Make(skcmsProfile);
#else // LIBPNG >= 1.6
return SkEncodedInfo::ICCProfile::MakeSRGB();
#endif // LIBPNG >= 1.6 #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) { 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. // We use kRGB and kRGBA formats because color PNGs are always RGB or RGBA.
if (16 == info.bitsPerComponent()) { if (16 == info.bitsPerComponent()) {
if (SkEncodedInfo::kRGBA_Color == info.color()) { if (SkEncodedInfo::kRGBA_Color == info.color()) {
return SkColorSpaceXform::kRGBA_U16_BE_ColorFormat; return skcms_PixelFormat_RGBA_16161616;
} else if (SkEncodedInfo::kRGB_Color == info.color()) { } 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) { void SkPngCodec::applyXformRow(void* dst, const void* src) {
@ -484,10 +478,9 @@ void SkPngCodec::applyXformRow(void* dst, const void* src) {
class SkPngNormalDecoder : public SkPngCodec { class SkPngNormalDecoder : public SkPngCodec {
public: public:
SkPngNormalDecoder(const SkEncodedInfo& info, const SkImageInfo& imageInfo, SkPngNormalDecoder(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
std::unique_ptr<SkStream> stream, SkPngChunkReader* reader, SkPngChunkReader* reader, png_structp png_ptr, png_infop info_ptr, int bitDepth)
png_structp png_ptr, png_infop info_ptr, int bitDepth) : INHERITED(std::move(info), std::move(stream), reader, png_ptr, info_ptr, bitDepth)
: INHERITED(info, imageInfo, std::move(stream), reader, png_ptr, info_ptr, bitDepth)
, fRowsWrittenToOutput(0) , fRowsWrittenToOutput(0)
, fDst(nullptr) , fDst(nullptr)
, fRowBytes(0) , fRowBytes(0)
@ -607,10 +600,10 @@ private:
class SkPngInterlacedDecoder : public SkPngCodec { class SkPngInterlacedDecoder : public SkPngCodec {
public: public:
SkPngInterlacedDecoder(const SkEncodedInfo& info, const SkImageInfo& imageInfo, SkPngInterlacedDecoder(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
std::unique_ptr<SkStream> stream, SkPngChunkReader* reader, png_structp png_ptr, SkPngChunkReader* reader, png_structp png_ptr,
png_infop info_ptr, int bitDepth, int numberPasses) 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) , fNumberPasses(numberPasses)
, fFirstRow(0) , fFirstRow(0)
, fLastRow(0) , fLastRow(0)
@ -918,36 +911,33 @@ void AutoCleanPng::infoCallback(size_t idatLength) {
if (fOutCodec) { if (fOutCodec) {
SkASSERT(nullptr == *fOutCodec); SkASSERT(nullptr == *fOutCodec);
sk_sp<SkColorSpace> colorSpace = read_color_space(fPng_ptr, fInfo_ptr); auto profile = read_color_profile(fPng_ptr, fInfo_ptr);
if (colorSpace) { if (profile) {
switch (colorSpace->type()) { switch (profile->profile()->data_color_space) {
case SkColorSpace::kCMYK_Type: case skcms_Signature_CMYK:
colorSpace = nullptr; profile = nullptr;
break; break;
case SkColorSpace::kGray_Type: case skcms_Signature_Gray:
if (SkEncodedInfo::kGray_Color != color && if (SkEncodedInfo::kGray_Color != color &&
SkEncodedInfo::kGrayAlpha_Color != color) SkEncodedInfo::kGrayAlpha_Color != color)
{ {
colorSpace = nullptr; profile = nullptr;
} }
break; break;
case SkColorSpace::kRGB_Type: default:
break; break;
} }
} }
if (!colorSpace) { if (!profile) {
// Treat unsupported/invalid color spaces as sRGB. // 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) { if (encodedColorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
png_color_8p sigBits; png_color_8p sigBits;
if (png_get_sBIT(fPng_ptr, fInfo_ptr, &sigBits)) { if (png_get_sBIT(fPng_ptr, fInfo_ptr, &sigBits)) {
if (8 == sigBits->alpha && kGraySigBit_GrayAlphaIsJustAlpha == sigBits->gray) { if (8 == sigBits->alpha && kGraySigBit_GrayAlphaIsJustAlpha == sigBits->gray) {
imageInfo = imageInfo.makeColorType(kAlpha_8_SkColorType); color = SkEncodedInfo::kXAlpha_Color;
} }
} }
} else if (SkEncodedInfo::kOpaque_Alpha == alpha) { } 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 (png_get_sBIT(fPng_ptr, fInfo_ptr, &sigBits)) {
if (5 == sigBits->red && 6 == sigBits->green && 5 == sigBits->blue) { if (5 == sigBits->red && 6 == sigBits->green && 5 == sigBits->blue) {
// Recommend a decode to 565 if the sBIT indicates 565. // 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) { 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); std::unique_ptr<SkStream>(fStream), fChunkReader, fPng_ptr, fInfo_ptr, bitDepth);
} else { } else {
*fOutCodec = new SkPngInterlacedDecoder(encodedInfo, imageInfo, *fOutCodec = new SkPngInterlacedDecoder(std::move(encodedInfo),
std::unique_ptr<SkStream>(fStream), fChunkReader, fPng_ptr, fInfo_ptr, bitDepth, std::unique_ptr<SkStream>(fStream), fChunkReader, fPng_ptr, fInfo_ptr, bitDepth,
numberPasses); numberPasses);
} }
@ -976,10 +968,9 @@ void AutoCleanPng::infoCallback(size_t idatLength) {
this->releasePngPtrs(); this->releasePngPtrs();
} }
SkPngCodec::SkPngCodec(const SkEncodedInfo& encodedInfo, const SkImageInfo& imageInfo, SkPngCodec::SkPngCodec(SkEncodedInfo&& encodedInfo, std::unique_ptr<SkStream> stream,
std::unique_ptr<SkStream> stream, SkPngChunkReader* chunkReader, SkPngChunkReader* chunkReader, void* png_ptr, void* info_ptr, int bitDepth)
void* png_ptr, void* info_ptr, int bitDepth) : INHERITED(std::move(encodedInfo), png_select_xform_format(encodedInfo), std::move(stream))
: INHERITED(encodedInfo, imageInfo, png_select_xform_format(encodedInfo), std::move(stream))
, fPngChunkReader(SkSafeRef(chunkReader)) , fPngChunkReader(SkSafeRef(chunkReader))
, fPng_ptr(png_ptr) , fPng_ptr(png_ptr)
, fInfo_ptr(info_ptr) , fInfo_ptr(info_ptr)

View File

@ -8,7 +8,6 @@
#define SkPngCodec_DEFINED #define SkPngCodec_DEFINED
#include "SkCodec.h" #include "SkCodec.h"
#include "SkColorSpaceXform.h"
#include "SkColorTable.h" #include "SkColorTable.h"
#include "SkPngChunkReader.h" #include "SkPngChunkReader.h"
#include "SkEncodedImageFormat.h" #include "SkEncodedImageFormat.h"
@ -45,8 +44,8 @@ protected:
void* fPtr; void* fPtr;
}; };
SkPngCodec(const SkEncodedInfo&, const SkImageInfo&, std::unique_ptr<SkStream>, SkPngCodec(SkEncodedInfo&&, std::unique_ptr<SkStream>, SkPngChunkReader*,
SkPngChunkReader*, void* png_ptr, void* info_ptr, int bitDepth); void* png_ptr, void* info_ptr, int bitDepth);
Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, int*) Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, int*)
override; override;

View File

@ -504,10 +504,6 @@ public:
} }
} }
const SkEncodedInfo& getEncodedInfo() const {
return fEncodedInfo;
}
int width() const { int width() const {
return fWidth; return fWidth;
} }
@ -602,8 +598,6 @@ private:
SkDngImage(SkRawStream* stream) SkDngImage(SkRawStream* stream)
: fStream(stream) : fStream(stream)
, fEncodedInfo(SkEncodedInfo::Make(SkEncodedInfo::kRGB_Color,
SkEncodedInfo::kOpaque_Alpha, 8))
{} {}
dng_memory_allocator fAllocator; dng_memory_allocator fAllocator;
@ -615,11 +609,21 @@ private:
int fWidth; int fWidth;
int fHeight; int fHeight;
SkEncodedInfo fEncodedInfo;
bool fIsScalable; bool fIsScalable;
bool fIsXtransImage; 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 * 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, * SkJpegCodec. If PIEX returns kFail, then the file is invalid, return nullptr. In other cases,
@ -644,16 +648,22 @@ std::unique_ptr<SkCodec> SkRawCodec::MakeFromStream(std::unique_ptr<SkStream> st
return nullptr; return nullptr;
} }
sk_sp<SkColorSpace> colorSpace; std::unique_ptr<SkEncodedInfo::ICCProfile> profile;
switch (imageData.color_space) { switch (imageData.color_space) {
case ::piex::PreviewImageData::kSrgb: case ::piex::PreviewImageData::kSrgb:
colorSpace = SkColorSpace::MakeSRGB(); profile = SkEncodedInfo::ICCProfile::MakeSRGB();
break; break;
case ::piex::PreviewImageData::kAdobeRgb: case ::piex::PreviewImageData::kAdobeRgb: {
colorSpace = SkColorSpace::MakeRGB(g2Dot2_TransferFn, constexpr skcms_TransferFunction twoDotTwo =
SkColorSpace::kAdobeRGB_Gamut); { 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; break;
} }
}
// Theoretically PIEX can return JPEG compressed image or uncompressed RGB image. We only // Theoretically PIEX can return JPEG compressed image or uncompressed RGB image. We only
// handle the JPEG compressed preview image here. // handle the JPEG compressed preview image here.
@ -670,7 +680,7 @@ std::unique_ptr<SkCodec> SkRawCodec::MakeFromStream(std::unique_ptr<SkStream> st
return nullptr; return nullptr;
} }
return SkJpegCodec::MakeFromStream(std::move(memoryStream), result, 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()) { if (this->colorXform()) {
swizzler->swizzle(xformBuffer.get(), &srcRow[0]); swizzler->swizzle(xformBuffer.get(), &srcRow[0]);
this->applyColorXform(dstRow, xformBuffer.get(), dstInfo.width(), kOpaque_SkAlphaType); this->applyColorXform(dstRow, xformBuffer.get(), dstInfo.width());
} else { } else {
swizzler->swizzle(dstRow, &srcRow[0]); swizzler->swizzle(dstRow, &srcRow[0]);
} }
@ -796,7 +806,8 @@ bool SkRawCodec::onDimensionsSupported(const SkISize& dim) {
SkRawCodec::~SkRawCodec() {} SkRawCodec::~SkRawCodec() {}
SkRawCodec::SkRawCodec(SkDngImage* dngImage) SkRawCodec::SkRawCodec(SkDngImage* dngImage)
: INHERITED(dngImage->width(), dngImage->height(), dngImage->getEncodedInfo(), : INHERITED(SkEncodedInfo::MakeSRGB(dngImage->width(), dngImage->height(),
SkColorSpaceXform::kRGBA_8888_ColorFormat, nullptr, SkEncodedInfo::kRGB_Color,
SkColorSpace::MakeSRGB()) SkEncodedInfo::kOpaque_Alpha, 8),
skcms_PixelFormat_RGBA_8888, nullptr)
, fDngImage(dngImage) {} , fDngImage(dngImage) {}

View File

@ -890,6 +890,7 @@ SkSwizzler* SkSwizzler::CreateSwizzler(const SkEncodedInfo& encodedInfo,
return nullptr; return nullptr;
} }
break; break;
case SkEncodedInfo::kXAlpha_Color:
case SkEncodedInfo::kGrayAlpha_Color: case SkEncodedInfo::kGrayAlpha_Color:
switch (dstInfo.colorType()) { switch (dstInfo.colorType()) {
case kRGBA_8888_SkColorType: case kRGBA_8888_SkColorType:
@ -963,6 +964,10 @@ SkSwizzler* SkSwizzler::CreateSwizzler(const SkEncodedInfo& encodedInfo,
return nullptr; return nullptr;
} }
break; 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: case SkEncodedInfo::kRGB_Color:
switch (dstInfo.colorType()) { switch (dstInfo.colorType()) {
case kRGBA_8888_SkColorType: case kRGBA_8888_SkColorType:

View File

@ -97,11 +97,10 @@ bool SkWbmpCodec::readRow(uint8_t* row) {
return this->stream()->read(row, fSrcRowBytes) == fSrcRowBytes; return this->stream()->read(row, fSrcRowBytes) == fSrcRowBytes;
} }
SkWbmpCodec::SkWbmpCodec(int width, int height, const SkEncodedInfo& info, SkWbmpCodec::SkWbmpCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream)
std::unique_ptr<SkStream> stream)
// Wbmp does not need a colorXform, so choose an arbitrary srcFormat. // Wbmp does not need a colorXform, so choose an arbitrary srcFormat.
: INHERITED(width, height, info, SkColorSpaceXform::ColorFormat(), : INHERITED(std::move(info), skcms_PixelFormat(),
std::move(stream), SkColorSpace::MakeSRGB()) std::move(stream))
, fSrcRowBytes(get_src_row_bytes(this->getInfo().width())) , fSrcRowBytes(get_src_row_bytes(this->getInfo().width()))
, fSwizzler(nullptr) , fSwizzler(nullptr)
{} {}
@ -111,7 +110,7 @@ SkEncodedImageFormat SkWbmpCodec::onGetEncodedFormat() const {
} }
bool SkWbmpCodec::conversionSupported(const SkImageInfo& dst, SkColorType /*srcColor*/, 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); return valid_color_type(dst) && valid_alpha(dst.alphaType(), srcIsOpaque);
} }
@ -159,10 +158,9 @@ std::unique_ptr<SkCodec> SkWbmpCodec::MakeFromStream(std::unique_ptr<SkStream> s
return nullptr; return nullptr;
} }
*result = kSuccess; *result = kSuccess;
SkEncodedInfo info = SkEncodedInfo::Make(SkEncodedInfo::kGray_Color, auto info = SkEncodedInfo::Make(size.width(), size.height(), SkEncodedInfo::kGray_Color,
SkEncodedInfo::kOpaque_Alpha, 1); SkEncodedInfo::kOpaque_Alpha, 1);
return std::unique_ptr<SkCodec>(new SkWbmpCodec(size.width(), size.height(), info, return std::unique_ptr<SkCodec>(new SkWbmpCodec(std::move(info), std::move(stream)));
std::move(stream)));
} }
int SkWbmpCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) { int SkWbmpCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) {

View File

@ -29,7 +29,7 @@ protected:
const Options&, int*) override; const Options&, int*) override;
bool onRewind() override; bool onRewind() override;
bool conversionSupported(const SkImageInfo& dst, SkColorType srcColor, 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. // No need to Xform; all pixels are either black or white.
bool usesColorXform() const override { return false; } bool usesColorXform() const override { return false; }
private: private:
@ -48,7 +48,7 @@ private:
*/ */
bool readRow(uint8_t* row); 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; const size_t fSrcRowBytes;

View File

@ -13,7 +13,6 @@
#include "SkCodecAnimation.h" #include "SkCodecAnimation.h"
#include "SkCodecAnimationPriv.h" #include "SkCodecAnimationPriv.h"
#include "SkCodecPriv.h" #include "SkCodecPriv.h"
#include "SkColorSpaceXform.h"
#include "SkMakeUnique.h" #include "SkMakeUnique.h"
#include "SkRasterPipeline.h" #include "SkRasterPipeline.h"
#include "SkSampler.h" #include "SkSampler.h"
@ -89,15 +88,17 @@ std::unique_ptr<SkCodec> SkWebpCodec::MakeFromStream(std::unique_ptr<SkStream> s
} }
} }
sk_sp<SkColorSpace> colorSpace = nullptr; std::unique_ptr<SkEncodedInfo::ICCProfile> profile = nullptr;
{ {
WebPChunkIterator chunkIterator; WebPChunkIterator chunkIterator;
SkAutoTCallVProc<WebPChunkIterator, WebPDemuxReleaseChunkIterator> autoCI(&chunkIterator); SkAutoTCallVProc<WebPChunkIterator, WebPDemuxReleaseChunkIterator> autoCI(&chunkIterator);
if (WebPDemuxGetChunk(demux, "ICCP", 1, &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) { if (!profile || profile->profile()->data_color_space != skcms_Signature_RGB) {
colorSpace = SkColorSpace::MakeSRGB(); profile = SkEncodedInfo::ICCProfile::MakeSRGB();
} }
} }
@ -171,10 +172,9 @@ std::unique_ptr<SkCodec> SkWebpCodec::MakeFromStream(std::unique_ptr<SkStream> s
*result = kSuccess; *result = kSuccess;
SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8); SkEncodedInfo info = SkEncodedInfo::Make(width, height, color, alpha, 8, std::move(profile));
return std::unique_ptr<SkCodec>(new SkWebpCodec(width, height, info, std::move(colorSpace), return std::unique_ptr<SkCodec>(new SkWebpCodec(std::move(info), std::move(stream),
std::move(stream), demux.release(), std::move(data), demux.release(), std::move(data), origin));
origin));
} }
SkISize SkWebpCodec::onGetScaledDimensions(float desiredScale) const { SkISize SkWebpCodec::onGetScaledDimensions(float desiredScale) const {
@ -543,35 +543,8 @@ SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
webpDst.installPixels(webpInfo, dst, rowBytes); webpDst.installPixels(webpInfo, dst, rowBytes);
} }
// Choose the step when we will perform premultiplication. config.output.colorspace = webp_decode_mode(webpInfo.colorType(),
enum { frame.has_alpha && dstInfo.alphaType() == kPremul_SkAlphaType && !this->colorXform());
kNoPremul,
kBlendLine,
kColorXform,
kLibwebp,
};
auto choose_premul_step = [&]() {
if (!frame.has_alpha) {
// None necessary.
return kNoPremul;
}
if (blendWithPrevFrame) {
// Premultiply in blend_line, in a linear space.
return kBlendLine;
}
if (dstInfo.alphaType() != kPremul_SkAlphaType) {
// No blending is necessary, so we only need to premultiply if the
// client requested it.
return kNoPremul;
}
if (this->colorXform()) {
// Premultiply in the colorXform, in a linear space.
return kColorXform;
}
return kLibwebp;
};
const auto premulStep = choose_premul_step();
config.output.colorspace = webp_decode_mode(webpInfo.colorType(), premulStep == kLibwebp);
config.output.is_external_memory = 1; config.output.is_external_memory = 1;
config.output.u.RGBA.rgba = reinterpret_cast<uint8_t*>(webpDst.getAddr(dstX, dstY)); config.output.u.RGBA.rgba = reinterpret_cast<uint8_t*>(webpDst.getAddr(dstX, dstY));
@ -620,11 +593,8 @@ SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
xformDst = dst; xformDst = dst;
} }
const auto xformAlphaType = (premulStep == kColorXform) ? kPremul_SkAlphaType :
( frame.has_alpha) ? kUnpremul_SkAlphaType :
kOpaque_SkAlphaType ;
for (int y = 0; y < rowsDecoded; y++) { for (int y = 0; y < rowsDecoded; y++) {
this->applyColorXform(xformDst, xformSrc, scaledWidth, xformAlphaType); this->applyColorXform(xformDst, xformSrc, scaledWidth);
if (blendWithPrevFrame) { if (blendWithPrevFrame) {
blend_line(dstCT, dst, dstCT, xformDst, blend_line(dstCT, dst, dstCT, xformDst,
dstInfo.alphaType(), frame.has_alpha, scaledWidth); dstInfo.alphaType(), frame.has_alpha, scaledWidth);
@ -648,14 +618,14 @@ SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
return result; return result;
} }
SkWebpCodec::SkWebpCodec(int width, int height, const SkEncodedInfo& info, SkWebpCodec::SkWebpCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
sk_sp<SkColorSpace> colorSpace, std::unique_ptr<SkStream> stream,
WebPDemuxer* demux, sk_sp<SkData> data, SkEncodedOrigin origin) WebPDemuxer* demux, sk_sp<SkData> data, SkEncodedOrigin origin)
: INHERITED(width, height, info, SkColorSpaceXform::kBGRA_8888_ColorFormat, std::move(stream), : INHERITED(std::move(info), skcms_PixelFormat_BGRA_8888, std::move(stream),
std::move(colorSpace), origin) origin)
, fDemux(demux) , fDemux(demux)
, fData(std::move(data)) , fData(std::move(data))
, fFailed(false) , fFailed(false)
{ {
fFrameHolder.setScreenSize(width, height); const auto& eInfo = this->getEncodedInfo();
fFrameHolder.setScreenSize(eInfo.width(), eInfo.height());
} }

View File

@ -9,7 +9,6 @@
#define SkWebpCodec_DEFINED #define SkWebpCodec_DEFINED
#include "SkCodec.h" #include "SkCodec.h"
#include "SkColorSpace.h"
#include "SkEncodedImageFormat.h" #include "SkEncodedImageFormat.h"
#include "SkFrameHolder.h" #include "SkFrameHolder.h"
#include "SkImageInfo.h" #include "SkImageInfo.h"
@ -47,8 +46,8 @@ protected:
} }
private: private:
SkWebpCodec(int width, int height, const SkEncodedInfo&, sk_sp<SkColorSpace>, SkWebpCodec(SkEncodedInfo&&, std::unique_ptr<SkStream>, WebPDemuxer*, sk_sp<SkData>,
std::unique_ptr<SkStream>, WebPDemuxer*, sk_sp<SkData>, SkEncodedOrigin); SkEncodedOrigin);
SkAutoTCallVProc<WebPDemuxer, WebPDemuxDelete> fDemux; SkAutoTCallVProc<WebPDemuxer, WebPDemuxDelete> fDemux;

View File

@ -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) { DEF_TEST(Codec_crbug807324, r) {
if (GetResourcePath().isEmpty()) { if (GetResourcePath().isEmpty()) {
return; return;

42
tests/EncodedInfoTest.cpp Normal file
View File

@ -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));
}