Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(289)

Side by Side Diff: src/images/SkImageDecoder_libjpeg.cpp

Issue 12438025: Upstream changes from Android for decoding jpeg images. (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 1
2 /* 2 /*
3 * Copyright 2007 The Android Open Source Project 3 * Copyright 2007 The Android Open Source Project
4 * 4 *
5 * Use of this source code is governed by a BSD-style license that can be 5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file. 6 * found in the LICENSE file.
7 */ 7 */
8 8
9 9
10 #include "SkImageDecoder.h" 10 #include "SkImageDecoder.h"
11 #include "SkImageEncoder.h" 11 #include "SkImageEncoder.h"
12 #include "SkJpegUtility.h" 12 #include "SkJpegUtility.h"
13 #include "SkColorPriv.h" 13 #include "SkColorPriv.h"
14 #include "SkDither.h" 14 #include "SkDither.h"
15 #include "SkScaledBitmapSampler.h" 15 #include "SkScaledBitmapSampler.h"
16 #include "SkStream.h" 16 #include "SkStream.h"
17 #include "SkTemplates.h" 17 #include "SkTemplates.h"
18 #include "SkTime.h"
18 #include "SkUtils.h" 19 #include "SkUtils.h"
20 #include "SkRect.h"
21 #include "SkCanvas.h"
19 22
20 #include <stdio.h> 23 #include <stdio.h>
21 extern "C" { 24 extern "C" {
22 #include "jpeglib.h" 25 #include "jpeglib.h"
23 #include "jerror.h" 26 #include "jerror.h"
24 } 27 }
25 28
29 #if defined(SK_BUILD_FOR_ANDROID) && defined(SK_DEBUG)
30 #define SK_BUILD_FOR_ANDROID_FRAMEWORK
31 #endif
djsollen 2013/03/18 20:11:31 these lines need to be commented out. I also need
djsollen 2013/03/19 15:38:25 Done.
32
robertphillips 2013/03/19 13:15:21 These enable ... for encoding/decoding
djsollen 2013/03/19 15:38:25 Done.
26 // this enables timing code to report milliseconds for an encode 33 // this enables timing code to report milliseconds for an encode
27 //#define TIME_ENCODE 34 //#define TIME_ENCODE
28 //#define TIME_DECODE 35 //#define TIME_DECODE
29 36
30 // this enables our rgb->yuv code, which is faster than libjpeg on ARM 37 // this enables our rgb->yuv code, which is faster than libjpeg on ARM
31 // disable for the moment, as we have some glitches when width != multiple of 4 38 // disable for the moment, as we have some glitches when width != multiple of 4
32 #define WE_CONVERT_TO_YUV 39 #define WE_CONVERT_TO_YUV
33 40
41 // If ANDROID_RGB is defined by in the jpeg headers it indicates that jpeg offer s
42 // support for two additional formats (1) JCS_RGBA_8888 and (2) JCS_RGB_565.
43
34 ////////////////////////////////////////////////////////////////////////// 44 //////////////////////////////////////////////////////////////////////////
35 ////////////////////////////////////////////////////////////////////////// 45 //////////////////////////////////////////////////////////////////////////
36 46
47 static void overwrite_mem_buffer_size(j_decompress_ptr cinfo) {
48 #ifdef SK_BUILD_FOR_ANDROID
robertphillips 2013/03/19 13:15:21 I think this comment could be improved. Why 30 vs.
djsollen 2013/03/19 15:38:25 Done.
49 /* Check if the memory cap property is set.
50 If so, use the memory size for jpeg decode.
51 */
robertphillips 2013/03/19 13:15:21 left justify the # lines?
djsollen 2013/03/19 15:38:25 Done.
52 #ifdef ANDROID_LARGE_MEMORY_DEVICE
53 cinfo->mem->max_memory_to_use = 30 * 1024 * 1024;
54 #else
55 cinfo->mem->max_memory_to_use = 5 * 1024 * 1024;
56 #endif
57 #endif // SK_BUILD_FOR_ANDROID
58 }
59
60 //////////////////////////////////////////////////////////////////////////
61 //////////////////////////////////////////////////////////////////////////
62
63 class SkJPEGImageIndex {
64 public:
65 SkJPEGImageIndex(SkStream* stream, SkImageDecoder* decoder)
66 : fSrcMgr(stream, decoder, true) {}
67
68 ~SkJPEGImageIndex() {
69 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
70 jpeg_destroy_huffman_index(&fHuffmanIndex);
71 #endif
72 jpeg_finish_decompress(&fCInfo);
73 jpeg_destroy_decompress(&fCInfo);
74 }
75
robertphillips 2013/03/19 13:15:21 This seems like it could use a comment.
djsollen 2013/03/19 15:38:25 Done.
76 void customizeInfo() {
77 overwrite_mem_buffer_size(&fCInfo);
78 fCInfo.src = &fSrcMgr;
79 }
80
81 jpeg_decompress_struct* cinfo() { return &fCInfo; }
82
83 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
84 huffman_index* huffmanIndex() { return &fHuffmanIndex; }
85 #endif
86
87 private:
88 skjpeg_source_mgr fSrcMgr;
89 jpeg_decompress_struct fCInfo;
90 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
91 huffman_index fHuffmanIndex;
92 #endif
93 };
94
37 class SkJPEGImageDecoder : public SkImageDecoder { 95 class SkJPEGImageDecoder : public SkImageDecoder {
38 public: 96 public:
97 SkJPEGImageDecoder() {
98 fImageIndex = NULL;
99 fImageWidth = 0;
100 fImageHeight = 0;
101 }
robertphillips 2013/03/19 13:15:21 virtual
djsollen 2013/03/19 15:38:25 Done.
102 ~SkJPEGImageDecoder() {
robertphillips 2013/03/19 13:15:21 remove "if (fImageIndex)"
djsollen 2013/03/19 15:38:25 Done.
103 if (fImageIndex)
104 delete fImageIndex;
105 }
39 virtual Format getFormat() const { 106 virtual Format getFormat() const {
40 return kJPEG_Format; 107 return kJPEG_Format;
41 } 108 }
42 109
43 protected: 110 protected:
44 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode); 111 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
112 virtual bool onBuildTileIndex(SkStream *stream, int *width, int *height) SK_ OVERRIDE;
113 virtual bool onDecodeRegion(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRI DE;
114 #endif
115 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
116
117 private:
118 SkJPEGImageIndex* fImageIndex;
119 int fImageWidth;
120 int fImageHeight;
robertphillips 2013/03/19 13:15:21 INHERITED
djsollen 2013/03/19 15:38:25 Done.
45 }; 121 };
46 122
47 ////////////////////////////////////////////////////////////////////////// 123 //////////////////////////////////////////////////////////////////////////
48 124
49 #include "SkTime.h"
50
51 class AutoTimeMillis {
52 public:
53 AutoTimeMillis(const char label[]) : fLabel(label) {
54 if (!fLabel) {
55 fLabel = "";
56 }
57 fNow = SkTime::GetMSecs();
58 }
59 ~AutoTimeMillis() {
60 SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow);
61 }
62 private:
63 const char* fLabel;
64 SkMSec fNow;
65 };
66
67 /* Automatically clean up after throwing an exception */ 125 /* Automatically clean up after throwing an exception */
68 class JPEGAutoClean { 126 class JPEGAutoClean {
69 public: 127 public:
70 JPEGAutoClean(): cinfo_ptr(NULL) {} 128 JPEGAutoClean(): cinfo_ptr(NULL) {}
71 ~JPEGAutoClean() { 129 ~JPEGAutoClean() {
72 if (cinfo_ptr) { 130 if (cinfo_ptr) {
73 jpeg_destroy_decompress(cinfo_ptr); 131 jpeg_destroy_decompress(cinfo_ptr);
74 } 132 }
75 } 133 }
76 void set(jpeg_decompress_struct* info) { 134 void set(jpeg_decompress_struct* info) {
77 cinfo_ptr = info; 135 cinfo_ptr = info;
78 } 136 }
79 private: 137 private:
80 jpeg_decompress_struct* cinfo_ptr; 138 jpeg_decompress_struct* cinfo_ptr;
81 }; 139 };
82 140
83 #ifdef SK_BUILD_FOR_ANDROID
84
85 /* For non-ndk builds we could look at the system's jpeg memory cap and use it
86 * if it is set. However, for now we will use the NDK compliant hardcoded values
87 */
88 //#include <cutils/properties.h>
89 //static const char KEY_MEM_CAP[] = "ro.media.dec.jpeg.memcap";
90
91 static void overwrite_mem_buffer_size(j_decompress_ptr cinfo) {
92 #ifdef ANDROID_LARGE_MEMORY_DEVICE
93 cinfo->mem->max_memory_to_use = 30 * 1024 * 1024;
94 #else
95 cinfo->mem->max_memory_to_use = 5 * 1024 * 1024;
96 #endif
97 }
98 #endif
99
100
101 /////////////////////////////////////////////////////////////////////////////// 141 ///////////////////////////////////////////////////////////////////////////////
102 142
103 /* If we need to better match the request, we might examine the image and 143 /* If we need to better match the request, we might examine the image and
104 output dimensions, and determine if the downsampling jpeg provided is 144 output dimensions, and determine if the downsampling jpeg provided is
105 not sufficient. If so, we can recompute a modified sampleSize value to 145 not sufficient. If so, we can recompute a modified sampleSize value to
106 make up the difference. 146 make up the difference.
107 147
108 To skip this additional scaling, just set sampleSize = 1; below. 148 To skip this additional scaling, just set sampleSize = 1; below.
109 */ 149 */
110 static int recompute_sampleSize(int sampleSize, 150 static int recompute_sampleSize(int sampleSize,
111 const jpeg_decompress_struct& cinfo) { 151 const jpeg_decompress_struct& cinfo) {
112 return sampleSize * cinfo.output_width / cinfo.image_width; 152 return sampleSize * cinfo.output_width / cinfo.image_width;
113 } 153 }
114 154
115 static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) { 155 static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) {
116 /* These are initialized to 0, so if they have non-zero values, we assume 156 /* These are initialized to 0, so if they have non-zero values, we assume
117 they are "valid" (i.e. have been computed by libjpeg) 157 they are "valid" (i.e. have been computed by libjpeg)
118 */ 158 */
119 return cinfo.output_width != 0 && cinfo.output_height != 0; 159 return 0 != cinfo.output_width && 0 != cinfo.output_height;
120 } 160 }
121 161
122 static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, 162 static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, int count ) {
123 int count) {
124 for (int i = 0; i < count; i++) { 163 for (int i = 0; i < count; i++) {
125 JSAMPLE* rowptr = (JSAMPLE*)buffer; 164 JSAMPLE* rowptr = (JSAMPLE*)buffer;
126 int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1); 165 int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
166 if (1 != row_count) {
167 return false;
168 }
169 }
170 return true;
171 }
172
173 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
174 static bool skip_src_rows_tile(jpeg_decompress_struct* cinfo,
175 huffman_index *index, void* buffer, int count) {
176 for (int i = 0; i < count; i++) {
177 JSAMPLE* rowptr = (JSAMPLE*)buffer;
178 int row_count = jpeg_read_tile_scanline(cinfo, index, &rowptr);
robertphillips 2013/03/19 13:15:21 1 !=
djsollen 2013/03/19 15:38:25 Done.
127 if (row_count != 1) { 179 if (row_count != 1) {
128 return false; 180 return false;
129 } 181 }
130 } 182 }
131 return true; 183 return true;
132 } 184 }
185 #endif
133 186
134 // This guy exists just to aid in debugging, as it allows debuggers to just 187 // This guy exists just to aid in debugging, as it allows debuggers to just
135 // set a break-point in one place to see all error exists. 188 // set a break-point in one place to see all error exists.
136 static bool return_false(const jpeg_decompress_struct& cinfo, 189 static bool return_false(const jpeg_decompress_struct& cinfo,
137 const SkBitmap& bm, const char msg[]) { 190 const SkBitmap& bm, const char msg[]) {
138 #if 0 191 #ifdef SK_DEBUG
139 SkDebugf("libjpeg error %d <%s> from %s [%d %d]", cinfo.err->msg_code, 192 SkDebugf("libjpeg error %d <%s> from %s [%d %d]", cinfo.err->msg_code,
140 cinfo.err->jpeg_message_table[cinfo.err->msg_code], msg, 193 cinfo.err->jpeg_message_table[cinfo.err->msg_code], msg,
141 bm.width(), bm.height()); 194 bm.width(), bm.height());
142 #endif 195 #endif
143 return false; // must always return false 196 return false; // must always return false
144 } 197 }
145 198
146 // Convert a scanline of CMYK samples to RGBX in place. Note that this 199 // Convert a scanline of CMYK samples to RGBX in place. Note that this
147 // method moves the "scanline" pointer in its processing 200 // method moves the "scanline" pointer in its processing
148 static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) { 201 static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
(...skipping 12 matching lines...) Expand all
161 for (unsigned int x = 0; x < width; ++x, scanline += 4) { 214 for (unsigned int x = 0; x < width; ++x, scanline += 4) {
162 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]); 215 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]);
163 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]); 216 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]);
164 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]); 217 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]);
165 scanline[3] = 255; 218 scanline[3] = 255;
166 } 219 }
167 } 220 }
168 221
169 bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { 222 bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
170 #ifdef TIME_DECODE 223 #ifdef TIME_DECODE
171 AutoTimeMillis atm("JPEG Decode"); 224 SkAutoTime atm("JPEG Decode");
172 #endif 225 #endif
173 226
174 SkAutoMalloc srcStorage;
175 JPEGAutoClean autoClean; 227 JPEGAutoClean autoClean;
176 228
177 jpeg_decompress_struct cinfo; 229 jpeg_decompress_struct cinfo;
178 skjpeg_error_mgr sk_err; 230 skjpeg_error_mgr errorManager;
179 skjpeg_source_mgr sk_stream(stream, this, false); 231 skjpeg_source_mgr srcManager(stream, this, false);
180 232
181 cinfo.err = jpeg_std_error(&sk_err); 233 cinfo.err = jpeg_std_error(&errorManager);
182 sk_err.error_exit = skjpeg_error_exit; 234 errorManager.error_exit = skjpeg_error_exit;
183 235
184 // All objects need to be instantiated before this setjmp call so that 236 // All objects need to be instantiated before this setjmp call so that
185 // they will be cleaned up properly if an error occurs. 237 // they will be cleaned up properly if an error occurs.
186 if (setjmp(sk_err.fJmpBuf)) { 238 if (setjmp(errorManager.fJmpBuf)) {
187 return return_false(cinfo, *bm, "setjmp"); 239 return return_false(cinfo, *bm, "setjmp");
188 } 240 }
189 241
190 jpeg_create_decompress(&cinfo); 242 jpeg_create_decompress(&cinfo);
191 autoClean.set(&cinfo); 243 autoClean.set(&cinfo);
192 244
193 #ifdef SK_BUILD_FOR_ANDROID
194 overwrite_mem_buffer_size(&cinfo); 245 overwrite_mem_buffer_size(&cinfo);
195 #endif
196 246
197 //jpeg_stdio_src(&cinfo, file); 247 //jpeg_stdio_src(&cinfo, file);
198 cinfo.src = &sk_stream; 248 cinfo.src = &srcManager;
199 249
200 int status = jpeg_read_header(&cinfo, true); 250 int status = jpeg_read_header(&cinfo, true);
201 if (status != JPEG_HEADER_OK) { 251 if (status != JPEG_HEADER_OK) {
202 return return_false(cinfo, *bm, "read_header"); 252 return return_false(cinfo, *bm, "read_header");
203 } 253 }
204 254
205 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it 255 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
206 can) much faster that we, just use their num/denom api to approximate 256 can) much faster that we, just use their num/denom api to approximate
207 the size. 257 the size.
208 */ 258 */
209 int sampleSize = this->getSampleSize(); 259 int sampleSize = this->getSampleSize();
210 260
211 cinfo.dct_method = JDCT_IFAST; 261 if (this->getPreferQualityOverSpeed()) {
262 cinfo.dct_method = JDCT_ISLOW;
263 } else {
264 cinfo.dct_method = JDCT_IFAST;
265 }
266
212 cinfo.scale_num = 1; 267 cinfo.scale_num = 1;
213 cinfo.scale_denom = sampleSize; 268 cinfo.scale_denom = sampleSize;
214 269
215 /* this gives about 30% performance improvement. In theory it may 270 /* this gives about 30% performance improvement. In theory it may
216 reduce the visual quality, in practice I'm not seeing a difference 271 reduce the visual quality, in practice I'm not seeing a difference
217 */ 272 */
218 cinfo.do_fancy_upsampling = 0; 273 cinfo.do_fancy_upsampling = 0;
219 274
220 /* this gives another few percents */ 275 /* this gives another few percents */
221 cinfo.do_block_smoothing = 0; 276 cinfo.do_block_smoothing = 0;
(...skipping 21 matching lines...) Expand all
243 if (SkBitmap::kARGB_8888_Config == config && JCS_CMYK != cinfo.out_color_spa ce) { 298 if (SkBitmap::kARGB_8888_Config == config && JCS_CMYK != cinfo.out_color_spa ce) {
244 cinfo.out_color_space = JCS_RGBA_8888; 299 cinfo.out_color_space = JCS_RGBA_8888;
245 } else if (SkBitmap::kRGB_565_Config == config && JCS_CMYK != cinfo.out_colo r_space) { 300 } else if (SkBitmap::kRGB_565_Config == config && JCS_CMYK != cinfo.out_colo r_space) {
246 cinfo.out_color_space = JCS_RGB_565; 301 cinfo.out_color_space = JCS_RGB_565;
247 if (this->getDitherImage()) { 302 if (this->getDitherImage()) {
248 cinfo.dither_mode = JDITHER_ORDERED; 303 cinfo.dither_mode = JDITHER_ORDERED;
249 } 304 }
250 } 305 }
251 #endif 306 #endif
252 307
253 if (sampleSize == 1 && mode == SkImageDecoder::kDecodeBounds_Mode) { 308 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
254 bm->setConfig(config, cinfo.image_width, cinfo.image_height); 309 bm->setConfig(config, cinfo.image_width, cinfo.image_height);
255 bm->setIsOpaque(true); 310 bm->setIsOpaque(true);
256 return true; 311 return true;
257 } 312 }
258 313
259 /* image_width and image_height are the original dimensions, available 314 /* image_width and image_height are the original dimensions, available
260 after jpeg_read_header(). To see the scaled dimensions, we have to call 315 after jpeg_read_header(). To see the scaled dimensions, we have to call
261 jpeg_start_decompress(), and then read output_width and output_height. 316 jpeg_start_decompress(), and then read output_width and output_height.
262 */ 317 */
263 if (!jpeg_start_decompress(&cinfo)) { 318 if (!jpeg_start_decompress(&cinfo)) {
264 /* If we failed here, we may still have enough information to return 319 /* If we failed here, we may still have enough information to return
265 to the caller if they just wanted (subsampled bounds). If sampleSize 320 to the caller if they just wanted (subsampled bounds). If sampleSize
266 was 1, then we would have already returned. Thus we just check if 321 was 1, then we would have already returned. Thus we just check if
267 we're in kDecodeBounds_Mode, and that we have valid output sizes. 322 we're in kDecodeBounds_Mode, and that we have valid output sizes.
268 323
269 One reason to fail here is that we have insufficient stream data 324 One reason to fail here is that we have insufficient stream data
270 to complete the setup. However, output dimensions seem to get 325 to complete the setup. However, output dimensions seem to get
271 computed very early, which is why this special check can pay off. 326 computed very early, which is why this special check can pay off.
272 */ 327 */
273 if (SkImageDecoder::kDecodeBounds_Mode == mode && 328 if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimension s(cinfo)) {
274 valid_output_dimensions(cinfo)) {
275 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height, 329 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
276 recompute_sampleSize(sampleSize, cinfo)); 330 recompute_sampleSize(sampleSize, cinfo));
277 bm->setConfig(config, smpl.scaledWidth(), smpl.scaledHeight()); 331 bm->setConfig(config, smpl.scaledWidth(), smpl.scaledHeight());
278 bm->setIsOpaque(true); 332 bm->setIsOpaque(true);
279 return true; 333 return true;
280 } else { 334 } else {
281 return return_false(cinfo, *bm, "start_decompress"); 335 return return_false(cinfo, *bm, "start_decompress");
282 } 336 }
283 } 337 }
284 sampleSize = recompute_sampleSize(sampleSize, cinfo); 338 sampleSize = recompute_sampleSize(sampleSize, cinfo);
285 339
286 // should we allow the Chooser (if present) to pick a config for us??? 340 // should we allow the Chooser (if present) to pick a config for us???
287 if (!this->chooseFromOneChoice(config, cinfo.output_width, 341 if (!this->chooseFromOneChoice(config, cinfo.output_width, cinfo.output_heig ht)) {
288 cinfo.output_height)) {
289 return return_false(cinfo, *bm, "chooseFromOneChoice"); 342 return return_false(cinfo, *bm, "chooseFromOneChoice");
290 } 343 }
291 344
345 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampl eSize);
346
347 bm->lockPixels();
348 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
349 bm->unlockPixels();
robertphillips 2013/03/19 13:15:21 const?
djsollen 2013/03/19 15:38:25 Done.
350 const bool reuseBitmap = (rowptr != NULL);
351 if (reuseBitmap && (sampler.scaledWidth() != bm->width() ||
352 sampler.scaledHeight() != bm->height())) {
353 // Dimensions must match
354 return false;
355 }
356
357 if (!reuseBitmap) {
358 bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
359 bm->setIsOpaque(true);
djsollen 2013/03/18 20:11:31 the above two lines need moved to line 346
robertphillips 2013/03/19 13:15:21 What if the bitmap is being reused?
djsollen 2013/03/19 15:38:25 Your right it needs to stay here. I updated the i
360 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
361 return true;
362 }
363 if (!this->allocPixelRef(bm, NULL)) {
364 return return_false(cinfo, *bm, "allocPixelRef");
365 }
366 } else if (SkImageDecoder::kDecodeBounds_Mode == mode) {
367 return true;
368 }
369
370 SkAutoLockPixels alp(*bm);
371
292 #ifdef ANDROID_RGB 372 #ifdef ANDROID_RGB
293 /* short-circuit the SkScaledBitmapSampler when possible, as this gives 373 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
294 a significant performance boost. 374 a significant performance boost.
295 */ 375 */
296 if (sampleSize == 1 && 376 if (sampleSize == 1 &&
297 ((config == SkBitmap::kARGB_8888_Config && 377 ((config == SkBitmap::kARGB_8888_Config &&
298 cinfo.out_color_space == JCS_RGBA_8888) || 378 cinfo.out_color_space == JCS_RGBA_8888) ||
299 (config == SkBitmap::kRGB_565_Config && 379 (config == SkBitmap::kRGB_565_Config &&
300 cinfo.out_color_space == JCS_RGB_565))) 380 cinfo.out_color_space == JCS_RGB_565)))
301 { 381 {
302 bm->setConfig(config, cinfo.output_width, cinfo.output_height); 382 rowptr = (JSAMPLE*)bm->getPixels();
303 bm->setIsOpaque(true);
304 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
305 return true;
306 }
307 if (!this->allocPixelRef(bm, NULL)) {
308 return return_false(cinfo, *bm, "allocPixelRef");
309 }
310 SkAutoLockPixels alp(*bm);
311 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
312 INT32 const bpr = bm->rowBytes(); 383 INT32 const bpr = bm->rowBytes();
313 384
314 while (cinfo.output_scanline < cinfo.output_height) { 385 while (cinfo.output_scanline < cinfo.output_height) {
djsollen 2013/03/18 20:11:31 remove space
djsollen 2013/03/19 15:38:25 Done.
315 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1); 386 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
316 // if row_count == 0, then we didn't get a scanline, so abort. 387 // if row_count == 0, then we didn't get a scanline, so abort.
317 // if we supported partial images, we might return true in this case 388 // if we supported partial images, we might return true in this case
318 if (0 == row_count) { 389 if (0 == row_count) {
319 return return_false(cinfo, *bm, "read_scanlines"); 390 return return_false(cinfo, *bm, "read_scanlines");
320 } 391 }
321 if (this->shouldCancelDecode()) { 392 if (this->shouldCancelDecode()) {
322 return return_false(cinfo, *bm, "shouldCancelDecode"); 393 return return_false(cinfo, *bm, "shouldCancelDecode");
323 } 394 }
324 rowptr += bpr; 395 rowptr += bpr;
325 } 396 }
397 if (reuseBitmap) {
398 bm->notifyPixelsChanged();
399 }
326 jpeg_finish_decompress(&cinfo); 400 jpeg_finish_decompress(&cinfo);
327 return true; 401 return true;
328 } 402 }
329 #endif 403 #endif
330 404
331 // check for supported formats 405 // check for supported formats
332 SkScaledBitmapSampler::SrcConfig sc; 406 SkScaledBitmapSampler::SrcConfig sc;
333 if (JCS_CMYK == cinfo.out_color_space) { 407 if (JCS_CMYK == cinfo.out_color_space) {
334 // In this case we will manually convert the CMYK values to RGB 408 // In this case we will manually convert the CMYK values to RGB
335 sc = SkScaledBitmapSampler::kRGBX; 409 sc = SkScaledBitmapSampler::kRGBX;
336 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_spa ce) { 410 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_spa ce) {
337 sc = SkScaledBitmapSampler::kRGB; 411 sc = SkScaledBitmapSampler::kRGB;
338 #ifdef ANDROID_RGB 412 #ifdef ANDROID_RGB
339 } else if (JCS_RGBA_8888 == cinfo.out_color_space) { 413 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
340 sc = SkScaledBitmapSampler::kRGBX; 414 sc = SkScaledBitmapSampler::kRGBX;
341 } else if (JCS_RGB_565 == cinfo.out_color_space) { 415 } else if (JCS_RGB_565 == cinfo.out_color_space) {
342 sc = SkScaledBitmapSampler::kRGB_565; 416 sc = SkScaledBitmapSampler::kRGB_565;
343 #endif 417 #endif
344 } else if (1 == cinfo.out_color_components && 418 } else if (1 == cinfo.out_color_components &&
345 JCS_GRAYSCALE == cinfo.out_color_space) { 419 JCS_GRAYSCALE == cinfo.out_color_space) {
346 sc = SkScaledBitmapSampler::kGray; 420 sc = SkScaledBitmapSampler::kGray;
347 } else { 421 } else {
348 return return_false(cinfo, *bm, "jpeg colorspace"); 422 return return_false(cinfo, *bm, "jpeg colorspace");
349 } 423 }
350 424
351 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height,
352 sampleSize);
353
354 bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
355 // jpegs are always opaque (i.e. have no per-pixel alpha)
356 bm->setIsOpaque(true);
357
358 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
359 return true;
360 }
361 if (!this->allocPixelRef(bm, NULL)) {
362 return return_false(cinfo, *bm, "allocPixelRef");
363 }
364
365 SkAutoLockPixels alp(*bm);
366 if (!sampler.begin(bm, sc, this->getDitherImage())) { 425 if (!sampler.begin(bm, sc, this->getDitherImage())) {
367 return return_false(cinfo, *bm, "sampler.begin"); 426 return return_false(cinfo, *bm, "sampler.begin");
368 } 427 }
369 428
370 // The CMYK work-around relies on 4 components per pixel here 429 // The CMYK work-around relies on 4 components per pixel here
371 uint8_t* srcRow = (uint8_t*)srcStorage.reset(cinfo.output_width * 4); 430 SkAutoMalloc srcStorage(cinfo.output_width * 4);
431 uint8_t* srcRow = (uint8_t*)srcStorage.get();
372 432
373 // Possibly skip initial rows [sampler.srcY0] 433 // Possibly skip initial rows [sampler.srcY0]
374 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) { 434 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
375 return return_false(cinfo, *bm, "skip rows"); 435 return return_false(cinfo, *bm, "skip rows");
376 } 436 }
377 437
378 // now loop through scanlines until y == bm->height() - 1 438 // now loop through scanlines until y == bm->height() - 1
379 for (int y = 0;; y++) { 439 for (int y = 0;; y++) {
380 JSAMPLE* rowptr = (JSAMPLE*)srcRow; 440 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
381 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1); 441 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
(...skipping 17 matching lines...) Expand all
399 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) { 459 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
400 return return_false(cinfo, *bm, "skip rows"); 460 return return_false(cinfo, *bm, "skip rows");
401 } 461 }
402 } 462 }
403 463
404 // we formally skip the rest, so we don't get a complaint from libjpeg 464 // we formally skip the rest, so we don't get a complaint from libjpeg
405 if (!skip_src_rows(&cinfo, srcRow, 465 if (!skip_src_rows(&cinfo, srcRow,
406 cinfo.output_height - cinfo.output_scanline)) { 466 cinfo.output_height - cinfo.output_scanline)) {
407 return return_false(cinfo, *bm, "skip rows"); 467 return return_false(cinfo, *bm, "skip rows");
408 } 468 }
469 if (reuseBitmap) {
470 bm->notifyPixelsChanged();
471 }
409 jpeg_finish_decompress(&cinfo); 472 jpeg_finish_decompress(&cinfo);
410 473
411 // SkDebugf("------------------- bm2 size %d [%d %d] %d\n", bm->getSize(), bm ->width(), bm->height(), bm->config());
412 return true; 474 return true;
413 } 475 }
414 476
477 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
478 bool SkJPEGImageDecoder::onBuildTileIndex(SkStream* stream, int *width, int *hei ght) {
479
480 SkJPEGImageIndex* imageIndex = SkNEW_ARGS(SkJPEGImageIndex, (stream, this));
481 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
482 huffman_index* huffmanIndex = imageIndex->huffmanIndex();
483
484 skjpeg_error_mgr sk_err;
485 cinfo->err = jpeg_std_error(&sk_err);
486 sk_err.error_exit = skjpeg_error_exit;
487
488 // All objects need to be instantiated before this setjmp call so that
489 // they will be cleaned up properly if an error occurs.
490 if (setjmp(sk_err.fJmpBuf)) {
491 return false;
492 }
493
494 // create the cinfo used to create/build the huffmanIndex
495 jpeg_create_decompress(cinfo);
496 imageIndex->customizeInfo();
497 cinfo->do_fancy_upsampling = 0;
498 cinfo->do_block_smoothing = 0;
499
500 int status = jpeg_read_header(cinfo, true);
robertphillips 2013/03/19 13:15:21 constant on LHS
djsollen 2013/03/19 15:38:25 Done.
501 if (status != JPEG_HEADER_OK) {
502 SkDELETE(imageIndex);
503 return false;
504 }
505
506 jpeg_create_huffman_index(cinfo, huffmanIndex);
507 cinfo->scale_num = 1;
508 cinfo->scale_denom = 1;
509 if (!jpeg_build_huffman_index(cinfo, huffmanIndex)) {
510 SkDELETE(imageIndex);
511 return false;
512 }
513
514 // destroy the cinfo used to create/build the huffman index
515 jpeg_destroy_decompress(cinfo);
516
517 // Init decoder to image decode mode
518 jpeg_create_decompress(cinfo);
519 imageIndex->customizeInfo();
520
521 status = jpeg_read_header(cinfo, true);
robertphillips 2013/03/19 13:15:21 const on LHS
djsollen 2013/03/19 15:38:25 Done.
522 if (status != JPEG_HEADER_OK) {
523 SkDELETE(imageIndex);
524 return false;
525 }
526
527 cinfo->out_color_space = JCS_RGBA_8888;
528 cinfo->do_fancy_upsampling = 0;
529 cinfo->do_block_smoothing = 0;
530
531 // instead of jpeg_start_decompress() we start a tiled decompress
532 jpeg_start_tile_decompress(cinfo);
533
534 cinfo->scale_num = 1;
535 *height = cinfo->output_height;
536 *width = cinfo->output_width;
537 fImageWidth = *width;
538 fImageHeight = *height;
539
robertphillips 2013/03/19 13:15:21 remove "if (fImageIndex) {" and matching "}".
djsollen 2013/03/19 15:38:25 Done.
540 if (fImageIndex) {
541 SkDELETE(fImageIndex);
542 }
543 fImageIndex = imageIndex;
544
545 return true;
546 }
547
548 bool SkJPEGImageDecoder::onDecodeRegion(SkBitmap* bm, const SkIRect& region) {
robertphillips 2013/03/19 13:15:21 NULL on LHS
djsollen 2013/03/19 15:38:25 Done.
549 if (fImageIndex == NULL) {
550 return false;
551 }
552 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
553
554 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
555 if (!rect.intersect(region)) {
556 // If the requested region is entirely outside the image return false
557 return false;
558 }
559
560
561 skjpeg_error_mgr errorManager;
562 cinfo->err = jpeg_std_error(&errorManager);
563 errorManager.error_exit = skjpeg_error_exit;
564 if (setjmp(errorManager.fJmpBuf)) {
565 return false;
566 }
567
568 int requestedSampleSize = this->getSampleSize();
569 cinfo->scale_denom = requestedSampleSize;
570
571 if (this->getPreferQualityOverSpeed()) {
572 cinfo->dct_method = JDCT_ISLOW;
573 } else {
574 cinfo->dct_method = JDCT_IFAST;
575 }
576
577 SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false);
578 if (config != SkBitmap::kARGB_8888_Config &&
579 config != SkBitmap::kARGB_4444_Config &&
580 config != SkBitmap::kRGB_565_Config) {
581 config = SkBitmap::kARGB_8888_Config;
582 }
583
584 /* default format is RGB */
585 cinfo->out_color_space = JCS_RGB;
586
587 #ifdef ANDROID_RGB
588 cinfo->dither_mode = JDITHER_NONE;
589 if (SkBitmap::kARGB_8888_Config == config) {
590 cinfo->out_color_space = JCS_RGBA_8888;
591 } else if (SkBitmap::kRGB_565_Config == config) {
592 cinfo->out_color_space = JCS_RGB_565;
593 if (this->getDitherImage()) {
594 cinfo->dither_mode = JDITHER_ORDERED;
595 }
596 }
597 #endif
598
599 int startX = rect.fLeft;
600 int startY = rect.fTop;
601 int width = rect.width();
602 int height = rect.height();
603
604 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
605 &startX, &startY, &width, &height);
606 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
607 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_siz e);
608
609 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
610
611 SkBitmap bitmap;
612 bitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
613 bitmap.setIsOpaque(true);
614
615 // Check ahead of time if the swap(dest, src) is possible or not.
616 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
617 // swap happening. If no, then we will use alloc to allocate pixels to
618 // prevent garbage collection.
619 int w = rect.width() / actualSampleSize;
620 int h = rect.height() / actualSampleSize;
621 bool swapOnly = (rect == region) && bm->isNull() &&
622 (w == bitmap.width()) && (h == bitmap.height()) &&
623 ((startX - rect.x()) / actualSampleSize == 0) &&
624 ((startY - rect.y()) / actualSampleSize == 0);
625 if (swapOnly) {
626 if (!this->allocPixelRef(&bitmap, NULL)) {
627 return return_false(*cinfo, bitmap, "allocPixelRef");
628 }
629 } else {
630 if (!bitmap.allocPixels()) {
631 return return_false(*cinfo, bitmap, "allocPixels");
632 }
633 }
634
635 SkAutoLockPixels alp(bitmap);
636
637 #ifdef ANDROID_RGB
638 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
639 a significant performance boost.
640 */
641 if (skiaSampleSize == 1 &&
642 ((config == SkBitmap::kARGB_8888_Config &&
643 cinfo->out_color_space == JCS_RGBA_8888) ||
644 (config == SkBitmap::kRGB_565_Config &&
645 cinfo->out_color_space == JCS_RGB_565)))
646 {
647 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
648 INT32 const bpr = bitmap.rowBytes();
649 int rowTotalCount = 0;
650
651 while (rowTotalCount < height) {
652 int rowCount = jpeg_read_tile_scanline(cinfo,
653 fImageIndex->huffmanIndex(),
654 &rowptr);
655 // if row_count == 0, then we didn't get a scanline, so abort.
656 // if we supported partial images, we might return true in this case
657 if (0 == rowCount) {
658 return return_false(*cinfo, bitmap, "read_scanlines");
659 }
660 if (this->shouldCancelDecode()) {
661 return return_false(*cinfo, bitmap, "shouldCancelDecode");
662 }
663 rowTotalCount += rowCount;
664 rowptr += bpr;
665 }
666
667 if (swapOnly) {
668 bm->swap(bitmap);
669 } else {
670 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
671 region.width(), region.height(), startX, startY);
672 }
673 return true;
674 }
675 #endif
676
677 // check for supported formats
678 SkScaledBitmapSampler::SrcConfig sc;
679 if (JCS_CMYK == cinfo->out_color_space) {
680 // In this case we will manually convert the CMYK values to RGB
681 sc = SkScaledBitmapSampler::kRGBX;
682 } else if (3 == cinfo->out_color_components && JCS_RGB == cinfo->out_color_s pace) {
683 sc = SkScaledBitmapSampler::kRGB;
684 #ifdef ANDROID_RGB
685 } else if (JCS_RGBA_8888 == cinfo->out_color_space) {
686 sc = SkScaledBitmapSampler::kRGBX;
687 } else if (JCS_RGB_565 == cinfo->out_color_space) {
688 sc = SkScaledBitmapSampler::kRGB_565;
689 #endif
690 } else if (1 == cinfo->out_color_components &&
691 JCS_GRAYSCALE == cinfo->out_color_space) {
692 sc = SkScaledBitmapSampler::kGray;
693 } else {
694 return return_false(*cinfo, *bm, "jpeg colorspace");
695 }
696
697 if (!sampler.begin(&bitmap, sc, this->getDitherImage())) {
698 return return_false(*cinfo, bitmap, "sampler.begin");
699 }
700
701 // The CMYK work-around relies on 4 components per pixel here
702 SkAutoMalloc srcStorage(width * 4);
703 uint8_t* srcRow = (uint8_t*)srcStorage.get();
704
705 // Possibly skip initial rows [sampler.srcY0]
706 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler. srcY0())) {
707 return return_false(*cinfo, bitmap, "skip rows");
708 }
709
710 // now loop through scanlines until y == bitmap->height() - 1
711 for (int y = 0;; y++) {
712 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
713 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex (), &rowptr);
714 if (0 == row_count) {
715 return return_false(*cinfo, bitmap, "read_scanlines");
716 }
717 if (this->shouldCancelDecode()) {
718 return return_false(*cinfo, bitmap, "shouldCancelDecode");
719 }
720
721 if (JCS_CMYK == cinfo->out_color_space) {
722 convert_CMYK_to_RGB(srcRow, width);
723 }
724
725 sampler.next(srcRow);
726 if (bitmap.height() - 1 == y) {
727 // we're done
728 break;
729 }
730
731 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
732 sampler.srcDY() - 1)) {
733 return return_false(*cinfo, bitmap, "skip rows");
734 }
735 }
736 if (swapOnly) {
737 bm->swap(bitmap);
738 } else {
739 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
740 region.width(), region.height(), startX, startY);
741 }
742 return true;
743 }
744 #endif
745
415 /////////////////////////////////////////////////////////////////////////////// 746 ///////////////////////////////////////////////////////////////////////////////
416 747
417 #include "SkColorPriv.h" 748 #include "SkColorPriv.h"
418 749
419 // taken from jcolor.c in libjpeg 750 // taken from jcolor.c in libjpeg
420 #if 0 // 16bit - precise but slow 751 #if 0 // 16bit - precise but slow
421 #define CYR 19595 // 0.299 752 #define CYR 19595 // 0.299
422 #define CYG 38470 // 0.587 753 #define CYG 38470 // 0.587
423 #define CYB 7471 // 0.114 754 #define CYB 7471 // 0.114
424 755
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after
575 return Write_Index_YUV; 906 return Write_Index_YUV;
576 default: 907 default:
577 return NULL; 908 return NULL;
578 } 909 }
579 } 910 }
580 911
581 class SkJPEGImageEncoder : public SkImageEncoder { 912 class SkJPEGImageEncoder : public SkImageEncoder {
582 protected: 913 protected:
583 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) { 914 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
584 #ifdef TIME_ENCODE 915 #ifdef TIME_ENCODE
585 AutoTimeMillis atm("JPEG Encode"); 916 SkAutoTime atm("JPEG Encode");
586 #endif 917 #endif
587 918
588 const WriteScanline writer = ChooseWriter(bm); 919 const WriteScanline writer = ChooseWriter(bm);
589 if (NULL == writer) { 920 if (NULL == writer) {
590 return false; 921 return false;
591 } 922 }
592 923
593 SkAutoLockPixels alp(bm); 924 SkAutoLockPixels alp(bm);
594 if (NULL == bm.getPixels()) { 925 if (NULL == bm.getPixels()) {
595 return false; 926 return false;
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
672 return SkNEW(SkJPEGImageDecoder); 1003 return SkNEW(SkJPEGImageDecoder);
673 } 1004 }
674 1005
675 static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) { 1006 static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
676 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL; 1007 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
677 } 1008 }
678 1009
679 1010
680 static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libjpeg_dfactory); 1011 static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libjpeg_dfactory);
681 static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libjpeg_efact ory); 1012 static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libjpeg_efact ory);
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698