blob: 89bfefcd45f2ae68b98a7d6030fbac7f9a7f0eef [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"
mtklein8ca88e42016-02-03 09:21:44 -080014#include "SkMSAN.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000015#include "SkScaledBitmapSampler.h"
16#include "SkStream.h"
17#include "SkTemplates.h"
djsollen@google.com11399402013-03-20 17:45:27 +000018#include "SkTime.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000019#include "SkUtils.h"
halcanary@google.com2a103182013-10-14 12:49:15 +000020#include "SkRTConf.h"
djsollen@google.com11399402013-03-20 17:45:27 +000021#include "SkRect.h"
22#include "SkCanvas.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000023
halcanary@google.comfed30372013-10-04 12:46:45 +000024
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000025#include <stdio.h>
26extern "C" {
27 #include "jpeglib.h"
28 #include "jerror.h"
29}
30
djsollen@google.com11399402013-03-20 17:45:27 +000031// These enable timing code that report milliseconds for an encoding/decoding
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000032//#define TIME_ENCODE
33//#define TIME_DECODE
34
35// this enables our rgb->yuv code, which is faster than libjpeg on ARM
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000036#define WE_CONVERT_TO_YUV
37
djsollen@google.com11399402013-03-20 17:45:27 +000038// If ANDROID_RGB is defined by in the jpeg headers it indicates that jpeg offers
39// support for two additional formats (1) JCS_RGBA_8888 and (2) JCS_RGB_565.
40
halcanary@google.com2a103182013-10-14 12:49:15 +000041#define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS true
halcanary@google.com04b57f82013-10-14 20:08:48 +000042#define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_ERRORS true
halcanary@google.com2a103182013-10-14 12:49:15 +000043SK_CONF_DECLARE(bool, c_suppressJPEGImageDecoderWarnings,
44 "images.jpeg.suppressDecoderWarnings",
45 DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS,
46 "Suppress most JPG warnings when calling decode functions.");
halcanary@google.com04b57f82013-10-14 20:08:48 +000047SK_CONF_DECLARE(bool, c_suppressJPEGImageDecoderErrors,
48 "images.jpeg.suppressDecoderErrors",
49 DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_ERRORS,
50 "Suppress most JPG error messages when decode "
51 "function fails.");
halcanary@google.comfed30372013-10-04 12:46:45 +000052
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000053//////////////////////////////////////////////////////////////////////////
54//////////////////////////////////////////////////////////////////////////
55
halcanary@google.comfed30372013-10-04 12:46:45 +000056static void do_nothing_emit_message(jpeg_common_struct*, int) {
57 /* do nothing */
58}
halcanary@google.com04b57f82013-10-14 20:08:48 +000059static void do_nothing_output_message(j_common_ptr) {
60 /* do nothing */
61}
halcanary@google.comfed30372013-10-04 12:46:45 +000062
scroggo@google.com590a5af2013-08-07 21:09:13 +000063static void initialize_info(jpeg_decompress_struct* cinfo, skjpeg_source_mgr* src_mgr) {
halcanary96fcdcc2015-08-27 07:41:13 -070064 SkASSERT(cinfo != nullptr);
65 SkASSERT(src_mgr != nullptr);
scroggo@google.com590a5af2013-08-07 21:09:13 +000066 jpeg_create_decompress(cinfo);
scroggo@google.com590a5af2013-08-07 21:09:13 +000067 cinfo->src = src_mgr;
halcanary@google.comfed30372013-10-04 12:46:45 +000068 /* To suppress warnings with a SK_DEBUG binary, set the
69 * environment variable "skia_images_jpeg_suppressDecoderWarnings"
70 * to "true". Inside a program that links to skia:
71 * SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true); */
72 if (c_suppressJPEGImageDecoderWarnings) {
73 cinfo->err->emit_message = &do_nothing_emit_message;
74 }
halcanary@google.com04b57f82013-10-14 20:08:48 +000075 /* To suppress error messages with a SK_DEBUG binary, set the
76 * environment variable "skia_images_jpeg_suppressDecoderErrors"
77 * to "true". Inside a program that links to skia:
78 * SK_CONF_SET("images.jpeg.suppressDecoderErrors", true); */
79 if (c_suppressJPEGImageDecoderErrors) {
80 cinfo->err->output_message = &do_nothing_output_message;
81 }
scroggo@google.com590a5af2013-08-07 21:09:13 +000082}
83
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000084class SkJPEGImageDecoder : public SkImageDecoder {
85public:
djsollen@google.com11399402013-03-20 17:45:27 +000086
mtklein36352bf2015-03-25 18:17:31 -070087 Format getFormat() const override {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000088 return kJPEG_Format;
89 }
90
91protected:
mtklein36352bf2015-03-25 18:17:31 -070092 Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
tfarina7831a4b2015-04-27 07:53:07 -070093 bool onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
94 void* planes[3], size_t rowBytes[3],
95 SkYUVColorSpace* colorSpace) override;
djsollen@google.com11399402013-03-20 17:45:27 +000096
97private:
djsollen@google.com11399402013-03-20 17:45:27 +000098
scroggo@google.com590a5af2013-08-07 21:09:13 +000099 /**
reed6c225732014-06-09 19:52:07 -0700100 * Determine the appropriate bitmap colortype and out_color_space based on
scroggo@google.com590a5af2013-08-07 21:09:13 +0000101 * both the preference of the caller and the jpeg_color_space on the
102 * jpeg_decompress_struct passed in.
103 * Must be called after jpeg_read_header.
104 */
reed6c225732014-06-09 19:52:07 -0700105 SkColorType getBitmapColorType(jpeg_decompress_struct*);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000106
djsollen@google.com11399402013-03-20 17:45:27 +0000107 typedef SkImageDecoder INHERITED;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000108};
109
110//////////////////////////////////////////////////////////////////////////
111
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000112/* Automatically clean up after throwing an exception */
113class JPEGAutoClean {
114public:
halcanary96fcdcc2015-08-27 07:41:13 -0700115 JPEGAutoClean(): cinfo_ptr(nullptr) {}
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000116 ~JPEGAutoClean() {
117 if (cinfo_ptr) {
118 jpeg_destroy_decompress(cinfo_ptr);
119 }
120 }
121 void set(jpeg_decompress_struct* info) {
122 cinfo_ptr = info;
123 }
124private:
125 jpeg_decompress_struct* cinfo_ptr;
126};
127
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000128///////////////////////////////////////////////////////////////////////////////
129
130/* If we need to better match the request, we might examine the image and
131 output dimensions, and determine if the downsampling jpeg provided is
132 not sufficient. If so, we can recompute a modified sampleSize value to
133 make up the difference.
134
135 To skip this additional scaling, just set sampleSize = 1; below.
136 */
137static int recompute_sampleSize(int sampleSize,
138 const jpeg_decompress_struct& cinfo) {
139 return sampleSize * cinfo.output_width / cinfo.image_width;
140}
141
142static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) {
143 /* These are initialized to 0, so if they have non-zero values, we assume
144 they are "valid" (i.e. have been computed by libjpeg)
145 */
djsollen@google.com11399402013-03-20 17:45:27 +0000146 return 0 != cinfo.output_width && 0 != cinfo.output_height;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000147}
148
djsollen@google.com11399402013-03-20 17:45:27 +0000149static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, int count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000150 for (int i = 0; i < count; i++) {
151 JSAMPLE* rowptr = (JSAMPLE*)buffer;
152 int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
djsollen@google.com11399402013-03-20 17:45:27 +0000153 if (1 != row_count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000154 return false;
155 }
156 }
157 return true;
158}
159
scroggo2a120802014-10-22 12:07:00 -0700160///////////////////////////////////////////////////////////////////////////////
161
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000162// This guy exists just to aid in debugging, as it allows debuggers to just
163// set a break-point in one place to see all error exists.
scroggo2a120802014-10-22 12:07:00 -0700164static void print_jpeg_decoder_errors(const jpeg_decompress_struct& cinfo,
sugoib227e372014-10-16 13:10:57 -0700165 int width, int height, const char caller[]) {
halcanary@google.com04b57f82013-10-14 20:08:48 +0000166 if (!(c_suppressJPEGImageDecoderErrors)) {
167 char buffer[JMSG_LENGTH_MAX];
168 cinfo.err->format_message((const j_common_ptr)&cinfo, buffer);
169 SkDebugf("libjpeg error %d <%s> from %s [%d %d]\n",
sugoib227e372014-10-16 13:10:57 -0700170 cinfo.err->msg_code, buffer, caller, width, height);
halcanary@google.com04b57f82013-10-14 20:08:48 +0000171 }
scroggo2a120802014-10-22 12:07:00 -0700172}
173
174static bool return_false(const jpeg_decompress_struct& cinfo,
175 const char caller[]) {
176 print_jpeg_decoder_errors(cinfo, 0, 0, caller);
177 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000178}
179
scroggo2a120802014-10-22 12:07:00 -0700180static SkImageDecoder::Result return_failure(const jpeg_decompress_struct& cinfo,
181 const SkBitmap& bm, const char caller[]) {
182 print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller);
183 return SkImageDecoder::kFailure;
184}
185
186///////////////////////////////////////////////////////////////////////////////
187
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000188// Convert a scanline of CMYK samples to RGBX in place. Note that this
189// method moves the "scanline" pointer in its processing
190static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
191 // At this point we've received CMYK pixels from libjpeg. We
rmistry@google.comd6176b02012-08-23 18:14:13 +0000192 // perform a crude conversion to RGB (based on the formulae
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000193 // from easyrgb.com):
194 // CMYK -> CMY
195 // C = ( C * (1 - K) + K ) // for each CMY component
196 // CMY -> RGB
197 // R = ( 1 - C ) * 255 // for each RGB component
198 // Unfortunately we are seeing inverted CMYK so all the original terms
199 // are 1-. This yields:
200 // CMYK -> CMY
201 // C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K
202 // The conversion from CMY->RGB remains the same
203 for (unsigned int x = 0; x < width; ++x, scanline += 4) {
204 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]);
205 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]);
206 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]);
207 scanline[3] = 255;
208 }
209}
210
scroggo@google.com590a5af2013-08-07 21:09:13 +0000211/**
212 * Common code for setting the error manager.
213 */
214static void set_error_mgr(jpeg_decompress_struct* cinfo, skjpeg_error_mgr* errorManager) {
halcanary96fcdcc2015-08-27 07:41:13 -0700215 SkASSERT(cinfo != nullptr);
216 SkASSERT(errorManager != nullptr);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000217 cinfo->err = jpeg_std_error(errorManager);
218 errorManager->error_exit = skjpeg_error_exit;
219}
220
221/**
scroggo@google.com590a5af2013-08-07 21:09:13 +0000222 * Common code for setting the dct method.
223 */
224static void set_dct_method(const SkImageDecoder& decoder, jpeg_decompress_struct* cinfo) {
halcanary96fcdcc2015-08-27 07:41:13 -0700225 SkASSERT(cinfo != nullptr);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000226 cinfo->dct_method = JDCT_ISLOW;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000227}
228
reed6c225732014-06-09 19:52:07 -0700229SkColorType SkJPEGImageDecoder::getBitmapColorType(jpeg_decompress_struct* cinfo) {
halcanary96fcdcc2015-08-27 07:41:13 -0700230 SkASSERT(cinfo != nullptr);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000231
232 SrcDepth srcDepth = k32Bit_SrcDepth;
233 if (JCS_GRAYSCALE == cinfo->jpeg_color_space) {
234 srcDepth = k8BitGray_SrcDepth;
235 }
236
reed6c225732014-06-09 19:52:07 -0700237 SkColorType colorType = this->getPrefColorType(srcDepth, /*hasAlpha*/ false);
238 switch (colorType) {
239 case kAlpha_8_SkColorType:
240 // Only respect A8 colortype if the original is grayscale,
scroggo@google.com590a5af2013-08-07 21:09:13 +0000241 // in which case we will treat the grayscale as alpha
242 // values.
243 if (cinfo->jpeg_color_space != JCS_GRAYSCALE) {
reed6c225732014-06-09 19:52:07 -0700244 colorType = kN32_SkColorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000245 }
246 break;
reed6c225732014-06-09 19:52:07 -0700247 case kN32_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000248 // Fall through.
reed6c225732014-06-09 19:52:07 -0700249 case kARGB_4444_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000250 // Fall through.
reed6c225732014-06-09 19:52:07 -0700251 case kRGB_565_SkColorType:
252 // These are acceptable destination colortypes.
scroggo@google.com590a5af2013-08-07 21:09:13 +0000253 break;
254 default:
reed6c225732014-06-09 19:52:07 -0700255 // Force all other colortypes to 8888.
256 colorType = kN32_SkColorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000257 break;
258 }
259
260 switch (cinfo->jpeg_color_space) {
261 case JCS_CMYK:
262 // Fall through.
263 case JCS_YCCK:
264 // libjpeg cannot convert from CMYK or YCCK to RGB - here we set up
265 // so libjpeg will give us CMYK samples back and we will later
266 // manually convert them to RGB
267 cinfo->out_color_space = JCS_CMYK;
268 break;
269 case JCS_GRAYSCALE:
reed6c225732014-06-09 19:52:07 -0700270 if (kAlpha_8_SkColorType == colorType) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000271 cinfo->out_color_space = JCS_GRAYSCALE;
272 break;
273 }
274 // The data is JCS_GRAYSCALE, but the caller wants some sort of RGB
reed6c225732014-06-09 19:52:07 -0700275 // colortype. Fall through to set to the default.
scroggo@google.com590a5af2013-08-07 21:09:13 +0000276 default:
277 cinfo->out_color_space = JCS_RGB;
278 break;
279 }
reed6c225732014-06-09 19:52:07 -0700280 return colorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000281}
282
scroggo@google.com590a5af2013-08-07 21:09:13 +0000283/**
reed6c225732014-06-09 19:52:07 -0700284 * Based on the colortype and dither mode, adjust out_color_space and
285 * dither_mode of cinfo. Only does work in ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000286 */
287static void adjust_out_color_space_and_dither(jpeg_decompress_struct* cinfo,
reed6c225732014-06-09 19:52:07 -0700288 SkColorType colorType,
scroggo@google.com590a5af2013-08-07 21:09:13 +0000289 const SkImageDecoder& decoder) {
halcanary96fcdcc2015-08-27 07:41:13 -0700290 SkASSERT(cinfo != nullptr);
reed6c225732014-06-09 19:52:07 -0700291#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000292 cinfo->dither_mode = JDITHER_NONE;
293 if (JCS_CMYK == cinfo->out_color_space) {
294 return;
295 }
reed6c225732014-06-09 19:52:07 -0700296 switch (colorType) {
297 case kN32_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000298 cinfo->out_color_space = JCS_RGBA_8888;
299 break;
reed6c225732014-06-09 19:52:07 -0700300 case kRGB_565_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000301 cinfo->out_color_space = JCS_RGB_565;
302 if (decoder.getDitherImage()) {
303 cinfo->dither_mode = JDITHER_ORDERED;
304 }
305 break;
306 default:
307 break;
308 }
scroggo@google.com590a5af2013-08-07 21:09:13 +0000309#endif
reed6c225732014-06-09 19:52:07 -0700310}
scroggo@google.com590a5af2013-08-07 21:09:13 +0000311
halcanary@google.comfed30372013-10-04 12:46:45 +0000312/**
313 Sets all pixels in given bitmap to SK_ColorWHITE for all rows >= y.
314 Used when decoding fails partway through reading scanlines to fill
315 remaining lines. */
316static void fill_below_level(int y, SkBitmap* bitmap) {
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000317 SkIRect rect = SkIRect::MakeLTRB(0, y, bitmap->width(), bitmap->height());
halcanary@google.comfed30372013-10-04 12:46:45 +0000318 SkCanvas canvas(*bitmap);
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000319 canvas.clipRect(SkRect::Make(rect));
halcanary@google.comfed30372013-10-04 12:46:45 +0000320 canvas.drawColor(SK_ColorWHITE);
321}
322
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000323/**
324 * Get the config and bytes per pixel of the source data. Return
325 * whether the data is supported.
326 */
327static bool get_src_config(const jpeg_decompress_struct& cinfo,
328 SkScaledBitmapSampler::SrcConfig* sc,
329 int* srcBytesPerPixel) {
halcanary96fcdcc2015-08-27 07:41:13 -0700330 SkASSERT(sc != nullptr && srcBytesPerPixel != nullptr);
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000331 if (JCS_CMYK == cinfo.out_color_space) {
332 // In this case we will manually convert the CMYK values to RGB
333 *sc = SkScaledBitmapSampler::kRGBX;
334 // The CMYK work-around relies on 4 components per pixel here
335 *srcBytesPerPixel = 4;
336 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
337 *sc = SkScaledBitmapSampler::kRGB;
338 *srcBytesPerPixel = 3;
339#ifdef ANDROID_RGB
340 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
341 *sc = SkScaledBitmapSampler::kRGBX;
342 *srcBytesPerPixel = 4;
343 } else if (JCS_RGB_565 == cinfo.out_color_space) {
344 *sc = SkScaledBitmapSampler::kRGB_565;
345 *srcBytesPerPixel = 2;
346#endif
347 } else if (1 == cinfo.out_color_components &&
348 JCS_GRAYSCALE == cinfo.out_color_space) {
349 *sc = SkScaledBitmapSampler::kGray;
350 *srcBytesPerPixel = 1;
351 } else {
352 return false;
353 }
354 return true;
355}
halcanary@google.comfed30372013-10-04 12:46:45 +0000356
scroggo2a120802014-10-22 12:07:00 -0700357SkImageDecoder::Result SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000358#ifdef TIME_DECODE
djsollen@google.com11399402013-03-20 17:45:27 +0000359 SkAutoTime atm("JPEG Decode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000360#endif
361
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000362 JPEGAutoClean autoClean;
363
364 jpeg_decompress_struct cinfo;
scroggo@google.comd4c35652013-08-01 15:03:42 +0000365 skjpeg_source_mgr srcManager(stream, this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000366
scroggo@google.com590a5af2013-08-07 21:09:13 +0000367 skjpeg_error_mgr errorManager;
368 set_error_mgr(&cinfo, &errorManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000369
370 // All objects need to be instantiated before this setjmp call so that
371 // they will be cleaned up properly if an error occurs.
djsollen@google.com11399402013-03-20 17:45:27 +0000372 if (setjmp(errorManager.fJmpBuf)) {
scroggo2a120802014-10-22 12:07:00 -0700373 return return_failure(cinfo, *bm, "setjmp");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000374 }
375
scroggo@google.com590a5af2013-08-07 21:09:13 +0000376 initialize_info(&cinfo, &srcManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000377 autoClean.set(&cinfo);
378
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000379 int status = jpeg_read_header(&cinfo, true);
380 if (status != JPEG_HEADER_OK) {
scroggo2a120802014-10-22 12:07:00 -0700381 return return_failure(cinfo, *bm, "read_header");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000382 }
383
384 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
385 can) much faster that we, just use their num/denom api to approximate
386 the size.
387 */
388 int sampleSize = this->getSampleSize();
389
scroggo@google.com590a5af2013-08-07 21:09:13 +0000390 set_dct_method(*this, &cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000391
scroggo@google.com590a5af2013-08-07 21:09:13 +0000392 SkASSERT(1 == cinfo.scale_num);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000393 cinfo.scale_denom = sampleSize;
394
reed6c225732014-06-09 19:52:07 -0700395 const SkColorType colorType = this->getBitmapColorType(&cinfo);
396 const SkAlphaType alphaType = kAlpha_8_SkColorType == colorType ?
397 kPremul_SkAlphaType : kOpaque_SkAlphaType;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000398
reed6c225732014-06-09 19:52:07 -0700399 adjust_out_color_space_and_dither(&cinfo, colorType, *this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000400
djsollen@google.com11399402013-03-20 17:45:27 +0000401 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000402 // Assume an A8 bitmap is not opaque to avoid the check of each
403 // individual pixel. It is very unlikely to be opaque, since
404 // an opaque A8 bitmap would not be very interesting.
405 // Otherwise, a jpeg image is opaque.
scroggo2a120802014-10-22 12:07:00 -0700406 bool success = bm->setInfo(SkImageInfo::Make(cinfo.image_width, cinfo.image_height,
407 colorType, alphaType));
408 return success ? kSuccess : kFailure;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000409 }
410
411 /* image_width and image_height are the original dimensions, available
412 after jpeg_read_header(). To see the scaled dimensions, we have to call
413 jpeg_start_decompress(), and then read output_width and output_height.
414 */
415 if (!jpeg_start_decompress(&cinfo)) {
416 /* If we failed here, we may still have enough information to return
417 to the caller if they just wanted (subsampled bounds). If sampleSize
418 was 1, then we would have already returned. Thus we just check if
419 we're in kDecodeBounds_Mode, and that we have valid output sizes.
420
421 One reason to fail here is that we have insufficient stream data
422 to complete the setup. However, output dimensions seem to get
423 computed very early, which is why this special check can pay off.
424 */
djsollen@google.com11399402013-03-20 17:45:27 +0000425 if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000426 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
427 recompute_sampleSize(sampleSize, cinfo));
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000428 // Assume an A8 bitmap is not opaque to avoid the check of each
429 // individual pixel. It is very unlikely to be opaque, since
430 // an opaque A8 bitmap would not be very interesting.
431 // Otherwise, a jpeg image is opaque.
scroggo2a120802014-10-22 12:07:00 -0700432 bool success = bm->setInfo(SkImageInfo::Make(smpl.scaledWidth(), smpl.scaledHeight(),
433 colorType, alphaType));
434 return success ? kSuccess : kFailure;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000435 } else {
scroggo2a120802014-10-22 12:07:00 -0700436 return return_failure(cinfo, *bm, "start_decompress");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000437 }
438 }
439 sampleSize = recompute_sampleSize(sampleSize, cinfo);
440
djsollen@google.com11399402013-03-20 17:45:27 +0000441 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000442 // Assume an A8 bitmap is not opaque to avoid the check of each
443 // individual pixel. It is very unlikely to be opaque, since
444 // an opaque A8 bitmap would not be very interesting.
445 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -0700446 bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),
447 colorType, alphaType));
scroggo@google.combc69ce92013-07-09 15:45:14 +0000448 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
scroggo2a120802014-10-22 12:07:00 -0700449 return kSuccess;
scroggo@google.combc69ce92013-07-09 15:45:14 +0000450 }
halcanary96fcdcc2015-08-27 07:41:13 -0700451 if (!this->allocPixelRef(bm, nullptr)) {
scroggo2a120802014-10-22 12:07:00 -0700452 return return_failure(cinfo, *bm, "allocPixelRef");
djsollen@google.com11399402013-03-20 17:45:27 +0000453 }
454
455 SkAutoLockPixels alp(*bm);
456
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000457#ifdef ANDROID_RGB
458 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
459 a significant performance boost.
460 */
461 if (sampleSize == 1 &&
reed6c225732014-06-09 19:52:07 -0700462 ((kN32_SkColorType == colorType && cinfo.out_color_space == JCS_RGBA_8888) ||
463 (kRGB_565_SkColorType == colorType && cinfo.out_color_space == JCS_RGB_565)))
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000464 {
scroggo@google.combc69ce92013-07-09 15:45:14 +0000465 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000466 INT32 const bpr = bm->rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000467
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000468 while (cinfo.output_scanline < cinfo.output_height) {
469 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000470 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000471 // if row_count == 0, then we didn't get a scanline,
472 // so return early. We will return a partial image.
473 fill_below_level(cinfo.output_scanline, bm);
474 cinfo.output_scanline = cinfo.output_height;
scroggo2a120802014-10-22 12:07:00 -0700475 jpeg_finish_decompress(&cinfo);
476 return kPartialSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000477 }
478 if (this->shouldCancelDecode()) {
scroggo2a120802014-10-22 12:07:00 -0700479 return return_failure(cinfo, *bm, "shouldCancelDecode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000480 }
481 rowptr += bpr;
482 }
483 jpeg_finish_decompress(&cinfo);
scroggo2a120802014-10-22 12:07:00 -0700484 return kSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000485 }
486#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000487
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000488 // check for supported formats
489 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000490 int srcBytesPerPixel;
491
492 if (!get_src_config(cinfo, &sc, &srcBytesPerPixel)) {
scroggo2a120802014-10-22 12:07:00 -0700493 return return_failure(cinfo, *bm, "jpeg colorspace");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000494 }
495
scroggo@google.com8d239242013-10-01 17:27:15 +0000496 if (!sampler.begin(bm, sc, *this)) {
scroggo2a120802014-10-22 12:07:00 -0700497 return return_failure(cinfo, *bm, "sampler.begin");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000498 }
499
scroggo565901d2015-12-10 10:44:13 -0800500 SkAutoTMalloc<uint8_t> srcStorage(cinfo.output_width * srcBytesPerPixel);
501 uint8_t* srcRow = srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000502
503 // Possibly skip initial rows [sampler.srcY0]
504 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
scroggo2a120802014-10-22 12:07:00 -0700505 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000506 }
507
508 // now loop through scanlines until y == bm->height() - 1
509 for (int y = 0;; y++) {
510 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
511 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
mtkleine721a8e2016-02-06 19:12:23 -0800512 sk_msan_mark_initialized(srcRow, srcRow + cinfo.output_width * srcBytesPerPixel,
513 "skbug.com/4550");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000514 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000515 // if row_count == 0, then we didn't get a scanline,
516 // so return early. We will return a partial image.
517 fill_below_level(y, bm);
518 cinfo.output_scanline = cinfo.output_height;
scroggo2a120802014-10-22 12:07:00 -0700519 jpeg_finish_decompress(&cinfo);
scroggoc6b8ffa2014-12-17 06:55:02 -0800520 return kPartialSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000521 }
522 if (this->shouldCancelDecode()) {
scroggo2a120802014-10-22 12:07:00 -0700523 return return_failure(cinfo, *bm, "shouldCancelDecode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000524 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000525
526 if (JCS_CMYK == cinfo.out_color_space) {
527 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
528 }
529
mtklein8ca88e42016-02-03 09:21:44 -0800530
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000531 sampler.next(srcRow);
532 if (bm->height() - 1 == y) {
533 // we're done
534 break;
535 }
536
537 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
scroggo2a120802014-10-22 12:07:00 -0700538 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000539 }
540 }
541
542 // we formally skip the rest, so we don't get a complaint from libjpeg
543 if (!skip_src_rows(&cinfo, srcRow,
544 cinfo.output_height - cinfo.output_scanline)) {
scroggo2a120802014-10-22 12:07:00 -0700545 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000546 }
547 jpeg_finish_decompress(&cinfo);
548
scroggo2a120802014-10-22 12:07:00 -0700549 return kSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000550}
551
scroggo2a120802014-10-22 12:07:00 -0700552///////////////////////////////////////////////////////////////////////////////
553
sugoib227e372014-10-16 13:10:57 -0700554enum SizeType {
555 kSizeForMemoryAllocation_SizeType,
556 kActualSize_SizeType
557};
558
559static SkISize compute_yuv_size(const jpeg_decompress_struct& info, int component,
560 SizeType sizeType) {
561 if (sizeType == kSizeForMemoryAllocation_SizeType) {
562 return SkISize::Make(info.cur_comp_info[component]->width_in_blocks * DCTSIZE,
563 info.cur_comp_info[component]->height_in_blocks * DCTSIZE);
564 }
565 return SkISize::Make(info.cur_comp_info[component]->downsampled_width,
566 info.cur_comp_info[component]->downsampled_height);
567}
568
mtkleinb3e5e4d2015-03-25 13:13:43 -0700569static bool appears_to_be_yuv(const jpeg_decompress_struct& info) {
570 return (info.jpeg_color_space == JCS_YCbCr)
571 && (DCTSIZE == 8)
572 && (info.num_components == 3)
573 && (info.comps_in_scan >= info.num_components)
574 && (info.scale_denom <= 8)
575 && (info.cur_comp_info[0])
576 && (info.cur_comp_info[1])
577 && (info.cur_comp_info[2])
578 && (info.cur_comp_info[1]->h_samp_factor == 1)
579 && (info.cur_comp_info[1]->v_samp_factor == 1)
580 && (info.cur_comp_info[2]->h_samp_factor == 1)
581 && (info.cur_comp_info[2]->v_samp_factor == 1);
582}
583
sugoib227e372014-10-16 13:10:57 -0700584static void update_components_sizes(const jpeg_decompress_struct& cinfo, SkISize componentSizes[3],
585 SizeType sizeType) {
mtkleinb3e5e4d2015-03-25 13:13:43 -0700586 SkASSERT(appears_to_be_yuv(cinfo));
sugoib227e372014-10-16 13:10:57 -0700587 for (int i = 0; i < 3; ++i) {
588 componentSizes[i] = compute_yuv_size(cinfo, i, sizeType);
589 }
590}
591
592static bool output_raw_data(jpeg_decompress_struct& cinfo, void* planes[3], size_t rowBytes[3]) {
mtkleinb3e5e4d2015-03-25 13:13:43 -0700593 SkASSERT(appears_to_be_yuv(cinfo));
sugoib227e372014-10-16 13:10:57 -0700594 // U size and V size have to be the same if we're calling output_raw_data()
595 SkISize uvSize = compute_yuv_size(cinfo, 1, kSizeForMemoryAllocation_SizeType);
596 SkASSERT(uvSize == compute_yuv_size(cinfo, 2, kSizeForMemoryAllocation_SizeType));
597
598 JSAMPARRAY bufferraw[3];
599 JSAMPROW bufferraw2[32];
600 bufferraw[0] = &bufferraw2[0]; // Y channel rows (8 or 16)
601 bufferraw[1] = &bufferraw2[16]; // U channel rows (8)
602 bufferraw[2] = &bufferraw2[24]; // V channel rows (8)
603 int yWidth = cinfo.output_width;
604 int yHeight = cinfo.output_height;
605 int yMaxH = yHeight - 1;
606 int v = cinfo.cur_comp_info[0]->v_samp_factor;
607 int uvMaxH = uvSize.height() - 1;
608 JSAMPROW outputY = static_cast<JSAMPROW>(planes[0]);
609 JSAMPROW outputU = static_cast<JSAMPROW>(planes[1]);
610 JSAMPROW outputV = static_cast<JSAMPROW>(planes[2]);
611 size_t rowBytesY = rowBytes[0];
612 size_t rowBytesU = rowBytes[1];
613 size_t rowBytesV = rowBytes[2];
614
615 int yScanlinesToRead = DCTSIZE * v;
sugoif421ec62015-02-19 05:32:08 -0800616 SkAutoMalloc lastRowStorage(rowBytesY * 4);
sugoib227e372014-10-16 13:10:57 -0700617 JSAMPROW yLastRow = (JSAMPROW)lastRowStorage.get();
sugoif421ec62015-02-19 05:32:08 -0800618 JSAMPROW uLastRow = yLastRow + rowBytesY;
619 JSAMPROW vLastRow = uLastRow + rowBytesY;
620 JSAMPROW dummyRow = vLastRow + rowBytesY;
sugoib227e372014-10-16 13:10:57 -0700621
622 while (cinfo.output_scanline < cinfo.output_height) {
623 // Request 8 or 16 scanlines: returns 0 or more scanlines.
624 bool hasYLastRow(false), hasUVLastRow(false);
625 // Assign 8 or 16 rows of memory to read the Y channel.
626 for (int i = 0; i < yScanlinesToRead; ++i) {
627 int scanline = (cinfo.output_scanline + i);
628 if (scanline < yMaxH) {
629 bufferraw2[i] = &outputY[scanline * rowBytesY];
630 } else if (scanline == yMaxH) {
631 bufferraw2[i] = yLastRow;
632 hasYLastRow = true;
633 } else {
634 bufferraw2[i] = dummyRow;
635 }
636 }
637 int scaledScanline = cinfo.output_scanline / v;
638 // Assign 8 rows of memory to read the U and V channels.
639 for (int i = 0; i < 8; ++i) {
640 int scanline = (scaledScanline + i);
641 if (scanline < uvMaxH) {
642 bufferraw2[16 + i] = &outputU[scanline * rowBytesU];
643 bufferraw2[24 + i] = &outputV[scanline * rowBytesV];
644 } else if (scanline == uvMaxH) {
645 bufferraw2[16 + i] = uLastRow;
646 bufferraw2[24 + i] = vLastRow;
647 hasUVLastRow = true;
648 } else {
649 bufferraw2[16 + i] = dummyRow;
650 bufferraw2[24 + i] = dummyRow;
651 }
652 }
653 JDIMENSION scanlinesRead = jpeg_read_raw_data(&cinfo, bufferraw, yScanlinesToRead);
654
scroggo2a120802014-10-22 12:07:00 -0700655 if (scanlinesRead == 0) {
sugoib227e372014-10-16 13:10:57 -0700656 return false;
scroggo2a120802014-10-22 12:07:00 -0700657 }
sugoib227e372014-10-16 13:10:57 -0700658
659 if (hasYLastRow) {
660 memcpy(&outputY[yMaxH * rowBytesY], yLastRow, yWidth);
661 }
662 if (hasUVLastRow) {
663 memcpy(&outputU[uvMaxH * rowBytesU], uLastRow, uvSize.width());
664 memcpy(&outputV[uvMaxH * rowBytesV], vLastRow, uvSize.width());
665 }
666 }
667
668 cinfo.output_scanline = SkMin32(cinfo.output_scanline, cinfo.output_height);
669
670 return true;
671}
672
673bool SkJPEGImageDecoder::onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
674 void* planes[3], size_t rowBytes[3],
675 SkYUVColorSpace* colorSpace) {
676#ifdef TIME_DECODE
677 SkAutoTime atm("JPEG YUV8 Decode");
678#endif
sugoib227e372014-10-16 13:10:57 -0700679 if (this->getSampleSize() != 1) {
680 return false; // Resizing not supported
681 }
682
683 JPEGAutoClean autoClean;
684
685 jpeg_decompress_struct cinfo;
686 skjpeg_source_mgr srcManager(stream, this);
687
688 skjpeg_error_mgr errorManager;
689 set_error_mgr(&cinfo, &errorManager);
690
691 // All objects need to be instantiated before this setjmp call so that
692 // they will be cleaned up properly if an error occurs.
693 if (setjmp(errorManager.fJmpBuf)) {
scroggo2a120802014-10-22 12:07:00 -0700694 return return_false(cinfo, "setjmp YUV8");
sugoib227e372014-10-16 13:10:57 -0700695 }
696
697 initialize_info(&cinfo, &srcManager);
698 autoClean.set(&cinfo);
699
700 int status = jpeg_read_header(&cinfo, true);
701 if (status != JPEG_HEADER_OK) {
scroggo2a120802014-10-22 12:07:00 -0700702 return return_false(cinfo, "read_header YUV8");
sugoib227e372014-10-16 13:10:57 -0700703 }
704
mtkleinb3e5e4d2015-03-25 13:13:43 -0700705 if (!appears_to_be_yuv(cinfo)) {
sugoib227e372014-10-16 13:10:57 -0700706 // It's not an error to not be encoded in YUV, so no need to use return_false()
707 return false;
708 }
709
710 cinfo.out_color_space = JCS_YCbCr;
711 cinfo.raw_data_out = TRUE;
712
713 if (!planes || !planes[0] || !rowBytes || !rowBytes[0]) { // Compute size only
714 update_components_sizes(cinfo, componentSizes, kSizeForMemoryAllocation_SizeType);
715 return true;
716 }
717
718 set_dct_method(*this, &cinfo);
719
720 SkASSERT(1 == cinfo.scale_num);
721 cinfo.scale_denom = 1;
722
sugoib227e372014-10-16 13:10:57 -0700723#ifdef ANDROID_RGB
724 cinfo.dither_mode = JDITHER_NONE;
725#endif
726
727 /* image_width and image_height are the original dimensions, available
728 after jpeg_read_header(). To see the scaled dimensions, we have to call
729 jpeg_start_decompress(), and then read output_width and output_height.
730 */
731 if (!jpeg_start_decompress(&cinfo)) {
scroggo2a120802014-10-22 12:07:00 -0700732 return return_false(cinfo, "start_decompress YUV8");
sugoib227e372014-10-16 13:10:57 -0700733 }
734
mtkleinb3e5e4d2015-03-25 13:13:43 -0700735 // Seems like jpeg_start_decompress is updating our opinion of whether cinfo represents YUV.
736 // Again, not really an error.
737 if (!appears_to_be_yuv(cinfo)) {
738 return false;
739 }
740
sugoib227e372014-10-16 13:10:57 -0700741 if (!output_raw_data(cinfo, planes, rowBytes)) {
scroggo2a120802014-10-22 12:07:00 -0700742 return return_false(cinfo, "output_raw_data");
sugoib227e372014-10-16 13:10:57 -0700743 }
744
745 update_components_sizes(cinfo, componentSizes, kActualSize_SizeType);
746 jpeg_finish_decompress(&cinfo);
747
halcanary96fcdcc2015-08-27 07:41:13 -0700748 if (nullptr != colorSpace) {
sugoib227e372014-10-16 13:10:57 -0700749 *colorSpace = kJPEG_SkYUVColorSpace;
750 }
751
752 return true;
753}
754
scroggo2a120802014-10-22 12:07:00 -0700755///////////////////////////////////////////////////////////////////////////////
756
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000757#include "SkColorPriv.h"
758
759// taken from jcolor.c in libjpeg
760#if 0 // 16bit - precise but slow
761 #define CYR 19595 // 0.299
762 #define CYG 38470 // 0.587
763 #define CYB 7471 // 0.114
764
765 #define CUR -11059 // -0.16874
766 #define CUG -21709 // -0.33126
767 #define CUB 32768 // 0.5
768
769 #define CVR 32768 // 0.5
770 #define CVG -27439 // -0.41869
771 #define CVB -5329 // -0.08131
772
773 #define CSHIFT 16
774#else // 8bit - fast, slightly less precise
775 #define CYR 77 // 0.299
776 #define CYG 150 // 0.587
777 #define CYB 29 // 0.114
778
779 #define CUR -43 // -0.16874
780 #define CUG -85 // -0.33126
781 #define CUB 128 // 0.5
782
783 #define CVR 128 // 0.5
784 #define CVG -107 // -0.41869
785 #define CVB -21 // -0.08131
786
787 #define CSHIFT 8
788#endif
789
790static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
791 int r = SkGetPackedR32(c);
792 int g = SkGetPackedG32(c);
793 int b = SkGetPackedB32(c);
794
795 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
796 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
797 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
798
799 dst[0] = SkToU8(y);
800 dst[1] = SkToU8(u + 128);
801 dst[2] = SkToU8(v + 128);
802}
803
804static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
805 int r = SkGetPackedR4444(c);
806 int g = SkGetPackedG4444(c);
807 int b = SkGetPackedB4444(c);
808
809 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
810 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
811 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
812
813 dst[0] = SkToU8(y);
814 dst[1] = SkToU8(u + 128);
815 dst[2] = SkToU8(v + 128);
816}
817
818static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
819 int r = SkGetPackedR16(c);
820 int g = SkGetPackedG16(c);
821 int b = SkGetPackedB16(c);
822
823 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
824 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
825 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
826
827 dst[0] = SkToU8(y);
828 dst[1] = SkToU8(u + 128);
829 dst[2] = SkToU8(v + 128);
830}
831
832///////////////////////////////////////////////////////////////////////////////
833
834typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
835 const void* SK_RESTRICT src, int width,
836 const SkPMColor* SK_RESTRICT ctable);
837
838static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
839 const void* SK_RESTRICT srcRow, int width,
840 const SkPMColor*) {
841 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
842 while (--width >= 0) {
843#ifdef WE_CONVERT_TO_YUV
844 rgb2yuv_32(dst, *src++);
845#else
846 uint32_t c = *src++;
847 dst[0] = SkGetPackedR32(c);
848 dst[1] = SkGetPackedG32(c);
849 dst[2] = SkGetPackedB32(c);
850#endif
851 dst += 3;
852 }
853}
854
855static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
856 const void* SK_RESTRICT srcRow, int width,
857 const SkPMColor*) {
858 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
859 while (--width >= 0) {
860#ifdef WE_CONVERT_TO_YUV
861 rgb2yuv_4444(dst, *src++);
862#else
863 SkPMColor16 c = *src++;
864 dst[0] = SkPacked4444ToR32(c);
865 dst[1] = SkPacked4444ToG32(c);
866 dst[2] = SkPacked4444ToB32(c);
867#endif
868 dst += 3;
869 }
870}
871
872static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
873 const void* SK_RESTRICT srcRow, int width,
874 const SkPMColor*) {
875 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
876 while (--width >= 0) {
877#ifdef WE_CONVERT_TO_YUV
878 rgb2yuv_16(dst, *src++);
879#else
880 uint16_t c = *src++;
881 dst[0] = SkPacked16ToR32(c);
882 dst[1] = SkPacked16ToG32(c);
883 dst[2] = SkPacked16ToB32(c);
884#endif
885 dst += 3;
886 }
887}
888
889static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
890 const void* SK_RESTRICT srcRow, int width,
891 const SkPMColor* SK_RESTRICT ctable) {
892 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
893 while (--width >= 0) {
894#ifdef WE_CONVERT_TO_YUV
895 rgb2yuv_32(dst, ctable[*src++]);
896#else
897 uint32_t c = ctable[*src++];
898 dst[0] = SkGetPackedR32(c);
899 dst[1] = SkGetPackedG32(c);
900 dst[2] = SkGetPackedB32(c);
901#endif
902 dst += 3;
903 }
904}
905
906static WriteScanline ChooseWriter(const SkBitmap& bm) {
reed6c225732014-06-09 19:52:07 -0700907 switch (bm.colorType()) {
908 case kN32_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000909 return Write_32_YUV;
reed6c225732014-06-09 19:52:07 -0700910 case kRGB_565_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000911 return Write_16_YUV;
reed6c225732014-06-09 19:52:07 -0700912 case kARGB_4444_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000913 return Write_4444_YUV;
reed6c225732014-06-09 19:52:07 -0700914 case kIndex_8_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000915 return Write_Index_YUV;
916 default:
halcanary96fcdcc2015-08-27 07:41:13 -0700917 return nullptr;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000918 }
919}
920
921class SkJPEGImageEncoder : public SkImageEncoder {
922protected:
923 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
924#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +0000925 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000926#endif
927
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000928 SkAutoLockPixels alp(bm);
halcanary96fcdcc2015-08-27 07:41:13 -0700929 if (nullptr == bm.getPixels()) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000930 return false;
931 }
932
933 jpeg_compress_struct cinfo;
934 skjpeg_error_mgr sk_err;
935 skjpeg_destination_mgr sk_wstream(stream);
936
937 // allocate these before set call setjmp
scroggo565901d2015-12-10 10:44:13 -0800938 SkAutoTMalloc<uint8_t> oneRow;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000939
940 cinfo.err = jpeg_std_error(&sk_err);
941 sk_err.error_exit = skjpeg_error_exit;
942 if (setjmp(sk_err.fJmpBuf)) {
943 return false;
944 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000945
mtklein@google.com8d725b22013-07-24 16:20:05 +0000946 // Keep after setjmp or mark volatile.
947 const WriteScanline writer = ChooseWriter(bm);
halcanary96fcdcc2015-08-27 07:41:13 -0700948 if (nullptr == writer) {
mtklein@google.com8d725b22013-07-24 16:20:05 +0000949 return false;
950 }
951
952 jpeg_create_compress(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000953 cinfo.dest = &sk_wstream;
954 cinfo.image_width = bm.width();
955 cinfo.image_height = bm.height();
956 cinfo.input_components = 3;
957#ifdef WE_CONVERT_TO_YUV
958 cinfo.in_color_space = JCS_YCbCr;
959#else
960 cinfo.in_color_space = JCS_RGB;
961#endif
962 cinfo.input_gamma = 1;
963
964 jpeg_set_defaults(&cinfo);
benjaminwagner0a356202016-01-14 18:13:32 -0800965 cinfo.optimize_coding = TRUE;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000966 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +0000967#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000968 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +0000969#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000970
971 jpeg_start_compress(&cinfo, TRUE);
972
973 const int width = bm.width();
scroggo565901d2015-12-10 10:44:13 -0800974 uint8_t* oneRowP = oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000975
halcanary96fcdcc2015-08-27 07:41:13 -0700976 const SkPMColor* colors = bm.getColorTable() ? bm.getColorTable()->readColors() : nullptr;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000977 const void* srcRow = bm.getPixels();
978
979 while (cinfo.next_scanline < cinfo.image_height) {
980 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
981
982 writer(oneRowP, srcRow, width, colors);
983 row_pointer[0] = oneRowP;
984 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
985 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
986 }
987
988 jpeg_finish_compress(&cinfo);
989 jpeg_destroy_compress(&cinfo);
990
991 return true;
992 }
993};
994
995///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +0000996DEFINE_DECODER_CREATOR(JPEGImageDecoder);
997DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
998///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000999
scroggo@google.comb5571b32013-09-25 21:34:24 +00001000static bool is_jpeg(SkStreamRewindable* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001001 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001002 static const size_t HEADER_SIZE = sizeof(gHeader);
1003
1004 char buffer[HEADER_SIZE];
1005 size_t len = stream->read(buffer, HEADER_SIZE);
1006
1007 if (len != HEADER_SIZE) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001008 return false; // can't read enough
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001009 }
1010 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001011 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001012 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001013 return true;
1014}
1015
scroggo@google.comb5571b32013-09-25 21:34:24 +00001016
1017static SkImageDecoder* sk_libjpeg_dfactory(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001018 if (is_jpeg(stream)) {
halcanary385fe4d2015-08-26 13:07:48 -07001019 return new SkJPEGImageDecoder;
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001020 }
halcanary96fcdcc2015-08-27 07:41:13 -07001021 return nullptr;
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001022}
1023
scroggo@google.comb5571b32013-09-25 21:34:24 +00001024static SkImageDecoder::Format get_format_jpeg(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001025 if (is_jpeg(stream)) {
1026 return SkImageDecoder::kJPEG_Format;
1027 }
1028 return SkImageDecoder::kUnknown_Format;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001029}
1030
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001031static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
halcanary96fcdcc2015-08-27 07:41:13 -07001032 return (SkImageEncoder::kJPEG_Type == t) ? new SkJPEGImageEncoder : nullptr;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001033}
1034
mtklein@google.combd6343b2013-09-04 17:20:18 +00001035static SkImageDecoder_DecodeReg gDReg(sk_libjpeg_dfactory);
1036static SkImageDecoder_FormatReg gFormatReg(get_format_jpeg);
1037static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory);