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