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