GIFImageReader.cpp [plain text]
#include "config.h"
#include "GIFImageReader.h"
#include <string.h>
#include "GIFImageDecoder.h"
#include "ImageSource.h"
using WebCore::GIFImageDecoder;
#define PR_BEGIN_MACRO do {
#define PR_END_MACRO } while (0)
#define GETN(n,s) \
PR_BEGIN_MACRO \
bytes_to_consume = (n); \
state = (s); \
PR_END_MACRO
#define GETINT16(p) ((p)[1]<<8|(p)[0])
bool GIFImageReader::output_row()
{
GIFFrameReader* gs = frame_reader;
int drow_start, drow_end;
drow_start = drow_end = gs->irow;
if (gs->progressive_display && gs->interlaced && gs->ipass < 4) {
unsigned 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;
if (((gs->height - 1) - drow_end) <= row_shift)
drow_end = gs->height - 1;
if (drow_start < 0)
drow_start = 0;
if ((unsigned)drow_end >= gs->height)
drow_end = gs->height - 1;
}
if ((unsigned)drow_start >= gs->height)
return true;
if (clientptr && frame_reader &&
!clientptr->haveDecodedRow(images_count - 1, frame_reader->rowbuf, frame_reader->rowend,
drow_start, drow_end - drow_start + 1,
gs->progressive_display && gs->interlaced && gs->ipass > 1))
return false;
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));
}
return true;
}
bool GIFImageReader::do_lzw(const unsigned char *q)
{
GIFFrameReader* gs = frame_reader;
if (!gs)
return true;
int code;
int incode;
const unsigned char *ch;
int avail = gs->avail;
int bits = gs->bits;
int cnt = count;
int codesize = gs->codesize;
int codemask = gs->codemask;
int oldcode = gs->oldcode;
int clear_code = gs->clear_code;
unsigned char firstchar = gs->firstchar;
int datum = gs->datum;
if (!gs->prefix) {
gs->prefix = new unsigned short[MAX_BITS];
memset(gs->prefix, 0, MAX_BITS * sizeof(short));
}
unsigned short *prefix = gs->prefix;
unsigned char *stackp = gs->stackp;
unsigned char *suffix = gs->suffix;
unsigned char *stack = gs->stack;
unsigned char *rowp = gs->rowp;
unsigned char *rowend = gs->rowend;
unsigned rows_remaining = gs->rows_remaining;
if (rowp == rowend)
return true;
#define OUTPUT_ROW \
PR_BEGIN_MACRO \
if (!output_row()) \
return false; \
rows_remaining--; \
rowp = frame_reader->rowp; \
if (!rows_remaining) \
goto END; \
PR_END_MACRO
for (ch = q; cnt-- > 0; ch++)
{
datum += ((int) *ch) << bits;
bits += 8;
while (bits >= codesize)
{
code = datum & codemask;
datum >>= codesize;
bits -= codesize;
if (code == clear_code) {
codesize = gs->datasize + 1;
codemask = (1 << codesize) - 1;
avail = clear_code + 2;
oldcode = -1;
continue;
}
if (code == (clear_code + 1)) {
if (!rows_remaining)
return true;
return clientptr ? clientptr->setFailed() : false;
}
if (oldcode == -1) {
*rowp++ = suffix[code];
if (rowp == rowend)
OUTPUT_ROW;
firstchar = oldcode = code;
continue;
}
incode = code;
if (code >= avail) {
*stackp++ = firstchar;
code = oldcode;
if (stackp == stack + MAX_BITS)
return clientptr ? clientptr->setFailed() : false;
}
while (code >= clear_code)
{
if (code >= MAX_BITS || code == prefix[code])
return clientptr ? clientptr->setFailed() : false;
*stackp++ = suffix[code];
code = prefix[code];
if (stackp == stack + MAX_BITS)
return clientptr ? clientptr->setFailed() : false;
}
*stackp++ = firstchar = suffix[code];
if (avail < 4096) {
prefix[avail] = oldcode;
suffix[avail] = firstchar;
avail++;
if (((avail & codemask) == 0) && (avail < 4096)) {
codesize++;
codemask += avail;
}
}
oldcode = incode;
do {
*rowp++ = *--stackp;
if (rowp == rowend) {
OUTPUT_ROW;
}
} while (stackp > stack);
}
}
END:
gs->avail = avail;
gs->bits = bits;
gs->codesize = codesize;
gs->codemask = codemask;
count = cnt;
gs->oldcode = oldcode;
gs->firstchar = firstchar;
gs->datum = datum;
gs->stackp = stackp;
gs->rowp = rowp;
gs->rows_remaining = rows_remaining;
return true;
}
bool GIFImageReader::read(const unsigned char *buf, unsigned len,
GIFImageDecoder::GIFQuery query, unsigned haltAtFrame)
{
if (!len) {
return true;
}
const unsigned char *q = buf;
unsigned char* p = 0;
if (state == gif_global_colormap)
p = global_colormap;
else if (state == gif_image_colormap)
p = frame_reader ? frame_reader->local_colormap : 0;
else if (bytes_in_hold)
p = hold;
else
p = 0;
if (p || (state == gif_global_colormap) || (state == gif_image_colormap)) {
unsigned l = len < bytes_to_consume ? len : bytes_to_consume;
if (p)
memcpy(p + bytes_in_hold, buf, l);
if (l < bytes_to_consume) {
bytes_in_hold += l;
bytes_to_consume -= l;
if (clientptr)
clientptr->decodingHalted(0);
return false;
}
bytes_in_hold = 0;
q = p;
}
for (;len >= bytes_to_consume; q=buf) {
buf += bytes_to_consume;
len -= bytes_to_consume;
switch (state)
{
case gif_lzw:
if (!do_lzw(q))
return false; GETN(1, gif_sub_block);
break;
case gif_lzw_start:
{
int datasize = *q;
if (datasize >= MAX_LZW_BITS)
return clientptr ? clientptr->setFailed() : false;
int clear_code = 1 << datasize;
if (clear_code >= MAX_BITS)
return clientptr ? clientptr->setFailed() : false;
if (frame_reader) {
frame_reader->datasize = datasize;
frame_reader->clear_code = clear_code;
frame_reader->avail = frame_reader->clear_code + 2;
frame_reader->oldcode = -1;
frame_reader->codesize = frame_reader->datasize + 1;
frame_reader->codemask = (1 << frame_reader->codesize) - 1;
frame_reader->datum = frame_reader->bits = 0;
if (!frame_reader->suffix)
frame_reader->suffix = new unsigned char[MAX_BITS];
memset(frame_reader->suffix, 0, MAX_BITS);
for (int i = 0; i < frame_reader->clear_code; i++)
frame_reader->suffix[i] = i;
if (!frame_reader->stack)
frame_reader->stack = new unsigned char[MAX_BITS];
frame_reader->stackp = frame_reader->stack;
}
GETN(1, gif_sub_block);
}
break;
case gif_type:
{
if (!strncmp((char*)q, "GIF89a", 6))
version = 89;
else if (!strncmp((char*)q, "GIF87a", 6))
version = 87;
else
return clientptr ? clientptr->setFailed() : false;
GETN(7, gif_global_header);
}
break;
case gif_global_header:
{
screen_width = GETINT16(q);
screen_height = GETINT16(q + 2);
if (clientptr && !clientptr->setSize(screen_width, screen_height))
return false;
screen_bgcolor = q[5];
global_colormap_size = 2<<(q[4]&0x07);
if ((q[4] & 0x80) && global_colormap_size > 0) {
const unsigned size = 3*global_colormap_size;
if (query != GIFImageDecoder::GIFFrameCountQuery)
global_colormap = new unsigned char[size];
if (len < size) {
GETN(size, gif_global_colormap);
break;
}
if (global_colormap)
memcpy(global_colormap, buf, size);
buf += size;
len -= size;
}
GETN(1, gif_image_start);
}
break;
case gif_global_colormap:
GETN(1, gif_image_start);
break;
case gif_image_start:
{
if (*q == ';') {
GETN(0, gif_done);
break;
}
if (*q == '!') {
GETN(2, gif_extension);
break;
}
if (*q != ',')
return clientptr ? clientptr->setFailed() : false;
GETN(9, gif_image_header);
}
break;
case gif_extension:
{
int len = count = q[1];
gstate es = gif_skip_block;
switch (*q)
{
case 0xf9:
es = gif_control_extension;
break;
case 0x01:
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 (query != GIFImageDecoder::GIFFrameCountQuery) {
if (!frame_reader)
frame_reader = new GIFFrameReader();
}
if (frame_reader) {
if (*q & 0x1) {
frame_reader->tpixel = q[3];
frame_reader->is_transparent = true;
} else {
frame_reader->is_transparent = false;
}
frame_reader->disposal_method = (WebCore::ImageFrame::FrameDisposalMethod)(((*q) >> 2) & 0x7);
if (frame_reader->disposal_method == 4)
frame_reader->disposal_method = WebCore::ImageFrame::DisposeOverwritePrevious;
frame_reader->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:
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;
case gif_netscape_extension_block:
if (*q)
GETN(*q, gif_consume_netscape_extension);
else
GETN(1, gif_image_start);
break;
case gif_consume_netscape_extension:
{
int netscape_extension = q[0] & 7;
if (netscape_extension == 1) {
loop_count = GETINT16(q + 1);
if (loop_count == 0)
loop_count = WebCore::cAnimationLoopInfinite;
GETN(1, gif_netscape_extension_block);
}
else if (netscape_extension == 2) {
GETN(1, gif_netscape_extension_block);
} else {
return clientptr ? clientptr->setFailed() : false;
}
break;
}
case gif_image_header:
{
unsigned height, width, x_offset, y_offset;
x_offset = GETINT16(q);
y_offset = GETINT16(q + 2);
width = GETINT16(q + 4);
height = GETINT16(q + 6);
if ((images_decoded == 0) &&
((screen_height < height) || (screen_width < width) ||
(version == 87)))
{
screen_height = height;
screen_width = width;
x_offset = 0;
y_offset = 0;
if (clientptr && !clientptr->setSize(screen_width, screen_height))
return false;
}
if (!height || !width) {
height = screen_height;
width = screen_width;
if (!height || !width)
return clientptr ? clientptr->setFailed() : false;
}
if (query == GIFImageDecoder::GIFSizeQuery || haltAtFrame == images_decoded) {
if (clientptr)
clientptr->decodingHalted(len + 9);
GETN(9, gif_image_header);
return true;
}
images_count = images_decoded + 1;
if (query == GIFImageDecoder::GIFFullQuery && !frame_reader)
frame_reader = new GIFFrameReader();
if (frame_reader) {
frame_reader->x_offset = x_offset;
frame_reader->y_offset = y_offset;
frame_reader->height = height;
frame_reader->width = width;
if (screen_width < width) {
delete []frame_reader->rowbuf;
screen_width = width;
frame_reader->rowbuf = new unsigned char[screen_width];
} else if (!frame_reader->rowbuf) {
frame_reader->rowbuf = new unsigned char[screen_width];
}
if (!frame_reader->rowbuf)
return clientptr ? clientptr->setFailed() : false;
if (screen_height < height)
screen_height = height;
if (q[8] & 0x40) {
frame_reader->interlaced = true;
frame_reader->ipass = 1;
} else {
frame_reader->interlaced = false;
frame_reader->ipass = 0;
}
if (images_decoded == 0) {
frame_reader->progressive_display = true;
} else {
frame_reader->progressive_display = false;
}
frame_reader->irow = 0;
frame_reader->rows_remaining = frame_reader->height;
frame_reader->rowend = frame_reader->rowbuf + frame_reader->width;
frame_reader->rowp = frame_reader->rowbuf;
}
if (q[8] & 0x80)
{
int num_colors = 2 << (q[8] & 0x7);
const unsigned size = 3*num_colors;
unsigned char *map = frame_reader ? frame_reader->local_colormap : 0;
if (frame_reader && (!map || (num_colors > frame_reader->local_colormap_size))) {
delete []map;
map = new unsigned char[size];
if (!map)
return clientptr ? clientptr->setFailed() : false;
}
if (frame_reader) {
frame_reader->local_colormap = map;
frame_reader->local_colormap_size = num_colors;
frame_reader->is_local_colormap_defined = true;
}
if (len < size) {
GETN(size, gif_image_colormap);
break;
}
if (frame_reader)
memcpy(frame_reader->local_colormap, buf, size);
buf += size;
len -= size;
} else if (frame_reader) {
frame_reader->is_local_colormap_defined = false;
}
GETN(1, gif_lzw_start);
}
break;
case gif_image_colormap:
GETN(1, gif_lzw_start);
break;
case gif_sub_block:
{
if ((count = *q) != 0)
{
if (frame_reader && frame_reader->rows_remaining == 0) {
GETN(1, gif_sub_block);
}
GETN(count, gif_lzw);
}
else
{
images_decoded++;
if (clientptr && frame_reader && !clientptr->frameComplete(images_decoded - 1, frame_reader->delay_time, frame_reader->disposal_method))
return false;
if (frame_reader) {
frame_reader->is_local_colormap_defined = false;
frame_reader->is_transparent = false;
}
GETN(1, gif_image_start);
}
}
break;
case gif_done:
if (clientptr)
clientptr->gifComplete();
return true;
default:
break;
}
}
bytes_in_hold = len;
if (len) {
unsigned char* p;
if (state == gif_global_colormap)
p = global_colormap;
else if (state == gif_image_colormap)
p = frame_reader ? frame_reader->local_colormap : 0;
else
p = hold;
if (p)
memcpy(p, buf, len);
bytes_to_consume -= len;
}
if (clientptr)
clientptr->decodingHalted(0);
return false;
}