Checking for valid colorType, alphaType, colorSpace in SkCodec

* Refactor to share code between SkPngCodec and SkWebpCodec
* Didn't end up sharing with SkJpegCodec but did refactor
  that code a bit
* Disallow conversions to F16 with non-linear color spaces
* Fail to decode if we fail to create a SkColorSpaceXform
  (should be an assert soon).  We used to fallback on a
  legacy decode if we failed to create the transform.
* A bunch of name changes

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2319293003

Committed: https://skia.googlesource.com/skia/+/7a9900d6d34e437bb24beb5524a1f6488ae138c9
Review-Url: https://codereview.chromium.org/2319293003
This commit is contained in:
msarett 2016-09-08 11:55:16 -07:00 committed by Commit bot
parent 6a2b1941c1
commit 2ecc35ffa5
15 changed files with 121 additions and 97 deletions

View File

@ -162,10 +162,13 @@ void ColorCodecBench::onDelayedSetup() {
fSrcInfo = codec->getInfo().makeColorType(kRGBA_8888_SkColorType);
fDstInfo = fSrcInfo.makeColorSpace(fDstSpace);
if (FLAGS_half) {
fDstInfo = fDstInfo.makeColorType(kRGBA_F16_SkColorType);
fDstSpace = fDstSpace->makeLinearGamma();
}
fDstInfo = fSrcInfo.makeColorSpace(fDstSpace);
fDst.reset(fDstInfo.getSafeSize(fDstInfo.minRowBytes()));
if (FLAGS_xform_only) {

View File

@ -896,6 +896,9 @@ Error ColorCodecSrc::draw(SkCanvas* canvas) const {
if (kUnpremul_SkAlphaType == decodeInfo.alphaType()) {
decodeInfo = decodeInfo.makeAlphaType(kPremul_SkAlphaType);
}
if (kRGBA_F16_SkColorType == fColorType) {
decodeInfo = decodeInfo.makeColorSpace(decodeInfo.colorSpace()->makeLinearGamma());
}
SkImageInfo bitmapInfo = decodeInfo;
if (kRGBA_8888_SkColorType == decodeInfo.colorType() ||

View File

@ -135,5 +135,8 @@ bool SkBitmapRegionCodec::decodeRegion(SkBitmap* bitmap, SkBRDAllocator* allocat
}
bool SkBitmapRegionCodec::conversionSupported(SkColorType colorType) {
return conversion_possible(fCodec->getInfo().makeColorType(colorType), fCodec->getInfo());
// Enable legacy behavior.
sk_sp<SkColorSpace> colorSpace = nullptr;
SkImageInfo dstInfo = fCodec->getInfo().makeColorType(colorType).makeColorSpace(colorSpace);
return conversion_possible_ignore_color_space(dstInfo, fCodec->getInfo());
}

View File

@ -603,7 +603,7 @@ int32_t SkBmpCodec::getDstRow(int32_t y, int32_t height) const {
SkCodec::Result SkBmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) {
if (!conversion_possible(dstInfo, this->getInfo())) {
if (!conversion_possible_ignore_color_space(dstInfo, this->getInfo())) {
SkCodecPrintf("Error: cannot convert input type to output type.\n");
return kInvalidConversion;
}

View File

@ -39,7 +39,7 @@ SkCodec::Result SkBmpMaskCodec::onGetPixels(const SkImageInfo& dstInfo,
return kInvalidScale;
}
if (!conversion_possible(dstInfo, this->getInfo())) {
if (!conversion_possible_ignore_color_space(dstInfo, this->getInfo())) {
SkCodecPrintf("Error: cannot convert input type to output type.\n");
return kInvalidConversion;
}

View File

@ -44,7 +44,7 @@ SkCodec::Result SkBmpRLECodec::onGetPixels(const SkImageInfo& dstInfo,
// Subsets are not supported.
return kUnimplemented;
}
if (!conversion_possible(dstInfo, this->getInfo())) {
if (!conversion_possible_ignore_color_space(dstInfo, this->getInfo())) {
SkCodecPrintf("Error: cannot convert input type to output type.\n");
return kInvalidConversion;
}

View File

@ -48,7 +48,7 @@ SkCodec::Result SkBmpStandardCodec::onGetPixels(const SkImageInfo& dstInfo,
SkCodecPrintf("Error: scaling not supported.\n");
return kInvalidScale;
}
if (!conversion_possible(dstInfo, this->getInfo())) {
if (!conversion_possible_ignore_color_space(dstInfo, this->getInfo())) {
SkCodecPrintf("Error: cannot convert input type to output type.\n");
return kInvalidConversion;
}

View File

@ -107,14 +107,18 @@ static inline bool valid_alpha(SkAlphaType dstAlpha, SkAlphaType srcAlpha) {
}
/*
* Original version of conversion_possible that does not account for color spaces.
* Used by codecs that have not been updated to support color spaces.
*
* Most of our codecs support the same conversions:
* - opaque to any alpha type
* - 565 only if opaque
* - premul to unpremul and vice versa
* - always support N32
* - always support RGBA, BGRA
* - otherwise match the src color type
*/
static inline bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) {
static inline bool conversion_possible_ignore_color_space(const SkImageInfo& dst,
const SkImageInfo& src) {
// Ensure the alpha type is valid
if (!valid_alpha(dst.alphaType(), src.alphaType())) {
return false;
@ -335,4 +339,41 @@ static inline SkAlphaType select_alpha_xform(SkAlphaType dstAlphaType, SkAlphaTy
return (kOpaque_SkAlphaType == srcAlphaType) ? kOpaque_SkAlphaType : dstAlphaType;
}
/*
* Alpha Type Conversions
* - kOpaque to kOpaque, kUnpremul, kPremul is valid
* - kUnpremul to kUnpremul, kPremul is valid
*
* Color Type Conversions
* - Always support kRGBA_8888, kBGRA_8888
* - Support kRGBA_F16 when there is a linear dst color space
* - Support kIndex8 if it matches the src
* - Support k565 if kOpaque and color correction is not required
* - Support k565 if it matches the src, kOpaque, and color correction is not required
*/
static inline bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) {
// Ensure the alpha type is valid.
if (!valid_alpha(dst.alphaType(), src.alphaType())) {
return false;
}
// Check for supported color types.
switch (dst.colorType()) {
case kRGBA_8888_SkColorType:
case kBGRA_8888_SkColorType:
return true;
case kRGBA_F16_SkColorType:
return dst.colorSpace() && dst.colorSpace()->gammaIsLinear();
case kIndex_8_SkColorType:
return kIndex_8_SkColorType == src.colorType();
case kRGB_565_SkColorType:
return kOpaque_SkAlphaType == src.alphaType() && !needs_color_xform(dst, src);
case kGray_8_SkColorType:
return kGray_8_SkColorType == src.colorType() &&
kOpaque_SkAlphaType == src.alphaType() && !needs_color_xform(dst, src);
default:
return false;
}
}
#endif // SkCodecPriv_DEFINED

View File

@ -450,9 +450,8 @@ void SkGifCodec::initializeColorTable(const SkImageInfo& dstInfo, SkPMColor* inp
SkCodec::Result SkGifCodec::prepareToDecode(const SkImageInfo& dstInfo, SkPMColor* inputColorPtr,
int* inputColorCount, const Options& opts) {
// Check for valid input parameters
if (!conversion_possible(dstInfo, this->getInfo())) {
return gif_error("Cannot convert input type to output type.\n",
kInvalidConversion);
if (!conversion_possible_ignore_color_space(dstInfo, this->getInfo())) {
return gif_error("Cannot convert input type to output type.\n", kInvalidConversion);
}
// Initialize color table and copy to the client if necessary

View File

@ -357,7 +357,7 @@ bool SkJpegCodec::onRewind() {
* image has been implemented
* Sets the output color space
*/
bool SkJpegCodec::setOutputColorSpace(const SkImageInfo& dstInfo, bool needsColorXform) {
bool SkJpegCodec::setOutputColorSpace(const SkImageInfo& dstInfo) {
if (kUnknown_SkAlphaType == dstInfo.alphaType()) {
return false;
}
@ -384,7 +384,7 @@ bool SkJpegCodec::setOutputColorSpace(const SkImageInfo& dstInfo, bool needsColo
case kBGRA_8888_SkColorType:
if (isCMYK) {
fDecoderMgr->dinfo()->out_color_space = JCS_CMYK;
} else if (needsColorXform) {
} else if (fColorXform) {
// Our color transformation code requires RGBA order inputs, but it'll swizzle
// to BGRA for us.
fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA;
@ -393,7 +393,7 @@ bool SkJpegCodec::setOutputColorSpace(const SkImageInfo& dstInfo, bool needsColo
}
return true;
case kRGB_565_SkColorType:
if (needsColorXform) {
if (fColorXform) {
return false;
}
@ -405,14 +405,17 @@ bool SkJpegCodec::setOutputColorSpace(const SkImageInfo& dstInfo, bool needsColo
}
return true;
case kGray_8_SkColorType:
if (needsColorXform || JCS_GRAYSCALE != encodedColorType) {
if (fColorXform || JCS_GRAYSCALE != encodedColorType) {
return false;
}
fDecoderMgr->dinfo()->out_color_space = JCS_GRAYSCALE;
return true;
case kRGBA_F16_SkColorType:
SkASSERT(needsColorXform);
SkASSERT(fColorXform);
if (!dstInfo.colorSpace()->gammaIsLinear()) {
return false;
}
if (isCMYK) {
fDecoderMgr->dinfo()->out_color_space = JCS_CMYK;
@ -545,14 +548,11 @@ 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
bool needsColorXform = needs_color_xform(dstInfo, this->getInfo());
if (!this->setOutputColorSpace(dstInfo, needsColorXform)) {
return fDecoderMgr->returnFailure("setOutputColorSpace", kInvalidConversion);
}
this->initializeColorXform(dstInfo);
if (!this->initializeColorXform(dstInfo, needsColorXform)) {
return fDecoderMgr->returnFailure("initializeColorXform", kInvalidParameters);
// 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)) {
@ -630,16 +630,12 @@ void SkJpegCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options&
SkASSERT(fSwizzler);
}
bool SkJpegCodec::initializeColorXform(const SkImageInfo& dstInfo, bool needsColorXform) {
if (needsColorXform) {
void SkJpegCodec::initializeColorXform(const SkImageInfo& dstInfo) {
if (needs_color_xform(dstInfo, this->getInfo())) {
fColorXform = SkColorSpaceXform::New(sk_ref_sp(this->getInfo().colorSpace()),
sk_ref_sp(dstInfo.colorSpace()));
if (!fColorXform && kRGBA_F16_SkColorType == dstInfo.colorType()) {
return false;
}
SkASSERT(fColorXform);
}
return true;
}
SkSampler* SkJpegCodec::getSampler(bool createIfNecessary) {
@ -661,14 +657,11 @@ SkCodec::Result SkJpegCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
return kInvalidInput;
}
// Check if we can decode to the requested destination and set the output color space
bool needsColorXform = needs_color_xform(dstInfo, this->getInfo());
if (!this->setOutputColorSpace(dstInfo, needsColorXform)) {
return kInvalidConversion;
}
this->initializeColorXform(dstInfo);
if (!this->initializeColorXform(dstInfo, needsColorXform)) {
return fDecoderMgr->returnFailure("initializeColorXform", kInvalidParameters);
// 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())) {

View File

@ -104,10 +104,10 @@ private:
*
* Sets the output color space.
*/
bool setOutputColorSpace(const SkImageInfo& dst, bool needsColorXform);
bool setOutputColorSpace(const SkImageInfo& dst);
void initializeSwizzler(const SkImageInfo& dstInfo, const Options& options);
bool initializeColorXform(const SkImageInfo& dstInfo, bool needsColorXform);
void initializeColorXform(const SkImageInfo& dstInfo);
void allocateStorage(const SkImageInfo& dstInfo);
int readRows(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, int count);

View File

@ -357,25 +357,6 @@ static int bytes_per_pixel(int bitsPerPixel) {
return bitsPerPixel / 8;
}
static bool png_conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) {
// Ensure the alpha type is valid
if (!valid_alpha(dst.alphaType(), src.alphaType())) {
return false;
}
// Check for supported color types
switch (dst.colorType()) {
case kRGBA_8888_SkColorType:
case kBGRA_8888_SkColorType:
case kRGBA_F16_SkColorType:
return true;
case kRGB_565_SkColorType:
return kOpaque_SkAlphaType == src.alphaType();
default:
return dst.colorType() == src.colorType();
}
}
void SkPngCodec::allocateStorage(const SkImageInfo& dstInfo) {
switch (fXformMode) {
case kSwizzleOnly_XformMode:
@ -422,7 +403,7 @@ public:
Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options,
SkPMColor ctable[], int* ctableCount) override {
if (!png_conversion_possible(dstInfo, this->getInfo()) ||
if (!conversion_possible(dstInfo, this->getInfo()) ||
!this->initializeXforms(dstInfo, options, ctable, ctableCount))
{
return kInvalidConversion;
@ -489,7 +470,7 @@ public:
Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options,
SkPMColor ctable[], int* ctableCount) override {
if (!png_conversion_possible(dstInfo, this->getInfo()) ||
if (!conversion_possible(dstInfo, this->getInfo()) ||
!this->initializeXforms(dstInfo, options, ctable, ctableCount))
{
return kInvalidConversion;
@ -807,20 +788,10 @@ bool SkPngCodec::initializeXforms(const SkImageInfo& dstInfo, const Options& opt
fSwizzler.reset(nullptr);
fColorXform = nullptr;
bool needsColorXform = needs_color_xform(dstInfo, this->getInfo());
if (needsColorXform) {
if (kGray_8_SkColorType == dstInfo.colorType() ||
kRGB_565_SkColorType == dstInfo.colorType())
{
return false;
}
if (needs_color_xform(dstInfo, this->getInfo())) {
fColorXform = SkColorSpaceXform::New(sk_ref_sp(this->getInfo().colorSpace()),
sk_ref_sp(dstInfo.colorSpace()));
if (!fColorXform && kRGBA_F16_SkColorType == dstInfo.colorType()) {
return false;
}
SkASSERT(fColorXform);
}
// If the image is RGBA and we have a color xform, we can skip the swizzler.
@ -907,7 +878,7 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
size_t rowBytes, const Options& options,
SkPMColor ctable[], int* ctableCount,
int* rowsDecoded) {
if (!png_conversion_possible(dstInfo, this->getInfo()) ||
if (!conversion_possible(dstInfo, this->getInfo()) ||
!this->initializeXforms(dstInfo, options, ctable, ctableCount))
{
return kInvalidConversion;

View File

@ -683,7 +683,7 @@ SkCodec::Result SkRawCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
size_t dstRowBytes, const Options& options,
SkPMColor ctable[], int* ctableCount,
int* rowsDecoded) {
if (!conversion_possible(requestedInfo, this->getInfo())) {
if (!conversion_possible_ignore_color_space(requestedInfo, this->getInfo())) {
SkCodecPrintf("Error: cannot convert input type to output type.\n");
return kInvalidConversion;
}

View File

@ -144,25 +144,6 @@ SkCodec* SkWebpCodec::NewFromStream(SkStream* stream) {
streamDeleter.release(), demux.release(), std::move(data));
}
static bool webp_conversion_possible(const SkImageInfo& dst, const SkImageInfo& src,
SkColorSpaceXform* colorXform) {
if (!valid_alpha(dst.alphaType(), src.alphaType())) {
return false;
}
switch (dst.colorType()) {
case kRGBA_F16_SkColorType:
return nullptr != colorXform;
case kBGRA_8888_SkColorType:
case kRGBA_8888_SkColorType:
return true;
case kRGB_565_SkColorType:
return nullptr == colorXform && src.alphaType() == kOpaque_SkAlphaType;
default:
return false;
}
}
SkISize SkWebpCodec::onGetScaledDimensions(float desiredScale) const {
SkISize dim = this->getInfo().dimensions();
// SkCodec treats zero dimensional images as errors, so the minimum size
@ -212,15 +193,15 @@ bool SkWebpCodec::onGetValidSubset(SkIRect* desiredSubset) const {
SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t rowBytes,
const Options& options, SkPMColor*, int*,
int* rowsDecodedPtr) {
if (!conversion_possible(dstInfo, this->getInfo())) {
return kInvalidConversion;
}
std::unique_ptr<SkColorSpaceXform> colorXform = nullptr;
if (needs_color_xform(dstInfo, this->getInfo())) {
colorXform = SkColorSpaceXform::New(sk_ref_sp(this->getInfo().colorSpace()),
sk_ref_sp(dstInfo.colorSpace()));
}
if (!webp_conversion_possible(dstInfo, this->getInfo(), colorXform.get())) {
return kInvalidConversion;
SkASSERT(colorXform);
}
WebPDecoderConfig config;

View File

@ -1120,3 +1120,33 @@ DEF_TEST(Codec_PngRoundTrip, r) {
REPORTER_ASSERT(r, SkCodec::kSuccess == result);
check_round_trip(r, bm2);
}
static void test_conversion_possible(skiatest::Reporter* r, const char* path,
bool testScanlineDecoder) {
SkAutoTDelete<SkStream> stream(resource(path));
SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream.release()));
SkImageInfo infoF16 = codec->getInfo().makeColorType(kRGBA_F16_SkColorType);
SkBitmap bm;
bm.allocPixels(infoF16);
SkCodec::Result result = codec->getPixels(infoF16, bm.getPixels(), bm.rowBytes());
REPORTER_ASSERT(r, SkCodec::kInvalidConversion == result);
if (testScanlineDecoder) {
result = codec->startScanlineDecode(infoF16);
REPORTER_ASSERT(r, SkCodec::kInvalidConversion == result);
}
infoF16 = infoF16.makeColorSpace(infoF16.colorSpace()->makeLinearGamma());
result = codec->getPixels(infoF16, bm.getPixels(), bm.rowBytes());
REPORTER_ASSERT(r, SkCodec::kSuccess == result);
if (testScanlineDecoder) {
result = codec->startScanlineDecode(infoF16);
REPORTER_ASSERT(r, SkCodec::kSuccess == result);
}
}
DEF_TEST(Codec_F16ConversionPossible, r) {
test_conversion_possible(r, "color_wheel.webp", false);
test_conversion_possible(r, "mandrill_512_q075.jpg", true);
test_conversion_possible(r, "yellow_rose.png", true);
}