image/decoders/nsGIFDecoder2.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/image/decoders/nsGIFDecoder2.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1138 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + *
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +/*
    1.10 +The Graphics Interchange Format(c) is the copyright property of CompuServe
    1.11 +Incorporated. Only CompuServe Incorporated is authorized to define, redefine,
    1.12 +enhance, alter, modify or change in any way the definition of the format.
    1.13 +
    1.14 +CompuServe Incorporated hereby grants a limited, non-exclusive, royalty-free
    1.15 +license for the use of the Graphics Interchange Format(sm) in computer
    1.16 +software; computer software utilizing GIF(sm) must acknowledge ownership of the
    1.17 +Graphics Interchange Format and its Service Mark by CompuServe Incorporated, in
    1.18 +User and Technical Documentation. Computer software utilizing GIF, which is
    1.19 +distributed or may be distributed without User or Technical Documentation must
    1.20 +display to the screen or printer a message acknowledging ownership of the
    1.21 +Graphics Interchange Format and the Service Mark by CompuServe Incorporated; in
    1.22 +this case, the acknowledgement may be displayed in an opening screen or leading
    1.23 +banner, or a closing screen or trailing banner. A message such as the following
    1.24 +may be used:
    1.25 +
    1.26 +    "The Graphics Interchange Format(c) is the Copyright property of
    1.27 +    CompuServe Incorporated. GIF(sm) is a Service Mark property of
    1.28 +    CompuServe Incorporated."
    1.29 +
    1.30 +For further information, please contact :
    1.31 +
    1.32 +    CompuServe Incorporated
    1.33 +    Graphics Technology Department
    1.34 +    5000 Arlington Center Boulevard
    1.35 +    Columbus, Ohio  43220
    1.36 +    U. S. A.
    1.37 +
    1.38 +CompuServe Incorporated maintains a mailing list with all those individuals and
    1.39 +organizations who wish to receive copies of this document when it is corrected
    1.40 +or revised. This service is offered free of charge; please provide us with your
    1.41 +mailing address.
    1.42 +*/
    1.43 +
    1.44 +#include <stddef.h>
    1.45 +
    1.46 +#include "nsGIFDecoder2.h"
    1.47 +#include "nsIInputStream.h"
    1.48 +#include "RasterImage.h"
    1.49 +
    1.50 +#include "gfxColor.h"
    1.51 +#include "gfxPlatform.h"
    1.52 +#include "qcms.h"
    1.53 +#include <algorithm>
    1.54 +
    1.55 +namespace mozilla {
    1.56 +namespace image {
    1.57 +
    1.58 +/*
    1.59 + * GETN(n, s) requests at least 'n' bytes available from 'q', at start of state 's'
    1.60 + *
    1.61 + * Colormaps are directly copied in the resp. global_colormap or the local_colormap of the PAL image frame
    1.62 + * So a fixed buffer in gif_struct is good enough.
    1.63 + * This buffer is only needed to copy left-over data from one GifWrite call to the next
    1.64 + */
    1.65 +#define GETN(n,s)                      \
    1.66 +  PR_BEGIN_MACRO                       \
    1.67 +    mGIFStruct.bytes_to_consume = (n); \
    1.68 +    mGIFStruct.state = (s);            \
    1.69 +  PR_END_MACRO
    1.70 +
    1.71 +/* Get a 16-bit value stored in little-endian format */
    1.72 +#define GETINT16(p)   ((p)[1]<<8|(p)[0])
    1.73 +//////////////////////////////////////////////////////////////////////
    1.74 +// GIF Decoder Implementation
    1.75 +
    1.76 +nsGIFDecoder2::nsGIFDecoder2(RasterImage &aImage)
    1.77 +  : Decoder(aImage)
    1.78 +  , mCurrentRow(-1)
    1.79 +  , mLastFlushedRow(-1)
    1.80 +  , mOldColor(0)
    1.81 +  , mCurrentFrame(-1)
    1.82 +  , mCurrentPass(0)
    1.83 +  , mLastFlushedPass(0)
    1.84 +  , mGIFOpen(false)
    1.85 +  , mSawTransparency(false)
    1.86 +{
    1.87 +  // Clear out the structure, excluding the arrays
    1.88 +  memset(&mGIFStruct, 0, sizeof(mGIFStruct));
    1.89 +
    1.90 +  // Initialize as "animate once" in case no NETSCAPE2.0 extension is found
    1.91 +  mGIFStruct.loop_count = 1;
    1.92 +
    1.93 +  // Start with the version (GIF89a|GIF87a)
    1.94 +  mGIFStruct.state = gif_type;
    1.95 +  mGIFStruct.bytes_to_consume = 6;
    1.96 +}
    1.97 +
    1.98 +nsGIFDecoder2::~nsGIFDecoder2()
    1.99 +{
   1.100 +  moz_free(mGIFStruct.local_colormap);
   1.101 +  moz_free(mGIFStruct.hold);
   1.102 +}
   1.103 +
   1.104 +void
   1.105 +nsGIFDecoder2::FinishInternal()
   1.106 +{
   1.107 +  NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call FinishInternal after error!");
   1.108 +
   1.109 +  // If the GIF got cut off, handle it anyway
   1.110 +  if (!IsSizeDecode() && mGIFOpen) {
   1.111 +    if (mCurrentFrame == mGIFStruct.images_decoded)
   1.112 +      EndImageFrame();
   1.113 +    PostDecodeDone(mGIFStruct.loop_count - 1);
   1.114 +    mGIFOpen = false;
   1.115 +  }
   1.116 +}
   1.117 +
   1.118 +// Push any new rows according to mCurrentPass/mLastFlushedPass and
   1.119 +// mCurrentRow/mLastFlushedRow.  Note: caller is responsible for
   1.120 +// updating mlastFlushed{Row,Pass}.
   1.121 +void
   1.122 +nsGIFDecoder2::FlushImageData(uint32_t fromRow, uint32_t rows)
   1.123 +{
   1.124 +  nsIntRect r(mGIFStruct.x_offset, mGIFStruct.y_offset + fromRow, mGIFStruct.width, rows);
   1.125 +  PostInvalidation(r);
   1.126 +}
   1.127 +
   1.128 +void
   1.129 +nsGIFDecoder2::FlushImageData()
   1.130 +{
   1.131 +  switch (mCurrentPass - mLastFlushedPass) {
   1.132 +    case 0:  // same pass
   1.133 +      if (mCurrentRow - mLastFlushedRow)
   1.134 +        FlushImageData(mLastFlushedRow + 1, mCurrentRow - mLastFlushedRow);
   1.135 +      break;
   1.136 +  
   1.137 +    case 1:  // one pass on - need to handle bottom & top rects
   1.138 +      FlushImageData(0, mCurrentRow + 1);
   1.139 +      FlushImageData(mLastFlushedRow + 1, mGIFStruct.height - (mLastFlushedRow + 1));
   1.140 +      break;
   1.141 +
   1.142 +    default:   // more than one pass on - push the whole frame
   1.143 +      FlushImageData(0, mGIFStruct.height);
   1.144 +  }
   1.145 +}
   1.146 +
   1.147 +//******************************************************************************
   1.148 +// GIF decoder callback methods. Part of public API for GIF2
   1.149 +//******************************************************************************
   1.150 +
   1.151 +//******************************************************************************
   1.152 +void nsGIFDecoder2::BeginGIF()
   1.153 +{
   1.154 +  if (mGIFOpen)
   1.155 +    return;
   1.156 +
   1.157 +  mGIFOpen = true;
   1.158 +
   1.159 +  PostSize(mGIFStruct.screen_width, mGIFStruct.screen_height);
   1.160 +}
   1.161 +
   1.162 +//******************************************************************************
   1.163 +void nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
   1.164 +{
   1.165 +  gfxImageFormat format;
   1.166 +  if (mGIFStruct.is_transparent)
   1.167 +    format = gfxImageFormat::ARGB32;
   1.168 +  else
   1.169 +    format = gfxImageFormat::RGB24;
   1.170 +
   1.171 +  MOZ_ASSERT(HasSize());
   1.172 +
   1.173 +  // Use correct format, RGB for first frame, PAL for following frames
   1.174 +  // and include transparency to allow for optimization of opaque images
   1.175 +  if (mGIFStruct.images_decoded) {
   1.176 +    // Image data is stored with original depth and palette
   1.177 +    NeedNewFrame(mGIFStruct.images_decoded, mGIFStruct.x_offset,
   1.178 +                 mGIFStruct.y_offset, mGIFStruct.width, mGIFStruct.height,
   1.179 +                 format, aDepth);
   1.180 +  }
   1.181 +
   1.182 +  // Our first full frame is automatically created by the image decoding
   1.183 +  // infrastructure. Just use it as long as it matches up.
   1.184 +  else if (!GetCurrentFrame()->GetRect().IsEqualEdges(nsIntRect(mGIFStruct.x_offset,
   1.185 +                                                                mGIFStruct.y_offset,
   1.186 +                                                                mGIFStruct.width,
   1.187 +                                                                mGIFStruct.height))) {
   1.188 +    // Regardless of depth of input, image is decoded into 24bit RGB
   1.189 +    NeedNewFrame(mGIFStruct.images_decoded, mGIFStruct.x_offset,
   1.190 +                 mGIFStruct.y_offset, mGIFStruct.width, mGIFStruct.height,
   1.191 +                 format);
   1.192 +  } else {
   1.193 +    // Our preallocated frame matches up, with the possible exception of alpha.
   1.194 +    if (format == gfxImageFormat::RGB24) {
   1.195 +      GetCurrentFrame()->SetHasNoAlpha();
   1.196 +    }
   1.197 +  }
   1.198 +
   1.199 +  mCurrentFrame = mGIFStruct.images_decoded;
   1.200 +}
   1.201 +
   1.202 +
   1.203 +//******************************************************************************
   1.204 +void nsGIFDecoder2::EndImageFrame()
   1.205 +{
   1.206 +  FrameBlender::FrameAlpha alpha = FrameBlender::kFrameHasAlpha;
   1.207 +
   1.208 +  // First flush all pending image data 
   1.209 +  if (!mGIFStruct.images_decoded) {
   1.210 +    // Only need to flush first frame
   1.211 +    FlushImageData();
   1.212 +
   1.213 +    // If the first frame is smaller in height than the entire image, send an
   1.214 +    // invalidation for the area it does not have data for.
   1.215 +    // This will clear the remaining bits of the placeholder. (Bug 37589)
   1.216 +    const uint32_t realFrameHeight = mGIFStruct.height + mGIFStruct.y_offset;
   1.217 +    if (realFrameHeight < mGIFStruct.screen_height) {
   1.218 +      nsIntRect r(0, realFrameHeight,
   1.219 +                  mGIFStruct.screen_width,
   1.220 +                  mGIFStruct.screen_height - realFrameHeight);
   1.221 +      PostInvalidation(r);
   1.222 +    }
   1.223 +    // This transparency check is only valid for first frame
   1.224 +    if (mGIFStruct.is_transparent && !mSawTransparency) {
   1.225 +      alpha = FrameBlender::kFrameOpaque;
   1.226 +    }
   1.227 +  }
   1.228 +  mCurrentRow = mLastFlushedRow = -1;
   1.229 +  mCurrentPass = mLastFlushedPass = 0;
   1.230 +
   1.231 +  // Only add frame if we have any rows at all
   1.232 +  if (mGIFStruct.rows_remaining != mGIFStruct.height) {
   1.233 +    if (mGIFStruct.rows_remaining && mGIFStruct.images_decoded) {
   1.234 +      // Clear the remaining rows (only needed for the animation frames)
   1.235 +      uint8_t *rowp = mImageData + ((mGIFStruct.height - mGIFStruct.rows_remaining) * mGIFStruct.width);
   1.236 +      memset(rowp, 0, mGIFStruct.rows_remaining * mGIFStruct.width);
   1.237 +    }
   1.238 +  }
   1.239 +
   1.240 +  // Unconditionally increment images_decoded, because we unconditionally
   1.241 +  // append frames in BeginImageFrame(). This ensures that images_decoded
   1.242 +  // always refers to the frame in mImage we're currently decoding,
   1.243 +  // even if some of them weren't decoded properly and thus are blank.
   1.244 +  mGIFStruct.images_decoded++;
   1.245 +
   1.246 +  // Tell the superclass we finished a frame
   1.247 +  PostFrameStop(alpha,
   1.248 +                FrameBlender::FrameDisposalMethod(mGIFStruct.disposal_method),
   1.249 +                mGIFStruct.delay_time);
   1.250 +
   1.251 +  // Reset the transparent pixel
   1.252 +  if (mOldColor) {
   1.253 +    mColormap[mGIFStruct.tpixel] = mOldColor;
   1.254 +    mOldColor = 0;
   1.255 +  }
   1.256 +
   1.257 +  mCurrentFrame = -1;
   1.258 +}
   1.259 +
   1.260 +
   1.261 +//******************************************************************************
   1.262 +// Send the data to the display front-end.
   1.263 +uint32_t nsGIFDecoder2::OutputRow()
   1.264 +{
   1.265 +  int drow_start, drow_end;
   1.266 +  drow_start = drow_end = mGIFStruct.irow;
   1.267 +
   1.268 +  /* Protect against too much image data */
   1.269 +  if ((unsigned)drow_start >= mGIFStruct.height) {
   1.270 +    NS_WARNING("GIF2.cpp::OutputRow - too much image data");
   1.271 +    return 0;
   1.272 +  }
   1.273 +
   1.274 +  if (!mGIFStruct.images_decoded) {
   1.275 +    /*
   1.276 +     * Haeberli-inspired hack for interlaced GIFs: Replicate lines while
   1.277 +     * displaying to diminish the "venetian-blind" effect as the image is
   1.278 +     * loaded. Adjust pixel vertical positions to avoid the appearance of the
   1.279 +     * image crawling up the screen as successive passes are drawn.
   1.280 +     */
   1.281 +    if (mGIFStruct.progressive_display && mGIFStruct.interlaced && (mGIFStruct.ipass < 4)) {
   1.282 +      /* ipass = 1,2,3 results in resp. row_dup = 7,3,1 and row_shift = 3,1,0 */
   1.283 +      const uint32_t row_dup = 15 >> mGIFStruct.ipass;
   1.284 +      const uint32_t row_shift = row_dup >> 1;
   1.285 +  
   1.286 +      drow_start -= row_shift;
   1.287 +      drow_end = drow_start + row_dup;
   1.288 +  
   1.289 +      /* Extend if bottom edge isn't covered because of the shift upward. */
   1.290 +      if (((mGIFStruct.height - 1) - drow_end) <= row_shift)
   1.291 +        drow_end = mGIFStruct.height - 1;
   1.292 +  
   1.293 +      /* Clamp first and last rows to upper and lower edge of image. */
   1.294 +      if (drow_start < 0)
   1.295 +        drow_start = 0;
   1.296 +      if ((unsigned)drow_end >= mGIFStruct.height)
   1.297 +        drow_end = mGIFStruct.height - 1;
   1.298 +    }
   1.299 +
   1.300 +    // Row to process
   1.301 +    const uint32_t bpr = sizeof(uint32_t) * mGIFStruct.width; 
   1.302 +    uint8_t *rowp = mImageData + (mGIFStruct.irow * bpr);
   1.303 +
   1.304 +    // Convert color indices to Cairo pixels
   1.305 +    uint8_t *from = rowp + mGIFStruct.width;
   1.306 +    uint32_t *to = ((uint32_t*)rowp) + mGIFStruct.width;
   1.307 +    uint32_t *cmap = mColormap;
   1.308 +    for (uint32_t c = mGIFStruct.width; c > 0; c--) {
   1.309 +      *--to = cmap[*--from];
   1.310 +    }
   1.311 +  
   1.312 +    // check for alpha (only for first frame)
   1.313 +    if (mGIFStruct.is_transparent && !mSawTransparency) {
   1.314 +      const uint32_t *rgb = (uint32_t*)rowp;
   1.315 +      for (uint32_t i = mGIFStruct.width; i > 0; i--) {
   1.316 +        if (*rgb++ == 0) {
   1.317 +          mSawTransparency = true;
   1.318 +          break;
   1.319 +        }
   1.320 +      }
   1.321 +    }
   1.322 +
   1.323 +    // Duplicate rows
   1.324 +    if (drow_end > drow_start) {
   1.325 +      // irow is the current row filled
   1.326 +      for (int r = drow_start; r <= drow_end; r++) {
   1.327 +        if (r != int(mGIFStruct.irow)) {
   1.328 +          memcpy(mImageData + (r * bpr), rowp, bpr);
   1.329 +        }
   1.330 +      }
   1.331 +    }
   1.332 +  }
   1.333 +
   1.334 +  mCurrentRow = drow_end;
   1.335 +  mCurrentPass = mGIFStruct.ipass;
   1.336 +  if (mGIFStruct.ipass == 1)
   1.337 +    mLastFlushedPass = mGIFStruct.ipass;   // interlaced starts at 1
   1.338 +
   1.339 +  if (!mGIFStruct.interlaced) {
   1.340 +    mGIFStruct.irow++;
   1.341 +  } else {
   1.342 +    static const uint8_t kjump[5] = { 1, 8, 8, 4, 2 };
   1.343 +    do {
   1.344 +      // Row increments resp. per 8,8,4,2 rows
   1.345 +      mGIFStruct.irow += kjump[mGIFStruct.ipass];
   1.346 +      if (mGIFStruct.irow >= mGIFStruct.height) {
   1.347 +        // Next pass starts resp. at row 4,2,1,0
   1.348 +        mGIFStruct.irow = 8 >> mGIFStruct.ipass;
   1.349 +        mGIFStruct.ipass++;
   1.350 +      }
   1.351 +    } while (mGIFStruct.irow >= mGIFStruct.height);
   1.352 +  }
   1.353 +
   1.354 +  return --mGIFStruct.rows_remaining;
   1.355 +}
   1.356 +
   1.357 +//******************************************************************************
   1.358 +/* Perform Lempel-Ziv-Welch decoding */
   1.359 +bool
   1.360 +nsGIFDecoder2::DoLzw(const uint8_t *q)
   1.361 +{
   1.362 +  if (!mGIFStruct.rows_remaining)
   1.363 +    return true;
   1.364 +
   1.365 +  /* Copy all the decoder state variables into locals so the compiler
   1.366 +   * won't worry about them being aliased.  The locals will be homed
   1.367 +   * back into the GIF decoder structure when we exit.
   1.368 +   */
   1.369 +  int avail       = mGIFStruct.avail;
   1.370 +  int bits        = mGIFStruct.bits;
   1.371 +  int codesize    = mGIFStruct.codesize;
   1.372 +  int codemask    = mGIFStruct.codemask;
   1.373 +  int count       = mGIFStruct.count;
   1.374 +  int oldcode     = mGIFStruct.oldcode;
   1.375 +  const int clear_code = ClearCode();
   1.376 +  uint8_t firstchar = mGIFStruct.firstchar;
   1.377 +  int32_t datum     = mGIFStruct.datum;
   1.378 +  uint16_t *prefix  = mGIFStruct.prefix;
   1.379 +  uint8_t *stackp   = mGIFStruct.stackp;
   1.380 +  uint8_t *suffix   = mGIFStruct.suffix;
   1.381 +  uint8_t *stack    = mGIFStruct.stack;
   1.382 +  uint8_t *rowp     = mGIFStruct.rowp;
   1.383 +
   1.384 +  uint32_t bpr = mGIFStruct.width;
   1.385 +  if (!mGIFStruct.images_decoded) 
   1.386 +    bpr *= sizeof(uint32_t);
   1.387 +  uint8_t *rowend   = mImageData + (bpr * mGIFStruct.irow) + mGIFStruct.width;
   1.388 +
   1.389 +#define OUTPUT_ROW()                                        \
   1.390 +  PR_BEGIN_MACRO                                            \
   1.391 +    if (!OutputRow())                                       \
   1.392 +      goto END;                                             \
   1.393 +    rowp = mImageData + mGIFStruct.irow * bpr;              \
   1.394 +    rowend = rowp + mGIFStruct.width;                       \
   1.395 +  PR_END_MACRO
   1.396 +
   1.397 +  for (const uint8_t* ch = q; count-- > 0; ch++)
   1.398 +  {
   1.399 +    /* Feed the next byte into the decoder's 32-bit input buffer. */
   1.400 +    datum += ((int32_t) *ch) << bits;
   1.401 +    bits += 8;
   1.402 +
   1.403 +    /* Check for underflow of decoder's 32-bit input buffer. */
   1.404 +    while (bits >= codesize)
   1.405 +    {
   1.406 +      /* Get the leading variable-length symbol from the data stream */
   1.407 +      int code = datum & codemask;
   1.408 +      datum >>= codesize;
   1.409 +      bits -= codesize;
   1.410 +
   1.411 +      /* Reset the dictionary to its original state, if requested */
   1.412 +      if (code == clear_code) {
   1.413 +        codesize = mGIFStruct.datasize + 1;
   1.414 +        codemask = (1 << codesize) - 1;
   1.415 +        avail = clear_code + 2;
   1.416 +        oldcode = -1;
   1.417 +        continue;
   1.418 +      }
   1.419 +
   1.420 +      /* Check for explicit end-of-stream code */
   1.421 +      if (code == (clear_code + 1)) {
   1.422 +        /* end-of-stream should only appear after all image data */
   1.423 +        return (mGIFStruct.rows_remaining == 0);
   1.424 +      }
   1.425 +
   1.426 +      if (oldcode == -1) {
   1.427 +        if (code >= MAX_BITS)
   1.428 +          return false;
   1.429 +        *rowp++ = suffix[code] & mColorMask; // ensure index is within colormap
   1.430 +        if (rowp == rowend)
   1.431 +          OUTPUT_ROW();
   1.432 +
   1.433 +        firstchar = oldcode = code;
   1.434 +        continue;
   1.435 +      }
   1.436 +
   1.437 +      int incode = code;
   1.438 +      if (code >= avail) {
   1.439 +        *stackp++ = firstchar;
   1.440 +        code = oldcode;
   1.441 +
   1.442 +        if (stackp >= stack + MAX_BITS)
   1.443 +          return false;
   1.444 +      }
   1.445 +
   1.446 +      while (code >= clear_code)
   1.447 +      {
   1.448 +        if ((code >= MAX_BITS) || (code == prefix[code]))
   1.449 +          return false;
   1.450 +
   1.451 +        *stackp++ = suffix[code];
   1.452 +        code = prefix[code];
   1.453 +
   1.454 +        if (stackp == stack + MAX_BITS)
   1.455 +          return false;
   1.456 +      }
   1.457 +
   1.458 +      *stackp++ = firstchar = suffix[code];
   1.459 +
   1.460 +      /* Define a new codeword in the dictionary. */
   1.461 +      if (avail < 4096) {
   1.462 +        prefix[avail] = oldcode;
   1.463 +        suffix[avail] = firstchar;
   1.464 +        avail++;
   1.465 +
   1.466 +        /* If we've used up all the codewords of a given length
   1.467 +         * increase the length of codewords by one bit, but don't
   1.468 +         * exceed the specified maximum codeword size of 12 bits.
   1.469 +         */
   1.470 +        if (((avail & codemask) == 0) && (avail < 4096)) {
   1.471 +          codesize++;
   1.472 +          codemask += avail;
   1.473 +        }
   1.474 +      }
   1.475 +      oldcode = incode;
   1.476 +
   1.477 +      /* Copy the decoded data out to the scanline buffer. */
   1.478 +      do {
   1.479 +        *rowp++ = *--stackp & mColorMask; // ensure index is within colormap
   1.480 +        if (rowp == rowend)
   1.481 +          OUTPUT_ROW();
   1.482 +      } while (stackp > stack);
   1.483 +    }
   1.484 +  }
   1.485 +
   1.486 +  END:
   1.487 +
   1.488 +  /* Home the local copies of the GIF decoder state variables */
   1.489 +  mGIFStruct.avail = avail;
   1.490 +  mGIFStruct.bits = bits;
   1.491 +  mGIFStruct.codesize = codesize;
   1.492 +  mGIFStruct.codemask = codemask;
   1.493 +  mGIFStruct.count = count;
   1.494 +  mGIFStruct.oldcode = oldcode;
   1.495 +  mGIFStruct.firstchar = firstchar;
   1.496 +  mGIFStruct.datum = datum;
   1.497 +  mGIFStruct.stackp = stackp;
   1.498 +  mGIFStruct.rowp = rowp;
   1.499 +
   1.500 +  return true;
   1.501 +}
   1.502 +
   1.503 +/** 
   1.504 + * Expand the colormap from RGB to Packed ARGB as needed by Cairo.
   1.505 + * And apply any LCMS transformation.
   1.506 + */
   1.507 +static void ConvertColormap(uint32_t *aColormap, uint32_t aColors)
   1.508 +{
   1.509 +  // Apply CMS transformation if enabled and available
   1.510 +  if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
   1.511 +    qcms_transform *transform = gfxPlatform::GetCMSRGBTransform();
   1.512 +    if (transform)
   1.513 +      qcms_transform_data(transform, aColormap, aColormap, aColors);
   1.514 +  }
   1.515 +  // Convert from the GIF's RGB format to the Cairo format.
   1.516 +  // Work from end to begin, because of the in-place expansion
   1.517 +  uint8_t *from = ((uint8_t *)aColormap) + 3 * aColors;
   1.518 +  uint32_t *to = aColormap + aColors;
   1.519 +
   1.520 +  // Convert color entries to Cairo format
   1.521 +
   1.522 +  // set up for loops below
   1.523 +  if (!aColors) return;
   1.524 +  uint32_t c = aColors;
   1.525 +
   1.526 +  // copy as bytes until source pointer is 32-bit-aligned
   1.527 +  // NB: can't use 32-bit reads, they might read off the end of the buffer
   1.528 +  for (; (NS_PTR_TO_UINT32(from) & 0x3) && c; --c) {
   1.529 +    from -= 3;
   1.530 +    *--to = gfxPackedPixel(0xFF, from[0], from[1], from[2]);
   1.531 +  }
   1.532 +
   1.533 +  // bulk copy of pixels.
   1.534 +  while (c >= 4) {
   1.535 +    from -= 12;
   1.536 +    to   -=  4;
   1.537 +    c    -=  4;
   1.538 +    GFX_BLOCK_RGB_TO_FRGB(from,to);
   1.539 +  }
   1.540 +
   1.541 +  // copy remaining pixel(s)
   1.542 +  // NB: can't use 32-bit reads, they might read off the end of the buffer
   1.543 +  while (c--) {
   1.544 +    from -= 3;
   1.545 +    *--to = gfxPackedPixel(0xFF, from[0], from[1], from[2]);
   1.546 +  }
   1.547 +}
   1.548 +
   1.549 +void
   1.550 +nsGIFDecoder2::WriteInternal(const char *aBuffer, uint32_t aCount, DecodeStrategy)
   1.551 +{
   1.552 +  NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!");
   1.553 +
   1.554 +  // These variables changed names, and renaming would make a much bigger patch :(
   1.555 +  const uint8_t *buf = (const uint8_t *)aBuffer;
   1.556 +  uint32_t len = aCount;
   1.557 +
   1.558 +  const uint8_t *q = buf;
   1.559 +
   1.560 +  // Add what we have sofar to the block
   1.561 +  // If previous call to me left something in the hold first complete current block
   1.562 +  // Or if we are filling the colormaps, first complete the colormap
   1.563 +  uint8_t* p = (mGIFStruct.state == gif_global_colormap) ? (uint8_t*)mGIFStruct.global_colormap :
   1.564 +               (mGIFStruct.state == gif_image_colormap) ? (uint8_t*)mColormap :
   1.565 +               (mGIFStruct.bytes_in_hold) ? mGIFStruct.hold : nullptr;
   1.566 +
   1.567 +  if (len == 0 && buf == nullptr) {
   1.568 +    // We've just gotten the frame we asked for. Time to use the data we
   1.569 +    // stashed away.
   1.570 +    len = mGIFStruct.bytes_in_hold;
   1.571 +    q = buf = p;
   1.572 +  } else if (p) {
   1.573 +    // Add what we have sofar to the block
   1.574 +    uint32_t l = std::min(len, mGIFStruct.bytes_to_consume);
   1.575 +    memcpy(p+mGIFStruct.bytes_in_hold, buf, l);
   1.576 +
   1.577 +    if (l < mGIFStruct.bytes_to_consume) {
   1.578 +      // Not enough in 'buf' to complete current block, get more
   1.579 +      mGIFStruct.bytes_in_hold += l;
   1.580 +      mGIFStruct.bytes_to_consume -= l;
   1.581 +      return;
   1.582 +    }
   1.583 +    // Point 'q' to complete block in hold (or in colormap)
   1.584 +    q = p;
   1.585 +  }
   1.586 +
   1.587 +  // Invariant:
   1.588 +  //    'q' is start of current to be processed block (hold, colormap or buf)
   1.589 +  //    'bytes_to_consume' is number of bytes to consume from 'buf'
   1.590 +  //    'buf' points to the bytes to be consumed from the input buffer
   1.591 +  //    'len' is number of bytes left in input buffer from position 'buf'.
   1.592 +  //    At entrance of the for loop will 'buf' will be moved 'bytes_to_consume'
   1.593 +  //    to point to next buffer, 'len' is adjusted accordingly.
   1.594 +  //    So that next round in for loop, q gets pointed to the next buffer.
   1.595 +
   1.596 +  for (;len >= mGIFStruct.bytes_to_consume; q=buf, mGIFStruct.bytes_in_hold = 0) {
   1.597 +    // Eat the current block from the buffer, q keeps pointed at current block
   1.598 +    buf += mGIFStruct.bytes_to_consume;
   1.599 +    len -= mGIFStruct.bytes_to_consume;
   1.600 +
   1.601 +    switch (mGIFStruct.state)
   1.602 +    {
   1.603 +    case gif_lzw:
   1.604 +      if (!DoLzw(q)) {
   1.605 +        mGIFStruct.state = gif_error;
   1.606 +        break;
   1.607 +      }
   1.608 +      GETN(1, gif_sub_block);
   1.609 +      break;
   1.610 +
   1.611 +    case gif_lzw_start:
   1.612 +    {
   1.613 +      // Make sure the transparent pixel is transparent in the colormap
   1.614 +      if (mGIFStruct.is_transparent) {
   1.615 +        // Save old value so we can restore it later
   1.616 +        if (mColormap == mGIFStruct.global_colormap)
   1.617 +            mOldColor = mColormap[mGIFStruct.tpixel];
   1.618 +        mColormap[mGIFStruct.tpixel] = 0;
   1.619 +      }
   1.620 +
   1.621 +      /* Initialize LZW parser/decoder */
   1.622 +      mGIFStruct.datasize = *q;
   1.623 +      const int clear_code = ClearCode();
   1.624 +      if (mGIFStruct.datasize > MAX_LZW_BITS ||
   1.625 +          clear_code >= MAX_BITS) {
   1.626 +        mGIFStruct.state = gif_error;
   1.627 +        break;
   1.628 +      }
   1.629 +
   1.630 +      mGIFStruct.avail = clear_code + 2;
   1.631 +      mGIFStruct.oldcode = -1;
   1.632 +      mGIFStruct.codesize = mGIFStruct.datasize + 1;
   1.633 +      mGIFStruct.codemask = (1 << mGIFStruct.codesize) - 1;
   1.634 +      mGIFStruct.datum = mGIFStruct.bits = 0;
   1.635 +
   1.636 +      /* init the tables */
   1.637 +      for (int i = 0; i < clear_code; i++)
   1.638 +        mGIFStruct.suffix[i] = i;
   1.639 +
   1.640 +      mGIFStruct.stackp = mGIFStruct.stack;
   1.641 +
   1.642 +      GETN(1, gif_sub_block);
   1.643 +    }
   1.644 +    break;
   1.645 +
   1.646 +    /* All GIF files begin with "GIF87a" or "GIF89a" */
   1.647 +    case gif_type:
   1.648 +      if (!strncmp((char*)q, "GIF89a", 6)) {
   1.649 +        mGIFStruct.version = 89;
   1.650 +      } else if (!strncmp((char*)q, "GIF87a", 6)) {
   1.651 +        mGIFStruct.version = 87;
   1.652 +      } else {
   1.653 +        mGIFStruct.state = gif_error;
   1.654 +        break;
   1.655 +      }
   1.656 +      GETN(7, gif_global_header);
   1.657 +      break;
   1.658 +
   1.659 +    case gif_global_header:
   1.660 +      /* This is the height and width of the "screen" or
   1.661 +       * frame into which images are rendered.  The
   1.662 +       * individual images can be smaller than the
   1.663 +       * screen size and located with an origin anywhere
   1.664 +       * within the screen.
   1.665 +       */
   1.666 +
   1.667 +      mGIFStruct.screen_width = GETINT16(q);
   1.668 +      mGIFStruct.screen_height = GETINT16(q + 2);
   1.669 +      mGIFStruct.global_colormap_depth = (q[4]&0x07) + 1;
   1.670 +
   1.671 +      if (IsSizeDecode()) {
   1.672 +        MOZ_ASSERT(!mGIFOpen, "Gif should not be open at this point");
   1.673 +        PostSize(mGIFStruct.screen_width, mGIFStruct.screen_height);
   1.674 +        return;
   1.675 +      }
   1.676 +
   1.677 +      // screen_bgcolor is not used
   1.678 +      //mGIFStruct.screen_bgcolor = q[5];
   1.679 +      // q[6] = Pixel Aspect Ratio
   1.680 +      //   Not used
   1.681 +      //   float aspect = (float)((q[6] + 15) / 64.0);
   1.682 +
   1.683 +      if (q[4] & 0x80) { /* global map */
   1.684 +        // Get the global colormap
   1.685 +        const uint32_t size = (3 << mGIFStruct.global_colormap_depth);
   1.686 +        if (len < size) {
   1.687 +          // Use 'hold' pattern to get the global colormap
   1.688 +          GETN(size, gif_global_colormap);
   1.689 +          break;
   1.690 +        }
   1.691 +        // Copy everything, go to colormap state to do CMS correction
   1.692 +        memcpy(mGIFStruct.global_colormap, buf, size);
   1.693 +        buf += size;
   1.694 +        len -= size;
   1.695 +        GETN(0, gif_global_colormap);
   1.696 +        break;
   1.697 +      }
   1.698 +
   1.699 +      GETN(1, gif_image_start);
   1.700 +      break;
   1.701 +
   1.702 +    case gif_global_colormap:
   1.703 +      // Everything is already copied into global_colormap
   1.704 +      // Convert into Cairo colors including CMS transformation
   1.705 +      ConvertColormap(mGIFStruct.global_colormap, 1<<mGIFStruct.global_colormap_depth);
   1.706 +      GETN(1, gif_image_start);
   1.707 +      break;
   1.708 +
   1.709 +    case gif_image_start:
   1.710 +      switch (*q) {
   1.711 +        case GIF_TRAILER:
   1.712 +          mGIFStruct.state = gif_done;
   1.713 +          break;
   1.714 +
   1.715 +        case GIF_EXTENSION_INTRODUCER:
   1.716 +          GETN(2, gif_extension);
   1.717 +          break;
   1.718 +
   1.719 +        case GIF_IMAGE_SEPARATOR:
   1.720 +          GETN(9, gif_image_header);
   1.721 +          break;
   1.722 +
   1.723 +        default:
   1.724 +          /* If we get anything other than GIF_IMAGE_SEPARATOR, 
   1.725 +           * GIF_EXTENSION_INTRODUCER, or GIF_TRAILER, there is extraneous data
   1.726 +           * between blocks. The GIF87a spec tells us to keep reading
   1.727 +           * until we find an image separator, but GIF89a says such
   1.728 +           * a file is corrupt. We follow GIF89a and bail out. */
   1.729 +          if (mGIFStruct.images_decoded > 0) {
   1.730 +            /* The file is corrupt, but one or more images have
   1.731 +             * been decoded correctly. In this case, we proceed
   1.732 +             * as if the file were correctly terminated and set
   1.733 +             * the state to gif_done, so the GIF will display.
   1.734 +             */
   1.735 +            mGIFStruct.state = gif_done;
   1.736 +          } else {
   1.737 +            /* No images decoded, there is nothing to display. */
   1.738 +            mGIFStruct.state = gif_error;
   1.739 +          }
   1.740 +      }
   1.741 +      break;
   1.742 +
   1.743 +    case gif_extension:
   1.744 +      mGIFStruct.bytes_to_consume = q[1];
   1.745 +      if (mGIFStruct.bytes_to_consume) {
   1.746 +        switch (*q) {
   1.747 +        case GIF_GRAPHIC_CONTROL_LABEL:
   1.748 +          // The GIF spec mandates that the GIFControlExtension header block length is 4 bytes,
   1.749 +          // and the parser for this block reads 4 bytes, so we must enforce that the buffer
   1.750 +          // contains at least this many bytes. If the GIF specifies a different length, we
   1.751 +          // allow that, so long as it's larger; the additional data will simply be ignored.
   1.752 +          mGIFStruct.state = gif_control_extension;
   1.753 +          mGIFStruct.bytes_to_consume = std::max(mGIFStruct.bytes_to_consume, 4u);
   1.754 +          break;
   1.755 +
   1.756 +        // The GIF spec also specifies the lengths of the following two extensions' headers
   1.757 +        // (as 12 and 11 bytes, respectively). Because we ignore the plain text extension entirely
   1.758 +        // and sanity-check the actual length of the application extension header before reading it,
   1.759 +        // we allow GIFs to deviate from these values in either direction. This is important for
   1.760 +        // real-world compatibility, as GIFs in the wild exist with application extension headers
   1.761 +        // that are both shorter and longer than 11 bytes.
   1.762 +        case GIF_APPLICATION_EXTENSION_LABEL:
   1.763 +          mGIFStruct.state = gif_application_extension;
   1.764 +          break;
   1.765 +
   1.766 +        case GIF_PLAIN_TEXT_LABEL:
   1.767 +          mGIFStruct.state = gif_skip_block;
   1.768 +          break;
   1.769 +
   1.770 +        case GIF_COMMENT_LABEL:
   1.771 +          mGIFStruct.state = gif_consume_comment;
   1.772 +          break;
   1.773 +
   1.774 +        default:
   1.775 +          mGIFStruct.state = gif_skip_block;
   1.776 +        }
   1.777 +      } else {
   1.778 +        GETN(1, gif_image_start);
   1.779 +      }
   1.780 +      break;
   1.781 +
   1.782 +    case gif_consume_block:
   1.783 +      if (!*q)
   1.784 +        GETN(1, gif_image_start);
   1.785 +      else
   1.786 +        GETN(*q, gif_skip_block);
   1.787 +      break;
   1.788 +
   1.789 +    case gif_skip_block:
   1.790 +      GETN(1, gif_consume_block);
   1.791 +      break;
   1.792 +
   1.793 +    case gif_control_extension:
   1.794 +      mGIFStruct.is_transparent = *q & 0x1;
   1.795 +      mGIFStruct.tpixel = q[3];
   1.796 +      mGIFStruct.disposal_method = ((*q) >> 2) & 0x7;
   1.797 +      // Some specs say 3rd bit (value 4), other specs say value 3
   1.798 +      // Let's choose 3 (the more popular)
   1.799 +      if (mGIFStruct.disposal_method == 4)
   1.800 +        mGIFStruct.disposal_method = 3;
   1.801 +      mGIFStruct.delay_time = GETINT16(q + 1) * 10;
   1.802 +      GETN(1, gif_consume_block);
   1.803 +      break;
   1.804 +
   1.805 +    case gif_comment_extension:
   1.806 +      if (*q)
   1.807 +        GETN(*q, gif_consume_comment);
   1.808 +      else
   1.809 +        GETN(1, gif_image_start);
   1.810 +      break;
   1.811 +
   1.812 +    case gif_consume_comment:
   1.813 +      GETN(1, gif_comment_extension);
   1.814 +      break;
   1.815 +
   1.816 +    case gif_application_extension:
   1.817 +      /* Check for netscape application extension */
   1.818 +      if (mGIFStruct.bytes_to_consume == 11 &&
   1.819 +          (!strncmp((char*)q, "NETSCAPE2.0", 11) ||
   1.820 +           !strncmp((char*)q, "ANIMEXTS1.0", 11)))
   1.821 +        GETN(1, gif_netscape_extension_block);
   1.822 +      else
   1.823 +        GETN(1, gif_consume_block);
   1.824 +      break;
   1.825 +
   1.826 +    /* Netscape-specific GIF extension: animation looping */
   1.827 +    case gif_netscape_extension_block:
   1.828 +      if (*q)
   1.829 +        // We might need to consume 3 bytes in
   1.830 +        // gif_consume_netscape_extension, so make sure we have at least that.
   1.831 +        GETN(std::max(3, static_cast<int>(*q)), gif_consume_netscape_extension);
   1.832 +      else
   1.833 +        GETN(1, gif_image_start);
   1.834 +      break;
   1.835 +
   1.836 +    /* Parse netscape-specific application extensions */
   1.837 +    case gif_consume_netscape_extension:
   1.838 +      switch (q[0] & 7) {
   1.839 +        case 1:
   1.840 +          /* Loop entire animation specified # of times.  Only read the
   1.841 +             loop count during the first iteration. */
   1.842 +          mGIFStruct.loop_count = GETINT16(q + 1);
   1.843 +          GETN(1, gif_netscape_extension_block);
   1.844 +          break;
   1.845 +
   1.846 +        case 2:
   1.847 +          /* Wait for specified # of bytes to enter buffer */
   1.848 +          // Don't do this, this extension doesn't exist (isn't used at all)
   1.849 +          // and doesn't do anything, as our streaming/buffering takes care of it all...
   1.850 +          // See: http://semmix.pl/color/exgraf/eeg24.htm
   1.851 +          GETN(1, gif_netscape_extension_block);
   1.852 +          break;
   1.853 +
   1.854 +        default:
   1.855 +          // 0,3-7 are yet to be defined netscape extension codes
   1.856 +          mGIFStruct.state = gif_error;
   1.857 +      }
   1.858 +      break;
   1.859 +
   1.860 +    case gif_image_header:
   1.861 +    {
   1.862 +      /* Get image offsets, with respect to the screen origin */
   1.863 +      mGIFStruct.x_offset = GETINT16(q);
   1.864 +      mGIFStruct.y_offset = GETINT16(q + 2);
   1.865 +
   1.866 +      /* Get image width and height. */
   1.867 +      mGIFStruct.width  = GETINT16(q + 4);
   1.868 +      mGIFStruct.height = GETINT16(q + 6);
   1.869 +
   1.870 +      if (!mGIFStruct.images_decoded) {
   1.871 +        /* Work around broken GIF files where the logical screen
   1.872 +         * size has weird width or height.  We assume that GIF87a
   1.873 +         * files don't contain animations.
   1.874 +         */
   1.875 +        if ((mGIFStruct.screen_height < mGIFStruct.height) ||
   1.876 +            (mGIFStruct.screen_width < mGIFStruct.width) ||
   1.877 +            (mGIFStruct.version == 87)) {
   1.878 +          mGIFStruct.screen_height = mGIFStruct.height;
   1.879 +          mGIFStruct.screen_width = mGIFStruct.width;
   1.880 +          mGIFStruct.x_offset = 0;
   1.881 +          mGIFStruct.y_offset = 0;
   1.882 +        }    
   1.883 +        // Create the image container with the right size.
   1.884 +        BeginGIF();
   1.885 +        if (HasError()) {
   1.886 +          // Setting the size led to an error.
   1.887 +          mGIFStruct.state = gif_error;
   1.888 +          return;
   1.889 +        }
   1.890 +
   1.891 +        // If we were doing a size decode, we're done
   1.892 +        if (IsSizeDecode())
   1.893 +          return;
   1.894 +      }
   1.895 +
   1.896 +      /* Work around more broken GIF files that have zero image
   1.897 +         width or height */
   1.898 +      if (!mGIFStruct.height || !mGIFStruct.width) {
   1.899 +        mGIFStruct.height = mGIFStruct.screen_height;
   1.900 +        mGIFStruct.width = mGIFStruct.screen_width;
   1.901 +        if (!mGIFStruct.height || !mGIFStruct.width) {
   1.902 +          mGIFStruct.state = gif_error;
   1.903 +          break;
   1.904 +        }
   1.905 +      }
   1.906 +
   1.907 +      /* Depth of colors is determined by colormap */
   1.908 +      /* (q[8] & 0x80) indicates local colormap */
   1.909 +      /* bits per pixel is (q[8]&0x07 + 1) when local colormap is set */
   1.910 +      uint32_t depth = mGIFStruct.global_colormap_depth;
   1.911 +      if (q[8] & 0x80)
   1.912 +        depth = (q[8]&0x07) + 1;
   1.913 +      uint32_t realDepth = depth;
   1.914 +      while (mGIFStruct.tpixel >= (1 << realDepth) && (realDepth < 8)) {
   1.915 +        realDepth++;
   1.916 +      } 
   1.917 +      // Mask to limit the color values within the colormap
   1.918 +      mColorMask = 0xFF >> (8 - realDepth);
   1.919 +      BeginImageFrame(realDepth);
   1.920 +
   1.921 +      if (NeedsNewFrame()) {
   1.922 +        // We now need a new frame from the decoder framework. We leave all our
   1.923 +        // data in the buffer as if it wasn't consumed, copy to our hold and return
   1.924 +        // to the decoder framework.
   1.925 +        uint32_t size = len + mGIFStruct.bytes_to_consume + mGIFStruct.bytes_in_hold;
   1.926 +        if (size) {
   1.927 +          if (SetHold(q, mGIFStruct.bytes_to_consume + mGIFStruct.bytes_in_hold, buf, len)) {
   1.928 +            // Back into the decoder infrastructure so we can get called again.
   1.929 +            GETN(9, gif_image_header_continue);
   1.930 +            return;
   1.931 +          }
   1.932 +        }
   1.933 +        break;
   1.934 +      } else {
   1.935 +        // FALL THROUGH
   1.936 +      }
   1.937 +    }
   1.938 +
   1.939 +    case gif_image_header_continue:
   1.940 +    {
   1.941 +      // While decoders can reuse frames, we unconditionally increment
   1.942 +      // mGIFStruct.images_decoded when we're done with a frame, so we both can
   1.943 +      // and need to zero out the colormap and image data after every new frame.
   1.944 +      memset(mImageData, 0, mImageDataLength);
   1.945 +      if (mColormap) {
   1.946 +        memset(mColormap, 0, mColormapSize);
   1.947 +      }
   1.948 +
   1.949 +      if (!mGIFStruct.images_decoded) {
   1.950 +        // Send a onetime invalidation for the first frame if it has a y-axis offset. 
   1.951 +        // Otherwise, the area may never be refreshed and the placeholder will remain
   1.952 +        // on the screen. (Bug 37589)
   1.953 +        if (mGIFStruct.y_offset > 0) {
   1.954 +          nsIntRect r(0, 0, mGIFStruct.screen_width, mGIFStruct.y_offset);
   1.955 +          PostInvalidation(r);
   1.956 +        }
   1.957 +      }
   1.958 +
   1.959 +      if (q[8] & 0x40) {
   1.960 +        mGIFStruct.interlaced = true;
   1.961 +        mGIFStruct.ipass = 1;
   1.962 +      } else {
   1.963 +        mGIFStruct.interlaced = false;
   1.964 +        mGIFStruct.ipass = 0;
   1.965 +      }
   1.966 +
   1.967 +      /* Only apply the Haeberli display hack on the first frame */
   1.968 +      mGIFStruct.progressive_display = (mGIFStruct.images_decoded == 0);
   1.969 +
   1.970 +      /* Clear state from last image */
   1.971 +      mGIFStruct.irow = 0;
   1.972 +      mGIFStruct.rows_remaining = mGIFStruct.height;
   1.973 +      mGIFStruct.rowp = mImageData;
   1.974 +
   1.975 +      /* Depth of colors is determined by colormap */
   1.976 +      /* (q[8] & 0x80) indicates local colormap */
   1.977 +      /* bits per pixel is (q[8]&0x07 + 1) when local colormap is set */
   1.978 +      uint32_t depth = mGIFStruct.global_colormap_depth;
   1.979 +      if (q[8] & 0x80)
   1.980 +        depth = (q[8]&0x07) + 1;
   1.981 +      uint32_t realDepth = depth;
   1.982 +      while (mGIFStruct.tpixel >= (1 << realDepth) && (realDepth < 8)) {
   1.983 +        realDepth++;
   1.984 +      }
   1.985 +
   1.986 +      if (q[8] & 0x80) /* has a local colormap? */
   1.987 +      {
   1.988 +        mGIFStruct.local_colormap_size = 1 << depth;
   1.989 +        if (!mGIFStruct.images_decoded) {
   1.990 +          // First frame has local colormap, allocate space for it
   1.991 +          // as the image frame doesn't have its own palette
   1.992 +          mColormapSize = sizeof(uint32_t) << realDepth;
   1.993 +          if (!mGIFStruct.local_colormap) {
   1.994 +            mGIFStruct.local_colormap = (uint32_t*)moz_xmalloc(mColormapSize);
   1.995 +          }
   1.996 +          mColormap = mGIFStruct.local_colormap;
   1.997 +        }
   1.998 +        const uint32_t size = 3 << depth;
   1.999 +        if (mColormapSize > size) {
  1.1000 +          // Clear the notfilled part of the colormap
  1.1001 +          memset(((uint8_t*)mColormap) + size, 0, mColormapSize - size);
  1.1002 +        }
  1.1003 +        if (len < size) {
  1.1004 +          // Use 'hold' pattern to get the image colormap
  1.1005 +          GETN(size, gif_image_colormap);
  1.1006 +          break;
  1.1007 +        }
  1.1008 +        // Copy everything, go to colormap state to do CMS correction
  1.1009 +        memcpy(mColormap, buf, size);
  1.1010 +        buf += size;
  1.1011 +        len -= size;
  1.1012 +        GETN(0, gif_image_colormap);
  1.1013 +        break;
  1.1014 +      } else {
  1.1015 +        /* Switch back to the global palette */
  1.1016 +        if (mGIFStruct.images_decoded) {
  1.1017 +          // Copy global colormap into the palette of current frame
  1.1018 +          memcpy(mColormap, mGIFStruct.global_colormap, mColormapSize);
  1.1019 +        } else {
  1.1020 +          mColormap = mGIFStruct.global_colormap;
  1.1021 +        }
  1.1022 +      }
  1.1023 +      GETN(1, gif_lzw_start);
  1.1024 +    }
  1.1025 +    break;
  1.1026 +
  1.1027 +    case gif_image_colormap:
  1.1028 +      // Everything is already copied into local_colormap
  1.1029 +      // Convert into Cairo colors including CMS transformation
  1.1030 +      ConvertColormap(mColormap, mGIFStruct.local_colormap_size);
  1.1031 +      GETN(1, gif_lzw_start);
  1.1032 +      break;
  1.1033 +
  1.1034 +    case gif_sub_block:
  1.1035 +      mGIFStruct.count = *q;
  1.1036 +      if (mGIFStruct.count) {
  1.1037 +        /* Still working on the same image: Process next LZW data block */
  1.1038 +        /* Make sure there are still rows left. If the GIF data */
  1.1039 +        /* is corrupt, we may not get an explicit terminator.   */
  1.1040 +        if (!mGIFStruct.rows_remaining) {
  1.1041 +#ifdef DONT_TOLERATE_BROKEN_GIFS
  1.1042 +          mGIFStruct.state = gif_error;
  1.1043 +          break;
  1.1044 +#else
  1.1045 +          /* This is an illegal GIF, but we remain tolerant. */
  1.1046 +          GETN(1, gif_sub_block);
  1.1047 +#endif
  1.1048 +          if (mGIFStruct.count == GIF_TRAILER) {
  1.1049 +            /* Found a terminator anyway, so consider the image done */
  1.1050 +            GETN(1, gif_done);
  1.1051 +            break;
  1.1052 +          }
  1.1053 +        }
  1.1054 +        GETN(mGIFStruct.count, gif_lzw);
  1.1055 +      } else {
  1.1056 +        /* See if there are any more images in this sequence. */
  1.1057 +        EndImageFrame();
  1.1058 +        GETN(1, gif_image_start);
  1.1059 +      }
  1.1060 +      break;
  1.1061 +
  1.1062 +    case gif_done:
  1.1063 +      MOZ_ASSERT(!IsSizeDecode(), "Size decodes shouldn't reach gif_done");
  1.1064 +      FinishInternal();
  1.1065 +      goto done;
  1.1066 +
  1.1067 +    case gif_error:
  1.1068 +      PostDataError();
  1.1069 +      return;
  1.1070 +
  1.1071 +    // We shouldn't ever get here.
  1.1072 +    default:
  1.1073 +      break;
  1.1074 +    }
  1.1075 +  }
  1.1076 +
  1.1077 +  // if an error state is set but no data remains, code flow reaches here
  1.1078 +  if (mGIFStruct.state == gif_error) {
  1.1079 +      PostDataError();
  1.1080 +      return;
  1.1081 +  }
  1.1082 +
  1.1083 +  // Copy the leftover into mGIFStruct.hold
  1.1084 +  if (len) {
  1.1085 +    // Add what we have sofar to the block
  1.1086 +    if (mGIFStruct.state != gif_global_colormap && mGIFStruct.state != gif_image_colormap) {
  1.1087 +      if (!SetHold(buf, len)) {
  1.1088 +        PostDataError();
  1.1089 +        return;
  1.1090 +      }
  1.1091 +    } else {
  1.1092 +      uint8_t* p = (mGIFStruct.state == gif_global_colormap) ? (uint8_t*)mGIFStruct.global_colormap :
  1.1093 +                                                               (uint8_t*)mColormap;
  1.1094 +      memcpy(p, buf, len);
  1.1095 +      mGIFStruct.bytes_in_hold = len;
  1.1096 +    }
  1.1097 +
  1.1098 +    mGIFStruct.bytes_to_consume -= len;
  1.1099 +  }
  1.1100 +
  1.1101 +// We want to flush before returning if we're on the first frame
  1.1102 +done:
  1.1103 +  if (!mGIFStruct.images_decoded) {
  1.1104 +    FlushImageData();
  1.1105 +    mLastFlushedRow = mCurrentRow;
  1.1106 +    mLastFlushedPass = mCurrentPass;
  1.1107 +  }
  1.1108 +
  1.1109 +  return;
  1.1110 +}
  1.1111 +
  1.1112 +bool
  1.1113 +nsGIFDecoder2::SetHold(const uint8_t* buf1, uint32_t count1, const uint8_t* buf2 /* = nullptr */, uint32_t count2 /* = 0 */)
  1.1114 +{
  1.1115 +  // We have to handle the case that buf currently points to hold
  1.1116 +  uint8_t* newHold = (uint8_t *) moz_malloc(std::max(uint32_t(MIN_HOLD_SIZE), count1 + count2));
  1.1117 +  if (!newHold) {
  1.1118 +    mGIFStruct.state = gif_error;
  1.1119 +    return false;
  1.1120 +  }
  1.1121 +
  1.1122 +  memcpy(newHold, buf1, count1);
  1.1123 +  if (buf2) {
  1.1124 +    memcpy(newHold + count1, buf2, count2);
  1.1125 +  }
  1.1126 +
  1.1127 +  moz_free(mGIFStruct.hold);
  1.1128 +  mGIFStruct.hold = newHold;
  1.1129 +  mGIFStruct.bytes_in_hold = count1 + count2;
  1.1130 +  return true;
  1.1131 +}
  1.1132 +
  1.1133 +Telemetry::ID
  1.1134 +nsGIFDecoder2::SpeedHistogram()
  1.1135 +{
  1.1136 +  return Telemetry::IMAGE_DECODE_SPEED_GIF;
  1.1137 +}
  1.1138 +
  1.1139 +
  1.1140 +} // namespace image
  1.1141 +} // namespace mozilla

mercurial