| Index: src/images/SkImageDecoder_libjpeg.cpp
|
| diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp
|
| index 99401e6b627cdc9fd023b304a6c40789c3fbec3d..6459f4237d8d21e04a332adc6070efef7ab93d1b 100644
|
| --- a/src/images/SkImageDecoder_libjpeg.cpp
|
| +++ b/src/images/SkImageDecoder_libjpeg.cpp
|
| @@ -54,6 +54,22 @@ SK_CONF_DECLARE(bool, c_suppressJPEGImageDecoderErrors,
|
| "Suppress most JPG error messages when decode "
|
| "function fails.");
|
|
|
| +// Enum for YUV decoding
|
| +enum YUVSubsampling {
|
| + kUNKNOWN_YUVSubsampling,
|
| + k410_YUVSubsampling,
|
| + k411_YUVSubsampling,
|
| + k420_YUVSubsampling,
|
| + k422_YUVSubsampling,
|
| + k440_YUVSubsampling,
|
| + k444_YUVSubsampling
|
| +};
|
| +
|
| +enum SizeType {
|
| + kSizeForMemoryAllocation_SizeType,
|
| + kActualSize_SizeType
|
| +};
|
| +
|
| //////////////////////////////////////////////////////////////////////////
|
| //////////////////////////////////////////////////////////////////////////
|
|
|
| @@ -102,6 +118,66 @@ static void initialize_info(jpeg_decompress_struct* cinfo, skjpeg_source_mgr* sr
|
| }
|
| }
|
|
|
| +static SkISize compute_yuv_size(const jpeg_decompress_struct& info, int component,
|
| + SizeType sizeType)
|
| +{
|
| + if (sizeType == kSizeForMemoryAllocation_SizeType) {
|
| + return SkISize::Make(info.cur_comp_info[component]->width_in_blocks * DCTSIZE,
|
| + info.cur_comp_info[component]->height_in_blocks * DCTSIZE);
|
| + }
|
| + return SkISize::Make(info.cur_comp_info[component]->downsampled_width,
|
| + info.cur_comp_info[component]->downsampled_height);
|
| +}
|
| +
|
| +static YUVSubsampling yuv_subsampling(const jpeg_decompress_struct& info)
|
| +{
|
| + if ((DCTSIZE == 8)
|
| + && (info.num_components == 3)
|
| + && (info.comps_in_scan >= info.num_components)
|
| + && (info.scale_denom <= 8)
|
| + && (info.cur_comp_info[0])
|
| + && (info.cur_comp_info[1])
|
| + && (info.cur_comp_info[2])
|
| + && (info.cur_comp_info[1]->h_samp_factor == 1)
|
| + && (info.cur_comp_info[1]->v_samp_factor == 1)
|
| + && (info.cur_comp_info[2]->h_samp_factor == 1)
|
| + && (info.cur_comp_info[2]->v_samp_factor == 1)) {
|
| + int h = info.cur_comp_info[0]->h_samp_factor;
|
| + int v = info.cur_comp_info[0]->v_samp_factor;
|
| + // 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 (v == 1) {
|
| + switch (h) {
|
| + case 1:
|
| + return k444_YUVSubsampling;
|
| + case 2:
|
| + return k422_YUVSubsampling;
|
| + case 4:
|
| + return k411_YUVSubsampling;
|
| + default:
|
| + break;
|
| + }
|
| + } else if (v == 2) {
|
| + switch (h) {
|
| + case 1:
|
| + return k440_YUVSubsampling;
|
| + case 2:
|
| + return k420_YUVSubsampling;
|
| + case 4:
|
| + return k410_YUVSubsampling;
|
| + default:
|
| + break;
|
| + }
|
| + }
|
| + }
|
| +
|
| + return kUNKNOWN_YUVSubsampling;
|
| +}
|
| +
|
| #ifdef SK_BUILD_FOR_ANDROID
|
| class SkJPEGImageIndex {
|
| public:
|
| @@ -239,6 +315,8 @@ protected:
|
| virtual bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE;
|
| #endif
|
| virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
|
| + virtual bool onDecodeToYUV(SkStream* stream, SkISize componentSizes[3],
|
| + SkImagePlanes* imagePlanes) SK_OVERRIDE;
|
|
|
| private:
|
| #ifdef SK_BUILD_FOR_ANDROID
|
| @@ -325,16 +403,21 @@ static bool skip_src_rows_tile(jpeg_decompress_struct* cinfo,
|
| // This guy exists just to aid in debugging, as it allows debuggers to just
|
| // set a break-point in one place to see all error exists.
|
| static bool return_false(const jpeg_decompress_struct& cinfo,
|
| - const SkBitmap& bm, const char caller[]) {
|
| + int width, int height, const char caller[]) {
|
| if (!(c_suppressJPEGImageDecoderErrors)) {
|
| char buffer[JMSG_LENGTH_MAX];
|
| cinfo.err->format_message((const j_common_ptr)&cinfo, buffer);
|
| SkDebugf("libjpeg error %d <%s> from %s [%d %d]\n",
|
| - cinfo.err->msg_code, buffer, caller, bm.width(), bm.height());
|
| + cinfo.err->msg_code, buffer, caller, width, height);
|
| }
|
| return false; // must always return false
|
| }
|
|
|
| +static bool return_false(const jpeg_decompress_struct& cinfo,
|
| + const SkBitmap& bm, const char caller[]) {
|
| + return return_false(cinfo, bm.width(), bm.height(), caller);
|
| +}
|
| +
|
| // Convert a scanline of CMYK samples to RGBX in place. Note that this
|
| // method moves the "scanline" pointer in its processing
|
| static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
|
| @@ -726,6 +809,165 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
|
| return true;
|
| }
|
|
|
| +static void update_components_sizes(const jpeg_decompress_struct& cinfo, SkISize componentSizes[3],
|
| + SizeType sizeType) {
|
| + for (int i = 0; i < 3; ++i) {
|
| + componentSizes[i] = compute_yuv_size(cinfo, i, sizeType);
|
| + }
|
| +}
|
| +
|
| +static bool output_raw_data(jpeg_decompress_struct& cinfo, SkImagePlanes* imagePlanes) {
|
| + // U size and V size have to be the same if we're calling output_raw_data()
|
| + SkISize uvSize = compute_yuv_size(cinfo, 1, kSizeForMemoryAllocation_SizeType);
|
| + SkASSERT(uvSize == compute_yuv_size(cinfo, 2, kSizeForMemoryAllocation_SizeType));
|
| +
|
| + JSAMPARRAY bufferraw[3];
|
| + JSAMPROW bufferraw2[32];
|
| + bufferraw[0] = &bufferraw2[0]; // Y channel rows (8 or 16)
|
| + bufferraw[1] = &bufferraw2[16]; // U channel rows (8)
|
| + bufferraw[2] = &bufferraw2[24]; // V channel rows (8)
|
| + int yWidth = cinfo.output_width;
|
| + int yHeight = cinfo.output_height;
|
| + int yMaxH = yHeight - 1;
|
| + int v = cinfo.cur_comp_info[0]->v_samp_factor;
|
| + int uvMaxH = uvSize.height() - 1;
|
| + JSAMPROW outputY = static_cast<JSAMPROW>(imagePlanes->plane(0));
|
| + JSAMPROW outputU = static_cast<JSAMPROW>(imagePlanes->plane(1));
|
| + JSAMPROW outputV = static_cast<JSAMPROW>(imagePlanes->plane(2));
|
| + size_t rowBytesY = imagePlanes->rowBytes(0);
|
| + size_t rowBytesU = imagePlanes->rowBytes(1);
|
| + size_t rowBytesV = imagePlanes->rowBytes(2);
|
| +
|
| + int yScanlinesToRead = DCTSIZE * v;
|
| + SkAutoMalloc lastRowStorage(yWidth * 8);
|
| + JSAMPROW yLastRow = (JSAMPROW)lastRowStorage.get();
|
| + JSAMPROW uLastRow = yLastRow + 2 * yWidth;
|
| + JSAMPROW vLastRow = uLastRow + 2 * yWidth;
|
| + JSAMPROW dummyRow = vLastRow + 2 * yWidth;
|
| +
|
| + while (cinfo.output_scanline < cinfo.output_height) {
|
| + // Request 8 or 16 scanlines: returns 0 or more scanlines.
|
| + bool hasYLastRow(false), hasUVLastRow(false);
|
| + // Assign 8 or 16 rows of memory to read the Y channel.
|
| + for (int i = 0; i < yScanlinesToRead; ++i) {
|
| + int scanline = (cinfo.output_scanline + i);
|
| + if (scanline < yMaxH) {
|
| + bufferraw2[i] = &outputY[scanline * rowBytesY];
|
| + } else if (scanline == yMaxH) {
|
| + bufferraw2[i] = yLastRow;
|
| + hasYLastRow = true;
|
| + } else {
|
| + bufferraw2[i] = dummyRow;
|
| + }
|
| + }
|
| + int scaledScanline = cinfo.output_scanline / v;
|
| + // Assign 8 rows of memory to read the U and V channels.
|
| + for (int i = 0; i < 8; ++i) {
|
| + int scanline = (scaledScanline + i);
|
| + if (scanline < uvMaxH) {
|
| + bufferraw2[16 + i] = &outputU[scanline * rowBytesU];
|
| + bufferraw2[24 + i] = &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(&cinfo, bufferraw, yScanlinesToRead);
|
| +
|
| + if (scanlinesRead == 0)
|
| + return false;
|
| +
|
| + if (hasYLastRow) {
|
| + memcpy(&outputY[yMaxH * rowBytesY], yLastRow, yWidth);
|
| + }
|
| + if (hasUVLastRow) {
|
| + memcpy(&outputU[uvMaxH * rowBytesU], uLastRow, uvSize.width());
|
| + memcpy(&outputV[uvMaxH * rowBytesV], vLastRow, uvSize.width());
|
| + }
|
| + }
|
| +
|
| + cinfo.output_scanline = SkMin32(cinfo.output_scanline, cinfo.output_height);
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool SkJPEGImageDecoder::onDecodeToYUV(SkStream* stream, SkISize componentSizes[3],
|
| + SkImagePlanes* imagePlanes) {
|
| +#ifdef TIME_DECODE
|
| + SkAutoTime atm("JPEG Decode");
|
| +#endif
|
| +
|
| + if (this->getSampleSize() != 1) {
|
| + return false; // Resizing not supported
|
| + }
|
| +
|
| + JPEGAutoClean autoClean;
|
| +
|
| + jpeg_decompress_struct cinfo;
|
| + skjpeg_source_mgr srcManager(stream, this);
|
| +
|
| + skjpeg_error_mgr errorManager;
|
| + set_error_mgr(&cinfo, &errorManager);
|
| +
|
| + // All objects need to be instantiated before this setjmp call so that
|
| + // they will be cleaned up properly if an error occurs.
|
| + if (setjmp(errorManager.fJmpBuf)) {
|
| + return return_false(cinfo, 0, 0, "setjmp");
|
| + }
|
| +
|
| + initialize_info(&cinfo, &srcManager);
|
| + autoClean.set(&cinfo);
|
| +
|
| + int status = jpeg_read_header(&cinfo, true);
|
| + if (status != JPEG_HEADER_OK) {
|
| + return return_false(cinfo, 0, 0, "read_header");
|
| + }
|
| +
|
| + if (cinfo.jpeg_color_space != JCS_YCbCr) {
|
| + // It's not an error to not be encoded in YUV, so no need to use return_false()
|
| + return false;
|
| + }
|
| +
|
| + cinfo.out_color_space = JCS_YCbCr;
|
| + cinfo.raw_data_out = TRUE;
|
| +
|
| + if (NULL == imagePlanes) { // Compute size only
|
| + update_components_sizes(cinfo, componentSizes, kSizeForMemoryAllocation_SizeType);
|
| + return true;
|
| + }
|
| +
|
| + set_dct_method(*this, &cinfo);
|
| +
|
| + SkASSERT(1 == cinfo.scale_num);
|
| + cinfo.scale_denom = 1;
|
| +
|
| + turn_off_visual_optimizations(&cinfo);
|
| +
|
| +#ifdef ANDROID_RGB
|
| + cinfo.dither_mode = JDITHER_NONE;
|
| +#endif
|
| +
|
| + /* image_width and image_height are the original dimensions, available
|
| + after jpeg_read_header(). To see the scaled dimensions, we have to call
|
| + jpeg_start_decompress(), and then read output_width and output_height.
|
| + */
|
| + if (!jpeg_start_decompress(&cinfo)) {
|
| + return return_false(cinfo, 0, 0, "start_decompress");
|
| + }
|
| +
|
| + if (!output_raw_data(cinfo, imagePlanes)) {
|
| + return return_false(cinfo, 0, 0, "output_raw_data");
|
| + }
|
| +
|
| + update_components_sizes(cinfo, componentSizes, kActualSize_SizeType);
|
| + jpeg_finish_decompress(&cinfo);
|
| + return true;
|
| +}
|
| +
|
| #ifdef SK_BUILD_FOR_ANDROID
|
| bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
|
|
|
|
|