зеркало из https://github.com/mozilla/pjs.git
Bug 373794: crash with bad gif image, r=tor@acm.org, sr=pavlov@pavlov.net
This commit is contained in:
Родитель
a3b4536465
Коммит
bb1beb4913
|
@ -1,947 +0,0 @@
|
|||
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1998
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Chris Saari <saari@netscape.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/*
|
||||
The Graphics Interchange Format(c) is the copyright property of CompuServe
|
||||
Incorporated. Only CompuServe Incorporated is authorized to define, redefine,
|
||||
enhance, alter, modify or change in any way the definition of the format.
|
||||
|
||||
CompuServe Incorporated hereby grants a limited, non-exclusive, royalty-free
|
||||
license for the use of the Graphics Interchange Format(sm) in computer
|
||||
software; computer software utilizing GIF(sm) must acknowledge ownership of the
|
||||
Graphics Interchange Format and its Service Mark by CompuServe Incorporated, in
|
||||
User and Technical Documentation. Computer software utilizing GIF, which is
|
||||
distributed or may be distributed without User or Technical Documentation must
|
||||
display to the screen or printer a message acknowledging ownership of the
|
||||
Graphics Interchange Format and the Service Mark by CompuServe Incorporated; in
|
||||
this case, the acknowledgement may be displayed in an opening screen or leading
|
||||
banner, or a closing screen or trailing banner. A message such as the following
|
||||
may be used:
|
||||
|
||||
"The Graphics Interchange Format(c) is the Copyright property of
|
||||
CompuServe Incorporated. GIF(sm) is a Service Mark property of
|
||||
CompuServe Incorporated."
|
||||
|
||||
For further information, please contact :
|
||||
|
||||
CompuServe Incorporated
|
||||
Graphics Technology Department
|
||||
5000 Arlington Center Boulevard
|
||||
Columbus, Ohio 43220
|
||||
U. S. A.
|
||||
|
||||
CompuServe Incorporated maintains a mailing list with all those individuals and
|
||||
organizations who wish to receive copies of this document when it is corrected
|
||||
or revised. This service is offered free of charge; please provide us with your
|
||||
mailing address.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include "prtypes.h"
|
||||
#include "prmem.h"
|
||||
#include "prlog.h"
|
||||
#include "GIF2.h"
|
||||
|
||||
#include "nsGIFDecoder2.h"
|
||||
|
||||
/*
|
||||
* GETN(n, s) requests at least 'n' bytes available from 'q', at start of state 's'
|
||||
*
|
||||
* Note, the hold will never need to be bigger than 256 bytes to gather up in the hold,
|
||||
* as each GIF block (except colormaps) can never be bigger than 256 bytes.
|
||||
* Colormaps are directly copied in the resp. global_colormap or dynamically allocated local_colormap.
|
||||
* So a fixed buffer in gif_struct is good enough.
|
||||
* This buffer is only needed to copy left-over data from one GifWrite call to the next
|
||||
*/
|
||||
#define GETN(n,s) \
|
||||
PR_BEGIN_MACRO \
|
||||
gs->bytes_to_consume = (n); \
|
||||
gs->state = (s); \
|
||||
PR_END_MACRO
|
||||
|
||||
/* Get a 16-bit value stored in little-endian format */
|
||||
#define GETINT16(p) ((p)[1]<<8|(p)[0])
|
||||
|
||||
//******************************************************************************
|
||||
// Send the data to the display front-end.
|
||||
static void output_row(gif_struct *gs)
|
||||
{
|
||||
int width, drow_start, drow_end;
|
||||
|
||||
drow_start = drow_end = gs->irow;
|
||||
|
||||
/*
|
||||
* Haeberli-inspired hack for interlaced GIFs: Replicate lines while
|
||||
* displaying to diminish the "venetian-blind" effect as the image is
|
||||
* loaded. Adjust pixel vertical positions to avoid the appearance of the
|
||||
* image crawling up the screen as successive passes are drawn.
|
||||
*/
|
||||
if (gs->progressive_display && gs->interlaced && (gs->ipass < 4)) {
|
||||
PRUintn row_dup = 0, row_shift = 0;
|
||||
|
||||
switch (gs->ipass) {
|
||||
case 1:
|
||||
row_dup = 7;
|
||||
row_shift = 3;
|
||||
break;
|
||||
case 2:
|
||||
row_dup = 3;
|
||||
row_shift = 1;
|
||||
break;
|
||||
case 3:
|
||||
row_dup = 1;
|
||||
row_shift = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
drow_start -= row_shift;
|
||||
drow_end = drow_start + row_dup;
|
||||
|
||||
/* Extend if bottom edge isn't covered because of the shift upward. */
|
||||
if (((gs->height - 1) - drow_end) <= row_shift)
|
||||
drow_end = gs->height - 1;
|
||||
|
||||
/* Clamp first and last rows to upper and lower edge of image. */
|
||||
if (drow_start < 0)
|
||||
drow_start = 0;
|
||||
if ((PRUintn)drow_end >= gs->height)
|
||||
drow_end = gs->height - 1;
|
||||
}
|
||||
|
||||
/* Protect against too much image data */
|
||||
if ((PRUintn)drow_start >= gs->height) {
|
||||
NS_WARNING("GIF2.cpp::output_row - too much image data");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check for scanline below edge of logical screen */
|
||||
if ((gs->y_offset + gs->irow) < gs->screen_height) {
|
||||
/* Clip if right edge of image exceeds limits */
|
||||
if ((gs->x_offset + gs->width) > gs->screen_width)
|
||||
width = gs->screen_width - gs->x_offset;
|
||||
else
|
||||
width = gs->width;
|
||||
|
||||
if (width > 0)
|
||||
/* Decoded data available callback */
|
||||
nsGIFDecoder2::HaveDecodedRow(
|
||||
gs->clientptr,
|
||||
gs->rowbuf, // Pointer to single scanline temporary buffer
|
||||
drow_start, // Row number
|
||||
drow_end - drow_start + 1, // Number of times to duplicate the row?
|
||||
gs->ipass); // interlace pass (1-4)
|
||||
}
|
||||
|
||||
gs->rowp = gs->rowbuf;
|
||||
|
||||
if (!gs->interlaced)
|
||||
gs->irow++;
|
||||
else {
|
||||
do {
|
||||
switch (gs->ipass)
|
||||
{
|
||||
case 1:
|
||||
gs->irow += 8;
|
||||
if (gs->irow >= gs->height) {
|
||||
gs->ipass++;
|
||||
gs->irow = 4;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
gs->irow += 8;
|
||||
if (gs->irow >= gs->height) {
|
||||
gs->ipass++;
|
||||
gs->irow = 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
gs->irow += 4;
|
||||
if (gs->irow >= gs->height) {
|
||||
gs->ipass++;
|
||||
gs->irow = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
gs->irow += 2;
|
||||
if (gs->irow >= gs->height){
|
||||
gs->ipass++;
|
||||
gs->irow = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} while (gs->irow > (gs->height - 1));
|
||||
}
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
/* Perform Lempel-Ziv-Welch decoding */
|
||||
static int do_lzw(gif_struct *gs, const PRUint8 *q)
|
||||
{
|
||||
int code;
|
||||
int incode;
|
||||
const PRUint8 *ch;
|
||||
|
||||
/* Copy all the decoder state variables into locals so the compiler
|
||||
* won't worry about them being aliased. The locals will be homed
|
||||
* back into the GIF decoder structure when we exit.
|
||||
*/
|
||||
int avail = gs->avail;
|
||||
int bits = gs->bits;
|
||||
int codesize = gs->codesize;
|
||||
int codemask = gs->codemask;
|
||||
int count = gs->count;
|
||||
int oldcode = gs->oldcode;
|
||||
int clear_code = gs->clear_code;
|
||||
PRUint8 firstchar = gs->firstchar;
|
||||
PRInt32 datum = gs->datum;
|
||||
PRUint16 *prefix = gs->prefix;
|
||||
PRUint8 *stackp = gs->stackp;
|
||||
PRUint8 *suffix = gs->suffix;
|
||||
PRUint8 *stack = gs->stack;
|
||||
PRUint8 *rowp = gs->rowp;
|
||||
PRUint8 *rowend = gs->rowend;
|
||||
PRUintn rows_remaining = gs->rows_remaining;
|
||||
|
||||
if (rowp == rowend)
|
||||
return 0;
|
||||
|
||||
#define OUTPUT_ROW(gs) \
|
||||
PR_BEGIN_MACRO \
|
||||
output_row(gs); \
|
||||
rows_remaining--; \
|
||||
rowp = gs->rowp; \
|
||||
if (!rows_remaining) \
|
||||
goto END; \
|
||||
PR_END_MACRO
|
||||
|
||||
for (ch = q; count-- > 0; ch++)
|
||||
{
|
||||
/* Feed the next byte into the decoder's 32-bit input buffer. */
|
||||
datum += ((int32) *ch) << bits;
|
||||
bits += 8;
|
||||
|
||||
/* Check for underflow of decoder's 32-bit input buffer. */
|
||||
while (bits >= codesize)
|
||||
{
|
||||
/* Get the leading variable-length symbol from the data stream */
|
||||
code = datum & codemask;
|
||||
datum >>= codesize;
|
||||
bits -= codesize;
|
||||
|
||||
/* Reset the dictionary to its original state, if requested */
|
||||
if (code == clear_code) {
|
||||
codesize = gs->datasize + 1;
|
||||
codemask = (1 << codesize) - 1;
|
||||
avail = clear_code + 2;
|
||||
oldcode = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check for explicit end-of-stream code */
|
||||
if (code == (clear_code + 1)) {
|
||||
/* end-of-stream should only appear after all image data */
|
||||
if (rows_remaining != 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (oldcode == -1) {
|
||||
*rowp++ = suffix[code];
|
||||
if (rowp == rowend)
|
||||
OUTPUT_ROW(gs);
|
||||
|
||||
firstchar = oldcode = code;
|
||||
continue;
|
||||
}
|
||||
|
||||
incode = code;
|
||||
if (code >= avail) {
|
||||
*stackp++ = firstchar;
|
||||
code = oldcode;
|
||||
|
||||
if (stackp == stack + MAX_BITS)
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (code >= clear_code)
|
||||
{
|
||||
if (code == prefix[code])
|
||||
return -1;
|
||||
|
||||
*stackp++ = suffix[code];
|
||||
code = prefix[code];
|
||||
|
||||
if (stackp == stack + MAX_BITS)
|
||||
return -1;
|
||||
}
|
||||
|
||||
*stackp++ = firstchar = suffix[code];
|
||||
|
||||
/* Define a new codeword in the dictionary. */
|
||||
if (avail < 4096) {
|
||||
prefix[avail] = oldcode;
|
||||
suffix[avail] = firstchar;
|
||||
avail++;
|
||||
|
||||
/* If we've used up all the codewords of a given length
|
||||
* increase the length of codewords by one bit, but don't
|
||||
* exceed the specified maximum codeword size of 12 bits.
|
||||
*/
|
||||
if (((avail & codemask) == 0) && (avail < 4096)) {
|
||||
codesize++;
|
||||
codemask += avail;
|
||||
}
|
||||
}
|
||||
oldcode = incode;
|
||||
|
||||
/* Copy the decoded data out to the scanline buffer. */
|
||||
do {
|
||||
*rowp++ = *--stackp;
|
||||
if (rowp == rowend) {
|
||||
OUTPUT_ROW(gs);
|
||||
}
|
||||
} while (stackp > stack);
|
||||
}
|
||||
}
|
||||
|
||||
END:
|
||||
|
||||
/* Home the local copies of the GIF decoder state variables */
|
||||
gs->avail = avail;
|
||||
gs->bits = bits;
|
||||
gs->codesize = codesize;
|
||||
gs->codemask = codemask;
|
||||
gs->count = count;
|
||||
gs->oldcode = oldcode;
|
||||
gs->firstchar = firstchar;
|
||||
gs->datum = datum;
|
||||
gs->stackp = stackp;
|
||||
gs->rowp = rowp;
|
||||
gs->rows_remaining = rows_remaining;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* setup for gif_struct decoding
|
||||
*/
|
||||
PRBool GIFInit(gif_struct* gs, void* aClientData)
|
||||
{
|
||||
NS_ASSERTION(gs, "Got null argument");
|
||||
if (!gs)
|
||||
return PR_FALSE;
|
||||
|
||||
// Clear out the structure, excluding the arrays
|
||||
memset(gs, 0, sizeof(gif_struct));
|
||||
gs->clientptr = aClientData;
|
||||
|
||||
// Start with the version (GIF89a|GIF87a)
|
||||
gs->state = gif_type;
|
||||
gs->bytes_to_consume = 6;
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* process data arriving from the stream for the gif decoder
|
||||
*/
|
||||
|
||||
PRStatus gif_write(gif_struct *gs, const PRUint8 *buf, PRUint32 len)
|
||||
{
|
||||
if (!gs || !len)
|
||||
return PR_FAILURE;
|
||||
|
||||
const PRUint8 *q = buf;
|
||||
|
||||
// Add what we have sofar to the block
|
||||
// If previous call to me left something in the hold first complete current block
|
||||
// Or if we are filling the colormaps, first complete the colormap
|
||||
PRUint8* p;
|
||||
if (gs->state == gif_global_colormap)
|
||||
p = gs->global_colormap;
|
||||
else if (gs->state == gif_image_colormap)
|
||||
p = gs->local_colormap;
|
||||
else if (gs->bytes_in_hold)
|
||||
p = gs->hold;
|
||||
else
|
||||
p = nsnull;
|
||||
|
||||
if (p) {
|
||||
// Add what we have sofar to the block
|
||||
PRUint32 l = PR_MIN(len, gs->bytes_to_consume);
|
||||
memcpy(p+gs->bytes_in_hold, buf, l);
|
||||
|
||||
if (l < gs->bytes_to_consume) {
|
||||
// Not enough in 'buf' to complete current block, get more
|
||||
gs->bytes_in_hold += l;
|
||||
gs->bytes_to_consume -= l;
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
// Reset hold buffer count
|
||||
gs->bytes_in_hold = 0;
|
||||
// Point 'q' to complete block in hold (or in colormap)
|
||||
q = p;
|
||||
}
|
||||
|
||||
// Invariant:
|
||||
// 'q' is start of current to be processed block (hold, colormap or buf)
|
||||
// 'bytes_to_consume' is number of bytes to consume from 'buf'
|
||||
// 'buf' points to the bytes to be consumed from the input buffer
|
||||
// 'len' is number of bytes left in input buffer from position 'buf'.
|
||||
// At entrance of the for loop will 'buf' will be moved 'bytes_to_consume'
|
||||
// to point to next buffer, 'len' is adjusted accordingly.
|
||||
// So that next round in for loop, q gets pointed to the next buffer.
|
||||
|
||||
for (;len >= gs->bytes_to_consume; q=buf) {
|
||||
// Eat the current block from the buffer, q keeps pointed at current block
|
||||
buf += gs->bytes_to_consume;
|
||||
len -= gs->bytes_to_consume;
|
||||
|
||||
switch (gs->state)
|
||||
{
|
||||
case gif_lzw:
|
||||
if (do_lzw(gs, q) < 0) {
|
||||
gs->state = gif_error;
|
||||
break;
|
||||
}
|
||||
GETN(1, gif_sub_block);
|
||||
break;
|
||||
|
||||
case gif_lzw_start:
|
||||
{
|
||||
/* Initialize LZW parser/decoder */
|
||||
gs->datasize = *q;
|
||||
if (gs->datasize > MAX_LZW_BITS) {
|
||||
gs->state = gif_error;
|
||||
break;
|
||||
}
|
||||
|
||||
gs->clear_code = 1 << gs->datasize;
|
||||
gs->avail = gs->clear_code + 2;
|
||||
gs->oldcode = -1;
|
||||
gs->codesize = gs->datasize + 1;
|
||||
gs->codemask = (1 << gs->codesize) - 1;
|
||||
|
||||
gs->datum = gs->bits = 0;
|
||||
|
||||
if (gs->clear_code >= MAX_BITS) {
|
||||
gs->state = gif_error;
|
||||
break;
|
||||
}
|
||||
|
||||
/* init the tables */
|
||||
for (int i = 0; i < gs->clear_code; i++)
|
||||
gs->suffix[i] = i;
|
||||
|
||||
gs->stackp = gs->stack;
|
||||
|
||||
GETN(1, gif_sub_block);
|
||||
}
|
||||
break;
|
||||
|
||||
/* All GIF files begin with "GIF87a" or "GIF89a" */
|
||||
case gif_type:
|
||||
{
|
||||
if (!strncmp((char*)q, "GIF89a", 6)) {
|
||||
gs->version = 89;
|
||||
} else if (!strncmp((char*)q, "GIF87a", 6)) {
|
||||
gs->version = 87;
|
||||
} else {
|
||||
gs->state = gif_error;
|
||||
break;
|
||||
}
|
||||
GETN(7, gif_global_header);
|
||||
}
|
||||
break;
|
||||
|
||||
case gif_global_header:
|
||||
{
|
||||
/* This is the height and width of the "screen" or
|
||||
* frame into which images are rendered. The
|
||||
* individual images can be smaller than the
|
||||
* screen size and located with an origin anywhere
|
||||
* within the screen.
|
||||
*/
|
||||
|
||||
gs->screen_width = GETINT16(q);
|
||||
gs->screen_height = GETINT16(q + 2);
|
||||
|
||||
gs->screen_bgcolor = q[5];
|
||||
|
||||
gs->global_colormap_size = 2<<(q[4]&0x07);
|
||||
|
||||
// XXX make callback
|
||||
nsGIFDecoder2::BeginGIF(
|
||||
gs->clientptr,
|
||||
gs->screen_width,
|
||||
gs->screen_height,
|
||||
gs->screen_bgcolor);
|
||||
|
||||
if (q[4] & 0x80) { /* global map */
|
||||
// Get the global colormap
|
||||
const PRUint32 size = 3*gs->global_colormap_size;
|
||||
if (len < size) {
|
||||
// Use 'hold' pattern to get the global colormap
|
||||
GETN(size, gif_global_colormap);
|
||||
break;
|
||||
}
|
||||
// Copy everything and directly go to gif_lzw_start
|
||||
memcpy(gs->global_colormap, buf, size);
|
||||
buf += size;
|
||||
len -= size;
|
||||
}
|
||||
|
||||
GETN(1, gif_image_start);
|
||||
|
||||
// q[6] = Pixel Aspect Ratio
|
||||
// Not used
|
||||
// float aspect = (float)((q[6] + 15) / 64.0);
|
||||
}
|
||||
break;
|
||||
|
||||
case gif_global_colormap:
|
||||
// Everything is already copied into global_colormap
|
||||
GETN(1, gif_image_start);
|
||||
break;
|
||||
|
||||
case gif_image_start:
|
||||
{
|
||||
if (*q == ';') { /* terminator */
|
||||
gs->state = gif_done;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*q == '!') { /* extension */
|
||||
GETN(2, gif_extension);
|
||||
break;
|
||||
}
|
||||
|
||||
/* If we get anything other than ',' (image separator), '!'
|
||||
* (extension), or ';' (trailer), there is extraneous data
|
||||
* between blocks. The GIF87a spec tells us to keep reading
|
||||
* until we find an image separator, but GIF89a says such
|
||||
* a file is corrupt. We follow GIF89a and bail out. */
|
||||
if (*q != ',') {
|
||||
if (gs->images_decoded > 0) {
|
||||
/* The file is corrupt, but one or more images have
|
||||
* been decoded correctly. In this case, we proceed
|
||||
* as if the file were correctly terminated and set
|
||||
* the state to gif_done, so the GIF will display.
|
||||
*/
|
||||
gs->state = gif_done;
|
||||
} else {
|
||||
/* No images decoded, there is nothing to display. */
|
||||
gs->state = gif_error;
|
||||
}
|
||||
break;
|
||||
} else
|
||||
GETN(9, gif_image_header);
|
||||
}
|
||||
break;
|
||||
|
||||
case gif_extension:
|
||||
{
|
||||
int len = gs->count = q[1];
|
||||
gstate es = gif_skip_block;
|
||||
|
||||
switch (*q)
|
||||
{
|
||||
case 0xf9:
|
||||
es = gif_control_extension;
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
// ignoring plain text extension
|
||||
break;
|
||||
|
||||
case 0xff:
|
||||
es = gif_application_extension;
|
||||
break;
|
||||
|
||||
case 0xfe:
|
||||
es = gif_consume_comment;
|
||||
break;
|
||||
}
|
||||
|
||||
if (len)
|
||||
GETN(len, es);
|
||||
else
|
||||
GETN(1, gif_image_start);
|
||||
}
|
||||
break;
|
||||
|
||||
case gif_consume_block:
|
||||
if (!*q)
|
||||
GETN(1, gif_image_start);
|
||||
else
|
||||
GETN(*q, gif_skip_block);
|
||||
break;
|
||||
|
||||
case gif_skip_block:
|
||||
GETN(1, gif_consume_block);
|
||||
break;
|
||||
|
||||
case gif_control_extension:
|
||||
{
|
||||
if (*q & 0x1) {
|
||||
gs->tpixel = q[3];
|
||||
gs->is_transparent = PR_TRUE;
|
||||
} else {
|
||||
gs->is_transparent = PR_FALSE;
|
||||
// ignoring gfx control extension
|
||||
}
|
||||
gs->disposal_method = ((*q) >> 2) & 0x7;
|
||||
// Some specs say 3rd bit (value 4), other specs say value 3
|
||||
// Let's choose 3 (the more popular)
|
||||
if (gs->disposal_method == 4)
|
||||
gs->disposal_method = 3;
|
||||
gs->delay_time = GETINT16(q + 1) * 10;
|
||||
GETN(1, gif_consume_block);
|
||||
}
|
||||
break;
|
||||
|
||||
case gif_comment_extension:
|
||||
{
|
||||
if (*q)
|
||||
GETN(*q, gif_consume_comment);
|
||||
else
|
||||
GETN(1, gif_image_start);
|
||||
}
|
||||
break;
|
||||
|
||||
case gif_consume_comment:
|
||||
GETN(1, gif_comment_extension);
|
||||
break;
|
||||
|
||||
case gif_application_extension:
|
||||
/* Check for netscape application extension */
|
||||
if (!strncmp((char*)q, "NETSCAPE2.0", 11) ||
|
||||
!strncmp((char*)q, "ANIMEXTS1.0", 11))
|
||||
GETN(1, gif_netscape_extension_block);
|
||||
else
|
||||
GETN(1, gif_consume_block);
|
||||
break;
|
||||
|
||||
/* Netscape-specific GIF extension: animation looping */
|
||||
case gif_netscape_extension_block:
|
||||
if (*q)
|
||||
GETN(*q, gif_consume_netscape_extension);
|
||||
else
|
||||
GETN(1, gif_image_start);
|
||||
break;
|
||||
|
||||
/* Parse netscape-specific application extensions */
|
||||
case gif_consume_netscape_extension:
|
||||
{
|
||||
int netscape_extension = q[0] & 7;
|
||||
|
||||
/* Loop entire animation specified # of times. Only read the
|
||||
loop count during the first iteration. */
|
||||
if (netscape_extension == 1) {
|
||||
gs->loop_count = GETINT16(q + 1);
|
||||
|
||||
/* Zero loop count is infinite animation loop request */
|
||||
if (gs->loop_count == 0)
|
||||
gs->loop_count = -1;
|
||||
|
||||
GETN(1, gif_netscape_extension_block);
|
||||
}
|
||||
/* Wait for specified # of bytes to enter buffer */
|
||||
else if (netscape_extension == 2) {
|
||||
// Don't do this, this extension doesn't exist (isn't used at all)
|
||||
// and doesn't do anything, as our streaming/buffering takes care of it all...
|
||||
// See: http://semmix.pl/color/exgraf/eeg24.htm
|
||||
GETN(1, gif_netscape_extension_block);
|
||||
} else
|
||||
gs->state = gif_error; // 0,3-7 are yet to be defined netscape
|
||||
// extension codes
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case gif_image_header:
|
||||
{
|
||||
PRUintn height, width;
|
||||
|
||||
/* Get image offsets, with respect to the screen origin */
|
||||
gs->x_offset = GETINT16(q);
|
||||
gs->y_offset = GETINT16(q + 2);
|
||||
|
||||
/* Get image width and height. */
|
||||
width = GETINT16(q + 4);
|
||||
height = GETINT16(q + 6);
|
||||
|
||||
/* Work around broken GIF files where the logical screen
|
||||
* size has weird width or height. We assume that GIF87a
|
||||
* files don't contain animations.
|
||||
*/
|
||||
if ((gs->images_decoded == 0) &&
|
||||
((gs->screen_height < height) || (gs->screen_width < width) ||
|
||||
(gs->version == 87)))
|
||||
{
|
||||
gs->screen_height = height;
|
||||
gs->screen_width = width;
|
||||
gs->x_offset = 0;
|
||||
gs->y_offset = 0;
|
||||
|
||||
nsGIFDecoder2::BeginGIF(gs->clientptr,
|
||||
gs->screen_width,
|
||||
gs->screen_height,
|
||||
gs->screen_bgcolor);
|
||||
}
|
||||
|
||||
/* Work around more broken GIF files that have zero image
|
||||
width or height */
|
||||
if (!height || !width) {
|
||||
height = gs->screen_height;
|
||||
width = gs->screen_width;
|
||||
if (!height || !width) {
|
||||
gs->state = gif_error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gs->height = height;
|
||||
gs->width = width;
|
||||
|
||||
nsGIFDecoder2::BeginImageFrame(gs->clientptr,
|
||||
gs->images_decoded + 1, /* Frame number, 1-n */
|
||||
gs->x_offset, /* X offset in logical screen */
|
||||
gs->y_offset, /* Y offset in logical screen */
|
||||
width,
|
||||
height);
|
||||
|
||||
/* This case will never be taken if this is the first image */
|
||||
/* being decoded. If any of the later images are larger */
|
||||
/* than the screen size, we need to reallocate buffers. */
|
||||
if (gs->screen_width < width) {
|
||||
/* XXX Deviant! */
|
||||
|
||||
gs->rowbuf = (PRUint8*)PR_REALLOC(gs->rowbuf, width);
|
||||
|
||||
if (!gs->rowbuf) {
|
||||
gs->state = gif_oom;
|
||||
break;
|
||||
}
|
||||
|
||||
gs->screen_width = width;
|
||||
if (gs->screen_height < gs->height)
|
||||
gs->screen_height = gs->height;
|
||||
|
||||
}
|
||||
else {
|
||||
if (!gs->rowbuf)
|
||||
gs->rowbuf = (PRUint8*)PR_MALLOC(gs->screen_width);
|
||||
}
|
||||
|
||||
if (!gs->rowbuf) {
|
||||
gs->state = gif_oom;
|
||||
break;
|
||||
}
|
||||
|
||||
if (q[8] & 0x40) {
|
||||
gs->interlaced = PR_TRUE;
|
||||
gs->ipass = 1;
|
||||
} else {
|
||||
gs->interlaced = PR_FALSE;
|
||||
gs->ipass = 0;
|
||||
}
|
||||
|
||||
if (gs->images_decoded == 0) {
|
||||
gs->progressive_display = PR_TRUE;
|
||||
} else {
|
||||
/* Overlaying interlaced, transparent GIFs over
|
||||
existing image data using the Haeberli display hack
|
||||
requires saving the underlying image in order to
|
||||
avoid jaggies at the transparency edges. We are
|
||||
unprepared to deal with that, so don't display such
|
||||
images progressively */
|
||||
gs->progressive_display = PR_FALSE;
|
||||
}
|
||||
|
||||
/* Clear state from last image */
|
||||
gs->irow = 0;
|
||||
gs->rows_remaining = gs->height;
|
||||
gs->rowend = gs->rowbuf + gs->width;
|
||||
gs->rowp = gs->rowbuf;
|
||||
|
||||
/* bits per pixel is q[8]&0x07 */
|
||||
|
||||
if (q[8] & 0x80) /* has a local colormap? */
|
||||
{
|
||||
int num_colors = 2 << (q[8] & 0x7);
|
||||
const PRUint32 size = 3*num_colors;
|
||||
PRUint8 *map = gs->local_colormap;
|
||||
if (!map || (num_colors > gs->local_colormap_size)) {
|
||||
map = (PRUint8*)PR_REALLOC(map, size);
|
||||
if (!map) {
|
||||
gs->state = gif_oom;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Switch to the new local palette after it loads */
|
||||
gs->local_colormap = map;
|
||||
gs->local_colormap_size = num_colors;
|
||||
gs->is_local_colormap_defined = PR_TRUE;
|
||||
|
||||
if (len < size) {
|
||||
// Use 'hold' pattern to get the image colormap
|
||||
GETN(size, gif_image_colormap);
|
||||
break;
|
||||
}
|
||||
// Copy everything and directly go to gif_lzw_start
|
||||
memcpy(gs->local_colormap, buf, size);
|
||||
buf += size;
|
||||
len -= size;
|
||||
} else {
|
||||
/* Switch back to the global palette */
|
||||
gs->is_local_colormap_defined = PR_FALSE;
|
||||
}
|
||||
GETN(1, gif_lzw_start);
|
||||
}
|
||||
break;
|
||||
|
||||
case gif_image_colormap:
|
||||
// Everything is already copied into local_colormap
|
||||
GETN(1, gif_lzw_start);
|
||||
break;
|
||||
|
||||
case gif_sub_block:
|
||||
{
|
||||
if ((gs->count = *q) != 0)
|
||||
/* Still working on the same image: Process next LZW data block */
|
||||
{
|
||||
/* Make sure there are still rows left. If the GIF data */
|
||||
/* is corrupt, we may not get an explicit terminator. */
|
||||
if (gs->rows_remaining == 0) {
|
||||
/* This is an illegal GIF, but we remain tolerant. */
|
||||
#ifdef DONT_TOLERATE_BROKEN_GIFS
|
||||
gs->state = gif_error;
|
||||
break;
|
||||
#else
|
||||
GETN(1, gif_sub_block);
|
||||
#endif
|
||||
}
|
||||
GETN(gs->count, gif_lzw);
|
||||
}
|
||||
else
|
||||
/* See if there are any more images in this sequence. */
|
||||
{
|
||||
gs->images_decoded++;
|
||||
|
||||
nsGIFDecoder2::EndImageFrame(gs->clientptr,
|
||||
gs->images_decoded,
|
||||
gs->delay_time);
|
||||
|
||||
/* Clear state from this image */
|
||||
gs->is_local_colormap_defined = PR_FALSE;
|
||||
gs->is_transparent = PR_FALSE;
|
||||
|
||||
/* An image can specify a delay time before which to display
|
||||
subsequent images. */
|
||||
if (gs->delay_time < MINIMUM_DELAY_TIME)
|
||||
gs->delay_time = MINIMUM_DELAY_TIME;
|
||||
|
||||
GETN(1, gif_image_start);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case gif_done:
|
||||
nsGIFDecoder2::EndGIF(gs->clientptr, gs->loop_count);
|
||||
return PR_SUCCESS;
|
||||
break;
|
||||
|
||||
// Handle out of memory errors
|
||||
case gif_oom:
|
||||
return PR_FAILURE;
|
||||
|
||||
// Handle general errors
|
||||
case gif_error:
|
||||
nsGIFDecoder2::EndGIF(gs->clientptr, gs->loop_count);
|
||||
return PR_SUCCESS;
|
||||
|
||||
// We shouldn't ever get here.
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the leftover into gs->hold
|
||||
gs->bytes_in_hold = len;
|
||||
if (len) {
|
||||
// Add what we have sofar to the block
|
||||
PRUint8* p;
|
||||
if (gs->state == gif_global_colormap)
|
||||
p = gs->global_colormap;
|
||||
else if (gs->state == gif_image_colormap)
|
||||
p = gs->local_colormap;
|
||||
else
|
||||
p = gs->hold;
|
||||
memcpy(p, buf, len);
|
||||
gs->bytes_to_consume -= len;
|
||||
}
|
||||
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
/* Free up all the data structures associated with decoding a GIF */
|
||||
void gif_destroy(gif_struct *gs)
|
||||
{
|
||||
if (!gs)
|
||||
return;
|
||||
|
||||
/* Clear any pending timeouts */
|
||||
if (gs->delay_time)
|
||||
gs->delay_time = 0;
|
||||
|
||||
PR_FREEIF(gs->rowbuf);
|
||||
PR_FREEIF(gs->local_colormap);
|
||||
}
|
||||
|
Загрузка…
Ссылка в новой задаче