image/decoders/nsGIFDecoder2.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial