LCOV - code coverage report
Current view: top level - image/decoders - nsJPEGDecoder.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 386 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 20 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13