1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/image/decoders/nsJPEGDecoder.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,943 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "ImageLogging.h" 1.11 +#include "nsJPEGDecoder.h" 1.12 +#include "Orientation.h" 1.13 +#include "EXIF.h" 1.14 + 1.15 +#include "nsIInputStream.h" 1.16 + 1.17 +#include "nspr.h" 1.18 +#include "nsCRT.h" 1.19 +#include "gfxColor.h" 1.20 + 1.21 +#include "jerror.h" 1.22 + 1.23 +#include "gfxPlatform.h" 1.24 + 1.25 +extern "C" { 1.26 +#include "iccjpeg.h" 1.27 +} 1.28 + 1.29 +#if defined(IS_BIG_ENDIAN) 1.30 +#define MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB JCS_EXT_XRGB 1.31 +#else 1.32 +#define MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB JCS_EXT_BGRX 1.33 +#endif 1.34 + 1.35 +static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width); 1.36 + 1.37 +namespace mozilla { 1.38 +namespace image { 1.39 + 1.40 +#if defined(PR_LOGGING) 1.41 +static PRLogModuleInfo * 1.42 +GetJPEGLog() 1.43 +{ 1.44 + static PRLogModuleInfo *sJPEGLog; 1.45 + if (!sJPEGLog) 1.46 + sJPEGLog = PR_NewLogModule("JPEGDecoder"); 1.47 + return sJPEGLog; 1.48 +} 1.49 + 1.50 +static PRLogModuleInfo * 1.51 +GetJPEGDecoderAccountingLog() 1.52 +{ 1.53 + static PRLogModuleInfo *sJPEGDecoderAccountingLog; 1.54 + if (!sJPEGDecoderAccountingLog) 1.55 + sJPEGDecoderAccountingLog = PR_NewLogModule("JPEGDecoderAccounting"); 1.56 + return sJPEGDecoderAccountingLog; 1.57 +} 1.58 +#else 1.59 +#define GetJPEGLog() 1.60 +#define GetJPEGDecoderAccountingLog() 1.61 +#endif 1.62 + 1.63 +static qcms_profile* 1.64 +GetICCProfile(struct jpeg_decompress_struct &info) 1.65 +{ 1.66 + JOCTET* profilebuf; 1.67 + uint32_t profileLength; 1.68 + qcms_profile* profile = nullptr; 1.69 + 1.70 + if (read_icc_profile(&info, &profilebuf, &profileLength)) { 1.71 + profile = qcms_profile_from_memory(profilebuf, profileLength); 1.72 + free(profilebuf); 1.73 + } 1.74 + 1.75 + return profile; 1.76 +} 1.77 + 1.78 +METHODDEF(void) init_source (j_decompress_ptr jd); 1.79 +METHODDEF(boolean) fill_input_buffer (j_decompress_ptr jd); 1.80 +METHODDEF(void) skip_input_data (j_decompress_ptr jd, long num_bytes); 1.81 +METHODDEF(void) term_source (j_decompress_ptr jd); 1.82 +METHODDEF(void) my_error_exit (j_common_ptr cinfo); 1.83 + 1.84 +/* Normal JFIF markers can't have more bytes than this. */ 1.85 +#define MAX_JPEG_MARKER_LENGTH (((uint32_t)1 << 16) - 1) 1.86 + 1.87 + 1.88 +nsJPEGDecoder::nsJPEGDecoder(RasterImage& aImage, Decoder::DecodeStyle aDecodeStyle) 1.89 + : Decoder(aImage) 1.90 + , mDecodeStyle(aDecodeStyle) 1.91 +{ 1.92 + mState = JPEG_HEADER; 1.93 + mReading = true; 1.94 + mImageData = nullptr; 1.95 + 1.96 + mBytesToSkip = 0; 1.97 + memset(&mInfo, 0, sizeof(jpeg_decompress_struct)); 1.98 + memset(&mSourceMgr, 0, sizeof(mSourceMgr)); 1.99 + mInfo.client_data = (void*)this; 1.100 + 1.101 + mSegment = nullptr; 1.102 + mSegmentLen = 0; 1.103 + 1.104 + mBackBuffer = nullptr; 1.105 + mBackBufferLen = mBackBufferSize = mBackBufferUnreadLen = 0; 1.106 + 1.107 + mInProfile = nullptr; 1.108 + mTransform = nullptr; 1.109 + 1.110 + mCMSMode = 0; 1.111 + 1.112 + PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, 1.113 + ("nsJPEGDecoder::nsJPEGDecoder: Creating JPEG decoder %p", 1.114 + this)); 1.115 +} 1.116 + 1.117 +nsJPEGDecoder::~nsJPEGDecoder() 1.118 +{ 1.119 + // Step 8: Release JPEG decompression object 1.120 + mInfo.src = nullptr; 1.121 + jpeg_destroy_decompress(&mInfo); 1.122 + 1.123 + PR_FREEIF(mBackBuffer); 1.124 + if (mTransform) 1.125 + qcms_transform_release(mTransform); 1.126 + if (mInProfile) 1.127 + qcms_profile_release(mInProfile); 1.128 + 1.129 + PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, 1.130 + ("nsJPEGDecoder::~nsJPEGDecoder: Destroying JPEG decoder %p", 1.131 + this)); 1.132 +} 1.133 + 1.134 +Telemetry::ID 1.135 +nsJPEGDecoder::SpeedHistogram() 1.136 +{ 1.137 + return Telemetry::IMAGE_DECODE_SPEED_JPEG; 1.138 +} 1.139 + 1.140 +void 1.141 +nsJPEGDecoder::InitInternal() 1.142 +{ 1.143 + mCMSMode = gfxPlatform::GetCMSMode(); 1.144 + if ((mDecodeFlags & DECODER_NO_COLORSPACE_CONVERSION) != 0) 1.145 + mCMSMode = eCMSMode_Off; 1.146 + 1.147 + /* We set up the normal JPEG error routines, then override error_exit. */ 1.148 + mInfo.err = jpeg_std_error(&mErr.pub); 1.149 + /* mInfo.err = jpeg_std_error(&mErr.pub); */ 1.150 + mErr.pub.error_exit = my_error_exit; 1.151 + /* Establish the setjmp return context for my_error_exit to use. */ 1.152 + if (setjmp(mErr.setjmp_buffer)) { 1.153 + /* If we get here, the JPEG code has signaled an error. 1.154 + * We need to clean up the JPEG object, close the input file, and return. 1.155 + */ 1.156 + PostDecoderError(NS_ERROR_FAILURE); 1.157 + return; 1.158 + } 1.159 + 1.160 + /* Step 1: allocate and initialize JPEG decompression object */ 1.161 + jpeg_create_decompress(&mInfo); 1.162 + /* Set the source manager */ 1.163 + mInfo.src = &mSourceMgr; 1.164 + 1.165 + /* Step 2: specify data source (eg, a file) */ 1.166 + 1.167 + /* Setup callback functions. */ 1.168 + mSourceMgr.init_source = init_source; 1.169 + mSourceMgr.fill_input_buffer = fill_input_buffer; 1.170 + mSourceMgr.skip_input_data = skip_input_data; 1.171 + mSourceMgr.resync_to_restart = jpeg_resync_to_restart; 1.172 + mSourceMgr.term_source = term_source; 1.173 + 1.174 + /* Record app markers for ICC data */ 1.175 + for (uint32_t m = 0; m < 16; m++) 1.176 + jpeg_save_markers(&mInfo, JPEG_APP0 + m, 0xFFFF); 1.177 +} 1.178 + 1.179 +void 1.180 +nsJPEGDecoder::FinishInternal() 1.181 +{ 1.182 + /* If we're not in any sort of error case, flush the decoder. 1.183 + * 1.184 + * XXXbholley - It seems wrong that this should be necessary, but at the 1.185 + * moment I'm just folding the contents of Flush() into Close() so that 1.186 + * we can get rid of it. 1.187 + * 1.188 + * XXX(seth): It'd be great to get rid of this. For now, we treat this as a 1.189 + * write to a synchronous decoder, which means that this must be called only 1.190 + * on the main thread. (That's asserted in Decoder::Finish and 1.191 + * Decoder::FinishSharedDecoder.) 1.192 + */ 1.193 + if ((mState != JPEG_DONE && mState != JPEG_SINK_NON_JPEG_TRAILER) && 1.194 + (mState != JPEG_ERROR) && 1.195 + !IsSizeDecode()) 1.196 + this->Write(nullptr, 0, DECODE_SYNC); 1.197 +} 1.198 + 1.199 +void 1.200 +nsJPEGDecoder::WriteInternal(const char *aBuffer, uint32_t aCount, DecodeStrategy) 1.201 +{ 1.202 + mSegment = (const JOCTET *)aBuffer; 1.203 + mSegmentLen = aCount; 1.204 + 1.205 + NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!"); 1.206 + 1.207 + /* Return here if there is a fatal error within libjpeg. */ 1.208 + nsresult error_code; 1.209 + // This cast to nsresult makes sense because setjmp() returns whatever we 1.210 + // passed to longjmp(), which was actually an nsresult. 1.211 + if ((error_code = (nsresult)setjmp(mErr.setjmp_buffer)) != NS_OK) { 1.212 + if (error_code == NS_ERROR_FAILURE) { 1.213 + PostDataError(); 1.214 + /* Error due to corrupt stream - return NS_OK and consume silently 1.215 + so that libpr0n doesn't throw away a partial image load */ 1.216 + mState = JPEG_SINK_NON_JPEG_TRAILER; 1.217 + PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, 1.218 + ("} (setjmp returned NS_ERROR_FAILURE)")); 1.219 + return; 1.220 + } else { 1.221 + /* Error due to reasons external to the stream (probably out of 1.222 + memory) - let libpr0n attempt to clean up, even though 1.223 + mozilla is seconds away from falling flat on its face. */ 1.224 + PostDecoderError(error_code); 1.225 + mState = JPEG_ERROR; 1.226 + PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, 1.227 + ("} (setjmp returned an error)")); 1.228 + return; 1.229 + } 1.230 + } 1.231 + 1.232 + PR_LOG(GetJPEGLog(), PR_LOG_DEBUG, 1.233 + ("[this=%p] nsJPEGDecoder::Write -- processing JPEG data\n", this)); 1.234 + 1.235 + switch (mState) { 1.236 + case JPEG_HEADER: 1.237 + { 1.238 + LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- entering JPEG_HEADER case"); 1.239 + 1.240 + /* Step 3: read file parameters with jpeg_read_header() */ 1.241 + if (jpeg_read_header(&mInfo, TRUE) == JPEG_SUSPENDED) { 1.242 + PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, 1.243 + ("} (JPEG_SUSPENDED)")); 1.244 + return; /* I/O suspension */ 1.245 + } 1.246 + 1.247 + int sampleSize = mImage.GetRequestedSampleSize(); 1.248 + if (sampleSize > 0) { 1.249 + mInfo.scale_num = 1; 1.250 + mInfo.scale_denom = sampleSize; 1.251 + } 1.252 + 1.253 + /* Used to set up image size so arrays can be allocated */ 1.254 + jpeg_calc_output_dimensions(&mInfo); 1.255 + 1.256 + // Post our size to the superclass 1.257 + PostSize(mInfo.output_width, mInfo.output_height, ReadOrientationFromEXIF()); 1.258 + if (HasError()) { 1.259 + // Setting the size led to an error. 1.260 + mState = JPEG_ERROR; 1.261 + return; 1.262 + } 1.263 + 1.264 + /* If we're doing a size decode, we're done. */ 1.265 + if (IsSizeDecode()) 1.266 + return; 1.267 + 1.268 + /* We're doing a full decode. */ 1.269 + if (mCMSMode != eCMSMode_Off && 1.270 + (mInProfile = GetICCProfile(mInfo)) != nullptr) { 1.271 + uint32_t profileSpace = qcms_profile_get_color_space(mInProfile); 1.272 + bool mismatch = false; 1.273 + 1.274 +#ifdef DEBUG_tor 1.275 + fprintf(stderr, "JPEG profileSpace: 0x%08X\n", profileSpace); 1.276 +#endif 1.277 + switch (mInfo.jpeg_color_space) { 1.278 + case JCS_GRAYSCALE: 1.279 + if (profileSpace == icSigRgbData) 1.280 + mInfo.out_color_space = JCS_RGB; 1.281 + else if (profileSpace != icSigGrayData) 1.282 + mismatch = true; 1.283 + break; 1.284 + case JCS_RGB: 1.285 + if (profileSpace != icSigRgbData) 1.286 + mismatch = true; 1.287 + break; 1.288 + case JCS_YCbCr: 1.289 + if (profileSpace == icSigRgbData) 1.290 + mInfo.out_color_space = JCS_RGB; 1.291 + else 1.292 + // qcms doesn't support ycbcr 1.293 + mismatch = true; 1.294 + break; 1.295 + case JCS_CMYK: 1.296 + case JCS_YCCK: 1.297 + // qcms doesn't support cmyk 1.298 + mismatch = true; 1.299 + break; 1.300 + default: 1.301 + mState = JPEG_ERROR; 1.302 + PostDataError(); 1.303 + PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, 1.304 + ("} (unknown colorpsace (1))")); 1.305 + return; 1.306 + } 1.307 + 1.308 + if (!mismatch) { 1.309 + qcms_data_type type; 1.310 + switch (mInfo.out_color_space) { 1.311 + case JCS_GRAYSCALE: 1.312 + type = QCMS_DATA_GRAY_8; 1.313 + break; 1.314 + case JCS_RGB: 1.315 + type = QCMS_DATA_RGB_8; 1.316 + break; 1.317 + default: 1.318 + mState = JPEG_ERROR; 1.319 + PostDataError(); 1.320 + PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, 1.321 + ("} (unknown colorpsace (2))")); 1.322 + return; 1.323 + } 1.324 +#if 0 1.325 + /* We don't currently support CMYK profiles. The following 1.326 + * code dealt with lcms types. Add something like this 1.327 + * back when we gain support for CMYK. 1.328 + */ 1.329 + /* Adobe Photoshop writes YCCK/CMYK files with inverted data */ 1.330 + if (mInfo.out_color_space == JCS_CMYK) 1.331 + type |= FLAVOR_SH(mInfo.saw_Adobe_marker ? 1 : 0); 1.332 +#endif 1.333 + 1.334 + if (gfxPlatform::GetCMSOutputProfile()) { 1.335 + 1.336 + /* Calculate rendering intent. */ 1.337 + int intent = gfxPlatform::GetRenderingIntent(); 1.338 + if (intent == -1) 1.339 + intent = qcms_profile_get_rendering_intent(mInProfile); 1.340 + 1.341 + /* Create the color management transform. */ 1.342 + mTransform = qcms_transform_create(mInProfile, 1.343 + type, 1.344 + gfxPlatform::GetCMSOutputProfile(), 1.345 + QCMS_DATA_RGB_8, 1.346 + (qcms_intent)intent); 1.347 + } 1.348 + } else { 1.349 +#ifdef DEBUG_tor 1.350 + fprintf(stderr, "ICM profile colorspace mismatch\n"); 1.351 +#endif 1.352 + } 1.353 + } 1.354 + 1.355 + if (!mTransform) { 1.356 + switch (mInfo.jpeg_color_space) { 1.357 + case JCS_GRAYSCALE: 1.358 + case JCS_RGB: 1.359 + case JCS_YCbCr: 1.360 + // if we're not color managing we can decode directly to 1.361 + // MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB 1.362 + if (mCMSMode != eCMSMode_All) { 1.363 + mInfo.out_color_space = MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB; 1.364 + mInfo.out_color_components = 4; 1.365 + } else { 1.366 + mInfo.out_color_space = JCS_RGB; 1.367 + } 1.368 + break; 1.369 + case JCS_CMYK: 1.370 + case JCS_YCCK: 1.371 + /* libjpeg can convert from YCCK to CMYK, but not to RGB */ 1.372 + mInfo.out_color_space = JCS_CMYK; 1.373 + break; 1.374 + default: 1.375 + mState = JPEG_ERROR; 1.376 + PostDataError(); 1.377 + PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, 1.378 + ("} (unknown colorpsace (3))")); 1.379 + return; 1.380 + break; 1.381 + } 1.382 + } 1.383 + 1.384 + /* 1.385 + * Don't allocate a giant and superfluous memory buffer 1.386 + * when not doing a progressive decode. 1.387 + */ 1.388 + mInfo.buffered_image = mDecodeStyle == PROGRESSIVE && jpeg_has_multiple_scans(&mInfo); 1.389 + 1.390 + if (!mImageData) { 1.391 + mState = JPEG_ERROR; 1.392 + PostDecoderError(NS_ERROR_OUT_OF_MEMORY); 1.393 + PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, 1.394 + ("} (could not initialize image frame)")); 1.395 + return; 1.396 + } 1.397 + 1.398 + PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, 1.399 + (" JPEGDecoderAccounting: nsJPEGDecoder::Write -- created image frame with %ux%u pixels", 1.400 + mInfo.output_width, mInfo.output_height)); 1.401 + 1.402 + mState = JPEG_START_DECOMPRESS; 1.403 + } 1.404 + 1.405 + case JPEG_START_DECOMPRESS: 1.406 + { 1.407 + LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- entering JPEG_START_DECOMPRESS case"); 1.408 + /* Step 4: set parameters for decompression */ 1.409 + 1.410 + /* FIXME -- Should reset dct_method and dither mode 1.411 + * for final pass of progressive JPEG 1.412 + */ 1.413 + mInfo.dct_method = JDCT_ISLOW; 1.414 + mInfo.dither_mode = JDITHER_FS; 1.415 + mInfo.do_fancy_upsampling = TRUE; 1.416 + mInfo.enable_2pass_quant = FALSE; 1.417 + mInfo.do_block_smoothing = TRUE; 1.418 + 1.419 + /* Step 5: Start decompressor */ 1.420 + if (jpeg_start_decompress(&mInfo) == FALSE) { 1.421 + PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, 1.422 + ("} (I/O suspension after jpeg_start_decompress())")); 1.423 + return; /* I/O suspension */ 1.424 + } 1.425 + 1.426 + 1.427 + /* If this is a progressive JPEG ... */ 1.428 + mState = mInfo.buffered_image ? JPEG_DECOMPRESS_PROGRESSIVE : JPEG_DECOMPRESS_SEQUENTIAL; 1.429 + } 1.430 + 1.431 + case JPEG_DECOMPRESS_SEQUENTIAL: 1.432 + { 1.433 + if (mState == JPEG_DECOMPRESS_SEQUENTIAL) 1.434 + { 1.435 + LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- JPEG_DECOMPRESS_SEQUENTIAL case"); 1.436 + 1.437 + bool suspend; 1.438 + OutputScanlines(&suspend); 1.439 + 1.440 + if (suspend) { 1.441 + PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, 1.442 + ("} (I/O suspension after OutputScanlines() - SEQUENTIAL)")); 1.443 + return; /* I/O suspension */ 1.444 + } 1.445 + 1.446 + /* If we've completed image output ... */ 1.447 + NS_ASSERTION(mInfo.output_scanline == mInfo.output_height, "We didn't process all of the data!"); 1.448 + mState = JPEG_DONE; 1.449 + } 1.450 + } 1.451 + 1.452 + case JPEG_DECOMPRESS_PROGRESSIVE: 1.453 + { 1.454 + if (mState == JPEG_DECOMPRESS_PROGRESSIVE) 1.455 + { 1.456 + LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- JPEG_DECOMPRESS_PROGRESSIVE case"); 1.457 + 1.458 + int status; 1.459 + do { 1.460 + status = jpeg_consume_input(&mInfo); 1.461 + } while ((status != JPEG_SUSPENDED) && 1.462 + (status != JPEG_REACHED_EOI)); 1.463 + 1.464 + for (;;) { 1.465 + if (mInfo.output_scanline == 0) { 1.466 + int scan = mInfo.input_scan_number; 1.467 + 1.468 + /* if we haven't displayed anything yet (output_scan_number==0) 1.469 + and we have enough data for a complete scan, force output 1.470 + of the last full scan */ 1.471 + if ((mInfo.output_scan_number == 0) && 1.472 + (scan > 1) && 1.473 + (status != JPEG_REACHED_EOI)) 1.474 + scan--; 1.475 + 1.476 + if (!jpeg_start_output(&mInfo, scan)) { 1.477 + PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, 1.478 + ("} (I/O suspension after jpeg_start_output() - PROGRESSIVE)")); 1.479 + return; /* I/O suspension */ 1.480 + } 1.481 + } 1.482 + 1.483 + if (mInfo.output_scanline == 0xffffff) 1.484 + mInfo.output_scanline = 0; 1.485 + 1.486 + bool suspend; 1.487 + OutputScanlines(&suspend); 1.488 + 1.489 + if (suspend) { 1.490 + if (mInfo.output_scanline == 0) { 1.491 + /* didn't manage to read any lines - flag so we don't call 1.492 + jpeg_start_output() multiple times for the same scan */ 1.493 + mInfo.output_scanline = 0xffffff; 1.494 + } 1.495 + PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, 1.496 + ("} (I/O suspension after OutputScanlines() - PROGRESSIVE)")); 1.497 + return; /* I/O suspension */ 1.498 + } 1.499 + 1.500 + if (mInfo.output_scanline == mInfo.output_height) 1.501 + { 1.502 + if (!jpeg_finish_output(&mInfo)) { 1.503 + PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, 1.504 + ("} (I/O suspension after jpeg_finish_output() - PROGRESSIVE)")); 1.505 + return; /* I/O suspension */ 1.506 + } 1.507 + 1.508 + if (jpeg_input_complete(&mInfo) && 1.509 + (mInfo.input_scan_number == mInfo.output_scan_number)) 1.510 + break; 1.511 + 1.512 + mInfo.output_scanline = 0; 1.513 + } 1.514 + } 1.515 + 1.516 + mState = JPEG_DONE; 1.517 + } 1.518 + } 1.519 + 1.520 + case JPEG_DONE: 1.521 + { 1.522 + LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::ProcessData -- entering JPEG_DONE case"); 1.523 + 1.524 + /* Step 7: Finish decompression */ 1.525 + 1.526 + if (jpeg_finish_decompress(&mInfo) == FALSE) { 1.527 + PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, 1.528 + ("} (I/O suspension after jpeg_finish_decompress() - DONE)")); 1.529 + return; /* I/O suspension */ 1.530 + } 1.531 + 1.532 + mState = JPEG_SINK_NON_JPEG_TRAILER; 1.533 + 1.534 + /* we're done dude */ 1.535 + break; 1.536 + } 1.537 + case JPEG_SINK_NON_JPEG_TRAILER: 1.538 + PR_LOG(GetJPEGLog(), PR_LOG_DEBUG, 1.539 + ("[this=%p] nsJPEGDecoder::ProcessData -- entering JPEG_SINK_NON_JPEG_TRAILER case\n", this)); 1.540 + 1.541 + break; 1.542 + 1.543 + case JPEG_ERROR: 1.544 + NS_ABORT_IF_FALSE(0, "Should always return immediately after error and not re-enter decoder"); 1.545 + } 1.546 + 1.547 + PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG, 1.548 + ("} (end of function)")); 1.549 + return; 1.550 +} 1.551 + 1.552 +Orientation 1.553 +nsJPEGDecoder::ReadOrientationFromEXIF() 1.554 +{ 1.555 + jpeg_saved_marker_ptr marker; 1.556 + 1.557 + // Locate the APP1 marker, where EXIF data is stored, in the marker list. 1.558 + for (marker = mInfo.marker_list ; marker != nullptr ; marker = marker->next) { 1.559 + if (marker->marker == JPEG_APP0 + 1) 1.560 + break; 1.561 + } 1.562 + 1.563 + // If we're at the end of the list, there's no EXIF data. 1.564 + if (!marker) 1.565 + return Orientation(); 1.566 + 1.567 + // Extract the orientation information. 1.568 + EXIFData exif = EXIFParser::Parse(marker->data, 1.569 + static_cast<uint32_t>(marker->data_length)); 1.570 + return exif.orientation; 1.571 +} 1.572 + 1.573 +void 1.574 +nsJPEGDecoder::NotifyDone() 1.575 +{ 1.576 + PostFrameStop(FrameBlender::kFrameOpaque); 1.577 + PostDecodeDone(); 1.578 +} 1.579 + 1.580 +void 1.581 +nsJPEGDecoder::OutputScanlines(bool* suspend) 1.582 +{ 1.583 + *suspend = false; 1.584 + 1.585 + const uint32_t top = mInfo.output_scanline; 1.586 + 1.587 + while ((mInfo.output_scanline < mInfo.output_height)) { 1.588 + /* Use the Cairo image buffer as scanline buffer */ 1.589 + uint32_t *imageRow = ((uint32_t*)mImageData) + 1.590 + (mInfo.output_scanline * mInfo.output_width); 1.591 + 1.592 + if (mInfo.out_color_space == MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB) { 1.593 + /* Special case: scanline will be directly converted into packed ARGB */ 1.594 + if (jpeg_read_scanlines(&mInfo, (JSAMPARRAY)&imageRow, 1) != 1) { 1.595 + *suspend = true; /* suspend */ 1.596 + break; 1.597 + } 1.598 + continue; /* all done for this row! */ 1.599 + } 1.600 + 1.601 + JSAMPROW sampleRow = (JSAMPROW)imageRow; 1.602 + if (mInfo.output_components == 3) { 1.603 + /* Put the pixels at end of row to enable in-place expansion */ 1.604 + sampleRow += mInfo.output_width; 1.605 + } 1.606 + 1.607 + /* Request one scanline. Returns 0 or 1 scanlines. */ 1.608 + if (jpeg_read_scanlines(&mInfo, &sampleRow, 1) != 1) { 1.609 + *suspend = true; /* suspend */ 1.610 + break; 1.611 + } 1.612 + 1.613 + if (mTransform) { 1.614 + JSAMPROW source = sampleRow; 1.615 + if (mInfo.out_color_space == JCS_GRAYSCALE) { 1.616 + /* Convert from the 1byte grey pixels at begin of row 1.617 + to the 3byte RGB byte pixels at 'end' of row */ 1.618 + sampleRow += mInfo.output_width; 1.619 + } 1.620 + qcms_transform_data(mTransform, source, sampleRow, mInfo.output_width); 1.621 + /* Move 3byte RGB data to end of row */ 1.622 + if (mInfo.out_color_space == JCS_CMYK) { 1.623 + memmove(sampleRow + mInfo.output_width, 1.624 + sampleRow, 1.625 + 3 * mInfo.output_width); 1.626 + sampleRow += mInfo.output_width; 1.627 + } 1.628 + } else { 1.629 + if (mInfo.out_color_space == JCS_CMYK) { 1.630 + /* Convert from CMYK to RGB */ 1.631 + /* We cannot convert directly to Cairo, as the CMSRGBTransform may wants to do a RGB transform... */ 1.632 + /* Would be better to have platform CMSenabled transformation from CMYK to (A)RGB... */ 1.633 + cmyk_convert_rgb((JSAMPROW)imageRow, mInfo.output_width); 1.634 + sampleRow += mInfo.output_width; 1.635 + } 1.636 + if (mCMSMode == eCMSMode_All) { 1.637 + /* No embedded ICC profile - treat as sRGB */ 1.638 + qcms_transform *transform = gfxPlatform::GetCMSRGBTransform(); 1.639 + if (transform) { 1.640 + qcms_transform_data(transform, sampleRow, sampleRow, mInfo.output_width); 1.641 + } 1.642 + } 1.643 + } 1.644 + 1.645 + // counter for while() loops below 1.646 + uint32_t idx = mInfo.output_width; 1.647 + 1.648 + // copy as bytes until source pointer is 32-bit-aligned 1.649 + for (; (NS_PTR_TO_UINT32(sampleRow) & 0x3) && idx; --idx) { 1.650 + *imageRow++ = gfxPackedPixel(0xFF, sampleRow[0], sampleRow[1], sampleRow[2]); 1.651 + sampleRow += 3; 1.652 + } 1.653 + 1.654 + // copy pixels in blocks of 4 1.655 + while (idx >= 4) { 1.656 + GFX_BLOCK_RGB_TO_FRGB(sampleRow, imageRow); 1.657 + idx -= 4; 1.658 + sampleRow += 12; 1.659 + imageRow += 4; 1.660 + } 1.661 + 1.662 + // copy remaining pixel(s) 1.663 + while (idx--) { 1.664 + // 32-bit read of final pixel will exceed buffer, so read bytes 1.665 + *imageRow++ = gfxPackedPixel(0xFF, sampleRow[0], sampleRow[1], sampleRow[2]); 1.666 + sampleRow += 3; 1.667 + } 1.668 + } 1.669 + 1.670 + if (top != mInfo.output_scanline) { 1.671 + nsIntRect r(0, top, mInfo.output_width, mInfo.output_scanline-top); 1.672 + PostInvalidation(r); 1.673 + } 1.674 + 1.675 +} 1.676 + 1.677 + 1.678 +/* Override the standard error method in the IJG JPEG decoder code. */ 1.679 +METHODDEF(void) 1.680 +my_error_exit (j_common_ptr cinfo) 1.681 +{ 1.682 + decoder_error_mgr *err = (decoder_error_mgr *) cinfo->err; 1.683 + 1.684 + /* Convert error to a browser error code */ 1.685 + nsresult error_code = err->pub.msg_code == JERR_OUT_OF_MEMORY 1.686 + ? NS_ERROR_OUT_OF_MEMORY 1.687 + : NS_ERROR_FAILURE; 1.688 + 1.689 +#ifdef DEBUG 1.690 + char buffer[JMSG_LENGTH_MAX]; 1.691 + 1.692 + /* Create the message */ 1.693 + (*err->pub.format_message) (cinfo, buffer); 1.694 + 1.695 + fprintf(stderr, "JPEG decoding error:\n%s\n", buffer); 1.696 +#endif 1.697 + 1.698 + /* Return control to the setjmp point. We pass an nsresult masquerading as 1.699 + * an int, which works because the setjmp() caller casts it back. */ 1.700 + longjmp(err->setjmp_buffer, static_cast<int>(error_code)); 1.701 +} 1.702 + 1.703 +/******************************************************************************/ 1.704 +/*----------------------------------------------------------------------------- 1.705 + * This is the callback routine from the IJG JPEG library used to supply new 1.706 + * data to the decompressor when its input buffer is exhausted. It juggles 1.707 + * multiple buffers in an attempt to avoid unnecessary copying of input data. 1.708 + * 1.709 + * (A simpler scheme is possible: It's much easier to use only a single 1.710 + * buffer; when fill_input_buffer() is called, move any unconsumed data 1.711 + * (beyond the current pointer/count) down to the beginning of this buffer and 1.712 + * then load new data into the remaining buffer space. This approach requires 1.713 + * a little more data copying but is far easier to get right.) 1.714 + * 1.715 + * At any one time, the JPEG decompressor is either reading from the necko 1.716 + * input buffer, which is volatile across top-level calls to the IJG library, 1.717 + * or the "backtrack" buffer. The backtrack buffer contains the remaining 1.718 + * unconsumed data from the necko buffer after parsing was suspended due 1.719 + * to insufficient data in some previous call to the IJG library. 1.720 + * 1.721 + * When suspending, the decompressor will back up to a convenient restart 1.722 + * point (typically the start of the current MCU). The variables 1.723 + * next_input_byte & bytes_in_buffer indicate where the restart point will be 1.724 + * if the current call returns FALSE. Data beyond this point must be 1.725 + * rescanned after resumption, so it must be preserved in case the decompressor 1.726 + * decides to backtrack. 1.727 + * 1.728 + * Returns: 1.729 + * TRUE if additional data is available, FALSE if no data present and 1.730 + * the JPEG library should therefore suspend processing of input stream 1.731 + *---------------------------------------------------------------------------*/ 1.732 + 1.733 +/******************************************************************************/ 1.734 +/* data source manager method */ 1.735 +/******************************************************************************/ 1.736 + 1.737 + 1.738 +/******************************************************************************/ 1.739 +/* data source manager method 1.740 + Initialize source. This is called by jpeg_read_header() before any 1.741 + data is actually read. May leave 1.742 + bytes_in_buffer set to 0 (in which case a fill_input_buffer() call 1.743 + will occur immediately). 1.744 +*/ 1.745 +METHODDEF(void) 1.746 +init_source (j_decompress_ptr jd) 1.747 +{ 1.748 +} 1.749 + 1.750 +/******************************************************************************/ 1.751 +/* data source manager method 1.752 + Skip num_bytes worth of data. The buffer pointer and count should 1.753 + be advanced over num_bytes input bytes, refilling the buffer as 1.754 + needed. This is used to skip over a potentially large amount of 1.755 + uninteresting data (such as an APPn marker). In some applications 1.756 + it may be possible to optimize away the reading of the skipped data, 1.757 + but it's not clear that being smart is worth much trouble; large 1.758 + skips are uncommon. bytes_in_buffer may be zero on return. 1.759 + A zero or negative skip count should be treated as a no-op. 1.760 +*/ 1.761 +METHODDEF(void) 1.762 +skip_input_data (j_decompress_ptr jd, long num_bytes) 1.763 +{ 1.764 + struct jpeg_source_mgr *src = jd->src; 1.765 + nsJPEGDecoder *decoder = (nsJPEGDecoder *)(jd->client_data); 1.766 + 1.767 + if (num_bytes > (long)src->bytes_in_buffer) { 1.768 + /* 1.769 + * Can't skip it all right now until we get more data from 1.770 + * network stream. Set things up so that fill_input_buffer 1.771 + * will skip remaining amount. 1.772 + */ 1.773 + decoder->mBytesToSkip = (size_t)num_bytes - src->bytes_in_buffer; 1.774 + src->next_input_byte += src->bytes_in_buffer; 1.775 + src->bytes_in_buffer = 0; 1.776 + 1.777 + } else { 1.778 + /* Simple case. Just advance buffer pointer */ 1.779 + 1.780 + src->bytes_in_buffer -= (size_t)num_bytes; 1.781 + src->next_input_byte += num_bytes; 1.782 + } 1.783 +} 1.784 + 1.785 + 1.786 +/******************************************************************************/ 1.787 +/* data source manager method 1.788 + This is called whenever bytes_in_buffer has reached zero and more 1.789 + data is wanted. In typical applications, it should read fresh data 1.790 + into the buffer (ignoring the current state of next_input_byte and 1.791 + bytes_in_buffer), reset the pointer & count to the start of the 1.792 + buffer, and return TRUE indicating that the buffer has been reloaded. 1.793 + It is not necessary to fill the buffer entirely, only to obtain at 1.794 + least one more byte. bytes_in_buffer MUST be set to a positive value 1.795 + if TRUE is returned. A FALSE return should only be used when I/O 1.796 + suspension is desired. 1.797 +*/ 1.798 +METHODDEF(boolean) 1.799 +fill_input_buffer (j_decompress_ptr jd) 1.800 +{ 1.801 + struct jpeg_source_mgr *src = jd->src; 1.802 + nsJPEGDecoder *decoder = (nsJPEGDecoder *)(jd->client_data); 1.803 + 1.804 + if (decoder->mReading) { 1.805 + const JOCTET *new_buffer = decoder->mSegment; 1.806 + uint32_t new_buflen = decoder->mSegmentLen; 1.807 + 1.808 + if (!new_buffer || new_buflen == 0) 1.809 + return false; /* suspend */ 1.810 + 1.811 + decoder->mSegmentLen = 0; 1.812 + 1.813 + if (decoder->mBytesToSkip) { 1.814 + if (decoder->mBytesToSkip < new_buflen) { 1.815 + /* All done skipping bytes; Return what's left. */ 1.816 + new_buffer += decoder->mBytesToSkip; 1.817 + new_buflen -= decoder->mBytesToSkip; 1.818 + decoder->mBytesToSkip = 0; 1.819 + } else { 1.820 + /* Still need to skip some more data in the future */ 1.821 + decoder->mBytesToSkip -= (size_t)new_buflen; 1.822 + return false; /* suspend */ 1.823 + } 1.824 + } 1.825 + 1.826 + decoder->mBackBufferUnreadLen = src->bytes_in_buffer; 1.827 + 1.828 + src->next_input_byte = new_buffer; 1.829 + src->bytes_in_buffer = (size_t)new_buflen; 1.830 + decoder->mReading = false; 1.831 + 1.832 + return true; 1.833 + } 1.834 + 1.835 + if (src->next_input_byte != decoder->mSegment) { 1.836 + /* Backtrack data has been permanently consumed. */ 1.837 + decoder->mBackBufferUnreadLen = 0; 1.838 + decoder->mBackBufferLen = 0; 1.839 + } 1.840 + 1.841 + /* Save remainder of netlib buffer in backtrack buffer */ 1.842 + const uint32_t new_backtrack_buflen = src->bytes_in_buffer + decoder->mBackBufferLen; 1.843 + 1.844 + /* Make sure backtrack buffer is big enough to hold new data. */ 1.845 + if (decoder->mBackBufferSize < new_backtrack_buflen) { 1.846 + /* Check for malformed MARKER segment lengths, before allocating space for it */ 1.847 + if (new_backtrack_buflen > MAX_JPEG_MARKER_LENGTH) { 1.848 + my_error_exit((j_common_ptr)(&decoder->mInfo)); 1.849 + } 1.850 + 1.851 + /* Round up to multiple of 256 bytes. */ 1.852 + const size_t roundup_buflen = ((new_backtrack_buflen + 255) >> 8) << 8; 1.853 + JOCTET *buf = (JOCTET *)PR_REALLOC(decoder->mBackBuffer, roundup_buflen); 1.854 + /* Check for OOM */ 1.855 + if (!buf) { 1.856 + decoder->mInfo.err->msg_code = JERR_OUT_OF_MEMORY; 1.857 + my_error_exit((j_common_ptr)(&decoder->mInfo)); 1.858 + } 1.859 + decoder->mBackBuffer = buf; 1.860 + decoder->mBackBufferSize = roundup_buflen; 1.861 + } 1.862 + 1.863 + /* Copy remainder of netlib segment into backtrack buffer. */ 1.864 + memmove(decoder->mBackBuffer + decoder->mBackBufferLen, 1.865 + src->next_input_byte, 1.866 + src->bytes_in_buffer); 1.867 + 1.868 + /* Point to start of data to be rescanned. */ 1.869 + src->next_input_byte = decoder->mBackBuffer + decoder->mBackBufferLen - decoder->mBackBufferUnreadLen; 1.870 + src->bytes_in_buffer += decoder->mBackBufferUnreadLen; 1.871 + decoder->mBackBufferLen = (size_t)new_backtrack_buflen; 1.872 + decoder->mReading = true; 1.873 + 1.874 + return false; 1.875 +} 1.876 + 1.877 +/******************************************************************************/ 1.878 +/* data source manager method */ 1.879 +/* 1.880 + * Terminate source --- called by jpeg_finish_decompress() after all 1.881 + * data has been read to clean up JPEG source manager. NOT called by 1.882 + * jpeg_abort() or jpeg_destroy(). 1.883 + */ 1.884 +METHODDEF(void) 1.885 +term_source (j_decompress_ptr jd) 1.886 +{ 1.887 + nsJPEGDecoder *decoder = (nsJPEGDecoder *)(jd->client_data); 1.888 + 1.889 + // This function shouldn't be called if we ran into an error we didn't 1.890 + // recover from. 1.891 + NS_ABORT_IF_FALSE(decoder->mState != JPEG_ERROR, 1.892 + "Calling term_source on a JPEG with mState == JPEG_ERROR!"); 1.893 + 1.894 + // Notify using a helper method to get around protectedness issues. 1.895 + decoder->NotifyDone(); 1.896 +} 1.897 + 1.898 +} // namespace image 1.899 +} // namespace mozilla 1.900 + 1.901 + 1.902 +/**************** Inverted CMYK -> RGB conversion **************/ 1.903 +/* 1.904 + * Input is (Inverted) CMYK stored as 4 bytes per pixel. 1.905 + * Output is RGB stored as 3 bytes per pixel. 1.906 + * @param row Points to row buffer containing the CMYK bytes for each pixel in the row. 1.907 + * @param width Number of pixels in the row. 1.908 + */ 1.909 +static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width) 1.910 +{ 1.911 + /* Work from end to front to shrink from 4 bytes per pixel to 3 */ 1.912 + JSAMPROW in = row + width*4; 1.913 + JSAMPROW out = in; 1.914 + 1.915 + for (uint32_t i = width; i > 0; i--) { 1.916 + in -= 4; 1.917 + out -= 3; 1.918 + 1.919 + // Source is 'Inverted CMYK', output is RGB. 1.920 + // See: http://www.easyrgb.com/math.php?MATH=M12#text12 1.921 + // Or: http://www.ilkeratalay.com/colorspacesfaq.php#rgb 1.922 + 1.923 + // From CMYK to CMY 1.924 + // C = ( C * ( 1 - K ) + K ) 1.925 + // M = ( M * ( 1 - K ) + K ) 1.926 + // Y = ( Y * ( 1 - K ) + K ) 1.927 + 1.928 + // From Inverted CMYK to CMY is thus: 1.929 + // C = ( (1-iC) * (1 - (1-iK)) + (1-iK) ) => 1 - iC*iK 1.930 + // Same for M and Y 1.931 + 1.932 + // Convert from CMY (0..1) to RGB (0..1) 1.933 + // R = 1 - C => 1 - (1 - iC*iK) => iC*iK 1.934 + // G = 1 - M => 1 - (1 - iM*iK) => iM*iK 1.935 + // B = 1 - Y => 1 - (1 - iY*iK) => iY*iK 1.936 + 1.937 + // Convert from Inverted CMYK (0..255) to RGB (0..255) 1.938 + const uint32_t iC = in[0]; 1.939 + const uint32_t iM = in[1]; 1.940 + const uint32_t iY = in[2]; 1.941 + const uint32_t iK = in[3]; 1.942 + out[0] = iC*iK/255; // Red 1.943 + out[1] = iM*iK/255; // Green 1.944 + out[2] = iY*iK/255; // Blue 1.945 + } 1.946 +}