*/
#include "avformat.h"
+int gif_write(ByteIOContext *pb, AVImageInfo *info);
+
//#define DEBUG
#define MAXBITS 12
int background_color_index;
int transparent_color_index;
int color_resolution;
- int image_count;
uint8_t *image_buf;
+ int image_linesize;
+ uint32_t *image_palette;
+ int pix_fmt;
+
/* after the frame is displayed, the disposal method is used */
int gce_disposal;
/* delay during which the frame is shown */
0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF
};
-static int gif_probe(AVProbeData * pd)
+/* Probe gif video format or gif image format. The current heuristic
+ supposes the gif87a is always a single image. For gif89a, we
+ consider it as a video only if a GCE extension is present in the
+ first kilobyte. */
+static int gif_video_probe(AVProbeData * pd)
+{
+ const uint8_t *p, *p_end;
+ int bits_per_pixel, has_global_palette, ext_code, ext_len;
+ int gce_flags, gce_disposal;
+
+ if (pd->buf_size < 24 ||
+ memcmp(pd->buf, gif89a_sig, 6) != 0)
+ return 0;
+ p_end = pd->buf + pd->buf_size;
+ p = pd->buf + 6;
+ bits_per_pixel = (p[4] & 0x07) + 1;
+ has_global_palette = (p[4] & 0x80);
+ p += 7;
+ if (has_global_palette)
+ p += (1 << bits_per_pixel) * 3;
+ for(;;) {
+ if (p >= p_end)
+ return 0;
+ if (*p != '!')
+ break;
+ p++;
+ if (p >= p_end)
+ return 0;
+ ext_code = *p++;
+ if (p >= p_end)
+ return 0;
+ ext_len = *p++;
+ if (ext_code == 0xf9) {
+ if (p >= p_end)
+ return 0;
+ /* if GCE extension found with gce_disposal != 0: it is
+ likely to be an animation */
+ gce_flags = *p++;
+ gce_disposal = (gce_flags >> 2) & 0x7;
+ if (gce_disposal != 0)
+ return AVPROBE_SCORE_MAX;
+ else
+ return 0;
+ }
+ for(;;) {
+ if (ext_len == 0)
+ break;
+ p += ext_len;
+ if (p >= p_end)
+ return 0;
+ ext_len = *p++;
+ }
+ }
+ return 0;
+}
+
+static int gif_image_probe(AVProbeData * pd)
{
if (pd->buf_size >= 24 &&
(memcmp(pd->buf, gif87a_sig, 6) == 0 ||
memcmp(pd->buf, gif89a_sig, 6) == 0))
- return AVPROBE_SCORE_MAX;
+ return AVPROBE_SCORE_MAX - 1;
else
return 0;
}
+
static void GLZWDecodeInit(GifState * s, int csize)
{
/* read buffer */
return len - l;
}
-static int gif_read_image(AVFormatContext * s1, AVPacket * pkt)
+static int gif_read_image(GifState *s)
{
- GifState *s = s1->priv_data;
+ ByteIOContext *f = s->f;
int left, top, width, height, bits_per_pixel, code_size, flags;
- int is_interleaved, has_local_palette, y, x, linesize;
- ByteIOContext *f = &s1->pb;
- uint8_t *ptr, *line, *d, *spal, *palette, *sptr;
+ int is_interleaved, has_local_palette, y, x, pass, y1, linesize, n, i;
+ uint8_t *ptr, *line, *d, *spal, *palette, *sptr, *ptr1;
left = get_le16(f);
top = get_le16(f);
palette = s->local_palette;
} else {
palette = s->global_palette;
+ bits_per_pixel = s->bits_per_pixel;
}
- /* allocate local image (XXX: horrible, needs API change) */
- if (s->image_count == 0) {
- /* modify screen width/height if invalid */
- if (left + width > s->screen_width)
- s->screen_width = left + width;
- if (top + height > s->screen_height)
- s->screen_width = top + height;
- s->image_buf = av_malloc(s->screen_width * s->screen_height * 3);
- if (!s->image_buf)
+ /* verify that all the image is inside the screen dimensions */
+ if (left + width > s->screen_width ||
+ top + height > s->screen_height)
+ return -EINVAL;
+
+ /* build the palette */
+ if (s->pix_fmt == PIX_FMT_RGB24) {
+ line = av_malloc(width);
+ if (!line)
return -ENOMEM;
} else {
- /* verify that all the image is inside the screen dimensions */
- if (left + width > s->screen_width ||
- top + height > s->screen_height)
- return -EINVAL;
+ n = (1 << bits_per_pixel);
+ spal = palette;
+ for(i = 0; i < n; i++) {
+ s->image_palette[i] = (0xff << 24) |
+ (spal[0] << 16) | (spal[1] << 8) | (spal[2]);
+ spal += 3;
+ }
+ for(; i < 256; i++)
+ s->image_palette[i] = (0xff << 24);
+ /* handle transparency */
+ if (s->transparent_color_index >= 0)
+ s->image_palette[s->transparent_color_index] = 0;
+ line = NULL;
}
- s->image_count++;
-
- line = av_malloc(width);
- if (!line)
- return -ENOMEM;
/* now get the image data */
s->f = f;
code_size = get_byte(f);
GLZWDecodeInit(s, code_size);
- /* read all the image and transcode it to RGB24 (horrible) */
- linesize = s->screen_width * 3;
- ptr = s->image_buf + top * linesize + (left * 3);
+ /* read all the image */
+ linesize = s->image_linesize;
+ ptr1 = s->image_buf + top * linesize + (left * 3);
+ ptr = ptr1;
+ pass = 0;
+ y1 = 0;
for (y = 0; y < height; y++) {
- GLZWDecode(s, line, width);
- d = ptr;
- sptr = line;
- for(x = 0; x < width; x++) {
- spal = palette + sptr[0] * 3;
- d[0] = spal[0];
- d[1] = spal[1];
- d[2] = spal[2];
- d += 3;
- sptr++;
+ if (s->pix_fmt == PIX_FMT_RGB24) {
+ /* transcode to RGB24 */
+ GLZWDecode(s, line, width);
+ d = ptr;
+ sptr = line;
+ for(x = 0; x < width; x++) {
+ spal = palette + sptr[0] * 3;
+ d[0] = spal[0];
+ d[1] = spal[1];
+ d[2] = spal[2];
+ d += 3;
+ sptr++;
+ }
+ } else {
+ GLZWDecode(s, ptr, width);
+ }
+ if (is_interleaved) {
+ switch(pass) {
+ default:
+ case 0:
+ case 1:
+ y1 += 8;
+ ptr += linesize * 8;
+ if (y1 >= height) {
+ y1 = 4;
+ if (pass == 0)
+ ptr = ptr1 + linesize * 4;
+ else
+ ptr = ptr1 + linesize * 2;
+ pass++;
+ }
+ break;
+ case 2:
+ y1 += 4;
+ ptr += linesize * 4;
+ if (y1 >= height) {
+ y1 = 1;
+ ptr = ptr1 + linesize;
+ pass++;
+ }
+ break;
+ case 3:
+ y1 += 2;
+ ptr += linesize * 2;
+ break;
+ }
+ } else {
+ ptr += linesize;
}
- ptr += linesize;
}
av_free(line);
/* read the garbage data until end marker is found */
while (!s->eob_reached)
GetCode(s);
-
- /* output image */
- /* XXX: avoid copying */
- if (av_new_packet(pkt, s->screen_width * s->screen_height * 3)) {
- av_free(line);
- return -EIO;
- }
- pkt->stream_index = 0;
- memcpy(pkt->data, s->image_buf, s->screen_width * s->screen_height * 3);
return 0;
}
-static int gif_read_extension(AVFormatContext * s1)
+static int gif_read_extension(GifState *s)
{
- GifState *s = s1->priv_data;
- ByteIOContext *f = &s1->pb;
+ ByteIOContext *f = s->f;
int ext_code, ext_len, i, gce_flags, gce_transparent_index;
/* extension */
return 0;
}
-static int gif_read_header(AVFormatContext * s1,
- AVFormatParameters * ap)
+static int gif_read_header1(GifState *s)
{
- GifState *s = s1->priv_data;
- ByteIOContext *f = &s1->pb;
- AVStream *st;
+ ByteIOContext *f = s->f;
uint8_t sig[6];
int ret, v, n;
int has_global_palette;
s->transparent_color_index = -1;
s->screen_width = get_le16(f);
s->screen_height = get_le16(f);
+ if( (unsigned)s->screen_width > 32767
+ || (unsigned)s->screen_height > 32767){
+ av_log(NULL, AV_LOG_ERROR, "picture size too large\n");
+ return -1;
+ }
+
v = get_byte(f);
s->color_resolution = ((v & 0x70) >> 4) + 1;
has_global_palette = (v & 0x80);
n = 1 << s->bits_per_pixel;
get_buffer(f, s->global_palette, n * 3);
}
- /* now we are ready: build format streams */
- st = av_new_stream(s1, 0);
- if (!st)
- return -1;
-
- st->codec.codec_type = CODEC_TYPE_VIDEO;
- st->codec.codec_id = CODEC_ID_RAWVIDEO;
- st->codec.frame_rate = 5 * FRAME_RATE_BASE;
- /* XXX: check if screen size is always valid */
- st->codec.width = s->screen_width;
- st->codec.height = s->screen_height;
- st->codec.pix_fmt = PIX_FMT_RGB24;
return 0;
}
-static int gif_read_packet(AVFormatContext * s1,
- AVPacket * pkt)
+static int gif_parse_next_image(GifState *s)
{
- ByteIOContext *f = &s1->pb;
+ ByteIOContext *f = s->f;
int ret, code;
for (;;) {
#endif
switch (code) {
case ',':
- if (gif_read_image(s1, pkt) < 0)
- return -EIO;
+ if (gif_read_image(s) < 0)
+ return AVERROR_IO;
ret = 0;
goto the_end;
case ';':
/* end of image */
- ret = -EIO;
+ ret = AVERROR_IO;
goto the_end;
case '!':
- if (gif_read_extension(s1) < 0)
- return -EIO;
+ if (gif_read_extension(s) < 0)
+ return AVERROR_IO;
break;
case EOF:
default:
/* error or errneous EOF */
- ret = -EIO;
+ ret = AVERROR_IO;
goto the_end;
}
}
return ret;
}
+static int gif_read_header(AVFormatContext * s1,
+ AVFormatParameters * ap)
+{
+ GifState *s = s1->priv_data;
+ ByteIOContext *f = &s1->pb;
+ AVStream *st;
+
+ s->f = f;
+ if (gif_read_header1(s) < 0)
+ return -1;
+
+ /* allocate image buffer */
+ s->image_linesize = s->screen_width * 3;
+ s->image_buf = av_malloc(s->screen_height * s->image_linesize);
+ if (!s->image_buf)
+ return -ENOMEM;
+ s->pix_fmt = PIX_FMT_RGB24;
+ /* now we are ready: build format streams */
+ st = av_new_stream(s1, 0);
+ if (!st)
+ return -1;
+
+ st->codec->codec_type = CODEC_TYPE_VIDEO;
+ st->codec->codec_id = CODEC_ID_RAWVIDEO;
+ st->codec->time_base.den = 5;
+ st->codec->time_base.num = 1;
+ /* XXX: check if screen size is always valid */
+ st->codec->width = s->screen_width;
+ st->codec->height = s->screen_height;
+ st->codec->pix_fmt = PIX_FMT_RGB24;
+ return 0;
+}
+
+static int gif_read_packet(AVFormatContext * s1,
+ AVPacket * pkt)
+{
+ GifState *s = s1->priv_data;
+ int ret;
+
+ ret = gif_parse_next_image(s);
+ if (ret < 0)
+ return ret;
+
+ /* XXX: avoid copying */
+ if (av_new_packet(pkt, s->screen_width * s->screen_height * 3)) {
+ return AVERROR_IO;
+ }
+ pkt->stream_index = 0;
+ memcpy(pkt->data, s->image_buf, s->screen_width * s->screen_height * 3);
+ return 0;
+}
+
static int gif_read_close(AVFormatContext *s1)
{
GifState *s = s1->priv_data;
return 0;
}
+/* read gif as image */
+static int gif_read(ByteIOContext *f,
+ int (*alloc_cb)(void *opaque, AVImageInfo *info), void *opaque)
+{
+ GifState s1, *s = &s1;
+ AVImageInfo info1, *info = &info1;
+ int ret;
+
+ memset(s, 0, sizeof(GifState));
+ s->f = f;
+ if (gif_read_header1(s) < 0)
+ return -1;
+ info->width = s->screen_width;
+ info->height = s->screen_height;
+ info->pix_fmt = PIX_FMT_PAL8;
+ ret = alloc_cb(opaque, info);
+ if (ret)
+ return ret;
+ s->image_buf = info->pict.data[0];
+ s->image_linesize = info->pict.linesize[0];
+ s->image_palette = (uint32_t *)info->pict.data[1];
+
+ if (gif_parse_next_image(s) < 0)
+ return -1;
+ return 0;
+}
+
AVInputFormat gif_iformat =
{
"gif",
"gif format",
sizeof(GifState),
- gif_probe,
+ gif_video_probe,
gif_read_header,
gif_read_packet,
gif_read_close,
};
+
+AVImageFormat gif_image_format = {
+ "gif",
+ "gif",
+ gif_image_probe,
+ gif_read,
+ (1 << PIX_FMT_PAL8),
+ gif_write,
+};