diff --git a/include/core/SkImageDecoder.h b/include/core/SkImageDecoder.h index 94831762de..5910d33a99 100644 --- a/include/core/SkImageDecoder.h +++ b/include/core/SkImageDecoder.h @@ -47,15 +47,6 @@ public: */ virtual Format getFormat() const; - /** If planes or rowBytes is NULL, decodes the header and computes componentSizes - for memory allocation. - Otherwise, decodes the YUV planes into the provided image planes and - updates componentSizes to the final image size. - Returns whether the decoding was successful. - */ - bool decodeYUV8Planes(SkStream* stream, SkISize componentSizes[3], void* planes[3], - size_t rowBytes[3], SkYUVColorSpace*); - /** Return the format of the SkStreamRewindable or kUnknown_Format if it cannot be determined. Rewinds the stream before returning. */ @@ -348,17 +339,6 @@ protected: return false; } - /** If planes or rowBytes is NULL, decodes the header and computes componentSizes - for memory allocation. - Otherwise, decodes the YUV planes into the provided image planes and - updates componentSizes to the final image size. - Returns whether the decoding was successful. - */ - virtual bool onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3], void* planes[3], - size_t rowBytes[3], SkYUVColorSpace*) { - return false; - } - /* * Crop a rectangle from the src Bitmap to the dest Bitmap. src and dst are * both sampled by sampleSize from an original Bitmap. diff --git a/src/images/SkDecodingImageGenerator.cpp b/src/images/SkDecodingImageGenerator.cpp index a90c1cf01f..3b5cb784ed 100644 --- a/src/images/SkDecodingImageGenerator.cpp +++ b/src/images/SkDecodingImageGenerator.cpp @@ -45,8 +45,6 @@ protected: virtual bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor ctable[], int* ctableCount) SK_OVERRIDE; - virtual bool onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3], - SkYUVColorSpace* colorSpace) SK_OVERRIDE; private: typedef SkImageGenerator INHERITED; @@ -206,20 +204,6 @@ bool DecodingImageGenerator::onGetPixels(const SkImageInfo& info, return true; } -bool DecodingImageGenerator::onGetYUV8Planes(SkISize sizes[3], void* planes[3], - size_t rowBytes[3], SkYUVColorSpace* colorSpace) { - if (!fStream->rewind()) { - return false; - } - - SkAutoTDelete decoder(SkImageDecoder::Factory(fStream)); - if (NULL == decoder.get()) { - return false; - } - - return decoder->decodeYUV8Planes(fStream, sizes, planes, rowBytes, colorSpace); -} - // A contructor-type function that returns NULL on failure. This // prevents the returned SkImageGenerator from ever being in a bad // state. Called by both Create() functions diff --git a/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp index 39dabf4477..b25e7dbbab 100644 --- a/src/images/SkImageDecoder.cpp +++ b/src/images/SkImageDecoder.cpp @@ -285,8 +285,3 @@ bool SkImageDecoder::DecodeStream(SkStreamRewindable* stream, SkBitmap* bm, SkCo } return success; } - -bool SkImageDecoder::decodeYUV8Planes(SkStream* stream, SkISize componentSizes[3], void* planes[3], - size_t rowBytes[3], SkYUVColorSpace* colorSpace) { - return this->onDecodeYUV8Planes(stream, componentSizes, planes, rowBytes, colorSpace); -} diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp index 937fe903dc..99401e6b62 100644 --- a/src/images/SkImageDecoder_libjpeg.cpp +++ b/src/images/SkImageDecoder_libjpeg.cpp @@ -239,9 +239,6 @@ protected: virtual bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE; #endif virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE; - virtual bool onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3], - void* planes[3], size_t rowBytes[3], - SkYUVColorSpace* colorSpace) SK_OVERRIDE; private: #ifdef SK_BUILD_FOR_ANDROID @@ -328,21 +325,16 @@ 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, - int width, int height, const char caller[]) { + const SkBitmap& bm, 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, width, height); + cinfo.err->msg_code, buffer, caller, bm.width(), bm.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) { @@ -734,246 +726,6 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { return true; } -enum SizeType { - kSizeForMemoryAllocation_SizeType, - kActualSize_SizeType -}; - -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); -} - -// Enum for YUV decoding -enum YUVSubsampling { - kUNKNOWN_YUVSubsampling, - k410_YUVSubsampling, - k411_YUVSubsampling, - k420_YUVSubsampling, - k422_YUVSubsampling, - k440_YUVSubsampling, - k444_YUVSubsampling -}; - -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; -} - -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, void* planes[3], size_t rowBytes[3]) { - // 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(planes[0]); - JSAMPROW outputU = static_cast(planes[1]); - JSAMPROW outputV = static_cast(planes[2]); - size_t rowBytesY = rowBytes[0]; - size_t rowBytesU = rowBytes[1]; - size_t rowBytesV = 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::onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3], - void* planes[3], size_t rowBytes[3], - SkYUVColorSpace* colorSpace) { -#ifdef TIME_DECODE - SkAutoTime atm("JPEG YUV8 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 (!planes || !planes[0] || !rowBytes || !rowBytes[0]) { // 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, planes, rowBytes)) { - return return_false(cinfo, 0, 0, "output_raw_data"); - } - - update_components_sizes(cinfo, componentSizes, kActualSize_SizeType); - jpeg_finish_decompress(&cinfo); - - if (NULL != colorSpace) { - *colorSpace = kJPEG_SkYUVColorSpace; - } - - return true; -} - #ifdef SK_BUILD_FOR_ANDROID bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) { diff --git a/tests/JpegTest.cpp b/tests/JpegTest.cpp index 8828926ef9..f8784a2d26 100644 --- a/tests/JpegTest.cpp +++ b/tests/JpegTest.cpp @@ -6,12 +6,11 @@ */ #include "SkBitmap.h" -#include "SkDecodingImageGenerator.h" +#include "SkData.h" #include "SkForceLinking.h" +#include "SkImage.h" #include "SkImageDecoder.h" -#include "SkPixelRef.h" #include "SkStream.h" -#include "SkTemplates.h" #include "Test.h" __SK_FORCE_IMAGE_DECODER_LINKING; @@ -453,47 +452,3 @@ DEF_TEST(Jpeg, reporter) { SkASSERT(writeSuccess); #endif } - -DEF_TEST(Jpeg_YUV, reporter) { - size_t len = sizeof(goodJpegImage); - SkMemoryStream* stream = SkNEW_ARGS(SkMemoryStream, (goodJpegImage, len)); - - SkBitmap bitmap; - SkDecodingImageGenerator::Options opts; - bool pixelsInstalled = SkInstallDiscardablePixelRef( - SkDecodingImageGenerator::Create(stream, opts), &bitmap); - REPORTER_ASSERT(reporter, pixelsInstalled); - - if (!pixelsInstalled) { - return; - } - - SkPixelRef* pixelRef = bitmap.pixelRef(); - SkISize yuvSizes[3]; - bool sizesComputed = (NULL != pixelRef) && pixelRef->getYUV8Planes(yuvSizes, NULL, NULL, NULL); - REPORTER_ASSERT(reporter, sizesComputed); - - if (!sizesComputed) { - return; - } - - // Allocate the memory for YUV - size_t totalSize(0); - size_t sizes[3], rowBytes[3]; - const int32_t expected_sizes[] = {128, 64, 64}; - for (int i = 0; i < 3; ++i) { - rowBytes[i] = yuvSizes[i].fWidth; - totalSize += sizes[i] = rowBytes[i] * yuvSizes[i].fHeight; - REPORTER_ASSERT(reporter, rowBytes[i] == (size_t)expected_sizes[i]); - REPORTER_ASSERT(reporter, yuvSizes[i].fWidth == expected_sizes[i]); - REPORTER_ASSERT(reporter, yuvSizes[i].fHeight == expected_sizes[i]); - } - SkAutoMalloc storage(totalSize); - void* planes[3]; - planes[0] = storage.get(); - planes[1] = (uint8_t*)planes[0] + sizes[0]; - planes[2] = (uint8_t*)planes[1] + sizes[1]; - - // Get the YUV planes - REPORTER_ASSERT(reporter, pixelRef->getYUV8Planes(yuvSizes, planes, rowBytes, NULL)); -}