blob: 6a032fdb9626703ffe3d8a669b497759339d6bd0 [file] [log] [blame]
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001/*
2 * Copyright 2007 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8
9#include "SkImageDecoder.h"
10#include "SkImageEncoder.h"
scroggo27631bd2015-06-15 09:10:03 -070011#include "SkJpegUtility.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000012#include "SkColorPriv.h"
13#include "SkDither.h"
14#include "SkScaledBitmapSampler.h"
15#include "SkStream.h"
16#include "SkTemplates.h"
djsollen@google.com11399402013-03-20 17:45:27 +000017#include "SkTime.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000018#include "SkUtils.h"
halcanary@google.com2a103182013-10-14 12:49:15 +000019#include "SkRTConf.h"
djsollen@google.com11399402013-03-20 17:45:27 +000020#include "SkRect.h"
21#include "SkCanvas.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000022
halcanary@google.comfed30372013-10-04 12:46:45 +000023
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000024#include <stdio.h>
25extern "C" {
26 #include "jpeglib.h"
27 #include "jerror.h"
28}
29
djsollen@google.com11399402013-03-20 17:45:27 +000030// These enable timing code that report milliseconds for an encoding/decoding
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000031//#define TIME_ENCODE
32//#define TIME_DECODE
33
34// this enables our rgb->yuv code, which is faster than libjpeg on ARM
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000035#define WE_CONVERT_TO_YUV
36
djsollen@google.com11399402013-03-20 17:45:27 +000037// If ANDROID_RGB is defined by in the jpeg headers it indicates that jpeg offers
38// support for two additional formats (1) JCS_RGBA_8888 and (2) JCS_RGB_565.
39
halcanary@google.com2a103182013-10-14 12:49:15 +000040#define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS true
halcanary@google.com04b57f82013-10-14 20:08:48 +000041#define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_ERRORS true
halcanary@google.com2a103182013-10-14 12:49:15 +000042SK_CONF_DECLARE(bool, c_suppressJPEGImageDecoderWarnings,
43 "images.jpeg.suppressDecoderWarnings",
44 DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS,
45 "Suppress most JPG warnings when calling decode functions.");
halcanary@google.com04b57f82013-10-14 20:08:48 +000046SK_CONF_DECLARE(bool, c_suppressJPEGImageDecoderErrors,
47 "images.jpeg.suppressDecoderErrors",
48 DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_ERRORS,
49 "Suppress most JPG error messages when decode "
50 "function fails.");
halcanary@google.comfed30372013-10-04 12:46:45 +000051
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000052//////////////////////////////////////////////////////////////////////////
53//////////////////////////////////////////////////////////////////////////
54
halcanary@google.comfed30372013-10-04 12:46:45 +000055static void do_nothing_emit_message(jpeg_common_struct*, int) {
56 /* do nothing */
57}
halcanary@google.com04b57f82013-10-14 20:08:48 +000058static void do_nothing_output_message(j_common_ptr) {
59 /* do nothing */
60}
halcanary@google.comfed30372013-10-04 12:46:45 +000061
scroggo@google.com590a5af2013-08-07 21:09:13 +000062static void initialize_info(jpeg_decompress_struct* cinfo, skjpeg_source_mgr* src_mgr) {
halcanary96fcdcc2015-08-27 07:41:13 -070063 SkASSERT(cinfo != nullptr);
64 SkASSERT(src_mgr != nullptr);
scroggo@google.com590a5af2013-08-07 21:09:13 +000065 jpeg_create_decompress(cinfo);
scroggo@google.com590a5af2013-08-07 21:09:13 +000066 cinfo->src = src_mgr;
halcanary@google.comfed30372013-10-04 12:46:45 +000067 /* To suppress warnings with a SK_DEBUG binary, set the
68 * environment variable "skia_images_jpeg_suppressDecoderWarnings"
69 * to "true". Inside a program that links to skia:
70 * SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true); */
71 if (c_suppressJPEGImageDecoderWarnings) {
72 cinfo->err->emit_message = &do_nothing_emit_message;
73 }
halcanary@google.com04b57f82013-10-14 20:08:48 +000074 /* To suppress error messages with a SK_DEBUG binary, set the
75 * environment variable "skia_images_jpeg_suppressDecoderErrors"
76 * to "true". Inside a program that links to skia:
77 * SK_CONF_SET("images.jpeg.suppressDecoderErrors", true); */
78 if (c_suppressJPEGImageDecoderErrors) {
79 cinfo->err->output_message = &do_nothing_output_message;
80 }
scroggo@google.com590a5af2013-08-07 21:09:13 +000081}
82
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000083class SkJPEGImageDecoder : public SkImageDecoder {
84public:
djsollen@google.com11399402013-03-20 17:45:27 +000085
mtklein36352bf2015-03-25 18:17:31 -070086 Format getFormat() const override {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000087 return kJPEG_Format;
88 }
89
90protected:
mtklein36352bf2015-03-25 18:17:31 -070091 Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
tfarina7831a4b2015-04-27 07:53:07 -070092 bool onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
93 void* planes[3], size_t rowBytes[3],
94 SkYUVColorSpace* colorSpace) override;
djsollen@google.com11399402013-03-20 17:45:27 +000095
96private:
djsollen@google.com11399402013-03-20 17:45:27 +000097
scroggo@google.com590a5af2013-08-07 21:09:13 +000098 /**
reed6c225732014-06-09 19:52:07 -070099 * Determine the appropriate bitmap colortype and out_color_space based on
scroggo@google.com590a5af2013-08-07 21:09:13 +0000100 * both the preference of the caller and the jpeg_color_space on the
101 * jpeg_decompress_struct passed in.
102 * Must be called after jpeg_read_header.
103 */
reed6c225732014-06-09 19:52:07 -0700104 SkColorType getBitmapColorType(jpeg_decompress_struct*);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000105
djsollen@google.com11399402013-03-20 17:45:27 +0000106 typedef SkImageDecoder INHERITED;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000107};
108
109//////////////////////////////////////////////////////////////////////////
110
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000111/* Automatically clean up after throwing an exception */
112class JPEGAutoClean {
113public:
halcanary96fcdcc2015-08-27 07:41:13 -0700114 JPEGAutoClean(): cinfo_ptr(nullptr) {}
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000115 ~JPEGAutoClean() {
116 if (cinfo_ptr) {
117 jpeg_destroy_decompress(cinfo_ptr);
118 }
119 }
120 void set(jpeg_decompress_struct* info) {
121 cinfo_ptr = info;
122 }
123private:
124 jpeg_decompress_struct* cinfo_ptr;
125};
126
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000127///////////////////////////////////////////////////////////////////////////////
128
129/* If we need to better match the request, we might examine the image and
130 output dimensions, and determine if the downsampling jpeg provided is
131 not sufficient. If so, we can recompute a modified sampleSize value to
132 make up the difference.
133
134 To skip this additional scaling, just set sampleSize = 1; below.
135 */
136static int recompute_sampleSize(int sampleSize,
137 const jpeg_decompress_struct& cinfo) {
138 return sampleSize * cinfo.output_width / cinfo.image_width;
139}
140
141static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) {
142 /* These are initialized to 0, so if they have non-zero values, we assume
143 they are "valid" (i.e. have been computed by libjpeg)
144 */
djsollen@google.com11399402013-03-20 17:45:27 +0000145 return 0 != cinfo.output_width && 0 != cinfo.output_height;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000146}
147
djsollen@google.com11399402013-03-20 17:45:27 +0000148static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, int count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000149 for (int i = 0; i < count; i++) {
150 JSAMPLE* rowptr = (JSAMPLE*)buffer;
151 int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
djsollen@google.com11399402013-03-20 17:45:27 +0000152 if (1 != row_count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000153 return false;
154 }
155 }
156 return true;
157}
158
scroggo2a120802014-10-22 12:07:00 -0700159///////////////////////////////////////////////////////////////////////////////
160
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000161// This guy exists just to aid in debugging, as it allows debuggers to just
162// set a break-point in one place to see all error exists.
scroggo2a120802014-10-22 12:07:00 -0700163static void print_jpeg_decoder_errors(const jpeg_decompress_struct& cinfo,
sugoib227e372014-10-16 13:10:57 -0700164 int width, int height, const char caller[]) {
halcanary@google.com04b57f82013-10-14 20:08:48 +0000165 if (!(c_suppressJPEGImageDecoderErrors)) {
166 char buffer[JMSG_LENGTH_MAX];
167 cinfo.err->format_message((const j_common_ptr)&cinfo, buffer);
168 SkDebugf("libjpeg error %d <%s> from %s [%d %d]\n",
sugoib227e372014-10-16 13:10:57 -0700169 cinfo.err->msg_code, buffer, caller, width, height);
halcanary@google.com04b57f82013-10-14 20:08:48 +0000170 }
scroggo2a120802014-10-22 12:07:00 -0700171}
172
173static bool return_false(const jpeg_decompress_struct& cinfo,
174 const char caller[]) {
175 print_jpeg_decoder_errors(cinfo, 0, 0, caller);
176 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000177}
178
scroggo2a120802014-10-22 12:07:00 -0700179static SkImageDecoder::Result return_failure(const jpeg_decompress_struct& cinfo,
180 const SkBitmap& bm, const char caller[]) {
181 print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller);
182 return SkImageDecoder::kFailure;
183}
184
185///////////////////////////////////////////////////////////////////////////////
186
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000187// Convert a scanline of CMYK samples to RGBX in place. Note that this
188// method moves the "scanline" pointer in its processing
189static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
190 // At this point we've received CMYK pixels from libjpeg. We
rmistry@google.comd6176b02012-08-23 18:14:13 +0000191 // perform a crude conversion to RGB (based on the formulae
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000192 // from easyrgb.com):
193 // CMYK -> CMY
194 // C = ( C * (1 - K) + K ) // for each CMY component
195 // CMY -> RGB
196 // R = ( 1 - C ) * 255 // for each RGB component
197 // Unfortunately we are seeing inverted CMYK so all the original terms
198 // are 1-. This yields:
199 // CMYK -> CMY
200 // C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K
201 // The conversion from CMY->RGB remains the same
202 for (unsigned int x = 0; x < width; ++x, scanline += 4) {
203 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]);
204 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]);
205 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]);
206 scanline[3] = 255;
207 }
208}
209
scroggo@google.com590a5af2013-08-07 21:09:13 +0000210/**
211 * Common code for setting the error manager.
212 */
213static void set_error_mgr(jpeg_decompress_struct* cinfo, skjpeg_error_mgr* errorManager) {
halcanary96fcdcc2015-08-27 07:41:13 -0700214 SkASSERT(cinfo != nullptr);
215 SkASSERT(errorManager != nullptr);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000216 cinfo->err = jpeg_std_error(errorManager);
217 errorManager->error_exit = skjpeg_error_exit;
218}
219
220/**
scroggo@google.com590a5af2013-08-07 21:09:13 +0000221 * Common code for setting the dct method.
222 */
223static void set_dct_method(const SkImageDecoder& decoder, jpeg_decompress_struct* cinfo) {
halcanary96fcdcc2015-08-27 07:41:13 -0700224 SkASSERT(cinfo != nullptr);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000225 cinfo->dct_method = JDCT_ISLOW;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000226}
227
reed6c225732014-06-09 19:52:07 -0700228SkColorType SkJPEGImageDecoder::getBitmapColorType(jpeg_decompress_struct* cinfo) {
halcanary96fcdcc2015-08-27 07:41:13 -0700229 SkASSERT(cinfo != nullptr);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000230
231 SrcDepth srcDepth = k32Bit_SrcDepth;
232 if (JCS_GRAYSCALE == cinfo->jpeg_color_space) {
233 srcDepth = k8BitGray_SrcDepth;
234 }
235
reed6c225732014-06-09 19:52:07 -0700236 SkColorType colorType = this->getPrefColorType(srcDepth, /*hasAlpha*/ false);
237 switch (colorType) {
238 case kAlpha_8_SkColorType:
239 // Only respect A8 colortype if the original is grayscale,
scroggo@google.com590a5af2013-08-07 21:09:13 +0000240 // in which case we will treat the grayscale as alpha
241 // values.
242 if (cinfo->jpeg_color_space != JCS_GRAYSCALE) {
reed6c225732014-06-09 19:52:07 -0700243 colorType = kN32_SkColorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000244 }
245 break;
reed6c225732014-06-09 19:52:07 -0700246 case kN32_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000247 // Fall through.
reed6c225732014-06-09 19:52:07 -0700248 case kARGB_4444_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000249 // Fall through.
reed6c225732014-06-09 19:52:07 -0700250 case kRGB_565_SkColorType:
251 // These are acceptable destination colortypes.
scroggo@google.com590a5af2013-08-07 21:09:13 +0000252 break;
253 default:
reed6c225732014-06-09 19:52:07 -0700254 // Force all other colortypes to 8888.
255 colorType = kN32_SkColorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000256 break;
257 }
258
259 switch (cinfo->jpeg_color_space) {
260 case JCS_CMYK:
261 // Fall through.
262 case JCS_YCCK:
263 // libjpeg cannot convert from CMYK or YCCK to RGB - here we set up
264 // so libjpeg will give us CMYK samples back and we will later
265 // manually convert them to RGB
266 cinfo->out_color_space = JCS_CMYK;
267 break;
268 case JCS_GRAYSCALE:
reed6c225732014-06-09 19:52:07 -0700269 if (kAlpha_8_SkColorType == colorType) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000270 cinfo->out_color_space = JCS_GRAYSCALE;
271 break;
272 }
273 // The data is JCS_GRAYSCALE, but the caller wants some sort of RGB
reed6c225732014-06-09 19:52:07 -0700274 // colortype. Fall through to set to the default.
scroggo@google.com590a5af2013-08-07 21:09:13 +0000275 default:
276 cinfo->out_color_space = JCS_RGB;
277 break;
278 }
reed6c225732014-06-09 19:52:07 -0700279 return colorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000280}
281
scroggo@google.com590a5af2013-08-07 21:09:13 +0000282/**
reed6c225732014-06-09 19:52:07 -0700283 * Based on the colortype and dither mode, adjust out_color_space and
284 * dither_mode of cinfo. Only does work in ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000285 */
286static void adjust_out_color_space_and_dither(jpeg_decompress_struct* cinfo,
reed6c225732014-06-09 19:52:07 -0700287 SkColorType colorType,
scroggo@google.com590a5af2013-08-07 21:09:13 +0000288 const SkImageDecoder& decoder) {
halcanary96fcdcc2015-08-27 07:41:13 -0700289 SkASSERT(cinfo != nullptr);
reed6c225732014-06-09 19:52:07 -0700290#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000291 cinfo->dither_mode = JDITHER_NONE;
292 if (JCS_CMYK == cinfo->out_color_space) {
293 return;
294 }
reed6c225732014-06-09 19:52:07 -0700295 switch (colorType) {
296 case kN32_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000297 cinfo->out_color_space = JCS_RGBA_8888;
298 break;
reed6c225732014-06-09 19:52:07 -0700299 case kRGB_565_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000300 cinfo->out_color_space = JCS_RGB_565;
301 if (decoder.getDitherImage()) {
302 cinfo->dither_mode = JDITHER_ORDERED;
303 }
304 break;
305 default:
306 break;
307 }
scroggo@google.com590a5af2013-08-07 21:09:13 +0000308#endif
reed6c225732014-06-09 19:52:07 -0700309}
scroggo@google.com590a5af2013-08-07 21:09:13 +0000310
halcanary@google.comfed30372013-10-04 12:46:45 +0000311/**
312 Sets all pixels in given bitmap to SK_ColorWHITE for all rows >= y.
313 Used when decoding fails partway through reading scanlines to fill
314 remaining lines. */
315static void fill_below_level(int y, SkBitmap* bitmap) {
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000316 SkIRect rect = SkIRect::MakeLTRB(0, y, bitmap->width(), bitmap->height());
halcanary@google.comfed30372013-10-04 12:46:45 +0000317 SkCanvas canvas(*bitmap);
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000318 canvas.clipRect(SkRect::Make(rect));
halcanary@google.comfed30372013-10-04 12:46:45 +0000319 canvas.drawColor(SK_ColorWHITE);
320}
321
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000322/**
323 * Get the config and bytes per pixel of the source data. Return
324 * whether the data is supported.
325 */
326static bool get_src_config(const jpeg_decompress_struct& cinfo,
327 SkScaledBitmapSampler::SrcConfig* sc,
328 int* srcBytesPerPixel) {
halcanary96fcdcc2015-08-27 07:41:13 -0700329 SkASSERT(sc != nullptr && srcBytesPerPixel != nullptr);
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000330 if (JCS_CMYK == cinfo.out_color_space) {
331 // In this case we will manually convert the CMYK values to RGB
332 *sc = SkScaledBitmapSampler::kRGBX;
333 // The CMYK work-around relies on 4 components per pixel here
334 *srcBytesPerPixel = 4;
335 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
336 *sc = SkScaledBitmapSampler::kRGB;
337 *srcBytesPerPixel = 3;
338#ifdef ANDROID_RGB
339 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
340 *sc = SkScaledBitmapSampler::kRGBX;
341 *srcBytesPerPixel = 4;
342 } else if (JCS_RGB_565 == cinfo.out_color_space) {
343 *sc = SkScaledBitmapSampler::kRGB_565;
344 *srcBytesPerPixel = 2;
345#endif
346 } else if (1 == cinfo.out_color_components &&
347 JCS_GRAYSCALE == cinfo.out_color_space) {
348 *sc = SkScaledBitmapSampler::kGray;
349 *srcBytesPerPixel = 1;
350 } else {
351 return false;
352 }
353 return true;
354}
halcanary@google.comfed30372013-10-04 12:46:45 +0000355
scroggo2a120802014-10-22 12:07:00 -0700356SkImageDecoder::Result SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000357#ifdef TIME_DECODE
djsollen@google.com11399402013-03-20 17:45:27 +0000358 SkAutoTime atm("JPEG Decode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000359#endif
360
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000361 JPEGAutoClean autoClean;
362
363 jpeg_decompress_struct cinfo;
scroggo@google.comd4c35652013-08-01 15:03:42 +0000364 skjpeg_source_mgr srcManager(stream, this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000365
scroggo@google.com590a5af2013-08-07 21:09:13 +0000366 skjpeg_error_mgr errorManager;
367 set_error_mgr(&cinfo, &errorManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000368
369 // All objects need to be instantiated before this setjmp call so that
370 // they will be cleaned up properly if an error occurs.
djsollen@google.com11399402013-03-20 17:45:27 +0000371 if (setjmp(errorManager.fJmpBuf)) {
scroggo2a120802014-10-22 12:07:00 -0700372 return return_failure(cinfo, *bm, "setjmp");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000373 }
374
scroggo@google.com590a5af2013-08-07 21:09:13 +0000375 initialize_info(&cinfo, &srcManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000376 autoClean.set(&cinfo);
377
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000378 int status = jpeg_read_header(&cinfo, true);
379 if (status != JPEG_HEADER_OK) {
scroggo2a120802014-10-22 12:07:00 -0700380 return return_failure(cinfo, *bm, "read_header");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000381 }
382
383 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
384 can) much faster that we, just use their num/denom api to approximate
385 the size.
386 */
387 int sampleSize = this->getSampleSize();
388
scroggo@google.com590a5af2013-08-07 21:09:13 +0000389 set_dct_method(*this, &cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000390
scroggo@google.com590a5af2013-08-07 21:09:13 +0000391 SkASSERT(1 == cinfo.scale_num);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000392 cinfo.scale_denom = sampleSize;
393
reed6c225732014-06-09 19:52:07 -0700394 const SkColorType colorType = this->getBitmapColorType(&cinfo);
395 const SkAlphaType alphaType = kAlpha_8_SkColorType == colorType ?
396 kPremul_SkAlphaType : kOpaque_SkAlphaType;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000397
reed6c225732014-06-09 19:52:07 -0700398 adjust_out_color_space_and_dither(&cinfo, colorType, *this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000399
djsollen@google.com11399402013-03-20 17:45:27 +0000400 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000401 // Assume an A8 bitmap is not opaque to avoid the check of each
402 // individual pixel. It is very unlikely to be opaque, since
403 // an opaque A8 bitmap would not be very interesting.
404 // Otherwise, a jpeg image is opaque.
scroggo2a120802014-10-22 12:07:00 -0700405 bool success = bm->setInfo(SkImageInfo::Make(cinfo.image_width, cinfo.image_height,
406 colorType, alphaType));
407 return success ? kSuccess : kFailure;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000408 }
409
410 /* image_width and image_height are the original dimensions, available
411 after jpeg_read_header(). To see the scaled dimensions, we have to call
412 jpeg_start_decompress(), and then read output_width and output_height.
413 */
414 if (!jpeg_start_decompress(&cinfo)) {
415 /* If we failed here, we may still have enough information to return
416 to the caller if they just wanted (subsampled bounds). If sampleSize
417 was 1, then we would have already returned. Thus we just check if
418 we're in kDecodeBounds_Mode, and that we have valid output sizes.
419
420 One reason to fail here is that we have insufficient stream data
421 to complete the setup. However, output dimensions seem to get
422 computed very early, which is why this special check can pay off.
423 */
djsollen@google.com11399402013-03-20 17:45:27 +0000424 if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000425 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
426 recompute_sampleSize(sampleSize, cinfo));
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000427 // Assume an A8 bitmap is not opaque to avoid the check of each
428 // individual pixel. It is very unlikely to be opaque, since
429 // an opaque A8 bitmap would not be very interesting.
430 // Otherwise, a jpeg image is opaque.
scroggo2a120802014-10-22 12:07:00 -0700431 bool success = bm->setInfo(SkImageInfo::Make(smpl.scaledWidth(), smpl.scaledHeight(),
432 colorType, alphaType));
433 return success ? kSuccess : kFailure;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000434 } else {
scroggo2a120802014-10-22 12:07:00 -0700435 return return_failure(cinfo, *bm, "start_decompress");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000436 }
437 }
438 sampleSize = recompute_sampleSize(sampleSize, cinfo);
439
djsollen@google.com11399402013-03-20 17:45:27 +0000440 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000441 // Assume an A8 bitmap is not opaque to avoid the check of each
442 // individual pixel. It is very unlikely to be opaque, since
443 // an opaque A8 bitmap would not be very interesting.
444 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -0700445 bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),
446 colorType, alphaType));
scroggo@google.combc69ce92013-07-09 15:45:14 +0000447 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
scroggo2a120802014-10-22 12:07:00 -0700448 return kSuccess;
scroggo@google.combc69ce92013-07-09 15:45:14 +0000449 }
halcanary96fcdcc2015-08-27 07:41:13 -0700450 if (!this->allocPixelRef(bm, nullptr)) {
scroggo2a120802014-10-22 12:07:00 -0700451 return return_failure(cinfo, *bm, "allocPixelRef");
djsollen@google.com11399402013-03-20 17:45:27 +0000452 }
453
454 SkAutoLockPixels alp(*bm);
455
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000456#ifdef ANDROID_RGB
457 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
458 a significant performance boost.
459 */
460 if (sampleSize == 1 &&
reed6c225732014-06-09 19:52:07 -0700461 ((kN32_SkColorType == colorType && cinfo.out_color_space == JCS_RGBA_8888) ||
462 (kRGB_565_SkColorType == colorType && cinfo.out_color_space == JCS_RGB_565)))
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000463 {
scroggo@google.combc69ce92013-07-09 15:45:14 +0000464 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000465 INT32 const bpr = bm->rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000466
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000467 while (cinfo.output_scanline < cinfo.output_height) {
468 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000469 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000470 // if row_count == 0, then we didn't get a scanline,
471 // so return early. We will return a partial image.
472 fill_below_level(cinfo.output_scanline, bm);
473 cinfo.output_scanline = cinfo.output_height;
scroggo2a120802014-10-22 12:07:00 -0700474 jpeg_finish_decompress(&cinfo);
475 return kPartialSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000476 }
477 if (this->shouldCancelDecode()) {
scroggo2a120802014-10-22 12:07:00 -0700478 return return_failure(cinfo, *bm, "shouldCancelDecode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000479 }
480 rowptr += bpr;
481 }
482 jpeg_finish_decompress(&cinfo);
scroggo2a120802014-10-22 12:07:00 -0700483 return kSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000484 }
485#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000486
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000487 // check for supported formats
488 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000489 int srcBytesPerPixel;
490
491 if (!get_src_config(cinfo, &sc, &srcBytesPerPixel)) {
scroggo2a120802014-10-22 12:07:00 -0700492 return return_failure(cinfo, *bm, "jpeg colorspace");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000493 }
494
scroggo@google.com8d239242013-10-01 17:27:15 +0000495 if (!sampler.begin(bm, sc, *this)) {
scroggo2a120802014-10-22 12:07:00 -0700496 return return_failure(cinfo, *bm, "sampler.begin");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000497 }
498
scroggo565901d2015-12-10 10:44:13 -0800499 SkAutoTMalloc<uint8_t> srcStorage(cinfo.output_width * srcBytesPerPixel);
500 uint8_t* srcRow = srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000501
502 // Possibly skip initial rows [sampler.srcY0]
503 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
scroggo2a120802014-10-22 12:07:00 -0700504 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000505 }
506
507 // now loop through scanlines until y == bm->height() - 1
508 for (int y = 0;; y++) {
509 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
510 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
511 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000512 // if row_count == 0, then we didn't get a scanline,
513 // so return early. We will return a partial image.
514 fill_below_level(y, bm);
515 cinfo.output_scanline = cinfo.output_height;
scroggo2a120802014-10-22 12:07:00 -0700516 jpeg_finish_decompress(&cinfo);
scroggoc6b8ffa2014-12-17 06:55:02 -0800517 return kPartialSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000518 }
519 if (this->shouldCancelDecode()) {
scroggo2a120802014-10-22 12:07:00 -0700520 return return_failure(cinfo, *bm, "shouldCancelDecode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000521 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000522
523 if (JCS_CMYK == cinfo.out_color_space) {
524 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
525 }
526
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000527 sampler.next(srcRow);
528 if (bm->height() - 1 == y) {
529 // we're done
530 break;
531 }
532
533 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
scroggo2a120802014-10-22 12:07:00 -0700534 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000535 }
536 }
537
538 // we formally skip the rest, so we don't get a complaint from libjpeg
539 if (!skip_src_rows(&cinfo, srcRow,
540 cinfo.output_height - cinfo.output_scanline)) {
scroggo2a120802014-10-22 12:07:00 -0700541 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000542 }
543 jpeg_finish_decompress(&cinfo);
544
scroggo2a120802014-10-22 12:07:00 -0700545 return kSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000546}
547
scroggo2a120802014-10-22 12:07:00 -0700548///////////////////////////////////////////////////////////////////////////////
549
sugoib227e372014-10-16 13:10:57 -0700550enum SizeType {
551 kSizeForMemoryAllocation_SizeType,
552 kActualSize_SizeType
553};
554
555static SkISize compute_yuv_size(const jpeg_decompress_struct& info, int component,
556 SizeType sizeType) {
557 if (sizeType == kSizeForMemoryAllocation_SizeType) {
558 return SkISize::Make(info.cur_comp_info[component]->width_in_blocks * DCTSIZE,
559 info.cur_comp_info[component]->height_in_blocks * DCTSIZE);
560 }
561 return SkISize::Make(info.cur_comp_info[component]->downsampled_width,
562 info.cur_comp_info[component]->downsampled_height);
563}
564
mtkleinb3e5e4d2015-03-25 13:13:43 -0700565static bool appears_to_be_yuv(const jpeg_decompress_struct& info) {
566 return (info.jpeg_color_space == JCS_YCbCr)
567 && (DCTSIZE == 8)
568 && (info.num_components == 3)
569 && (info.comps_in_scan >= info.num_components)
570 && (info.scale_denom <= 8)
571 && (info.cur_comp_info[0])
572 && (info.cur_comp_info[1])
573 && (info.cur_comp_info[2])
574 && (info.cur_comp_info[1]->h_samp_factor == 1)
575 && (info.cur_comp_info[1]->v_samp_factor == 1)
576 && (info.cur_comp_info[2]->h_samp_factor == 1)
577 && (info.cur_comp_info[2]->v_samp_factor == 1);
578}
579
sugoib227e372014-10-16 13:10:57 -0700580static void update_components_sizes(const jpeg_decompress_struct& cinfo, SkISize componentSizes[3],
581 SizeType sizeType) {
mtkleinb3e5e4d2015-03-25 13:13:43 -0700582 SkASSERT(appears_to_be_yuv(cinfo));
sugoib227e372014-10-16 13:10:57 -0700583 for (int i = 0; i < 3; ++i) {
584 componentSizes[i] = compute_yuv_size(cinfo, i, sizeType);
585 }
586}
587
588static bool output_raw_data(jpeg_decompress_struct& cinfo, void* planes[3], size_t rowBytes[3]) {
mtkleinb3e5e4d2015-03-25 13:13:43 -0700589 SkASSERT(appears_to_be_yuv(cinfo));
sugoib227e372014-10-16 13:10:57 -0700590 // U size and V size have to be the same if we're calling output_raw_data()
591 SkISize uvSize = compute_yuv_size(cinfo, 1, kSizeForMemoryAllocation_SizeType);
592 SkASSERT(uvSize == compute_yuv_size(cinfo, 2, kSizeForMemoryAllocation_SizeType));
593
594 JSAMPARRAY bufferraw[3];
595 JSAMPROW bufferraw2[32];
596 bufferraw[0] = &bufferraw2[0]; // Y channel rows (8 or 16)
597 bufferraw[1] = &bufferraw2[16]; // U channel rows (8)
598 bufferraw[2] = &bufferraw2[24]; // V channel rows (8)
599 int yWidth = cinfo.output_width;
600 int yHeight = cinfo.output_height;
601 int yMaxH = yHeight - 1;
602 int v = cinfo.cur_comp_info[0]->v_samp_factor;
603 int uvMaxH = uvSize.height() - 1;
604 JSAMPROW outputY = static_cast<JSAMPROW>(planes[0]);
605 JSAMPROW outputU = static_cast<JSAMPROW>(planes[1]);
606 JSAMPROW outputV = static_cast<JSAMPROW>(planes[2]);
607 size_t rowBytesY = rowBytes[0];
608 size_t rowBytesU = rowBytes[1];
609 size_t rowBytesV = rowBytes[2];
610
611 int yScanlinesToRead = DCTSIZE * v;
sugoif421ec62015-02-19 05:32:08 -0800612 SkAutoMalloc lastRowStorage(rowBytesY * 4);
sugoib227e372014-10-16 13:10:57 -0700613 JSAMPROW yLastRow = (JSAMPROW)lastRowStorage.get();
sugoif421ec62015-02-19 05:32:08 -0800614 JSAMPROW uLastRow = yLastRow + rowBytesY;
615 JSAMPROW vLastRow = uLastRow + rowBytesY;
616 JSAMPROW dummyRow = vLastRow + rowBytesY;
sugoib227e372014-10-16 13:10:57 -0700617
618 while (cinfo.output_scanline < cinfo.output_height) {
619 // Request 8 or 16 scanlines: returns 0 or more scanlines.
620 bool hasYLastRow(false), hasUVLastRow(false);
621 // Assign 8 or 16 rows of memory to read the Y channel.
622 for (int i = 0; i < yScanlinesToRead; ++i) {
623 int scanline = (cinfo.output_scanline + i);
624 if (scanline < yMaxH) {
625 bufferraw2[i] = &outputY[scanline * rowBytesY];
626 } else if (scanline == yMaxH) {
627 bufferraw2[i] = yLastRow;
628 hasYLastRow = true;
629 } else {
630 bufferraw2[i] = dummyRow;
631 }
632 }
633 int scaledScanline = cinfo.output_scanline / v;
634 // Assign 8 rows of memory to read the U and V channels.
635 for (int i = 0; i < 8; ++i) {
636 int scanline = (scaledScanline + i);
637 if (scanline < uvMaxH) {
638 bufferraw2[16 + i] = &outputU[scanline * rowBytesU];
639 bufferraw2[24 + i] = &outputV[scanline * rowBytesV];
640 } else if (scanline == uvMaxH) {
641 bufferraw2[16 + i] = uLastRow;
642 bufferraw2[24 + i] = vLastRow;
643 hasUVLastRow = true;
644 } else {
645 bufferraw2[16 + i] = dummyRow;
646 bufferraw2[24 + i] = dummyRow;
647 }
648 }
649 JDIMENSION scanlinesRead = jpeg_read_raw_data(&cinfo, bufferraw, yScanlinesToRead);
650
scroggo2a120802014-10-22 12:07:00 -0700651 if (scanlinesRead == 0) {
sugoib227e372014-10-16 13:10:57 -0700652 return false;
scroggo2a120802014-10-22 12:07:00 -0700653 }
sugoib227e372014-10-16 13:10:57 -0700654
655 if (hasYLastRow) {
656 memcpy(&outputY[yMaxH * rowBytesY], yLastRow, yWidth);
657 }
658 if (hasUVLastRow) {
659 memcpy(&outputU[uvMaxH * rowBytesU], uLastRow, uvSize.width());
660 memcpy(&outputV[uvMaxH * rowBytesV], vLastRow, uvSize.width());
661 }
662 }
663
664 cinfo.output_scanline = SkMin32(cinfo.output_scanline, cinfo.output_height);
665
666 return true;
667}
668
669bool SkJPEGImageDecoder::onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
670 void* planes[3], size_t rowBytes[3],
671 SkYUVColorSpace* colorSpace) {
672#ifdef TIME_DECODE
673 SkAutoTime atm("JPEG YUV8 Decode");
674#endif
sugoib227e372014-10-16 13:10:57 -0700675 if (this->getSampleSize() != 1) {
676 return false; // Resizing not supported
677 }
678
679 JPEGAutoClean autoClean;
680
681 jpeg_decompress_struct cinfo;
682 skjpeg_source_mgr srcManager(stream, this);
683
684 skjpeg_error_mgr errorManager;
685 set_error_mgr(&cinfo, &errorManager);
686
687 // All objects need to be instantiated before this setjmp call so that
688 // they will be cleaned up properly if an error occurs.
689 if (setjmp(errorManager.fJmpBuf)) {
scroggo2a120802014-10-22 12:07:00 -0700690 return return_false(cinfo, "setjmp YUV8");
sugoib227e372014-10-16 13:10:57 -0700691 }
692
693 initialize_info(&cinfo, &srcManager);
694 autoClean.set(&cinfo);
695
696 int status = jpeg_read_header(&cinfo, true);
697 if (status != JPEG_HEADER_OK) {
scroggo2a120802014-10-22 12:07:00 -0700698 return return_false(cinfo, "read_header YUV8");
sugoib227e372014-10-16 13:10:57 -0700699 }
700
mtkleinb3e5e4d2015-03-25 13:13:43 -0700701 if (!appears_to_be_yuv(cinfo)) {
sugoib227e372014-10-16 13:10:57 -0700702 // It's not an error to not be encoded in YUV, so no need to use return_false()
703 return false;
704 }
705
706 cinfo.out_color_space = JCS_YCbCr;
707 cinfo.raw_data_out = TRUE;
708
709 if (!planes || !planes[0] || !rowBytes || !rowBytes[0]) { // Compute size only
710 update_components_sizes(cinfo, componentSizes, kSizeForMemoryAllocation_SizeType);
711 return true;
712 }
713
714 set_dct_method(*this, &cinfo);
715
716 SkASSERT(1 == cinfo.scale_num);
717 cinfo.scale_denom = 1;
718
sugoib227e372014-10-16 13:10:57 -0700719#ifdef ANDROID_RGB
720 cinfo.dither_mode = JDITHER_NONE;
721#endif
722
723 /* image_width and image_height are the original dimensions, available
724 after jpeg_read_header(). To see the scaled dimensions, we have to call
725 jpeg_start_decompress(), and then read output_width and output_height.
726 */
727 if (!jpeg_start_decompress(&cinfo)) {
scroggo2a120802014-10-22 12:07:00 -0700728 return return_false(cinfo, "start_decompress YUV8");
sugoib227e372014-10-16 13:10:57 -0700729 }
730
mtkleinb3e5e4d2015-03-25 13:13:43 -0700731 // Seems like jpeg_start_decompress is updating our opinion of whether cinfo represents YUV.
732 // Again, not really an error.
733 if (!appears_to_be_yuv(cinfo)) {
734 return false;
735 }
736
sugoib227e372014-10-16 13:10:57 -0700737 if (!output_raw_data(cinfo, planes, rowBytes)) {
scroggo2a120802014-10-22 12:07:00 -0700738 return return_false(cinfo, "output_raw_data");
sugoib227e372014-10-16 13:10:57 -0700739 }
740
741 update_components_sizes(cinfo, componentSizes, kActualSize_SizeType);
742 jpeg_finish_decompress(&cinfo);
743
halcanary96fcdcc2015-08-27 07:41:13 -0700744 if (nullptr != colorSpace) {
sugoib227e372014-10-16 13:10:57 -0700745 *colorSpace = kJPEG_SkYUVColorSpace;
746 }
747
748 return true;
749}
750
scroggo2a120802014-10-22 12:07:00 -0700751///////////////////////////////////////////////////////////////////////////////
752
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000753#include "SkColorPriv.h"
754
755// taken from jcolor.c in libjpeg
756#if 0 // 16bit - precise but slow
757 #define CYR 19595 // 0.299
758 #define CYG 38470 // 0.587
759 #define CYB 7471 // 0.114
760
761 #define CUR -11059 // -0.16874
762 #define CUG -21709 // -0.33126
763 #define CUB 32768 // 0.5
764
765 #define CVR 32768 // 0.5
766 #define CVG -27439 // -0.41869
767 #define CVB -5329 // -0.08131
768
769 #define CSHIFT 16
770#else // 8bit - fast, slightly less precise
771 #define CYR 77 // 0.299
772 #define CYG 150 // 0.587
773 #define CYB 29 // 0.114
774
775 #define CUR -43 // -0.16874
776 #define CUG -85 // -0.33126
777 #define CUB 128 // 0.5
778
779 #define CVR 128 // 0.5
780 #define CVG -107 // -0.41869
781 #define CVB -21 // -0.08131
782
783 #define CSHIFT 8
784#endif
785
786static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
787 int r = SkGetPackedR32(c);
788 int g = SkGetPackedG32(c);
789 int b = SkGetPackedB32(c);
790
791 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
792 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
793 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
794
795 dst[0] = SkToU8(y);
796 dst[1] = SkToU8(u + 128);
797 dst[2] = SkToU8(v + 128);
798}
799
800static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
801 int r = SkGetPackedR4444(c);
802 int g = SkGetPackedG4444(c);
803 int b = SkGetPackedB4444(c);
804
805 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
806 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
807 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
808
809 dst[0] = SkToU8(y);
810 dst[1] = SkToU8(u + 128);
811 dst[2] = SkToU8(v + 128);
812}
813
814static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
815 int r = SkGetPackedR16(c);
816 int g = SkGetPackedG16(c);
817 int b = SkGetPackedB16(c);
818
819 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
820 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
821 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
822
823 dst[0] = SkToU8(y);
824 dst[1] = SkToU8(u + 128);
825 dst[2] = SkToU8(v + 128);
826}
827
828///////////////////////////////////////////////////////////////////////////////
829
830typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
831 const void* SK_RESTRICT src, int width,
832 const SkPMColor* SK_RESTRICT ctable);
833
834static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
835 const void* SK_RESTRICT srcRow, int width,
836 const SkPMColor*) {
837 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
838 while (--width >= 0) {
839#ifdef WE_CONVERT_TO_YUV
840 rgb2yuv_32(dst, *src++);
841#else
842 uint32_t c = *src++;
843 dst[0] = SkGetPackedR32(c);
844 dst[1] = SkGetPackedG32(c);
845 dst[2] = SkGetPackedB32(c);
846#endif
847 dst += 3;
848 }
849}
850
851static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
852 const void* SK_RESTRICT srcRow, int width,
853 const SkPMColor*) {
854 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
855 while (--width >= 0) {
856#ifdef WE_CONVERT_TO_YUV
857 rgb2yuv_4444(dst, *src++);
858#else
859 SkPMColor16 c = *src++;
860 dst[0] = SkPacked4444ToR32(c);
861 dst[1] = SkPacked4444ToG32(c);
862 dst[2] = SkPacked4444ToB32(c);
863#endif
864 dst += 3;
865 }
866}
867
868static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
869 const void* SK_RESTRICT srcRow, int width,
870 const SkPMColor*) {
871 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
872 while (--width >= 0) {
873#ifdef WE_CONVERT_TO_YUV
874 rgb2yuv_16(dst, *src++);
875#else
876 uint16_t c = *src++;
877 dst[0] = SkPacked16ToR32(c);
878 dst[1] = SkPacked16ToG32(c);
879 dst[2] = SkPacked16ToB32(c);
880#endif
881 dst += 3;
882 }
883}
884
885static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
886 const void* SK_RESTRICT srcRow, int width,
887 const SkPMColor* SK_RESTRICT ctable) {
888 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
889 while (--width >= 0) {
890#ifdef WE_CONVERT_TO_YUV
891 rgb2yuv_32(dst, ctable[*src++]);
892#else
893 uint32_t c = ctable[*src++];
894 dst[0] = SkGetPackedR32(c);
895 dst[1] = SkGetPackedG32(c);
896 dst[2] = SkGetPackedB32(c);
897#endif
898 dst += 3;
899 }
900}
901
902static WriteScanline ChooseWriter(const SkBitmap& bm) {
reed6c225732014-06-09 19:52:07 -0700903 switch (bm.colorType()) {
904 case kN32_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000905 return Write_32_YUV;
reed6c225732014-06-09 19:52:07 -0700906 case kRGB_565_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000907 return Write_16_YUV;
reed6c225732014-06-09 19:52:07 -0700908 case kARGB_4444_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000909 return Write_4444_YUV;
reed6c225732014-06-09 19:52:07 -0700910 case kIndex_8_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000911 return Write_Index_YUV;
912 default:
halcanary96fcdcc2015-08-27 07:41:13 -0700913 return nullptr;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000914 }
915}
916
917class SkJPEGImageEncoder : public SkImageEncoder {
918protected:
919 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
920#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +0000921 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000922#endif
923
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000924 SkAutoLockPixels alp(bm);
halcanary96fcdcc2015-08-27 07:41:13 -0700925 if (nullptr == bm.getPixels()) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000926 return false;
927 }
928
929 jpeg_compress_struct cinfo;
930 skjpeg_error_mgr sk_err;
931 skjpeg_destination_mgr sk_wstream(stream);
932
933 // allocate these before set call setjmp
scroggo565901d2015-12-10 10:44:13 -0800934 SkAutoTMalloc<uint8_t> oneRow;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000935
936 cinfo.err = jpeg_std_error(&sk_err);
937 sk_err.error_exit = skjpeg_error_exit;
938 if (setjmp(sk_err.fJmpBuf)) {
939 return false;
940 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000941
mtklein@google.com8d725b22013-07-24 16:20:05 +0000942 // Keep after setjmp or mark volatile.
943 const WriteScanline writer = ChooseWriter(bm);
halcanary96fcdcc2015-08-27 07:41:13 -0700944 if (nullptr == writer) {
mtklein@google.com8d725b22013-07-24 16:20:05 +0000945 return false;
946 }
947
948 jpeg_create_compress(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000949 cinfo.dest = &sk_wstream;
950 cinfo.image_width = bm.width();
951 cinfo.image_height = bm.height();
952 cinfo.input_components = 3;
953#ifdef WE_CONVERT_TO_YUV
954 cinfo.in_color_space = JCS_YCbCr;
955#else
956 cinfo.in_color_space = JCS_RGB;
957#endif
958 cinfo.input_gamma = 1;
959
960 jpeg_set_defaults(&cinfo);
benjaminwagner0a356202016-01-14 18:13:32 -0800961 cinfo.optimize_coding = TRUE;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000962 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +0000963#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000964 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +0000965#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000966
967 jpeg_start_compress(&cinfo, TRUE);
968
969 const int width = bm.width();
scroggo565901d2015-12-10 10:44:13 -0800970 uint8_t* oneRowP = oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000971
halcanary96fcdcc2015-08-27 07:41:13 -0700972 const SkPMColor* colors = bm.getColorTable() ? bm.getColorTable()->readColors() : nullptr;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000973 const void* srcRow = bm.getPixels();
974
975 while (cinfo.next_scanline < cinfo.image_height) {
976 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
977
978 writer(oneRowP, srcRow, width, colors);
979 row_pointer[0] = oneRowP;
980 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
981 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
982 }
983
984 jpeg_finish_compress(&cinfo);
985 jpeg_destroy_compress(&cinfo);
986
987 return true;
988 }
989};
990
991///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +0000992DEFINE_DECODER_CREATOR(JPEGImageDecoder);
993DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
994///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000995
scroggo@google.comb5571b32013-09-25 21:34:24 +0000996static bool is_jpeg(SkStreamRewindable* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +0000997 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000998 static const size_t HEADER_SIZE = sizeof(gHeader);
999
1000 char buffer[HEADER_SIZE];
1001 size_t len = stream->read(buffer, HEADER_SIZE);
1002
1003 if (len != HEADER_SIZE) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001004 return false; // can't read enough
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001005 }
1006 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001007 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001008 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001009 return true;
1010}
1011
scroggo@google.comb5571b32013-09-25 21:34:24 +00001012
1013static SkImageDecoder* sk_libjpeg_dfactory(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001014 if (is_jpeg(stream)) {
halcanary385fe4d2015-08-26 13:07:48 -07001015 return new SkJPEGImageDecoder;
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001016 }
halcanary96fcdcc2015-08-27 07:41:13 -07001017 return nullptr;
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001018}
1019
scroggo@google.comb5571b32013-09-25 21:34:24 +00001020static SkImageDecoder::Format get_format_jpeg(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001021 if (is_jpeg(stream)) {
1022 return SkImageDecoder::kJPEG_Format;
1023 }
1024 return SkImageDecoder::kUnknown_Format;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001025}
1026
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001027static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
halcanary96fcdcc2015-08-27 07:41:13 -07001028 return (SkImageEncoder::kJPEG_Type == t) ? new SkJPEGImageEncoder : nullptr;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001029}
1030
mtklein@google.combd6343b2013-09-04 17:20:18 +00001031static SkImageDecoder_DecodeReg gDReg(sk_libjpeg_dfactory);
1032static SkImageDecoder_FormatReg gFormatReg(get_format_jpeg);
1033static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory);