image/decoders/nsJPEGDecoder.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 *
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "ImageLogging.h"
michael@0 8 #include "nsJPEGDecoder.h"
michael@0 9 #include "Orientation.h"
michael@0 10 #include "EXIF.h"
michael@0 11
michael@0 12 #include "nsIInputStream.h"
michael@0 13
michael@0 14 #include "nspr.h"
michael@0 15 #include "nsCRT.h"
michael@0 16 #include "gfxColor.h"
michael@0 17
michael@0 18 #include "jerror.h"
michael@0 19
michael@0 20 #include "gfxPlatform.h"
michael@0 21
michael@0 22 extern "C" {
michael@0 23 #include "iccjpeg.h"
michael@0 24 }
michael@0 25
michael@0 26 #if defined(IS_BIG_ENDIAN)
michael@0 27 #define MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB JCS_EXT_XRGB
michael@0 28 #else
michael@0 29 #define MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB JCS_EXT_BGRX
michael@0 30 #endif
michael@0 31
michael@0 32 static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width);
michael@0 33
michael@0 34 namespace mozilla {
michael@0 35 namespace image {
michael@0 36
michael@0 37 #if defined(PR_LOGGING)
michael@0 38 static PRLogModuleInfo *
michael@0 39 GetJPEGLog()
michael@0 40 {
michael@0 41 static PRLogModuleInfo *sJPEGLog;
michael@0 42 if (!sJPEGLog)
michael@0 43 sJPEGLog = PR_NewLogModule("JPEGDecoder");
michael@0 44 return sJPEGLog;
michael@0 45 }
michael@0 46
michael@0 47 static PRLogModuleInfo *
michael@0 48 GetJPEGDecoderAccountingLog()
michael@0 49 {
michael@0 50 static PRLogModuleInfo *sJPEGDecoderAccountingLog;
michael@0 51 if (!sJPEGDecoderAccountingLog)
michael@0 52 sJPEGDecoderAccountingLog = PR_NewLogModule("JPEGDecoderAccounting");
michael@0 53 return sJPEGDecoderAccountingLog;
michael@0 54 }
michael@0 55 #else
michael@0 56 #define GetJPEGLog()
michael@0 57 #define GetJPEGDecoderAccountingLog()
michael@0 58 #endif
michael@0 59
michael@0 60 static qcms_profile*
michael@0 61 GetICCProfile(struct jpeg_decompress_struct &info)
michael@0 62 {
michael@0 63 JOCTET* profilebuf;
michael@0 64 uint32_t profileLength;
michael@0 65 qcms_profile* profile = nullptr;
michael@0 66
michael@0 67 if (read_icc_profile(&info, &profilebuf, &profileLength)) {
michael@0 68 profile = qcms_profile_from_memory(profilebuf, profileLength);
michael@0 69 free(profilebuf);
michael@0 70 }
michael@0 71
michael@0 72 return profile;
michael@0 73 }
michael@0 74
michael@0 75 METHODDEF(void) init_source (j_decompress_ptr jd);
michael@0 76 METHODDEF(boolean) fill_input_buffer (j_decompress_ptr jd);
michael@0 77 METHODDEF(void) skip_input_data (j_decompress_ptr jd, long num_bytes);
michael@0 78 METHODDEF(void) term_source (j_decompress_ptr jd);
michael@0 79 METHODDEF(void) my_error_exit (j_common_ptr cinfo);
michael@0 80
michael@0 81 /* Normal JFIF markers can't have more bytes than this. */
michael@0 82 #define MAX_JPEG_MARKER_LENGTH (((uint32_t)1 << 16) - 1)
michael@0 83
michael@0 84
michael@0 85 nsJPEGDecoder::nsJPEGDecoder(RasterImage& aImage, Decoder::DecodeStyle aDecodeStyle)
michael@0 86 : Decoder(aImage)
michael@0 87 , mDecodeStyle(aDecodeStyle)
michael@0 88 {
michael@0 89 mState = JPEG_HEADER;
michael@0 90 mReading = true;
michael@0 91 mImageData = nullptr;
michael@0 92
michael@0 93 mBytesToSkip = 0;
michael@0 94 memset(&mInfo, 0, sizeof(jpeg_decompress_struct));
michael@0 95 memset(&mSourceMgr, 0, sizeof(mSourceMgr));
michael@0 96 mInfo.client_data = (void*)this;
michael@0 97
michael@0 98 mSegment = nullptr;
michael@0 99 mSegmentLen = 0;
michael@0 100
michael@0 101 mBackBuffer = nullptr;
michael@0 102 mBackBufferLen = mBackBufferSize = mBackBufferUnreadLen = 0;
michael@0 103
michael@0 104 mInProfile = nullptr;
michael@0 105 mTransform = nullptr;
michael@0 106
michael@0 107 mCMSMode = 0;
michael@0 108
michael@0 109 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
michael@0 110 ("nsJPEGDecoder::nsJPEGDecoder: Creating JPEG decoder %p",
michael@0 111 this));
michael@0 112 }
michael@0 113
michael@0 114 nsJPEGDecoder::~nsJPEGDecoder()
michael@0 115 {
michael@0 116 // Step 8: Release JPEG decompression object
michael@0 117 mInfo.src = nullptr;
michael@0 118 jpeg_destroy_decompress(&mInfo);
michael@0 119
michael@0 120 PR_FREEIF(mBackBuffer);
michael@0 121 if (mTransform)
michael@0 122 qcms_transform_release(mTransform);
michael@0 123 if (mInProfile)
michael@0 124 qcms_profile_release(mInProfile);
michael@0 125
michael@0 126 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
michael@0 127 ("nsJPEGDecoder::~nsJPEGDecoder: Destroying JPEG decoder %p",
michael@0 128 this));
michael@0 129 }
michael@0 130
michael@0 131 Telemetry::ID
michael@0 132 nsJPEGDecoder::SpeedHistogram()
michael@0 133 {
michael@0 134 return Telemetry::IMAGE_DECODE_SPEED_JPEG;
michael@0 135 }
michael@0 136
michael@0 137 void
michael@0 138 nsJPEGDecoder::InitInternal()
michael@0 139 {
michael@0 140 mCMSMode = gfxPlatform::GetCMSMode();
michael@0 141 if ((mDecodeFlags & DECODER_NO_COLORSPACE_CONVERSION) != 0)
michael@0 142 mCMSMode = eCMSMode_Off;
michael@0 143
michael@0 144 /* We set up the normal JPEG error routines, then override error_exit. */
michael@0 145 mInfo.err = jpeg_std_error(&mErr.pub);
michael@0 146 /* mInfo.err = jpeg_std_error(&mErr.pub); */
michael@0 147 mErr.pub.error_exit = my_error_exit;
michael@0 148 /* Establish the setjmp return context for my_error_exit to use. */
michael@0 149 if (setjmp(mErr.setjmp_buffer)) {
michael@0 150 /* If we get here, the JPEG code has signaled an error.
michael@0 151 * We need to clean up the JPEG object, close the input file, and return.
michael@0 152 */
michael@0 153 PostDecoderError(NS_ERROR_FAILURE);
michael@0 154 return;
michael@0 155 }
michael@0 156
michael@0 157 /* Step 1: allocate and initialize JPEG decompression object */
michael@0 158 jpeg_create_decompress(&mInfo);
michael@0 159 /* Set the source manager */
michael@0 160 mInfo.src = &mSourceMgr;
michael@0 161
michael@0 162 /* Step 2: specify data source (eg, a file) */
michael@0 163
michael@0 164 /* Setup callback functions. */
michael@0 165 mSourceMgr.init_source = init_source;
michael@0 166 mSourceMgr.fill_input_buffer = fill_input_buffer;
michael@0 167 mSourceMgr.skip_input_data = skip_input_data;
michael@0 168 mSourceMgr.resync_to_restart = jpeg_resync_to_restart;
michael@0 169 mSourceMgr.term_source = term_source;
michael@0 170
michael@0 171 /* Record app markers for ICC data */
michael@0 172 for (uint32_t m = 0; m < 16; m++)
michael@0 173 jpeg_save_markers(&mInfo, JPEG_APP0 + m, 0xFFFF);
michael@0 174 }
michael@0 175
michael@0 176 void
michael@0 177 nsJPEGDecoder::FinishInternal()
michael@0 178 {
michael@0 179 /* If we're not in any sort of error case, flush the decoder.
michael@0 180 *
michael@0 181 * XXXbholley - It seems wrong that this should be necessary, but at the
michael@0 182 * moment I'm just folding the contents of Flush() into Close() so that
michael@0 183 * we can get rid of it.
michael@0 184 *
michael@0 185 * XXX(seth): It'd be great to get rid of this. For now, we treat this as a
michael@0 186 * write to a synchronous decoder, which means that this must be called only
michael@0 187 * on the main thread. (That's asserted in Decoder::Finish and
michael@0 188 * Decoder::FinishSharedDecoder.)
michael@0 189 */
michael@0 190 if ((mState != JPEG_DONE && mState != JPEG_SINK_NON_JPEG_TRAILER) &&
michael@0 191 (mState != JPEG_ERROR) &&
michael@0 192 !IsSizeDecode())
michael@0 193 this->Write(nullptr, 0, DECODE_SYNC);
michael@0 194 }
michael@0 195
michael@0 196 void
michael@0 197 nsJPEGDecoder::WriteInternal(const char *aBuffer, uint32_t aCount, DecodeStrategy)
michael@0 198 {
michael@0 199 mSegment = (const JOCTET *)aBuffer;
michael@0 200 mSegmentLen = aCount;
michael@0 201
michael@0 202 NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!");
michael@0 203
michael@0 204 /* Return here if there is a fatal error within libjpeg. */
michael@0 205 nsresult error_code;
michael@0 206 // This cast to nsresult makes sense because setjmp() returns whatever we
michael@0 207 // passed to longjmp(), which was actually an nsresult.
michael@0 208 if ((error_code = (nsresult)setjmp(mErr.setjmp_buffer)) != NS_OK) {
michael@0 209 if (error_code == NS_ERROR_FAILURE) {
michael@0 210 PostDataError();
michael@0 211 /* Error due to corrupt stream - return NS_OK and consume silently
michael@0 212 so that libpr0n doesn't throw away a partial image load */
michael@0 213 mState = JPEG_SINK_NON_JPEG_TRAILER;
michael@0 214 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
michael@0 215 ("} (setjmp returned NS_ERROR_FAILURE)"));
michael@0 216 return;
michael@0 217 } else {
michael@0 218 /* Error due to reasons external to the stream (probably out of
michael@0 219 memory) - let libpr0n attempt to clean up, even though
michael@0 220 mozilla is seconds away from falling flat on its face. */
michael@0 221 PostDecoderError(error_code);
michael@0 222 mState = JPEG_ERROR;
michael@0 223 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
michael@0 224 ("} (setjmp returned an error)"));
michael@0 225 return;
michael@0 226 }
michael@0 227 }
michael@0 228
michael@0 229 PR_LOG(GetJPEGLog(), PR_LOG_DEBUG,
michael@0 230 ("[this=%p] nsJPEGDecoder::Write -- processing JPEG data\n", this));
michael@0 231
michael@0 232 switch (mState) {
michael@0 233 case JPEG_HEADER:
michael@0 234 {
michael@0 235 LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- entering JPEG_HEADER case");
michael@0 236
michael@0 237 /* Step 3: read file parameters with jpeg_read_header() */
michael@0 238 if (jpeg_read_header(&mInfo, TRUE) == JPEG_SUSPENDED) {
michael@0 239 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
michael@0 240 ("} (JPEG_SUSPENDED)"));
michael@0 241 return; /* I/O suspension */
michael@0 242 }
michael@0 243
michael@0 244 int sampleSize = mImage.GetRequestedSampleSize();
michael@0 245 if (sampleSize > 0) {
michael@0 246 mInfo.scale_num = 1;
michael@0 247 mInfo.scale_denom = sampleSize;
michael@0 248 }
michael@0 249
michael@0 250 /* Used to set up image size so arrays can be allocated */
michael@0 251 jpeg_calc_output_dimensions(&mInfo);
michael@0 252
michael@0 253 // Post our size to the superclass
michael@0 254 PostSize(mInfo.output_width, mInfo.output_height, ReadOrientationFromEXIF());
michael@0 255 if (HasError()) {
michael@0 256 // Setting the size led to an error.
michael@0 257 mState = JPEG_ERROR;
michael@0 258 return;
michael@0 259 }
michael@0 260
michael@0 261 /* If we're doing a size decode, we're done. */
michael@0 262 if (IsSizeDecode())
michael@0 263 return;
michael@0 264
michael@0 265 /* We're doing a full decode. */
michael@0 266 if (mCMSMode != eCMSMode_Off &&
michael@0 267 (mInProfile = GetICCProfile(mInfo)) != nullptr) {
michael@0 268 uint32_t profileSpace = qcms_profile_get_color_space(mInProfile);
michael@0 269 bool mismatch = false;
michael@0 270
michael@0 271 #ifdef DEBUG_tor
michael@0 272 fprintf(stderr, "JPEG profileSpace: 0x%08X\n", profileSpace);
michael@0 273 #endif
michael@0 274 switch (mInfo.jpeg_color_space) {
michael@0 275 case JCS_GRAYSCALE:
michael@0 276 if (profileSpace == icSigRgbData)
michael@0 277 mInfo.out_color_space = JCS_RGB;
michael@0 278 else if (profileSpace != icSigGrayData)
michael@0 279 mismatch = true;
michael@0 280 break;
michael@0 281 case JCS_RGB:
michael@0 282 if (profileSpace != icSigRgbData)
michael@0 283 mismatch = true;
michael@0 284 break;
michael@0 285 case JCS_YCbCr:
michael@0 286 if (profileSpace == icSigRgbData)
michael@0 287 mInfo.out_color_space = JCS_RGB;
michael@0 288 else
michael@0 289 // qcms doesn't support ycbcr
michael@0 290 mismatch = true;
michael@0 291 break;
michael@0 292 case JCS_CMYK:
michael@0 293 case JCS_YCCK:
michael@0 294 // qcms doesn't support cmyk
michael@0 295 mismatch = true;
michael@0 296 break;
michael@0 297 default:
michael@0 298 mState = JPEG_ERROR;
michael@0 299 PostDataError();
michael@0 300 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
michael@0 301 ("} (unknown colorpsace (1))"));
michael@0 302 return;
michael@0 303 }
michael@0 304
michael@0 305 if (!mismatch) {
michael@0 306 qcms_data_type type;
michael@0 307 switch (mInfo.out_color_space) {
michael@0 308 case JCS_GRAYSCALE:
michael@0 309 type = QCMS_DATA_GRAY_8;
michael@0 310 break;
michael@0 311 case JCS_RGB:
michael@0 312 type = QCMS_DATA_RGB_8;
michael@0 313 break;
michael@0 314 default:
michael@0 315 mState = JPEG_ERROR;
michael@0 316 PostDataError();
michael@0 317 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
michael@0 318 ("} (unknown colorpsace (2))"));
michael@0 319 return;
michael@0 320 }
michael@0 321 #if 0
michael@0 322 /* We don't currently support CMYK profiles. The following
michael@0 323 * code dealt with lcms types. Add something like this
michael@0 324 * back when we gain support for CMYK.
michael@0 325 */
michael@0 326 /* Adobe Photoshop writes YCCK/CMYK files with inverted data */
michael@0 327 if (mInfo.out_color_space == JCS_CMYK)
michael@0 328 type |= FLAVOR_SH(mInfo.saw_Adobe_marker ? 1 : 0);
michael@0 329 #endif
michael@0 330
michael@0 331 if (gfxPlatform::GetCMSOutputProfile()) {
michael@0 332
michael@0 333 /* Calculate rendering intent. */
michael@0 334 int intent = gfxPlatform::GetRenderingIntent();
michael@0 335 if (intent == -1)
michael@0 336 intent = qcms_profile_get_rendering_intent(mInProfile);
michael@0 337
michael@0 338 /* Create the color management transform. */
michael@0 339 mTransform = qcms_transform_create(mInProfile,
michael@0 340 type,
michael@0 341 gfxPlatform::GetCMSOutputProfile(),
michael@0 342 QCMS_DATA_RGB_8,
michael@0 343 (qcms_intent)intent);
michael@0 344 }
michael@0 345 } else {
michael@0 346 #ifdef DEBUG_tor
michael@0 347 fprintf(stderr, "ICM profile colorspace mismatch\n");
michael@0 348 #endif
michael@0 349 }
michael@0 350 }
michael@0 351
michael@0 352 if (!mTransform) {
michael@0 353 switch (mInfo.jpeg_color_space) {
michael@0 354 case JCS_GRAYSCALE:
michael@0 355 case JCS_RGB:
michael@0 356 case JCS_YCbCr:
michael@0 357 // if we're not color managing we can decode directly to
michael@0 358 // MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB
michael@0 359 if (mCMSMode != eCMSMode_All) {
michael@0 360 mInfo.out_color_space = MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB;
michael@0 361 mInfo.out_color_components = 4;
michael@0 362 } else {
michael@0 363 mInfo.out_color_space = JCS_RGB;
michael@0 364 }
michael@0 365 break;
michael@0 366 case JCS_CMYK:
michael@0 367 case JCS_YCCK:
michael@0 368 /* libjpeg can convert from YCCK to CMYK, but not to RGB */
michael@0 369 mInfo.out_color_space = JCS_CMYK;
michael@0 370 break;
michael@0 371 default:
michael@0 372 mState = JPEG_ERROR;
michael@0 373 PostDataError();
michael@0 374 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
michael@0 375 ("} (unknown colorpsace (3))"));
michael@0 376 return;
michael@0 377 break;
michael@0 378 }
michael@0 379 }
michael@0 380
michael@0 381 /*
michael@0 382 * Don't allocate a giant and superfluous memory buffer
michael@0 383 * when not doing a progressive decode.
michael@0 384 */
michael@0 385 mInfo.buffered_image = mDecodeStyle == PROGRESSIVE && jpeg_has_multiple_scans(&mInfo);
michael@0 386
michael@0 387 if (!mImageData) {
michael@0 388 mState = JPEG_ERROR;
michael@0 389 PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
michael@0 390 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
michael@0 391 ("} (could not initialize image frame)"));
michael@0 392 return;
michael@0 393 }
michael@0 394
michael@0 395 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
michael@0 396 (" JPEGDecoderAccounting: nsJPEGDecoder::Write -- created image frame with %ux%u pixels",
michael@0 397 mInfo.output_width, mInfo.output_height));
michael@0 398
michael@0 399 mState = JPEG_START_DECOMPRESS;
michael@0 400 }
michael@0 401
michael@0 402 case JPEG_START_DECOMPRESS:
michael@0 403 {
michael@0 404 LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- entering JPEG_START_DECOMPRESS case");
michael@0 405 /* Step 4: set parameters for decompression */
michael@0 406
michael@0 407 /* FIXME -- Should reset dct_method and dither mode
michael@0 408 * for final pass of progressive JPEG
michael@0 409 */
michael@0 410 mInfo.dct_method = JDCT_ISLOW;
michael@0 411 mInfo.dither_mode = JDITHER_FS;
michael@0 412 mInfo.do_fancy_upsampling = TRUE;
michael@0 413 mInfo.enable_2pass_quant = FALSE;
michael@0 414 mInfo.do_block_smoothing = TRUE;
michael@0 415
michael@0 416 /* Step 5: Start decompressor */
michael@0 417 if (jpeg_start_decompress(&mInfo) == FALSE) {
michael@0 418 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
michael@0 419 ("} (I/O suspension after jpeg_start_decompress())"));
michael@0 420 return; /* I/O suspension */
michael@0 421 }
michael@0 422
michael@0 423
michael@0 424 /* If this is a progressive JPEG ... */
michael@0 425 mState = mInfo.buffered_image ? JPEG_DECOMPRESS_PROGRESSIVE : JPEG_DECOMPRESS_SEQUENTIAL;
michael@0 426 }
michael@0 427
michael@0 428 case JPEG_DECOMPRESS_SEQUENTIAL:
michael@0 429 {
michael@0 430 if (mState == JPEG_DECOMPRESS_SEQUENTIAL)
michael@0 431 {
michael@0 432 LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- JPEG_DECOMPRESS_SEQUENTIAL case");
michael@0 433
michael@0 434 bool suspend;
michael@0 435 OutputScanlines(&suspend);
michael@0 436
michael@0 437 if (suspend) {
michael@0 438 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
michael@0 439 ("} (I/O suspension after OutputScanlines() - SEQUENTIAL)"));
michael@0 440 return; /* I/O suspension */
michael@0 441 }
michael@0 442
michael@0 443 /* If we've completed image output ... */
michael@0 444 NS_ASSERTION(mInfo.output_scanline == mInfo.output_height, "We didn't process all of the data!");
michael@0 445 mState = JPEG_DONE;
michael@0 446 }
michael@0 447 }
michael@0 448
michael@0 449 case JPEG_DECOMPRESS_PROGRESSIVE:
michael@0 450 {
michael@0 451 if (mState == JPEG_DECOMPRESS_PROGRESSIVE)
michael@0 452 {
michael@0 453 LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- JPEG_DECOMPRESS_PROGRESSIVE case");
michael@0 454
michael@0 455 int status;
michael@0 456 do {
michael@0 457 status = jpeg_consume_input(&mInfo);
michael@0 458 } while ((status != JPEG_SUSPENDED) &&
michael@0 459 (status != JPEG_REACHED_EOI));
michael@0 460
michael@0 461 for (;;) {
michael@0 462 if (mInfo.output_scanline == 0) {
michael@0 463 int scan = mInfo.input_scan_number;
michael@0 464
michael@0 465 /* if we haven't displayed anything yet (output_scan_number==0)
michael@0 466 and we have enough data for a complete scan, force output
michael@0 467 of the last full scan */
michael@0 468 if ((mInfo.output_scan_number == 0) &&
michael@0 469 (scan > 1) &&
michael@0 470 (status != JPEG_REACHED_EOI))
michael@0 471 scan--;
michael@0 472
michael@0 473 if (!jpeg_start_output(&mInfo, scan)) {
michael@0 474 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
michael@0 475 ("} (I/O suspension after jpeg_start_output() - PROGRESSIVE)"));
michael@0 476 return; /* I/O suspension */
michael@0 477 }
michael@0 478 }
michael@0 479
michael@0 480 if (mInfo.output_scanline == 0xffffff)
michael@0 481 mInfo.output_scanline = 0;
michael@0 482
michael@0 483 bool suspend;
michael@0 484 OutputScanlines(&suspend);
michael@0 485
michael@0 486 if (suspend) {
michael@0 487 if (mInfo.output_scanline == 0) {
michael@0 488 /* didn't manage to read any lines - flag so we don't call
michael@0 489 jpeg_start_output() multiple times for the same scan */
michael@0 490 mInfo.output_scanline = 0xffffff;
michael@0 491 }
michael@0 492 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
michael@0 493 ("} (I/O suspension after OutputScanlines() - PROGRESSIVE)"));
michael@0 494 return; /* I/O suspension */
michael@0 495 }
michael@0 496
michael@0 497 if (mInfo.output_scanline == mInfo.output_height)
michael@0 498 {
michael@0 499 if (!jpeg_finish_output(&mInfo)) {
michael@0 500 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
michael@0 501 ("} (I/O suspension after jpeg_finish_output() - PROGRESSIVE)"));
michael@0 502 return; /* I/O suspension */
michael@0 503 }
michael@0 504
michael@0 505 if (jpeg_input_complete(&mInfo) &&
michael@0 506 (mInfo.input_scan_number == mInfo.output_scan_number))
michael@0 507 break;
michael@0 508
michael@0 509 mInfo.output_scanline = 0;
michael@0 510 }
michael@0 511 }
michael@0 512
michael@0 513 mState = JPEG_DONE;
michael@0 514 }
michael@0 515 }
michael@0 516
michael@0 517 case JPEG_DONE:
michael@0 518 {
michael@0 519 LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::ProcessData -- entering JPEG_DONE case");
michael@0 520
michael@0 521 /* Step 7: Finish decompression */
michael@0 522
michael@0 523 if (jpeg_finish_decompress(&mInfo) == FALSE) {
michael@0 524 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
michael@0 525 ("} (I/O suspension after jpeg_finish_decompress() - DONE)"));
michael@0 526 return; /* I/O suspension */
michael@0 527 }
michael@0 528
michael@0 529 mState = JPEG_SINK_NON_JPEG_TRAILER;
michael@0 530
michael@0 531 /* we're done dude */
michael@0 532 break;
michael@0 533 }
michael@0 534 case JPEG_SINK_NON_JPEG_TRAILER:
michael@0 535 PR_LOG(GetJPEGLog(), PR_LOG_DEBUG,
michael@0 536 ("[this=%p] nsJPEGDecoder::ProcessData -- entering JPEG_SINK_NON_JPEG_TRAILER case\n", this));
michael@0 537
michael@0 538 break;
michael@0 539
michael@0 540 case JPEG_ERROR:
michael@0 541 NS_ABORT_IF_FALSE(0, "Should always return immediately after error and not re-enter decoder");
michael@0 542 }
michael@0 543
michael@0 544 PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
michael@0 545 ("} (end of function)"));
michael@0 546 return;
michael@0 547 }
michael@0 548
michael@0 549 Orientation
michael@0 550 nsJPEGDecoder::ReadOrientationFromEXIF()
michael@0 551 {
michael@0 552 jpeg_saved_marker_ptr marker;
michael@0 553
michael@0 554 // Locate the APP1 marker, where EXIF data is stored, in the marker list.
michael@0 555 for (marker = mInfo.marker_list ; marker != nullptr ; marker = marker->next) {
michael@0 556 if (marker->marker == JPEG_APP0 + 1)
michael@0 557 break;
michael@0 558 }
michael@0 559
michael@0 560 // If we're at the end of the list, there's no EXIF data.
michael@0 561 if (!marker)
michael@0 562 return Orientation();
michael@0 563
michael@0 564 // Extract the orientation information.
michael@0 565 EXIFData exif = EXIFParser::Parse(marker->data,
michael@0 566 static_cast<uint32_t>(marker->data_length));
michael@0 567 return exif.orientation;
michael@0 568 }
michael@0 569
michael@0 570 void
michael@0 571 nsJPEGDecoder::NotifyDone()
michael@0 572 {
michael@0 573 PostFrameStop(FrameBlender::kFrameOpaque);
michael@0 574 PostDecodeDone();
michael@0 575 }
michael@0 576
michael@0 577 void
michael@0 578 nsJPEGDecoder::OutputScanlines(bool* suspend)
michael@0 579 {
michael@0 580 *suspend = false;
michael@0 581
michael@0 582 const uint32_t top = mInfo.output_scanline;
michael@0 583
michael@0 584 while ((mInfo.output_scanline < mInfo.output_height)) {
michael@0 585 /* Use the Cairo image buffer as scanline buffer */
michael@0 586 uint32_t *imageRow = ((uint32_t*)mImageData) +
michael@0 587 (mInfo.output_scanline * mInfo.output_width);
michael@0 588
michael@0 589 if (mInfo.out_color_space == MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB) {
michael@0 590 /* Special case: scanline will be directly converted into packed ARGB */
michael@0 591 if (jpeg_read_scanlines(&mInfo, (JSAMPARRAY)&imageRow, 1) != 1) {
michael@0 592 *suspend = true; /* suspend */
michael@0 593 break;
michael@0 594 }
michael@0 595 continue; /* all done for this row! */
michael@0 596 }
michael@0 597
michael@0 598 JSAMPROW sampleRow = (JSAMPROW)imageRow;
michael@0 599 if (mInfo.output_components == 3) {
michael@0 600 /* Put the pixels at end of row to enable in-place expansion */
michael@0 601 sampleRow += mInfo.output_width;
michael@0 602 }
michael@0 603
michael@0 604 /* Request one scanline. Returns 0 or 1 scanlines. */
michael@0 605 if (jpeg_read_scanlines(&mInfo, &sampleRow, 1) != 1) {
michael@0 606 *suspend = true; /* suspend */
michael@0 607 break;
michael@0 608 }
michael@0 609
michael@0 610 if (mTransform) {
michael@0 611 JSAMPROW source = sampleRow;
michael@0 612 if (mInfo.out_color_space == JCS_GRAYSCALE) {
michael@0 613 /* Convert from the 1byte grey pixels at begin of row
michael@0 614 to the 3byte RGB byte pixels at 'end' of row */
michael@0 615 sampleRow += mInfo.output_width;
michael@0 616 }
michael@0 617 qcms_transform_data(mTransform, source, sampleRow, mInfo.output_width);
michael@0 618 /* Move 3byte RGB data to end of row */
michael@0 619 if (mInfo.out_color_space == JCS_CMYK) {
michael@0 620 memmove(sampleRow + mInfo.output_width,
michael@0 621 sampleRow,
michael@0 622 3 * mInfo.output_width);
michael@0 623 sampleRow += mInfo.output_width;
michael@0 624 }
michael@0 625 } else {
michael@0 626 if (mInfo.out_color_space == JCS_CMYK) {
michael@0 627 /* Convert from CMYK to RGB */
michael@0 628 /* We cannot convert directly to Cairo, as the CMSRGBTransform may wants to do a RGB transform... */
michael@0 629 /* Would be better to have platform CMSenabled transformation from CMYK to (A)RGB... */
michael@0 630 cmyk_convert_rgb((JSAMPROW)imageRow, mInfo.output_width);
michael@0 631 sampleRow += mInfo.output_width;
michael@0 632 }
michael@0 633 if (mCMSMode == eCMSMode_All) {
michael@0 634 /* No embedded ICC profile - treat as sRGB */
michael@0 635 qcms_transform *transform = gfxPlatform::GetCMSRGBTransform();
michael@0 636 if (transform) {
michael@0 637 qcms_transform_data(transform, sampleRow, sampleRow, mInfo.output_width);
michael@0 638 }
michael@0 639 }
michael@0 640 }
michael@0 641
michael@0 642 // counter for while() loops below
michael@0 643 uint32_t idx = mInfo.output_width;
michael@0 644
michael@0 645 // copy as bytes until source pointer is 32-bit-aligned
michael@0 646 for (; (NS_PTR_TO_UINT32(sampleRow) & 0x3) && idx; --idx) {
michael@0 647 *imageRow++ = gfxPackedPixel(0xFF, sampleRow[0], sampleRow[1], sampleRow[2]);
michael@0 648 sampleRow += 3;
michael@0 649 }
michael@0 650
michael@0 651 // copy pixels in blocks of 4
michael@0 652 while (idx >= 4) {
michael@0 653 GFX_BLOCK_RGB_TO_FRGB(sampleRow, imageRow);
michael@0 654 idx -= 4;
michael@0 655 sampleRow += 12;
michael@0 656 imageRow += 4;
michael@0 657 }
michael@0 658
michael@0 659 // copy remaining pixel(s)
michael@0 660 while (idx--) {
michael@0 661 // 32-bit read of final pixel will exceed buffer, so read bytes
michael@0 662 *imageRow++ = gfxPackedPixel(0xFF, sampleRow[0], sampleRow[1], sampleRow[2]);
michael@0 663 sampleRow += 3;
michael@0 664 }
michael@0 665 }
michael@0 666
michael@0 667 if (top != mInfo.output_scanline) {
michael@0 668 nsIntRect r(0, top, mInfo.output_width, mInfo.output_scanline-top);
michael@0 669 PostInvalidation(r);
michael@0 670 }
michael@0 671
michael@0 672 }
michael@0 673
michael@0 674
michael@0 675 /* Override the standard error method in the IJG JPEG decoder code. */
michael@0 676 METHODDEF(void)
michael@0 677 my_error_exit (j_common_ptr cinfo)
michael@0 678 {
michael@0 679 decoder_error_mgr *err = (decoder_error_mgr *) cinfo->err;
michael@0 680
michael@0 681 /* Convert error to a browser error code */
michael@0 682 nsresult error_code = err->pub.msg_code == JERR_OUT_OF_MEMORY
michael@0 683 ? NS_ERROR_OUT_OF_MEMORY
michael@0 684 : NS_ERROR_FAILURE;
michael@0 685
michael@0 686 #ifdef DEBUG
michael@0 687 char buffer[JMSG_LENGTH_MAX];
michael@0 688
michael@0 689 /* Create the message */
michael@0 690 (*err->pub.format_message) (cinfo, buffer);
michael@0 691
michael@0 692 fprintf(stderr, "JPEG decoding error:\n%s\n", buffer);
michael@0 693 #endif
michael@0 694
michael@0 695 /* Return control to the setjmp point. We pass an nsresult masquerading as
michael@0 696 * an int, which works because the setjmp() caller casts it back. */
michael@0 697 longjmp(err->setjmp_buffer, static_cast<int>(error_code));
michael@0 698 }
michael@0 699
michael@0 700 /******************************************************************************/
michael@0 701 /*-----------------------------------------------------------------------------
michael@0 702 * This is the callback routine from the IJG JPEG library used to supply new
michael@0 703 * data to the decompressor when its input buffer is exhausted. It juggles
michael@0 704 * multiple buffers in an attempt to avoid unnecessary copying of input data.
michael@0 705 *
michael@0 706 * (A simpler scheme is possible: It's much easier to use only a single
michael@0 707 * buffer; when fill_input_buffer() is called, move any unconsumed data
michael@0 708 * (beyond the current pointer/count) down to the beginning of this buffer and
michael@0 709 * then load new data into the remaining buffer space. This approach requires
michael@0 710 * a little more data copying but is far easier to get right.)
michael@0 711 *
michael@0 712 * At any one time, the JPEG decompressor is either reading from the necko
michael@0 713 * input buffer, which is volatile across top-level calls to the IJG library,
michael@0 714 * or the "backtrack" buffer. The backtrack buffer contains the remaining
michael@0 715 * unconsumed data from the necko buffer after parsing was suspended due
michael@0 716 * to insufficient data in some previous call to the IJG library.
michael@0 717 *
michael@0 718 * When suspending, the decompressor will back up to a convenient restart
michael@0 719 * point (typically the start of the current MCU). The variables
michael@0 720 * next_input_byte & bytes_in_buffer indicate where the restart point will be
michael@0 721 * if the current call returns FALSE. Data beyond this point must be
michael@0 722 * rescanned after resumption, so it must be preserved in case the decompressor
michael@0 723 * decides to backtrack.
michael@0 724 *
michael@0 725 * Returns:
michael@0 726 * TRUE if additional data is available, FALSE if no data present and
michael@0 727 * the JPEG library should therefore suspend processing of input stream
michael@0 728 *---------------------------------------------------------------------------*/
michael@0 729
michael@0 730 /******************************************************************************/
michael@0 731 /* data source manager method */
michael@0 732 /******************************************************************************/
michael@0 733
michael@0 734
michael@0 735 /******************************************************************************/
michael@0 736 /* data source manager method
michael@0 737 Initialize source. This is called by jpeg_read_header() before any
michael@0 738 data is actually read. May leave
michael@0 739 bytes_in_buffer set to 0 (in which case a fill_input_buffer() call
michael@0 740 will occur immediately).
michael@0 741 */
michael@0 742 METHODDEF(void)
michael@0 743 init_source (j_decompress_ptr jd)
michael@0 744 {
michael@0 745 }
michael@0 746
michael@0 747 /******************************************************************************/
michael@0 748 /* data source manager method
michael@0 749 Skip num_bytes worth of data. The buffer pointer and count should
michael@0 750 be advanced over num_bytes input bytes, refilling the buffer as
michael@0 751 needed. This is used to skip over a potentially large amount of
michael@0 752 uninteresting data (such as an APPn marker). In some applications
michael@0 753 it may be possible to optimize away the reading of the skipped data,
michael@0 754 but it's not clear that being smart is worth much trouble; large
michael@0 755 skips are uncommon. bytes_in_buffer may be zero on return.
michael@0 756 A zero or negative skip count should be treated as a no-op.
michael@0 757 */
michael@0 758 METHODDEF(void)
michael@0 759 skip_input_data (j_decompress_ptr jd, long num_bytes)
michael@0 760 {
michael@0 761 struct jpeg_source_mgr *src = jd->src;
michael@0 762 nsJPEGDecoder *decoder = (nsJPEGDecoder *)(jd->client_data);
michael@0 763
michael@0 764 if (num_bytes > (long)src->bytes_in_buffer) {
michael@0 765 /*
michael@0 766 * Can't skip it all right now until we get more data from
michael@0 767 * network stream. Set things up so that fill_input_buffer
michael@0 768 * will skip remaining amount.
michael@0 769 */
michael@0 770 decoder->mBytesToSkip = (size_t)num_bytes - src->bytes_in_buffer;
michael@0 771 src->next_input_byte += src->bytes_in_buffer;
michael@0 772 src->bytes_in_buffer = 0;
michael@0 773
michael@0 774 } else {
michael@0 775 /* Simple case. Just advance buffer pointer */
michael@0 776
michael@0 777 src->bytes_in_buffer -= (size_t)num_bytes;
michael@0 778 src->next_input_byte += num_bytes;
michael@0 779 }
michael@0 780 }
michael@0 781
michael@0 782
michael@0 783 /******************************************************************************/
michael@0 784 /* data source manager method
michael@0 785 This is called whenever bytes_in_buffer has reached zero and more
michael@0 786 data is wanted. In typical applications, it should read fresh data
michael@0 787 into the buffer (ignoring the current state of next_input_byte and
michael@0 788 bytes_in_buffer), reset the pointer & count to the start of the
michael@0 789 buffer, and return TRUE indicating that the buffer has been reloaded.
michael@0 790 It is not necessary to fill the buffer entirely, only to obtain at
michael@0 791 least one more byte. bytes_in_buffer MUST be set to a positive value
michael@0 792 if TRUE is returned. A FALSE return should only be used when I/O
michael@0 793 suspension is desired.
michael@0 794 */
michael@0 795 METHODDEF(boolean)
michael@0 796 fill_input_buffer (j_decompress_ptr jd)
michael@0 797 {
michael@0 798 struct jpeg_source_mgr *src = jd->src;
michael@0 799 nsJPEGDecoder *decoder = (nsJPEGDecoder *)(jd->client_data);
michael@0 800
michael@0 801 if (decoder->mReading) {
michael@0 802 const JOCTET *new_buffer = decoder->mSegment;
michael@0 803 uint32_t new_buflen = decoder->mSegmentLen;
michael@0 804
michael@0 805 if (!new_buffer || new_buflen == 0)
michael@0 806 return false; /* suspend */
michael@0 807
michael@0 808 decoder->mSegmentLen = 0;
michael@0 809
michael@0 810 if (decoder->mBytesToSkip) {
michael@0 811 if (decoder->mBytesToSkip < new_buflen) {
michael@0 812 /* All done skipping bytes; Return what's left. */
michael@0 813 new_buffer += decoder->mBytesToSkip;
michael@0 814 new_buflen -= decoder->mBytesToSkip;
michael@0 815 decoder->mBytesToSkip = 0;
michael@0 816 } else {
michael@0 817 /* Still need to skip some more data in the future */
michael@0 818 decoder->mBytesToSkip -= (size_t)new_buflen;
michael@0 819 return false; /* suspend */
michael@0 820 }
michael@0 821 }
michael@0 822
michael@0 823 decoder->mBackBufferUnreadLen = src->bytes_in_buffer;
michael@0 824
michael@0 825 src->next_input_byte = new_buffer;
michael@0 826 src->bytes_in_buffer = (size_t)new_buflen;
michael@0 827 decoder->mReading = false;
michael@0 828
michael@0 829 return true;
michael@0 830 }
michael@0 831
michael@0 832 if (src->next_input_byte != decoder->mSegment) {
michael@0 833 /* Backtrack data has been permanently consumed. */
michael@0 834 decoder->mBackBufferUnreadLen = 0;
michael@0 835 decoder->mBackBufferLen = 0;
michael@0 836 }
michael@0 837
michael@0 838 /* Save remainder of netlib buffer in backtrack buffer */
michael@0 839 const uint32_t new_backtrack_buflen = src->bytes_in_buffer + decoder->mBackBufferLen;
michael@0 840
michael@0 841 /* Make sure backtrack buffer is big enough to hold new data. */
michael@0 842 if (decoder->mBackBufferSize < new_backtrack_buflen) {
michael@0 843 /* Check for malformed MARKER segment lengths, before allocating space for it */
michael@0 844 if (new_backtrack_buflen > MAX_JPEG_MARKER_LENGTH) {
michael@0 845 my_error_exit((j_common_ptr)(&decoder->mInfo));
michael@0 846 }
michael@0 847
michael@0 848 /* Round up to multiple of 256 bytes. */
michael@0 849 const size_t roundup_buflen = ((new_backtrack_buflen + 255) >> 8) << 8;
michael@0 850 JOCTET *buf = (JOCTET *)PR_REALLOC(decoder->mBackBuffer, roundup_buflen);
michael@0 851 /* Check for OOM */
michael@0 852 if (!buf) {
michael@0 853 decoder->mInfo.err->msg_code = JERR_OUT_OF_MEMORY;
michael@0 854 my_error_exit((j_common_ptr)(&decoder->mInfo));
michael@0 855 }
michael@0 856 decoder->mBackBuffer = buf;
michael@0 857 decoder->mBackBufferSize = roundup_buflen;
michael@0 858 }
michael@0 859
michael@0 860 /* Copy remainder of netlib segment into backtrack buffer. */
michael@0 861 memmove(decoder->mBackBuffer + decoder->mBackBufferLen,
michael@0 862 src->next_input_byte,
michael@0 863 src->bytes_in_buffer);
michael@0 864
michael@0 865 /* Point to start of data to be rescanned. */
michael@0 866 src->next_input_byte = decoder->mBackBuffer + decoder->mBackBufferLen - decoder->mBackBufferUnreadLen;
michael@0 867 src->bytes_in_buffer += decoder->mBackBufferUnreadLen;
michael@0 868 decoder->mBackBufferLen = (size_t)new_backtrack_buflen;
michael@0 869 decoder->mReading = true;
michael@0 870
michael@0 871 return false;
michael@0 872 }
michael@0 873
michael@0 874 /******************************************************************************/
michael@0 875 /* data source manager method */
michael@0 876 /*
michael@0 877 * Terminate source --- called by jpeg_finish_decompress() after all
michael@0 878 * data has been read to clean up JPEG source manager. NOT called by
michael@0 879 * jpeg_abort() or jpeg_destroy().
michael@0 880 */
michael@0 881 METHODDEF(void)
michael@0 882 term_source (j_decompress_ptr jd)
michael@0 883 {
michael@0 884 nsJPEGDecoder *decoder = (nsJPEGDecoder *)(jd->client_data);
michael@0 885
michael@0 886 // This function shouldn't be called if we ran into an error we didn't
michael@0 887 // recover from.
michael@0 888 NS_ABORT_IF_FALSE(decoder->mState != JPEG_ERROR,
michael@0 889 "Calling term_source on a JPEG with mState == JPEG_ERROR!");
michael@0 890
michael@0 891 // Notify using a helper method to get around protectedness issues.
michael@0 892 decoder->NotifyDone();
michael@0 893 }
michael@0 894
michael@0 895 } // namespace image
michael@0 896 } // namespace mozilla
michael@0 897
michael@0 898
michael@0 899 /**************** Inverted CMYK -> RGB conversion **************/
michael@0 900 /*
michael@0 901 * Input is (Inverted) CMYK stored as 4 bytes per pixel.
michael@0 902 * Output is RGB stored as 3 bytes per pixel.
michael@0 903 * @param row Points to row buffer containing the CMYK bytes for each pixel in the row.
michael@0 904 * @param width Number of pixels in the row.
michael@0 905 */
michael@0 906 static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width)
michael@0 907 {
michael@0 908 /* Work from end to front to shrink from 4 bytes per pixel to 3 */
michael@0 909 JSAMPROW in = row + width*4;
michael@0 910 JSAMPROW out = in;
michael@0 911
michael@0 912 for (uint32_t i = width; i > 0; i--) {
michael@0 913 in -= 4;
michael@0 914 out -= 3;
michael@0 915
michael@0 916 // Source is 'Inverted CMYK', output is RGB.
michael@0 917 // See: http://www.easyrgb.com/math.php?MATH=M12#text12
michael@0 918 // Or: http://www.ilkeratalay.com/colorspacesfaq.php#rgb
michael@0 919
michael@0 920 // From CMYK to CMY
michael@0 921 // C = ( C * ( 1 - K ) + K )
michael@0 922 // M = ( M * ( 1 - K ) + K )
michael@0 923 // Y = ( Y * ( 1 - K ) + K )
michael@0 924
michael@0 925 // From Inverted CMYK to CMY is thus:
michael@0 926 // C = ( (1-iC) * (1 - (1-iK)) + (1-iK) ) => 1 - iC*iK
michael@0 927 // Same for M and Y
michael@0 928
michael@0 929 // Convert from CMY (0..1) to RGB (0..1)
michael@0 930 // R = 1 - C => 1 - (1 - iC*iK) => iC*iK
michael@0 931 // G = 1 - M => 1 - (1 - iM*iK) => iM*iK
michael@0 932 // B = 1 - Y => 1 - (1 - iY*iK) => iY*iK
michael@0 933
michael@0 934 // Convert from Inverted CMYK (0..255) to RGB (0..255)
michael@0 935 const uint32_t iC = in[0];
michael@0 936 const uint32_t iM = in[1];
michael@0 937 const uint32_t iY = in[2];
michael@0 938 const uint32_t iK = in[3];
michael@0 939 out[0] = iC*iK/255; // Red
michael@0 940 out[1] = iM*iK/255; // Green
michael@0 941 out[2] = iY*iK/255; // Blue
michael@0 942 }
michael@0 943 }

mercurial