Chromium Code Reviews| Index: Source/platform/image-decoders/jpeg/JPEGImageDecoder.cpp |
| diff --git a/Source/platform/image-decoders/jpeg/JPEGImageDecoder.cpp b/Source/platform/image-decoders/jpeg/JPEGImageDecoder.cpp |
| index 90db0a868ff04a89f4320bbc189088933ca789e9..01c88edf665db7680822c41c71b6131641f5c102 100644 |
| --- a/Source/platform/image-decoders/jpeg/JPEGImageDecoder.cpp |
| +++ b/Source/platform/image-decoders/jpeg/JPEGImageDecoder.cpp |
| @@ -242,6 +242,14 @@ static void readColorProfile(jpeg_decompress_struct* info, ColorProfile& colorPr |
| } |
| #endif |
| +static void computeUVSize(const jpeg_decompress_struct* info, int& width, int& height) |
|
Stephen White
2014/07/23 21:45:53
I prefer pointers to non-const refs, but that's ju
Peter Kasting
2014/07/23 22:17:37
I agree -- pointers seem both more common and clea
sugoi1
2014/07/24 15:48:30
Done.
sugoi1
2014/07/24 15:48:30
Done.
|
| +{ |
| + int h = info->cur_comp_info[0]->h_samp_factor; |
|
Peter Kasting
2014/07/23 22:17:37
Nit: Personally, I wouldn't add spaces to line up
sugoi1
2014/07/24 15:48:31
Done.
|
| + int v = info->cur_comp_info[0]->v_samp_factor; |
| + width = (info->output_width + h - 1) / h; |
| + height = (info->output_height + v - 1) / v; |
| +} |
| + |
| class JPEGImageReader { |
| WTF_MAKE_FAST_ALLOCATED; |
| public: |
| @@ -347,10 +355,37 @@ public: |
| return false; // I/O suspension. |
| switch (m_info.jpeg_color_space) { |
| + case JCS_YCbCr: |
| + // libjpeg can convert YCbCr image pixels to RGB. |
| + m_info.out_color_space = rgbOutputColorSpace(); |
| + if (m_decoder->hardwareDecoding() |
| + && (DCTSIZE == 8) // This code handles a DCTSIZE of 8 |
|
Stephen White
2014/07/23 21:45:53
Comment seems superfluous to me.
Peter Kasting
2014/07/23 22:17:37
Indeed, please remove.
sugoi1
2014/07/24 15:48:30
Done.
sugoi1
2014/07/24 15:48:30
Done.
|
| + && (m_info.num_components == 3) |
| + && (m_info.scale_denom <= 8) |
| + && (m_info.cur_comp_info[1]->h_samp_factor == 1) |
| + && (m_info.cur_comp_info[1]->v_samp_factor == 1) |
| + && (m_info.cur_comp_info[2]->h_samp_factor == 1) |
| + && (m_info.cur_comp_info[2]->v_samp_factor == 1)) { |
| + int h = m_info.cur_comp_info[0]->h_samp_factor; |
| + int v = m_info.cur_comp_info[0]->v_samp_factor; |
| + // Only set YUV mode if the format is supported |
| + // 4:4:4 : (h == 1) && (v == 1) |
| + // 4:4:0 : (h == 1) && (v == 2) |
| + // 4:2:2 : (h == 2) && (v == 1) |
| + // 4:2:0 : (h == 2) && (v == 2) |
| + // 4:1:1 : (h == 4) && (v == 1) |
| + // 4:1:0 : (h == 4) && (v == 2) |
| + if (((h == 1) || (h == 2) || (h == 4)) && ((v == 1) || (v == 2))) { |
| + m_info.out_color_space = JCS_YCbCr; |
| + m_info.raw_data_out = TRUE; |
| + |
| + // printf("YUV 4:%d:%d\n", 4 / h, (v == 1) ? 4 / h : 0); |
|
Stephen White
2014/07/23 21:45:53
Please remove commented-out code.
sugoi1
2014/07/24 15:48:30
Done.
|
| + } |
| + } |
| + break; |
| case JCS_GRAYSCALE: |
| case JCS_RGB: |
| - case JCS_YCbCr: |
| - // libjpeg can convert GRAYSCALE and YCbCr image pixels to RGB. |
| + // libjpeg can convert GRAYSCALE image pixels to RGB. |
| m_info.out_color_space = rgbOutputColorSpace(); |
| #if defined(TURBO_JPEG_RGB_SWIZZLE) |
| if (m_info.saw_JFIF_marker) |
| @@ -427,7 +462,7 @@ public: |
| // to jpeg_start_compress(). |
| // FIXME: note that some output color spaces do not need the samples |
| // buffer. Remove this allocation for those color spaces. |
| - m_samples = (*m_info.mem->alloc_sarray)(reinterpret_cast<j_common_ptr>(&m_info), JPOOL_IMAGE, m_info.output_width * 4, 1); |
| + m_samples = (*m_info.mem->alloc_sarray)(reinterpret_cast<j_common_ptr>(&m_info), JPOOL_IMAGE, m_info.output_width * 4, m_info.out_color_space == JCS_YCbCr ? 2: 1); |
|
Peter Kasting
2014/07/23 22:17:37
Nit: Space before ':'
sugoi1
2014/07/24 15:48:30
Done.
|
| // Start decompressor. |
| if (!jpeg_start_decompress(&m_info)) |
| @@ -597,6 +632,7 @@ JPEGImageDecoder::JPEGImageDecoder(ImageSource::AlphaOption alphaOption, |
| size_t maxDecodedBytes) |
| : ImageDecoder(alphaOption, gammaAndColorProfileOption, maxDecodedBytes) |
| , m_hasColorProfile(false) |
| + , m_hardwareDecoding(false) |
| { |
| } |
| @@ -629,6 +665,20 @@ void JPEGImageDecoder::setDecodedSize(unsigned width, unsigned height) |
| m_decodedSize = IntSize(width, height); |
| } |
| +IntSize JPEGImageDecoder::decodedSize(int component) const |
| +{ |
| + if (((component == 1) || (component == 2)) && m_reader.get()) { // Asking for U or V |
| + const jpeg_decompress_struct* info = m_reader->info(); |
| + if (info && (info->out_color_space == JCS_YCbCr)) { |
| + int w(0), h(0); |
|
Stephen White
2014/07/23 21:45:53
Initializers look superfluous here: computeUVSize(
sugoi1
2014/07/24 15:48:31
Done.
|
| + computeUVSize(info, w, h); |
| + return IntSize(w, h); |
| + } |
| + } |
| + |
| + return m_decodedSize; |
| +} |
| + |
| unsigned JPEGImageDecoder::desiredScaleNumerator() const |
| { |
| size_t originalBytes = size().width() * size().height() * 4; |
| @@ -644,6 +694,15 @@ unsigned JPEGImageDecoder::desiredScaleNumerator() const |
| return scaleNumerator; |
| } |
| +bool JPEGImageDecoder::doHardwareDecoding() |
| +{ |
| + setHardwareDecoding(true); |
| + PlatformInstrumentation::willDecodeImage("JPEG"); |
| + decode(false); |
| + PlatformInstrumentation::didDecodeImage(); |
| + return !failed(); |
| +} |
| + |
| ImageFrame* JPEGImageDecoder::frameBufferAtIndex(size_t index) |
| { |
| if (index) |
| @@ -671,28 +730,37 @@ bool JPEGImageDecoder::setFailed() |
| return ImageDecoder::setFailed(); |
| } |
| +void JPEGImageDecoder::setDecodingBuffers(OwnPtr<DecodingBuffers>& decodingBuffers) |
| +{ |
| + m_decodingBuffers = decodingBuffers.release(); |
| +} |
| + |
| template <J_COLOR_SPACE colorSpace> void setPixel(ImageFrame& buffer, ImageFrame::PixelData* pixel, JSAMPARRAY samples, int column) |
| { |
| - JSAMPLE* jsample = *samples + column * (colorSpace == JCS_RGB ? 3 : 4); |
| + ASSERT_NOT_REACHED(); |
| +} |
| - switch (colorSpace) { |
| - case JCS_RGB: |
| - buffer.setRGBARaw(pixel, jsample[0], jsample[1], jsample[2], 255); |
| - break; |
| - case JCS_CMYK: |
| - // 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.setRGBARaw(pixel, jsample[0] * k / 255, jsample[1] * k / 255, jsample[2] * k / 255, 255); |
| - break; |
| - } |
| +template <> void setPixel<JCS_RGB>(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) |
| +{ |
| + 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.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) |
| @@ -721,6 +789,84 @@ template <J_COLOR_SPACE colorSpace> bool outputRows(JPEGImageReader* reader, Ima |
| return true; |
| } |
| +static bool outputRawData(JPEGImageReader* reader, const DecodingBuffers* decodingBuffers) |
| +{ |
| + JSAMPARRAY samples = reader->samples(); |
| + jpeg_decompress_struct* info = reader->info(); |
| + unsigned char* bufferraw[3]; |
| + unsigned char* bufferraw2[32]; |
| + bufferraw[0] = (unsigned char*)&bufferraw2[0]; |
|
Peter Kasting
2014/07/23 22:17:37
Nit: Use C++-style casts. (Many places)
sugoi1
2014/07/24 15:48:30
Done.
|
| + bufferraw[1] = (unsigned char*)&bufferraw2[16]; |
| + bufferraw[2] = (unsigned char*)&bufferraw2[24]; |
| + int yWidth = info->output_width; |
|
Peter Kasting
2014/07/23 22:17:37
Nit: Again, I personally wouldn't line up the equa
sugoi1
2014/07/24 15:48:31
Done.
|
| + int yHeight = info->output_height; |
| + int yMaxH = yHeight - 1; |
| + int v = info->cur_comp_info[0]->v_samp_factor; |
| + int uvWidth(0), uvHeight(0); |
| + computeUVSize(info, uvWidth, uvHeight); |
| + int uvMaxH = uvHeight - 1; |
| + unsigned char* outputY = (unsigned char*)decodingBuffers->getPlane(0); |
| + unsigned char* outputU = (unsigned char*)decodingBuffers->getPlane(1); |
| + unsigned char* outputV = (unsigned char*)decodingBuffers->getPlane(2); |
| + size_t rowBytesY = decodingBuffers->getRowBytes(0); |
| + size_t rowBytesU = decodingBuffers->getRowBytes(1); |
| + size_t rowBytesV = decodingBuffers->getRowBytes(2); |
| + |
| + int scanlinesToRead = DCTSIZE * v; |
| + unsigned char* yLastRow = *samples; |
| + unsigned char* uLastRow = yLastRow + 2 * yWidth; |
| + unsigned char* vLastRow = uLastRow + 2 * yWidth; |
| + unsigned char* dummyRow = vLastRow + 2 * yWidth; |
| + |
| + while (info->output_scanline < info->output_height) { |
| + // Request 8 or 16 scanlines: returns 0 or more scanlines. |
| + bool hasYLastRow(false), hasUVLastRow(false); |
| + for (int i = 0; i < scanlinesToRead; ++i) { |
| + int scanline = (info->output_scanline + i); |
|
Peter Kasting
2014/07/23 22:17:37
Nit: Extra space
sugoi1
2014/07/24 15:48:30
Done.
|
| + if (scanline < yMaxH) { |
| + bufferraw2[i] = &((unsigned char*)outputY)[scanline * rowBytesY]; |
| + } else if (scanline == yMaxH) { |
| + bufferraw2[i] = yLastRow; |
| + hasYLastRow = true; |
| + } else { |
| + bufferraw2[i] = dummyRow; |
| + } |
| + } |
| + int scaledScanline = info->output_scanline / v; |
| + for (int i = 0; i < 8; ++i) { |
| + int scanline = (scaledScanline + i); |
| + if (scanline < uvMaxH) { |
| + bufferraw2[16+i] = &((unsigned char*)outputU)[scanline * rowBytesU]; |
|
Peter Kasting
2014/07/23 22:17:37
Nit: Spaces around '+'
sugoi1
2014/07/24 15:48:30
Done.
|
| + bufferraw2[24+i] = &((unsigned char*)outputV)[scanline * rowBytesV]; |
| + } else if (scanline == uvMaxH) { |
| + bufferraw2[16+i] = uLastRow; |
| + bufferraw2[24+i] = vLastRow; |
| + hasUVLastRow = true; |
| + } else { |
| + bufferraw2[16+i] = dummyRow; |
| + bufferraw2[24+i] = dummyRow; |
| + } |
| + } |
| + JDIMENSION scanlinesRead = jpeg_read_raw_data(info, (JSAMPIMAGE)bufferraw, scanlinesToRead); |
| + if (scanlinesRead == 0) { |
|
Peter Kasting
2014/07/23 22:17:37
Nit: No {}
sugoi1
2014/07/24 15:48:30
Done.
|
| + return false; |
| + } |
| + if (hasYLastRow) { |
| + memcpy(&outputY[ yMaxH * rowBytesY], yLastRow, yWidth); |
|
Peter Kasting
2014/07/23 22:17:37
Nit: Extra space
sugoi1
2014/07/24 15:48:30
Done.
|
| + } |
| + if (hasUVLastRow) { |
| + memcpy(&outputU[uvMaxH * rowBytesU], uLastRow, uvWidth); |
| + memcpy(&outputV[uvMaxH * rowBytesV], vLastRow, uvWidth); |
| + } |
| + } |
| + |
| + if (info->output_scanline > info->output_height) { |
| + info->output_scanline = info->output_height; |
|
Peter Kasting
2014/07/23 22:17:37
Nit: Shorter:
info->output_scanline = std::mi
sugoi1
2014/07/24 15:48:31
Done.
|
| + } |
| + |
| + return true; |
| +} |
| + |
| bool JPEGImageDecoder::outputScanlines() |
| { |
| if (m_frameBufferCache.isEmpty()) |
| @@ -728,6 +874,10 @@ bool JPEGImageDecoder::outputScanlines() |
| jpeg_decompress_struct* info = m_reader->info(); |
| + if ((JCS_YCbCr == info->out_color_space) && (m_decodingBuffers.get())) { |
| + return outputRawData(m_reader.get(), m_decodingBuffers.get()); |
| + } |
| + |
| // Initialize the framebuffer if needed. |
| ImageFrame& buffer = m_frameBufferCache[0]; |
| if (buffer.status() == ImageFrame::FrameEmpty) { |