OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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); |
OLD | NEW |