| 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();
|
| }
|
|
|