gfx/skia/trunk/src/images/SkImageDecoder_libjpeg.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /*
michael@0 2 * Copyright 2007 The Android Open Source Project
michael@0 3 *
michael@0 4 * Use of this source code is governed by a BSD-style license that can be
michael@0 5 * found in the LICENSE file.
michael@0 6 */
michael@0 7
michael@0 8
michael@0 9 #include "SkImageDecoder.h"
michael@0 10 #include "SkImageEncoder.h"
michael@0 11 #include "SkJpegUtility.h"
michael@0 12 #include "SkColorPriv.h"
michael@0 13 #include "SkDither.h"
michael@0 14 #include "SkScaledBitmapSampler.h"
michael@0 15 #include "SkStream.h"
michael@0 16 #include "SkTemplates.h"
michael@0 17 #include "SkTime.h"
michael@0 18 #include "SkUtils.h"
michael@0 19 #include "SkRTConf.h"
michael@0 20 #include "SkRect.h"
michael@0 21 #include "SkCanvas.h"
michael@0 22
michael@0 23
michael@0 24 #include <stdio.h>
michael@0 25 extern "C" {
michael@0 26 #include "jpeglib.h"
michael@0 27 #include "jerror.h"
michael@0 28 }
michael@0 29
michael@0 30 // These enable timing code that report milliseconds for an encoding/decoding
michael@0 31 //#define TIME_ENCODE
michael@0 32 //#define TIME_DECODE
michael@0 33
michael@0 34 // this enables our rgb->yuv code, which is faster than libjpeg on ARM
michael@0 35 #define WE_CONVERT_TO_YUV
michael@0 36
michael@0 37 // If ANDROID_RGB is defined by in the jpeg headers it indicates that jpeg offers
michael@0 38 // support for two additional formats (1) JCS_RGBA_8888 and (2) JCS_RGB_565.
michael@0 39
michael@0 40 #if defined(SK_DEBUG)
michael@0 41 #define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS false
michael@0 42 #define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_ERRORS false
michael@0 43 #else // !defined(SK_DEBUG)
michael@0 44 #define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS true
michael@0 45 #define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_ERRORS true
michael@0 46 #endif // defined(SK_DEBUG)
michael@0 47 SK_CONF_DECLARE(bool, c_suppressJPEGImageDecoderWarnings,
michael@0 48 "images.jpeg.suppressDecoderWarnings",
michael@0 49 DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS,
michael@0 50 "Suppress most JPG warnings when calling decode functions.");
michael@0 51 SK_CONF_DECLARE(bool, c_suppressJPEGImageDecoderErrors,
michael@0 52 "images.jpeg.suppressDecoderErrors",
michael@0 53 DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_ERRORS,
michael@0 54 "Suppress most JPG error messages when decode "
michael@0 55 "function fails.");
michael@0 56
michael@0 57 //////////////////////////////////////////////////////////////////////////
michael@0 58 //////////////////////////////////////////////////////////////////////////
michael@0 59
michael@0 60 static void overwrite_mem_buffer_size(jpeg_decompress_struct* cinfo) {
michael@0 61 #ifdef SK_BUILD_FOR_ANDROID
michael@0 62 /* Check if the device indicates that it has a large amount of system memory
michael@0 63 * if so, increase the memory allocation to 30MB instead of the default 5MB.
michael@0 64 */
michael@0 65 #ifdef ANDROID_LARGE_MEMORY_DEVICE
michael@0 66 cinfo->mem->max_memory_to_use = 30 * 1024 * 1024;
michael@0 67 #else
michael@0 68 cinfo->mem->max_memory_to_use = 5 * 1024 * 1024;
michael@0 69 #endif
michael@0 70 #endif // SK_BUILD_FOR_ANDROID
michael@0 71 }
michael@0 72
michael@0 73 //////////////////////////////////////////////////////////////////////////
michael@0 74 //////////////////////////////////////////////////////////////////////////
michael@0 75
michael@0 76 static void do_nothing_emit_message(jpeg_common_struct*, int) {
michael@0 77 /* do nothing */
michael@0 78 }
michael@0 79 static void do_nothing_output_message(j_common_ptr) {
michael@0 80 /* do nothing */
michael@0 81 }
michael@0 82
michael@0 83 static void initialize_info(jpeg_decompress_struct* cinfo, skjpeg_source_mgr* src_mgr) {
michael@0 84 SkASSERT(cinfo != NULL);
michael@0 85 SkASSERT(src_mgr != NULL);
michael@0 86 jpeg_create_decompress(cinfo);
michael@0 87 overwrite_mem_buffer_size(cinfo);
michael@0 88 cinfo->src = src_mgr;
michael@0 89 /* To suppress warnings with a SK_DEBUG binary, set the
michael@0 90 * environment variable "skia_images_jpeg_suppressDecoderWarnings"
michael@0 91 * to "true". Inside a program that links to skia:
michael@0 92 * SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true); */
michael@0 93 if (c_suppressJPEGImageDecoderWarnings) {
michael@0 94 cinfo->err->emit_message = &do_nothing_emit_message;
michael@0 95 }
michael@0 96 /* To suppress error messages with a SK_DEBUG binary, set the
michael@0 97 * environment variable "skia_images_jpeg_suppressDecoderErrors"
michael@0 98 * to "true". Inside a program that links to skia:
michael@0 99 * SK_CONF_SET("images.jpeg.suppressDecoderErrors", true); */
michael@0 100 if (c_suppressJPEGImageDecoderErrors) {
michael@0 101 cinfo->err->output_message = &do_nothing_output_message;
michael@0 102 }
michael@0 103 }
michael@0 104
michael@0 105 #ifdef SK_BUILD_FOR_ANDROID
michael@0 106 class SkJPEGImageIndex {
michael@0 107 public:
michael@0 108 SkJPEGImageIndex(SkStreamRewindable* stream, SkImageDecoder* decoder)
michael@0 109 : fSrcMgr(stream, decoder)
michael@0 110 , fInfoInitialized(false)
michael@0 111 , fHuffmanCreated(false)
michael@0 112 , fDecompressStarted(false)
michael@0 113 {
michael@0 114 SkDEBUGCODE(fReadHeaderSucceeded = false;)
michael@0 115 }
michael@0 116
michael@0 117 ~SkJPEGImageIndex() {
michael@0 118 if (fHuffmanCreated) {
michael@0 119 // Set to false before calling the libjpeg function, in case
michael@0 120 // the libjpeg function calls longjmp. Our setjmp handler may
michael@0 121 // attempt to delete this SkJPEGImageIndex, thus entering this
michael@0 122 // destructor again. Setting fHuffmanCreated to false first
michael@0 123 // prevents an infinite loop.
michael@0 124 fHuffmanCreated = false;
michael@0 125 jpeg_destroy_huffman_index(&fHuffmanIndex);
michael@0 126 }
michael@0 127 if (fDecompressStarted) {
michael@0 128 // Like fHuffmanCreated, set to false before calling libjpeg
michael@0 129 // function to prevent potential infinite loop.
michael@0 130 fDecompressStarted = false;
michael@0 131 jpeg_finish_decompress(&fCInfo);
michael@0 132 }
michael@0 133 if (fInfoInitialized) {
michael@0 134 this->destroyInfo();
michael@0 135 }
michael@0 136 }
michael@0 137
michael@0 138 /**
michael@0 139 * Destroy the cinfo struct.
michael@0 140 * After this call, if a huffman index was already built, it
michael@0 141 * can be used after calling initializeInfoAndReadHeader
michael@0 142 * again. Must not be called after startTileDecompress except
michael@0 143 * in the destructor.
michael@0 144 */
michael@0 145 void destroyInfo() {
michael@0 146 SkASSERT(fInfoInitialized);
michael@0 147 SkASSERT(!fDecompressStarted);
michael@0 148 // Like fHuffmanCreated, set to false before calling libjpeg
michael@0 149 // function to prevent potential infinite loop.
michael@0 150 fInfoInitialized = false;
michael@0 151 jpeg_destroy_decompress(&fCInfo);
michael@0 152 SkDEBUGCODE(fReadHeaderSucceeded = false;)
michael@0 153 }
michael@0 154
michael@0 155 /**
michael@0 156 * Initialize the cinfo struct.
michael@0 157 * Calls jpeg_create_decompress, makes customizations, and
michael@0 158 * finally calls jpeg_read_header. Returns true if jpeg_read_header
michael@0 159 * returns JPEG_HEADER_OK.
michael@0 160 * If cinfo was already initialized, destroyInfo must be called to
michael@0 161 * destroy the old one. Must not be called after startTileDecompress.
michael@0 162 */
michael@0 163 bool initializeInfoAndReadHeader() {
michael@0 164 SkASSERT(!fInfoInitialized && !fDecompressStarted);
michael@0 165 initialize_info(&fCInfo, &fSrcMgr);
michael@0 166 fInfoInitialized = true;
michael@0 167 const bool success = (JPEG_HEADER_OK == jpeg_read_header(&fCInfo, true));
michael@0 168 SkDEBUGCODE(fReadHeaderSucceeded = success;)
michael@0 169 return success;
michael@0 170 }
michael@0 171
michael@0 172 jpeg_decompress_struct* cinfo() { return &fCInfo; }
michael@0 173
michael@0 174 huffman_index* huffmanIndex() { return &fHuffmanIndex; }
michael@0 175
michael@0 176 /**
michael@0 177 * Build the index to be used for tile based decoding.
michael@0 178 * Must only be called after a successful call to
michael@0 179 * initializeInfoAndReadHeader and must not be called more
michael@0 180 * than once.
michael@0 181 */
michael@0 182 bool buildHuffmanIndex() {
michael@0 183 SkASSERT(fReadHeaderSucceeded);
michael@0 184 SkASSERT(!fHuffmanCreated);
michael@0 185 jpeg_create_huffman_index(&fCInfo, &fHuffmanIndex);
michael@0 186 SkASSERT(1 == fCInfo.scale_num && 1 == fCInfo.scale_denom);
michael@0 187 fHuffmanCreated = jpeg_build_huffman_index(&fCInfo, &fHuffmanIndex);
michael@0 188 return fHuffmanCreated;
michael@0 189 }
michael@0 190
michael@0 191 /**
michael@0 192 * Start tile based decoding. Must only be called after a
michael@0 193 * successful call to buildHuffmanIndex, and must only be
michael@0 194 * called once.
michael@0 195 */
michael@0 196 bool startTileDecompress() {
michael@0 197 SkASSERT(fHuffmanCreated);
michael@0 198 SkASSERT(fReadHeaderSucceeded);
michael@0 199 SkASSERT(!fDecompressStarted);
michael@0 200 if (jpeg_start_tile_decompress(&fCInfo)) {
michael@0 201 fDecompressStarted = true;
michael@0 202 return true;
michael@0 203 }
michael@0 204 return false;
michael@0 205 }
michael@0 206
michael@0 207 private:
michael@0 208 skjpeg_source_mgr fSrcMgr;
michael@0 209 jpeg_decompress_struct fCInfo;
michael@0 210 huffman_index fHuffmanIndex;
michael@0 211 bool fInfoInitialized;
michael@0 212 bool fHuffmanCreated;
michael@0 213 bool fDecompressStarted;
michael@0 214 SkDEBUGCODE(bool fReadHeaderSucceeded;)
michael@0 215 };
michael@0 216 #endif
michael@0 217
michael@0 218 class SkJPEGImageDecoder : public SkImageDecoder {
michael@0 219 public:
michael@0 220 #ifdef SK_BUILD_FOR_ANDROID
michael@0 221 SkJPEGImageDecoder() {
michael@0 222 fImageIndex = NULL;
michael@0 223 fImageWidth = 0;
michael@0 224 fImageHeight = 0;
michael@0 225 }
michael@0 226
michael@0 227 virtual ~SkJPEGImageDecoder() {
michael@0 228 SkDELETE(fImageIndex);
michael@0 229 }
michael@0 230 #endif
michael@0 231
michael@0 232 virtual Format getFormat() const {
michael@0 233 return kJPEG_Format;
michael@0 234 }
michael@0 235
michael@0 236 protected:
michael@0 237 #ifdef SK_BUILD_FOR_ANDROID
michael@0 238 virtual bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE;
michael@0 239 virtual bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE;
michael@0 240 #endif
michael@0 241 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
michael@0 242
michael@0 243 private:
michael@0 244 #ifdef SK_BUILD_FOR_ANDROID
michael@0 245 SkJPEGImageIndex* fImageIndex;
michael@0 246 int fImageWidth;
michael@0 247 int fImageHeight;
michael@0 248 #endif
michael@0 249
michael@0 250 /**
michael@0 251 * Determine the appropriate bitmap config and out_color_space based on
michael@0 252 * both the preference of the caller and the jpeg_color_space on the
michael@0 253 * jpeg_decompress_struct passed in.
michael@0 254 * Must be called after jpeg_read_header.
michael@0 255 */
michael@0 256 SkBitmap::Config getBitmapConfig(jpeg_decompress_struct*);
michael@0 257
michael@0 258 typedef SkImageDecoder INHERITED;
michael@0 259 };
michael@0 260
michael@0 261 //////////////////////////////////////////////////////////////////////////
michael@0 262
michael@0 263 /* Automatically clean up after throwing an exception */
michael@0 264 class JPEGAutoClean {
michael@0 265 public:
michael@0 266 JPEGAutoClean(): cinfo_ptr(NULL) {}
michael@0 267 ~JPEGAutoClean() {
michael@0 268 if (cinfo_ptr) {
michael@0 269 jpeg_destroy_decompress(cinfo_ptr);
michael@0 270 }
michael@0 271 }
michael@0 272 void set(jpeg_decompress_struct* info) {
michael@0 273 cinfo_ptr = info;
michael@0 274 }
michael@0 275 private:
michael@0 276 jpeg_decompress_struct* cinfo_ptr;
michael@0 277 };
michael@0 278
michael@0 279 ///////////////////////////////////////////////////////////////////////////////
michael@0 280
michael@0 281 /* If we need to better match the request, we might examine the image and
michael@0 282 output dimensions, and determine if the downsampling jpeg provided is
michael@0 283 not sufficient. If so, we can recompute a modified sampleSize value to
michael@0 284 make up the difference.
michael@0 285
michael@0 286 To skip this additional scaling, just set sampleSize = 1; below.
michael@0 287 */
michael@0 288 static int recompute_sampleSize(int sampleSize,
michael@0 289 const jpeg_decompress_struct& cinfo) {
michael@0 290 return sampleSize * cinfo.output_width / cinfo.image_width;
michael@0 291 }
michael@0 292
michael@0 293 static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) {
michael@0 294 /* These are initialized to 0, so if they have non-zero values, we assume
michael@0 295 they are "valid" (i.e. have been computed by libjpeg)
michael@0 296 */
michael@0 297 return 0 != cinfo.output_width && 0 != cinfo.output_height;
michael@0 298 }
michael@0 299
michael@0 300 static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, int count) {
michael@0 301 for (int i = 0; i < count; i++) {
michael@0 302 JSAMPLE* rowptr = (JSAMPLE*)buffer;
michael@0 303 int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
michael@0 304 if (1 != row_count) {
michael@0 305 return false;
michael@0 306 }
michael@0 307 }
michael@0 308 return true;
michael@0 309 }
michael@0 310
michael@0 311 #ifdef SK_BUILD_FOR_ANDROID
michael@0 312 static bool skip_src_rows_tile(jpeg_decompress_struct* cinfo,
michael@0 313 huffman_index *index, void* buffer, int count) {
michael@0 314 for (int i = 0; i < count; i++) {
michael@0 315 JSAMPLE* rowptr = (JSAMPLE*)buffer;
michael@0 316 int row_count = jpeg_read_tile_scanline(cinfo, index, &rowptr);
michael@0 317 if (1 != row_count) {
michael@0 318 return false;
michael@0 319 }
michael@0 320 }
michael@0 321 return true;
michael@0 322 }
michael@0 323 #endif
michael@0 324
michael@0 325 // This guy exists just to aid in debugging, as it allows debuggers to just
michael@0 326 // set a break-point in one place to see all error exists.
michael@0 327 static bool return_false(const jpeg_decompress_struct& cinfo,
michael@0 328 const SkBitmap& bm, const char caller[]) {
michael@0 329 if (!(c_suppressJPEGImageDecoderErrors)) {
michael@0 330 char buffer[JMSG_LENGTH_MAX];
michael@0 331 cinfo.err->format_message((const j_common_ptr)&cinfo, buffer);
michael@0 332 SkDebugf("libjpeg error %d <%s> from %s [%d %d]\n",
michael@0 333 cinfo.err->msg_code, buffer, caller, bm.width(), bm.height());
michael@0 334 }
michael@0 335 return false; // must always return false
michael@0 336 }
michael@0 337
michael@0 338 // Convert a scanline of CMYK samples to RGBX in place. Note that this
michael@0 339 // method moves the "scanline" pointer in its processing
michael@0 340 static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
michael@0 341 // At this point we've received CMYK pixels from libjpeg. We
michael@0 342 // perform a crude conversion to RGB (based on the formulae
michael@0 343 // from easyrgb.com):
michael@0 344 // CMYK -> CMY
michael@0 345 // C = ( C * (1 - K) + K ) // for each CMY component
michael@0 346 // CMY -> RGB
michael@0 347 // R = ( 1 - C ) * 255 // for each RGB component
michael@0 348 // Unfortunately we are seeing inverted CMYK so all the original terms
michael@0 349 // are 1-. This yields:
michael@0 350 // CMYK -> CMY
michael@0 351 // C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K
michael@0 352 // The conversion from CMY->RGB remains the same
michael@0 353 for (unsigned int x = 0; x < width; ++x, scanline += 4) {
michael@0 354 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]);
michael@0 355 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]);
michael@0 356 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]);
michael@0 357 scanline[3] = 255;
michael@0 358 }
michael@0 359 }
michael@0 360
michael@0 361 /**
michael@0 362 * Common code for setting the error manager.
michael@0 363 */
michael@0 364 static void set_error_mgr(jpeg_decompress_struct* cinfo, skjpeg_error_mgr* errorManager) {
michael@0 365 SkASSERT(cinfo != NULL);
michael@0 366 SkASSERT(errorManager != NULL);
michael@0 367 cinfo->err = jpeg_std_error(errorManager);
michael@0 368 errorManager->error_exit = skjpeg_error_exit;
michael@0 369 }
michael@0 370
michael@0 371 /**
michael@0 372 * Common code for turning off upsampling and smoothing. Turning these
michael@0 373 * off helps performance without showing noticable differences in the
michael@0 374 * resulting bitmap.
michael@0 375 */
michael@0 376 static void turn_off_visual_optimizations(jpeg_decompress_struct* cinfo) {
michael@0 377 SkASSERT(cinfo != NULL);
michael@0 378 /* this gives about 30% performance improvement. In theory it may
michael@0 379 reduce the visual quality, in practice I'm not seeing a difference
michael@0 380 */
michael@0 381 cinfo->do_fancy_upsampling = 0;
michael@0 382
michael@0 383 /* this gives another few percents */
michael@0 384 cinfo->do_block_smoothing = 0;
michael@0 385 }
michael@0 386
michael@0 387 /**
michael@0 388 * Common code for setting the dct method.
michael@0 389 */
michael@0 390 static void set_dct_method(const SkImageDecoder& decoder, jpeg_decompress_struct* cinfo) {
michael@0 391 SkASSERT(cinfo != NULL);
michael@0 392 #ifdef DCT_IFAST_SUPPORTED
michael@0 393 if (decoder.getPreferQualityOverSpeed()) {
michael@0 394 cinfo->dct_method = JDCT_ISLOW;
michael@0 395 } else {
michael@0 396 cinfo->dct_method = JDCT_IFAST;
michael@0 397 }
michael@0 398 #else
michael@0 399 cinfo->dct_method = JDCT_ISLOW;
michael@0 400 #endif
michael@0 401 }
michael@0 402
michael@0 403 SkBitmap::Config SkJPEGImageDecoder::getBitmapConfig(jpeg_decompress_struct* cinfo) {
michael@0 404 SkASSERT(cinfo != NULL);
michael@0 405
michael@0 406 SrcDepth srcDepth = k32Bit_SrcDepth;
michael@0 407 if (JCS_GRAYSCALE == cinfo->jpeg_color_space) {
michael@0 408 srcDepth = k8BitGray_SrcDepth;
michael@0 409 }
michael@0 410
michael@0 411 SkBitmap::Config config = this->getPrefConfig(srcDepth, /*hasAlpha*/ false);
michael@0 412 switch (config) {
michael@0 413 case SkBitmap::kA8_Config:
michael@0 414 // Only respect A8 config if the original is grayscale,
michael@0 415 // in which case we will treat the grayscale as alpha
michael@0 416 // values.
michael@0 417 if (cinfo->jpeg_color_space != JCS_GRAYSCALE) {
michael@0 418 config = SkBitmap::kARGB_8888_Config;
michael@0 419 }
michael@0 420 break;
michael@0 421 case SkBitmap::kARGB_8888_Config:
michael@0 422 // Fall through.
michael@0 423 case SkBitmap::kARGB_4444_Config:
michael@0 424 // Fall through.
michael@0 425 case SkBitmap::kRGB_565_Config:
michael@0 426 // These are acceptable destination configs.
michael@0 427 break;
michael@0 428 default:
michael@0 429 // Force all other configs to 8888.
michael@0 430 config = SkBitmap::kARGB_8888_Config;
michael@0 431 break;
michael@0 432 }
michael@0 433
michael@0 434 switch (cinfo->jpeg_color_space) {
michael@0 435 case JCS_CMYK:
michael@0 436 // Fall through.
michael@0 437 case JCS_YCCK:
michael@0 438 // libjpeg cannot convert from CMYK or YCCK to RGB - here we set up
michael@0 439 // so libjpeg will give us CMYK samples back and we will later
michael@0 440 // manually convert them to RGB
michael@0 441 cinfo->out_color_space = JCS_CMYK;
michael@0 442 break;
michael@0 443 case JCS_GRAYSCALE:
michael@0 444 if (SkBitmap::kA8_Config == config) {
michael@0 445 cinfo->out_color_space = JCS_GRAYSCALE;
michael@0 446 break;
michael@0 447 }
michael@0 448 // The data is JCS_GRAYSCALE, but the caller wants some sort of RGB
michael@0 449 // config. Fall through to set to the default.
michael@0 450 default:
michael@0 451 cinfo->out_color_space = JCS_RGB;
michael@0 452 break;
michael@0 453 }
michael@0 454 return config;
michael@0 455 }
michael@0 456
michael@0 457 #ifdef ANDROID_RGB
michael@0 458 /**
michael@0 459 * Based on the config and dither mode, adjust out_color_space and
michael@0 460 * dither_mode of cinfo.
michael@0 461 */
michael@0 462 static void adjust_out_color_space_and_dither(jpeg_decompress_struct* cinfo,
michael@0 463 SkBitmap::Config config,
michael@0 464 const SkImageDecoder& decoder) {
michael@0 465 SkASSERT(cinfo != NULL);
michael@0 466 cinfo->dither_mode = JDITHER_NONE;
michael@0 467 if (JCS_CMYK == cinfo->out_color_space) {
michael@0 468 return;
michael@0 469 }
michael@0 470 switch(config) {
michael@0 471 case SkBitmap::kARGB_8888_Config:
michael@0 472 cinfo->out_color_space = JCS_RGBA_8888;
michael@0 473 break;
michael@0 474 case SkBitmap::kRGB_565_Config:
michael@0 475 cinfo->out_color_space = JCS_RGB_565;
michael@0 476 if (decoder.getDitherImage()) {
michael@0 477 cinfo->dither_mode = JDITHER_ORDERED;
michael@0 478 }
michael@0 479 break;
michael@0 480 default:
michael@0 481 break;
michael@0 482 }
michael@0 483 }
michael@0 484 #endif
michael@0 485
michael@0 486
michael@0 487 /**
michael@0 488 Sets all pixels in given bitmap to SK_ColorWHITE for all rows >= y.
michael@0 489 Used when decoding fails partway through reading scanlines to fill
michael@0 490 remaining lines. */
michael@0 491 static void fill_below_level(int y, SkBitmap* bitmap) {
michael@0 492 SkIRect rect = SkIRect::MakeLTRB(0, y, bitmap->width(), bitmap->height());
michael@0 493 SkCanvas canvas(*bitmap);
michael@0 494 canvas.clipRect(SkRect::Make(rect));
michael@0 495 canvas.drawColor(SK_ColorWHITE);
michael@0 496 }
michael@0 497
michael@0 498 /**
michael@0 499 * Get the config and bytes per pixel of the source data. Return
michael@0 500 * whether the data is supported.
michael@0 501 */
michael@0 502 static bool get_src_config(const jpeg_decompress_struct& cinfo,
michael@0 503 SkScaledBitmapSampler::SrcConfig* sc,
michael@0 504 int* srcBytesPerPixel) {
michael@0 505 SkASSERT(sc != NULL && srcBytesPerPixel != NULL);
michael@0 506 if (JCS_CMYK == cinfo.out_color_space) {
michael@0 507 // In this case we will manually convert the CMYK values to RGB
michael@0 508 *sc = SkScaledBitmapSampler::kRGBX;
michael@0 509 // The CMYK work-around relies on 4 components per pixel here
michael@0 510 *srcBytesPerPixel = 4;
michael@0 511 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
michael@0 512 *sc = SkScaledBitmapSampler::kRGB;
michael@0 513 *srcBytesPerPixel = 3;
michael@0 514 #ifdef ANDROID_RGB
michael@0 515 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
michael@0 516 *sc = SkScaledBitmapSampler::kRGBX;
michael@0 517 *srcBytesPerPixel = 4;
michael@0 518 } else if (JCS_RGB_565 == cinfo.out_color_space) {
michael@0 519 *sc = SkScaledBitmapSampler::kRGB_565;
michael@0 520 *srcBytesPerPixel = 2;
michael@0 521 #endif
michael@0 522 } else if (1 == cinfo.out_color_components &&
michael@0 523 JCS_GRAYSCALE == cinfo.out_color_space) {
michael@0 524 *sc = SkScaledBitmapSampler::kGray;
michael@0 525 *srcBytesPerPixel = 1;
michael@0 526 } else {
michael@0 527 return false;
michael@0 528 }
michael@0 529 return true;
michael@0 530 }
michael@0 531
michael@0 532 bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
michael@0 533 #ifdef TIME_DECODE
michael@0 534 SkAutoTime atm("JPEG Decode");
michael@0 535 #endif
michael@0 536
michael@0 537 JPEGAutoClean autoClean;
michael@0 538
michael@0 539 jpeg_decompress_struct cinfo;
michael@0 540 skjpeg_source_mgr srcManager(stream, this);
michael@0 541
michael@0 542 skjpeg_error_mgr errorManager;
michael@0 543 set_error_mgr(&cinfo, &errorManager);
michael@0 544
michael@0 545 // All objects need to be instantiated before this setjmp call so that
michael@0 546 // they will be cleaned up properly if an error occurs.
michael@0 547 if (setjmp(errorManager.fJmpBuf)) {
michael@0 548 return return_false(cinfo, *bm, "setjmp");
michael@0 549 }
michael@0 550
michael@0 551 initialize_info(&cinfo, &srcManager);
michael@0 552 autoClean.set(&cinfo);
michael@0 553
michael@0 554 int status = jpeg_read_header(&cinfo, true);
michael@0 555 if (status != JPEG_HEADER_OK) {
michael@0 556 return return_false(cinfo, *bm, "read_header");
michael@0 557 }
michael@0 558
michael@0 559 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
michael@0 560 can) much faster that we, just use their num/denom api to approximate
michael@0 561 the size.
michael@0 562 */
michael@0 563 int sampleSize = this->getSampleSize();
michael@0 564
michael@0 565 set_dct_method(*this, &cinfo);
michael@0 566
michael@0 567 SkASSERT(1 == cinfo.scale_num);
michael@0 568 cinfo.scale_denom = sampleSize;
michael@0 569
michael@0 570 turn_off_visual_optimizations(&cinfo);
michael@0 571
michael@0 572 const SkBitmap::Config config = this->getBitmapConfig(&cinfo);
michael@0 573
michael@0 574 #ifdef ANDROID_RGB
michael@0 575 adjust_out_color_space_and_dither(&cinfo, config, *this);
michael@0 576 #endif
michael@0 577
michael@0 578 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
michael@0 579 // Assume an A8 bitmap is not opaque to avoid the check of each
michael@0 580 // individual pixel. It is very unlikely to be opaque, since
michael@0 581 // an opaque A8 bitmap would not be very interesting.
michael@0 582 // Otherwise, a jpeg image is opaque.
michael@0 583 return bm->setConfig(config, cinfo.image_width, cinfo.image_height, 0,
michael@0 584 SkBitmap::kA8_Config == config ?
michael@0 585 kPremul_SkAlphaType : kOpaque_SkAlphaType);
michael@0 586 }
michael@0 587
michael@0 588 /* image_width and image_height are the original dimensions, available
michael@0 589 after jpeg_read_header(). To see the scaled dimensions, we have to call
michael@0 590 jpeg_start_decompress(), and then read output_width and output_height.
michael@0 591 */
michael@0 592 if (!jpeg_start_decompress(&cinfo)) {
michael@0 593 /* If we failed here, we may still have enough information to return
michael@0 594 to the caller if they just wanted (subsampled bounds). If sampleSize
michael@0 595 was 1, then we would have already returned. Thus we just check if
michael@0 596 we're in kDecodeBounds_Mode, and that we have valid output sizes.
michael@0 597
michael@0 598 One reason to fail here is that we have insufficient stream data
michael@0 599 to complete the setup. However, output dimensions seem to get
michael@0 600 computed very early, which is why this special check can pay off.
michael@0 601 */
michael@0 602 if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
michael@0 603 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
michael@0 604 recompute_sampleSize(sampleSize, cinfo));
michael@0 605 // Assume an A8 bitmap is not opaque to avoid the check of each
michael@0 606 // individual pixel. It is very unlikely to be opaque, since
michael@0 607 // an opaque A8 bitmap would not be very interesting.
michael@0 608 // Otherwise, a jpeg image is opaque.
michael@0 609 return bm->setConfig(config, smpl.scaledWidth(), smpl.scaledHeight(),
michael@0 610 0, SkBitmap::kA8_Config == config ?
michael@0 611 kPremul_SkAlphaType : kOpaque_SkAlphaType);
michael@0 612 } else {
michael@0 613 return return_false(cinfo, *bm, "start_decompress");
michael@0 614 }
michael@0 615 }
michael@0 616 sampleSize = recompute_sampleSize(sampleSize, cinfo);
michael@0 617
michael@0 618 // should we allow the Chooser (if present) to pick a config for us???
michael@0 619 if (!this->chooseFromOneChoice(config, cinfo.output_width, cinfo.output_height)) {
michael@0 620 return return_false(cinfo, *bm, "chooseFromOneChoice");
michael@0 621 }
michael@0 622
michael@0 623 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
michael@0 624 // Assume an A8 bitmap is not opaque to avoid the check of each
michael@0 625 // individual pixel. It is very unlikely to be opaque, since
michael@0 626 // an opaque A8 bitmap would not be very interesting.
michael@0 627 // Otherwise, a jpeg image is opaque.
michael@0 628 bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight(), 0,
michael@0 629 SkBitmap::kA8_Config != config ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
michael@0 630 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
michael@0 631 return true;
michael@0 632 }
michael@0 633 if (!this->allocPixelRef(bm, NULL)) {
michael@0 634 return return_false(cinfo, *bm, "allocPixelRef");
michael@0 635 }
michael@0 636
michael@0 637 SkAutoLockPixels alp(*bm);
michael@0 638
michael@0 639 #ifdef ANDROID_RGB
michael@0 640 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
michael@0 641 a significant performance boost.
michael@0 642 */
michael@0 643 if (sampleSize == 1 &&
michael@0 644 ((config == SkBitmap::kARGB_8888_Config &&
michael@0 645 cinfo.out_color_space == JCS_RGBA_8888) ||
michael@0 646 (config == SkBitmap::kRGB_565_Config &&
michael@0 647 cinfo.out_color_space == JCS_RGB_565)))
michael@0 648 {
michael@0 649 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
michael@0 650 INT32 const bpr = bm->rowBytes();
michael@0 651
michael@0 652 while (cinfo.output_scanline < cinfo.output_height) {
michael@0 653 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
michael@0 654 if (0 == row_count) {
michael@0 655 // if row_count == 0, then we didn't get a scanline,
michael@0 656 // so return early. We will return a partial image.
michael@0 657 fill_below_level(cinfo.output_scanline, bm);
michael@0 658 cinfo.output_scanline = cinfo.output_height;
michael@0 659 break; // Skip to jpeg_finish_decompress()
michael@0 660 }
michael@0 661 if (this->shouldCancelDecode()) {
michael@0 662 return return_false(cinfo, *bm, "shouldCancelDecode");
michael@0 663 }
michael@0 664 rowptr += bpr;
michael@0 665 }
michael@0 666 jpeg_finish_decompress(&cinfo);
michael@0 667 return true;
michael@0 668 }
michael@0 669 #endif
michael@0 670
michael@0 671 // check for supported formats
michael@0 672 SkScaledBitmapSampler::SrcConfig sc;
michael@0 673 int srcBytesPerPixel;
michael@0 674
michael@0 675 if (!get_src_config(cinfo, &sc, &srcBytesPerPixel)) {
michael@0 676 return return_false(cinfo, *bm, "jpeg colorspace");
michael@0 677 }
michael@0 678
michael@0 679 if (!sampler.begin(bm, sc, *this)) {
michael@0 680 return return_false(cinfo, *bm, "sampler.begin");
michael@0 681 }
michael@0 682
michael@0 683 SkAutoMalloc srcStorage(cinfo.output_width * srcBytesPerPixel);
michael@0 684 uint8_t* srcRow = (uint8_t*)srcStorage.get();
michael@0 685
michael@0 686 // Possibly skip initial rows [sampler.srcY0]
michael@0 687 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
michael@0 688 return return_false(cinfo, *bm, "skip rows");
michael@0 689 }
michael@0 690
michael@0 691 // now loop through scanlines until y == bm->height() - 1
michael@0 692 for (int y = 0;; y++) {
michael@0 693 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
michael@0 694 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
michael@0 695 if (0 == row_count) {
michael@0 696 // if row_count == 0, then we didn't get a scanline,
michael@0 697 // so return early. We will return a partial image.
michael@0 698 fill_below_level(y, bm);
michael@0 699 cinfo.output_scanline = cinfo.output_height;
michael@0 700 break; // Skip to jpeg_finish_decompress()
michael@0 701 }
michael@0 702 if (this->shouldCancelDecode()) {
michael@0 703 return return_false(cinfo, *bm, "shouldCancelDecode");
michael@0 704 }
michael@0 705
michael@0 706 if (JCS_CMYK == cinfo.out_color_space) {
michael@0 707 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
michael@0 708 }
michael@0 709
michael@0 710 sampler.next(srcRow);
michael@0 711 if (bm->height() - 1 == y) {
michael@0 712 // we're done
michael@0 713 break;
michael@0 714 }
michael@0 715
michael@0 716 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
michael@0 717 return return_false(cinfo, *bm, "skip rows");
michael@0 718 }
michael@0 719 }
michael@0 720
michael@0 721 // we formally skip the rest, so we don't get a complaint from libjpeg
michael@0 722 if (!skip_src_rows(&cinfo, srcRow,
michael@0 723 cinfo.output_height - cinfo.output_scanline)) {
michael@0 724 return return_false(cinfo, *bm, "skip rows");
michael@0 725 }
michael@0 726 jpeg_finish_decompress(&cinfo);
michael@0 727
michael@0 728 return true;
michael@0 729 }
michael@0 730
michael@0 731 #ifdef SK_BUILD_FOR_ANDROID
michael@0 732 bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
michael@0 733
michael@0 734 SkAutoTDelete<SkJPEGImageIndex> imageIndex(SkNEW_ARGS(SkJPEGImageIndex, (stream, this)));
michael@0 735 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
michael@0 736
michael@0 737 skjpeg_error_mgr sk_err;
michael@0 738 set_error_mgr(cinfo, &sk_err);
michael@0 739
michael@0 740 // All objects need to be instantiated before this setjmp call so that
michael@0 741 // they will be cleaned up properly if an error occurs.
michael@0 742 if (setjmp(sk_err.fJmpBuf)) {
michael@0 743 return false;
michael@0 744 }
michael@0 745
michael@0 746 // create the cinfo used to create/build the huffmanIndex
michael@0 747 if (!imageIndex->initializeInfoAndReadHeader()) {
michael@0 748 return false;
michael@0 749 }
michael@0 750
michael@0 751 if (!imageIndex->buildHuffmanIndex()) {
michael@0 752 return false;
michael@0 753 }
michael@0 754
michael@0 755 // destroy the cinfo used to create/build the huffman index
michael@0 756 imageIndex->destroyInfo();
michael@0 757
michael@0 758 // Init decoder to image decode mode
michael@0 759 if (!imageIndex->initializeInfoAndReadHeader()) {
michael@0 760 return false;
michael@0 761 }
michael@0 762
michael@0 763 // FIXME: This sets cinfo->out_color_space, which we may change later
michael@0 764 // based on the config in onDecodeSubset. This should be fine, since
michael@0 765 // jpeg_init_read_tile_scanline will check out_color_space again after
michael@0 766 // that change (when it calls jinit_color_deconverter).
michael@0 767 (void) this->getBitmapConfig(cinfo);
michael@0 768
michael@0 769 turn_off_visual_optimizations(cinfo);
michael@0 770
michael@0 771 // instead of jpeg_start_decompress() we start a tiled decompress
michael@0 772 if (!imageIndex->startTileDecompress()) {
michael@0 773 return false;
michael@0 774 }
michael@0 775
michael@0 776 SkASSERT(1 == cinfo->scale_num);
michael@0 777 fImageWidth = cinfo->output_width;
michael@0 778 fImageHeight = cinfo->output_height;
michael@0 779
michael@0 780 if (width) {
michael@0 781 *width = fImageWidth;
michael@0 782 }
michael@0 783 if (height) {
michael@0 784 *height = fImageHeight;
michael@0 785 }
michael@0 786
michael@0 787 SkDELETE(fImageIndex);
michael@0 788 fImageIndex = imageIndex.detach();
michael@0 789
michael@0 790 return true;
michael@0 791 }
michael@0 792
michael@0 793 bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
michael@0 794 if (NULL == fImageIndex) {
michael@0 795 return false;
michael@0 796 }
michael@0 797 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
michael@0 798
michael@0 799 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
michael@0 800 if (!rect.intersect(region)) {
michael@0 801 // If the requested region is entirely outside the image return false
michael@0 802 return false;
michael@0 803 }
michael@0 804
michael@0 805
michael@0 806 skjpeg_error_mgr errorManager;
michael@0 807 set_error_mgr(cinfo, &errorManager);
michael@0 808
michael@0 809 if (setjmp(errorManager.fJmpBuf)) {
michael@0 810 return false;
michael@0 811 }
michael@0 812
michael@0 813 int requestedSampleSize = this->getSampleSize();
michael@0 814 cinfo->scale_denom = requestedSampleSize;
michael@0 815
michael@0 816 set_dct_method(*this, cinfo);
michael@0 817
michael@0 818 const SkBitmap::Config config = this->getBitmapConfig(cinfo);
michael@0 819 #ifdef ANDROID_RGB
michael@0 820 adjust_out_color_space_and_dither(cinfo, config, *this);
michael@0 821 #endif
michael@0 822
michael@0 823 int startX = rect.fLeft;
michael@0 824 int startY = rect.fTop;
michael@0 825 int width = rect.width();
michael@0 826 int height = rect.height();
michael@0 827
michael@0 828 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
michael@0 829 &startX, &startY, &width, &height);
michael@0 830 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
michael@0 831 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
michael@0 832
michael@0 833 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
michael@0 834
michael@0 835 SkBitmap bitmap;
michael@0 836 bitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
michael@0 837 // Assume an A8 bitmap is not opaque to avoid the check of each
michael@0 838 // individual pixel. It is very unlikely to be opaque, since
michael@0 839 // an opaque A8 bitmap would not be very interesting.
michael@0 840 // Otherwise, a jpeg image is opaque.
michael@0 841 bitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight(), 0,
michael@0 842 config == SkBitmap::kA8_Config ? kPremul_SkAlphaType :
michael@0 843 kOpaque_SkAlphaType);
michael@0 844
michael@0 845 // Check ahead of time if the swap(dest, src) is possible or not.
michael@0 846 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
michael@0 847 // swap happening. If no, then we will use alloc to allocate pixels to
michael@0 848 // prevent garbage collection.
michael@0 849 int w = rect.width() / actualSampleSize;
michael@0 850 int h = rect.height() / actualSampleSize;
michael@0 851 bool swapOnly = (rect == region) && bm->isNull() &&
michael@0 852 (w == bitmap.width()) && (h == bitmap.height()) &&
michael@0 853 ((startX - rect.x()) / actualSampleSize == 0) &&
michael@0 854 ((startY - rect.y()) / actualSampleSize == 0);
michael@0 855 if (swapOnly) {
michael@0 856 if (!this->allocPixelRef(&bitmap, NULL)) {
michael@0 857 return return_false(*cinfo, bitmap, "allocPixelRef");
michael@0 858 }
michael@0 859 } else {
michael@0 860 if (!bitmap.allocPixels()) {
michael@0 861 return return_false(*cinfo, bitmap, "allocPixels");
michael@0 862 }
michael@0 863 }
michael@0 864
michael@0 865 SkAutoLockPixels alp(bitmap);
michael@0 866
michael@0 867 #ifdef ANDROID_RGB
michael@0 868 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
michael@0 869 a significant performance boost.
michael@0 870 */
michael@0 871 if (skiaSampleSize == 1 &&
michael@0 872 ((config == SkBitmap::kARGB_8888_Config &&
michael@0 873 cinfo->out_color_space == JCS_RGBA_8888) ||
michael@0 874 (config == SkBitmap::kRGB_565_Config &&
michael@0 875 cinfo->out_color_space == JCS_RGB_565)))
michael@0 876 {
michael@0 877 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
michael@0 878 INT32 const bpr = bitmap.rowBytes();
michael@0 879 int rowTotalCount = 0;
michael@0 880
michael@0 881 while (rowTotalCount < height) {
michael@0 882 int rowCount = jpeg_read_tile_scanline(cinfo,
michael@0 883 fImageIndex->huffmanIndex(),
michael@0 884 &rowptr);
michael@0 885 // if rowCount == 0, then we didn't get a scanline, so abort.
michael@0 886 // onDecodeSubset() relies on onBuildTileIndex(), which
michael@0 887 // needs a complete image to succeed.
michael@0 888 if (0 == rowCount) {
michael@0 889 return return_false(*cinfo, bitmap, "read_scanlines");
michael@0 890 }
michael@0 891 if (this->shouldCancelDecode()) {
michael@0 892 return return_false(*cinfo, bitmap, "shouldCancelDecode");
michael@0 893 }
michael@0 894 rowTotalCount += rowCount;
michael@0 895 rowptr += bpr;
michael@0 896 }
michael@0 897
michael@0 898 if (swapOnly) {
michael@0 899 bm->swap(bitmap);
michael@0 900 } else {
michael@0 901 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
michael@0 902 region.width(), region.height(), startX, startY);
michael@0 903 }
michael@0 904 return true;
michael@0 905 }
michael@0 906 #endif
michael@0 907
michael@0 908 // check for supported formats
michael@0 909 SkScaledBitmapSampler::SrcConfig sc;
michael@0 910 int srcBytesPerPixel;
michael@0 911
michael@0 912 if (!get_src_config(*cinfo, &sc, &srcBytesPerPixel)) {
michael@0 913 return return_false(*cinfo, *bm, "jpeg colorspace");
michael@0 914 }
michael@0 915
michael@0 916 if (!sampler.begin(&bitmap, sc, *this)) {
michael@0 917 return return_false(*cinfo, bitmap, "sampler.begin");
michael@0 918 }
michael@0 919
michael@0 920 SkAutoMalloc srcStorage(width * srcBytesPerPixel);
michael@0 921 uint8_t* srcRow = (uint8_t*)srcStorage.get();
michael@0 922
michael@0 923 // Possibly skip initial rows [sampler.srcY0]
michael@0 924 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
michael@0 925 return return_false(*cinfo, bitmap, "skip rows");
michael@0 926 }
michael@0 927
michael@0 928 // now loop through scanlines until y == bitmap->height() - 1
michael@0 929 for (int y = 0;; y++) {
michael@0 930 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
michael@0 931 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
michael@0 932 // if row_count == 0, then we didn't get a scanline, so abort.
michael@0 933 // onDecodeSubset() relies on onBuildTileIndex(), which
michael@0 934 // needs a complete image to succeed.
michael@0 935 if (0 == row_count) {
michael@0 936 return return_false(*cinfo, bitmap, "read_scanlines");
michael@0 937 }
michael@0 938 if (this->shouldCancelDecode()) {
michael@0 939 return return_false(*cinfo, bitmap, "shouldCancelDecode");
michael@0 940 }
michael@0 941
michael@0 942 if (JCS_CMYK == cinfo->out_color_space) {
michael@0 943 convert_CMYK_to_RGB(srcRow, width);
michael@0 944 }
michael@0 945
michael@0 946 sampler.next(srcRow);
michael@0 947 if (bitmap.height() - 1 == y) {
michael@0 948 // we're done
michael@0 949 break;
michael@0 950 }
michael@0 951
michael@0 952 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
michael@0 953 sampler.srcDY() - 1)) {
michael@0 954 return return_false(*cinfo, bitmap, "skip rows");
michael@0 955 }
michael@0 956 }
michael@0 957 if (swapOnly) {
michael@0 958 bm->swap(bitmap);
michael@0 959 } else {
michael@0 960 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
michael@0 961 region.width(), region.height(), startX, startY);
michael@0 962 }
michael@0 963 return true;
michael@0 964 }
michael@0 965 #endif
michael@0 966
michael@0 967 ///////////////////////////////////////////////////////////////////////////////
michael@0 968
michael@0 969 #include "SkColorPriv.h"
michael@0 970
michael@0 971 // taken from jcolor.c in libjpeg
michael@0 972 #if 0 // 16bit - precise but slow
michael@0 973 #define CYR 19595 // 0.299
michael@0 974 #define CYG 38470 // 0.587
michael@0 975 #define CYB 7471 // 0.114
michael@0 976
michael@0 977 #define CUR -11059 // -0.16874
michael@0 978 #define CUG -21709 // -0.33126
michael@0 979 #define CUB 32768 // 0.5
michael@0 980
michael@0 981 #define CVR 32768 // 0.5
michael@0 982 #define CVG -27439 // -0.41869
michael@0 983 #define CVB -5329 // -0.08131
michael@0 984
michael@0 985 #define CSHIFT 16
michael@0 986 #else // 8bit - fast, slightly less precise
michael@0 987 #define CYR 77 // 0.299
michael@0 988 #define CYG 150 // 0.587
michael@0 989 #define CYB 29 // 0.114
michael@0 990
michael@0 991 #define CUR -43 // -0.16874
michael@0 992 #define CUG -85 // -0.33126
michael@0 993 #define CUB 128 // 0.5
michael@0 994
michael@0 995 #define CVR 128 // 0.5
michael@0 996 #define CVG -107 // -0.41869
michael@0 997 #define CVB -21 // -0.08131
michael@0 998
michael@0 999 #define CSHIFT 8
michael@0 1000 #endif
michael@0 1001
michael@0 1002 static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
michael@0 1003 int r = SkGetPackedR32(c);
michael@0 1004 int g = SkGetPackedG32(c);
michael@0 1005 int b = SkGetPackedB32(c);
michael@0 1006
michael@0 1007 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
michael@0 1008 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
michael@0 1009 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
michael@0 1010
michael@0 1011 dst[0] = SkToU8(y);
michael@0 1012 dst[1] = SkToU8(u + 128);
michael@0 1013 dst[2] = SkToU8(v + 128);
michael@0 1014 }
michael@0 1015
michael@0 1016 static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
michael@0 1017 int r = SkGetPackedR4444(c);
michael@0 1018 int g = SkGetPackedG4444(c);
michael@0 1019 int b = SkGetPackedB4444(c);
michael@0 1020
michael@0 1021 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
michael@0 1022 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
michael@0 1023 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
michael@0 1024
michael@0 1025 dst[0] = SkToU8(y);
michael@0 1026 dst[1] = SkToU8(u + 128);
michael@0 1027 dst[2] = SkToU8(v + 128);
michael@0 1028 }
michael@0 1029
michael@0 1030 static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
michael@0 1031 int r = SkGetPackedR16(c);
michael@0 1032 int g = SkGetPackedG16(c);
michael@0 1033 int b = SkGetPackedB16(c);
michael@0 1034
michael@0 1035 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
michael@0 1036 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
michael@0 1037 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
michael@0 1038
michael@0 1039 dst[0] = SkToU8(y);
michael@0 1040 dst[1] = SkToU8(u + 128);
michael@0 1041 dst[2] = SkToU8(v + 128);
michael@0 1042 }
michael@0 1043
michael@0 1044 ///////////////////////////////////////////////////////////////////////////////
michael@0 1045
michael@0 1046 typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
michael@0 1047 const void* SK_RESTRICT src, int width,
michael@0 1048 const SkPMColor* SK_RESTRICT ctable);
michael@0 1049
michael@0 1050 static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
michael@0 1051 const void* SK_RESTRICT srcRow, int width,
michael@0 1052 const SkPMColor*) {
michael@0 1053 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
michael@0 1054 while (--width >= 0) {
michael@0 1055 #ifdef WE_CONVERT_TO_YUV
michael@0 1056 rgb2yuv_32(dst, *src++);
michael@0 1057 #else
michael@0 1058 uint32_t c = *src++;
michael@0 1059 dst[0] = SkGetPackedR32(c);
michael@0 1060 dst[1] = SkGetPackedG32(c);
michael@0 1061 dst[2] = SkGetPackedB32(c);
michael@0 1062 #endif
michael@0 1063 dst += 3;
michael@0 1064 }
michael@0 1065 }
michael@0 1066
michael@0 1067 static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
michael@0 1068 const void* SK_RESTRICT srcRow, int width,
michael@0 1069 const SkPMColor*) {
michael@0 1070 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
michael@0 1071 while (--width >= 0) {
michael@0 1072 #ifdef WE_CONVERT_TO_YUV
michael@0 1073 rgb2yuv_4444(dst, *src++);
michael@0 1074 #else
michael@0 1075 SkPMColor16 c = *src++;
michael@0 1076 dst[0] = SkPacked4444ToR32(c);
michael@0 1077 dst[1] = SkPacked4444ToG32(c);
michael@0 1078 dst[2] = SkPacked4444ToB32(c);
michael@0 1079 #endif
michael@0 1080 dst += 3;
michael@0 1081 }
michael@0 1082 }
michael@0 1083
michael@0 1084 static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
michael@0 1085 const void* SK_RESTRICT srcRow, int width,
michael@0 1086 const SkPMColor*) {
michael@0 1087 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
michael@0 1088 while (--width >= 0) {
michael@0 1089 #ifdef WE_CONVERT_TO_YUV
michael@0 1090 rgb2yuv_16(dst, *src++);
michael@0 1091 #else
michael@0 1092 uint16_t c = *src++;
michael@0 1093 dst[0] = SkPacked16ToR32(c);
michael@0 1094 dst[1] = SkPacked16ToG32(c);
michael@0 1095 dst[2] = SkPacked16ToB32(c);
michael@0 1096 #endif
michael@0 1097 dst += 3;
michael@0 1098 }
michael@0 1099 }
michael@0 1100
michael@0 1101 static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
michael@0 1102 const void* SK_RESTRICT srcRow, int width,
michael@0 1103 const SkPMColor* SK_RESTRICT ctable) {
michael@0 1104 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
michael@0 1105 while (--width >= 0) {
michael@0 1106 #ifdef WE_CONVERT_TO_YUV
michael@0 1107 rgb2yuv_32(dst, ctable[*src++]);
michael@0 1108 #else
michael@0 1109 uint32_t c = ctable[*src++];
michael@0 1110 dst[0] = SkGetPackedR32(c);
michael@0 1111 dst[1] = SkGetPackedG32(c);
michael@0 1112 dst[2] = SkGetPackedB32(c);
michael@0 1113 #endif
michael@0 1114 dst += 3;
michael@0 1115 }
michael@0 1116 }
michael@0 1117
michael@0 1118 static WriteScanline ChooseWriter(const SkBitmap& bm) {
michael@0 1119 switch (bm.config()) {
michael@0 1120 case SkBitmap::kARGB_8888_Config:
michael@0 1121 return Write_32_YUV;
michael@0 1122 case SkBitmap::kRGB_565_Config:
michael@0 1123 return Write_16_YUV;
michael@0 1124 case SkBitmap::kARGB_4444_Config:
michael@0 1125 return Write_4444_YUV;
michael@0 1126 case SkBitmap::kIndex8_Config:
michael@0 1127 return Write_Index_YUV;
michael@0 1128 default:
michael@0 1129 return NULL;
michael@0 1130 }
michael@0 1131 }
michael@0 1132
michael@0 1133 class SkJPEGImageEncoder : public SkImageEncoder {
michael@0 1134 protected:
michael@0 1135 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
michael@0 1136 #ifdef TIME_ENCODE
michael@0 1137 SkAutoTime atm("JPEG Encode");
michael@0 1138 #endif
michael@0 1139
michael@0 1140 SkAutoLockPixels alp(bm);
michael@0 1141 if (NULL == bm.getPixels()) {
michael@0 1142 return false;
michael@0 1143 }
michael@0 1144
michael@0 1145 jpeg_compress_struct cinfo;
michael@0 1146 skjpeg_error_mgr sk_err;
michael@0 1147 skjpeg_destination_mgr sk_wstream(stream);
michael@0 1148
michael@0 1149 // allocate these before set call setjmp
michael@0 1150 SkAutoMalloc oneRow;
michael@0 1151 SkAutoLockColors ctLocker;
michael@0 1152
michael@0 1153 cinfo.err = jpeg_std_error(&sk_err);
michael@0 1154 sk_err.error_exit = skjpeg_error_exit;
michael@0 1155 if (setjmp(sk_err.fJmpBuf)) {
michael@0 1156 return false;
michael@0 1157 }
michael@0 1158
michael@0 1159 // Keep after setjmp or mark volatile.
michael@0 1160 const WriteScanline writer = ChooseWriter(bm);
michael@0 1161 if (NULL == writer) {
michael@0 1162 return false;
michael@0 1163 }
michael@0 1164
michael@0 1165 jpeg_create_compress(&cinfo);
michael@0 1166 cinfo.dest = &sk_wstream;
michael@0 1167 cinfo.image_width = bm.width();
michael@0 1168 cinfo.image_height = bm.height();
michael@0 1169 cinfo.input_components = 3;
michael@0 1170 #ifdef WE_CONVERT_TO_YUV
michael@0 1171 cinfo.in_color_space = JCS_YCbCr;
michael@0 1172 #else
michael@0 1173 cinfo.in_color_space = JCS_RGB;
michael@0 1174 #endif
michael@0 1175 cinfo.input_gamma = 1;
michael@0 1176
michael@0 1177 jpeg_set_defaults(&cinfo);
michael@0 1178 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
michael@0 1179 #ifdef DCT_IFAST_SUPPORTED
michael@0 1180 cinfo.dct_method = JDCT_IFAST;
michael@0 1181 #endif
michael@0 1182
michael@0 1183 jpeg_start_compress(&cinfo, TRUE);
michael@0 1184
michael@0 1185 const int width = bm.width();
michael@0 1186 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
michael@0 1187
michael@0 1188 const SkPMColor* colors = ctLocker.lockColors(bm);
michael@0 1189 const void* srcRow = bm.getPixels();
michael@0 1190
michael@0 1191 while (cinfo.next_scanline < cinfo.image_height) {
michael@0 1192 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
michael@0 1193
michael@0 1194 writer(oneRowP, srcRow, width, colors);
michael@0 1195 row_pointer[0] = oneRowP;
michael@0 1196 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
michael@0 1197 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
michael@0 1198 }
michael@0 1199
michael@0 1200 jpeg_finish_compress(&cinfo);
michael@0 1201 jpeg_destroy_compress(&cinfo);
michael@0 1202
michael@0 1203 return true;
michael@0 1204 }
michael@0 1205 };
michael@0 1206
michael@0 1207 ///////////////////////////////////////////////////////////////////////////////
michael@0 1208 DEFINE_DECODER_CREATOR(JPEGImageDecoder);
michael@0 1209 DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
michael@0 1210 ///////////////////////////////////////////////////////////////////////////////
michael@0 1211
michael@0 1212 static bool is_jpeg(SkStreamRewindable* stream) {
michael@0 1213 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
michael@0 1214 static const size_t HEADER_SIZE = sizeof(gHeader);
michael@0 1215
michael@0 1216 char buffer[HEADER_SIZE];
michael@0 1217 size_t len = stream->read(buffer, HEADER_SIZE);
michael@0 1218
michael@0 1219 if (len != HEADER_SIZE) {
michael@0 1220 return false; // can't read enough
michael@0 1221 }
michael@0 1222 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
michael@0 1223 return false;
michael@0 1224 }
michael@0 1225 return true;
michael@0 1226 }
michael@0 1227
michael@0 1228
michael@0 1229 static SkImageDecoder* sk_libjpeg_dfactory(SkStreamRewindable* stream) {
michael@0 1230 if (is_jpeg(stream)) {
michael@0 1231 return SkNEW(SkJPEGImageDecoder);
michael@0 1232 }
michael@0 1233 return NULL;
michael@0 1234 }
michael@0 1235
michael@0 1236 static SkImageDecoder::Format get_format_jpeg(SkStreamRewindable* stream) {
michael@0 1237 if (is_jpeg(stream)) {
michael@0 1238 return SkImageDecoder::kJPEG_Format;
michael@0 1239 }
michael@0 1240 return SkImageDecoder::kUnknown_Format;
michael@0 1241 }
michael@0 1242
michael@0 1243 static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
michael@0 1244 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
michael@0 1245 }
michael@0 1246
michael@0 1247 static SkImageDecoder_DecodeReg gDReg(sk_libjpeg_dfactory);
michael@0 1248 static SkImageDecoder_FormatReg gFormatReg(get_format_jpeg);
michael@0 1249 static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory);

mercurial