Use SkSwizzler to convert from CMYK
Move convert_CMYK_to_RGBA into two functions in SkSwizzler: one for 565 and one for 8888. For simplicity, when converting to 8888, we no longer convert in place. BUG=skia:4476 Committed: https://skia.googlesource.com/skia/+/450ee8f26d39f975cf6af37a27de658ae5a9fa10 Review URL: https://codereview.chromium.org/1411083009
This commit is contained in:
parent
19e82e3b9f
commit
ef27d89b07
@ -23,69 +23,6 @@ extern "C" {
|
|||||||
#include "jpeglib.h"
|
#include "jpeglib.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Convert a row of CMYK samples to RGBA in place.
|
|
||||||
* Note that this method moves the row pointer.
|
|
||||||
* @param width the number of pixels in the row that is being converted
|
|
||||||
* CMYK is stored as four bytes per pixel
|
|
||||||
*/
|
|
||||||
static void convert_CMYK_to_RGBA(uint8_t* row, uint32_t width) {
|
|
||||||
// We will implement a crude conversion from CMYK -> RGB using formulas
|
|
||||||
// from easyrgb.com.
|
|
||||||
//
|
|
||||||
// CMYK -> CMY
|
|
||||||
// C = C * (1 - K) + K
|
|
||||||
// M = M * (1 - K) + K
|
|
||||||
// Y = Y * (1 - K) + K
|
|
||||||
//
|
|
||||||
// libjpeg actually gives us inverted CMYK, so we must subtract the
|
|
||||||
// original terms from 1.
|
|
||||||
// CMYK -> CMY
|
|
||||||
// C = (1 - C) * (1 - (1 - K)) + (1 - K)
|
|
||||||
// M = (1 - M) * (1 - (1 - K)) + (1 - K)
|
|
||||||
// Y = (1 - Y) * (1 - (1 - K)) + (1 - K)
|
|
||||||
//
|
|
||||||
// Simplifying the above expression.
|
|
||||||
// CMYK -> CMY
|
|
||||||
// C = 1 - CK
|
|
||||||
// M = 1 - MK
|
|
||||||
// Y = 1 - YK
|
|
||||||
//
|
|
||||||
// CMY -> RGB
|
|
||||||
// R = (1 - C) * 255
|
|
||||||
// G = (1 - M) * 255
|
|
||||||
// B = (1 - Y) * 255
|
|
||||||
//
|
|
||||||
// Therefore the full conversion is below. This can be verified at
|
|
||||||
// www.rapidtables.com (assuming inverted CMYK).
|
|
||||||
// CMYK -> RGB
|
|
||||||
// R = C * K * 255
|
|
||||||
// G = M * K * 255
|
|
||||||
// B = Y * K * 255
|
|
||||||
//
|
|
||||||
// As a final note, we have treated the CMYK values as if they were on
|
|
||||||
// a scale from 0-1, when in fact they are 8-bit ints scaling from 0-255.
|
|
||||||
// We must divide each CMYK component by 255 to obtain the true conversion
|
|
||||||
// we should perform.
|
|
||||||
// CMYK -> RGB
|
|
||||||
// R = C * K / 255
|
|
||||||
// G = M * K / 255
|
|
||||||
// B = Y * K / 255
|
|
||||||
for (uint32_t x = 0; x < width; x++, row += 4) {
|
|
||||||
#if defined(SK_PMCOLOR_IS_RGBA)
|
|
||||||
row[0] = SkMulDiv255Round(row[0], row[3]);
|
|
||||||
row[1] = SkMulDiv255Round(row[1], row[3]);
|
|
||||||
row[2] = SkMulDiv255Round(row[2], row[3]);
|
|
||||||
#else
|
|
||||||
uint8_t tmp = row[0];
|
|
||||||
row[0] = SkMulDiv255Round(row[2], row[3]);
|
|
||||||
row[1] = SkMulDiv255Round(row[1], row[3]);
|
|
||||||
row[2] = SkMulDiv255Round(tmp, row[3]);
|
|
||||||
#endif
|
|
||||||
row[3] = 0xFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SkJpegCodec::IsJpeg(SkStream* stream) {
|
bool SkJpegCodec::IsJpeg(SkStream* stream) {
|
||||||
static const uint8_t jpegSig[] = { 0xFF, 0xD8, 0xFF };
|
static const uint8_t jpegSig[] = { 0xFF, 0xD8, 0xFF };
|
||||||
char buffer[sizeof(jpegSig)];
|
char buffer[sizeof(jpegSig)];
|
||||||
@ -263,10 +200,7 @@ bool SkJpegCodec::setOutputColorSpace(const SkImageInfo& dst) {
|
|||||||
return true;
|
return true;
|
||||||
case kRGB_565_SkColorType:
|
case kRGB_565_SkColorType:
|
||||||
if (isCMYK) {
|
if (isCMYK) {
|
||||||
// FIXME (msarett): We need to support 565 here. It's not hard to do, considering
|
fDecoderMgr->dinfo()->out_color_space = JCS_CMYK;
|
||||||
// we already convert CMYK to RGBA, I just need to do it. I think it might be
|
|
||||||
// best to do this in SkSwizzler and also move convert_CMYK_to_RGBA into SkSwizzler.
|
|
||||||
return false;
|
|
||||||
} else {
|
} else {
|
||||||
#if defined(GOOGLE3)
|
#if defined(GOOGLE3)
|
||||||
return false;
|
return false;
|
||||||
@ -365,9 +299,22 @@ SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo,
|
|||||||
// If it's not, we want to know because it means our strategy is not optimal.
|
// If it's not, we want to know because it means our strategy is not optimal.
|
||||||
SkASSERT(1 == dinfo->rec_outbuf_height);
|
SkASSERT(1 == dinfo->rec_outbuf_height);
|
||||||
|
|
||||||
|
if (JCS_CMYK == dinfo->out_color_space) {
|
||||||
|
this->initializeSwizzler(dstInfo, options);
|
||||||
|
}
|
||||||
|
|
||||||
// Perform the decode a single row at a time
|
// Perform the decode a single row at a time
|
||||||
uint32_t dstHeight = dstInfo.height();
|
uint32_t dstHeight = dstInfo.height();
|
||||||
JSAMPLE* dstRow = (JSAMPLE*) dst;
|
|
||||||
|
JSAMPLE* dstRow;
|
||||||
|
if (fSwizzler) {
|
||||||
|
// write data to storage row, then sample using swizzler
|
||||||
|
dstRow = fSrcRow;
|
||||||
|
} else {
|
||||||
|
// write data directly to dst
|
||||||
|
dstRow = (JSAMPLE*) dst;
|
||||||
|
}
|
||||||
|
|
||||||
for (uint32_t y = 0; y < dstHeight; y++) {
|
for (uint32_t y = 0; y < dstHeight; y++) {
|
||||||
// Read rows of the image
|
// Read rows of the image
|
||||||
uint32_t lines = jpeg_read_scanlines(dinfo, &dstRow, 1);
|
uint32_t lines = jpeg_read_scanlines(dinfo, &dstRow, 1);
|
||||||
@ -379,13 +326,13 @@ SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo,
|
|||||||
return fDecoderMgr->returnFailure("Incomplete image data", kIncompleteInput);
|
return fDecoderMgr->returnFailure("Incomplete image data", kIncompleteInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to RGBA if necessary
|
if (fSwizzler) {
|
||||||
if (JCS_CMYK == dinfo->out_color_space) {
|
// use swizzler to sample row
|
||||||
convert_CMYK_to_RGBA(dstRow, dstInfo.width());
|
fSwizzler->swizzle(dst, dstRow);
|
||||||
|
dst = SkTAddOffset<JSAMPLE>(dst, dstRowBytes);
|
||||||
|
} else {
|
||||||
|
dstRow = SkTAddOffset<JSAMPLE>(dstRow, dstRowBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move to the next row
|
|
||||||
dstRow = SkTAddOffset<JSAMPLE>(dstRow, dstRowBytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return kSuccess;
|
return kSuccess;
|
||||||
@ -393,26 +340,30 @@ SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo,
|
|||||||
|
|
||||||
void SkJpegCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& options) {
|
void SkJpegCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& options) {
|
||||||
SkSwizzler::SrcConfig srcConfig = SkSwizzler::kUnknown;
|
SkSwizzler::SrcConfig srcConfig = SkSwizzler::kUnknown;
|
||||||
switch (dstInfo.colorType()) {
|
if (JCS_CMYK == fDecoderMgr->dinfo()->out_color_space) {
|
||||||
case kGray_8_SkColorType:
|
srcConfig = SkSwizzler::kCMYK;
|
||||||
srcConfig = SkSwizzler::kGray;
|
} else {
|
||||||
break;
|
switch (dstInfo.colorType()) {
|
||||||
case kRGBA_8888_SkColorType:
|
case kGray_8_SkColorType:
|
||||||
srcConfig = SkSwizzler::kRGBX;
|
srcConfig = SkSwizzler::kGray;
|
||||||
break;
|
break;
|
||||||
case kBGRA_8888_SkColorType:
|
case kRGBA_8888_SkColorType:
|
||||||
srcConfig = SkSwizzler::kBGRX;
|
srcConfig = SkSwizzler::kRGBX;
|
||||||
break;
|
break;
|
||||||
case kRGB_565_SkColorType:
|
case kBGRA_8888_SkColorType:
|
||||||
srcConfig = SkSwizzler::kRGB_565;
|
srcConfig = SkSwizzler::kBGRX;
|
||||||
break;
|
break;
|
||||||
default:
|
case kRGB_565_SkColorType:
|
||||||
// This function should only be called if the colorType is supported by jpeg
|
srcConfig = SkSwizzler::kRGB_565;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// This function should only be called if the colorType is supported by jpeg
|
||||||
#if defined(GOOGLE3)
|
#if defined(GOOGLE3)
|
||||||
SK_CRASH();
|
SK_CRASH();
|
||||||
#else
|
#else
|
||||||
SkASSERT(false);
|
SkASSERT(false);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fSwizzler.reset(SkSwizzler::CreateSwizzler(srcConfig, nullptr, dstInfo, options));
|
fSwizzler.reset(SkSwizzler::CreateSwizzler(srcConfig, nullptr, dstInfo, options));
|
||||||
@ -454,8 +405,9 @@ SkCodec::Result SkJpegCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
|
|||||||
return kInvalidInput;
|
return kInvalidInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We will need a swizzler if we are performing a subset decode
|
// We will need a swizzler if we are performing a subset decode or
|
||||||
if (options.fSubset) {
|
// converting from CMYK.
|
||||||
|
if (options.fSubset || JCS_CMYK == fDecoderMgr->dinfo()->out_color_space) {
|
||||||
this->initializeSwizzler(dstInfo, options);
|
this->initializeSwizzler(dstInfo, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -485,12 +437,7 @@ int SkJpegCodec::onGetScanlines(void* dst, int count, size_t rowBytes) {
|
|||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to RGBA if necessary
|
if (fSwizzler) {
|
||||||
if (JCS_CMYK == fDecoderMgr->dinfo()->out_color_space) {
|
|
||||||
convert_CMYK_to_RGBA(dstRow, fDecoderMgr->dinfo()->output_width);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(fSwizzler) {
|
|
||||||
// use swizzler to sample row
|
// use swizzler to sample row
|
||||||
fSwizzler->swizzle(dst, dstRow);
|
fSwizzler->swizzle(dst, dstRow);
|
||||||
dst = SkTAddOffset<JSAMPLE>(dst, rowBytes);
|
dst = SkTAddOffset<JSAMPLE>(dst, rowBytes);
|
||||||
|
@ -480,6 +480,89 @@ static SkSwizzler::ResultAlpha swizzle_rgba_to_n32_premul_skipZ(
|
|||||||
return COMPUTE_RESULT_ALPHA;
|
return COMPUTE_RESULT_ALPHA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// kCMYK
|
||||||
|
//
|
||||||
|
// CMYK is stored as four bytes per pixel.
|
||||||
|
//
|
||||||
|
// We will implement a crude conversion from CMYK -> RGB using formulas
|
||||||
|
// from easyrgb.com.
|
||||||
|
//
|
||||||
|
// CMYK -> CMY
|
||||||
|
// C = C * (1 - K) + K
|
||||||
|
// M = M * (1 - K) + K
|
||||||
|
// Y = Y * (1 - K) + K
|
||||||
|
//
|
||||||
|
// libjpeg actually gives us inverted CMYK, so we must subtract the
|
||||||
|
// original terms from 1.
|
||||||
|
// CMYK -> CMY
|
||||||
|
// C = (1 - C) * (1 - (1 - K)) + (1 - K)
|
||||||
|
// M = (1 - M) * (1 - (1 - K)) + (1 - K)
|
||||||
|
// Y = (1 - Y) * (1 - (1 - K)) + (1 - K)
|
||||||
|
//
|
||||||
|
// Simplifying the above expression.
|
||||||
|
// CMYK -> CMY
|
||||||
|
// C = 1 - CK
|
||||||
|
// M = 1 - MK
|
||||||
|
// Y = 1 - YK
|
||||||
|
//
|
||||||
|
// CMY -> RGB
|
||||||
|
// R = (1 - C) * 255
|
||||||
|
// G = (1 - M) * 255
|
||||||
|
// B = (1 - Y) * 255
|
||||||
|
//
|
||||||
|
// Therefore the full conversion is below. This can be verified at
|
||||||
|
// www.rapidtables.com (assuming inverted CMYK).
|
||||||
|
// CMYK -> RGB
|
||||||
|
// R = C * K * 255
|
||||||
|
// G = M * K * 255
|
||||||
|
// B = Y * K * 255
|
||||||
|
//
|
||||||
|
// As a final note, we have treated the CMYK values as if they were on
|
||||||
|
// a scale from 0-1, when in fact they are 8-bit ints scaling from 0-255.
|
||||||
|
// We must divide each CMYK component by 255 to obtain the true conversion
|
||||||
|
// we should perform.
|
||||||
|
// CMYK -> RGB
|
||||||
|
// R = C * K / 255
|
||||||
|
// G = M * K / 255
|
||||||
|
// B = Y * K / 255
|
||||||
|
static SkSwizzler::ResultAlpha swizzle_cmyk_to_n32(
|
||||||
|
void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth,
|
||||||
|
int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) {
|
||||||
|
|
||||||
|
src += offset;
|
||||||
|
SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
|
||||||
|
for (int x = 0; x < dstWidth; x++) {
|
||||||
|
const uint8_t r = SkMulDiv255Round(src[0], src[3]);
|
||||||
|
const uint8_t g = SkMulDiv255Round(src[1], src[3]);
|
||||||
|
const uint8_t b = SkMulDiv255Round(src[2], src[3]);
|
||||||
|
|
||||||
|
dst[x] = SkPackARGB32NoCheck(0xFF, r, g, b);
|
||||||
|
src += deltaSrc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CMYK is always opaque
|
||||||
|
return SkSwizzler::kOpaque_ResultAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SkSwizzler::ResultAlpha swizzle_cmyk_to_565(
|
||||||
|
void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth,
|
||||||
|
int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) {
|
||||||
|
|
||||||
|
src += offset;
|
||||||
|
uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow;
|
||||||
|
for (int x = 0; x < dstWidth; x++) {
|
||||||
|
const uint8_t r = SkMulDiv255Round(src[0], src[3]);
|
||||||
|
const uint8_t g = SkMulDiv255Round(src[1], src[3]);
|
||||||
|
const uint8_t b = SkMulDiv255Round(src[2], src[3]);
|
||||||
|
|
||||||
|
dst[x] = SkPack888ToRGB16(r, g, b);
|
||||||
|
src += deltaSrc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CMYK is always opaque
|
||||||
|
return SkSwizzler::kOpaque_ResultAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
FIXME: This was my idea to cheat in order to continue taking advantage of skipping zeroes.
|
FIXME: This was my idea to cheat in order to continue taking advantage of skipping zeroes.
|
||||||
This would be fine for drawing normally, but not for drawing with transfer modes. Being
|
This would be fine for drawing normally, but not for drawing with transfer modes. Being
|
||||||
@ -672,6 +755,19 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc,
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case kCMYK:
|
||||||
|
switch (dstInfo.colorType()) {
|
||||||
|
case kN32_SkColorType:
|
||||||
|
proc = &swizzle_cmyk_to_n32;
|
||||||
|
break;
|
||||||
|
case kRGB_565_SkColorType:
|
||||||
|
proc = &swizzle_cmyk_to_565;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ public:
|
|||||||
kRGBA,
|
kRGBA,
|
||||||
kBGRA,
|
kBGRA,
|
||||||
kRGB_565,
|
kRGB_565,
|
||||||
|
kCMYK,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -97,6 +98,7 @@ public:
|
|||||||
case kRGBA:
|
case kRGBA:
|
||||||
case kBGRX:
|
case kBGRX:
|
||||||
case kBGRA:
|
case kBGRA:
|
||||||
|
case kCMYK:
|
||||||
return 32;
|
return 32;
|
||||||
default:
|
default:
|
||||||
SkASSERT(false);
|
SkASSERT(false);
|
||||||
|
@ -408,7 +408,7 @@ DEF_TEST(Codec, r) {
|
|||||||
check(r, "randPixels.gif", SkISize::Make(8, 8), true, false, true, false);
|
check(r, "randPixels.gif", SkISize::Make(8, 8), true, false, true, false);
|
||||||
|
|
||||||
// JPG
|
// JPG
|
||||||
check(r, "CMYK.jpg", SkISize::Make(642, 516), true, false, false);
|
check(r, "CMYK.jpg", SkISize::Make(642, 516), true, false, true);
|
||||||
check(r, "color_wheel.jpg", SkISize::Make(128, 128), true, false);
|
check(r, "color_wheel.jpg", SkISize::Make(128, 128), true, false);
|
||||||
// grayscale.jpg is too small to test incomplete
|
// grayscale.jpg is too small to test incomplete
|
||||||
check(r, "grayscale.jpg", SkISize::Make(128, 128), true, false, true, false);
|
check(r, "grayscale.jpg", SkISize::Make(128, 128), true, false, true, false);
|
||||||
|
Loading…
Reference in New Issue
Block a user