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/SkCodecImageGenerator.cpp",
"src/codec/SkColorTable.cpp",
"src/codec/SkEncodedInfo.cpp",
"src/codec/SkGifCodec.cpp",
"src/codec/SkMaskSwizzler.cpp",
"src/codec/SkMasks.cpp",
@ -2021,8 +2022,8 @@ if (skia_enable_tools) {
"tools/viewer/SlideDir.cpp",
"tools/viewer/StatsLayer.cpp",
"tools/viewer/SvgSlide.cpp",
"tools/viewer/TouchGesture.h",
"tools/viewer/TouchGesture.cpp",
"tools/viewer/TouchGesture.h",
"tools/viewer/Viewer.cpp",
]
libs = []

View File

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

View File

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

View File

@ -8,12 +8,25 @@
#ifndef SkEncodedInfo_DEFINED
#define SkEncodedInfo_DEFINED
#include "SkData.h"
#include "SkImageInfo.h"
class SkColorSpace;
#include "../../third_party/skcms/skcms.h"
struct SkEncodedInfo {
public:
class ICCProfile {
public:
static std::unique_ptr<ICCProfile> Make(sk_sp<SkData>);
static std::unique_ptr<ICCProfile> MakeSRGB();
static std::unique_ptr<ICCProfile> Make(const skcms_ICCProfile&);
const skcms_ICCProfile* profile() const { return &fProfile; }
private:
ICCProfile(const skcms_ICCProfile&, sk_sp<SkData> = nullptr);
skcms_ICCProfile fProfile;
sk_sp<SkData> fData;
};
enum Alpha {
kOpaque_Alpha,
@ -39,6 +52,20 @@ public:
// PNG
kGrayAlpha_Color,
// PNG with Skia-specific sBIT
// Like kGrayAlpha, except this expects to be treated as
// kAlpha_8_SkColorType, which ignores the gray component. If
// decoded to full color (e.g. kN32), the gray component is respected
// (so it can share code with kGrayAlpha).
kXAlpha_Color,
// PNG
// 565 images may be encoded to PNG by specifying the number of
// significant bits for each channel. This is a strange 565
// representation because the image is still encoded with 8 bits per
// component.
k565_Color,
// PNG, GIF, BMP
kPalette_Color,
@ -67,7 +94,18 @@ public:
kYCCK_Color,
};
static SkEncodedInfo Make(Color color, Alpha alpha, int bitsPerComponent) {
static SkEncodedInfo Make(int width, int height, Color color, Alpha alpha,
int bitsPerComponent) {
return Make(width, height, color, alpha, bitsPerComponent, nullptr);
}
static SkEncodedInfo MakeSRGB(int width, int height, Color color, Alpha alpha,
int bitsPerComponent) {
return Make(width, height, color, alpha, bitsPerComponent, ICCProfile::MakeSRGB());
}
static SkEncodedInfo Make(int width, int height, Color color, Alpha alpha,
int bitsPerComponent, std::unique_ptr<ICCProfile> profile) {
SkASSERT(1 == bitsPerComponent ||
2 == bitsPerComponent ||
4 == bitsPerComponent ||
@ -105,29 +143,51 @@ public:
SkASSERT(kOpaque_Alpha != alpha);
SkASSERT(8 == bitsPerComponent);
break;
case kXAlpha_Color:
SkASSERT(kUnpremul_Alpha == alpha);
SkASSERT(8 == bitsPerComponent);
break;
case k565_Color:
SkASSERT(kOpaque_Alpha == alpha);
SkASSERT(8 == bitsPerComponent);
break;
default:
SkASSERT(false);
break;
}
return SkEncodedInfo(color, alpha, bitsPerComponent);
return SkEncodedInfo(width, height, color, alpha, bitsPerComponent, std::move(profile));
}
/*
* Returns an SkImageInfo with Skia color and alpha types that are the
* closest possible match to the encoded info.
* Returns a recommended SkImageInfo.
*
* TODO: Leave this up to the client.
*/
SkImageInfo makeImageInfo(int width, int height, sk_sp<SkColorSpace> colorSpace) const {
auto ct = kGray_Color == fColor ? kGray_8_SkColorType :
kN32_SkColorType ;
SkImageInfo makeImageInfo() const {
auto ct = kGray_Color == fColor ? kGray_8_SkColorType :
kXAlpha_Color == fColor ? kAlpha_8_SkColorType :
k565_Color == fColor ? kRGB_565_SkColorType :
kN32_SkColorType ;
auto alpha = kOpaque_Alpha == fAlpha ? kOpaque_SkAlphaType
: kUnpremul_SkAlphaType;
return SkImageInfo::Make(width, height, ct, alpha, std::move(colorSpace));
sk_sp<SkColorSpace> cs = fProfile ? SkColorSpace::Make(*fProfile->profile())
: nullptr;
if (!cs) {
cs = SkColorSpace::MakeSRGB();
}
return SkImageInfo::Make(fWidth, fHeight, ct, alpha, std::move(cs));
}
Color color() const { return fColor; }
Alpha alpha() const { return fAlpha; }
int width() const { return fWidth; }
int height() const { return fHeight; }
Color color() const { return fColor; }
Alpha alpha() const { return fAlpha; }
bool opaque() const { return fAlpha == kOpaque_Alpha; }
const skcms_ICCProfile* profile() const {
if (!fProfile) return nullptr;
return fProfile->profile();
}
uint8_t bitsPerComponent() const { return fBitsPerComponent; }
@ -135,6 +195,7 @@ public:
switch (fColor) {
case kGray_Color:
return fBitsPerComponent;
case kXAlpha_Color:
case kGrayAlpha_Color:
return 2 * fBitsPerComponent;
case kPalette_Color:
@ -142,6 +203,7 @@ public:
case kRGB_Color:
case kBGR_Color:
case kYUV_Color:
case k565_Color:
return 3 * fBitsPerComponent;
case kRGBA_Color:
case kBGRA_Color:
@ -156,17 +218,38 @@ public:
}
}
private:
SkEncodedInfo(const SkEncodedInfo& orig) = delete;
SkEncodedInfo& operator=(const SkEncodedInfo&) = delete;
SkEncodedInfo(Color color, Alpha alpha, uint8_t bitsPerComponent)
: fColor(color)
SkEncodedInfo(SkEncodedInfo&& orig) = default;
SkEncodedInfo& operator=(SkEncodedInfo&&) = default;
// Explicit copy method, to avoid accidental copying.
SkEncodedInfo copy() const {
auto copy = SkEncodedInfo::Make(fWidth, fHeight, fColor, fAlpha, fBitsPerComponent);
if (fProfile) {
copy.fProfile.reset(new ICCProfile(*fProfile.get()));
}
return copy;
}
private:
SkEncodedInfo(int width, int height, Color color, Alpha alpha,
uint8_t bitsPerComponent, std::unique_ptr<ICCProfile> profile)
: fWidth(width)
, fHeight(height)
, fColor(color)
, fAlpha(alpha)
, fBitsPerComponent(bitsPerComponent)
, fProfile(std::move(profile))
{}
Color fColor;
Alpha fAlpha;
uint8_t fBitsPerComponent;
int fWidth;
int fHeight;
Color fColor;
Alpha fAlpha;
uint8_t fBitsPerComponent;
std::unique_ptr<ICCProfile> fProfile;
};
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 KiB

View File

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

View File

@ -25,7 +25,7 @@ public:
bool didCreateSrcBuffer() const { return fSrcBuffer != nullptr; }
protected:
SkBmpBaseCodec(int width, int height, const SkEncodedInfo& info, std::unique_ptr<SkStream>,
SkBmpBaseCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream>,
uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder);
uint8_t* srcBuffer() { return reinterpret_cast<uint8_t*>(fSrcBuffer.get()); }

View File

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

View File

@ -38,7 +38,7 @@ public:
protected:
SkBmpCodec(int width, int height, const SkEncodedInfo& info, std::unique_ptr<SkStream>,
SkBmpCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream>,
uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder);
SkEncodedImageFormat onGetEncodedFormat() const override { return SkEncodedImageFormat::kBMP; }
@ -103,7 +103,7 @@ protected:
* than RGBA.
*/
static constexpr SkColorType kXformSrcColorType = kBGRA_8888_SkColorType;
static constexpr auto kXformSrcColorFormat = SkColorSpaceXform::kBGRA_8888_ColorFormat;
static constexpr auto kXformSrcColorFormat = skcms_PixelFormat_BGRA_8888;
private:

View File

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

View File

@ -31,7 +31,7 @@ public:
* @param masks color masks for certain bmp formats
* @param rowOrder indicates whether rows are ordered top-down or bottom-up
*/
SkBmpMaskCodec(int width, int height, const SkEncodedInfo& info, std::unique_ptr<SkStream>,
SkBmpMaskCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream>,
uint16_t bitsPerPixel, SkMasks* masks,
SkCodec::SkScanlineOrder rowOrder);

View File

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

View File

@ -35,7 +35,7 @@ public:
* headers
* @param rowOrder indicates whether rows are ordered top-down or bottom-up
*/
SkBmpRLECodec(int width, int height, const SkEncodedInfo& info, std::unique_ptr<SkStream>,
SkBmpRLECodec(SkEncodedInfo&& info, std::unique_ptr<SkStream>,
uint16_t bitsPerPixel, uint32_t numColors, uint32_t bytesPerColor,
uint32_t offset, SkCodec::SkScanlineOrder rowOrder);

View File

@ -14,12 +14,12 @@
* Creates an instance of the decoder
* Called only by NewFromStream
*/
SkBmpStandardCodec::SkBmpStandardCodec(int width, int height, const SkEncodedInfo& info,
std::unique_ptr<SkStream> stream, uint16_t bitsPerPixel,
uint32_t numColors, uint32_t bytesPerColor, uint32_t offset,
SkBmpStandardCodec::SkBmpStandardCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream,
uint16_t bitsPerPixel, uint32_t numColors,
uint32_t bytesPerColor, uint32_t offset,
SkCodec::SkScanlineOrder rowOrder,
bool isOpaque, bool inIco)
: INHERITED(width, height, info, std::move(stream), bitsPerPixel, rowOrder)
: INHERITED(std::move(info), std::move(stream), bitsPerPixel, rowOrder)
, fColorTable(nullptr)
, fNumColors(numColors)
, fBytesPerColor(bytesPerColor)
@ -146,20 +146,33 @@ SkCodec::Result SkBmpStandardCodec::onGetPixels(const SkImageInfo& dstInfo,
return true;
}
static SkEncodedInfo make_info(SkEncodedInfo::Color color,
SkEncodedInfo::Alpha alpha, int bitsPerPixel) {
// This is just used for the swizzler, which does not need the width or height.
return SkEncodedInfo::Make(0, 0, color, alpha, bitsPerPixel);
}
SkEncodedInfo SkBmpStandardCodec::swizzlerInfo() const {
const auto& info = this->getEncodedInfo();
if (fInIco) {
if (this->bitsPerPixel() <= 8) {
return make_info(SkEncodedInfo::kPalette_Color,
info.alpha(), this->bitsPerPixel());
}
if (this->bitsPerPixel() == 24) {
return make_info(SkEncodedInfo::kBGR_Color,
SkEncodedInfo::kOpaque_Alpha, 8);
}
}
return make_info(info.color(), info.alpha(), info.bitsPerComponent());
}
void SkBmpStandardCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts) {
// In the case of bmp-in-icos, we will report BGRA to the client,
// since we may be required to apply an alpha mask after the decode.
// However, the swizzler needs to know the actual format of the bmp.
SkEncodedInfo encodedInfo = this->getEncodedInfo();
if (fInIco) {
if (this->bitsPerPixel() <= 8) {
encodedInfo = SkEncodedInfo::Make(SkEncodedInfo::kPalette_Color,
encodedInfo.alpha(), this->bitsPerPixel());
} else if (this->bitsPerPixel() == 24) {
encodedInfo = SkEncodedInfo::Make(SkEncodedInfo::kBGR_Color,
SkEncodedInfo::kOpaque_Alpha, 8);
}
}
SkEncodedInfo encodedInfo = this->swizzlerInfo();
// Get a pointer to the color table if it exists
const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());

View File

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

View File

@ -9,7 +9,6 @@
#include "SkCodec.h"
#include "SkCodecPriv.h"
#include "SkColorSpace.h"
#include "SkColorSpaceXformPriv.h"
#include "SkData.h"
#include "SkFrameHolder.h"
#include "SkGifCodec.h"
@ -126,26 +125,10 @@ std::unique_ptr<SkCodec> SkCodec::MakeFromData(sk_sp<SkData> data, SkPngChunkRea
return MakeFromStream(SkMemoryStream::Make(std::move(data)), nullptr, reader);
}
SkCodec::SkCodec(int width, int height, const SkEncodedInfo& info,
XformFormat srcFormat, std::unique_ptr<SkStream> stream,
sk_sp<SkColorSpace> colorSpace, SkEncodedOrigin origin)
: fEncodedInfo(info)
, fSrcInfo(info.makeImageInfo(width, height, std::move(colorSpace)))
, fSrcXformFormat(srcFormat)
, fStream(std::move(stream))
, fNeedsRewind(false)
, fOrigin(origin)
, fDstInfo()
, fOptions()
, fCurrScanline(-1)
, fStartedIncrementalDecode(false)
{}
SkCodec::SkCodec(const SkEncodedInfo& info, const SkImageInfo& imageInfo,
XformFormat srcFormat, std::unique_ptr<SkStream> stream,
SkEncodedOrigin origin)
: fEncodedInfo(info)
, fSrcInfo(imageInfo)
SkCodec::SkCodec(SkEncodedInfo&& info, XformFormat srcFormat, std::unique_ptr<SkStream> stream,
SkEncodedOrigin origin)
: fEncodedInfo(std::move(info))
, fSrcInfo(fEncodedInfo.makeImageInfo())
, fSrcXformFormat(srcFormat)
, fStream(std::move(stream))
, fNeedsRewind(false)
@ -159,7 +142,7 @@ SkCodec::SkCodec(const SkEncodedInfo& info, const SkImageInfo& imageInfo,
SkCodec::~SkCodec() {}
bool SkCodec::conversionSupported(const SkImageInfo& dst, SkColorType srcColor,
bool srcIsOpaque, const SkColorSpace* srcCS) const {
bool srcIsOpaque, bool needsColorXform) {
if (!valid_alpha(dst.alphaType(), srcIsOpaque)) {
return false;
}
@ -173,8 +156,8 @@ bool SkCodec::conversionSupported(const SkImageInfo& dst, SkColorType srcColor,
case kRGB_565_SkColorType:
return srcIsOpaque;
case kGray_8_SkColorType:
return kGray_8_SkColorType == srcColor && srcIsOpaque &&
!needs_color_xform(dst, srcCS);
SkASSERT(!needsColorXform);
return kGray_8_SkColorType == srcColor && srcIsOpaque;
case kAlpha_8_SkColorType:
// conceptually we can convert anything into alpha_8, but we haven't actually coded
// all of those other conversions yet, so only return true for the case we have codec.
@ -182,7 +165,6 @@ bool SkCodec::conversionSupported(const SkImageInfo& dst, SkColorType srcColor,
default:
return false;
}
}
bool SkCodec::rewindIfNeeded() {
@ -245,13 +227,8 @@ SkCodec::Result SkCodec::handleFrameIndex(const SkImageInfo& info, void* pixels,
const Options& options) {
const int index = options.fFrameIndex;
if (0 == index) {
if (!this->conversionSupported(info, fSrcInfo.colorType(), fEncodedInfo.opaque(),
fSrcInfo.colorSpace())
|| !this->initializeColorXform(info, fEncodedInfo.alpha()))
{
return kInvalidConversion;
}
return kSuccess;
return this->initializeColorXform(info, fEncodedInfo.alpha(), fEncodedInfo.opaque())
? kSuccess : kInvalidConversion;
}
if (index < 0) {
@ -274,11 +251,6 @@ SkCodec::Result SkCodec::handleFrameIndex(const SkImageInfo& info, void* pixels,
const auto* frame = frameHolder->getFrame(index);
SkASSERT(frame);
if (!this->conversionSupported(info, fSrcInfo.colorType(), !frame->hasAlpha(),
fSrcInfo.colorSpace())) {
return kInvalidConversion;
}
const int requiredFrame = frame->getRequiredFrame();
if (requiredFrame != kNoFrame) {
if (options.fPriorFrame != kNoFrame) {
@ -324,7 +296,8 @@ SkCodec::Result SkCodec::handleFrameIndex(const SkImageInfo& info, void* pixels,
}
}
return this->initializeColorXform(info, frame->reportedAlpha()) ? kSuccess : kInvalidConversion;
return this->initializeColorXform(info, frame->reportedAlpha(), !frame->hasAlpha())
? kSuccess : kInvalidConversion;
}
SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
@ -612,63 +585,80 @@ void SkCodec::fillIncompleteImage(const SkImageInfo& info, void* dst, size_t row
}
}
static inline SkColorSpaceXform::ColorFormat select_xform_format_ct(SkColorType colorType) {
static inline bool select_xform_format(SkColorType colorType, bool forColorTable,
skcms_PixelFormat* outFormat) {
SkASSERT(outFormat);
switch (colorType) {
case kRGBA_8888_SkColorType:
return SkColorSpaceXform::kRGBA_8888_ColorFormat;
*outFormat = skcms_PixelFormat_RGBA_8888;
break;
case kBGRA_8888_SkColorType:
return SkColorSpaceXform::kBGRA_8888_ColorFormat;
*outFormat = skcms_PixelFormat_BGRA_8888;
break;
case kRGB_565_SkColorType:
if (forColorTable) {
#ifdef SK_PMCOLOR_IS_RGBA
return SkColorSpaceXform::kRGBA_8888_ColorFormat;
*outFormat = skcms_PixelFormat_RGBA_8888;
#else
return SkColorSpaceXform::kBGRA_8888_ColorFormat;
*outFormat = skcms_PixelFormat_BGRA_8888;
#endif
break;
}
*outFormat = skcms_PixelFormat_BGR_565;
break;
case kRGBA_F16_SkColorType:
*outFormat = skcms_PixelFormat_RGBA_hhhh;
break;
default:
SkASSERT(false);
return SkColorSpaceXform::kRGBA_8888_ColorFormat;
}
}
bool SkCodec::initializeColorXform(const SkImageInfo& dstInfo, SkEncodedInfo::Alpha encodedAlpha) {
fColorXform = nullptr;
fXformOnDecode = false;
if (!this->usesColorXform()) {
return true;
}
// FIXME: In SkWebpCodec, if a frame is blending with a prior frame, we don't need
// a colorXform to do a color correct premul, since the blend step will handle
// premultiplication. But there is no way to know whether we need to blend from
// inside this call.
if (needs_color_xform(dstInfo, fSrcInfo.colorSpace())) {
fColorXform = SkMakeColorSpaceXform(fSrcInfo.colorSpace(), dstInfo.colorSpace());
if (!fColorXform) {
return false;
}
// We will apply the color xform when reading the color table unless F16 is requested.
fXformOnDecode = SkEncodedInfo::kPalette_Color != fEncodedInfo.color()
|| kRGBA_F16_SkColorType == dstInfo.colorType();
if (fXformOnDecode) {
fDstXformFormat = select_xform_format(dstInfo.colorType());
} else {
fDstXformFormat = select_xform_format_ct(dstInfo.colorType());
}
}
return true;
}
void SkCodec::applyColorXform(void* dst, const void* src, int count, SkAlphaType at) const {
SkASSERT(fColorXform);
SkAssertResult(fColorXform->apply(fDstXformFormat, dst,
fSrcXformFormat, src,
count, at));
bool SkCodec::initializeColorXform(const SkImageInfo& dstInfo, SkEncodedInfo::Alpha encodedAlpha,
bool srcIsOpaque) {
fXformTime = kNo_XformTime;
bool needsColorXform = false;
if (this->usesColorXform() && dstInfo.colorSpace()) {
dstInfo.colorSpace()->toProfile(&fDstProfile);
if (kRGBA_F16_SkColorType == dstInfo.colorType()
|| !skcms_ApproximatelyEqualProfiles(fEncodedInfo.profile(), &fDstProfile) ) {
needsColorXform = true;
if (kGray_8_SkColorType == dstInfo.colorType()) {
return false;
}
}
}
if (!this->conversionSupported(dstInfo, fSrcInfo.colorType(), srcIsOpaque, needsColorXform)) {
return false;
}
if (needsColorXform) {
fXformTime = SkEncodedInfo::kPalette_Color != fEncodedInfo.color()
|| kRGBA_F16_SkColorType == dstInfo.colorType()
? kDecodeRow_XformTime : kPalette_XformTime;
if (!select_xform_format(dstInfo.colorType(), fXformTime == kPalette_XformTime,
&fDstXformFormat)) {
return false;
}
if (encodedAlpha == SkEncodedInfo::kUnpremul_Alpha
&& dstInfo.alphaType() == kPremul_SkAlphaType) {
fDstXformAlphaFormat = skcms_AlphaFormat_PremulAsEncoded;
} else {
fDstXformAlphaFormat = skcms_AlphaFormat_Unpremul;
}
}
return true;
}
void SkCodec::applyColorXform(void* dst, const void* src, int count) const {
auto alphaType = select_xform_alpha(fDstInfo.alphaType(), fSrcInfo.alphaType());
this->applyColorXform(dst, src, count, alphaType);
const auto* srcProfile = fEncodedInfo.profile();
SkASSERT(srcProfile);
SkAssertResult(skcms_Transform(src, fSrcXformFormat, skcms_AlphaFormat_Unpremul, srcProfile,
dst, fDstXformFormat, fDstXformAlphaFormat, &fDstProfile,
count));
}
std::vector<SkCodec::FrameInfo> SkCodec::getFrameInfo() {

View File

@ -9,8 +9,6 @@
#define SkCodecPriv_DEFINED
#include "SkColorData.h"
#include "SkColorSpaceXform.h"
#include "SkColorSpaceXformPriv.h"
#include "SkColorTable.h"
#include "SkEncodedInfo.h"
#include "SkEncodedOrigin.h"
@ -243,30 +241,6 @@ static inline PackColorProc choose_pack_color_proc(bool isPremul, SkColorType co
}
}
static inline bool needs_premul(SkAlphaType dstAT, SkEncodedInfo::Alpha encodedAlpha) {
return kPremul_SkAlphaType == dstAT && SkEncodedInfo::kUnpremul_Alpha == encodedAlpha;
}
static inline bool needs_color_xform(const SkImageInfo& dstInfo, const SkColorSpace* srcCS) {
// We never perform a color xform in legacy mode.
if (!dstInfo.colorSpace()) {
return false;
}
// None of the codecs we have output F16 natively, so if we're trying to decode to F16,
// we'll have to use SkColorSpaceXform to get there.
bool isF16 = kRGBA_F16_SkColorType == dstInfo.colorType();
// Need a color xform when dst space does not match the src.
bool srcDstNotEqual = !SkColorSpace::Equals(srcCS, dstInfo.colorSpace());
return isF16 || srcDstNotEqual;
}
static inline SkAlphaType select_xform_alpha(SkAlphaType dstAlphaType, SkAlphaType srcAlphaType) {
return (kOpaque_SkAlphaType == srcAlphaType) ? kOpaque_SkAlphaType : dstAlphaType;
}
bool is_orientation_marker(const uint8_t* data, size_t data_length, SkEncodedOrigin* orientation);
#endif // SkCodecPriv_DEFINED

View File

@ -5,4 +5,28 @@
* found in the LICENSE file.
*/
// Dummy file to assist in landing https://skia-review.googlesource.com/c/skia/+/136062
#include "SkEncodedInfo.h"
std::unique_ptr<SkEncodedInfo::ICCProfile> SkEncodedInfo::ICCProfile::Make(sk_sp<SkData> data) {
if (data) {
skcms_ICCProfile profile;
if (skcms_Parse(data->data(), data->size(), &profile)) {
return std::unique_ptr<ICCProfile>(new ICCProfile(profile, std::move(data)));
}
}
return nullptr;
}
std::unique_ptr<SkEncodedInfo::ICCProfile> SkEncodedInfo::ICCProfile::MakeSRGB() {
return std::unique_ptr<ICCProfile>(new ICCProfile(*skcms_sRGB_profile()));
}
std::unique_ptr<SkEncodedInfo::ICCProfile> SkEncodedInfo::ICCProfile::Make(
const skcms_ICCProfile& profile) {
return std::unique_ptr<ICCProfile>(new ICCProfile(profile));
}
SkEncodedInfo::ICCProfile::ICCProfile(const skcms_ICCProfile& profile, sk_sp<SkData> data)
: fProfile(profile)
, fData(std::move(data))
{}

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -504,10 +504,6 @@ public:
}
}
const SkEncodedInfo& getEncodedInfo() const {
return fEncodedInfo;
}
int width() const {
return fWidth;
}
@ -602,8 +598,6 @@ private:
SkDngImage(SkRawStream* stream)
: fStream(stream)
, fEncodedInfo(SkEncodedInfo::Make(SkEncodedInfo::kRGB_Color,
SkEncodedInfo::kOpaque_Alpha, 8))
{}
dng_memory_allocator fAllocator;
@ -615,11 +609,21 @@ private:
int fWidth;
int fHeight;
SkEncodedInfo fEncodedInfo;
bool fIsScalable;
bool fIsXtransImage;
};
static constexpr skcms_Matrix3x3 gAdobe_RGB_to_XYZD50 = {{
// ICC fixed-point (16.16) repesentation of:
// 0.60974, 0.20528, 0.14919,
// 0.31111, 0.62567, 0.06322,
// 0.01947, 0.06087, 0.74457,
{ SkFixedToFloat(0x9c18), SkFixedToFloat(0x348d), SkFixedToFloat(0x2631) }, // Rx, Gx, Bx
{ SkFixedToFloat(0x4fa5), SkFixedToFloat(0xa02c), SkFixedToFloat(0x102f) }, // Ry, Gy, By
{ SkFixedToFloat(0x04fc), SkFixedToFloat(0x0f95), SkFixedToFloat(0xbe9c) }, // Rz, Gz, Bz
}};
/*
* Tries to handle the image with PIEX. If PIEX returns kOk and finds the preview image, create a
* SkJpegCodec. If PIEX returns kFail, then the file is invalid, return nullptr. In other cases,
@ -644,15 +648,21 @@ std::unique_ptr<SkCodec> SkRawCodec::MakeFromStream(std::unique_ptr<SkStream> st
return nullptr;
}
sk_sp<SkColorSpace> colorSpace;
std::unique_ptr<SkEncodedInfo::ICCProfile> profile;
switch (imageData.color_space) {
case ::piex::PreviewImageData::kSrgb:
colorSpace = SkColorSpace::MakeSRGB();
profile = SkEncodedInfo::ICCProfile::MakeSRGB();
break;
case ::piex::PreviewImageData::kAdobeRgb:
colorSpace = SkColorSpace::MakeRGB(g2Dot2_TransferFn,
SkColorSpace::kAdobeRGB_Gamut);
case ::piex::PreviewImageData::kAdobeRgb: {
constexpr skcms_TransferFunction twoDotTwo =
{ 2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
skcms_ICCProfile skcmsProfile;
skcms_Init(&skcmsProfile);
skcms_SetTransferFunction(&skcmsProfile, &twoDotTwo);
skcms_SetXYZD50(&skcmsProfile, &gAdobe_RGB_to_XYZD50);
profile = SkEncodedInfo::ICCProfile::Make(skcmsProfile);
break;
}
}
// Theoretically PIEX can return JPEG compressed image or uncompressed RGB image. We only
@ -670,7 +680,7 @@ std::unique_ptr<SkCodec> SkRawCodec::MakeFromStream(std::unique_ptr<SkStream> st
return nullptr;
}
return SkJpegCodec::MakeFromStream(std::move(memoryStream), result,
std::move(colorSpace));
std::move(profile));
}
}
@ -746,7 +756,7 @@ SkCodec::Result SkRawCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
if (this->colorXform()) {
swizzler->swizzle(xformBuffer.get(), &srcRow[0]);
this->applyColorXform(dstRow, xformBuffer.get(), dstInfo.width(), kOpaque_SkAlphaType);
this->applyColorXform(dstRow, xformBuffer.get(), dstInfo.width());
} else {
swizzler->swizzle(dstRow, &srcRow[0]);
}
@ -796,7 +806,8 @@ bool SkRawCodec::onDimensionsSupported(const SkISize& dim) {
SkRawCodec::~SkRawCodec() {}
SkRawCodec::SkRawCodec(SkDngImage* dngImage)
: INHERITED(dngImage->width(), dngImage->height(), dngImage->getEncodedInfo(),
SkColorSpaceXform::kRGBA_8888_ColorFormat, nullptr,
SkColorSpace::MakeSRGB())
: INHERITED(SkEncodedInfo::MakeSRGB(dngImage->width(), dngImage->height(),
SkEncodedInfo::kRGB_Color,
SkEncodedInfo::kOpaque_Alpha, 8),
skcms_PixelFormat_RGBA_8888, nullptr)
, fDngImage(dngImage) {}

View File

@ -890,6 +890,7 @@ SkSwizzler* SkSwizzler::CreateSwizzler(const SkEncodedInfo& encodedInfo,
return nullptr;
}
break;
case SkEncodedInfo::kXAlpha_Color:
case SkEncodedInfo::kGrayAlpha_Color:
switch (dstInfo.colorType()) {
case kRGBA_8888_SkColorType:
@ -963,6 +964,10 @@ SkSwizzler* SkSwizzler::CreateSwizzler(const SkEncodedInfo& encodedInfo,
return nullptr;
}
break;
case SkEncodedInfo::k565_Color:
// Treat 565 exactly like RGB (since it's still encoded as 8 bits per component).
// We just mark as 565 when we have a hint that there are only 5/6/5 "significant"
// bits in each channel.
case SkEncodedInfo::kRGB_Color:
switch (dstInfo.colorType()) {
case kRGBA_8888_SkColorType:

View File

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

View File

@ -29,7 +29,7 @@ protected:
const Options&, int*) override;
bool onRewind() override;
bool conversionSupported(const SkImageInfo& dst, SkColorType srcColor,
bool srcIsOpaque, const SkColorSpace* srcCS) const override;
bool srcIsOpaque, bool needsXform) override;
// No need to Xform; all pixels are either black or white.
bool usesColorXform() const override { return false; }
private:
@ -48,7 +48,7 @@ private:
*/
bool readRow(uint8_t* row);
SkWbmpCodec(int width, int height, const SkEncodedInfo&, std::unique_ptr<SkStream>);
SkWbmpCodec(SkEncodedInfo&&, std::unique_ptr<SkStream>);
const size_t fSrcRowBytes;

View File

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

View File

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

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) {
if (GetResourcePath().isEmpty()) {
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));
}