LCOV - code coverage report
Current view: directory - image/decoders - nsGIFDecoder2.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 462 311 67.3 %
Date: 2012-06-02 Functions: 14 13 92.9 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2                 :  *
       3                 :  * ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is mozilla.org code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * Netscape Communications Corporation.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 2001
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Chris Saari <saari@netscape.com>
      25                 :  *   Bobby Holley <bobbyholley@gmail.com>
      26                 :  *
      27                 :  * Alternatively, the contents of this file may be used under the terms of
      28                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      29                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      31                 :  * of those above. If you wish to allow use of your version of this file only
      32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      33                 :  * use your version of this file under the terms of the MPL, indicate your
      34                 :  * decision by deleting the provisions above and replace them with the notice
      35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      36                 :  * the provisions above, a recipient may use your version of this file under
      37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      38                 :  *
      39                 :  * ***** END LICENSE BLOCK ***** */
      40                 : /*
      41                 : The Graphics Interchange Format(c) is the copyright property of CompuServe
      42                 : Incorporated. Only CompuServe Incorporated is authorized to define, redefine,
      43                 : enhance, alter, modify or change in any way the definition of the format.
      44                 : 
      45                 : CompuServe Incorporated hereby grants a limited, non-exclusive, royalty-free
      46                 : license for the use of the Graphics Interchange Format(sm) in computer
      47                 : software; computer software utilizing GIF(sm) must acknowledge ownership of the
      48                 : Graphics Interchange Format and its Service Mark by CompuServe Incorporated, in
      49                 : User and Technical Documentation. Computer software utilizing GIF, which is
      50                 : distributed or may be distributed without User or Technical Documentation must
      51                 : display to the screen or printer a message acknowledging ownership of the
      52                 : Graphics Interchange Format and the Service Mark by CompuServe Incorporated; in
      53                 : this case, the acknowledgement may be displayed in an opening screen or leading
      54                 : banner, or a closing screen or trailing banner. A message such as the following
      55                 : may be used:
      56                 : 
      57                 :     "The Graphics Interchange Format(c) is the Copyright property of
      58                 :     CompuServe Incorporated. GIF(sm) is a Service Mark property of
      59                 :     CompuServe Incorporated."
      60                 : 
      61                 : For further information, please contact :
      62                 : 
      63                 :     CompuServe Incorporated
      64                 :     Graphics Technology Department
      65                 :     5000 Arlington Center Boulevard
      66                 :     Columbus, Ohio  43220
      67                 :     U. S. A.
      68                 : 
      69                 : CompuServe Incorporated maintains a mailing list with all those individuals and
      70                 : organizations who wish to receive copies of this document when it is corrected
      71                 : or revised. This service is offered free of charge; please provide us with your
      72                 : mailing address.
      73                 : */
      74                 : 
      75                 : #include <stddef.h>
      76                 : #include "prmem.h"
      77                 : 
      78                 : #include "nsGIFDecoder2.h"
      79                 : #include "nsIInputStream.h"
      80                 : #include "imgIContainerObserver.h"
      81                 : #include "RasterImage.h"
      82                 : 
      83                 : #include "gfxColor.h"
      84                 : #include "gfxPlatform.h"
      85                 : #include "qcms.h"
      86                 : 
      87                 : namespace mozilla {
      88                 : namespace image {
      89                 : 
      90                 : /*
      91                 :  * GETN(n, s) requests at least 'n' bytes available from 'q', at start of state 's'
      92                 :  *
      93                 :  * Note, the hold will never need to be bigger than 256 bytes to gather up in the hold,
      94                 :  * as each GIF block (except colormaps) can never be bigger than 256 bytes.
      95                 :  * Colormaps are directly copied in the resp. global_colormap or the local_colormap of the PAL image frame
      96                 :  * So a fixed buffer in gif_struct is good enough.
      97                 :  * This buffer is only needed to copy left-over data from one GifWrite call to the next
      98                 :  */
      99                 : #define GETN(n,s)                      \
     100                 :   PR_BEGIN_MACRO                       \
     101                 :     mGIFStruct.bytes_to_consume = (n); \
     102                 :     mGIFStruct.state = (s);            \
     103                 :   PR_END_MACRO
     104                 : 
     105                 : /* Get a 16-bit value stored in little-endian format */
     106                 : #define GETINT16(p)   ((p)[1]<<8|(p)[0])
     107                 : //////////////////////////////////////////////////////////////////////
     108                 : // GIF Decoder Implementation
     109                 : 
     110               4 : nsGIFDecoder2::nsGIFDecoder2(RasterImage &aImage, imgIDecoderObserver* aObserver)
     111                 :   : Decoder(aImage, aObserver)
     112                 :   , mCurrentRow(-1)
     113                 :   , mLastFlushedRow(-1)
     114                 :   , mImageData(nsnull)
     115                 :   , mOldColor(0)
     116                 :   , mCurrentFrame(-1)
     117                 :   , mCurrentPass(0)
     118                 :   , mLastFlushedPass(0)
     119                 :   , mGIFOpen(false)
     120               4 :   , mSawTransparency(false)
     121                 : {
     122                 :   // Clear out the structure, excluding the arrays
     123               4 :   memset(&mGIFStruct, 0, sizeof(mGIFStruct));
     124                 : 
     125                 :   // Initialize as "animate once" in case no NETSCAPE2.0 extension is found
     126               4 :   mGIFStruct.loop_count = 1;
     127                 : 
     128                 :   // Start with the version (GIF89a|GIF87a)
     129               4 :   mGIFStruct.state = gif_type;
     130               4 :   mGIFStruct.bytes_to_consume = 6;
     131               4 : }
     132                 : 
     133              12 : nsGIFDecoder2::~nsGIFDecoder2()
     134                 : {
     135               4 :   if (mGIFStruct.local_colormap) {
     136               0 :     moz_free(mGIFStruct.local_colormap);
     137                 :   }
     138              16 : }
     139                 : 
     140                 : void
     141               4 : nsGIFDecoder2::FinishInternal()
     142                 : {
     143               4 :   NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call FinishInternal after error!");
     144                 : 
     145                 :   // If the GIF got cut off, handle it anyway
     146               4 :   if (!IsSizeDecode() && mGIFOpen) {
     147               2 :     if (mCurrentFrame == mGIFStruct.images_decoded)
     148               0 :       EndImageFrame();
     149               2 :     PostDecodeDone();
     150               2 :     mGIFOpen = false;
     151                 :   }
     152                 : 
     153               4 :   mImage.SetLoopCount(mGIFStruct.loop_count - 1);
     154               4 : }
     155                 : 
     156                 : // Push any new rows according to mCurrentPass/mLastFlushedPass and
     157                 : // mCurrentRow/mLastFlushedRow.  Note: caller is responsible for
     158                 : // updating mlastFlushed{Row,Pass}.
     159                 : void
     160               2 : nsGIFDecoder2::FlushImageData(PRUint32 fromRow, PRUint32 rows)
     161                 : {
     162               2 :   nsIntRect r(mGIFStruct.x_offset, mGIFStruct.y_offset + fromRow, mGIFStruct.width, rows);
     163               2 :   PostInvalidation(r);
     164               2 : }
     165                 : 
     166                 : void
     167               3 : nsGIFDecoder2::FlushImageData()
     168                 : {
     169               3 :   switch (mCurrentPass - mLastFlushedPass) {
     170                 :     case 0:  // same pass
     171               3 :       if (mCurrentRow - mLastFlushedRow)
     172               2 :         FlushImageData(mLastFlushedRow + 1, mCurrentRow - mLastFlushedRow);
     173               3 :       break;
     174                 :   
     175                 :     case 1:  // one pass on - need to handle bottom & top rects
     176               0 :       FlushImageData(0, mCurrentRow + 1);
     177               0 :       FlushImageData(mLastFlushedRow + 1, mGIFStruct.height - (mLastFlushedRow + 1));
     178               0 :       break;
     179                 : 
     180                 :     default:   // more than one pass on - push the whole frame
     181               0 :       FlushImageData(0, mGIFStruct.height);
     182                 :   }
     183               3 : }
     184                 : 
     185                 : //******************************************************************************
     186                 : // GIF decoder callback methods. Part of public API for GIF2
     187                 : //******************************************************************************
     188                 : 
     189                 : //******************************************************************************
     190               4 : void nsGIFDecoder2::BeginGIF()
     191                 : {
     192               4 :   if (mGIFOpen)
     193               0 :     return;
     194                 : 
     195               4 :   mGIFOpen = true;
     196                 : 
     197               4 :   PostSize(mGIFStruct.screen_width, mGIFStruct.screen_height);
     198                 : 
     199                 :   // If we're doing a size decode, we have what we came for
     200               4 :   if (IsSizeDecode())
     201               2 :     return;
     202                 : }
     203                 : 
     204                 : //******************************************************************************
     205               4 : nsresult nsGIFDecoder2::BeginImageFrame(PRUint16 aDepth)
     206                 : {
     207                 :   PRUint32 imageDataLength;
     208                 :   nsresult rv;
     209                 :   gfxASurface::gfxImageFormat format;
     210               4 :   if (mGIFStruct.is_transparent)
     211               4 :     format = gfxASurface::ImageFormatARGB32;
     212                 :   else
     213               0 :     format = gfxASurface::ImageFormatRGB24;
     214                 : 
     215                 :   // Use correct format, RGB for first frame, PAL for following frames
     216                 :   // and include transparency to allow for optimization of opaque images
     217               4 :   if (mGIFStruct.images_decoded) {
     218                 :     // Image data is stored with original depth and palette
     219                 :     rv = mImage.EnsureFrame(mGIFStruct.images_decoded,
     220                 :                             mGIFStruct.x_offset, mGIFStruct.y_offset,
     221                 :                             mGIFStruct.width, mGIFStruct.height,
     222                 :                             format, aDepth, &mImageData, &imageDataLength,
     223               2 :                             &mColormap, &mColormapSize);
     224                 :   } else {
     225                 :     // Regardless of depth of input, image is decoded into 24bit RGB
     226                 :     rv = mImage.EnsureFrame(mGIFStruct.images_decoded,
     227                 :                             mGIFStruct.x_offset, mGIFStruct.y_offset,
     228                 :                             mGIFStruct.width, mGIFStruct.height,
     229               2 :                             format, &mImageData, &imageDataLength);
     230                 :   }
     231                 : 
     232               4 :   if (NS_FAILED(rv))
     233               0 :     return rv;
     234                 : 
     235                 :   mImage.SetFrameDisposalMethod(mGIFStruct.images_decoded,
     236               4 :                                 mGIFStruct.disposal_method);
     237                 : 
     238                 :   // Tell the superclass we're starting a frame
     239               4 :   PostFrameStart();
     240                 : 
     241               4 :   if (!mGIFStruct.images_decoded) {
     242                 :     // Send a onetime invalidation for the first frame if it has a y-axis offset. 
     243                 :     // Otherwise, the area may never be refreshed and the placeholder will remain
     244                 :     // on the screen. (Bug 37589)
     245               2 :     if (mGIFStruct.y_offset > 0) {
     246                 :       PRInt32 imgWidth;
     247               0 :       mImage.GetWidth(&imgWidth);
     248               0 :       nsIntRect r(0, 0, imgWidth, mGIFStruct.y_offset);
     249               0 :       PostInvalidation(r);
     250                 :     }
     251                 :   }
     252                 : 
     253               4 :   mCurrentFrame = mGIFStruct.images_decoded;
     254               4 :   return NS_OK;
     255                 : }
     256                 : 
     257                 : 
     258                 : //******************************************************************************
     259               4 : void nsGIFDecoder2::EndImageFrame()
     260                 : {
     261                 :   // First flush all pending image data 
     262               4 :   if (!mGIFStruct.images_decoded) {
     263                 :     // Only need to flush first frame
     264               2 :     FlushImageData();
     265                 : 
     266                 :     // If the first frame is smaller in height than the entire image, send an
     267                 :     // invalidation for the area it does not have data for.
     268                 :     // This will clear the remaining bits of the placeholder. (Bug 37589)
     269               2 :     const PRUint32 realFrameHeight = mGIFStruct.height + mGIFStruct.y_offset;
     270               2 :     if (realFrameHeight < mGIFStruct.screen_height) {
     271                 :       nsIntRect r(0, realFrameHeight,
     272                 :                   mGIFStruct.screen_width,
     273               0 :                   mGIFStruct.screen_height - realFrameHeight);
     274               0 :       PostInvalidation(r);
     275                 :     }
     276                 :     // This transparency check is only valid for first frame
     277               2 :     if (mGIFStruct.is_transparent && !mSawTransparency) {
     278               1 :       mImage.SetFrameHasNoAlpha(mGIFStruct.images_decoded);
     279                 :     }
     280                 :   }
     281               4 :   mCurrentRow = mLastFlushedRow = -1;
     282               4 :   mCurrentPass = mLastFlushedPass = 0;
     283                 : 
     284                 :   // Only add frame if we have any rows at all
     285               4 :   if (mGIFStruct.rows_remaining != mGIFStruct.height) {
     286               4 :     if (mGIFStruct.rows_remaining && mGIFStruct.images_decoded) {
     287                 :       // Clear the remaining rows (only needed for the animation frames)
     288               0 :       PRUint8 *rowp = mImageData + ((mGIFStruct.height - mGIFStruct.rows_remaining) * mGIFStruct.width);
     289               0 :       memset(rowp, 0, mGIFStruct.rows_remaining * mGIFStruct.width);
     290                 :     }
     291                 : 
     292                 :     // We actually have the timeout information before we get the lzw encoded 
     293                 :     // image data, at least according to the spec, but we delay in setting the 
     294                 :     // timeout for the image until here to help ensure that we have the whole 
     295                 :     // image frame decoded before we go off and try to display another frame.
     296               4 :     mImage.SetFrameTimeout(mGIFStruct.images_decoded, mGIFStruct.delay_time);
     297                 :   }
     298                 : 
     299                 :   // Unconditionally increment images_decoded, because we unconditionally
     300                 :   // append frames in BeginImageFrame(). This ensures that images_decoded
     301                 :   // always refers to the frame in mImage we're currently decoding,
     302                 :   // even if some of them weren't decoded properly and thus are blank.
     303               4 :   mGIFStruct.images_decoded++;
     304                 : 
     305                 :   // Tell the superclass we finished a frame
     306               4 :   PostFrameStop();
     307                 : 
     308                 :   // Reset the transparent pixel
     309               4 :   if (mOldColor) {
     310               2 :     mColormap[mGIFStruct.tpixel] = mOldColor;
     311               2 :     mOldColor = 0;
     312                 :   }
     313                 : 
     314               4 :   mCurrentFrame = -1;
     315               4 : }
     316                 : 
     317                 : 
     318                 : //******************************************************************************
     319                 : // Send the data to the display front-end.
     320             332 : PRUint32 nsGIFDecoder2::OutputRow()
     321                 : {
     322                 :   int drow_start, drow_end;
     323             332 :   drow_start = drow_end = mGIFStruct.irow;
     324                 : 
     325                 :   /* Protect against too much image data */
     326             332 :   if ((PRUintn)drow_start >= mGIFStruct.height) {
     327               0 :     NS_WARNING("GIF2.cpp::OutputRow - too much image data");
     328               0 :     return 0;
     329                 :   }
     330                 : 
     331             332 :   if (!mGIFStruct.images_decoded) {
     332                 :     /*
     333                 :      * Haeberli-inspired hack for interlaced GIFs: Replicate lines while
     334                 :      * displaying to diminish the "venetian-blind" effect as the image is
     335                 :      * loaded. Adjust pixel vertical positions to avoid the appearance of the
     336                 :      * image crawling up the screen as successive passes are drawn.
     337                 :      */
     338             132 :     if (mGIFStruct.progressive_display && mGIFStruct.interlaced && (mGIFStruct.ipass < 4)) {
     339                 :       /* ipass = 1,2,3 results in resp. row_dup = 7,3,1 and row_shift = 3,1,0 */
     340               0 :       const PRUint32 row_dup = 15 >> mGIFStruct.ipass;
     341               0 :       const PRUint32 row_shift = row_dup >> 1;
     342                 :   
     343               0 :       drow_start -= row_shift;
     344               0 :       drow_end = drow_start + row_dup;
     345                 :   
     346                 :       /* Extend if bottom edge isn't covered because of the shift upward. */
     347               0 :       if (((mGIFStruct.height - 1) - drow_end) <= row_shift)
     348               0 :         drow_end = mGIFStruct.height - 1;
     349                 :   
     350                 :       /* Clamp first and last rows to upper and lower edge of image. */
     351               0 :       if (drow_start < 0)
     352               0 :         drow_start = 0;
     353               0 :       if ((PRUintn)drow_end >= mGIFStruct.height)
     354               0 :         drow_end = mGIFStruct.height - 1;
     355                 :     }
     356                 : 
     357                 :     // Row to process
     358             132 :     const PRUint32 bpr = sizeof(PRUint32) * mGIFStruct.width; 
     359             132 :     PRUint8 *rowp = mImageData + (mGIFStruct.irow * bpr);
     360                 : 
     361                 :     // Convert color indices to Cairo pixels
     362             132 :     PRUint8 *from = rowp + mGIFStruct.width;
     363             132 :     PRUint32 *to = ((PRUint32*)rowp) + mGIFStruct.width;
     364             132 :     PRUint32 *cmap = mColormap;
     365             132 :     if (mColorMask == 0xFF) {
     366            1056 :       for (PRUint32 c = mGIFStruct.width; c > 0; c--) {
     367            1024 :         *--to = cmap[*--from];
     368                 :       }
     369                 :     } else {
     370                 :       // Make sure that pixels within range of colormap.
     371             100 :       PRUint8 mask = mColorMask;
     372            7600 :       for (PRUint32 c = mGIFStruct.width; c > 0; c--) {
     373            7500 :         *--to = cmap[(*--from) & mask];
     374                 :       }
     375                 :     }
     376                 :   
     377                 :     // check for alpha (only for first frame)
     378             132 :     if (mGIFStruct.is_transparent && !mSawTransparency) {
     379              33 :       const PRUint32 *rgb = (PRUint32*)rowp;
     380            1057 :       for (PRUint32 i = mGIFStruct.width; i > 0; i--) {
     381            1025 :         if (*rgb++ == 0) {
     382               1 :           mSawTransparency = true;
     383               1 :           break;
     384                 :         }
     385                 :       }
     386                 :     }
     387                 : 
     388                 :     // Duplicate rows
     389             132 :     if (drow_end > drow_start) {
     390                 :       // irow is the current row filled
     391               0 :       for (int r = drow_start; r <= drow_end; r++) {
     392               0 :         if (r != int(mGIFStruct.irow)) {
     393               0 :           memcpy(mImageData + (r * bpr), rowp, bpr);
     394                 :         }
     395                 :       }
     396                 :     }
     397                 :   }
     398                 : 
     399             332 :   mCurrentRow = drow_end;
     400             332 :   mCurrentPass = mGIFStruct.ipass;
     401             332 :   if (mGIFStruct.ipass == 1)
     402               0 :     mLastFlushedPass = mGIFStruct.ipass;   // interlaced starts at 1
     403                 : 
     404             332 :   if (!mGIFStruct.interlaced) {
     405             332 :     mGIFStruct.irow++;
     406                 :   } else {
     407                 :     static const PRUint8 kjump[5] = { 1, 8, 8, 4, 2 };
     408               0 :     do {
     409                 :       // Row increments resp. per 8,8,4,2 rows
     410               0 :       mGIFStruct.irow += kjump[mGIFStruct.ipass];
     411               0 :       if (mGIFStruct.irow >= mGIFStruct.height) {
     412                 :         // Next pass starts resp. at row 4,2,1,0
     413               0 :         mGIFStruct.irow = 8 >> mGIFStruct.ipass;
     414               0 :         mGIFStruct.ipass++;
     415                 :       }
     416                 :     } while (mGIFStruct.irow >= mGIFStruct.height);
     417                 :   }
     418                 : 
     419             332 :   return --mGIFStruct.rows_remaining;
     420                 : }
     421                 : 
     422                 : //******************************************************************************
     423                 : /* Perform Lempel-Ziv-Welch decoding */
     424                 : bool
     425               7 : nsGIFDecoder2::DoLzw(const PRUint8 *q)
     426                 : {
     427               7 :   if (!mGIFStruct.rows_remaining)
     428               0 :     return true;
     429                 : 
     430                 :   /* Copy all the decoder state variables into locals so the compiler
     431                 :    * won't worry about them being aliased.  The locals will be homed
     432                 :    * back into the GIF decoder structure when we exit.
     433                 :    */
     434               7 :   int avail       = mGIFStruct.avail;
     435               7 :   int bits        = mGIFStruct.bits;
     436               7 :   int codesize    = mGIFStruct.codesize;
     437               7 :   int codemask    = mGIFStruct.codemask;
     438               7 :   int count       = mGIFStruct.count;
     439               7 :   int oldcode     = mGIFStruct.oldcode;
     440               7 :   const int clear_code = ClearCode();
     441               7 :   PRUint8 firstchar = mGIFStruct.firstchar;
     442               7 :   PRInt32 datum     = mGIFStruct.datum;
     443               7 :   PRUint16 *prefix  = mGIFStruct.prefix;
     444               7 :   PRUint8 *stackp   = mGIFStruct.stackp;
     445               7 :   PRUint8 *suffix   = mGIFStruct.suffix;
     446               7 :   PRUint8 *stack    = mGIFStruct.stack;
     447               7 :   PRUint8 *rowp     = mGIFStruct.rowp;
     448                 : 
     449               7 :   PRUint32 bpr = mGIFStruct.width;
     450               7 :   if (!mGIFStruct.images_decoded) 
     451               5 :     bpr *= sizeof(PRUint32);
     452               7 :   PRUint8 *rowend   = mImageData + (bpr * mGIFStruct.irow) + mGIFStruct.width;
     453                 : 
     454                 : #define OUTPUT_ROW()                                        \
     455                 :   PR_BEGIN_MACRO                                            \
     456                 :     if (!OutputRow())                                       \
     457                 :       goto END;                                             \
     458                 :     rowp = mImageData + mGIFStruct.irow * bpr;              \
     459                 :     rowend = rowp + mGIFStruct.width;                       \
     460                 :   PR_END_MACRO
     461                 : 
     462            1445 :   for (const PRUint8* ch = q; count-- > 0; ch++)
     463                 :   {
     464                 :     /* Feed the next byte into the decoder's 32-bit input buffer. */
     465            1442 :     datum += ((int32) *ch) << bits;
     466            1442 :     bits += 8;
     467                 : 
     468                 :     /* Check for underflow of decoder's 32-bit input buffer. */
     469            4224 :     while (bits >= codesize)
     470                 :     {
     471                 :       /* Get the leading variable-length symbol from the data stream */
     472            1344 :       int code = datum & codemask;
     473            1344 :       datum >>= codesize;
     474            1344 :       bits -= codesize;
     475                 : 
     476                 :       /* Reset the dictionary to its original state, if requested */
     477            1344 :       if (code == clear_code) {
     478               4 :         codesize = mGIFStruct.datasize + 1;
     479               4 :         codemask = (1 << codesize) - 1;
     480               4 :         avail = clear_code + 2;
     481               4 :         oldcode = -1;
     482               4 :         continue;
     483                 :       }
     484                 : 
     485                 :       /* Check for explicit end-of-stream code */
     486            1340 :       if (code == (clear_code + 1)) {
     487                 :         /* end-of-stream should only appear after all image data */
     488               0 :         return (mGIFStruct.rows_remaining == 0);
     489                 :       }
     490                 : 
     491            1340 :       if (oldcode == -1) {
     492               4 :         if (code >= MAX_BITS)
     493               0 :           return false;
     494               4 :         *rowp++ = suffix[code];
     495               4 :         if (rowp == rowend)
     496               0 :           OUTPUT_ROW();
     497                 : 
     498               4 :         firstchar = oldcode = code;
     499               4 :         continue;
     500                 :       }
     501                 : 
     502            1336 :       int incode = code;
     503            1336 :       if (code >= avail) {
     504             310 :         *stackp++ = firstchar;
     505             310 :         code = oldcode;
     506                 : 
     507             310 :         if (stackp >= stack + MAX_BITS)
     508               0 :           return false;
     509                 :       }
     510                 : 
     511           17446 :       while (code >= clear_code)
     512                 :       {
     513           14774 :         if ((code >= MAX_BITS) || (code == prefix[code]))
     514               0 :           return false;
     515                 : 
     516           14774 :         *stackp++ = suffix[code];
     517           14774 :         code = prefix[code];
     518                 : 
     519           14774 :         if (stackp == stack + MAX_BITS)
     520               0 :           return false;
     521                 :       }
     522                 : 
     523            1336 :       *stackp++ = firstchar = suffix[code];
     524                 : 
     525                 :       /* Define a new codeword in the dictionary. */
     526            1336 :       if (avail < 4096) {
     527            1336 :         prefix[avail] = oldcode;
     528            1336 :         suffix[avail] = firstchar;
     529            1336 :         avail++;
     530                 : 
     531                 :         /* If we've used up all the codewords of a given length
     532                 :          * increase the length of codewords by one bit, but don't
     533                 :          * exceed the specified maximum codeword size of 12 bits.
     534                 :          */
     535            1336 :         if (((avail & codemask) == 0) && (avail < 4096)) {
     536              16 :           codesize++;
     537              16 :           codemask += avail;
     538                 :         }
     539                 :       }
     540            1336 :       oldcode = incode;
     541                 : 
     542                 :       /* Copy the decoded data out to the scanline buffer. */
     543           16416 :       do {
     544           16420 :         *rowp++ = *--stackp;
     545           16420 :         if (rowp == rowend)
     546             332 :           OUTPUT_ROW();
     547                 :       } while (stackp > stack);
     548                 :     }
     549                 :   }
     550                 : 
     551                 :   END:
     552                 : 
     553                 :   /* Home the local copies of the GIF decoder state variables */
     554               7 :   mGIFStruct.avail = avail;
     555               7 :   mGIFStruct.bits = bits;
     556               7 :   mGIFStruct.codesize = codesize;
     557               7 :   mGIFStruct.codemask = codemask;
     558               7 :   mGIFStruct.count = count;
     559               7 :   mGIFStruct.oldcode = oldcode;
     560               7 :   mGIFStruct.firstchar = firstchar;
     561               7 :   mGIFStruct.datum = datum;
     562               7 :   mGIFStruct.stackp = stackp;
     563               7 :   mGIFStruct.rowp = rowp;
     564                 : 
     565               7 :   return true;
     566                 : }
     567                 : 
     568                 : /** 
     569                 :  * Expand the colormap from RGB to Packed ARGB as needed by Cairo.
     570                 :  * And apply any LCMS transformation.
     571                 :  */
     572               4 : static void ConvertColormap(PRUint32 *aColormap, PRUint32 aColors)
     573                 : {
     574                 :   // Apply CMS transformation if enabled and available
     575               4 :   if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
     576               0 :     qcms_transform *transform = gfxPlatform::GetCMSRGBTransform();
     577               0 :     if (transform)
     578               0 :       qcms_transform_data(transform, aColormap, aColormap, aColors);
     579                 :   }
     580                 :   // Convert from the GIF's RGB format to the Cairo format.
     581                 :   // Work from end to begin, because of the in-place expansion
     582               4 :   PRUint8 *from = ((PRUint8 *)aColormap) + 3 * aColors;
     583               4 :   PRUint32 *to = aColormap + aColors;
     584                 : 
     585                 :   // Convert color entries to Cairo format
     586                 : 
     587                 :   // set up for loops below
     588               4 :   if (!aColors) return;
     589               4 :   PRUint32 c = aColors;
     590                 : 
     591                 :   // copy as bytes until source pointer is 32-bit-aligned
     592                 :   // NB: can't use 32-bit reads, they might read off the end of the buffer
     593              10 :   for (; (NS_PTR_TO_UINT32(from) & 0x3) && c; --c) {
     594               6 :     from -= 3;
     595               6 :     *--to = GFX_PACKED_PIXEL(0xFF, from[0], from[1], from[2]);
     596                 :   }
     597                 : 
     598                 :   // bulk copy of pixels.
     599              72 :   while (c >= 4) {
     600              64 :     from -= 12;
     601              64 :     to   -=  4;
     602              64 :     c    -=  4;
     603              64 :     GFX_BLOCK_RGB_TO_FRGB(from,to);
     604                 :   }
     605                 : 
     606                 :   // copy remaining pixel(s)
     607                 :   // NB: can't use 32-bit reads, they might read off the end of the buffer
     608               8 :   while (c--) {
     609               0 :     from -= 3;
     610               0 :     *--to = GFX_PACKED_PIXEL(0xFF, from[0], from[1], from[2]);
     611                 :   }
     612                 : }
     613                 : 
     614                 : void
     615               5 : nsGIFDecoder2::WriteInternal(const char *aBuffer, PRUint32 aCount)
     616                 : {
     617               5 :   NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!");
     618                 : 
     619                 :   // These variables changed names, and renaming would make a much bigger patch :(
     620               5 :   const PRUint8 *buf = (const PRUint8 *)aBuffer;
     621               5 :   PRUint32 len = aCount;
     622                 : 
     623               5 :   const PRUint8 *q = buf;
     624                 : 
     625                 :   // Add what we have sofar to the block
     626                 :   // If previous call to me left something in the hold first complete current block
     627                 :   // Or if we are filling the colormaps, first complete the colormap
     628                 :   PRUint8* p = (mGIFStruct.state == gif_global_colormap) ? (PRUint8*)mGIFStruct.global_colormap :
     629                 :                (mGIFStruct.state == gif_image_colormap) ? (PRUint8*)mColormap :
     630               5 :                (mGIFStruct.bytes_in_hold) ? mGIFStruct.hold : nsnull;
     631               5 :   if (p) {
     632                 :     // Add what we have sofar to the block
     633               1 :     PRUint32 l = NS_MIN(len, mGIFStruct.bytes_to_consume);
     634               1 :     memcpy(p+mGIFStruct.bytes_in_hold, buf, l);
     635                 : 
     636               1 :     if (l < mGIFStruct.bytes_to_consume) {
     637                 :       // Not enough in 'buf' to complete current block, get more
     638               0 :       mGIFStruct.bytes_in_hold += l;
     639               0 :       mGIFStruct.bytes_to_consume -= l;
     640               0 :       return;
     641                 :     }
     642                 :     // Reset hold buffer count
     643               1 :     mGIFStruct.bytes_in_hold = 0;
     644                 :     // Point 'q' to complete block in hold (or in colormap)
     645               1 :     q = p;
     646                 :   }
     647                 : 
     648                 :   // Invariant:
     649                 :   //    'q' is start of current to be processed block (hold, colormap or buf)
     650                 :   //    'bytes_to_consume' is number of bytes to consume from 'buf'
     651                 :   //    'buf' points to the bytes to be consumed from the input buffer
     652                 :   //    'len' is number of bytes left in input buffer from position 'buf'.
     653                 :   //    At entrance of the for loop will 'buf' will be moved 'bytes_to_consume'
     654                 :   //    to point to next buffer, 'len' is adjusted accordingly.
     655                 :   //    So that next round in for loop, q gets pointed to the next buffer.
     656                 : 
     657              75 :   for (;len >= mGIFStruct.bytes_to_consume; q=buf) {
     658                 :     // Eat the current block from the buffer, q keeps pointed at current block
     659              72 :     buf += mGIFStruct.bytes_to_consume;
     660              72 :     len -= mGIFStruct.bytes_to_consume;
     661                 : 
     662              72 :     switch (mGIFStruct.state)
     663                 :     {
     664                 :     case gif_lzw:
     665               7 :       if (!DoLzw(q)) {
     666               0 :         mGIFStruct.state = gif_error;
     667               0 :         break;
     668                 :       }
     669               7 :       GETN(1, gif_sub_block);
     670               7 :       break;
     671                 : 
     672                 :     case gif_lzw_start:
     673                 :     {
     674                 :       // Make sure the transparent pixel is transparent in the colormap
     675               4 :       if (mGIFStruct.is_transparent) {
     676                 :         // Save old value so we can restore it later
     677               4 :         if (mColormap == mGIFStruct.global_colormap)
     678               2 :             mOldColor = mColormap[mGIFStruct.tpixel];
     679               4 :         mColormap[mGIFStruct.tpixel] = 0;
     680                 :       }
     681                 : 
     682                 :       /* Initialize LZW parser/decoder */
     683               4 :       mGIFStruct.datasize = *q;
     684               4 :       const int clear_code = ClearCode();
     685               4 :       if (mGIFStruct.datasize > MAX_LZW_BITS ||
     686                 :           clear_code >= MAX_BITS) {
     687               0 :         mGIFStruct.state = gif_error;
     688               0 :         break;
     689                 :       }
     690                 : 
     691               4 :       mGIFStruct.avail = clear_code + 2;
     692               4 :       mGIFStruct.oldcode = -1;
     693               4 :       mGIFStruct.codesize = mGIFStruct.datasize + 1;
     694               4 :       mGIFStruct.codemask = (1 << mGIFStruct.codesize) - 1;
     695               4 :       mGIFStruct.datum = mGIFStruct.bits = 0;
     696                 : 
     697                 :       /* init the tables */
     698             272 :       for (int i = 0; i < clear_code; i++)
     699             268 :         mGIFStruct.suffix[i] = i;
     700                 : 
     701               4 :       mGIFStruct.stackp = mGIFStruct.stack;
     702                 : 
     703               4 :       GETN(1, gif_sub_block);
     704                 :     }
     705               4 :     break;
     706                 : 
     707                 :     /* All GIF files begin with "GIF87a" or "GIF89a" */
     708                 :     case gif_type:
     709               4 :       if (!strncmp((char*)q, "GIF89a", 6)) {
     710               4 :         mGIFStruct.version = 89;
     711               0 :       } else if (!strncmp((char*)q, "GIF87a", 6)) {
     712               0 :         mGIFStruct.version = 87;
     713                 :       } else {
     714               0 :         mGIFStruct.state = gif_error;
     715               0 :         break;
     716                 :       }
     717               4 :       GETN(7, gif_global_header);
     718               4 :       break;
     719                 : 
     720                 :     case gif_global_header:
     721                 :       /* This is the height and width of the "screen" or
     722                 :        * frame into which images are rendered.  The
     723                 :        * individual images can be smaller than the
     724                 :        * screen size and located with an origin anywhere
     725                 :        * within the screen.
     726                 :        */
     727                 : 
     728               4 :       mGIFStruct.screen_width = GETINT16(q);
     729               4 :       mGIFStruct.screen_height = GETINT16(q + 2);
     730               4 :       mGIFStruct.global_colormap_depth = (q[4]&0x07) + 1;
     731                 : 
     732                 :       // screen_bgcolor is not used
     733                 :       //mGIFStruct.screen_bgcolor = q[5];
     734                 :       // q[6] = Pixel Aspect Ratio
     735                 :       //   Not used
     736                 :       //   float aspect = (float)((q[6] + 15) / 64.0);
     737                 : 
     738               4 :       if (q[4] & 0x80) { /* global map */
     739                 :         // Get the global colormap
     740               4 :         const PRUint32 size = (3 << mGIFStruct.global_colormap_depth);
     741               4 :         if (len < size) {
     742                 :           // Use 'hold' pattern to get the global colormap
     743               0 :           GETN(size, gif_global_colormap);
     744               0 :           break;
     745                 :         }
     746                 :         // Copy everything, go to colormap state to do CMS correction
     747               4 :         memcpy(mGIFStruct.global_colormap, buf, size);
     748               4 :         buf += size;
     749               4 :         len -= size;
     750               4 :         GETN(0, gif_global_colormap);
     751               4 :         break;
     752                 :       }
     753                 : 
     754               0 :       GETN(1, gif_image_start);
     755               0 :       break;
     756                 : 
     757                 :     case gif_global_colormap:
     758                 :       // Everything is already copied into global_colormap
     759                 :       // Convert into Cairo colors including CMS transformation
     760               4 :       ConvertColormap(mGIFStruct.global_colormap, 1<<mGIFStruct.global_colormap_depth);
     761               4 :       GETN(1, gif_image_start);
     762               4 :       break;
     763                 : 
     764                 :     case gif_image_start:
     765              14 :       switch (*q) {
     766                 :         case GIF_TRAILER:
     767               2 :           mGIFStruct.state = gif_done;
     768               2 :           break;
     769                 : 
     770                 :         case GIF_EXTENSION_INTRODUCER:
     771               6 :           GETN(2, gif_extension);
     772               6 :           break;
     773                 : 
     774                 :         case GIF_IMAGE_SEPARATOR:
     775               6 :           GETN(9, gif_image_header);
     776               6 :           break;
     777                 : 
     778                 :         default:
     779                 :           /* If we get anything other than GIF_IMAGE_SEPARATOR, 
     780                 :            * GIF_EXTENSION_INTRODUCER, or GIF_TRAILER, there is extraneous data
     781                 :            * between blocks. The GIF87a spec tells us to keep reading
     782                 :            * until we find an image separator, but GIF89a says such
     783                 :            * a file is corrupt. We follow GIF89a and bail out. */
     784               0 :           if (mGIFStruct.images_decoded > 0) {
     785                 :             /* The file is corrupt, but one or more images have
     786                 :              * been decoded correctly. In this case, we proceed
     787                 :              * as if the file were correctly terminated and set
     788                 :              * the state to gif_done, so the GIF will display.
     789                 :              */
     790               0 :             mGIFStruct.state = gif_done;
     791                 :           } else {
     792                 :             /* No images decoded, there is nothing to display. */
     793               0 :             mGIFStruct.state = gif_error;
     794                 :           }
     795                 :       }
     796              14 :       break;
     797                 : 
     798                 :     case gif_extension:
     799               6 :       mGIFStruct.bytes_to_consume = q[1];
     800               6 :       if (mGIFStruct.bytes_to_consume) {
     801               6 :         switch (*q) {
     802                 :         case GIF_GRAPHIC_CONTROL_LABEL:
     803               6 :           mGIFStruct.state = gif_control_extension;
     804               6 :           break;
     805                 :   
     806                 :         case GIF_APPLICATION_EXTENSION_LABEL:
     807               0 :           mGIFStruct.state = gif_application_extension;
     808               0 :           break;
     809                 :   
     810                 :         case GIF_COMMENT_LABEL:
     811               0 :           mGIFStruct.state = gif_consume_comment;
     812               0 :           break;
     813                 :   
     814                 :         default:
     815               0 :           mGIFStruct.state = gif_skip_block;
     816                 :         }
     817                 :       } else {
     818               0 :         GETN(1, gif_image_start);
     819                 :       }
     820               6 :       break;
     821                 : 
     822                 :     case gif_consume_block:
     823               6 :       if (!*q)
     824               6 :         GETN(1, gif_image_start);
     825                 :       else
     826               0 :         GETN(*q, gif_skip_block);
     827               6 :       break;
     828                 : 
     829                 :     case gif_skip_block:
     830               0 :       GETN(1, gif_consume_block);
     831               0 :       break;
     832                 : 
     833                 :     case gif_control_extension:
     834               6 :       mGIFStruct.is_transparent = *q & 0x1;
     835               6 :       mGIFStruct.tpixel = q[3];
     836               6 :       mGIFStruct.disposal_method = ((*q) >> 2) & 0x7;
     837                 :       // Some specs say 3rd bit (value 4), other specs say value 3
     838                 :       // Let's choose 3 (the more popular)
     839               6 :       if (mGIFStruct.disposal_method == 4)
     840               0 :         mGIFStruct.disposal_method = 3;
     841               6 :       mGIFStruct.delay_time = GETINT16(q + 1) * 10;
     842               6 :       GETN(1, gif_consume_block);
     843               6 :       break;
     844                 : 
     845                 :     case gif_comment_extension:
     846               0 :       if (*q)
     847               0 :         GETN(*q, gif_consume_comment);
     848                 :       else
     849               0 :         GETN(1, gif_image_start);
     850               0 :       break;
     851                 : 
     852                 :     case gif_consume_comment:
     853               0 :       GETN(1, gif_comment_extension);
     854               0 :       break;
     855                 : 
     856                 :     case gif_application_extension:
     857                 :       /* Check for netscape application extension */
     858               0 :       if (!strncmp((char*)q, "NETSCAPE2.0", 11) ||
     859               0 :         !strncmp((char*)q, "ANIMEXTS1.0", 11))
     860               0 :         GETN(1, gif_netscape_extension_block);
     861                 :       else
     862               0 :         GETN(1, gif_consume_block);
     863               0 :       break;
     864                 : 
     865                 :     /* Netscape-specific GIF extension: animation looping */
     866                 :     case gif_netscape_extension_block:
     867               0 :       if (*q)
     868               0 :         GETN(*q, gif_consume_netscape_extension);
     869                 :       else
     870               0 :         GETN(1, gif_image_start);
     871               0 :       break;
     872                 : 
     873                 :     /* Parse netscape-specific application extensions */
     874                 :     case gif_consume_netscape_extension:
     875               0 :       switch (q[0] & 7) {
     876                 :         case 1:
     877                 :           /* Loop entire animation specified # of times.  Only read the
     878                 :              loop count during the first iteration. */
     879               0 :           mGIFStruct.loop_count = GETINT16(q + 1);
     880               0 :           GETN(1, gif_netscape_extension_block);
     881               0 :           break;
     882                 :         
     883                 :         case 2:
     884                 :           /* Wait for specified # of bytes to enter buffer */
     885                 :           // Don't do this, this extension doesn't exist (isn't used at all) 
     886                 :           // and doesn't do anything, as our streaming/buffering takes care of it all...
     887                 :           // See: http://semmix.pl/color/exgraf/eeg24.htm
     888               0 :           GETN(1, gif_netscape_extension_block);
     889               0 :           break;
     890                 :   
     891                 :         default:
     892                 :           // 0,3-7 are yet to be defined netscape extension codes
     893               0 :           mGIFStruct.state = gif_error;
     894                 :       }
     895               0 :       break;
     896                 : 
     897                 :     case gif_image_header:
     898                 :     {
     899                 :       /* Get image offsets, with respect to the screen origin */
     900               6 :       mGIFStruct.x_offset = GETINT16(q);
     901               6 :       mGIFStruct.y_offset = GETINT16(q + 2);
     902                 : 
     903                 :       /* Get image width and height. */
     904               6 :       mGIFStruct.width  = GETINT16(q + 4);
     905               6 :       mGIFStruct.height = GETINT16(q + 6);
     906                 : 
     907               6 :       if (!mGIFStruct.images_decoded) {
     908                 :         /* Work around broken GIF files where the logical screen
     909                 :          * size has weird width or height.  We assume that GIF87a
     910                 :          * files don't contain animations.
     911                 :          */
     912               4 :         if ((mGIFStruct.screen_height < mGIFStruct.height) ||
     913                 :             (mGIFStruct.screen_width < mGIFStruct.width) ||
     914                 :             (mGIFStruct.version == 87)) {
     915               0 :           mGIFStruct.screen_height = mGIFStruct.height;
     916               0 :           mGIFStruct.screen_width = mGIFStruct.width;
     917               0 :           mGIFStruct.x_offset = 0;
     918               0 :           mGIFStruct.y_offset = 0;
     919                 :         }    
     920                 :         // Create the image container with the right size.
     921               4 :         BeginGIF();
     922               4 :         if (HasError()) {
     923                 :           // Setting the size lead to an error; this can happen when for example
     924                 :           // a multipart channel sends an image of a different size.
     925               0 :           mGIFStruct.state = gif_error;
     926               0 :           return;
     927                 :         }
     928                 : 
     929                 :         // If we were doing a size decode, we're done
     930               4 :         if (IsSizeDecode())
     931               2 :           return;
     932                 :       }
     933                 : 
     934                 :       /* Work around more broken GIF files that have zero image
     935                 :          width or height */
     936               4 :       if (!mGIFStruct.height || !mGIFStruct.width) {
     937               0 :         mGIFStruct.height = mGIFStruct.screen_height;
     938               0 :         mGIFStruct.width = mGIFStruct.screen_width;
     939               0 :         if (!mGIFStruct.height || !mGIFStruct.width) {
     940               0 :           mGIFStruct.state = gif_error;
     941               0 :           break;
     942                 :         }
     943                 :       }
     944                 : 
     945                 :       /* Depth of colors is determined by colormap */
     946                 :       /* (q[8] & 0x80) indicates local colormap */
     947                 :       /* bits per pixel is (q[8]&0x07 + 1) when local colormap is set */
     948               4 :       PRUint32 depth = mGIFStruct.global_colormap_depth;
     949               4 :       if (q[8] & 0x80)
     950               0 :         depth = (q[8]&0x07) + 1;
     951               4 :       PRUint32 realDepth = depth;
     952               8 :       while (mGIFStruct.tpixel >= (1 << realDepth) && (realDepth < 8)) {
     953               0 :         realDepth++;
     954                 :       } 
     955                 :       // Mask to limit the color values within the colormap
     956               4 :       mColorMask = 0xFF >> (8 - realDepth);
     957               4 :       nsresult rv = BeginImageFrame(realDepth);
     958               4 :       if (NS_FAILED(rv) || !mImageData) {
     959               0 :         mGIFStruct.state = gif_error;
     960               0 :         break;
     961                 :       }
     962                 : 
     963               4 :       if (q[8] & 0x40) {
     964               0 :         mGIFStruct.interlaced = true;
     965               0 :         mGIFStruct.ipass = 1;
     966                 :       } else {
     967               4 :         mGIFStruct.interlaced = false;
     968               4 :         mGIFStruct.ipass = 0;
     969                 :       }
     970                 : 
     971                 :       /* Only apply the Haeberli display hack on the first frame */
     972               4 :       mGIFStruct.progressive_display = (mGIFStruct.images_decoded == 0);
     973                 : 
     974                 :       /* Clear state from last image */
     975               4 :       mGIFStruct.irow = 0;
     976               4 :       mGIFStruct.rows_remaining = mGIFStruct.height;
     977               4 :       mGIFStruct.rowp = mImageData;
     978                 : 
     979                 :       /* bits per pixel is q[8]&0x07 */
     980                 : 
     981               4 :       if (q[8] & 0x80) /* has a local colormap? */
     982                 :       {
     983               0 :         mGIFStruct.local_colormap_size = 1 << depth;
     984               0 :         if (!mGIFStruct.images_decoded) {
     985                 :           // First frame has local colormap, allocate space for it
     986                 :           // as the image frame doesn't have its own palette
     987               0 :           mColormapSize = sizeof(PRUint32) << realDepth;
     988               0 :           if (!mGIFStruct.local_colormap) {
     989               0 :             mGIFStruct.local_colormap = (PRUint32*)moz_xmalloc(mColormapSize);
     990                 :           }
     991               0 :           mColormap = mGIFStruct.local_colormap;
     992                 :         }
     993               0 :         const PRUint32 size = 3 << depth;
     994               0 :         if (mColormapSize > size) {
     995                 :           // Clear the notfilled part of the colormap
     996               0 :           memset(((PRUint8*)mColormap) + size, 0, mColormapSize - size);
     997                 :         }
     998               0 :         if (len < size) {
     999                 :           // Use 'hold' pattern to get the image colormap
    1000               0 :           GETN(size, gif_image_colormap);
    1001               0 :           break;
    1002                 :         }
    1003                 :         // Copy everything, go to colormap state to do CMS correction
    1004               0 :         memcpy(mColormap, buf, size);
    1005               0 :         buf += size;
    1006               0 :         len -= size;
    1007               0 :         GETN(0, gif_image_colormap);
    1008               0 :         break;
    1009                 :       } else {
    1010                 :         /* Switch back to the global palette */
    1011               4 :         if (mGIFStruct.images_decoded) {
    1012                 :           // Copy global colormap into the palette of current frame
    1013               2 :           memcpy(mColormap, mGIFStruct.global_colormap, mColormapSize);
    1014                 :         } else {
    1015               2 :           mColormap = mGIFStruct.global_colormap;
    1016                 :         }
    1017                 :       }
    1018               4 :       GETN(1, gif_lzw_start);
    1019                 :     }
    1020               4 :     break;
    1021                 : 
    1022                 :     case gif_image_colormap:
    1023                 :       // Everything is already copied into local_colormap
    1024                 :       // Convert into Cairo colors including CMS transformation
    1025               0 :       ConvertColormap(mColormap, mGIFStruct.local_colormap_size);
    1026               0 :       GETN(1, gif_lzw_start);
    1027               0 :       break;
    1028                 : 
    1029                 :     case gif_sub_block:
    1030              11 :       mGIFStruct.count = *q;
    1031              11 :       if (mGIFStruct.count) {
    1032                 :         /* Still working on the same image: Process next LZW data block */
    1033                 :         /* Make sure there are still rows left. If the GIF data */
    1034                 :         /* is corrupt, we may not get an explicit terminator.   */
    1035               7 :         if (!mGIFStruct.rows_remaining) {
    1036                 : #ifdef DONT_TOLERATE_BROKEN_GIFS
    1037                 :           mGIFStruct.state = gif_error;
    1038                 :           break;
    1039                 : #else
    1040                 :           /* This is an illegal GIF, but we remain tolerant. */
    1041               0 :           GETN(1, gif_sub_block);
    1042                 : #endif
    1043               0 :           if (mGIFStruct.count == GIF_TRAILER) {
    1044                 :             /* Found a terminator anyway, so consider the image done */
    1045               0 :             GETN(1, gif_done);
    1046               0 :             break;
    1047                 :           }
    1048                 :         }
    1049               7 :         GETN(mGIFStruct.count, gif_lzw);
    1050                 :       } else {
    1051                 :         /* See if there are any more images in this sequence. */
    1052               4 :         EndImageFrame();
    1053               4 :         GETN(1, gif_image_start);
    1054                 :       }
    1055              11 :       break;
    1056                 : 
    1057                 :     case gif_done:
    1058               0 :       PostDecodeDone();
    1059               0 :       mGIFOpen = false;
    1060               0 :       goto done;
    1061                 : 
    1062                 :     case gif_error:
    1063               0 :       PostDataError();
    1064               0 :       return;
    1065                 : 
    1066                 :     // We shouldn't ever get here.
    1067                 :     default:
    1068               0 :       break;
    1069                 :     }
    1070                 :   }
    1071                 : 
    1072                 :   // if an error state is set but no data remains, code flow reaches here
    1073               3 :   if (mGIFStruct.state == gif_error) {
    1074               0 :       PostDataError();
    1075               0 :       return;
    1076                 :   }
    1077                 :   
    1078                 :   // Copy the leftover into mGIFStruct.hold
    1079               3 :   mGIFStruct.bytes_in_hold = len;
    1080               3 :   if (len) {
    1081                 :     // Add what we have sofar to the block
    1082                 :     PRUint8* p = (mGIFStruct.state == gif_global_colormap) ? (PRUint8*)mGIFStruct.global_colormap :
    1083                 :                  (mGIFStruct.state == gif_image_colormap) ? (PRUint8*)mColormap :
    1084               1 :                  mGIFStruct.hold;
    1085               1 :     memcpy(p, buf, len);
    1086               1 :     mGIFStruct.bytes_to_consume -= len;
    1087                 :   }
    1088                 : 
    1089                 : // We want to flush before returning if we're on the first frame
    1090                 : done:
    1091               3 :   if (!mGIFStruct.images_decoded) {
    1092               1 :     FlushImageData();
    1093               1 :     mLastFlushedRow = mCurrentRow;
    1094               1 :     mLastFlushedPass = mCurrentPass;
    1095                 :   }
    1096                 : 
    1097               3 :   return;
    1098                 : }
    1099                 : 
    1100                 : Telemetry::ID
    1101               0 : nsGIFDecoder2::SpeedHistogram()
    1102                 : {
    1103               0 :   return Telemetry::IMAGE_DECODE_SPEED_GIF;
    1104                 : }
    1105                 : 
    1106                 : 
    1107                 : } // namespace image
    1108                 : } // namespace mozilla

Generated by: LCOV version 1.7