Index: src/codec/SkJpegCodec.cpp |
diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp |
index 6850a7782512aa0cc731982c7fb3cff61c93b0ec..9975748cf4b4ce4010ea9dc79eca8cac6614fc93 100644 |
--- a/src/codec/SkJpegCodec.cpp |
+++ b/src/codec/SkJpegCodec.cpp |
@@ -21,6 +21,69 @@ |
extern "C" { |
#include "jerror.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) { |
@@ -200,7 +263,10 @@ |
return true; |
case kRGB_565_SkColorType: |
if (isCMYK) { |
- fDecoderMgr->dinfo()->out_color_space = JCS_CMYK; |
+ // FIXME (msarett): We need to support 565 here. It's not hard to do, considering |
+ // 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 { |
#if defined(GOOGLE3) |
return false; |
@@ -299,22 +365,9 @@ |
// If it's not, we want to know because it means our strategy is not optimal. |
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 |
uint32_t dstHeight = dstInfo.height(); |
- |
- JSAMPLE* dstRow; |
- if (fSwizzler) { |
- // write data to storage row, then sample using swizzler |
- dstRow = fSrcRow; |
- } else { |
- // write data directly to dst |
- dstRow = (JSAMPLE*) dst; |
- } |
- |
+ JSAMPLE* dstRow = (JSAMPLE*) dst; |
for (uint32_t y = 0; y < dstHeight; y++) { |
// Read rows of the image |
uint32_t lines = jpeg_read_scanlines(dinfo, &dstRow, 1); |
@@ -326,13 +379,13 @@ |
return fDecoderMgr->returnFailure("Incomplete image data", kIncompleteInput); |
} |
- if (fSwizzler) { |
- // use swizzler to sample row |
- fSwizzler->swizzle(dst, dstRow); |
- dst = SkTAddOffset<JSAMPLE>(dst, dstRowBytes); |
- } else { |
- dstRow = SkTAddOffset<JSAMPLE>(dstRow, dstRowBytes); |
- } |
+ // Convert to RGBA if necessary |
+ if (JCS_CMYK == dinfo->out_color_space) { |
+ convert_CMYK_to_RGBA(dstRow, dstInfo.width()); |
+ } |
+ |
+ // Move to the next row |
+ dstRow = SkTAddOffset<JSAMPLE>(dstRow, dstRowBytes); |
} |
return kSuccess; |
@@ -340,30 +393,26 @@ |
void SkJpegCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& options) { |
SkSwizzler::SrcConfig srcConfig = SkSwizzler::kUnknown; |
- if (JCS_CMYK == fDecoderMgr->dinfo()->out_color_space) { |
- srcConfig = SkSwizzler::kCMYK; |
- } else { |
- switch (dstInfo.colorType()) { |
- case kGray_8_SkColorType: |
- srcConfig = SkSwizzler::kGray; |
- break; |
- case kRGBA_8888_SkColorType: |
- srcConfig = SkSwizzler::kRGBX; |
- break; |
- case kBGRA_8888_SkColorType: |
- srcConfig = SkSwizzler::kBGRX; |
- break; |
- case kRGB_565_SkColorType: |
- srcConfig = SkSwizzler::kRGB_565; |
- break; |
- default: |
- // This function should only be called if the colorType is supported by jpeg |
+ switch (dstInfo.colorType()) { |
+ case kGray_8_SkColorType: |
+ srcConfig = SkSwizzler::kGray; |
+ break; |
+ case kRGBA_8888_SkColorType: |
+ srcConfig = SkSwizzler::kRGBX; |
+ break; |
+ case kBGRA_8888_SkColorType: |
+ srcConfig = SkSwizzler::kBGRX; |
+ break; |
+ case kRGB_565_SkColorType: |
+ srcConfig = SkSwizzler::kRGB_565; |
+ break; |
+ default: |
+ // This function should only be called if the colorType is supported by jpeg |
#if defined(GOOGLE3) |
- SK_CRASH(); |
+ SK_CRASH(); |
#else |
- SkASSERT(false); |
+ SkASSERT(false); |
#endif |
- } |
} |
fSwizzler.reset(SkSwizzler::CreateSwizzler(srcConfig, nullptr, dstInfo, options)); |
@@ -405,9 +454,8 @@ |
return kInvalidInput; |
} |
- // We will need a swizzler if we are performing a subset decode or |
- // converting from CMYK. |
- if (options.fSubset || JCS_CMYK == fDecoderMgr->dinfo()->out_color_space) { |
+ // We will need a swizzler if we are performing a subset decode |
+ if (options.fSubset) { |
this->initializeSwizzler(dstInfo, options); |
} |
@@ -437,7 +485,12 @@ |
return y; |
} |
- if (fSwizzler) { |
+ // Convert to RGBA if necessary |
+ if (JCS_CMYK == fDecoderMgr->dinfo()->out_color_space) { |
+ convert_CMYK_to_RGBA(dstRow, fDecoderMgr->dinfo()->output_width); |
+ } |
+ |
+ if(fSwizzler) { |
// use swizzler to sample row |
fSwizzler->swizzle(dst, dstRow); |
dst = SkTAddOffset<JSAMPLE>(dst, rowBytes); |