• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2007, The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "SkImageDecoder.h"
18 #include "SkImageEncoder.h"
19 #include "SkColorPriv.h"
20 #include "SkDither.h"
21 #include "SkScaledBitmapSampler.h"
22 #include "SkStream.h"
23 #include "SkTemplates.h"
24 #include "SkUtils.h"
25 
26 #include <stdio.h>
27 extern "C" {
28     #include "jpeglib.h"
29     #include "jerror.h"
30 }
31 
32 // this enables timing code to report milliseconds for an encode
33 //#define TIME_ENCODE
34 //#define TIME_DECODE
35 
36 // this enables our rgb->yuv code, which is faster than libjpeg on ARM
37 // disable for the moment, as we have some glitches when width != multiple of 4
38 #define WE_CONVERT_TO_YUV
39 
40 //////////////////////////////////////////////////////////////////////////
41 //////////////////////////////////////////////////////////////////////////
42 
43 class SkJPEGImageDecoder : public SkImageDecoder {
44 public:
getFormat() const45     virtual Format getFormat() const {
46         return kJPEG_Format;
47     }
48 
49 protected:
50     virtual bool onDecode(SkStream* stream, SkBitmap* bm,
51                           SkBitmap::Config pref, Mode);
52 };
53 
54 //////////////////////////////////////////////////////////////////////////
55 
56 #include "SkTime.h"
57 
58 class AutoTimeMillis {
59 public:
AutoTimeMillis(const char label[])60     AutoTimeMillis(const char label[]) : fLabel(label) {
61         if (!fLabel) {
62             fLabel = "";
63         }
64         fNow = SkTime::GetMSecs();
65     }
~AutoTimeMillis()66     ~AutoTimeMillis() {
67         SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow);
68     }
69 private:
70     const char* fLabel;
71     SkMSec      fNow;
72 };
73 
74 /* our source struct for directing jpeg to our stream object
75 */
76 struct sk_source_mgr : jpeg_source_mgr {
77     sk_source_mgr(SkStream* stream, SkImageDecoder* decoder);
78 
79     SkStream*   fStream;
80     const void* fMemoryBase;
81     size_t      fMemoryBaseSize;
82     SkImageDecoder* fDecoder;
83     enum {
84         kBufferSize = 1024
85     };
86     char    fBuffer[kBufferSize];
87 };
88 
89 /* Automatically clean up after throwing an exception */
90 class JPEGAutoClean {
91 public:
JPEGAutoClean()92     JPEGAutoClean(): cinfo_ptr(NULL) {}
~JPEGAutoClean()93     ~JPEGAutoClean() {
94         if (cinfo_ptr) {
95             jpeg_destroy_decompress(cinfo_ptr);
96         }
97     }
set(jpeg_decompress_struct * info)98     void set(jpeg_decompress_struct* info) {
99         cinfo_ptr = info;
100     }
101 private:
102     jpeg_decompress_struct* cinfo_ptr;
103 };
104 
sk_init_source(j_decompress_ptr cinfo)105 static void sk_init_source(j_decompress_ptr cinfo) {
106     sk_source_mgr*  src = (sk_source_mgr*)cinfo->src;
107     src->next_input_byte = (const JOCTET*)src->fBuffer;
108     src->bytes_in_buffer = 0;
109 }
110 
sk_fill_input_buffer(j_decompress_ptr cinfo)111 static boolean sk_fill_input_buffer(j_decompress_ptr cinfo) {
112     sk_source_mgr* src = (sk_source_mgr*)cinfo->src;
113     if (src->fDecoder != NULL && src->fDecoder->shouldCancelDecode()) {
114         return FALSE;
115     }
116     size_t bytes = src->fStream->read(src->fBuffer, sk_source_mgr::kBufferSize);
117     // note that JPEG is happy with less than the full read,
118     // as long as the result is non-zero
119     if (bytes == 0) {
120         return FALSE;
121     }
122 
123     src->next_input_byte = (const JOCTET*)src->fBuffer;
124     src->bytes_in_buffer = bytes;
125     return TRUE;
126 }
127 
sk_skip_input_data(j_decompress_ptr cinfo,long num_bytes)128 static void sk_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
129     SkASSERT(num_bytes > 0);
130 
131     sk_source_mgr*  src = (sk_source_mgr*)cinfo->src;
132 
133     long bytesToSkip = num_bytes - src->bytes_in_buffer;
134 
135     // check if the skip amount exceeds the current buffer
136     if (bytesToSkip > 0) {
137         size_t bytes = src->fStream->skip(bytesToSkip);
138         if (bytes != (size_t)bytesToSkip) {
139 //            SkDebugf("xxxxxxxxxxxxxx failure to skip request %d actual %d\n", bytesToSkip, bytes);
140             cinfo->err->error_exit((j_common_ptr)cinfo);
141         }
142         src->next_input_byte = (const JOCTET*)src->fBuffer;
143         src->bytes_in_buffer = 0;
144     } else {
145         src->next_input_byte += num_bytes;
146         src->bytes_in_buffer -= num_bytes;
147     }
148 }
149 
sk_resync_to_restart(j_decompress_ptr cinfo,int desired)150 static boolean sk_resync_to_restart(j_decompress_ptr cinfo, int desired) {
151     sk_source_mgr*  src = (sk_source_mgr*)cinfo->src;
152 
153     // what is the desired param for???
154 
155     if (!src->fStream->rewind()) {
156         SkDebugf("xxxxxxxxxxxxxx failure to rewind\n");
157         cinfo->err->error_exit((j_common_ptr)cinfo);
158         return FALSE;
159     }
160     src->next_input_byte = (const JOCTET*)src->fBuffer;
161     src->bytes_in_buffer = 0;
162     return TRUE;
163 }
164 
sk_term_source(j_decompress_ptr)165 static void sk_term_source(j_decompress_ptr /*cinfo*/) {}
166 
167 ///////////////////////////////////////////////////////////////////////////////
168 
skmem_init_source(j_decompress_ptr cinfo)169 static void skmem_init_source(j_decompress_ptr cinfo) {
170     sk_source_mgr*  src = (sk_source_mgr*)cinfo->src;
171     src->next_input_byte = (const JOCTET*)src->fMemoryBase;
172     src->bytes_in_buffer = src->fMemoryBaseSize;
173 }
174 
skmem_fill_input_buffer(j_decompress_ptr cinfo)175 static boolean skmem_fill_input_buffer(j_decompress_ptr cinfo) {
176     SkDebugf("xxxxxxxxxxxxxx skmem_fill_input_buffer called\n");
177     return FALSE;
178 }
179 
skmem_skip_input_data(j_decompress_ptr cinfo,long num_bytes)180 static void skmem_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
181     sk_source_mgr*  src = (sk_source_mgr*)cinfo->src;
182 //    SkDebugf("xxxxxxxxxxxxxx skmem_skip_input_data called %d\n", num_bytes);
183     src->next_input_byte = (const JOCTET*)((const char*)src->next_input_byte + num_bytes);
184     src->bytes_in_buffer -= num_bytes;
185 }
186 
skmem_resync_to_restart(j_decompress_ptr cinfo,int desired)187 static boolean skmem_resync_to_restart(j_decompress_ptr cinfo, int desired) {
188     SkDebugf("xxxxxxxxxxxxxx skmem_resync_to_restart called\n");
189     return TRUE;
190 }
191 
skmem_term_source(j_decompress_ptr)192 static void skmem_term_source(j_decompress_ptr /*cinfo*/) {}
193 
194 ///////////////////////////////////////////////////////////////////////////////
195 
sk_source_mgr(SkStream * stream,SkImageDecoder * decoder)196 sk_source_mgr::sk_source_mgr(SkStream* stream, SkImageDecoder* decoder) : fStream(stream) {
197     fDecoder = decoder;
198     const void* baseAddr = stream->getMemoryBase();
199     if (baseAddr && false) {
200         fMemoryBase = baseAddr;
201         fMemoryBaseSize = stream->getLength();
202 
203         init_source = skmem_init_source;
204         fill_input_buffer = skmem_fill_input_buffer;
205         skip_input_data = skmem_skip_input_data;
206         resync_to_restart = skmem_resync_to_restart;
207         term_source = skmem_term_source;
208     } else {
209         fMemoryBase = NULL;
210         fMemoryBaseSize = 0;
211 
212         init_source = sk_init_source;
213         fill_input_buffer = sk_fill_input_buffer;
214         skip_input_data = sk_skip_input_data;
215         resync_to_restart = sk_resync_to_restart;
216         term_source = sk_term_source;
217     }
218 //    SkDebugf("**************** use memorybase %p %d\n", fMemoryBase, fMemoryBaseSize);
219 }
220 
221 #include <setjmp.h>
222 
223 struct sk_error_mgr : jpeg_error_mgr {
224     jmp_buf fJmpBuf;
225 };
226 
sk_error_exit(j_common_ptr cinfo)227 static void sk_error_exit(j_common_ptr cinfo) {
228     sk_error_mgr* error = (sk_error_mgr*)cinfo->err;
229 
230     (*error->output_message) (cinfo);
231 
232     /* Let the memory manager delete any temp files before we die */
233     jpeg_destroy(cinfo);
234 
235     longjmp(error->fJmpBuf, -1);
236 }
237 
238 ///////////////////////////////////////////////////////////////////////////////
239 
skip_src_rows(jpeg_decompress_struct * cinfo,void * buffer,int count)240 static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer,
241                           int count) {
242     for (int i = 0; i < count; i++) {
243         JSAMPLE* rowptr = (JSAMPLE*)buffer;
244         int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
245         if (row_count != 1) {
246             return false;
247         }
248     }
249     return true;
250 }
251 
252 // This guy exists just to aid in debugging, as it allows debuggers to just
253 // set a break-point in one place to see all error exists.
return_false(const jpeg_decompress_struct & cinfo,const SkBitmap & bm,const char msg[])254 static bool return_false(const jpeg_decompress_struct& cinfo,
255                          const SkBitmap& bm, const char msg[]) {
256 #if 0
257     SkDebugf("libjpeg error %d <%s> from %s [%d %d]", cinfo.err->msg_code,
258              cinfo.err->jpeg_message_table[cinfo.err->msg_code], msg,
259              bm.width(), bm.height());
260 #endif
261     return false;   // must always return false
262 }
263 
onDecode(SkStream * stream,SkBitmap * bm,SkBitmap::Config prefConfig,Mode mode)264 bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm,
265                                   SkBitmap::Config prefConfig, Mode mode) {
266 #ifdef TIME_DECODE
267     AutoTimeMillis atm("JPEG Decode");
268 #endif
269 
270     SkAutoMalloc  srcStorage;
271     JPEGAutoClean autoClean;
272 
273     jpeg_decompress_struct  cinfo;
274     sk_error_mgr            sk_err;
275     sk_source_mgr           sk_stream(stream, this);
276 
277     cinfo.err = jpeg_std_error(&sk_err);
278     sk_err.error_exit = sk_error_exit;
279 
280     // All objects need to be instantiated before this setjmp call so that
281     // they will be cleaned up properly if an error occurs.
282     if (setjmp(sk_err.fJmpBuf)) {
283         return return_false(cinfo, *bm, "setjmp");
284     }
285 
286     jpeg_create_decompress(&cinfo);
287     autoClean.set(&cinfo);
288 
289     //jpeg_stdio_src(&cinfo, file);
290     cinfo.src = &sk_stream;
291 
292     int status = jpeg_read_header(&cinfo, true);
293     if (status != JPEG_HEADER_OK) {
294         return return_false(cinfo, *bm, "read_header");
295     }
296 
297     /*  Try to fulfill the requested sampleSize. Since jpeg can do it (when it
298         can) much faster that we, just use their num/denom api to approximate
299         the size.
300     */
301     int sampleSize = this->getSampleSize();
302 
303     cinfo.dct_method = JDCT_IFAST;
304     cinfo.scale_num = 1;
305     cinfo.scale_denom = sampleSize;
306 
307     /* this gives about 30% performance improvement. In theory it may
308        reduce the visual quality, in practice I'm not seeing a difference
309      */
310     cinfo.do_fancy_upsampling = 0;
311 
312     /* this gives another few percents */
313     cinfo.do_block_smoothing = 0;
314 
315     /* default format is RGB */
316     cinfo.out_color_space = JCS_RGB;
317 
318     SkBitmap::Config config = prefConfig;
319     // if no user preference, see what the device recommends
320     if (config == SkBitmap::kNo_Config)
321         config = SkImageDecoder::GetDeviceConfig();
322 
323     // only these make sense for jpegs
324     if (config != SkBitmap::kARGB_8888_Config &&
325         config != SkBitmap::kARGB_4444_Config &&
326         config != SkBitmap::kRGB_565_Config) {
327         config = SkBitmap::kARGB_8888_Config;
328     }
329 
330 #ifdef ANDROID_RGB
331     cinfo.dither_mode = JDITHER_NONE;
332     if (config == SkBitmap::kARGB_8888_Config) {
333         cinfo.out_color_space = JCS_RGBA_8888;
334     } else if (config == SkBitmap::kRGB_565_Config) {
335         if (sampleSize == 1) {
336             // SkScaledBitmapSampler can't handle RGB_565 yet,
337             // so don't even try.
338             cinfo.out_color_space = JCS_RGB_565;
339             if (this->getDitherImage()) {
340                 cinfo.dither_mode = JDITHER_ORDERED;
341             }
342         }
343     }
344 #endif
345 
346     if (sampleSize == 1 && mode == SkImageDecoder::kDecodeBounds_Mode) {
347         bm->setConfig(config, cinfo.image_width, cinfo.image_height);
348         bm->setIsOpaque(true);
349         return true;
350     }
351 
352     /*  image_width and image_height are the original dimensions, available
353         after jpeg_read_header(). To see the scaled dimensions, we have to call
354         jpeg_start_decompress(), and then read output_width and output_height.
355     */
356     if (!jpeg_start_decompress(&cinfo)) {
357         return return_false(cinfo, *bm, "start_decompress");
358     }
359 
360     /*  If we need to better match the request, we might examine the image and
361         output dimensions, and determine if the downsampling jpeg provided is
362         not sufficient. If so, we can recompute a modified sampleSize value to
363         make up the difference.
364 
365         To skip this additional scaling, just set sampleSize = 1; below.
366     */
367     sampleSize = sampleSize * cinfo.output_width / cinfo.image_width;
368 
369 
370     // should we allow the Chooser (if present) to pick a config for us???
371     if (!this->chooseFromOneChoice(config, cinfo.output_width,
372                                    cinfo.output_height)) {
373         return return_false(cinfo, *bm, "chooseFromOneChoice");
374     }
375 
376 #ifdef ANDROID_RGB
377     /* short-circuit the SkScaledBitmapSampler when possible, as this gives
378        a significant performance boost.
379     */
380     if (sampleSize == 1 &&
381         ((config == SkBitmap::kARGB_8888_Config &&
382                 cinfo.out_color_space == JCS_RGBA_8888) ||
383         (config == SkBitmap::kRGB_565_Config &&
384                 cinfo.out_color_space == JCS_RGB_565)))
385     {
386         bm->setConfig(config, cinfo.output_width, cinfo.output_height);
387         bm->setIsOpaque(true);
388         if (SkImageDecoder::kDecodeBounds_Mode == mode) {
389             return true;
390         }
391         if (!this->allocPixelRef(bm, NULL)) {
392             return return_false(cinfo, *bm, "allocPixelRef");
393         }
394         SkAutoLockPixels alp(*bm);
395         JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
396         INT32 const bpr =  bm->rowBytes();
397 
398         while (cinfo.output_scanline < cinfo.output_height) {
399             int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
400             // if row_count == 0, then we didn't get a scanline, so abort.
401             // if we supported partial images, we might return true in this case
402             if (0 == row_count) {
403                 return return_false(cinfo, *bm, "read_scanlines");
404             }
405             if (this->shouldCancelDecode()) {
406                 return return_false(cinfo, *bm, "shouldCancelDecode");
407             }
408             rowptr += bpr;
409         }
410         jpeg_finish_decompress(&cinfo);
411         return true;
412     }
413 #endif
414 
415     // check for supported formats
416     SkScaledBitmapSampler::SrcConfig sc;
417     if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
418         sc = SkScaledBitmapSampler::kRGB;
419 #ifdef ANDROID_RGB
420     } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
421         sc = SkScaledBitmapSampler::kRGBX;
422     //} else if (JCS_RGB_565 == cinfo.out_color_space) {
423     //    sc = SkScaledBitmapSampler::kRGB_565;
424 #endif
425     } else if (1 == cinfo.out_color_components &&
426                JCS_GRAYSCALE == cinfo.out_color_space) {
427         sc = SkScaledBitmapSampler::kGray;
428     } else {
429         return return_false(cinfo, *bm, "jpeg colorspace");
430     }
431 
432     SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height,
433                                   sampleSize);
434 
435     bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
436     // jpegs are always opauqe (i.e. have no per-pixel alpha)
437     bm->setIsOpaque(true);
438 
439     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
440         return true;
441     }
442     if (!this->allocPixelRef(bm, NULL)) {
443         return return_false(cinfo, *bm, "allocPixelRef");
444     }
445 
446     SkAutoLockPixels alp(*bm);
447     if (!sampler.begin(bm, sc, this->getDitherImage())) {
448         return return_false(cinfo, *bm, "sampler.begin");
449     }
450 
451     uint8_t* srcRow = (uint8_t*)srcStorage.alloc(cinfo.output_width * 4);
452 
453     //  Possibly skip initial rows [sampler.srcY0]
454     if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
455         return return_false(cinfo, *bm, "skip rows");
456     }
457 
458     // now loop through scanlines until y == bm->height() - 1
459     for (int y = 0;; y++) {
460         JSAMPLE* rowptr = (JSAMPLE*)srcRow;
461         int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
462         if (0 == row_count) {
463             return return_false(cinfo, *bm, "read_scanlines");
464         }
465         if (this->shouldCancelDecode()) {
466             return return_false(cinfo, *bm, "shouldCancelDecode");
467         }
468 
469         sampler.next(srcRow);
470         if (bm->height() - 1 == y) {
471             // we're done
472             break;
473         }
474 
475         if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
476             return return_false(cinfo, *bm, "skip rows");
477         }
478     }
479 
480     // we formally skip the rest, so we don't get a complaint from libjpeg
481     if (!skip_src_rows(&cinfo, srcRow,
482                        cinfo.output_height - cinfo.output_scanline)) {
483         return return_false(cinfo, *bm, "skip rows");
484     }
485     jpeg_finish_decompress(&cinfo);
486 
487 //    SkDebugf("------------------- bm2 size %d [%d %d] %d\n", bm->getSize(), bm->width(), bm->height(), bm->config());
488     return true;
489 }
490 
491 ///////////////////////////////////////////////////////////////////////////////
492 
493 #include "SkColorPriv.h"
494 
495 // taken from jcolor.c in libjpeg
496 #if 0   // 16bit - precise but slow
497     #define CYR     19595   // 0.299
498     #define CYG     38470   // 0.587
499     #define CYB      7471   // 0.114
500 
501     #define CUR    -11059   // -0.16874
502     #define CUG    -21709   // -0.33126
503     #define CUB     32768   // 0.5
504 
505     #define CVR     32768   // 0.5
506     #define CVG    -27439   // -0.41869
507     #define CVB     -5329   // -0.08131
508 
509     #define CSHIFT  16
510 #else      // 8bit - fast, slightly less precise
511     #define CYR     77    // 0.299
512     #define CYG     150    // 0.587
513     #define CYB      29    // 0.114
514 
515     #define CUR     -43    // -0.16874
516     #define CUG    -85    // -0.33126
517     #define CUB     128    // 0.5
518 
519     #define CVR      128   // 0.5
520     #define CVG     -107   // -0.41869
521     #define CVB      -21   // -0.08131
522 
523     #define CSHIFT  8
524 #endif
525 
rgb2yuv_32(uint8_t dst[],SkPMColor c)526 static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
527     int r = SkGetPackedR32(c);
528     int g = SkGetPackedG32(c);
529     int b = SkGetPackedB32(c);
530 
531     int  y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
532     int  u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
533     int  v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
534 
535     dst[0] = SkToU8(y);
536     dst[1] = SkToU8(u + 128);
537     dst[2] = SkToU8(v + 128);
538 }
539 
rgb2yuv_4444(uint8_t dst[],U16CPU c)540 static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
541     int r = SkGetPackedR4444(c);
542     int g = SkGetPackedG4444(c);
543     int b = SkGetPackedB4444(c);
544 
545     int  y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
546     int  u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
547     int  v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
548 
549     dst[0] = SkToU8(y);
550     dst[1] = SkToU8(u + 128);
551     dst[2] = SkToU8(v + 128);
552 }
553 
rgb2yuv_16(uint8_t dst[],U16CPU c)554 static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
555     int r = SkGetPackedR16(c);
556     int g = SkGetPackedG16(c);
557     int b = SkGetPackedB16(c);
558 
559     int  y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
560     int  u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
561     int  v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
562 
563     dst[0] = SkToU8(y);
564     dst[1] = SkToU8(u + 128);
565     dst[2] = SkToU8(v + 128);
566 }
567 
568 ///////////////////////////////////////////////////////////////////////////////
569 
570 typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
571                               const void* SK_RESTRICT src, int width,
572                               const SkPMColor* SK_RESTRICT ctable);
573 
Write_32_YUV(uint8_t * SK_RESTRICT dst,const void * SK_RESTRICT srcRow,int width,const SkPMColor *)574 static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
575                          const void* SK_RESTRICT srcRow, int width,
576                          const SkPMColor*) {
577     const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
578     while (--width >= 0) {
579 #ifdef WE_CONVERT_TO_YUV
580         rgb2yuv_32(dst, *src++);
581 #else
582         uint32_t c = *src++;
583         dst[0] = SkGetPackedR32(c);
584         dst[1] = SkGetPackedG32(c);
585         dst[2] = SkGetPackedB32(c);
586 #endif
587         dst += 3;
588     }
589 }
590 
Write_4444_YUV(uint8_t * SK_RESTRICT dst,const void * SK_RESTRICT srcRow,int width,const SkPMColor *)591 static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
592                            const void* SK_RESTRICT srcRow, int width,
593                            const SkPMColor*) {
594     const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
595     while (--width >= 0) {
596 #ifdef WE_CONVERT_TO_YUV
597         rgb2yuv_4444(dst, *src++);
598 #else
599         SkPMColor16 c = *src++;
600         dst[0] = SkPacked4444ToR32(c);
601         dst[1] = SkPacked4444ToG32(c);
602         dst[2] = SkPacked4444ToB32(c);
603 #endif
604         dst += 3;
605     }
606 }
607 
Write_16_YUV(uint8_t * SK_RESTRICT dst,const void * SK_RESTRICT srcRow,int width,const SkPMColor *)608 static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
609                          const void* SK_RESTRICT srcRow, int width,
610                          const SkPMColor*) {
611     const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
612     while (--width >= 0) {
613 #ifdef WE_CONVERT_TO_YUV
614         rgb2yuv_16(dst, *src++);
615 #else
616         uint16_t c = *src++;
617         dst[0] = SkPacked16ToR32(c);
618         dst[1] = SkPacked16ToG32(c);
619         dst[2] = SkPacked16ToB32(c);
620 #endif
621         dst += 3;
622     }
623 }
624 
Write_Index_YUV(uint8_t * SK_RESTRICT dst,const void * SK_RESTRICT srcRow,int width,const SkPMColor * SK_RESTRICT ctable)625 static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
626                             const void* SK_RESTRICT srcRow, int width,
627                             const SkPMColor* SK_RESTRICT ctable) {
628     const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
629     while (--width >= 0) {
630 #ifdef WE_CONVERT_TO_YUV
631         rgb2yuv_32(dst, ctable[*src++]);
632 #else
633         uint32_t c = ctable[*src++];
634         dst[0] = SkGetPackedR32(c);
635         dst[1] = SkGetPackedG32(c);
636         dst[2] = SkGetPackedB32(c);
637 #endif
638         dst += 3;
639     }
640 }
641 
ChooseWriter(const SkBitmap & bm)642 static WriteScanline ChooseWriter(const SkBitmap& bm) {
643     switch (bm.config()) {
644         case SkBitmap::kARGB_8888_Config:
645             return Write_32_YUV;
646         case SkBitmap::kRGB_565_Config:
647             return Write_16_YUV;
648         case SkBitmap::kARGB_4444_Config:
649             return Write_4444_YUV;
650         case SkBitmap::kIndex8_Config:
651             return Write_Index_YUV;
652         default:
653             return NULL;
654     }
655 }
656 
657 struct sk_destination_mgr : jpeg_destination_mgr {
658     sk_destination_mgr(SkWStream* stream);
659 
660     SkWStream*  fStream;
661 
662     enum {
663         kBufferSize = 1024
664     };
665     uint8_t fBuffer[kBufferSize];
666 };
667 
sk_init_destination(j_compress_ptr cinfo)668 static void sk_init_destination(j_compress_ptr cinfo) {
669     sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest;
670 
671     dest->next_output_byte = dest->fBuffer;
672     dest->free_in_buffer = sk_destination_mgr::kBufferSize;
673 }
674 
sk_empty_output_buffer(j_compress_ptr cinfo)675 static boolean sk_empty_output_buffer(j_compress_ptr cinfo) {
676     sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest;
677 
678 //  if (!dest->fStream->write(dest->fBuffer, sk_destination_mgr::kBufferSize - dest->free_in_buffer))
679     if (!dest->fStream->write(dest->fBuffer, sk_destination_mgr::kBufferSize)) {
680         ERREXIT(cinfo, JERR_FILE_WRITE);
681         return false;
682     }
683 
684     dest->next_output_byte = dest->fBuffer;
685     dest->free_in_buffer = sk_destination_mgr::kBufferSize;
686     return TRUE;
687 }
688 
sk_term_destination(j_compress_ptr cinfo)689 static void sk_term_destination (j_compress_ptr cinfo) {
690     sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest;
691 
692     size_t size = sk_destination_mgr::kBufferSize - dest->free_in_buffer;
693     if (size > 0) {
694         if (!dest->fStream->write(dest->fBuffer, size)) {
695             ERREXIT(cinfo, JERR_FILE_WRITE);
696             return;
697         }
698     }
699     dest->fStream->flush();
700 }
701 
sk_destination_mgr(SkWStream * stream)702 sk_destination_mgr::sk_destination_mgr(SkWStream* stream)
703         : fStream(stream) {
704     this->init_destination = sk_init_destination;
705     this->empty_output_buffer = sk_empty_output_buffer;
706     this->term_destination = sk_term_destination;
707 }
708 
709 class SkJPEGImageEncoder : public SkImageEncoder {
710 protected:
onEncode(SkWStream * stream,const SkBitmap & bm,int quality)711     virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
712 #ifdef TIME_ENCODE
713         AutoTimeMillis atm("JPEG Encode");
714 #endif
715 
716         const WriteScanline writer = ChooseWriter(bm);
717         if (NULL == writer) {
718             return false;
719         }
720 
721         SkAutoLockPixels alp(bm);
722         if (NULL == bm.getPixels()) {
723             return false;
724         }
725 
726         jpeg_compress_struct    cinfo;
727         sk_error_mgr            sk_err;
728         sk_destination_mgr      sk_wstream(stream);
729 
730         // allocate these before set call setjmp
731         SkAutoMalloc    oneRow;
732         SkAutoLockColors ctLocker;
733 
734         cinfo.err = jpeg_std_error(&sk_err);
735         sk_err.error_exit = sk_error_exit;
736         if (setjmp(sk_err.fJmpBuf)) {
737             return false;
738         }
739         jpeg_create_compress(&cinfo);
740 
741         cinfo.dest = &sk_wstream;
742         cinfo.image_width = bm.width();
743         cinfo.image_height = bm.height();
744         cinfo.input_components = 3;
745 #ifdef WE_CONVERT_TO_YUV
746         cinfo.in_color_space = JCS_YCbCr;
747 #else
748         cinfo.in_color_space = JCS_RGB;
749 #endif
750         cinfo.input_gamma = 1;
751 
752         jpeg_set_defaults(&cinfo);
753         jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
754         cinfo.dct_method = JDCT_IFAST;
755 
756         jpeg_start_compress(&cinfo, TRUE);
757 
758         const int       width = bm.width();
759         uint8_t*        oneRowP = (uint8_t*)oneRow.alloc(width * 3);
760 
761         const SkPMColor* colors = ctLocker.lockColors(bm);
762         const void*      srcRow = bm.getPixels();
763 
764         while (cinfo.next_scanline < cinfo.image_height) {
765             JSAMPROW row_pointer[1];    /* pointer to JSAMPLE row[s] */
766 
767             writer(oneRowP, srcRow, width, colors);
768             row_pointer[0] = oneRowP;
769             (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
770             srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
771         }
772 
773         jpeg_finish_compress(&cinfo);
774         jpeg_destroy_compress(&cinfo);
775 
776         return true;
777     }
778 };
779 
780 ///////////////////////////////////////////////////////////////////////////////
781 
782 #include "SkTRegistry.h"
783 
DFactory(SkStream * stream)784 static SkImageDecoder* DFactory(SkStream* stream) {
785     static const char gHeader[] = { 0xFF, 0xD8, 0xFF };
786     static const size_t HEADER_SIZE = sizeof(gHeader);
787 
788     char buffer[HEADER_SIZE];
789     size_t len = stream->read(buffer, HEADER_SIZE);
790 
791     if (len != HEADER_SIZE) {
792         return NULL;   // can't read enough
793     }
794     if (memcmp(buffer, gHeader, HEADER_SIZE)) {
795         return NULL;
796     }
797     return SkNEW(SkJPEGImageDecoder);
798 }
799 
EFactory(SkImageEncoder::Type t)800 static SkImageEncoder* EFactory(SkImageEncoder::Type t) {
801     return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
802 }
803 
804 static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(DFactory);
805 static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(EFactory);
806 
807