| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright 2007 The Android Open Source Project | 
|  | 3 | * | 
|  | 4 | * Use of this source code is governed by a BSD-style license that can be | 
|  | 5 | * found in the LICENSE file. | 
|  | 6 | */ | 
|  | 7 |  | 
|  | 8 |  | 
|  | 9 | #include "SkImageDecoder.h" | 
|  | 10 | #include "SkImageEncoder.h" | 
|  | 11 | #include "SkJpegUtility.h" | 
|  | 12 | #include "SkColorPriv.h" | 
|  | 13 | #include "SkDither.h" | 
|  | 14 | #include "SkScaledBitmapSampler.h" | 
|  | 15 | #include "SkStream.h" | 
|  | 16 | #include "SkTemplates.h" | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 17 | #include "SkTime.h" | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 18 | #include "SkUtils.h" | 
| halcanary@google.com | 2a10318 | 2013-10-14 12:49:15 +0000 | [diff] [blame] | 19 | #include "SkRTConf.h" | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 20 | #include "SkRect.h" | 
|  | 21 | #include "SkCanvas.h" | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 22 |  | 
| halcanary@google.com | fed3037 | 2013-10-04 12:46:45 +0000 | [diff] [blame] | 23 |  | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 24 | #include <stdio.h> | 
|  | 25 | extern "C" { | 
|  | 26 | #include "jpeglib.h" | 
|  | 27 | #include "jerror.h" | 
|  | 28 | } | 
|  | 29 |  | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 30 | // These enable timing code that report milliseconds for an encoding/decoding | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 31 | //#define TIME_ENCODE | 
|  | 32 | //#define TIME_DECODE | 
|  | 33 |  | 
|  | 34 | // this enables our rgb->yuv code, which is faster than libjpeg on ARM | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 35 | #define WE_CONVERT_TO_YUV | 
|  | 36 |  | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 37 | // If ANDROID_RGB is defined by in the jpeg headers it indicates that jpeg offers | 
|  | 38 | // support for two additional formats (1) JCS_RGBA_8888 and (2) JCS_RGB_565. | 
|  | 39 |  | 
| halcanary@google.com | fed3037 | 2013-10-04 12:46:45 +0000 | [diff] [blame] | 40 | #if defined(SK_DEBUG) | 
| halcanary@google.com | 2a10318 | 2013-10-14 12:49:15 +0000 | [diff] [blame] | 41 | #define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS false | 
| halcanary@google.com | 04b57f8 | 2013-10-14 20:08:48 +0000 | [diff] [blame] | 42 | #define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_ERRORS false | 
| halcanary@google.com | 2a10318 | 2013-10-14 12:49:15 +0000 | [diff] [blame] | 43 | #else  // !defined(SK_DEBUG) | 
|  | 44 | #define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS true | 
| halcanary@google.com | 04b57f8 | 2013-10-14 20:08:48 +0000 | [diff] [blame] | 45 | #define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_ERRORS true | 
| halcanary@google.com | fed3037 | 2013-10-04 12:46:45 +0000 | [diff] [blame] | 46 | #endif  // defined(SK_DEBUG) | 
| halcanary@google.com | 2a10318 | 2013-10-14 12:49:15 +0000 | [diff] [blame] | 47 | SK_CONF_DECLARE(bool, c_suppressJPEGImageDecoderWarnings, | 
|  | 48 | "images.jpeg.suppressDecoderWarnings", | 
|  | 49 | DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS, | 
|  | 50 | "Suppress most JPG warnings when calling decode functions."); | 
| halcanary@google.com | 04b57f8 | 2013-10-14 20:08:48 +0000 | [diff] [blame] | 51 | SK_CONF_DECLARE(bool, c_suppressJPEGImageDecoderErrors, | 
|  | 52 | "images.jpeg.suppressDecoderErrors", | 
|  | 53 | DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_ERRORS, | 
|  | 54 | "Suppress most JPG error messages when decode " | 
|  | 55 | "function fails."); | 
| halcanary@google.com | fed3037 | 2013-10-04 12:46:45 +0000 | [diff] [blame] | 56 |  | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 57 | ////////////////////////////////////////////////////////////////////////// | 
|  | 58 | ////////////////////////////////////////////////////////////////////////// | 
|  | 59 |  | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 60 | static void overwrite_mem_buffer_size(jpeg_decompress_struct* cinfo) { | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 61 | #ifdef SK_BUILD_FOR_ANDROID | 
|  | 62 | /* Check if the device indicates that it has a large amount of system memory | 
|  | 63 | * if so, increase the memory allocation to 30MB instead of the default 5MB. | 
|  | 64 | */ | 
|  | 65 | #ifdef ANDROID_LARGE_MEMORY_DEVICE | 
|  | 66 | cinfo->mem->max_memory_to_use = 30 * 1024 * 1024; | 
|  | 67 | #else | 
|  | 68 | cinfo->mem->max_memory_to_use = 5 * 1024 * 1024; | 
|  | 69 | #endif | 
|  | 70 | #endif // SK_BUILD_FOR_ANDROID | 
|  | 71 | } | 
|  | 72 |  | 
|  | 73 | ////////////////////////////////////////////////////////////////////////// | 
|  | 74 | ////////////////////////////////////////////////////////////////////////// | 
|  | 75 |  | 
| halcanary@google.com | fed3037 | 2013-10-04 12:46:45 +0000 | [diff] [blame] | 76 | static void do_nothing_emit_message(jpeg_common_struct*, int) { | 
|  | 77 | /* do nothing */ | 
|  | 78 | } | 
| halcanary@google.com | 04b57f8 | 2013-10-14 20:08:48 +0000 | [diff] [blame] | 79 | static void do_nothing_output_message(j_common_ptr) { | 
|  | 80 | /* do nothing */ | 
|  | 81 | } | 
| halcanary@google.com | fed3037 | 2013-10-04 12:46:45 +0000 | [diff] [blame] | 82 |  | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 83 | static void initialize_info(jpeg_decompress_struct* cinfo, skjpeg_source_mgr* src_mgr) { | 
|  | 84 | SkASSERT(cinfo != NULL); | 
|  | 85 | SkASSERT(src_mgr != NULL); | 
|  | 86 | jpeg_create_decompress(cinfo); | 
|  | 87 | overwrite_mem_buffer_size(cinfo); | 
|  | 88 | cinfo->src = src_mgr; | 
| halcanary@google.com | fed3037 | 2013-10-04 12:46:45 +0000 | [diff] [blame] | 89 | /* To suppress warnings with a SK_DEBUG binary, set the | 
|  | 90 | * environment variable "skia_images_jpeg_suppressDecoderWarnings" | 
|  | 91 | * to "true".  Inside a program that links to skia: | 
|  | 92 | * SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true); */ | 
|  | 93 | if (c_suppressJPEGImageDecoderWarnings) { | 
|  | 94 | cinfo->err->emit_message = &do_nothing_emit_message; | 
|  | 95 | } | 
| halcanary@google.com | 04b57f8 | 2013-10-14 20:08:48 +0000 | [diff] [blame] | 96 | /* To suppress error messages with a SK_DEBUG binary, set the | 
|  | 97 | * environment variable "skia_images_jpeg_suppressDecoderErrors" | 
|  | 98 | * to "true".  Inside a program that links to skia: | 
|  | 99 | * SK_CONF_SET("images.jpeg.suppressDecoderErrors", true); */ | 
|  | 100 | if (c_suppressJPEGImageDecoderErrors) { | 
|  | 101 | cinfo->err->output_message = &do_nothing_output_message; | 
|  | 102 | } | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 103 | } | 
|  | 104 |  | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 105 | #ifdef SK_BUILD_FOR_ANDROID | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 106 | class SkJPEGImageIndex { | 
|  | 107 | public: | 
| scroggo@google.com | b5571b3 | 2013-09-25 21:34:24 +0000 | [diff] [blame] | 108 | SkJPEGImageIndex(SkStreamRewindable* stream, SkImageDecoder* decoder) | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 109 | : fSrcMgr(stream, decoder) | 
|  | 110 | , fInfoInitialized(false) | 
|  | 111 | , fHuffmanCreated(false) | 
|  | 112 | , fDecompressStarted(false) | 
|  | 113 | { | 
|  | 114 | SkDEBUGCODE(fReadHeaderSucceeded = false;) | 
|  | 115 | } | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 116 |  | 
|  | 117 | ~SkJPEGImageIndex() { | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 118 | if (fHuffmanCreated) { | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 119 | // Set to false before calling the libjpeg function, in case | 
|  | 120 | // the libjpeg function calls longjmp. Our setjmp handler may | 
|  | 121 | // attempt to delete this SkJPEGImageIndex, thus entering this | 
|  | 122 | // destructor again. Setting fHuffmanCreated to false first | 
|  | 123 | // prevents an infinite loop. | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 124 | fHuffmanCreated = false; | 
|  | 125 | jpeg_destroy_huffman_index(&fHuffmanIndex); | 
|  | 126 | } | 
|  | 127 | if (fDecompressStarted) { | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 128 | // Like fHuffmanCreated, set to false before calling libjpeg | 
|  | 129 | // function to prevent potential infinite loop. | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 130 | fDecompressStarted = false; | 
|  | 131 | jpeg_finish_decompress(&fCInfo); | 
|  | 132 | } | 
|  | 133 | if (fInfoInitialized) { | 
|  | 134 | this->destroyInfo(); | 
|  | 135 | } | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 136 | } | 
|  | 137 |  | 
|  | 138 | /** | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 139 | *  Destroy the cinfo struct. | 
|  | 140 | *  After this call, if a huffman index was already built, it | 
|  | 141 | *  can be used after calling initializeInfoAndReadHeader | 
|  | 142 | *  again. Must not be called after startTileDecompress except | 
|  | 143 | *  in the destructor. | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 144 | */ | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 145 | void destroyInfo() { | 
|  | 146 | SkASSERT(fInfoInitialized); | 
|  | 147 | SkASSERT(!fDecompressStarted); | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 148 | // Like fHuffmanCreated, set to false before calling libjpeg | 
|  | 149 | // function to prevent potential infinite loop. | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 150 | fInfoInitialized = false; | 
|  | 151 | jpeg_destroy_decompress(&fCInfo); | 
|  | 152 | SkDEBUGCODE(fReadHeaderSucceeded = false;) | 
|  | 153 | } | 
|  | 154 |  | 
|  | 155 | /** | 
|  | 156 | *  Initialize the cinfo struct. | 
|  | 157 | *  Calls jpeg_create_decompress, makes customizations, and | 
|  | 158 | *  finally calls jpeg_read_header. Returns true if jpeg_read_header | 
|  | 159 | *  returns JPEG_HEADER_OK. | 
|  | 160 | *  If cinfo was already initialized, destroyInfo must be called to | 
|  | 161 | *  destroy the old one. Must not be called after startTileDecompress. | 
|  | 162 | */ | 
|  | 163 | bool initializeInfoAndReadHeader() { | 
|  | 164 | SkASSERT(!fInfoInitialized && !fDecompressStarted); | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 165 | initialize_info(&fCInfo, &fSrcMgr); | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 166 | fInfoInitialized = true; | 
|  | 167 | const bool success = (JPEG_HEADER_OK == jpeg_read_header(&fCInfo, true)); | 
|  | 168 | SkDEBUGCODE(fReadHeaderSucceeded = success;) | 
|  | 169 | return success; | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 170 | } | 
|  | 171 |  | 
|  | 172 | jpeg_decompress_struct* cinfo() { return &fCInfo; } | 
|  | 173 |  | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 174 | huffman_index* huffmanIndex() { return &fHuffmanIndex; } | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 175 |  | 
|  | 176 | /** | 
|  | 177 | *  Build the index to be used for tile based decoding. | 
|  | 178 | *  Must only be called after a successful call to | 
|  | 179 | *  initializeInfoAndReadHeader and must not be called more | 
|  | 180 | *  than once. | 
|  | 181 | */ | 
|  | 182 | bool buildHuffmanIndex() { | 
|  | 183 | SkASSERT(fReadHeaderSucceeded); | 
|  | 184 | SkASSERT(!fHuffmanCreated); | 
|  | 185 | jpeg_create_huffman_index(&fCInfo, &fHuffmanIndex); | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 186 | SkASSERT(1 == fCInfo.scale_num && 1 == fCInfo.scale_denom); | 
| scroggo@google.com | 57a5298 | 2013-08-27 20:42:22 +0000 | [diff] [blame] | 187 | fHuffmanCreated = jpeg_build_huffman_index(&fCInfo, &fHuffmanIndex); | 
|  | 188 | return fHuffmanCreated; | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 189 | } | 
|  | 190 |  | 
|  | 191 | /** | 
|  | 192 | *  Start tile based decoding. Must only be called after a | 
|  | 193 | *  successful call to buildHuffmanIndex, and must only be | 
|  | 194 | *  called once. | 
|  | 195 | */ | 
|  | 196 | bool startTileDecompress() { | 
|  | 197 | SkASSERT(fHuffmanCreated); | 
|  | 198 | SkASSERT(fReadHeaderSucceeded); | 
|  | 199 | SkASSERT(!fDecompressStarted); | 
|  | 200 | if (jpeg_start_tile_decompress(&fCInfo)) { | 
|  | 201 | fDecompressStarted = true; | 
|  | 202 | return true; | 
|  | 203 | } | 
|  | 204 | return false; | 
|  | 205 | } | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 206 |  | 
|  | 207 | private: | 
|  | 208 | skjpeg_source_mgr  fSrcMgr; | 
|  | 209 | jpeg_decompress_struct fCInfo; | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 210 | huffman_index fHuffmanIndex; | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 211 | bool fInfoInitialized; | 
|  | 212 | bool fHuffmanCreated; | 
|  | 213 | bool fDecompressStarted; | 
|  | 214 | SkDEBUGCODE(bool fReadHeaderSucceeded;) | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 215 | }; | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 216 | #endif | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 217 |  | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 218 | class SkJPEGImageDecoder : public SkImageDecoder { | 
|  | 219 | public: | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 220 | #ifdef SK_BUILD_FOR_ANDROID | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 221 | SkJPEGImageDecoder() { | 
|  | 222 | fImageIndex = NULL; | 
|  | 223 | fImageWidth = 0; | 
|  | 224 | fImageHeight = 0; | 
|  | 225 | } | 
|  | 226 |  | 
|  | 227 | virtual ~SkJPEGImageDecoder() { | 
|  | 228 | SkDELETE(fImageIndex); | 
|  | 229 | } | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 230 | #endif | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 231 |  | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 232 | virtual Format getFormat() const { | 
|  | 233 | return kJPEG_Format; | 
|  | 234 | } | 
|  | 235 |  | 
|  | 236 | protected: | 
| scroggo@google.com | d79277f | 2013-08-07 19:53:53 +0000 | [diff] [blame] | 237 | #ifdef SK_BUILD_FOR_ANDROID | 
| scroggo@google.com | b5571b3 | 2013-09-25 21:34:24 +0000 | [diff] [blame] | 238 | virtual bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE; | 
| scroggo@google.com | 7e6fcee | 2013-05-03 20:14:28 +0000 | [diff] [blame] | 239 | virtual bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE; | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 240 | #endif | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 241 | virtual Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE; | 
| sugoi | b227e37 | 2014-10-16 13:10:57 -0700 | [diff] [blame] | 242 | virtual bool onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3], | 
|  | 243 | void* planes[3], size_t rowBytes[3], | 
|  | 244 | SkYUVColorSpace* colorSpace) SK_OVERRIDE; | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 245 |  | 
|  | 246 | private: | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 247 | #ifdef SK_BUILD_FOR_ANDROID | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 248 | SkJPEGImageIndex* fImageIndex; | 
|  | 249 | int fImageWidth; | 
|  | 250 | int fImageHeight; | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 251 | #endif | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 252 |  | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 253 | /** | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 254 | *  Determine the appropriate bitmap colortype and out_color_space based on | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 255 | *  both the preference of the caller and the jpeg_color_space on the | 
|  | 256 | *  jpeg_decompress_struct passed in. | 
|  | 257 | *  Must be called after jpeg_read_header. | 
|  | 258 | */ | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 259 | SkColorType getBitmapColorType(jpeg_decompress_struct*); | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 260 |  | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 261 | typedef SkImageDecoder INHERITED; | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 262 | }; | 
|  | 263 |  | 
|  | 264 | ////////////////////////////////////////////////////////////////////////// | 
|  | 265 |  | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 266 | /* Automatically clean up after throwing an exception */ | 
|  | 267 | class JPEGAutoClean { | 
|  | 268 | public: | 
|  | 269 | JPEGAutoClean(): cinfo_ptr(NULL) {} | 
|  | 270 | ~JPEGAutoClean() { | 
|  | 271 | if (cinfo_ptr) { | 
|  | 272 | jpeg_destroy_decompress(cinfo_ptr); | 
|  | 273 | } | 
|  | 274 | } | 
|  | 275 | void set(jpeg_decompress_struct* info) { | 
|  | 276 | cinfo_ptr = info; | 
|  | 277 | } | 
|  | 278 | private: | 
|  | 279 | jpeg_decompress_struct* cinfo_ptr; | 
|  | 280 | }; | 
|  | 281 |  | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 282 | /////////////////////////////////////////////////////////////////////////////// | 
|  | 283 |  | 
|  | 284 | /*  If we need to better match the request, we might examine the image and | 
|  | 285 | output dimensions, and determine if the downsampling jpeg provided is | 
|  | 286 | not sufficient. If so, we can recompute a modified sampleSize value to | 
|  | 287 | make up the difference. | 
|  | 288 |  | 
|  | 289 | To skip this additional scaling, just set sampleSize = 1; below. | 
|  | 290 | */ | 
|  | 291 | static int recompute_sampleSize(int sampleSize, | 
|  | 292 | const jpeg_decompress_struct& cinfo) { | 
|  | 293 | return sampleSize * cinfo.output_width / cinfo.image_width; | 
|  | 294 | } | 
|  | 295 |  | 
|  | 296 | static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) { | 
|  | 297 | /* These are initialized to 0, so if they have non-zero values, we assume | 
|  | 298 | they are "valid" (i.e. have been computed by libjpeg) | 
|  | 299 | */ | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 300 | return 0 != cinfo.output_width && 0 != cinfo.output_height; | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 301 | } | 
|  | 302 |  | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 303 | static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, int count) { | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 304 | for (int i = 0; i < count; i++) { | 
|  | 305 | JSAMPLE* rowptr = (JSAMPLE*)buffer; | 
|  | 306 | int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1); | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 307 | if (1 != row_count) { | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 308 | return false; | 
|  | 309 | } | 
|  | 310 | } | 
|  | 311 | return true; | 
|  | 312 | } | 
|  | 313 |  | 
| scroggo@google.com | d79277f | 2013-08-07 19:53:53 +0000 | [diff] [blame] | 314 | #ifdef SK_BUILD_FOR_ANDROID | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 315 | static bool skip_src_rows_tile(jpeg_decompress_struct* cinfo, | 
|  | 316 | huffman_index *index, void* buffer, int count) { | 
|  | 317 | for (int i = 0; i < count; i++) { | 
|  | 318 | JSAMPLE* rowptr = (JSAMPLE*)buffer; | 
|  | 319 | int row_count = jpeg_read_tile_scanline(cinfo, index, &rowptr); | 
|  | 320 | if (1 != row_count) { | 
|  | 321 | return false; | 
|  | 322 | } | 
|  | 323 | } | 
|  | 324 | return true; | 
|  | 325 | } | 
|  | 326 | #endif | 
|  | 327 |  | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 328 | /////////////////////////////////////////////////////////////////////////////// | 
|  | 329 |  | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 330 | // This guy exists just to aid in debugging, as it allows debuggers to just | 
|  | 331 | // set a break-point in one place to see all error exists. | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 332 | static void print_jpeg_decoder_errors(const jpeg_decompress_struct& cinfo, | 
| sugoi | b227e37 | 2014-10-16 13:10:57 -0700 | [diff] [blame] | 333 | int width, int height, const char caller[]) { | 
| halcanary@google.com | 04b57f8 | 2013-10-14 20:08:48 +0000 | [diff] [blame] | 334 | if (!(c_suppressJPEGImageDecoderErrors)) { | 
|  | 335 | char buffer[JMSG_LENGTH_MAX]; | 
|  | 336 | cinfo.err->format_message((const j_common_ptr)&cinfo, buffer); | 
|  | 337 | SkDebugf("libjpeg error %d <%s> from %s [%d %d]\n", | 
| sugoi | b227e37 | 2014-10-16 13:10:57 -0700 | [diff] [blame] | 338 | cinfo.err->msg_code, buffer, caller, width, height); | 
| halcanary@google.com | 04b57f8 | 2013-10-14 20:08:48 +0000 | [diff] [blame] | 339 | } | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 340 | } | 
|  | 341 |  | 
|  | 342 | static bool return_false(const jpeg_decompress_struct& cinfo, | 
|  | 343 | const char caller[]) { | 
|  | 344 | print_jpeg_decoder_errors(cinfo, 0, 0, caller); | 
|  | 345 | return false; | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 346 | } | 
|  | 347 |  | 
| mtklein | df78d0a | 2014-10-23 10:13:21 -0700 | [diff] [blame] | 348 | #ifdef SK_BUILD_FOR_ANDROID | 
| sugoi | b227e37 | 2014-10-16 13:10:57 -0700 | [diff] [blame] | 349 | static bool return_false(const jpeg_decompress_struct& cinfo, | 
|  | 350 | const SkBitmap& bm, const char caller[]) { | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 351 | print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller); | 
|  | 352 | return false; | 
| sugoi | b227e37 | 2014-10-16 13:10:57 -0700 | [diff] [blame] | 353 | } | 
| mtklein | df78d0a | 2014-10-23 10:13:21 -0700 | [diff] [blame] | 354 | #endif | 
| sugoi | b227e37 | 2014-10-16 13:10:57 -0700 | [diff] [blame] | 355 |  | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 356 | static SkImageDecoder::Result return_failure(const jpeg_decompress_struct& cinfo, | 
|  | 357 | const SkBitmap& bm, const char caller[]) { | 
|  | 358 | print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller); | 
|  | 359 | return SkImageDecoder::kFailure; | 
|  | 360 | } | 
|  | 361 |  | 
|  | 362 | /////////////////////////////////////////////////////////////////////////////// | 
|  | 363 |  | 
| robertphillips@google.com | 8570b5c | 2012-03-20 17:40:58 +0000 | [diff] [blame] | 364 | // Convert a scanline of CMYK samples to RGBX in place. Note that this | 
|  | 365 | // method moves the "scanline" pointer in its processing | 
|  | 366 | static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) { | 
|  | 367 | // At this point we've received CMYK pixels from libjpeg. We | 
| rmistry@google.com | d6176b0 | 2012-08-23 18:14:13 +0000 | [diff] [blame] | 368 | // perform a crude conversion to RGB (based on the formulae | 
| robertphillips@google.com | 8570b5c | 2012-03-20 17:40:58 +0000 | [diff] [blame] | 369 | // from easyrgb.com): | 
|  | 370 | //  CMYK -> CMY | 
|  | 371 | //    C = ( C * (1 - K) + K )      // for each CMY component | 
|  | 372 | //  CMY -> RGB | 
|  | 373 | //    R = ( 1 - C ) * 255          // for each RGB component | 
|  | 374 | // Unfortunately we are seeing inverted CMYK so all the original terms | 
|  | 375 | // are 1-. This yields: | 
|  | 376 | //  CMYK -> CMY | 
|  | 377 | //    C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K | 
|  | 378 | // The conversion from CMY->RGB remains the same | 
|  | 379 | for (unsigned int x = 0; x < width; ++x, scanline += 4) { | 
|  | 380 | scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]); | 
|  | 381 | scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]); | 
|  | 382 | scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]); | 
|  | 383 | scanline[3] = 255; | 
|  | 384 | } | 
|  | 385 | } | 
|  | 386 |  | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 387 | /** | 
|  | 388 | *  Common code for setting the error manager. | 
|  | 389 | */ | 
|  | 390 | static void set_error_mgr(jpeg_decompress_struct* cinfo, skjpeg_error_mgr* errorManager) { | 
|  | 391 | SkASSERT(cinfo != NULL); | 
|  | 392 | SkASSERT(errorManager != NULL); | 
|  | 393 | cinfo->err = jpeg_std_error(errorManager); | 
|  | 394 | errorManager->error_exit = skjpeg_error_exit; | 
|  | 395 | } | 
|  | 396 |  | 
|  | 397 | /** | 
|  | 398 | *  Common code for turning off upsampling and smoothing. Turning these | 
|  | 399 | *  off helps performance without showing noticable differences in the | 
|  | 400 | *  resulting bitmap. | 
|  | 401 | */ | 
|  | 402 | static void turn_off_visual_optimizations(jpeg_decompress_struct* cinfo) { | 
|  | 403 | SkASSERT(cinfo != NULL); | 
|  | 404 | /* this gives about 30% performance improvement. In theory it may | 
|  | 405 | reduce the visual quality, in practice I'm not seeing a difference | 
|  | 406 | */ | 
|  | 407 | cinfo->do_fancy_upsampling = 0; | 
|  | 408 |  | 
|  | 409 | /* this gives another few percents */ | 
|  | 410 | cinfo->do_block_smoothing = 0; | 
|  | 411 | } | 
|  | 412 |  | 
|  | 413 | /** | 
|  | 414 | * Common code for setting the dct method. | 
|  | 415 | */ | 
|  | 416 | static void set_dct_method(const SkImageDecoder& decoder, jpeg_decompress_struct* cinfo) { | 
|  | 417 | SkASSERT(cinfo != NULL); | 
|  | 418 | #ifdef DCT_IFAST_SUPPORTED | 
|  | 419 | if (decoder.getPreferQualityOverSpeed()) { | 
|  | 420 | cinfo->dct_method = JDCT_ISLOW; | 
|  | 421 | } else { | 
|  | 422 | cinfo->dct_method = JDCT_IFAST; | 
|  | 423 | } | 
|  | 424 | #else | 
|  | 425 | cinfo->dct_method = JDCT_ISLOW; | 
|  | 426 | #endif | 
|  | 427 | } | 
|  | 428 |  | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 429 | SkColorType SkJPEGImageDecoder::getBitmapColorType(jpeg_decompress_struct* cinfo) { | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 430 | SkASSERT(cinfo != NULL); | 
|  | 431 |  | 
|  | 432 | SrcDepth srcDepth = k32Bit_SrcDepth; | 
|  | 433 | if (JCS_GRAYSCALE == cinfo->jpeg_color_space) { | 
|  | 434 | srcDepth = k8BitGray_SrcDepth; | 
|  | 435 | } | 
|  | 436 |  | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 437 | SkColorType colorType = this->getPrefColorType(srcDepth, /*hasAlpha*/ false); | 
|  | 438 | switch (colorType) { | 
|  | 439 | case kAlpha_8_SkColorType: | 
|  | 440 | // Only respect A8 colortype if the original is grayscale, | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 441 | // in which case we will treat the grayscale as alpha | 
|  | 442 | // values. | 
|  | 443 | if (cinfo->jpeg_color_space != JCS_GRAYSCALE) { | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 444 | colorType = kN32_SkColorType; | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 445 | } | 
|  | 446 | break; | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 447 | case kN32_SkColorType: | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 448 | // Fall through. | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 449 | case kARGB_4444_SkColorType: | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 450 | // Fall through. | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 451 | case kRGB_565_SkColorType: | 
|  | 452 | // These are acceptable destination colortypes. | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 453 | break; | 
|  | 454 | default: | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 455 | // Force all other colortypes to 8888. | 
|  | 456 | colorType = kN32_SkColorType; | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 457 | break; | 
|  | 458 | } | 
|  | 459 |  | 
|  | 460 | switch (cinfo->jpeg_color_space) { | 
|  | 461 | case JCS_CMYK: | 
|  | 462 | // Fall through. | 
|  | 463 | case JCS_YCCK: | 
|  | 464 | // libjpeg cannot convert from CMYK or YCCK to RGB - here we set up | 
|  | 465 | // so libjpeg will give us CMYK samples back and we will later | 
|  | 466 | // manually convert them to RGB | 
|  | 467 | cinfo->out_color_space = JCS_CMYK; | 
|  | 468 | break; | 
|  | 469 | case JCS_GRAYSCALE: | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 470 | if (kAlpha_8_SkColorType == colorType) { | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 471 | cinfo->out_color_space = JCS_GRAYSCALE; | 
|  | 472 | break; | 
|  | 473 | } | 
|  | 474 | // The data is JCS_GRAYSCALE, but the caller wants some sort of RGB | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 475 | // colortype. Fall through to set to the default. | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 476 | default: | 
|  | 477 | cinfo->out_color_space = JCS_RGB; | 
|  | 478 | break; | 
|  | 479 | } | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 480 | return colorType; | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 481 | } | 
|  | 482 |  | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 483 | /** | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 484 | *  Based on the colortype and dither mode, adjust out_color_space and | 
|  | 485 | *  dither_mode of cinfo. Only does work in ANDROID_RGB | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 486 | */ | 
|  | 487 | static void adjust_out_color_space_and_dither(jpeg_decompress_struct* cinfo, | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 488 | SkColorType colorType, | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 489 | const SkImageDecoder& decoder) { | 
|  | 490 | SkASSERT(cinfo != NULL); | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 491 | #ifdef ANDROID_RGB | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 492 | cinfo->dither_mode = JDITHER_NONE; | 
|  | 493 | if (JCS_CMYK == cinfo->out_color_space) { | 
|  | 494 | return; | 
|  | 495 | } | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 496 | switch (colorType) { | 
|  | 497 | case kN32_SkColorType: | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 498 | cinfo->out_color_space = JCS_RGBA_8888; | 
|  | 499 | break; | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 500 | case kRGB_565_SkColorType: | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 501 | cinfo->out_color_space = JCS_RGB_565; | 
|  | 502 | if (decoder.getDitherImage()) { | 
|  | 503 | cinfo->dither_mode = JDITHER_ORDERED; | 
|  | 504 | } | 
|  | 505 | break; | 
|  | 506 | default: | 
|  | 507 | break; | 
|  | 508 | } | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 509 | #endif | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 510 | } | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 511 |  | 
| halcanary@google.com | fed3037 | 2013-10-04 12:46:45 +0000 | [diff] [blame] | 512 | /** | 
|  | 513 | Sets all pixels in given bitmap to SK_ColorWHITE for all rows >= y. | 
|  | 514 | Used when decoding fails partway through reading scanlines to fill | 
|  | 515 | remaining lines. */ | 
|  | 516 | static void fill_below_level(int y, SkBitmap* bitmap) { | 
| halcanary@google.com | 2dcf36e | 2013-10-04 14:35:38 +0000 | [diff] [blame] | 517 | SkIRect rect = SkIRect::MakeLTRB(0, y, bitmap->width(), bitmap->height()); | 
| halcanary@google.com | fed3037 | 2013-10-04 12:46:45 +0000 | [diff] [blame] | 518 | SkCanvas canvas(*bitmap); | 
| halcanary@google.com | 2dcf36e | 2013-10-04 14:35:38 +0000 | [diff] [blame] | 519 | canvas.clipRect(SkRect::Make(rect)); | 
| halcanary@google.com | fed3037 | 2013-10-04 12:46:45 +0000 | [diff] [blame] | 520 | canvas.drawColor(SK_ColorWHITE); | 
|  | 521 | } | 
|  | 522 |  | 
| scroggo@google.com | 5ee18dd | 2013-10-21 20:47:31 +0000 | [diff] [blame] | 523 | /** | 
|  | 524 | *  Get the config and bytes per pixel of the source data. Return | 
|  | 525 | *  whether the data is supported. | 
|  | 526 | */ | 
|  | 527 | static bool get_src_config(const jpeg_decompress_struct& cinfo, | 
|  | 528 | SkScaledBitmapSampler::SrcConfig* sc, | 
|  | 529 | int* srcBytesPerPixel) { | 
|  | 530 | SkASSERT(sc != NULL && srcBytesPerPixel != NULL); | 
|  | 531 | if (JCS_CMYK == cinfo.out_color_space) { | 
|  | 532 | // In this case we will manually convert the CMYK values to RGB | 
|  | 533 | *sc = SkScaledBitmapSampler::kRGBX; | 
|  | 534 | // The CMYK work-around relies on 4 components per pixel here | 
|  | 535 | *srcBytesPerPixel = 4; | 
|  | 536 | } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) { | 
|  | 537 | *sc = SkScaledBitmapSampler::kRGB; | 
|  | 538 | *srcBytesPerPixel = 3; | 
|  | 539 | #ifdef ANDROID_RGB | 
|  | 540 | } else if (JCS_RGBA_8888 == cinfo.out_color_space) { | 
|  | 541 | *sc = SkScaledBitmapSampler::kRGBX; | 
|  | 542 | *srcBytesPerPixel = 4; | 
|  | 543 | } else if (JCS_RGB_565 == cinfo.out_color_space) { | 
|  | 544 | *sc = SkScaledBitmapSampler::kRGB_565; | 
|  | 545 | *srcBytesPerPixel = 2; | 
|  | 546 | #endif | 
|  | 547 | } else if (1 == cinfo.out_color_components && | 
|  | 548 | JCS_GRAYSCALE == cinfo.out_color_space) { | 
|  | 549 | *sc = SkScaledBitmapSampler::kGray; | 
|  | 550 | *srcBytesPerPixel = 1; | 
|  | 551 | } else { | 
|  | 552 | return false; | 
|  | 553 | } | 
|  | 554 | return true; | 
|  | 555 | } | 
| halcanary@google.com | fed3037 | 2013-10-04 12:46:45 +0000 | [diff] [blame] | 556 |  | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 557 | SkImageDecoder::Result SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 558 | #ifdef TIME_DECODE | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 559 | SkAutoTime atm("JPEG Decode"); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 560 | #endif | 
|  | 561 |  | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 562 | JPEGAutoClean autoClean; | 
|  | 563 |  | 
|  | 564 | jpeg_decompress_struct  cinfo; | 
| scroggo@google.com | d4c3565 | 2013-08-01 15:03:42 +0000 | [diff] [blame] | 565 | skjpeg_source_mgr       srcManager(stream, this); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 566 |  | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 567 | skjpeg_error_mgr errorManager; | 
|  | 568 | set_error_mgr(&cinfo, &errorManager); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 569 |  | 
|  | 570 | // All objects need to be instantiated before this setjmp call so that | 
|  | 571 | // they will be cleaned up properly if an error occurs. | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 572 | if (setjmp(errorManager.fJmpBuf)) { | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 573 | return return_failure(cinfo, *bm, "setjmp"); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 574 | } | 
|  | 575 |  | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 576 | initialize_info(&cinfo, &srcManager); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 577 | autoClean.set(&cinfo); | 
|  | 578 |  | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 579 | int status = jpeg_read_header(&cinfo, true); | 
|  | 580 | if (status != JPEG_HEADER_OK) { | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 581 | return return_failure(cinfo, *bm, "read_header"); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 582 | } | 
|  | 583 |  | 
|  | 584 | /*  Try to fulfill the requested sampleSize. Since jpeg can do it (when it | 
|  | 585 | can) much faster that we, just use their num/denom api to approximate | 
|  | 586 | the size. | 
|  | 587 | */ | 
|  | 588 | int sampleSize = this->getSampleSize(); | 
|  | 589 |  | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 590 | set_dct_method(*this, &cinfo); | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 591 |  | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 592 | SkASSERT(1 == cinfo.scale_num); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 593 | cinfo.scale_denom = sampleSize; | 
|  | 594 |  | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 595 | turn_off_visual_optimizations(&cinfo); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 596 |  | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 597 | const SkColorType colorType = this->getBitmapColorType(&cinfo); | 
|  | 598 | const SkAlphaType alphaType = kAlpha_8_SkColorType == colorType ? | 
|  | 599 | kPremul_SkAlphaType : kOpaque_SkAlphaType; | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 600 |  | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 601 | adjust_out_color_space_and_dither(&cinfo, colorType, *this); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 602 |  | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 603 | if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) { | 
| scroggo@google.com | 5ee18dd | 2013-10-21 20:47:31 +0000 | [diff] [blame] | 604 | // Assume an A8 bitmap is not opaque to avoid the check of each | 
|  | 605 | // individual pixel. It is very unlikely to be opaque, since | 
|  | 606 | // an opaque A8 bitmap would not be very interesting. | 
|  | 607 | // Otherwise, a jpeg image is opaque. | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 608 | bool success = bm->setInfo(SkImageInfo::Make(cinfo.image_width, cinfo.image_height, | 
|  | 609 | colorType, alphaType)); | 
|  | 610 | return success ? kSuccess : kFailure; | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 611 | } | 
|  | 612 |  | 
|  | 613 | /*  image_width and image_height are the original dimensions, available | 
|  | 614 | after jpeg_read_header(). To see the scaled dimensions, we have to call | 
|  | 615 | jpeg_start_decompress(), and then read output_width and output_height. | 
|  | 616 | */ | 
|  | 617 | if (!jpeg_start_decompress(&cinfo)) { | 
|  | 618 | /*  If we failed here, we may still have enough information to return | 
|  | 619 | to the caller if they just wanted (subsampled bounds). If sampleSize | 
|  | 620 | was 1, then we would have already returned. Thus we just check if | 
|  | 621 | we're in kDecodeBounds_Mode, and that we have valid output sizes. | 
|  | 622 |  | 
|  | 623 | One reason to fail here is that we have insufficient stream data | 
|  | 624 | to complete the setup. However, output dimensions seem to get | 
|  | 625 | computed very early, which is why this special check can pay off. | 
|  | 626 | */ | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 627 | if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) { | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 628 | SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height, | 
|  | 629 | recompute_sampleSize(sampleSize, cinfo)); | 
| scroggo@google.com | 5ee18dd | 2013-10-21 20:47:31 +0000 | [diff] [blame] | 630 | // Assume an A8 bitmap is not opaque to avoid the check of each | 
|  | 631 | // individual pixel. It is very unlikely to be opaque, since | 
|  | 632 | // an opaque A8 bitmap would not be very interesting. | 
|  | 633 | // Otherwise, a jpeg image is opaque. | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 634 | bool success = bm->setInfo(SkImageInfo::Make(smpl.scaledWidth(), smpl.scaledHeight(), | 
|  | 635 | colorType, alphaType)); | 
|  | 636 | return success ? kSuccess : kFailure; | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 637 | } else { | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 638 | return return_failure(cinfo, *bm, "start_decompress"); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 639 | } | 
|  | 640 | } | 
|  | 641 | sampleSize = recompute_sampleSize(sampleSize, cinfo); | 
|  | 642 |  | 
| reed | 5926b86 | 2014-06-11 10:33:13 -0700 | [diff] [blame] | 643 | #ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 644 | // should we allow the Chooser (if present) to pick a colortype for us??? | 
|  | 645 | if (!this->chooseFromOneChoice(colorType, cinfo.output_width, cinfo.output_height)) { | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 646 | return return_failure(cinfo, *bm, "chooseFromOneChoice"); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 647 | } | 
| reed | 5926b86 | 2014-06-11 10:33:13 -0700 | [diff] [blame] | 648 | #endif | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 649 |  | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 650 | SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize); | 
| scroggo@google.com | 5ee18dd | 2013-10-21 20:47:31 +0000 | [diff] [blame] | 651 | // Assume an A8 bitmap is not opaque to avoid the check of each | 
|  | 652 | // individual pixel. It is very unlikely to be opaque, since | 
|  | 653 | // an opaque A8 bitmap would not be very interesting. | 
|  | 654 | // Otherwise, a jpeg image is opaque. | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 655 | bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), | 
|  | 656 | colorType, alphaType)); | 
| scroggo@google.com | bc69ce9 | 2013-07-09 15:45:14 +0000 | [diff] [blame] | 657 | if (SkImageDecoder::kDecodeBounds_Mode == mode) { | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 658 | return kSuccess; | 
| scroggo@google.com | bc69ce9 | 2013-07-09 15:45:14 +0000 | [diff] [blame] | 659 | } | 
|  | 660 | if (!this->allocPixelRef(bm, NULL)) { | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 661 | return return_failure(cinfo, *bm, "allocPixelRef"); | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 662 | } | 
|  | 663 |  | 
|  | 664 | SkAutoLockPixels alp(*bm); | 
|  | 665 |  | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 666 | #ifdef ANDROID_RGB | 
|  | 667 | /* short-circuit the SkScaledBitmapSampler when possible, as this gives | 
|  | 668 | a significant performance boost. | 
|  | 669 | */ | 
|  | 670 | if (sampleSize == 1 && | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 671 | ((kN32_SkColorType == colorType && cinfo.out_color_space == JCS_RGBA_8888) || | 
|  | 672 | (kRGB_565_SkColorType == colorType && cinfo.out_color_space == JCS_RGB_565))) | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 673 | { | 
| scroggo@google.com | bc69ce9 | 2013-07-09 15:45:14 +0000 | [diff] [blame] | 674 | JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels(); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 675 | INT32 const bpr =  bm->rowBytes(); | 
| rmistry@google.com | d6176b0 | 2012-08-23 18:14:13 +0000 | [diff] [blame] | 676 |  | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 677 | while (cinfo.output_scanline < cinfo.output_height) { | 
|  | 678 | int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 679 | if (0 == row_count) { | 
| halcanary@google.com | fed3037 | 2013-10-04 12:46:45 +0000 | [diff] [blame] | 680 | // if row_count == 0, then we didn't get a scanline, | 
|  | 681 | // so return early.  We will return a partial image. | 
|  | 682 | fill_below_level(cinfo.output_scanline, bm); | 
|  | 683 | cinfo.output_scanline = cinfo.output_height; | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 684 | jpeg_finish_decompress(&cinfo); | 
|  | 685 | return kPartialSuccess; | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 686 | } | 
|  | 687 | if (this->shouldCancelDecode()) { | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 688 | return return_failure(cinfo, *bm, "shouldCancelDecode"); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 689 | } | 
|  | 690 | rowptr += bpr; | 
|  | 691 | } | 
|  | 692 | jpeg_finish_decompress(&cinfo); | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 693 | return kSuccess; | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 694 | } | 
|  | 695 | #endif | 
| robertphillips@google.com | 8570b5c | 2012-03-20 17:40:58 +0000 | [diff] [blame] | 696 |  | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 697 | // check for supported formats | 
|  | 698 | SkScaledBitmapSampler::SrcConfig sc; | 
| scroggo@google.com | 5ee18dd | 2013-10-21 20:47:31 +0000 | [diff] [blame] | 699 | int srcBytesPerPixel; | 
|  | 700 |  | 
|  | 701 | if (!get_src_config(cinfo, &sc, &srcBytesPerPixel)) { | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 702 | return return_failure(cinfo, *bm, "jpeg colorspace"); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 703 | } | 
|  | 704 |  | 
| scroggo@google.com | 8d23924 | 2013-10-01 17:27:15 +0000 | [diff] [blame] | 705 | if (!sampler.begin(bm, sc, *this)) { | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 706 | return return_failure(cinfo, *bm, "sampler.begin"); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 707 | } | 
|  | 708 |  | 
| scroggo@google.com | 5ee18dd | 2013-10-21 20:47:31 +0000 | [diff] [blame] | 709 | SkAutoMalloc srcStorage(cinfo.output_width * srcBytesPerPixel); | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 710 | uint8_t* srcRow = (uint8_t*)srcStorage.get(); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 711 |  | 
|  | 712 | //  Possibly skip initial rows [sampler.srcY0] | 
|  | 713 | if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) { | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 714 | return return_failure(cinfo, *bm, "skip rows"); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 715 | } | 
|  | 716 |  | 
|  | 717 | // now loop through scanlines until y == bm->height() - 1 | 
|  | 718 | for (int y = 0;; y++) { | 
|  | 719 | JSAMPLE* rowptr = (JSAMPLE*)srcRow; | 
|  | 720 | int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1); | 
|  | 721 | if (0 == row_count) { | 
| halcanary@google.com | fed3037 | 2013-10-04 12:46:45 +0000 | [diff] [blame] | 722 | // if row_count == 0, then we didn't get a scanline, | 
|  | 723 | // so return early.  We will return a partial image. | 
|  | 724 | fill_below_level(y, bm); | 
|  | 725 | cinfo.output_scanline = cinfo.output_height; | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 726 | jpeg_finish_decompress(&cinfo); | 
|  | 727 | return kSuccess; | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 728 | } | 
|  | 729 | if (this->shouldCancelDecode()) { | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 730 | return return_failure(cinfo, *bm, "shouldCancelDecode"); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 731 | } | 
| robertphillips@google.com | 8570b5c | 2012-03-20 17:40:58 +0000 | [diff] [blame] | 732 |  | 
|  | 733 | if (JCS_CMYK == cinfo.out_color_space) { | 
|  | 734 | convert_CMYK_to_RGB(srcRow, cinfo.output_width); | 
|  | 735 | } | 
|  | 736 |  | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 737 | sampler.next(srcRow); | 
|  | 738 | if (bm->height() - 1 == y) { | 
|  | 739 | // we're done | 
|  | 740 | break; | 
|  | 741 | } | 
|  | 742 |  | 
|  | 743 | if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) { | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 744 | return return_failure(cinfo, *bm, "skip rows"); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 745 | } | 
|  | 746 | } | 
|  | 747 |  | 
|  | 748 | // we formally skip the rest, so we don't get a complaint from libjpeg | 
|  | 749 | if (!skip_src_rows(&cinfo, srcRow, | 
|  | 750 | cinfo.output_height - cinfo.output_scanline)) { | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 751 | return return_failure(cinfo, *bm, "skip rows"); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 752 | } | 
|  | 753 | jpeg_finish_decompress(&cinfo); | 
|  | 754 |  | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 755 | return kSuccess; | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 756 | } | 
|  | 757 |  | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 758 | /////////////////////////////////////////////////////////////////////////////// | 
|  | 759 |  | 
| sugoi | b227e37 | 2014-10-16 13:10:57 -0700 | [diff] [blame] | 760 | enum SizeType { | 
|  | 761 | kSizeForMemoryAllocation_SizeType, | 
|  | 762 | kActualSize_SizeType | 
|  | 763 | }; | 
|  | 764 |  | 
|  | 765 | static SkISize compute_yuv_size(const jpeg_decompress_struct& info, int component, | 
|  | 766 | SizeType sizeType) { | 
|  | 767 | if (sizeType == kSizeForMemoryAllocation_SizeType) { | 
|  | 768 | return SkISize::Make(info.cur_comp_info[component]->width_in_blocks * DCTSIZE, | 
|  | 769 | info.cur_comp_info[component]->height_in_blocks * DCTSIZE); | 
|  | 770 | } | 
|  | 771 | return SkISize::Make(info.cur_comp_info[component]->downsampled_width, | 
|  | 772 | info.cur_comp_info[component]->downsampled_height); | 
|  | 773 | } | 
|  | 774 |  | 
| sugoi | b227e37 | 2014-10-16 13:10:57 -0700 | [diff] [blame] | 775 | static void update_components_sizes(const jpeg_decompress_struct& cinfo, SkISize componentSizes[3], | 
|  | 776 | SizeType sizeType) { | 
|  | 777 | for (int i = 0; i < 3; ++i) { | 
|  | 778 | componentSizes[i] = compute_yuv_size(cinfo, i, sizeType); | 
|  | 779 | } | 
|  | 780 | } | 
|  | 781 |  | 
|  | 782 | static bool output_raw_data(jpeg_decompress_struct& cinfo, void* planes[3], size_t rowBytes[3]) { | 
|  | 783 | // U size and V size have to be the same if we're calling output_raw_data() | 
|  | 784 | SkISize uvSize = compute_yuv_size(cinfo, 1, kSizeForMemoryAllocation_SizeType); | 
|  | 785 | SkASSERT(uvSize == compute_yuv_size(cinfo, 2, kSizeForMemoryAllocation_SizeType)); | 
|  | 786 |  | 
|  | 787 | JSAMPARRAY bufferraw[3]; | 
|  | 788 | JSAMPROW bufferraw2[32]; | 
|  | 789 | bufferraw[0] = &bufferraw2[0]; // Y channel rows (8 or 16) | 
|  | 790 | bufferraw[1] = &bufferraw2[16]; // U channel rows (8) | 
|  | 791 | bufferraw[2] = &bufferraw2[24]; // V channel rows (8) | 
|  | 792 | int yWidth = cinfo.output_width; | 
|  | 793 | int yHeight = cinfo.output_height; | 
|  | 794 | int yMaxH = yHeight - 1; | 
|  | 795 | int v = cinfo.cur_comp_info[0]->v_samp_factor; | 
|  | 796 | int uvMaxH = uvSize.height() - 1; | 
|  | 797 | JSAMPROW outputY = static_cast<JSAMPROW>(planes[0]); | 
|  | 798 | JSAMPROW outputU = static_cast<JSAMPROW>(planes[1]); | 
|  | 799 | JSAMPROW outputV = static_cast<JSAMPROW>(planes[2]); | 
|  | 800 | size_t rowBytesY = rowBytes[0]; | 
|  | 801 | size_t rowBytesU = rowBytes[1]; | 
|  | 802 | size_t rowBytesV = rowBytes[2]; | 
|  | 803 |  | 
|  | 804 | int yScanlinesToRead = DCTSIZE * v; | 
|  | 805 | SkAutoMalloc lastRowStorage(yWidth * 8); | 
|  | 806 | JSAMPROW yLastRow = (JSAMPROW)lastRowStorage.get(); | 
|  | 807 | JSAMPROW uLastRow = yLastRow + 2 * yWidth; | 
|  | 808 | JSAMPROW vLastRow = uLastRow + 2 * yWidth; | 
|  | 809 | JSAMPROW dummyRow = vLastRow + 2 * yWidth; | 
|  | 810 |  | 
|  | 811 | while (cinfo.output_scanline < cinfo.output_height) { | 
|  | 812 | // Request 8 or 16 scanlines: returns 0 or more scanlines. | 
|  | 813 | bool hasYLastRow(false), hasUVLastRow(false); | 
|  | 814 | // Assign 8 or 16 rows of memory to read the Y channel. | 
|  | 815 | for (int i = 0; i < yScanlinesToRead; ++i) { | 
|  | 816 | int scanline = (cinfo.output_scanline + i); | 
|  | 817 | if (scanline < yMaxH) { | 
|  | 818 | bufferraw2[i] = &outputY[scanline * rowBytesY]; | 
|  | 819 | } else if (scanline == yMaxH) { | 
|  | 820 | bufferraw2[i] = yLastRow; | 
|  | 821 | hasYLastRow = true; | 
|  | 822 | } else { | 
|  | 823 | bufferraw2[i] = dummyRow; | 
|  | 824 | } | 
|  | 825 | } | 
|  | 826 | int scaledScanline = cinfo.output_scanline / v; | 
|  | 827 | // Assign 8 rows of memory to read the U and V channels. | 
|  | 828 | for (int i = 0; i < 8; ++i) { | 
|  | 829 | int scanline = (scaledScanline + i); | 
|  | 830 | if (scanline < uvMaxH) { | 
|  | 831 | bufferraw2[16 + i] = &outputU[scanline * rowBytesU]; | 
|  | 832 | bufferraw2[24 + i] = &outputV[scanline * rowBytesV]; | 
|  | 833 | } else if (scanline == uvMaxH) { | 
|  | 834 | bufferraw2[16 + i] = uLastRow; | 
|  | 835 | bufferraw2[24 + i] = vLastRow; | 
|  | 836 | hasUVLastRow = true; | 
|  | 837 | } else { | 
|  | 838 | bufferraw2[16 + i] = dummyRow; | 
|  | 839 | bufferraw2[24 + i] = dummyRow; | 
|  | 840 | } | 
|  | 841 | } | 
|  | 842 | JDIMENSION scanlinesRead = jpeg_read_raw_data(&cinfo, bufferraw, yScanlinesToRead); | 
|  | 843 |  | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 844 | if (scanlinesRead == 0) { | 
| sugoi | b227e37 | 2014-10-16 13:10:57 -0700 | [diff] [blame] | 845 | return false; | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 846 | } | 
| sugoi | b227e37 | 2014-10-16 13:10:57 -0700 | [diff] [blame] | 847 |  | 
|  | 848 | if (hasYLastRow) { | 
|  | 849 | memcpy(&outputY[yMaxH * rowBytesY], yLastRow, yWidth); | 
|  | 850 | } | 
|  | 851 | if (hasUVLastRow) { | 
|  | 852 | memcpy(&outputU[uvMaxH * rowBytesU], uLastRow, uvSize.width()); | 
|  | 853 | memcpy(&outputV[uvMaxH * rowBytesV], vLastRow, uvSize.width()); | 
|  | 854 | } | 
|  | 855 | } | 
|  | 856 |  | 
|  | 857 | cinfo.output_scanline = SkMin32(cinfo.output_scanline, cinfo.output_height); | 
|  | 858 |  | 
|  | 859 | return true; | 
|  | 860 | } | 
|  | 861 |  | 
|  | 862 | bool SkJPEGImageDecoder::onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3], | 
|  | 863 | void* planes[3], size_t rowBytes[3], | 
|  | 864 | SkYUVColorSpace* colorSpace) { | 
|  | 865 | #ifdef TIME_DECODE | 
|  | 866 | SkAutoTime atm("JPEG YUV8 Decode"); | 
|  | 867 | #endif | 
|  | 868 |  | 
|  | 869 | if (this->getSampleSize() != 1) { | 
|  | 870 | return false; // Resizing not supported | 
|  | 871 | } | 
|  | 872 |  | 
|  | 873 | JPEGAutoClean autoClean; | 
|  | 874 |  | 
|  | 875 | jpeg_decompress_struct  cinfo; | 
|  | 876 | skjpeg_source_mgr       srcManager(stream, this); | 
|  | 877 |  | 
|  | 878 | skjpeg_error_mgr errorManager; | 
|  | 879 | set_error_mgr(&cinfo, &errorManager); | 
|  | 880 |  | 
|  | 881 | // All objects need to be instantiated before this setjmp call so that | 
|  | 882 | // they will be cleaned up properly if an error occurs. | 
|  | 883 | if (setjmp(errorManager.fJmpBuf)) { | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 884 | return return_false(cinfo, "setjmp YUV8"); | 
| sugoi | b227e37 | 2014-10-16 13:10:57 -0700 | [diff] [blame] | 885 | } | 
|  | 886 |  | 
|  | 887 | initialize_info(&cinfo, &srcManager); | 
|  | 888 | autoClean.set(&cinfo); | 
|  | 889 |  | 
|  | 890 | int status = jpeg_read_header(&cinfo, true); | 
|  | 891 | if (status != JPEG_HEADER_OK) { | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 892 | return return_false(cinfo, "read_header YUV8"); | 
| sugoi | b227e37 | 2014-10-16 13:10:57 -0700 | [diff] [blame] | 893 | } | 
|  | 894 |  | 
|  | 895 | if (cinfo.jpeg_color_space != JCS_YCbCr) { | 
|  | 896 | // It's not an error to not be encoded in YUV, so no need to use return_false() | 
|  | 897 | return false; | 
|  | 898 | } | 
|  | 899 |  | 
|  | 900 | cinfo.out_color_space = JCS_YCbCr; | 
|  | 901 | cinfo.raw_data_out = TRUE; | 
|  | 902 |  | 
|  | 903 | if (!planes || !planes[0] || !rowBytes || !rowBytes[0]) { // Compute size only | 
|  | 904 | update_components_sizes(cinfo, componentSizes, kSizeForMemoryAllocation_SizeType); | 
|  | 905 | return true; | 
|  | 906 | } | 
|  | 907 |  | 
|  | 908 | set_dct_method(*this, &cinfo); | 
|  | 909 |  | 
|  | 910 | SkASSERT(1 == cinfo.scale_num); | 
|  | 911 | cinfo.scale_denom = 1; | 
|  | 912 |  | 
|  | 913 | turn_off_visual_optimizations(&cinfo); | 
|  | 914 |  | 
|  | 915 | #ifdef ANDROID_RGB | 
|  | 916 | cinfo.dither_mode = JDITHER_NONE; | 
|  | 917 | #endif | 
|  | 918 |  | 
|  | 919 | /*  image_width and image_height are the original dimensions, available | 
|  | 920 | after jpeg_read_header(). To see the scaled dimensions, we have to call | 
|  | 921 | jpeg_start_decompress(), and then read output_width and output_height. | 
|  | 922 | */ | 
|  | 923 | if (!jpeg_start_decompress(&cinfo)) { | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 924 | return return_false(cinfo, "start_decompress YUV8"); | 
| sugoi | b227e37 | 2014-10-16 13:10:57 -0700 | [diff] [blame] | 925 | } | 
|  | 926 |  | 
|  | 927 | if (!output_raw_data(cinfo, planes, rowBytes)) { | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 928 | return return_false(cinfo, "output_raw_data"); | 
| sugoi | b227e37 | 2014-10-16 13:10:57 -0700 | [diff] [blame] | 929 | } | 
|  | 930 |  | 
|  | 931 | update_components_sizes(cinfo, componentSizes, kActualSize_SizeType); | 
|  | 932 | jpeg_finish_decompress(&cinfo); | 
|  | 933 |  | 
|  | 934 | if (NULL != colorSpace) { | 
|  | 935 | *colorSpace = kJPEG_SkYUVColorSpace; | 
|  | 936 | } | 
|  | 937 |  | 
|  | 938 | return true; | 
|  | 939 | } | 
|  | 940 |  | 
| scroggo | 2a12080 | 2014-10-22 12:07:00 -0700 | [diff] [blame] | 941 | /////////////////////////////////////////////////////////////////////////////// | 
|  | 942 |  | 
| scroggo@google.com | d79277f | 2013-08-07 19:53:53 +0000 | [diff] [blame] | 943 | #ifdef SK_BUILD_FOR_ANDROID | 
| scroggo@google.com | b5571b3 | 2013-09-25 21:34:24 +0000 | [diff] [blame] | 944 | bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) { | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 945 |  | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 946 | SkAutoTDelete<SkJPEGImageIndex> imageIndex(SkNEW_ARGS(SkJPEGImageIndex, (stream, this))); | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 947 | jpeg_decompress_struct* cinfo = imageIndex->cinfo(); | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 948 |  | 
|  | 949 | skjpeg_error_mgr sk_err; | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 950 | set_error_mgr(cinfo, &sk_err); | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 951 |  | 
|  | 952 | // All objects need to be instantiated before this setjmp call so that | 
|  | 953 | // they will be cleaned up properly if an error occurs. | 
|  | 954 | if (setjmp(sk_err.fJmpBuf)) { | 
|  | 955 | return false; | 
|  | 956 | } | 
|  | 957 |  | 
|  | 958 | // create the cinfo used to create/build the huffmanIndex | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 959 | if (!imageIndex->initializeInfoAndReadHeader()) { | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 960 | return false; | 
|  | 961 | } | 
|  | 962 |  | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 963 | if (!imageIndex->buildHuffmanIndex()) { | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 964 | return false; | 
|  | 965 | } | 
|  | 966 |  | 
|  | 967 | // destroy the cinfo used to create/build the huffman index | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 968 | imageIndex->destroyInfo(); | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 969 |  | 
|  | 970 | // Init decoder to image decode mode | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 971 | if (!imageIndex->initializeInfoAndReadHeader()) { | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 972 | return false; | 
|  | 973 | } | 
|  | 974 |  | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 975 | // FIXME: This sets cinfo->out_color_space, which we may change later | 
|  | 976 | // based on the config in onDecodeSubset. This should be fine, since | 
|  | 977 | // jpeg_init_read_tile_scanline will check out_color_space again after | 
|  | 978 | // that change (when it calls jinit_color_deconverter). | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 979 | (void) this->getBitmapColorType(cinfo); | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 980 |  | 
|  | 981 | turn_off_visual_optimizations(cinfo); | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 982 |  | 
|  | 983 | // instead of jpeg_start_decompress() we start a tiled decompress | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 984 | if (!imageIndex->startTileDecompress()) { | 
|  | 985 | return false; | 
|  | 986 | } | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 987 |  | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 988 | SkASSERT(1 == cinfo->scale_num); | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 989 | fImageWidth = cinfo->output_width; | 
|  | 990 | fImageHeight = cinfo->output_height; | 
|  | 991 |  | 
|  | 992 | if (width) { | 
|  | 993 | *width = fImageWidth; | 
|  | 994 | } | 
|  | 995 | if (height) { | 
|  | 996 | *height = fImageHeight; | 
|  | 997 | } | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 998 |  | 
|  | 999 | SkDELETE(fImageIndex); | 
| scroggo@google.com | a1a5154 | 2013-08-07 21:02:32 +0000 | [diff] [blame] | 1000 | fImageIndex = imageIndex.detach(); | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 1001 |  | 
|  | 1002 | return true; | 
|  | 1003 | } | 
|  | 1004 |  | 
| scroggo@google.com | 7e6fcee | 2013-05-03 20:14:28 +0000 | [diff] [blame] | 1005 | bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) { | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 1006 | if (NULL == fImageIndex) { | 
|  | 1007 | return false; | 
|  | 1008 | } | 
|  | 1009 | jpeg_decompress_struct* cinfo = fImageIndex->cinfo(); | 
|  | 1010 |  | 
|  | 1011 | SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight); | 
|  | 1012 | if (!rect.intersect(region)) { | 
|  | 1013 | // If the requested region is entirely outside the image return false | 
|  | 1014 | return false; | 
|  | 1015 | } | 
|  | 1016 |  | 
|  | 1017 |  | 
|  | 1018 | skjpeg_error_mgr errorManager; | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 1019 | set_error_mgr(cinfo, &errorManager); | 
|  | 1020 |  | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 1021 | if (setjmp(errorManager.fJmpBuf)) { | 
|  | 1022 | return false; | 
|  | 1023 | } | 
|  | 1024 |  | 
|  | 1025 | int requestedSampleSize = this->getSampleSize(); | 
|  | 1026 | cinfo->scale_denom = requestedSampleSize; | 
|  | 1027 |  | 
| scroggo@google.com | 590a5af | 2013-08-07 21:09:13 +0000 | [diff] [blame] | 1028 | set_dct_method(*this, cinfo); | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 1029 |  | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 1030 | const SkColorType colorType = this->getBitmapColorType(cinfo); | 
|  | 1031 | adjust_out_color_space_and_dither(cinfo, colorType, *this); | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 1032 |  | 
|  | 1033 | int startX = rect.fLeft; | 
|  | 1034 | int startY = rect.fTop; | 
|  | 1035 | int width = rect.width(); | 
|  | 1036 | int height = rect.height(); | 
|  | 1037 |  | 
|  | 1038 | jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), | 
|  | 1039 | &startX, &startY, &width, &height); | 
|  | 1040 | int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo); | 
|  | 1041 | int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size); | 
|  | 1042 |  | 
|  | 1043 | SkScaledBitmapSampler sampler(width, height, skiaSampleSize); | 
|  | 1044 |  | 
|  | 1045 | SkBitmap bitmap; | 
| scroggo@google.com | 5ee18dd | 2013-10-21 20:47:31 +0000 | [diff] [blame] | 1046 | // Assume an A8 bitmap is not opaque to avoid the check of each | 
|  | 1047 | // individual pixel. It is very unlikely to be opaque, since | 
|  | 1048 | // an opaque A8 bitmap would not be very interesting. | 
|  | 1049 | // Otherwise, a jpeg image is opaque. | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 1050 | bitmap.setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), colorType, | 
|  | 1051 | kAlpha_8_SkColorType == colorType ? | 
|  | 1052 | kPremul_SkAlphaType : kOpaque_SkAlphaType)); | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 1053 |  | 
|  | 1054 | // Check ahead of time if the swap(dest, src) is possible or not. | 
|  | 1055 | // If yes, then we will stick to AllocPixelRef since it's cheaper with the | 
|  | 1056 | // swap happening. If no, then we will use alloc to allocate pixels to | 
|  | 1057 | // prevent garbage collection. | 
|  | 1058 | int w = rect.width() / actualSampleSize; | 
|  | 1059 | int h = rect.height() / actualSampleSize; | 
|  | 1060 | bool swapOnly = (rect == region) && bm->isNull() && | 
|  | 1061 | (w == bitmap.width()) && (h == bitmap.height()) && | 
|  | 1062 | ((startX - rect.x()) / actualSampleSize == 0) && | 
|  | 1063 | ((startY - rect.y()) / actualSampleSize == 0); | 
|  | 1064 | if (swapOnly) { | 
|  | 1065 | if (!this->allocPixelRef(&bitmap, NULL)) { | 
|  | 1066 | return return_false(*cinfo, bitmap, "allocPixelRef"); | 
|  | 1067 | } | 
|  | 1068 | } else { | 
| reed | 8482504 | 2014-09-02 12:50:45 -0700 | [diff] [blame] | 1069 | if (!bitmap.tryAllocPixels()) { | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 1070 | return return_false(*cinfo, bitmap, "allocPixels"); | 
|  | 1071 | } | 
|  | 1072 | } | 
|  | 1073 |  | 
|  | 1074 | SkAutoLockPixels alp(bitmap); | 
|  | 1075 |  | 
|  | 1076 | #ifdef ANDROID_RGB | 
|  | 1077 | /* short-circuit the SkScaledBitmapSampler when possible, as this gives | 
|  | 1078 | a significant performance boost. | 
|  | 1079 | */ | 
|  | 1080 | if (skiaSampleSize == 1 && | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 1081 | ((kN32_SkColorType == colorType && cinfo->out_color_space == JCS_RGBA_8888) || | 
|  | 1082 | (kRGB_565_SkColorType == colorType && cinfo->out_color_space == JCS_RGB_565))) | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 1083 | { | 
|  | 1084 | JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels(); | 
|  | 1085 | INT32 const bpr = bitmap.rowBytes(); | 
|  | 1086 | int rowTotalCount = 0; | 
|  | 1087 |  | 
|  | 1088 | while (rowTotalCount < height) { | 
|  | 1089 | int rowCount = jpeg_read_tile_scanline(cinfo, | 
|  | 1090 | fImageIndex->huffmanIndex(), | 
|  | 1091 | &rowptr); | 
| halcanary@google.com | fed3037 | 2013-10-04 12:46:45 +0000 | [diff] [blame] | 1092 | // if rowCount == 0, then we didn't get a scanline, so abort. | 
|  | 1093 | // onDecodeSubset() relies on onBuildTileIndex(), which | 
|  | 1094 | // needs a complete image to succeed. | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 1095 | if (0 == rowCount) { | 
|  | 1096 | return return_false(*cinfo, bitmap, "read_scanlines"); | 
|  | 1097 | } | 
|  | 1098 | if (this->shouldCancelDecode()) { | 
|  | 1099 | return return_false(*cinfo, bitmap, "shouldCancelDecode"); | 
|  | 1100 | } | 
|  | 1101 | rowTotalCount += rowCount; | 
|  | 1102 | rowptr += bpr; | 
|  | 1103 | } | 
|  | 1104 |  | 
|  | 1105 | if (swapOnly) { | 
|  | 1106 | bm->swap(bitmap); | 
|  | 1107 | } else { | 
|  | 1108 | cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(), | 
|  | 1109 | region.width(), region.height(), startX, startY); | 
|  | 1110 | } | 
|  | 1111 | return true; | 
|  | 1112 | } | 
|  | 1113 | #endif | 
|  | 1114 |  | 
|  | 1115 | // check for supported formats | 
|  | 1116 | SkScaledBitmapSampler::SrcConfig sc; | 
| scroggo@google.com | 5ee18dd | 2013-10-21 20:47:31 +0000 | [diff] [blame] | 1117 | int srcBytesPerPixel; | 
|  | 1118 |  | 
|  | 1119 | if (!get_src_config(*cinfo, &sc, &srcBytesPerPixel)) { | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 1120 | return return_false(*cinfo, *bm, "jpeg colorspace"); | 
|  | 1121 | } | 
|  | 1122 |  | 
| scroggo@google.com | 8d23924 | 2013-10-01 17:27:15 +0000 | [diff] [blame] | 1123 | if (!sampler.begin(&bitmap, sc, *this)) { | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 1124 | return return_false(*cinfo, bitmap, "sampler.begin"); | 
|  | 1125 | } | 
|  | 1126 |  | 
| scroggo@google.com | 5ee18dd | 2013-10-21 20:47:31 +0000 | [diff] [blame] | 1127 | SkAutoMalloc  srcStorage(width * srcBytesPerPixel); | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 1128 | uint8_t* srcRow = (uint8_t*)srcStorage.get(); | 
|  | 1129 |  | 
|  | 1130 | //  Possibly skip initial rows [sampler.srcY0] | 
|  | 1131 | if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) { | 
|  | 1132 | return return_false(*cinfo, bitmap, "skip rows"); | 
|  | 1133 | } | 
|  | 1134 |  | 
|  | 1135 | // now loop through scanlines until y == bitmap->height() - 1 | 
|  | 1136 | for (int y = 0;; y++) { | 
|  | 1137 | JSAMPLE* rowptr = (JSAMPLE*)srcRow; | 
|  | 1138 | int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr); | 
| halcanary@google.com | fed3037 | 2013-10-04 12:46:45 +0000 | [diff] [blame] | 1139 | // if row_count == 0, then we didn't get a scanline, so abort. | 
|  | 1140 | // onDecodeSubset() relies on onBuildTileIndex(), which | 
|  | 1141 | // needs a complete image to succeed. | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 1142 | if (0 == row_count) { | 
|  | 1143 | return return_false(*cinfo, bitmap, "read_scanlines"); | 
|  | 1144 | } | 
|  | 1145 | if (this->shouldCancelDecode()) { | 
|  | 1146 | return return_false(*cinfo, bitmap, "shouldCancelDecode"); | 
|  | 1147 | } | 
|  | 1148 |  | 
|  | 1149 | if (JCS_CMYK == cinfo->out_color_space) { | 
|  | 1150 | convert_CMYK_to_RGB(srcRow, width); | 
|  | 1151 | } | 
|  | 1152 |  | 
|  | 1153 | sampler.next(srcRow); | 
|  | 1154 | if (bitmap.height() - 1 == y) { | 
|  | 1155 | // we're done | 
|  | 1156 | break; | 
|  | 1157 | } | 
|  | 1158 |  | 
|  | 1159 | if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, | 
|  | 1160 | sampler.srcDY() - 1)) { | 
|  | 1161 | return return_false(*cinfo, bitmap, "skip rows"); | 
|  | 1162 | } | 
|  | 1163 | } | 
|  | 1164 | if (swapOnly) { | 
|  | 1165 | bm->swap(bitmap); | 
|  | 1166 | } else { | 
|  | 1167 | cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(), | 
|  | 1168 | region.width(), region.height(), startX, startY); | 
|  | 1169 | } | 
|  | 1170 | return true; | 
|  | 1171 | } | 
|  | 1172 | #endif | 
|  | 1173 |  | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1174 | /////////////////////////////////////////////////////////////////////////////// | 
|  | 1175 |  | 
|  | 1176 | #include "SkColorPriv.h" | 
|  | 1177 |  | 
|  | 1178 | // taken from jcolor.c in libjpeg | 
|  | 1179 | #if 0   // 16bit - precise but slow | 
|  | 1180 | #define CYR     19595   // 0.299 | 
|  | 1181 | #define CYG     38470   // 0.587 | 
|  | 1182 | #define CYB      7471   // 0.114 | 
|  | 1183 |  | 
|  | 1184 | #define CUR    -11059   // -0.16874 | 
|  | 1185 | #define CUG    -21709   // -0.33126 | 
|  | 1186 | #define CUB     32768   // 0.5 | 
|  | 1187 |  | 
|  | 1188 | #define CVR     32768   // 0.5 | 
|  | 1189 | #define CVG    -27439   // -0.41869 | 
|  | 1190 | #define CVB     -5329   // -0.08131 | 
|  | 1191 |  | 
|  | 1192 | #define CSHIFT  16 | 
|  | 1193 | #else      // 8bit - fast, slightly less precise | 
|  | 1194 | #define CYR     77    // 0.299 | 
|  | 1195 | #define CYG     150    // 0.587 | 
|  | 1196 | #define CYB      29    // 0.114 | 
|  | 1197 |  | 
|  | 1198 | #define CUR     -43    // -0.16874 | 
|  | 1199 | #define CUG    -85    // -0.33126 | 
|  | 1200 | #define CUB     128    // 0.5 | 
|  | 1201 |  | 
|  | 1202 | #define CVR      128   // 0.5 | 
|  | 1203 | #define CVG     -107   // -0.41869 | 
|  | 1204 | #define CVB      -21   // -0.08131 | 
|  | 1205 |  | 
|  | 1206 | #define CSHIFT  8 | 
|  | 1207 | #endif | 
|  | 1208 |  | 
|  | 1209 | static void rgb2yuv_32(uint8_t dst[], SkPMColor c) { | 
|  | 1210 | int r = SkGetPackedR32(c); | 
|  | 1211 | int g = SkGetPackedG32(c); | 
|  | 1212 | int b = SkGetPackedB32(c); | 
|  | 1213 |  | 
|  | 1214 | int  y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT; | 
|  | 1215 | int  u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT; | 
|  | 1216 | int  v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT; | 
|  | 1217 |  | 
|  | 1218 | dst[0] = SkToU8(y); | 
|  | 1219 | dst[1] = SkToU8(u + 128); | 
|  | 1220 | dst[2] = SkToU8(v + 128); | 
|  | 1221 | } | 
|  | 1222 |  | 
|  | 1223 | static void rgb2yuv_4444(uint8_t dst[], U16CPU c) { | 
|  | 1224 | int r = SkGetPackedR4444(c); | 
|  | 1225 | int g = SkGetPackedG4444(c); | 
|  | 1226 | int b = SkGetPackedB4444(c); | 
|  | 1227 |  | 
|  | 1228 | int  y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4); | 
|  | 1229 | int  u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4); | 
|  | 1230 | int  v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4); | 
|  | 1231 |  | 
|  | 1232 | dst[0] = SkToU8(y); | 
|  | 1233 | dst[1] = SkToU8(u + 128); | 
|  | 1234 | dst[2] = SkToU8(v + 128); | 
|  | 1235 | } | 
|  | 1236 |  | 
|  | 1237 | static void rgb2yuv_16(uint8_t dst[], U16CPU c) { | 
|  | 1238 | int r = SkGetPackedR16(c); | 
|  | 1239 | int g = SkGetPackedG16(c); | 
|  | 1240 | int b = SkGetPackedB16(c); | 
|  | 1241 |  | 
|  | 1242 | int  y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2); | 
|  | 1243 | int  u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2); | 
|  | 1244 | int  v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2); | 
|  | 1245 |  | 
|  | 1246 | dst[0] = SkToU8(y); | 
|  | 1247 | dst[1] = SkToU8(u + 128); | 
|  | 1248 | dst[2] = SkToU8(v + 128); | 
|  | 1249 | } | 
|  | 1250 |  | 
|  | 1251 | /////////////////////////////////////////////////////////////////////////////// | 
|  | 1252 |  | 
|  | 1253 | typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst, | 
|  | 1254 | const void* SK_RESTRICT src, int width, | 
|  | 1255 | const SkPMColor* SK_RESTRICT ctable); | 
|  | 1256 |  | 
|  | 1257 | static void Write_32_YUV(uint8_t* SK_RESTRICT dst, | 
|  | 1258 | const void* SK_RESTRICT srcRow, int width, | 
|  | 1259 | const SkPMColor*) { | 
|  | 1260 | const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow; | 
|  | 1261 | while (--width >= 0) { | 
|  | 1262 | #ifdef WE_CONVERT_TO_YUV | 
|  | 1263 | rgb2yuv_32(dst, *src++); | 
|  | 1264 | #else | 
|  | 1265 | uint32_t c = *src++; | 
|  | 1266 | dst[0] = SkGetPackedR32(c); | 
|  | 1267 | dst[1] = SkGetPackedG32(c); | 
|  | 1268 | dst[2] = SkGetPackedB32(c); | 
|  | 1269 | #endif | 
|  | 1270 | dst += 3; | 
|  | 1271 | } | 
|  | 1272 | } | 
|  | 1273 |  | 
|  | 1274 | static void Write_4444_YUV(uint8_t* SK_RESTRICT dst, | 
|  | 1275 | const void* SK_RESTRICT srcRow, int width, | 
|  | 1276 | const SkPMColor*) { | 
|  | 1277 | const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow; | 
|  | 1278 | while (--width >= 0) { | 
|  | 1279 | #ifdef WE_CONVERT_TO_YUV | 
|  | 1280 | rgb2yuv_4444(dst, *src++); | 
|  | 1281 | #else | 
|  | 1282 | SkPMColor16 c = *src++; | 
|  | 1283 | dst[0] = SkPacked4444ToR32(c); | 
|  | 1284 | dst[1] = SkPacked4444ToG32(c); | 
|  | 1285 | dst[2] = SkPacked4444ToB32(c); | 
|  | 1286 | #endif | 
|  | 1287 | dst += 3; | 
|  | 1288 | } | 
|  | 1289 | } | 
|  | 1290 |  | 
|  | 1291 | static void Write_16_YUV(uint8_t* SK_RESTRICT dst, | 
|  | 1292 | const void* SK_RESTRICT srcRow, int width, | 
|  | 1293 | const SkPMColor*) { | 
|  | 1294 | const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow; | 
|  | 1295 | while (--width >= 0) { | 
|  | 1296 | #ifdef WE_CONVERT_TO_YUV | 
|  | 1297 | rgb2yuv_16(dst, *src++); | 
|  | 1298 | #else | 
|  | 1299 | uint16_t c = *src++; | 
|  | 1300 | dst[0] = SkPacked16ToR32(c); | 
|  | 1301 | dst[1] = SkPacked16ToG32(c); | 
|  | 1302 | dst[2] = SkPacked16ToB32(c); | 
|  | 1303 | #endif | 
|  | 1304 | dst += 3; | 
|  | 1305 | } | 
|  | 1306 | } | 
|  | 1307 |  | 
|  | 1308 | static void Write_Index_YUV(uint8_t* SK_RESTRICT dst, | 
|  | 1309 | const void* SK_RESTRICT srcRow, int width, | 
|  | 1310 | const SkPMColor* SK_RESTRICT ctable) { | 
|  | 1311 | const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow; | 
|  | 1312 | while (--width >= 0) { | 
|  | 1313 | #ifdef WE_CONVERT_TO_YUV | 
|  | 1314 | rgb2yuv_32(dst, ctable[*src++]); | 
|  | 1315 | #else | 
|  | 1316 | uint32_t c = ctable[*src++]; | 
|  | 1317 | dst[0] = SkGetPackedR32(c); | 
|  | 1318 | dst[1] = SkGetPackedG32(c); | 
|  | 1319 | dst[2] = SkGetPackedB32(c); | 
|  | 1320 | #endif | 
|  | 1321 | dst += 3; | 
|  | 1322 | } | 
|  | 1323 | } | 
|  | 1324 |  | 
|  | 1325 | static WriteScanline ChooseWriter(const SkBitmap& bm) { | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 1326 | switch (bm.colorType()) { | 
|  | 1327 | case kN32_SkColorType: | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1328 | return Write_32_YUV; | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 1329 | case kRGB_565_SkColorType: | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1330 | return Write_16_YUV; | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 1331 | case kARGB_4444_SkColorType: | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1332 | return Write_4444_YUV; | 
| reed | 6c22573 | 2014-06-09 19:52:07 -0700 | [diff] [blame] | 1333 | case kIndex_8_SkColorType: | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1334 | return Write_Index_YUV; | 
|  | 1335 | default: | 
|  | 1336 | return NULL; | 
|  | 1337 | } | 
|  | 1338 | } | 
|  | 1339 |  | 
|  | 1340 | class SkJPEGImageEncoder : public SkImageEncoder { | 
|  | 1341 | protected: | 
|  | 1342 | virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) { | 
|  | 1343 | #ifdef TIME_ENCODE | 
| djsollen@google.com | 1139940 | 2013-03-20 17:45:27 +0000 | [diff] [blame] | 1344 | SkAutoTime atm("JPEG Encode"); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1345 | #endif | 
|  | 1346 |  | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1347 | SkAutoLockPixels alp(bm); | 
|  | 1348 | if (NULL == bm.getPixels()) { | 
|  | 1349 | return false; | 
|  | 1350 | } | 
|  | 1351 |  | 
|  | 1352 | jpeg_compress_struct    cinfo; | 
|  | 1353 | skjpeg_error_mgr        sk_err; | 
|  | 1354 | skjpeg_destination_mgr  sk_wstream(stream); | 
|  | 1355 |  | 
|  | 1356 | // allocate these before set call setjmp | 
|  | 1357 | SkAutoMalloc    oneRow; | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1358 |  | 
|  | 1359 | cinfo.err = jpeg_std_error(&sk_err); | 
|  | 1360 | sk_err.error_exit = skjpeg_error_exit; | 
|  | 1361 | if (setjmp(sk_err.fJmpBuf)) { | 
|  | 1362 | return false; | 
|  | 1363 | } | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1364 |  | 
| mtklein@google.com | 8d725b2 | 2013-07-24 16:20:05 +0000 | [diff] [blame] | 1365 | // Keep after setjmp or mark volatile. | 
|  | 1366 | const WriteScanline writer = ChooseWriter(bm); | 
|  | 1367 | if (NULL == writer) { | 
|  | 1368 | return false; | 
|  | 1369 | } | 
|  | 1370 |  | 
|  | 1371 | jpeg_create_compress(&cinfo); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1372 | cinfo.dest = &sk_wstream; | 
|  | 1373 | cinfo.image_width = bm.width(); | 
|  | 1374 | cinfo.image_height = bm.height(); | 
|  | 1375 | cinfo.input_components = 3; | 
|  | 1376 | #ifdef WE_CONVERT_TO_YUV | 
|  | 1377 | cinfo.in_color_space = JCS_YCbCr; | 
|  | 1378 | #else | 
|  | 1379 | cinfo.in_color_space = JCS_RGB; | 
|  | 1380 | #endif | 
|  | 1381 | cinfo.input_gamma = 1; | 
|  | 1382 |  | 
|  | 1383 | jpeg_set_defaults(&cinfo); | 
|  | 1384 | jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); | 
| scroggo@google.com | b7decc5 | 2013-04-17 17:37:56 +0000 | [diff] [blame] | 1385 | #ifdef DCT_IFAST_SUPPORTED | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1386 | cinfo.dct_method = JDCT_IFAST; | 
| scroggo@google.com | b7decc5 | 2013-04-17 17:37:56 +0000 | [diff] [blame] | 1387 | #endif | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1388 |  | 
|  | 1389 | jpeg_start_compress(&cinfo, TRUE); | 
|  | 1390 |  | 
|  | 1391 | const int       width = bm.width(); | 
| robertphillips@google.com | 8570b5c | 2012-03-20 17:40:58 +0000 | [diff] [blame] | 1392 | uint8_t*        oneRowP = (uint8_t*)oneRow.reset(width * 3); | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1393 |  | 
| mtklein | 775b819 | 2014-12-02 09:11:25 -0800 | [diff] [blame^] | 1394 | const SkPMColor* colors = bm.getColorTable() ? bm.getColorTable()->readColors() : NULL; | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1395 | const void*      srcRow = bm.getPixels(); | 
|  | 1396 |  | 
|  | 1397 | while (cinfo.next_scanline < cinfo.image_height) { | 
|  | 1398 | JSAMPROW row_pointer[1];    /* pointer to JSAMPLE row[s] */ | 
|  | 1399 |  | 
|  | 1400 | writer(oneRowP, srcRow, width, colors); | 
|  | 1401 | row_pointer[0] = oneRowP; | 
|  | 1402 | (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); | 
|  | 1403 | srcRow = (const void*)((const char*)srcRow + bm.rowBytes()); | 
|  | 1404 | } | 
|  | 1405 |  | 
|  | 1406 | jpeg_finish_compress(&cinfo); | 
|  | 1407 | jpeg_destroy_compress(&cinfo); | 
|  | 1408 |  | 
|  | 1409 | return true; | 
|  | 1410 | } | 
|  | 1411 | }; | 
|  | 1412 |  | 
|  | 1413 | /////////////////////////////////////////////////////////////////////////////// | 
| robertphillips@google.com | ec51cb8 | 2012-03-23 18:13:47 +0000 | [diff] [blame] | 1414 | DEFINE_DECODER_CREATOR(JPEGImageDecoder); | 
|  | 1415 | DEFINE_ENCODER_CREATOR(JPEGImageEncoder); | 
|  | 1416 | /////////////////////////////////////////////////////////////////////////////// | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1417 |  | 
| scroggo@google.com | b5571b3 | 2013-09-25 21:34:24 +0000 | [diff] [blame] | 1418 | static bool is_jpeg(SkStreamRewindable* stream) { | 
| robertphillips@google.com | ec51cb8 | 2012-03-23 18:13:47 +0000 | [diff] [blame] | 1419 | static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF }; | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1420 | static const size_t HEADER_SIZE = sizeof(gHeader); | 
|  | 1421 |  | 
|  | 1422 | char buffer[HEADER_SIZE]; | 
|  | 1423 | size_t len = stream->read(buffer, HEADER_SIZE); | 
|  | 1424 |  | 
|  | 1425 | if (len != HEADER_SIZE) { | 
| scroggo@google.com | 39edf4c | 2013-04-25 17:33:51 +0000 | [diff] [blame] | 1426 | return false;   // can't read enough | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1427 | } | 
|  | 1428 | if (memcmp(buffer, gHeader, HEADER_SIZE)) { | 
| scroggo@google.com | 39edf4c | 2013-04-25 17:33:51 +0000 | [diff] [blame] | 1429 | return false; | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1430 | } | 
| scroggo@google.com | 39edf4c | 2013-04-25 17:33:51 +0000 | [diff] [blame] | 1431 | return true; | 
|  | 1432 | } | 
|  | 1433 |  | 
| scroggo@google.com | b5571b3 | 2013-09-25 21:34:24 +0000 | [diff] [blame] | 1434 |  | 
|  | 1435 | static SkImageDecoder* sk_libjpeg_dfactory(SkStreamRewindable* stream) { | 
| scroggo@google.com | 39edf4c | 2013-04-25 17:33:51 +0000 | [diff] [blame] | 1436 | if (is_jpeg(stream)) { | 
|  | 1437 | return SkNEW(SkJPEGImageDecoder); | 
|  | 1438 | } | 
|  | 1439 | return NULL; | 
|  | 1440 | } | 
|  | 1441 |  | 
| scroggo@google.com | b5571b3 | 2013-09-25 21:34:24 +0000 | [diff] [blame] | 1442 | static SkImageDecoder::Format get_format_jpeg(SkStreamRewindable* stream) { | 
| scroggo@google.com | 39edf4c | 2013-04-25 17:33:51 +0000 | [diff] [blame] | 1443 | if (is_jpeg(stream)) { | 
|  | 1444 | return SkImageDecoder::kJPEG_Format; | 
|  | 1445 | } | 
|  | 1446 | return SkImageDecoder::kUnknown_Format; | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1447 | } | 
|  | 1448 |  | 
| robertphillips@google.com | 8570b5c | 2012-03-20 17:40:58 +0000 | [diff] [blame] | 1449 | static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) { | 
| tomhudson@google.com | d33b26e | 2012-03-02 16:12:14 +0000 | [diff] [blame] | 1450 | return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL; | 
|  | 1451 | } | 
|  | 1452 |  | 
| mtklein@google.com | bd6343b | 2013-09-04 17:20:18 +0000 | [diff] [blame] | 1453 | static SkImageDecoder_DecodeReg gDReg(sk_libjpeg_dfactory); | 
|  | 1454 | static SkImageDecoder_FormatReg gFormatReg(get_format_jpeg); | 
|  | 1455 | static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory); |