Index: third_party/WebKit/Source/platform/image-decoders/jpeg/JPEGImageDecoder.cpp |
diff --git a/third_party/WebKit/Source/platform/image-decoders/jpeg/JPEGImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/jpeg/JPEGImageDecoder.cpp |
index a77a3f4bff35338403be10a21d71786f4ac15189..3b6045390f61bced1b4968e703a3bd4dabab61ee 100644 |
--- a/third_party/WebKit/Source/platform/image-decoders/jpeg/JPEGImageDecoder.cpp |
+++ b/third_party/WebKit/Source/platform/image-decoders/jpeg/JPEGImageDecoder.cpp |
@@ -65,7 +65,7 @@ inline J_COLOR_SPACE rgbOutputColorSpace() { return JCS_EXT_RGBA; } |
#else // Output little-endian BGRA pixels. |
inline J_COLOR_SPACE rgbOutputColorSpace() { return JCS_EXT_BGRA; } |
#endif |
-inline bool turboSwizzled(J_COLOR_SPACE colorSpace) { return colorSpace == JCS_EXT_RGBA || colorSpace == JCS_EXT_BGRA; } |
+inline bool turboSwizzled(J_COLOR_SPACE colorSpace) { return colorSpace == JCS_EXT_RGBA || colorSpace == JCS_EXT_BGRA || colorSpace == JCS_RGB565; } |
inline bool colorSpaceHasAlpha(J_COLOR_SPACE colorSpace) { return turboSwizzled(colorSpace); } |
#else |
inline J_COLOR_SPACE rgbOutputColorSpace() { return JCS_RGB; } |
@@ -432,6 +432,16 @@ public: |
bool decode(bool onlySize) |
{ |
+ if ((m_state == JPEG_DECOMPRESS_SEQUENTIAL || m_state == JPEG_DECOMPRESS_PROGRESSIVE) |
+ && m_decoder->hasFramebufferChanged()) { |
+ // when previous attempts to decode failed with not all data available, |
+ // decoder continues. Once that all data is available (and there is no alpha) |
+ // and client calls decode to rgb565 or downsampling, it is needed to restart decompression |
+ // FIXME restart decompression without reading header |
+ clearBuffer(); |
+ m_state = JPEG_HEADER; |
+ } |
+ |
// We need to do the setjmp here. Otherwise bad things will happen |
if (setjmp(m_err.setjmp_buffer)) |
return m_decoder->setFailed(); |
@@ -472,6 +482,21 @@ public: |
default: |
return m_decoder->setFailed(); |
} |
+ if (m_decoder->decodeToRGB565Enabled()) { |
+ if (m_info.out_color_space == rgbOutputColorSpace()) { |
+ // the rest would need to be converted manually on write |
+#if defined (JCS_ALPHA_EXTENSIONS) |
+ m_info.out_color_space = JCS_RGB565; |
+#endif |
+#if USE(QCMSLIB) |
+ // if there is colorprofile, use JCS_RGB as QCMS is not supporting RGB565 |
+ // conversion, RGB->RGB565 happens in outputRows<>() |
+ if (!m_decoder->ignoresGammaAndColorProfile()) |
+ m_info.out_color_space = JCS_RGB; |
+#endif |
+ } |
+ } |
+ // FALL THROUGH |
m_state = JPEG_START_DECOMPRESS; |
@@ -588,6 +613,7 @@ public: |
case JPEG_DECOMPRESS_PROGRESSIVE: |
if (m_state == JPEG_DECOMPRESS_PROGRESSIVE) { |
+ |
int status = 0; |
do { |
decoder_error_mgr* err = reinterpret_cast_ptr<decoder_error_mgr*>(m_info.err); |
@@ -780,6 +806,8 @@ void term_source(j_decompress_ptr jd) |
JPEGImageDecoder::JPEGImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOption colorOptions, size_t maxDecodedBytes) |
: ImageDecoder(alphaOption, colorOptions, maxDecodedBytes) |
, m_hasColorProfile(false) |
+ , m_decodeColor(ImageFrame::RGBA8888) |
+ , m_originalMaxDecodedBytes(maxDecodedBytes) |
{ |
} |
@@ -821,7 +849,8 @@ IntSize JPEGImageDecoder::decodedYUVSize(int component, ImageDecoder::SizeType s |
unsigned JPEGImageDecoder::desiredScaleNumerator() const |
{ |
- size_t originalBytes = size().width() * size().height() * 4; |
+ size_t originalBytes = size().width() * size().height() |
+ * (decodeToRGB565Enabled() ? 2 : 4); |
if (originalBytes <= m_maxDecodedBytes) { |
return scaleDenominator; |
} |
@@ -841,6 +870,64 @@ bool JPEGImageDecoder::canDecodeToYUV() |
return isSizeAvailable() && m_reader->info()->out_color_space == JCS_YCbCr; |
} |
+void JPEGImageDecoder::getAvailableDecodeAndScale(ImageFrame::ColorType* outType, float* scale, float* lowerScale) |
+{ |
+ if (outType) { |
+ if (*outType != ImageFrame::RGBA8888 && *outType != ImageFrame::RGB565) |
+ *outType = ImageFrame::RGBA8888; |
+ } |
+ if (scale) { |
+ unsigned upper = static_cast<unsigned>(ceil(*scale * scaleDenominator)); |
+ upper = (upper > scaleDenominator) ? scaleDenominator : upper; |
+ unsigned lower = upper - 1; |
+ *scale = static_cast<float>(upper) / static_cast<float>(scaleDenominator); |
+ |
+ if (lowerScale) |
+ *lowerScale = static_cast<float>(lower) / static_cast<float>(scaleDenominator); |
+ return; |
+ } |
+ if (lowerScale) |
+ *lowerScale = 1.0f; |
+} |
+ |
+bool JPEGImageDecoder::activateDecodeAndScale(ImageFrame::ColorType outColor, size_t maxDecodedBytes) |
+{ |
+ ASSERT(m_decodeColor == ImageFrame::RGBA8888); |
+ if (outColor == ImageFrame::RGB565) { |
+ m_decodeColor = ImageFrame::RGB565; |
+ } else { |
+ if (outColor != ImageFrame::RGBA8888) { |
+ return false; |
+ } |
+ m_decodeColor = ImageFrame::RGBA8888; |
+ } |
+ m_originalMaxDecodedBytes = m_maxDecodedBytes; |
+ m_maxDecodedBytes = maxDecodedBytes; |
+ return true; |
+} |
+ |
+void JPEGImageDecoder::disableDecodeAndScale() |
+{ |
+ m_decodeColor = ImageFrame::RGBA8888; |
+ m_maxDecodedBytes = m_originalMaxDecodedBytes; |
+} |
+ |
+bool JPEGImageDecoder::hasFramebufferChanged() const |
+{ |
+ // for decoding that doesn't restart (e.g. when previous decodings didn't have |
+ // all of the data, and meanwhile decoding to different output size was requested |
+ if (m_frameBufferCache.isEmpty() || hasImagePlanes()) { |
+ return false; |
+ } |
+ const ImageFrame& buffer = m_frameBufferCache[0]; |
+ // size is important here, not the decoded color - so use the current one |
+ // when checking size change |
+ if (buffer.status() != ImageFrame::FrameEmpty && !buffer.hasSize(m_decodedSize, m_decodeColor)) |
+ return true; |
+ return false; |
+} |
+ |
+ |
bool JPEGImageDecoder::decodeToYUV() |
{ |
if (!hasImagePlanes()) |
@@ -856,18 +943,18 @@ void JPEGImageDecoder::setImagePlanes(PassOwnPtr<ImagePlanes> imagePlanes) |
m_imagePlanes = imagePlanes; |
} |
-template <J_COLOR_SPACE colorSpace> void setPixel(ImageFrame& buffer, ImageFrame::PixelData* pixel, JSAMPARRAY samples, int column) |
+template <J_COLOR_SPACE colorSpace, class T> void setPixel(ImageFrame& buffer, T* pixel, JSAMPARRAY samples, int column) |
{ |
ASSERT_NOT_REACHED(); |
} |
-template <> void setPixel<JCS_RGB>(ImageFrame& buffer, ImageFrame::PixelData* pixel, JSAMPARRAY samples, int column) |
+template <> void setPixel<JCS_RGB, ImageFrame::PixelData>(ImageFrame& buffer, ImageFrame::PixelData* pixel, JSAMPARRAY samples, int column) |
{ |
JSAMPLE* jsample = *samples + column * 3; |
buffer.setRGBARaw(pixel, jsample[0], jsample[1], jsample[2], 255); |
} |
-template <> void setPixel<JCS_CMYK>(ImageFrame& buffer, ImageFrame::PixelData* pixel, JSAMPARRAY samples, int column) |
+template <> void setPixel<JCS_CMYK, ImageFrame::PixelData>(ImageFrame& buffer, ImageFrame::PixelData* pixel, JSAMPARRAY samples, int column) |
{ |
JSAMPLE* jsample = *samples + column * 4; |
@@ -884,7 +971,30 @@ template <> void setPixel<JCS_CMYK>(ImageFrame& buffer, ImageFrame::PixelData* p |
buffer.setRGBARaw(pixel, jsample[0] * k / 255, jsample[1] * k / 255, jsample[2] * k / 255, 255); |
} |
-template <J_COLOR_SPACE colorSpace> bool outputRows(JPEGImageReader* reader, ImageFrame& buffer) |
+template <> void setPixel<JCS_RGB, ImageFrame::PixelData16>(ImageFrame& buffer, ImageFrame::PixelData16* pixel, JSAMPARRAY samples, int column) |
+{ |
+ JSAMPLE* jsample = *samples + column * 3; |
+ buffer.setRGB565(pixel, jsample[0], jsample[1], jsample[2]); |
+} |
+ |
+template <> void setPixel<JCS_CMYK, ImageFrame::PixelData16>(ImageFrame& buffer, ImageFrame::PixelData16* pixel, JSAMPARRAY samples, int column) |
+{ |
+ JSAMPLE* jsample = *samples + column * 4; |
+ |
+ // Source is 'Inverted CMYK', output is RGB. |
+ // See: http://www.easyrgb.com/math.php?MATH=M12#text12 |
+ // Or: http://www.ilkeratalay.com/colorspacesfaq.php#rgb |
+ // From CMYK to CMY: |
+ // X = X * (1 - K ) + K [for X = C, M, or Y] |
+ // Thus, from Inverted CMYK to CMY is: |
+ // X = (1-iX) * (1 - (1-iK)) + (1-iK) => 1 - iX*iK |
+ // From CMY (0..1) to RGB (0..1): |
+ // R = 1 - C => 1 - (1 - iC*iK) => iC*iK [G and B similar] |
+ unsigned k = jsample[3]; |
+ buffer.setRGB565(pixel, jsample[0] * k / 255, jsample[1] * k / 255, jsample[2] * k / 255); |
+} |
+ |
+template <J_COLOR_SPACE colorSpace, class T> bool outputRows(JPEGImageReader* reader, ImageFrame& buffer) |
{ |
JSAMPARRAY samples = reader->samples(); |
jpeg_decompress_struct* info = reader->info(); |
@@ -901,9 +1011,9 @@ template <J_COLOR_SPACE colorSpace> bool outputRows(JPEGImageReader* reader, Ima |
if (reader->colorTransform() && colorSpace == JCS_RGB) |
qcms_transform_data(reader->colorTransform(), *samples, *samples, width); |
#endif |
- ImageFrame::PixelData* pixel = buffer.getAddr(0, y); |
+ T* pixel = buffer.getAddrT<T>(0, y); |
for (int x = 0; x < width; ++pixel, ++x) |
- setPixel<colorSpace>(buffer, pixel, samples, x); |
+ setPixel<colorSpace, T>(buffer, pixel, samples, x); |
} |
buffer.setPixelsChanged(true); |
@@ -1005,12 +1115,16 @@ bool JPEGImageDecoder::outputScanlines() |
ASSERT(info->output_width == static_cast<JDIMENSION>(m_decodedSize.width())); |
ASSERT(info->output_height == static_cast<JDIMENSION>(m_decodedSize.height())); |
- if (!buffer.setSize(info->output_width, info->output_height)) |
+ if (!buffer.setSize(info->output_width, info->output_height, decodeToRGB565Enabled() |
+ ? ImageFrame::RGB565 : ImageFrame::RGBA8888)) |
return setFailed(); |
buffer.setStatus(ImageFrame::FramePartial); |
// The buffer is transparent outside the decoded area while the image is |
// loading. The image will be marked fully opaque in complete(). |
- buffer.setHasAlpha(true); |
+ // For RGB565 this would just fail, having bitmap opaque during decoding |
+ // but, since RGB565 is used only for fully loaded content, this is not a problem |
+ if (!decodeToRGB565Enabled()) |
+ buffer.setHasAlpha(true); |
// For JPEGs, the frame always fills the entire image. |
buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); |
@@ -1019,9 +1133,12 @@ bool JPEGImageDecoder::outputScanlines() |
#if defined(TURBO_JPEG_RGB_SWIZZLE) |
if (turboSwizzled(info->out_color_space)) { |
while (info->output_scanline < info->output_height) { |
- unsigned char* row = reinterpret_cast_ptr<unsigned char*>(buffer.getAddr(0, info->output_scanline)); |
- if (jpeg_read_scanlines(info, &row, 1) != 1) |
+ unsigned char* row = decodeToRGB565Enabled() |
+ ? reinterpret_cast_ptr<unsigned char*>(buffer.getAddrT<ImageFrame::PixelData16>(0, info->output_scanline)) |
+ : reinterpret_cast_ptr<unsigned char*>(buffer.getAddr(0, info->output_scanline)); |
+ if (jpeg_read_scanlines(info, &row, 1) != 1) { |
return false; |
+ } |
#if USE(QCMSLIB) |
if (qcms_transform* transform = m_reader->colorTransform()) |
qcms_transform_data_type(transform, row, row, info->output_width, rgbOutputColorSpace() == JCS_EXT_BGRA ? QCMS_OUTPUT_BGRX : QCMS_OUTPUT_RGBX); |
@@ -1034,9 +1151,13 @@ bool JPEGImageDecoder::outputScanlines() |
switch (info->out_color_space) { |
case JCS_RGB: |
- return outputRows<JCS_RGB>(m_reader.get(), buffer); |
+ if (decodeToRGB565Enabled()) |
+ return outputRows<JCS_RGB, ImageFrame::PixelData16>(m_reader.get(), buffer); |
+ return outputRows<JCS_RGB, ImageFrame::PixelData>(m_reader.get(), buffer); |
case JCS_CMYK: |
- return outputRows<JCS_CMYK>(m_reader.get(), buffer); |
+ if (decodeToRGB565Enabled()) |
+ return outputRows<JCS_CMYK, ImageFrame::PixelData16>(m_reader.get(), buffer); |
+ return outputRows<JCS_CMYK, ImageFrame::PixelData>(m_reader.get(), buffer); |
default: |
ASSERT_NOT_REACHED(); |
} |