OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright 2007 The Android Open Source Project | 2 * Copyright 2007 The Android Open Source Project |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 | 8 |
9 #include "SkImageDecoder.h" | 9 #include "SkImageDecoder.h" |
10 #include "SkImageEncoder.h" | 10 #include "SkImageEncoder.h" |
(...skipping 12 matching lines...) Expand all Loading... | |
23 extern "C" { | 23 extern "C" { |
24 #include "jpeglib.h" | 24 #include "jpeglib.h" |
25 #include "jerror.h" | 25 #include "jerror.h" |
26 } | 26 } |
27 | 27 |
28 // These enable timing code that report milliseconds for an encoding/decoding | 28 // These enable timing code that report milliseconds for an encoding/decoding |
29 //#define TIME_ENCODE | 29 //#define TIME_ENCODE |
30 //#define TIME_DECODE | 30 //#define TIME_DECODE |
31 | 31 |
32 // this enables our rgb->yuv code, which is faster than libjpeg on ARM | 32 // this enables our rgb->yuv code, which is faster than libjpeg on ARM |
33 // disable for the moment, as we have some glitches when width != multiple of 4 | 33 // disable for the moment, as we have some glitches when width != multiple of 4 |
mtklein
2013/08/05 22:31:09
Off topic, but maybe we should delete this line, g
scroggo
2013/08/06 15:06:27
Done. This line has been here since we imported th
| |
34 #define WE_CONVERT_TO_YUV | 34 #define WE_CONVERT_TO_YUV |
35 | 35 |
36 // If ANDROID_RGB is defined by in the jpeg headers it indicates that jpeg offer s | 36 // If ANDROID_RGB is defined by in the jpeg headers it indicates that jpeg offer s |
37 // support for two additional formats (1) JCS_RGBA_8888 and (2) JCS_RGB_565. | 37 // support for two additional formats (1) JCS_RGBA_8888 and (2) JCS_RGB_565. |
38 | 38 |
39 ////////////////////////////////////////////////////////////////////////// | 39 ////////////////////////////////////////////////////////////////////////// |
40 ////////////////////////////////////////////////////////////////////////// | 40 ////////////////////////////////////////////////////////////////////////// |
41 | 41 |
42 static void overwrite_mem_buffer_size(j_decompress_ptr cinfo) { | 42 static void overwrite_mem_buffer_size(j_decompress_ptr cinfo) { |
mtklein
2013/08/05 22:31:09
Is j_decompress_ptr == jpeg_decompress_struct* ?
scroggo
2013/08/06 15:06:27
Yes. This is the only instance of j_decompress_ptr
| |
43 #ifdef SK_BUILD_FOR_ANDROID | 43 #ifdef SK_BUILD_FOR_ANDROID |
44 /* Check if the device indicates that it has a large amount of system memory | 44 /* Check if the device indicates that it has a large amount of system memory |
45 * if so, increase the memory allocation to 30MB instead of the default 5MB. | 45 * if so, increase the memory allocation to 30MB instead of the default 5MB. |
46 */ | 46 */ |
47 #ifdef ANDROID_LARGE_MEMORY_DEVICE | 47 #ifdef ANDROID_LARGE_MEMORY_DEVICE |
48 cinfo->mem->max_memory_to_use = 30 * 1024 * 1024; | 48 cinfo->mem->max_memory_to_use = 30 * 1024 * 1024; |
49 #else | 49 #else |
50 cinfo->mem->max_memory_to_use = 5 * 1024 * 1024; | 50 cinfo->mem->max_memory_to_use = 5 * 1024 * 1024; |
51 #endif | 51 #endif |
52 #endif // SK_BUILD_FOR_ANDROID | 52 #endif // SK_BUILD_FOR_ANDROID |
53 } | 53 } |
54 | 54 |
55 ////////////////////////////////////////////////////////////////////////// | 55 ////////////////////////////////////////////////////////////////////////// |
56 ////////////////////////////////////////////////////////////////////////// | 56 ////////////////////////////////////////////////////////////////////////// |
57 | 57 |
58 static void initialize_info(jpeg_decompress_struct* cinfo, skjpeg_source_mgr* sr c_mgr) { | |
mtklein
2013/08/05 22:31:09
cinfo is a pretty cryptic name. even just decompr
scroggo
2013/08/06 15:06:27
Agreed. Yes, the name is canonical.
| |
59 SkASSERT(cinfo != NULL); | |
60 SkASSERT(src_mgr != NULL); | |
61 jpeg_create_decompress(cinfo); | |
62 overwrite_mem_buffer_size(cinfo); | |
63 cinfo->src = src_mgr; | |
64 } | |
65 | |
58 #ifdef SK_BUILD_FOR_ANDROID | 66 #ifdef SK_BUILD_FOR_ANDROID |
59 class SkJPEGImageIndex { | 67 class SkJPEGImageIndex { |
60 public: | 68 public: |
61 SkJPEGImageIndex(SkStream* stream, SkImageDecoder* decoder) | 69 SkJPEGImageIndex(SkStream* stream, SkImageDecoder* decoder) |
62 : fSrcMgr(stream, decoder) | 70 : fSrcMgr(stream, decoder) |
63 , fInfoInitialized(false) | 71 , fInfoInitialized(false) |
64 , fHuffmanCreated(false) | 72 , fHuffmanCreated(false) |
65 , fDecompressStarted(false) | 73 , fDecompressStarted(false) |
66 { | 74 { |
67 SkDEBUGCODE(fReadHeaderSucceeded = false;) | 75 SkDEBUGCODE(fReadHeaderSucceeded = false;) |
68 } | 76 } |
69 | 77 |
70 ~SkJPEGImageIndex() { | 78 ~SkJPEGImageIndex() { |
mtklein
2013/08/05 22:31:09
Can you add a note in here about why we set the fl
scroggo
2013/08/06 15:06:27
Done.
| |
71 if (fHuffmanCreated) { | 79 if (fHuffmanCreated) { |
72 fHuffmanCreated = false; | 80 fHuffmanCreated = false; |
73 jpeg_destroy_huffman_index(&fHuffmanIndex); | 81 jpeg_destroy_huffman_index(&fHuffmanIndex); |
74 } | 82 } |
75 if (fDecompressStarted) { | 83 if (fDecompressStarted) { |
76 fDecompressStarted = false; | 84 fDecompressStarted = false; |
77 jpeg_finish_decompress(&fCInfo); | 85 jpeg_finish_decompress(&fCInfo); |
78 } | 86 } |
79 if (fInfoInitialized) { | 87 if (fInfoInitialized) { |
80 this->destroyInfo(); | 88 this->destroyInfo(); |
(...skipping 18 matching lines...) Expand all Loading... | |
99 /** | 107 /** |
100 * Initialize the cinfo struct. | 108 * Initialize the cinfo struct. |
101 * Calls jpeg_create_decompress, makes customizations, and | 109 * Calls jpeg_create_decompress, makes customizations, and |
102 * finally calls jpeg_read_header. Returns true if jpeg_read_header | 110 * finally calls jpeg_read_header. Returns true if jpeg_read_header |
103 * returns JPEG_HEADER_OK. | 111 * returns JPEG_HEADER_OK. |
104 * If cinfo was already initialized, destroyInfo must be called to | 112 * If cinfo was already initialized, destroyInfo must be called to |
105 * destroy the old one. Must not be called after startTileDecompress. | 113 * destroy the old one. Must not be called after startTileDecompress. |
106 */ | 114 */ |
107 bool initializeInfoAndReadHeader() { | 115 bool initializeInfoAndReadHeader() { |
108 SkASSERT(!fInfoInitialized && !fDecompressStarted); | 116 SkASSERT(!fInfoInitialized && !fDecompressStarted); |
109 jpeg_create_decompress(&fCInfo); | 117 initialize_info(&fCInfo, &fSrcMgr); |
110 overwrite_mem_buffer_size(&fCInfo); | |
111 fCInfo.src = &fSrcMgr; | |
112 fInfoInitialized = true; | 118 fInfoInitialized = true; |
113 const bool success = (JPEG_HEADER_OK == jpeg_read_header(&fCInfo, true)) ; | 119 const bool success = (JPEG_HEADER_OK == jpeg_read_header(&fCInfo, true)) ; |
114 SkDEBUGCODE(fReadHeaderSucceeded = success;) | 120 SkDEBUGCODE(fReadHeaderSucceeded = success;) |
115 return success; | 121 return success; |
116 } | 122 } |
117 | 123 |
118 jpeg_decompress_struct* cinfo() { return &fCInfo; } | 124 jpeg_decompress_struct* cinfo() { return &fCInfo; } |
mtklein
2013/08/05 22:31:09
these two guys can be const methods?
scroggo
2013/08/06 15:06:27
No, these are modified and passed to functions tha
mtklein
2013/08/06 15:22:48
just meant
jpeg_decompress_struct* cinfo() const
scroggo
2013/08/06 15:29:46
That gives me the following error:
../../../src/i
| |
119 | 125 |
120 huffman_index* huffmanIndex() { return &fHuffmanIndex; } | 126 huffman_index* huffmanIndex() { return &fHuffmanIndex; } |
121 | 127 |
122 /** | 128 /** |
123 * Build the index to be used for tile based decoding. | 129 * Build the index to be used for tile based decoding. |
124 * Must only be called after a successful call to | 130 * Must only be called after a successful call to |
125 * initializeInfoAndReadHeader and must not be called more | 131 * initializeInfoAndReadHeader and must not be called more |
126 * than once. | 132 * than once. |
127 */ | 133 */ |
128 bool buildHuffmanIndex() { | 134 bool buildHuffmanIndex() { |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
186 #endif | 192 #endif |
187 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE; | 193 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE; |
188 | 194 |
189 private: | 195 private: |
190 #ifdef SK_BUILD_FOR_ANDROID | 196 #ifdef SK_BUILD_FOR_ANDROID |
191 SkJPEGImageIndex* fImageIndex; | 197 SkJPEGImageIndex* fImageIndex; |
192 int fImageWidth; | 198 int fImageWidth; |
193 int fImageHeight; | 199 int fImageHeight; |
194 #endif | 200 #endif |
195 | 201 |
202 /** | |
203 * Determine the appropriate bitmap config and out_color_space based on | |
204 * both the preference of the caller and the jpeg_color_space on the | |
205 * jpeg_decompress_struct passed in. | |
206 * Must be called after jpeg_read_header. | |
207 */ | |
208 SkBitmap::Config getBitmapConfig(jpeg_decompress_struct*); | |
209 | |
196 typedef SkImageDecoder INHERITED; | 210 typedef SkImageDecoder INHERITED; |
197 }; | 211 }; |
198 | 212 |
199 ////////////////////////////////////////////////////////////////////////// | 213 ////////////////////////////////////////////////////////////////////////// |
200 | 214 |
201 /* Automatically clean up after throwing an exception */ | 215 /* Automatically clean up after throwing an exception */ |
202 class JPEGAutoClean { | 216 class JPEGAutoClean { |
203 public: | 217 public: |
204 JPEGAutoClean(): cinfo_ptr(NULL) {} | 218 JPEGAutoClean(): cinfo_ptr(NULL) {} |
205 ~JPEGAutoClean() { | 219 ~JPEGAutoClean() { |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
288 // C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K | 302 // C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K |
289 // The conversion from CMY->RGB remains the same | 303 // The conversion from CMY->RGB remains the same |
290 for (unsigned int x = 0; x < width; ++x, scanline += 4) { | 304 for (unsigned int x = 0; x < width; ++x, scanline += 4) { |
291 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]); | 305 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]); |
292 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]); | 306 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]); |
293 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]); | 307 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]); |
294 scanline[3] = 255; | 308 scanline[3] = 255; |
295 } | 309 } |
296 } | 310 } |
297 | 311 |
312 /** | |
313 * Common code for setting the error manager. | |
314 */ | |
315 static void set_error_mgr(jpeg_decompress_struct* cinfo, skjpeg_error_mgr* error Manager) { | |
316 SkASSERT(cinfo != NULL); | |
317 SkASSERT(errorManager != NULL); | |
318 cinfo->err = jpeg_std_error(errorManager); | |
319 errorManager->error_exit = skjpeg_error_exit; | |
320 } | |
321 | |
322 /** | |
323 * Common code for turning off upsampling and smoothing. Turning these | |
324 * off helps performance without showing noticable differences in the | |
325 * resulting bitmap. | |
326 */ | |
327 static void turn_off_visual_optimizations(jpeg_decompress_struct* cinfo) { | |
328 SkASSERT(cinfo != NULL); | |
329 /* this gives about 30% performance improvement. In theory it may | |
330 reduce the visual quality, in practice I'm not seeing a difference | |
331 */ | |
332 cinfo->do_fancy_upsampling = 0; | |
333 | |
334 /* this gives another few percents */ | |
335 cinfo->do_block_smoothing = 0; | |
336 } | |
337 | |
338 /** | |
339 * Common code for setting the dct method. | |
340 */ | |
341 static void set_dct_method(const SkImageDecoder& decoder, jpeg_decompress_struct * cinfo) { | |
342 SkASSERT(cinfo != NULL); | |
343 #ifdef DCT_IFAST_SUPPORTED | |
344 if (decoder.getPreferQualityOverSpeed()) { | |
345 cinfo->dct_method = JDCT_ISLOW; | |
346 } else { | |
347 cinfo->dct_method = JDCT_IFAST; | |
348 } | |
349 #else | |
350 cinfo->dct_method = JDCT_ISLOW; | |
351 #endif | |
352 } | |
353 | |
354 SkBitmap::Config SkJPEGImageDecoder::getBitmapConfig(jpeg_decompress_struct* cin fo) { | |
mtklein
2013/08/05 22:31:09
can we make this guy take a const&?
scroggo
2013/08/06 15:06:27
The Skia style guide states to use a pointer when
mtklein
2013/08/06 15:22:48
Right. It looks to me like it's only being read h
scroggo
2013/08/06 15:29:46
No, the second switch statement will modify it.
| |
355 SkASSERT(cinfo != NULL); | |
356 | |
357 SrcDepth srcDepth = k32Bit_SrcDepth; | |
358 if (JCS_GRAYSCALE == cinfo->jpeg_color_space) { | |
359 srcDepth = k8BitGray_SrcDepth; | |
360 } | |
361 | |
362 SkBitmap::Config config = this->getPrefConfig(srcDepth, false); | |
mtklein
2013/08/05 22:31:09
Would help my reading to add a reminder what the f
scroggo
2013/08/06 15:06:27
Done.
| |
363 switch (config) { | |
364 case SkBitmap::kA8_Config: | |
365 // Only respect A8 config if the original is grayscale, | |
366 // in which case we will treat the grayscale as alpha | |
367 // values. | |
368 if (cinfo->jpeg_color_space != JCS_GRAYSCALE) { | |
369 config = SkBitmap::kARGB_8888_Config; | |
370 } | |
371 break; | |
372 case SkBitmap::kARGB_8888_Config: | |
373 // Fall through. | |
374 case SkBitmap::kARGB_4444_Config: | |
375 // Fall through. | |
376 case SkBitmap::kRGB_565_Config: | |
377 // These are acceptable destination configs. | |
378 break; | |
379 default: | |
380 // Force all other configs to 8888. | |
381 config = SkBitmap::kARGB_8888_Config; | |
382 } | |
383 | |
384 switch (cinfo->jpeg_color_space) { | |
385 case JCS_CMYK: | |
386 // Fall through. | |
387 case JCS_YCCK: | |
scroggo
2013/08/05 21:47:51
In the old code, JCS_YCCK results in a request for
| |
388 // libjpeg cannot convert from CMYK or YCCK to RGB - here we set up | |
389 // so libjpeg will give us CMYK samples back and we will later | |
390 // manually convert them to RGB | |
391 cinfo->out_color_space = JCS_CMYK; | |
392 break; | |
393 case JCS_GRAYSCALE: | |
394 if (SkBitmap::kA8_Config == config) { | |
395 cinfo->out_color_space = JCS_GRAYSCALE; | |
396 break; | |
397 } | |
398 // The data is JCS_GRAYSCALE, but the caller wants some sort of RGB | |
399 // config. Fall through to set to the default. | |
400 default: | |
401 cinfo->out_color_space = JCS_RGB; | |
402 break; | |
mtklein
2013/08/05 22:31:09
just for consistency i'd add a break above or remo
scroggo
2013/08/06 15:06:27
Done.
| |
403 } | |
404 return config; | |
405 } | |
406 | |
407 #ifdef ANDROID_RGB | |
408 /** | |
409 * FIXME: Improve this name. Here is what this function does: | |
scroggo
2013/08/05 21:47:51
Suggestions?
| |
410 * - Set cinfo->dither_mode | |
411 * - change the out color space custom color spaces. | |
412 */ | |
413 static void apply_dither_mode(jpeg_decompress_struct* cinfo, SkBitmap::Config co nfig, | |
mtklein
2013/08/05 22:31:09
funky arg indent?
mtklein
2013/08/05 22:31:09
maybe adjustOutColorSpaceForDither?
scroggo
2013/08/06 15:06:27
How about adjust_out_color_space_and_dither?
scroggo
2013/08/06 15:06:27
Done.
| |
414 const SkImageDecoder& decoder) { | |
415 SkASSERT(cinfo != NULL); | |
416 cinfo->dither_mode = JDITHER_NONE; | |
417 if (JCS_CMYK == cinfo->out_color_space) { | |
scroggo
2013/08/05 21:47:51
This check was not happening in tile based decode.
| |
418 return; | |
419 } | |
420 switch(config) { | |
421 case SkBitmap::kARGB_8888_Config: | |
422 cinfo->out_color_space = JCS_RGBA_8888; | |
423 break; | |
424 case SkBitmap::kRGB_565_Config: | |
425 cinfo->out_color_space = JCS_RGB_565; | |
426 if (decoder.getDitherImage()) { | |
427 cinfo->dither_mode = JDITHER_ORDERED; | |
428 } | |
429 break; | |
430 default: | |
431 break; | |
432 } | |
433 } | |
434 #endif | |
435 | |
298 bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { | 436 bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { |
299 #ifdef TIME_DECODE | 437 #ifdef TIME_DECODE |
300 SkAutoTime atm("JPEG Decode"); | 438 SkAutoTime atm("JPEG Decode"); |
301 #endif | 439 #endif |
302 | 440 |
303 JPEGAutoClean autoClean; | 441 JPEGAutoClean autoClean; |
304 | 442 |
305 jpeg_decompress_struct cinfo; | 443 jpeg_decompress_struct cinfo; |
306 skjpeg_error_mgr errorManager; | |
307 skjpeg_source_mgr srcManager(stream, this); | 444 skjpeg_source_mgr srcManager(stream, this); |
308 | 445 |
309 cinfo.err = jpeg_std_error(&errorManager); | 446 skjpeg_error_mgr errorManager; |
310 errorManager.error_exit = skjpeg_error_exit; | 447 set_error_mgr(&cinfo, &errorManager); |
311 | 448 |
312 // All objects need to be instantiated before this setjmp call so that | 449 // All objects need to be instantiated before this setjmp call so that |
313 // they will be cleaned up properly if an error occurs. | 450 // they will be cleaned up properly if an error occurs. |
314 if (setjmp(errorManager.fJmpBuf)) { | 451 if (setjmp(errorManager.fJmpBuf)) { |
315 return return_false(cinfo, *bm, "setjmp"); | 452 return return_false(cinfo, *bm, "setjmp"); |
316 } | 453 } |
317 | 454 |
318 jpeg_create_decompress(&cinfo); | 455 initialize_info(&cinfo, &srcManager); |
319 autoClean.set(&cinfo); | 456 autoClean.set(&cinfo); |
320 | 457 |
321 overwrite_mem_buffer_size(&cinfo); | |
322 | |
323 //jpeg_stdio_src(&cinfo, file); | |
324 cinfo.src = &srcManager; | |
325 | |
326 int status = jpeg_read_header(&cinfo, true); | 458 int status = jpeg_read_header(&cinfo, true); |
327 if (status != JPEG_HEADER_OK) { | 459 if (status != JPEG_HEADER_OK) { |
328 return return_false(cinfo, *bm, "read_header"); | 460 return return_false(cinfo, *bm, "read_header"); |
329 } | 461 } |
330 | 462 |
331 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it | 463 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it |
332 can) much faster that we, just use their num/denom api to approximate | 464 can) much faster that we, just use their num/denom api to approximate |
333 the size. | 465 the size. |
334 */ | 466 */ |
335 int sampleSize = this->getSampleSize(); | 467 int sampleSize = this->getSampleSize(); |
336 | 468 |
337 #ifdef DCT_IFAST_SUPPORTED | 469 set_dct_method(*this, &cinfo); |
338 if (this->getPreferQualityOverSpeed()) { | |
339 cinfo.dct_method = JDCT_ISLOW; | |
340 } else { | |
341 cinfo.dct_method = JDCT_IFAST; | |
342 } | |
343 #else | |
344 cinfo.dct_method = JDCT_ISLOW; | |
345 #endif | |
346 | 470 |
347 cinfo.scale_num = 1; | |
scroggo
2013/08/05 21:47:51
scale_num defaults to 1, so this line is unnecessa
mtklein
2013/08/05 22:31:09
maybe add an assert like in the tiled code?
scroggo
2013/08/06 15:06:27
Done.
| |
348 cinfo.scale_denom = sampleSize; | 471 cinfo.scale_denom = sampleSize; |
349 | 472 |
350 /* this gives about 30% performance improvement. In theory it may | 473 turn_off_visual_optimizations(&cinfo); |
351 reduce the visual quality, in practice I'm not seeing a difference | |
352 */ | |
353 cinfo.do_fancy_upsampling = 0; | |
354 | 474 |
355 /* this gives another few percents */ | 475 SkBitmap::Config config = this->getBitmapConfig(&cinfo); |
mtklein
2013/08/05 22:31:09
const?
scroggo
2013/08/06 15:06:27
Done.
| |
356 cinfo.do_block_smoothing = 0; | |
357 | |
358 SrcDepth srcDepth = k32Bit_SrcDepth; | |
359 /* default format is RGB */ | |
360 if (cinfo.jpeg_color_space == JCS_CMYK) { | |
361 // libjpeg cannot convert from CMYK to RGB - here we set up | |
362 // so libjpeg will give us CMYK samples back and we will | |
363 // later manually convert them to RGB | |
364 cinfo.out_color_space = JCS_CMYK; | |
365 } else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) { | |
366 cinfo.out_color_space = JCS_GRAYSCALE; | |
367 srcDepth = k8BitGray_SrcDepth; | |
368 } else { | |
369 cinfo.out_color_space = JCS_RGB; | |
370 } | |
371 | |
372 SkBitmap::Config config = this->getPrefConfig(srcDepth, false); | |
373 // only these make sense for jpegs | |
374 if (SkBitmap::kA8_Config == config) { | |
375 if (cinfo.jpeg_color_space != JCS_GRAYSCALE) { | |
376 // Converting from a non grayscale image to A8 is | |
377 // not currently supported. | |
378 config = SkBitmap::kARGB_8888_Config; | |
379 // Change the output from jpeg back to RGB. | |
380 cinfo.out_color_space = JCS_RGB; | |
scroggo
2013/08/05 21:47:51
This line would incorrectly change out_color_space
| |
381 } | |
382 } else if (config != SkBitmap::kARGB_8888_Config && | |
383 config != SkBitmap::kARGB_4444_Config && | |
384 config != SkBitmap::kRGB_565_Config) { | |
385 config = SkBitmap::kARGB_8888_Config; | |
386 } | |
387 | 476 |
388 #ifdef ANDROID_RGB | 477 #ifdef ANDROID_RGB |
389 cinfo.dither_mode = JDITHER_NONE; | 478 apply_dither_mode(&cinfo, config, *this); |
390 if (SkBitmap::kARGB_8888_Config == config && JCS_CMYK != cinfo.out_color_spa ce) { | |
391 cinfo.out_color_space = JCS_RGBA_8888; | |
392 } else if (SkBitmap::kRGB_565_Config == config && JCS_CMYK != cinfo.out_colo r_space) { | |
393 cinfo.out_color_space = JCS_RGB_565; | |
394 if (this->getDitherImage()) { | |
395 cinfo.dither_mode = JDITHER_ORDERED; | |
396 } | |
397 } | |
398 #endif | 479 #endif |
399 | 480 |
400 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) { | 481 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) { |
401 bm->setConfig(config, cinfo.image_width, cinfo.image_height); | 482 bm->setConfig(config, cinfo.image_width, cinfo.image_height); |
402 bm->setIsOpaque(config != SkBitmap::kA8_Config); | 483 bm->setIsOpaque(config != SkBitmap::kA8_Config); |
403 return true; | 484 return true; |
404 } | 485 } |
405 | 486 |
406 /* image_width and image_height are the original dimensions, available | 487 /* image_width and image_height are the original dimensions, available |
407 after jpeg_read_header(). To see the scaled dimensions, we have to call | 488 after jpeg_read_header(). To see the scaled dimensions, we have to call |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
545 return true; | 626 return true; |
546 } | 627 } |
547 | 628 |
548 #ifdef SK_BUILD_FOR_ANDROID | 629 #ifdef SK_BUILD_FOR_ANDROID |
549 bool SkJPEGImageDecoder::onBuildTileIndex(SkStream* stream, int *width, int *hei ght) { | 630 bool SkJPEGImageDecoder::onBuildTileIndex(SkStream* stream, int *width, int *hei ght) { |
550 | 631 |
551 SkAutoTDelete<SkJPEGImageIndex> imageIndex(SkNEW_ARGS(SkJPEGImageIndex, (str eam, this))); | 632 SkAutoTDelete<SkJPEGImageIndex> imageIndex(SkNEW_ARGS(SkJPEGImageIndex, (str eam, this))); |
552 jpeg_decompress_struct* cinfo = imageIndex->cinfo(); | 633 jpeg_decompress_struct* cinfo = imageIndex->cinfo(); |
553 | 634 |
554 skjpeg_error_mgr sk_err; | 635 skjpeg_error_mgr sk_err; |
555 cinfo->err = jpeg_std_error(&sk_err); | 636 set_error_mgr(cinfo, &sk_err); |
556 sk_err.error_exit = skjpeg_error_exit; | |
557 | 637 |
558 // All objects need to be instantiated before this setjmp call so that | 638 // All objects need to be instantiated before this setjmp call so that |
559 // they will be cleaned up properly if an error occurs. | 639 // they will be cleaned up properly if an error occurs. |
560 if (setjmp(sk_err.fJmpBuf)) { | 640 if (setjmp(sk_err.fJmpBuf)) { |
561 return false; | 641 return false; |
562 } | 642 } |
563 | 643 |
564 // create the cinfo used to create/build the huffmanIndex | 644 // create the cinfo used to create/build the huffmanIndex |
565 if (!imageIndex->initializeInfoAndReadHeader()) { | 645 if (!imageIndex->initializeInfoAndReadHeader()) { |
566 return false; | 646 return false; |
567 } | 647 } |
568 | 648 |
569 if (!imageIndex->buildHuffmanIndex()) { | 649 if (!imageIndex->buildHuffmanIndex()) { |
570 return false; | 650 return false; |
571 } | 651 } |
572 | 652 |
573 // destroy the cinfo used to create/build the huffman index | 653 // destroy the cinfo used to create/build the huffman index |
574 imageIndex->destroyInfo(); | 654 imageIndex->destroyInfo(); |
575 | 655 |
576 // Init decoder to image decode mode | 656 // Init decoder to image decode mode |
577 if (!imageIndex->initializeInfoAndReadHeader()) { | 657 if (!imageIndex->initializeInfoAndReadHeader()) { |
578 return false; | 658 return false; |
579 } | 659 } |
580 | 660 |
581 cinfo->out_color_space = JCS_RGBA_8888; | 661 // FIXME: This sets cinfo->out_color_space, which we may change later |
582 cinfo->do_fancy_upsampling = 0; | 662 // based on the config in onDecodeSubset. This should be fine, since |
583 cinfo->do_block_smoothing = 0; | 663 // jpeg_init_read_tile_scanline will check out_color_space again after |
664 // that change (when it calls jinit_color_deconverter). | |
665 (void) this->getBitmapConfig(cinfo); | |
666 | |
667 turn_off_visual_optimizations(cinfo); | |
584 | 668 |
585 // instead of jpeg_start_decompress() we start a tiled decompress | 669 // instead of jpeg_start_decompress() we start a tiled decompress |
586 if (!imageIndex->startTileDecompress()) { | 670 if (!imageIndex->startTileDecompress()) { |
587 return false; | 671 return false; |
588 } | 672 } |
589 | 673 |
590 SkASSERT(1 == cinfo->scale_num); | 674 SkASSERT(1 == cinfo->scale_num); |
591 *height = cinfo->output_height; | 675 fImageWidth = cinfo->output_width; |
592 *width = cinfo->output_width; | 676 fImageHeight = cinfo->output_height; |
593 fImageWidth = *width; | 677 |
594 fImageHeight = *height; | 678 if (width) { |
679 *width = fImageWidth; | |
680 } | |
681 if (height) { | |
682 *height = fImageHeight; | |
683 } | |
595 | 684 |
596 SkDELETE(fImageIndex); | 685 SkDELETE(fImageIndex); |
597 fImageIndex = imageIndex.detach(); | 686 fImageIndex = imageIndex.detach(); |
598 | 687 |
599 return true; | 688 return true; |
600 } | 689 } |
601 | 690 |
602 bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) { | 691 bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) { |
603 if (NULL == fImageIndex) { | 692 if (NULL == fImageIndex) { |
604 return false; | 693 return false; |
605 } | 694 } |
606 jpeg_decompress_struct* cinfo = fImageIndex->cinfo(); | 695 jpeg_decompress_struct* cinfo = fImageIndex->cinfo(); |
607 | 696 |
608 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight); | 697 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight); |
609 if (!rect.intersect(region)) { | 698 if (!rect.intersect(region)) { |
610 // If the requested region is entirely outside the image return false | 699 // If the requested region is entirely outside the image return false |
611 return false; | 700 return false; |
612 } | 701 } |
613 | 702 |
614 | 703 |
615 skjpeg_error_mgr errorManager; | 704 skjpeg_error_mgr errorManager; |
616 cinfo->err = jpeg_std_error(&errorManager); | 705 set_error_mgr(cinfo, &errorManager); |
617 errorManager.error_exit = skjpeg_error_exit; | 706 |
618 if (setjmp(errorManager.fJmpBuf)) { | 707 if (setjmp(errorManager.fJmpBuf)) { |
619 return false; | 708 return false; |
620 } | 709 } |
621 | 710 |
622 int requestedSampleSize = this->getSampleSize(); | 711 int requestedSampleSize = this->getSampleSize(); |
623 cinfo->scale_denom = requestedSampleSize; | 712 cinfo->scale_denom = requestedSampleSize; |
624 | 713 |
625 if (this->getPreferQualityOverSpeed()) { | 714 set_dct_method(*this, cinfo); |
scroggo
2013/08/05 21:47:51
The new method checks #ifdef's where appropraiate.
| |
626 cinfo->dct_method = JDCT_ISLOW; | |
627 } else { | |
628 cinfo->dct_method = JDCT_IFAST; | |
629 } | |
630 | 715 |
631 SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false); | 716 SkBitmap::Config config = this->getBitmapConfig(cinfo); |
scroggo
2013/08/05 21:47:51
The new code has the behavior used in onDecode, so
|
mtklein
2013/08/05 22:31:09
const?
scroggo
2013/08/06 15:06:27
Done.
|
632 if (config != SkBitmap::kARGB_8888_Config && | |
633 config != SkBitmap::kARGB_4444_Config && | |
634 config != SkBitmap::kRGB_565_Config) { | |
635 config = SkBitmap::kARGB_8888_Config; | |
636 } | |
637 | |
638 /* default format is RGB */ | |
639 cinfo->out_color_space = JCS_RGB; | |
scroggo
2013/08/05 21:47:51
Likewise, the new code uses a valid color space.
| |
640 | |
641 #ifdef ANDROID_RGB | 717 #ifdef ANDROID_RGB |
642 cinfo->dither_mode = JDITHER_NONE; | 718 apply_dither_mode(cinfo, config, *this); |
643 if (SkBitmap::kARGB_8888_Config == config) { | |
scroggo
2013/08/05 21:47:51
The factored out function checks for cmyk.
| |
644 cinfo->out_color_space = JCS_RGBA_8888; | |
645 } else if (SkBitmap::kRGB_565_Config == config) { | |
646 cinfo->out_color_space = JCS_RGB_565; | |
647 if (this->getDitherImage()) { | |
648 cinfo->dither_mode = JDITHER_ORDERED; | |
649 } | |
650 } | |
651 #endif | 719 #endif |
652 | 720 |
653 int startX = rect.fLeft; | 721 int startX = rect.fLeft; |
654 int startY = rect.fTop; | 722 int startY = rect.fTop; |
655 int width = rect.width(); | 723 int width = rect.width(); |
656 int height = rect.height(); | 724 int height = rect.height(); |
657 | 725 |
658 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), | 726 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), |
659 &startX, &startY, &width, &height); | 727 &startX, &startY, &width, &height); |
660 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo); | 728 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo); |
(...skipping 414 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1075 } | 1143 } |
1076 | 1144 |
1077 static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) { | 1145 static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) { |
1078 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL; | 1146 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL; |
1079 } | 1147 } |
1080 | 1148 |
1081 | 1149 |
1082 static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libjpeg_dfactory); | 1150 static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libjpeg_dfactory); |
1083 static SkTRegistry<SkImageDecoder::Format, SkStream*> gFormatReg(get_format_jpeg ); | 1151 static SkTRegistry<SkImageDecoder::Format, SkStream*> gFormatReg(get_format_jpeg ); |
1084 static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libjpeg_efact ory); | 1152 static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libjpeg_efact ory); |
OLD | NEW |