LCOV - code coverage report
Current view: top level - image/decoders - nsGIFDecoder2.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 277 411 67.4 %
Date: 2017-07-14 16:53:18 Functions: 29 37 78.4 %
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             : The Graphics Interchange Format(c) is the copyright property of CompuServe
       8             : Incorporated. Only CompuServe Incorporated is authorized to define, redefine,
       9             : enhance, alter, modify or change in any way the definition of the format.
      10             : 
      11             : CompuServe Incorporated hereby grants a limited, non-exclusive, royalty-free
      12             : license for the use of the Graphics Interchange Format(sm) in computer
      13             : software; computer software utilizing GIF(sm) must acknowledge ownership of the
      14             : Graphics Interchange Format and its Service Mark by CompuServe Incorporated, in
      15             : User and Technical Documentation. Computer software utilizing GIF, which is
      16             : distributed or may be distributed without User or Technical Documentation must
      17             : display to the screen or printer a message acknowledging ownership of the
      18             : Graphics Interchange Format and the Service Mark by CompuServe Incorporated; in
      19             : this case, the acknowledgement may be displayed in an opening screen or leading
      20             : banner, or a closing screen or trailing banner. A message such as the following
      21             : may be used:
      22             : 
      23             :     "The Graphics Interchange Format(c) is the Copyright property of
      24             :     CompuServe Incorporated. GIF(sm) is a Service Mark property of
      25             :     CompuServe Incorporated."
      26             : 
      27             : For further information, please contact :
      28             : 
      29             :     CompuServe Incorporated
      30             :     Graphics Technology Department
      31             :     5000 Arlington Center Boulevard
      32             :     Columbus, Ohio  43220
      33             :     U. S. A.
      34             : 
      35             : CompuServe Incorporated maintains a mailing list with all those individuals and
      36             : organizations who wish to receive copies of this document when it is corrected
      37             : or revised. This service is offered free of charge; please provide us with your
      38             : mailing address.
      39             : */
      40             : 
      41             : #include "nsGIFDecoder2.h"
      42             : 
      43             : #include <stddef.h>
      44             : 
      45             : #include "imgFrame.h"
      46             : #include "mozilla/EndianUtils.h"
      47             : #include "nsIInputStream.h"
      48             : #include "RasterImage.h"
      49             : #include "SurfacePipeFactory.h"
      50             : 
      51             : #include "gfxColor.h"
      52             : #include "gfxPlatform.h"
      53             : #include "qcms.h"
      54             : #include <algorithm>
      55             : #include "mozilla/Telemetry.h"
      56             : 
      57             : using namespace mozilla::gfx;
      58             : 
      59             : using std::max;
      60             : 
      61             : namespace mozilla {
      62             : namespace image {
      63             : 
      64             : //////////////////////////////////////////////////////////////////////
      65             : // GIF Decoder Implementation
      66             : 
      67             : static const size_t GIF_HEADER_LEN = 6;
      68             : static const size_t GIF_SCREEN_DESCRIPTOR_LEN = 7;
      69             : static const size_t BLOCK_HEADER_LEN = 1;
      70             : static const size_t SUB_BLOCK_HEADER_LEN = 1;
      71             : static const size_t EXTENSION_HEADER_LEN = 2;
      72             : static const size_t GRAPHIC_CONTROL_EXTENSION_LEN = 4;
      73             : static const size_t APPLICATION_EXTENSION_LEN = 11;
      74             : static const size_t IMAGE_DESCRIPTOR_LEN = 9;
      75             : 
      76             : // Masks for reading color table information from packed fields in the screen
      77             : // descriptor and image descriptor blocks.
      78             : static const uint8_t PACKED_FIELDS_COLOR_TABLE_BIT = 0x80;
      79             : static const uint8_t PACKED_FIELDS_INTERLACED_BIT = 0x40;
      80             : static const uint8_t PACKED_FIELDS_TABLE_DEPTH_MASK = 0x07;
      81             : 
      82           2 : nsGIFDecoder2::nsGIFDecoder2(RasterImage* aImage)
      83             :   : Decoder(aImage)
      84           4 :   , mLexer(Transition::To(State::GIF_HEADER, GIF_HEADER_LEN),
      85             :            Transition::TerminateSuccess())
      86             :   , mOldColor(0)
      87             :   , mCurrentFrameIndex(-1)
      88             :   , mColorTablePos(0)
      89             :   , mGIFOpen(false)
      90           6 :   , mSawTransparency(false)
      91             : {
      92             :   // Clear out the structure, excluding the arrays.
      93           2 :   memset(&mGIFStruct, 0, sizeof(mGIFStruct));
      94           2 : }
      95             : 
      96           6 : nsGIFDecoder2::~nsGIFDecoder2()
      97             : {
      98           2 :   free(mGIFStruct.local_colormap);
      99           6 : }
     100             : 
     101             : nsresult
     102           4 : nsGIFDecoder2::FinishInternal()
     103             : {
     104           4 :   MOZ_ASSERT(!HasError(), "Shouldn't call FinishInternal after error!");
     105             : 
     106             :   // If the GIF got cut off, handle it anyway
     107           4 :   if (!IsMetadataDecode() && mGIFOpen) {
     108           1 :     if (mCurrentFrameIndex == mGIFStruct.images_decoded) {
     109           0 :       EndImageFrame();
     110             :     }
     111           1 :     PostDecodeDone(mGIFStruct.loop_count);
     112           1 :     mGIFOpen = false;
     113             :   }
     114             : 
     115           4 :   return NS_OK;
     116             : }
     117             : 
     118             : void
     119           1 : nsGIFDecoder2::FlushImageData()
     120             : {
     121           2 :   Maybe<SurfaceInvalidRect> invalidRect = mPipe.TakeInvalidRect();
     122           1 :   if (!invalidRect) {
     123           0 :     return;
     124             :   }
     125             : 
     126           1 :   PostInvalidation(invalidRect->mInputSpaceRect,
     127           2 :                    Some(invalidRect->mOutputSpaceRect));
     128             : }
     129             : 
     130             : //******************************************************************************
     131             : // GIF decoder callback methods. Part of public API for GIF2
     132             : //******************************************************************************
     133             : 
     134             : //******************************************************************************
     135             : void
     136           2 : nsGIFDecoder2::BeginGIF()
     137             : {
     138           2 :   if (mGIFOpen) {
     139           0 :     return;
     140             :   }
     141             : 
     142           2 :   mGIFOpen = true;
     143             : 
     144           2 :   PostSize(mGIFStruct.screen_width, mGIFStruct.screen_height);
     145             : }
     146             : 
     147             : bool
     148           2 : nsGIFDecoder2::CheckForTransparency(const IntRect& aFrameRect)
     149             : {
     150             :   // Check if the image has a transparent color in its palette.
     151           2 :   if (mGIFStruct.is_transparent) {
     152           2 :     PostHasTransparency();
     153           2 :     return true;
     154             :   }
     155             : 
     156           0 :   if (mGIFStruct.images_decoded > 0) {
     157           0 :     return false;  // We only care about first frame padding below.
     158             :   }
     159             : 
     160             :   // If we need padding on the first frame, that means we don't draw into part
     161             :   // of the image at all. Report that as transparency.
     162           0 :   IntRect imageRect(0, 0, mGIFStruct.screen_width, mGIFStruct.screen_height);
     163           0 :   if (!imageRect.IsEqualEdges(aFrameRect)) {
     164           0 :     PostHasTransparency();
     165           0 :     mSawTransparency = true;  // Make sure we don't optimize it away.
     166           0 :     return true;
     167             :   }
     168             : 
     169           0 :   return false;
     170             : }
     171             : 
     172             : //******************************************************************************
     173             : nsresult
     174           1 : nsGIFDecoder2::BeginImageFrame(const IntRect& aFrameRect,
     175             :                                uint16_t aDepth,
     176             :                                bool aIsInterlaced)
     177             : {
     178           1 :   MOZ_ASSERT(HasSize());
     179             : 
     180           1 :   bool hasTransparency = CheckForTransparency(aFrameRect);
     181             : 
     182             :   // Make sure there's no animation if we're downscaling.
     183           1 :   MOZ_ASSERT_IF(Size() != OutputSize(), !GetImageMetadata().HasAnimation());
     184             : 
     185             :   SurfacePipeFlags pipeFlags = aIsInterlaced
     186           1 :                              ? SurfacePipeFlags::DEINTERLACE
     187           1 :                              : SurfacePipeFlags();
     188             : 
     189           2 :   Maybe<SurfacePipe> pipe;
     190           1 :   if (mGIFStruct.images_decoded == 0) {
     191           1 :     gfx::SurfaceFormat format = hasTransparency ? SurfaceFormat::B8G8R8A8
     192           1 :                                                 : SurfaceFormat::B8G8R8X8;
     193             : 
     194             :     // The first frame may be displayed progressively.
     195           1 :     pipeFlags |= SurfacePipeFlags::PROGRESSIVE_DISPLAY;
     196             : 
     197             :     // The first frame is always decoded into an RGB surface.
     198             :     pipe =
     199           3 :       SurfacePipeFactory::CreateSurfacePipe(this, mGIFStruct.images_decoded,
     200           2 :                                             Size(), OutputSize(),
     201           1 :                                             aFrameRect, format, pipeFlags);
     202             :   } else {
     203             :     // This is an animation frame (and not the first). To minimize the memory
     204             :     // usage of animations, the image data is stored in paletted form.
     205             :     //
     206             :     // We should never use paletted surfaces with a draw target directly, so
     207             :     // the only practical difference between B8G8R8A8 and B8G8R8X8 is the
     208             :     // cleared pixel value if we get truncated. We want 0 in that case to
     209             :     // ensure it is an acceptable value for the color map as was the case
     210             :     // historically.
     211           0 :     MOZ_ASSERT(Size() == OutputSize());
     212             :     pipe =
     213           0 :       SurfacePipeFactory::CreatePalettedSurfacePipe(this, mGIFStruct.images_decoded,
     214           0 :                                                     Size(), aFrameRect,
     215             :                                                     SurfaceFormat::B8G8R8A8,
     216           0 :                                                     aDepth, pipeFlags);
     217             :   }
     218             : 
     219           1 :   mCurrentFrameIndex = mGIFStruct.images_decoded;
     220             : 
     221           1 :   if (!pipe) {
     222           0 :     mPipe = SurfacePipe();
     223           0 :     return NS_ERROR_FAILURE;
     224             :   }
     225             : 
     226           1 :   mPipe = Move(*pipe);
     227           1 :   return NS_OK;
     228             : }
     229             : 
     230             : 
     231             : //******************************************************************************
     232             : void
     233           1 : nsGIFDecoder2::EndImageFrame()
     234             : {
     235           1 :   Opacity opacity = Opacity::SOME_TRANSPARENCY;
     236             : 
     237           1 :   if (mGIFStruct.images_decoded == 0) {
     238             :     // We need to send invalidations for the first frame.
     239           1 :     FlushImageData();
     240             : 
     241             :     // The first frame was preallocated with alpha; if it wasn't transparent, we
     242             :     // should fix that. We can also mark it opaque unconditionally if we didn't
     243             :     // actually see any transparent pixels - this test is only valid for the
     244             :     // first frame.
     245           1 :     if (!mGIFStruct.is_transparent && !mSawTransparency) {
     246           0 :       opacity = Opacity::FULLY_OPAQUE;
     247             :     }
     248             :   }
     249             : 
     250             :   // Unconditionally increment images_decoded, because we unconditionally
     251             :   // append frames in BeginImageFrame(). This ensures that images_decoded
     252             :   // always refers to the frame in mImage we're currently decoding,
     253             :   // even if some of them weren't decoded properly and thus are blank.
     254           1 :   mGIFStruct.images_decoded++;
     255             : 
     256             :   // Tell the superclass we finished a frame
     257           4 :   PostFrameStop(opacity,
     258           1 :                 DisposalMethod(mGIFStruct.disposal_method),
     259           2 :                 FrameTimeout::FromRawMilliseconds(mGIFStruct.delay_time));
     260             : 
     261             :   // Reset the transparent pixel
     262           1 :   if (mOldColor) {
     263           1 :     mColormap[mGIFStruct.tpixel] = mOldColor;
     264           1 :     mOldColor = 0;
     265             :   }
     266             : 
     267           1 :   mCurrentFrameIndex = -1;
     268           1 : }
     269             : 
     270             : template <typename PixelSize>
     271             : PixelSize
     272          40 : nsGIFDecoder2::ColormapIndexToPixel(uint8_t aIndex)
     273             : {
     274             :   MOZ_ASSERT(sizeof(PixelSize) == sizeof(uint32_t));
     275             : 
     276             :   // Retrieve the next color, clamping to the size of the colormap.
     277          40 :   uint32_t color = mColormap[aIndex & mColorMask];
     278             : 
     279             :   // Check for transparency.
     280          40 :   if (mGIFStruct.is_transparent) {
     281          40 :     mSawTransparency = mSawTransparency || color == 0;
     282             :   }
     283             : 
     284          40 :   return color;
     285             : }
     286             : 
     287             : template <>
     288             : uint8_t
     289           0 : nsGIFDecoder2::ColormapIndexToPixel<uint8_t>(uint8_t aIndex)
     290             : {
     291           0 :   return aIndex & mColorMask;
     292             : }
     293             : 
     294             : template <typename PixelSize>
     295             : NextPixel<PixelSize>
     296          41 : nsGIFDecoder2::YieldPixel(const uint8_t* aData,
     297             :                           size_t aLength,
     298             :                           size_t* aBytesReadOut)
     299             : {
     300          41 :   MOZ_ASSERT(aData);
     301          41 :   MOZ_ASSERT(aBytesReadOut);
     302          41 :   MOZ_ASSERT(mGIFStruct.stackp >= mGIFStruct.stack);
     303             : 
     304             :   // Advance to the next byte we should read.
     305          41 :   const uint8_t* data = aData + *aBytesReadOut;
     306             : 
     307             :   // If we don't have any decoded data to yield, try to read some input and
     308             :   // produce some.
     309          41 :   if (mGIFStruct.stackp == mGIFStruct.stack) {
     310          35 :     while (mGIFStruct.bits < mGIFStruct.codesize && *aBytesReadOut < aLength) {
     311             :       // Feed the next byte into the decoder's 32-bit input buffer.
     312           9 :       mGIFStruct.datum += int32_t(*data) << mGIFStruct.bits;
     313           9 :       mGIFStruct.bits += 8;
     314           9 :       data += 1;
     315           9 :       *aBytesReadOut += 1;
     316             :     }
     317             : 
     318          17 :     if (mGIFStruct.bits < mGIFStruct.codesize) {
     319           0 :       return AsVariant(WriteState::NEED_MORE_DATA);
     320             :     }
     321             : 
     322             :     // Get the leading variable-length symbol from the data stream.
     323          17 :     int code = mGIFStruct.datum & mGIFStruct.codemask;
     324          17 :     mGIFStruct.datum >>= mGIFStruct.codesize;
     325          17 :     mGIFStruct.bits -= mGIFStruct.codesize;
     326             : 
     327          17 :     const int clearCode = ClearCode();
     328             : 
     329             :     // Reset the dictionary to its original state, if requested
     330          17 :     if (code == clearCode) {
     331           1 :       mGIFStruct.codesize = mGIFStruct.datasize + 1;
     332           1 :       mGIFStruct.codemask = (1 << mGIFStruct.codesize) - 1;
     333           1 :       mGIFStruct.avail = clearCode + 2;
     334           1 :       mGIFStruct.oldcode = -1;
     335           1 :       return AsVariant(WriteState::NEED_MORE_DATA);
     336             :     }
     337             : 
     338             :     // Check for explicit end-of-stream code. It should only appear after all
     339             :     // image data, but if that was the case we wouldn't be in this function, so
     340             :     // this is always an error condition.
     341          16 :     if (code == (clearCode + 1)) {
     342           0 :       return AsVariant(WriteState::FAILURE);
     343             :     }
     344             : 
     345          16 :     if (mGIFStruct.oldcode == -1) {
     346           1 :       if (code >= MAX_BITS) {
     347           0 :         return AsVariant(WriteState::FAILURE);  // The code's too big; something's wrong.
     348             :       }
     349             : 
     350           1 :       mGIFStruct.firstchar = mGIFStruct.oldcode = code;
     351             : 
     352             :       // Yield a pixel at the appropriate index in the colormap.
     353           1 :       mGIFStruct.pixels_remaining--;
     354           1 :       return AsVariant(ColormapIndexToPixel<PixelSize>(mGIFStruct.suffix[code]));
     355             :     }
     356             : 
     357          15 :     int incode = code;
     358          15 :     if (code >= mGIFStruct.avail) {
     359           0 :       *mGIFStruct.stackp++ = mGIFStruct.firstchar;
     360           0 :       code = mGIFStruct.oldcode;
     361             : 
     362           0 :       if (mGIFStruct.stackp >= mGIFStruct.stack + MAX_BITS) {
     363           0 :         return AsVariant(WriteState::FAILURE);  // Stack overflow; something's wrong.
     364             :       }
     365             :     }
     366             : 
     367          63 :     while (code >= clearCode) {
     368          24 :       if ((code >= MAX_BITS) || (code == mGIFStruct.prefix[code])) {
     369           0 :         return AsVariant(WriteState::FAILURE);
     370             :       }
     371             : 
     372          24 :       *mGIFStruct.stackp++ = mGIFStruct.suffix[code];
     373          24 :       code = mGIFStruct.prefix[code];
     374             : 
     375          24 :       if (mGIFStruct.stackp >= mGIFStruct.stack + MAX_BITS) {
     376           0 :         return AsVariant(WriteState::FAILURE);  // Stack overflow; something's wrong.
     377             :       }
     378             :     }
     379             : 
     380          15 :     *mGIFStruct.stackp++ = mGIFStruct.firstchar = mGIFStruct.suffix[code];
     381             : 
     382             :     // Define a new codeword in the dictionary.
     383          15 :     if (mGIFStruct.avail < 4096) {
     384          15 :       mGIFStruct.prefix[mGIFStruct.avail] = mGIFStruct.oldcode;
     385          15 :       mGIFStruct.suffix[mGIFStruct.avail] = mGIFStruct.firstchar;
     386          15 :       mGIFStruct.avail++;
     387             : 
     388             :       // If we've used up all the codewords of a given length increase the
     389             :       // length of codewords by one bit, but don't exceed the specified maximum
     390             :       // codeword size of 12 bits.
     391          17 :       if (((mGIFStruct.avail & mGIFStruct.codemask) == 0) &&
     392           2 :           (mGIFStruct.avail < 4096)) {
     393           2 :         mGIFStruct.codesize++;
     394           2 :         mGIFStruct.codemask += mGIFStruct.avail;
     395             :       }
     396             :     }
     397             : 
     398          15 :     mGIFStruct.oldcode = incode;
     399             :   }
     400             : 
     401          39 :   if (MOZ_UNLIKELY(mGIFStruct.stackp <= mGIFStruct.stack)) {
     402           0 :     MOZ_ASSERT_UNREACHABLE("No decoded data but we didn't return early?");
     403             :     return AsVariant(WriteState::FAILURE);
     404             :   }
     405             : 
     406             :   // Yield a pixel at the appropriate index in the colormap.
     407          39 :   mGIFStruct.pixels_remaining--;
     408          39 :   return AsVariant(ColormapIndexToPixel<PixelSize>(*--mGIFStruct.stackp));
     409             : }
     410             : 
     411             : /// Expand the colormap from RGB to Packed ARGB as needed by Cairo.
     412             : /// And apply any LCMS transformation.
     413             : static void
     414           2 : ConvertColormap(uint32_t* aColormap, uint32_t aColors)
     415             : {
     416             :   // Apply CMS transformation if enabled and available
     417           2 :   if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
     418           0 :     qcms_transform* transform = gfxPlatform::GetCMSRGBTransform();
     419           0 :     if (transform) {
     420           0 :       qcms_transform_data(transform, aColormap, aColormap, aColors);
     421             :     }
     422             :   }
     423             : 
     424             :   // Convert from the GIF's RGB format to the Cairo format.
     425             :   // Work from end to begin, because of the in-place expansion
     426           2 :   uint8_t* from = ((uint8_t*)aColormap) + 3 * aColors;
     427           2 :   uint32_t* to = aColormap + aColors;
     428             : 
     429             :   // Convert color entries to Cairo format
     430             : 
     431             :   // set up for loops below
     432           2 :   if (!aColors) {
     433           0 :     return;
     434             :   }
     435           2 :   uint32_t c = aColors;
     436             : 
     437             :   // copy as bytes until source pointer is 32-bit-aligned
     438             :   // NB: can't use 32-bit reads, they might read off the end of the buffer
     439           2 :   for (; (NS_PTR_TO_UINT32(from) & 0x3) && c; --c) {
     440           0 :     from -= 3;
     441           0 :     *--to = gfxPackedPixel(0xFF, from[0], from[1], from[2]);
     442             :   }
     443             : 
     444             :   // bulk copy of pixels.
     445           6 :   while (c >= 4) {
     446           2 :     from -= 12;
     447           2 :     to   -=  4;
     448           2 :     c    -=  4;
     449           2 :     GFX_BLOCK_RGB_TO_FRGB(from,to);
     450             :   }
     451             : 
     452             :   // copy remaining pixel(s)
     453             :   // NB: can't use 32-bit reads, they might read off the end of the buffer
     454           2 :   while (c--) {
     455           0 :     from -= 3;
     456           0 :     *--to = gfxPackedPixel(0xFF, from[0], from[1], from[2]);
     457             :   }
     458             : }
     459             : 
     460             : LexerResult
     461           2 : nsGIFDecoder2::DoDecode(SourceBufferIterator& aIterator, IResumable* aOnResume)
     462             : {
     463           2 :   MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
     464             : 
     465             :   return mLexer.Lex(aIterator, aOnResume,
     466          26 :                     [=](State aState, const char* aData, size_t aLength) {
     467          26 :     switch(aState) {
     468             :       case State::GIF_HEADER:
     469          23 :         return ReadGIFHeader(aData);
     470             :       case State::SCREEN_DESCRIPTOR:
     471           2 :         return ReadScreenDescriptor(aData);
     472             :       case State::GLOBAL_COLOR_TABLE:
     473           2 :         return ReadGlobalColorTable(aData, aLength);
     474             :       case State::FINISHED_GLOBAL_COLOR_TABLE:
     475           2 :         return FinishedGlobalColorTable();
     476             :       case State::BLOCK_HEADER:
     477           5 :         return ReadBlockHeader(aData);
     478             :       case State::EXTENSION_HEADER:
     479           2 :         return ReadExtensionHeader(aData);
     480             :       case State::GRAPHIC_CONTROL_EXTENSION:
     481           2 :         return ReadGraphicControlExtension(aData);
     482             :       case State::APPLICATION_IDENTIFIER:
     483           0 :         return ReadApplicationIdentifier(aData);
     484             :       case State::NETSCAPE_EXTENSION_SUB_BLOCK:
     485           0 :         return ReadNetscapeExtensionSubBlock(aData);
     486             :       case State::NETSCAPE_EXTENSION_DATA:
     487           0 :         return ReadNetscapeExtensionData(aData);
     488             :       case State::IMAGE_DESCRIPTOR:
     489           2 :         return ReadImageDescriptor(aData);
     490             :       case State::FINISH_IMAGE_DESCRIPTOR:
     491           0 :         return FinishImageDescriptor(aData);
     492             :       case State::LOCAL_COLOR_TABLE:
     493           0 :         return ReadLocalColorTable(aData, aLength);
     494             :       case State::FINISHED_LOCAL_COLOR_TABLE:
     495           0 :         return FinishedLocalColorTable();
     496             :       case State::IMAGE_DATA_BLOCK:
     497           1 :         return ReadImageDataBlock(aData);
     498             :       case State::IMAGE_DATA_SUB_BLOCK:
     499           2 :         return ReadImageDataSubBlock(aData);
     500             :       case State::LZW_DATA:
     501           1 :         return ReadLZWData(aData, aLength);
     502             :       case State::SKIP_LZW_DATA:
     503           0 :         return Transition::ContinueUnbuffered(State::SKIP_LZW_DATA);
     504             :       case State::FINISHED_LZW_DATA:
     505           1 :         return Transition::To(State::IMAGE_DATA_SUB_BLOCK, SUB_BLOCK_HEADER_LEN);
     506             :       case State::SKIP_SUB_BLOCKS:
     507           2 :         return SkipSubBlocks(aData);
     508             :       case State::SKIP_DATA_THEN_SKIP_SUB_BLOCKS:
     509           0 :         return Transition::ContinueUnbuffered(State::SKIP_DATA_THEN_SKIP_SUB_BLOCKS);
     510             :       case State::FINISHED_SKIPPING_DATA:
     511           0 :         return Transition::To(State::SKIP_SUB_BLOCKS, SUB_BLOCK_HEADER_LEN);
     512             :       default:
     513           0 :         MOZ_CRASH("Unknown State");
     514             :     }
     515           2 :   });
     516             : }
     517             : 
     518             : LexerTransition<nsGIFDecoder2::State>
     519           2 : nsGIFDecoder2::ReadGIFHeader(const char* aData)
     520             : {
     521             :   // We retrieve the version here but because many GIF encoders set header
     522             :   // fields incorrectly, we barely use it; features which should only appear in
     523             :   // GIF89a are always accepted.
     524           2 :   if (strncmp(aData, "GIF87a", GIF_HEADER_LEN) == 0) {
     525           0 :     mGIFStruct.version = 87;
     526           2 :   } else if (strncmp(aData, "GIF89a", GIF_HEADER_LEN) == 0) {
     527           2 :     mGIFStruct.version = 89;
     528             :   } else {
     529           0 :     return Transition::TerminateFailure();
     530             :   }
     531             : 
     532           2 :   return Transition::To(State::SCREEN_DESCRIPTOR, GIF_SCREEN_DESCRIPTOR_LEN);
     533             : }
     534             : 
     535             : LexerTransition<nsGIFDecoder2::State>
     536           2 : nsGIFDecoder2::ReadScreenDescriptor(const char* aData)
     537             : {
     538           2 :   mGIFStruct.screen_width  = LittleEndian::readUint16(aData + 0);
     539           2 :   mGIFStruct.screen_height = LittleEndian::readUint16(aData + 2);
     540             : 
     541           2 :   const uint8_t packedFields = aData[4];
     542             : 
     543             :   // XXX: Should we be capturing these values even if there is no global color
     544             :   // table?
     545           2 :   mGIFStruct.global_colormap_depth =
     546           2 :     (packedFields & PACKED_FIELDS_TABLE_DEPTH_MASK) + 1;
     547           2 :   mGIFStruct.global_colormap_count = 1 << mGIFStruct.global_colormap_depth;
     548             : 
     549             :   // We ignore several fields in the header. We don't care about the 'sort
     550             :   // flag', which indicates if the global color table's entries are sorted in
     551             :   // order of importance - if we need to render this image for a device with a
     552             :   // narrower color gamut than GIF supports we'll handle that at a different
     553             :   // layer. We have no use for the pixel aspect ratio as well. Finally, we
     554             :   // intentionally ignore the background color index, as implementing that
     555             :   // feature would not be web compatible - when a GIF image frame doesn't cover
     556             :   // the entire area of the image, the area that's not covered should always be
     557             :   // transparent.
     558             : 
     559           2 :   if (packedFields & PACKED_FIELDS_COLOR_TABLE_BIT) {
     560           2 :     MOZ_ASSERT(mColorTablePos == 0);
     561             : 
     562             :     // We read the global color table in unbuffered mode since it can be quite
     563             :     // large and it'd be preferable to avoid unnecessary copies.
     564           2 :     const size_t globalColorTableSize = 3 * mGIFStruct.global_colormap_count;
     565             :     return Transition::ToUnbuffered(State::FINISHED_GLOBAL_COLOR_TABLE,
     566             :                                     State::GLOBAL_COLOR_TABLE,
     567           2 :                                     globalColorTableSize);
     568             :   }
     569             : 
     570           0 :   return Transition::To(State::BLOCK_HEADER, BLOCK_HEADER_LEN);
     571             : }
     572             : 
     573             : LexerTransition<nsGIFDecoder2::State>
     574           2 : nsGIFDecoder2::ReadGlobalColorTable(const char* aData, size_t aLength)
     575             : {
     576           2 :   uint8_t* dest = reinterpret_cast<uint8_t*>(mGIFStruct.global_colormap)
     577           2 :                 + mColorTablePos;
     578           2 :   memcpy(dest, aData, aLength);
     579           2 :   mColorTablePos += aLength;
     580           2 :   return Transition::ContinueUnbuffered(State::GLOBAL_COLOR_TABLE);
     581             : }
     582             : 
     583             : LexerTransition<nsGIFDecoder2::State>
     584           2 : nsGIFDecoder2::FinishedGlobalColorTable()
     585             : {
     586           2 :   ConvertColormap(mGIFStruct.global_colormap, mGIFStruct.global_colormap_count);
     587           2 :   mColorTablePos = 0;
     588           2 :   return Transition::To(State::BLOCK_HEADER, BLOCK_HEADER_LEN);
     589             : }
     590             : 
     591             : LexerTransition<nsGIFDecoder2::State>
     592           5 : nsGIFDecoder2::ReadBlockHeader(const char* aData)
     593             : {
     594             :   // Determine what type of block we're dealing with.
     595           5 :   switch (aData[0]) {
     596             :     case GIF_EXTENSION_INTRODUCER:
     597           2 :       return Transition::To(State::EXTENSION_HEADER, EXTENSION_HEADER_LEN);
     598             : 
     599             :     case GIF_IMAGE_SEPARATOR:
     600           2 :       return Transition::To(State::IMAGE_DESCRIPTOR, IMAGE_DESCRIPTOR_LEN);
     601             : 
     602             :     case GIF_TRAILER:
     603           1 :       FinishInternal();
     604           1 :       return Transition::TerminateSuccess();
     605             : 
     606             :     default:
     607             :       // If we get anything other than GIF_IMAGE_SEPARATOR,
     608             :       // GIF_EXTENSION_INTRODUCER, or GIF_TRAILER, there is extraneous data
     609             :       // between blocks. The GIF87a spec tells us to keep reading until we find
     610             :       // an image separator, but GIF89a says such a file is corrupt. We follow
     611             :       // GIF89a and bail out.
     612             : 
     613           0 :       if (mGIFStruct.images_decoded > 0) {
     614             :         // The file is corrupt, but we successfully decoded some frames, so we
     615             :         // may as well consider the decode successful and display them.
     616           0 :         FinishInternal();
     617           0 :         return Transition::TerminateSuccess();
     618             :       }
     619             : 
     620             :       // No images decoded; there is nothing to display.
     621           0 :       return Transition::TerminateFailure();
     622             :   }
     623             : }
     624             : 
     625             : LexerTransition<nsGIFDecoder2::State>
     626           2 : nsGIFDecoder2::ReadExtensionHeader(const char* aData)
     627             : {
     628           2 :   const uint8_t label = aData[0];
     629           2 :   const uint8_t extensionHeaderLength = aData[1];
     630             : 
     631             :   // If the extension header is zero length, just treat it as a block terminator
     632             :   // and move on to the next block immediately.
     633           2 :   if (extensionHeaderLength == 0) {
     634           0 :     return Transition::To(State::BLOCK_HEADER, BLOCK_HEADER_LEN);
     635             :   }
     636             : 
     637           2 :   switch (label) {
     638             :     case GIF_GRAPHIC_CONTROL_LABEL:
     639             :       // The GIF spec mandates that the Control Extension header block length is
     640             :       // 4 bytes, and the parser for this block reads 4 bytes, so we must
     641             :       // enforce that the buffer contains at least this many bytes. If the GIF
     642             :       // specifies a different length, we allow that, so long as it's larger;
     643             :       // the additional data will simply be ignored.
     644             :       return Transition::To(State::GRAPHIC_CONTROL_EXTENSION,
     645           2 :                             max<uint8_t>(extensionHeaderLength,
     646           4 :                                          GRAPHIC_CONTROL_EXTENSION_LEN));
     647             : 
     648             :     case GIF_APPLICATION_EXTENSION_LABEL:
     649             :       // Again, the spec specifies that an application extension header is 11
     650             :       // bytes, but for compatibility with GIFs in the wild, we allow deviation
     651             :       // from the spec. This is important for real-world compatibility, as GIFs
     652             :       // in the wild exist with application extension headers that are both
     653             :       // shorter and longer than 11 bytes. However, we only try to actually
     654             :       // interpret the application extension if the length is correct;
     655             :       // otherwise, we just skip the block unconditionally.
     656           0 :       return extensionHeaderLength == APPLICATION_EXTENSION_LEN
     657             :            ? Transition::To(State::APPLICATION_IDENTIFIER, extensionHeaderLength)
     658             :            : Transition::ToUnbuffered(State::FINISHED_SKIPPING_DATA,
     659             :                                       State::SKIP_DATA_THEN_SKIP_SUB_BLOCKS,
     660           0 :                                       extensionHeaderLength);
     661             : 
     662             :     default:
     663             :       // Skip over any other type of extension block, including comment and
     664             :       // plain text blocks.
     665             :       return Transition::ToUnbuffered(State::FINISHED_SKIPPING_DATA,
     666             :                                       State::SKIP_DATA_THEN_SKIP_SUB_BLOCKS,
     667           0 :                                       extensionHeaderLength);
     668             :   }
     669             : }
     670             : 
     671             : LexerTransition<nsGIFDecoder2::State>
     672           2 : nsGIFDecoder2::ReadGraphicControlExtension(const char* aData)
     673             : {
     674           2 :   mGIFStruct.is_transparent = aData[0] & 0x1;
     675           2 :   mGIFStruct.tpixel = uint8_t(aData[3]);
     676           2 :   mGIFStruct.disposal_method = (aData[0] >> 2) & 0x7;
     677             : 
     678           2 :   if (mGIFStruct.disposal_method == 4) {
     679             :     // Some encoders (and apparently some specs) represent
     680             :     // DisposalMethod::RESTORE_PREVIOUS as 4, but 3 is used in the canonical
     681             :     // spec and is more popular, so we normalize to 3.
     682           0 :     mGIFStruct.disposal_method = 3;
     683           2 :   } else if (mGIFStruct.disposal_method > 4) {
     684             :     // This GIF is using a disposal method which is undefined in the spec.
     685             :     // Treat it as DisposalMethod::NOT_SPECIFIED.
     686           0 :     mGIFStruct.disposal_method = 0;
     687             :   }
     688             : 
     689           2 :   DisposalMethod method = DisposalMethod(mGIFStruct.disposal_method);
     690           2 :   if (method == DisposalMethod::CLEAR_ALL || method == DisposalMethod::CLEAR) {
     691             :     // We may have to display the background under this image during animation
     692             :     // playback, so we regard it as transparent.
     693           0 :     PostHasTransparency();
     694             :   }
     695             : 
     696           2 :   mGIFStruct.delay_time = LittleEndian::readUint16(aData + 1) * 10;
     697           2 :   if (mGIFStruct.delay_time > 0) {
     698           0 :     PostIsAnimated(FrameTimeout::FromRawMilliseconds(mGIFStruct.delay_time));
     699             :   }
     700             : 
     701           2 :   return Transition::To(State::SKIP_SUB_BLOCKS, SUB_BLOCK_HEADER_LEN);
     702             : }
     703             : 
     704             : LexerTransition<nsGIFDecoder2::State>
     705           0 : nsGIFDecoder2::ReadApplicationIdentifier(const char* aData)
     706             : {
     707           0 :   if ((strncmp(aData, "NETSCAPE2.0", 11) == 0) ||
     708           0 :       (strncmp(aData, "ANIMEXTS1.0", 11) == 0)) {
     709             :     // This is a Netscape application extension block.
     710             :     return Transition::To(State::NETSCAPE_EXTENSION_SUB_BLOCK,
     711           0 :                           SUB_BLOCK_HEADER_LEN);
     712             :   }
     713             : 
     714             :   // This is an application extension we don't care about. Just skip it.
     715           0 :   return Transition::To(State::SKIP_SUB_BLOCKS, SUB_BLOCK_HEADER_LEN);
     716             : }
     717             : 
     718             : LexerTransition<nsGIFDecoder2::State>
     719           0 : nsGIFDecoder2::ReadNetscapeExtensionSubBlock(const char* aData)
     720             : {
     721           0 :   const uint8_t blockLength = aData[0];
     722           0 :   if (blockLength == 0) {
     723             :     // We hit the block terminator.
     724           0 :     return Transition::To(State::BLOCK_HEADER, BLOCK_HEADER_LEN);
     725             :   }
     726             : 
     727             :   // We consume a minimum of 3 bytes in accordance with the specs for the
     728             :   // Netscape application extension block, such as they are.
     729           0 :   const size_t extensionLength = max<uint8_t>(blockLength, 3);
     730           0 :   return Transition::To(State::NETSCAPE_EXTENSION_DATA, extensionLength);
     731             : }
     732             : 
     733             : LexerTransition<nsGIFDecoder2::State>
     734           0 : nsGIFDecoder2::ReadNetscapeExtensionData(const char* aData)
     735             : {
     736             :   // Documentation for NETSCAPE2.0 / ANIMEXTS1.0 extensions can be found at:
     737             :   //   https://wiki.whatwg.org/wiki/GIF
     738             :   static const uint8_t NETSCAPE_LOOPING_EXTENSION_SUB_BLOCK_ID = 1;
     739             :   static const uint8_t NETSCAPE_BUFFERING_EXTENSION_SUB_BLOCK_ID = 2;
     740             : 
     741           0 :   const uint8_t subBlockID = aData[0] & 7;
     742           0 :   switch (subBlockID) {
     743             :     case NETSCAPE_LOOPING_EXTENSION_SUB_BLOCK_ID:
     744             :       // This is looping extension.
     745           0 :       mGIFStruct.loop_count = LittleEndian::readUint16(aData + 1);
     746             :       // Zero loop count is infinite animation loop request.
     747           0 :       if (mGIFStruct.loop_count == 0) {
     748           0 :         mGIFStruct.loop_count = -1;
     749             :       }
     750             : 
     751             :       return Transition::To(State::NETSCAPE_EXTENSION_SUB_BLOCK,
     752           0 :                             SUB_BLOCK_HEADER_LEN);
     753             : 
     754             :     case NETSCAPE_BUFFERING_EXTENSION_SUB_BLOCK_ID:
     755             :       // We allow, but ignore, this extension.
     756             :       return Transition::To(State::NETSCAPE_EXTENSION_SUB_BLOCK,
     757           0 :                             SUB_BLOCK_HEADER_LEN);
     758             : 
     759             :     default:
     760           0 :       return Transition::TerminateFailure();
     761             :   }
     762             : }
     763             : 
     764             : LexerTransition<nsGIFDecoder2::State>
     765           2 : nsGIFDecoder2::ReadImageDescriptor(const char* aData)
     766             : {
     767             :   // On the first frame, we don't need to yield, and none of the other checks
     768             :   // below apply, so we can just jump right into FinishImageDescriptor().
     769           2 :   if (mGIFStruct.images_decoded == 0) {
     770           2 :     return FinishImageDescriptor(aData);
     771             :   }
     772             : 
     773           0 :   if (!HasAnimation()) {
     774             :     // We should've already called PostIsAnimated(); this must be a corrupt
     775             :     // animated image with a first frame timeout of zero. Signal that we're
     776             :     // animated now, before the first-frame decode early exit below, so that
     777             :     // RasterImage can detect that this happened.
     778           0 :     PostIsAnimated(FrameTimeout::FromRawMilliseconds(0));
     779             :   }
     780             : 
     781           0 :   if (IsFirstFrameDecode()) {
     782             :     // We're about to get a second frame, but we only want the first. Stop
     783             :     // decoding now.
     784           0 :     FinishInternal();
     785           0 :     return Transition::TerminateSuccess();
     786             :   }
     787             : 
     788           0 :   MOZ_ASSERT(Size() == OutputSize(), "Downscaling an animated image?");
     789             : 
     790             :   // Yield to allow access to the previous frame before we start a new one.
     791           0 :   return Transition::ToAfterYield(State::FINISH_IMAGE_DESCRIPTOR);
     792             : }
     793             : 
     794             : LexerTransition<nsGIFDecoder2::State>
     795           2 : nsGIFDecoder2::FinishImageDescriptor(const char* aData)
     796             : {
     797           2 :   IntRect frameRect;
     798             : 
     799             :   // Get image offsets with respect to the screen origin.
     800           2 :   frameRect.x = LittleEndian::readUint16(aData + 0);
     801           2 :   frameRect.y = LittleEndian::readUint16(aData + 2);
     802           2 :   frameRect.width = LittleEndian::readUint16(aData + 4);
     803           2 :   frameRect.height = LittleEndian::readUint16(aData + 6);
     804             : 
     805           2 :   if (!mGIFStruct.images_decoded) {
     806             :     // Work around GIF files where
     807             :     //   * at least one of the logical screen dimensions is smaller than the
     808             :     //     same dimension in the first image, or
     809             :     //   * GIF87a files where the first image's dimensions do not match the
     810             :     //     logical screen dimensions.
     811           4 :     if (mGIFStruct.screen_height < frameRect.height ||
     812           4 :         mGIFStruct.screen_width < frameRect.width ||
     813           2 :         mGIFStruct.version == 87) {
     814           0 :       mGIFStruct.screen_height = frameRect.height;
     815           0 :       mGIFStruct.screen_width = frameRect.width;
     816           0 :       frameRect.MoveTo(0, 0);
     817             :     }
     818             : 
     819             :     // Create the image container with the right size.
     820           2 :     BeginGIF();
     821           2 :     if (HasError()) {
     822             :       // Setting the size led to an error.
     823           0 :       return Transition::TerminateFailure();
     824             :     }
     825             : 
     826             :     // If we're doing a metadata decode, we're done.
     827           2 :     if (IsMetadataDecode()) {
     828           1 :       CheckForTransparency(frameRect);
     829           1 :       FinishInternal();
     830           1 :       return Transition::TerminateSuccess();
     831             :     }
     832             :   }
     833             : 
     834             :   // Work around broken GIF files that have zero frame width or height; in this
     835             :   // case, we'll treat the frame as having the same size as the overall image.
     836           1 :   if (frameRect.height == 0 || frameRect.width == 0) {
     837           0 :     frameRect.height = mGIFStruct.screen_height;
     838           0 :     frameRect.width = mGIFStruct.screen_width;
     839             : 
     840             :     // If that still resulted in zero frame width or height, give up.
     841           0 :     if (frameRect.height == 0 || frameRect.width == 0) {
     842           0 :       return Transition::TerminateFailure();
     843             :     }
     844             :   }
     845             : 
     846             :   // Determine |depth| (log base 2 of the number of colors in the palette).
     847           1 :   bool haveLocalColorTable = false;
     848           1 :   uint16_t depth = 0;
     849           1 :   uint8_t packedFields = aData[8];
     850             : 
     851           1 :   if (packedFields & PACKED_FIELDS_COLOR_TABLE_BIT) {
     852             :     // Get the palette depth from the local color table.
     853           0 :     depth = (packedFields & PACKED_FIELDS_TABLE_DEPTH_MASK) + 1;
     854           0 :     haveLocalColorTable = true;
     855             :   } else {
     856             :     // Get the palette depth from the global color table.
     857           1 :     depth = mGIFStruct.global_colormap_depth;
     858             :   }
     859             : 
     860             :   // If the transparent color index is greater than the number of colors in the
     861             :   // color table, we may need a higher color depth than |depth| would specify.
     862             :   // Our internal representation of the image will instead use |realDepth|,
     863             :   // which is the smallest color depth that can accomodate the existing palette
     864             :   // *and* the transparent color index.
     865           1 :   uint16_t realDepth = depth;
     866           1 :   while (mGIFStruct.tpixel >= (1 << realDepth) &&
     867           0 :          realDepth < 8) {
     868           0 :     realDepth++;
     869             :   }
     870             : 
     871             :   // Create a mask used to ensure that color values fit within the colormap.
     872           1 :   mColorMask = 0xFF >> (8 - realDepth);
     873             : 
     874             :   // Determine if this frame is interlaced or not.
     875           1 :   const bool isInterlaced = packedFields & PACKED_FIELDS_INTERLACED_BIT;
     876             : 
     877             :   // Create the SurfacePipe we'll use to write output for this frame.
     878           1 :   if (NS_FAILED(BeginImageFrame(frameRect, realDepth, isInterlaced))) {
     879           0 :     return Transition::TerminateFailure();
     880             :   }
     881             : 
     882             :   // Clear state from last image.
     883           1 :   mGIFStruct.pixels_remaining = frameRect.width * frameRect.height;
     884             : 
     885           1 :   if (haveLocalColorTable) {
     886             :     // We have a local color table, so prepare to read it into the palette of
     887             :     // the current frame.
     888           0 :     mGIFStruct.local_colormap_size = 1 << depth;
     889             : 
     890           0 :     if (mGIFStruct.images_decoded == 0) {
     891             :       // The first frame has a local color table. Allocate space for it as we
     892             :       // use a BGRA or BGRX surface for the first frame; such surfaces don't
     893             :       // have their own palettes internally.
     894           0 :       mColormapSize = sizeof(uint32_t) << realDepth;
     895           0 :       if (!mGIFStruct.local_colormap) {
     896           0 :         mGIFStruct.local_colormap =
     897           0 :           static_cast<uint32_t*>(moz_xmalloc(mColormapSize));
     898             :       }
     899           0 :       mColormap = mGIFStruct.local_colormap;
     900             :     }
     901             : 
     902           0 :     const size_t size = 3 << depth;
     903           0 :     if (mColormapSize > size) {
     904             :       // Clear the part of the colormap which will be unused with this palette.
     905             :       // If a GIF references an invalid palette entry, ensure the entry is opaque white.
     906             :       // This is needed for Skia as if it isn't, RGBX surfaces will cause blending issues
     907             :       // with Skia.
     908           0 :       memset(reinterpret_cast<uint8_t*>(mColormap) + size, 0xFF,
     909           0 :              mColormapSize - size);
     910             :     }
     911             : 
     912           0 :     MOZ_ASSERT(mColorTablePos == 0);
     913             : 
     914             :     // We read the local color table in unbuffered mode since it can be quite
     915             :     // large and it'd be preferable to avoid unnecessary copies.
     916             :     return Transition::ToUnbuffered(State::FINISHED_LOCAL_COLOR_TABLE,
     917             :                                     State::LOCAL_COLOR_TABLE,
     918           0 :                                     size);
     919             :   }
     920             : 
     921             :   // There's no local color table; copy the global color table into the palette
     922             :   // of the current frame.
     923           1 :   if (mGIFStruct.images_decoded > 0) {
     924           0 :     memcpy(mColormap, mGIFStruct.global_colormap, mColormapSize);
     925             :   } else {
     926           1 :     mColormap = mGIFStruct.global_colormap;
     927             :   }
     928             : 
     929           1 :   return Transition::To(State::IMAGE_DATA_BLOCK, BLOCK_HEADER_LEN);
     930             : }
     931             : 
     932             : LexerTransition<nsGIFDecoder2::State>
     933           0 : nsGIFDecoder2::ReadLocalColorTable(const char* aData, size_t aLength)
     934             : {
     935           0 :   uint8_t* dest = reinterpret_cast<uint8_t*>(mColormap) + mColorTablePos;
     936           0 :   memcpy(dest, aData, aLength);
     937           0 :   mColorTablePos += aLength;
     938           0 :   return Transition::ContinueUnbuffered(State::LOCAL_COLOR_TABLE);
     939             : }
     940             : 
     941             : LexerTransition<nsGIFDecoder2::State>
     942           0 : nsGIFDecoder2::FinishedLocalColorTable()
     943             : {
     944           0 :   ConvertColormap(mColormap, mGIFStruct.local_colormap_size);
     945           0 :   mColorTablePos = 0;
     946           0 :   return Transition::To(State::IMAGE_DATA_BLOCK, BLOCK_HEADER_LEN);
     947             : }
     948             : 
     949             : LexerTransition<nsGIFDecoder2::State>
     950           1 : nsGIFDecoder2::ReadImageDataBlock(const char* aData)
     951             : {
     952             :   // Make sure the transparent pixel is transparent in the colormap.
     953           1 :   if (mGIFStruct.is_transparent) {
     954             :     // Save the old value so we can restore it later.
     955           1 :     if (mColormap == mGIFStruct.global_colormap) {
     956           1 :         mOldColor = mColormap[mGIFStruct.tpixel];
     957             :     }
     958           1 :     mColormap[mGIFStruct.tpixel] = 0;
     959             :   }
     960             : 
     961             :   // Initialize the LZW decoder.
     962           1 :   mGIFStruct.datasize = uint8_t(aData[0]);
     963           1 :   const int clearCode = ClearCode();
     964           1 :   if (mGIFStruct.datasize > MAX_LZW_BITS || clearCode >= MAX_BITS) {
     965           0 :     return Transition::TerminateFailure();
     966             :   }
     967             : 
     968           1 :   mGIFStruct.avail = clearCode + 2;
     969           1 :   mGIFStruct.oldcode = -1;
     970           1 :   mGIFStruct.codesize = mGIFStruct.datasize + 1;
     971           1 :   mGIFStruct.codemask = (1 << mGIFStruct.codesize) - 1;
     972           1 :   mGIFStruct.datum = mGIFStruct.bits = 0;
     973             : 
     974             :   // Initialize the tables.
     975           5 :   for (int i = 0; i < clearCode; i++) {
     976           4 :     mGIFStruct.suffix[i] = i;
     977             :   }
     978             : 
     979           1 :   mGIFStruct.stackp = mGIFStruct.stack;
     980             : 
     981             :   // Begin reading image data sub-blocks.
     982           1 :   return Transition::To(State::IMAGE_DATA_SUB_BLOCK, SUB_BLOCK_HEADER_LEN);
     983             : }
     984             : 
     985             : LexerTransition<nsGIFDecoder2::State>
     986           2 : nsGIFDecoder2::ReadImageDataSubBlock(const char* aData)
     987             : {
     988           2 :   const uint8_t subBlockLength = aData[0];
     989           2 :   if (subBlockLength == 0) {
     990             :     // We hit the block terminator.
     991           1 :     EndImageFrame();
     992           1 :     return Transition::To(State::BLOCK_HEADER, BLOCK_HEADER_LEN);
     993             :   }
     994             : 
     995           1 :   if (mGIFStruct.pixels_remaining == 0) {
     996             :     // We've already written to the entire image; we should've hit the block
     997             :     // terminator at this point. This image is corrupt, but we'll tolerate it.
     998             : 
     999           0 :     if (subBlockLength == GIF_TRAILER) {
    1000             :       // This GIF is missing the block terminator for the final block; we'll put
    1001             :       // up with it.
    1002           0 :       FinishInternal();
    1003           0 :       return Transition::TerminateSuccess();
    1004             :     }
    1005             : 
    1006             :     // We're not at the end of the image, so just skip the extra data.
    1007             :     return Transition::ToUnbuffered(State::FINISHED_LZW_DATA,
    1008             :                                     State::SKIP_LZW_DATA,
    1009           0 :                                     subBlockLength);
    1010             :   }
    1011             : 
    1012             :   // Handle the standard case: there's data in the sub-block and pixels left to
    1013             :   // fill in the image. We read the sub-block unbuffered so we can get pixels on
    1014             :   // the screen as soon as possible.
    1015             :   return Transition::ToUnbuffered(State::FINISHED_LZW_DATA,
    1016             :                                   State::LZW_DATA,
    1017           1 :                                   subBlockLength);
    1018             : }
    1019             : 
    1020             : LexerTransition<nsGIFDecoder2::State>
    1021           1 : nsGIFDecoder2::ReadLZWData(const char* aData, size_t aLength)
    1022             : {
    1023           1 :   const uint8_t* data = reinterpret_cast<const uint8_t*>(aData);
    1024           1 :   size_t length = aLength;
    1025             : 
    1026           7 :   while (mGIFStruct.pixels_remaining > 0 &&
    1027           2 :          (length > 0 || mGIFStruct.bits >= mGIFStruct.codesize)) {
    1028           2 :     size_t bytesRead = 0;
    1029             : 
    1030           2 :     auto result = mGIFStruct.images_decoded == 0
    1031          43 :       ? mPipe.WritePixels<uint32_t>([&]{ return YieldPixel<uint32_t>(data, length, &bytesRead); })
    1032           2 :       : mPipe.WritePixels<uint8_t>([&]{ return YieldPixel<uint8_t>(data, length, &bytesRead); });
    1033             : 
    1034           2 :     if (MOZ_UNLIKELY(bytesRead > length)) {
    1035           0 :       MOZ_ASSERT_UNREACHABLE("Overread?");
    1036             :       bytesRead = length;
    1037             :     }
    1038             : 
    1039             :     // Advance our position in the input based upon what YieldPixel() consumed.
    1040           2 :     data += bytesRead;
    1041           2 :     length -= bytesRead;
    1042             : 
    1043           2 :     switch (result) {
    1044             :       case WriteState::NEED_MORE_DATA:
    1045           1 :         continue;
    1046             : 
    1047             :       case WriteState::FINISHED:
    1048           1 :         NS_WARNING_ASSERTION(mGIFStruct.pixels_remaining <= 0,
    1049             :                              "too many pixels");
    1050           1 :         mGIFStruct.pixels_remaining = 0;
    1051           1 :         break;
    1052             : 
    1053             :       case WriteState::FAILURE:
    1054           0 :         return Transition::TerminateFailure();
    1055             :     }
    1056             :   }
    1057             : 
    1058             :   // We're done, but keep going until we consume all the data in the sub-block.
    1059           1 :   return Transition::ContinueUnbuffered(State::LZW_DATA);
    1060             : }
    1061             : 
    1062             : LexerTransition<nsGIFDecoder2::State>
    1063           2 : nsGIFDecoder2::SkipSubBlocks(const char* aData)
    1064             : {
    1065             :   // In the SKIP_SUB_BLOCKS state we skip over data sub-blocks that we're not
    1066             :   // interested in. Blocks consist of a block header (which can be up to 255
    1067             :   // bytes in length) and a series of data sub-blocks. Each data sub-block
    1068             :   // consists of a single byte length value, followed by the data itself. A data
    1069             :   // sub-block with a length of zero terminates the overall block.
    1070             :   // SKIP_SUB_BLOCKS reads a sub-block length value. If it's zero, we've arrived
    1071             :   // at the next block. Otherwise, we enter the SKIP_DATA_THEN_SKIP_SUB_BLOCKS
    1072             :   // state to skip over the sub-block data and return to SKIP_SUB_BLOCKS at the
    1073             :   // start of the next sub-block.
    1074             : 
    1075           2 :   const uint8_t nextSubBlockLength = aData[0];
    1076           2 :   if (nextSubBlockLength == 0) {
    1077             :     // We hit the block terminator, so the sequence of data sub-blocks is over;
    1078             :     // begin processing another block.
    1079           2 :     return Transition::To(State::BLOCK_HEADER, BLOCK_HEADER_LEN);
    1080             :   }
    1081             : 
    1082             :   // Skip to the next sub-block length value.
    1083             :   return Transition::ToUnbuffered(State::FINISHED_SKIPPING_DATA,
    1084             :                                   State::SKIP_DATA_THEN_SKIP_SUB_BLOCKS,
    1085           0 :                                   nextSubBlockLength);
    1086             : }
    1087             : 
    1088             : Maybe<Telemetry::HistogramID>
    1089           2 : nsGIFDecoder2::SpeedHistogram() const
    1090             : {
    1091           2 :   return Some(Telemetry::IMAGE_DECODE_SPEED_GIF);
    1092             : }
    1093             : 
    1094             : } // namespace image
    1095             : } // namespace mozilla

Generated by: LCOV version 1.13