Now progressive gif loading works! It's a little slow though. )-:

1999-11-02  Jonathan Blandford  <jrb@redhat.com>

	* src/io-gif.c (gif_main_loop): Now progressive gif loading works!
	It's a little slow though. )-:
This commit is contained in:
Jonathan Blandford
1999-11-02 23:49:22 +00:00
committed by Jonathan Blandford
parent 587d91caad
commit 53d5c49dbb
4 changed files with 333 additions and 144 deletions

View File

@@ -520,7 +520,7 @@ main (int argc, char **argv)
int nbytes;
nbytes = fread(buf, 1, readlen, file);
printf(".");
/* printf(".");*/
fflush(stdout);
if (gdk_pixbuf_loader_write (GDK_PIXBUF_LOADER (pixbuf_loader), buf, nbytes) == FALSE)
@@ -529,7 +529,7 @@ main (int argc, char **argv)
while (gtk_events_pending())
gtk_main_iteration();
}
printf("\n");
/* printf("\n");*/
gtk_timeout_remove (timeout);
gdk_pixbuf_loader_close (GDK_PIXBUF_LOADER (pixbuf_loader));
gtk_object_destroy (GTK_OBJECT(pixbuf_loader));

View File

@@ -1,3 +1,8 @@
1999-11-02 Jonathan Blandford <jrb@redhat.com>
* src/io-gif.c (gif_main_loop): Now progressive gif loading works!
It's a little slow though. )-:
1999-11-02 Elliot Lee <sopwith@redhat.com>
* src/gdk-pixbuf-loader.[ch], src/gdk-pixbuf-io.h: Make the arguments to the
write/load_increment operations const-correct.

View File

@@ -37,7 +37,7 @@ AM_CPPFLAGS = "-DPIXBUF_LIBDIR=\"$(libexecdir)\""
LDADDS = libgdk_pixbuf.la $(LIBART_LIBS) $(GLIB_LIBS)
testpixbuf_LDADD = $(LDADDS) $(LIBART_LIBS)
testpixbuf_LDADD = $(LDADDS) $(LIBART_LIBS) -lgnomeui -lart_lgpl -lgdk_imlib -lgtk -lgdk -lgmodule -lgnome -lgnomesupport
#
# The GdkPixBuf library

View File

@@ -54,13 +54,15 @@ typedef unsigned char CMap[3][MAXCOLORMAPSIZE];
/* Possible states we can be in. */
enum {
GIF_START = 1,
GIF_GET_COLORMAP = 2,
GIF_GET_NEXT_STEP = 3,
GIF_GET_EXTENTION = 4,
GIF_GET_COLORMAP2 = 5,
GIF_PREPARE_LZW = 6,
GIF_GET_LZW = 7,
GIF_DONE = 8
GIF_GET_COLORMAP,
GIF_GET_NEXT_STEP,
GIF_GET_FRAME_INFO,
GIF_GET_EXTENTION,
GIF_GET_COLORMAP2,
GIF_PREPARE_LZW,
GIF_LZW_FILL_BUFFER,
GIF_GET_LZW,
GIF_DONE,
};
@@ -105,20 +107,26 @@ struct _GifContext
guint size;
guint amount_needed;
/* our colormap context */
/* colormap context */
gint colormap_index;
gint colormap_flag;
/* our extension context */
/* extension context */
guchar extension_label;
guchar extension_flag;
/* get block context */
guchar block_count;
guchar block_buf[256];
guchar block_buf[280];
gint block_ptr;
/* our lwz context */
/* get_code context */
int code_curbit;
int code_lastbit;
int code_done;
int code_last_byte;
/* lwz context */
gint lwz_fresh;
gint lwz_code_size;
guchar lwz_set_code_size;
@@ -131,40 +139,71 @@ struct _GifContext
gint lwz_table[2][(1 << MAX_LWZ_BITS)];
gint lwz_stack[(1 << (MAX_LWZ_BITS)) * 2];
gint *lwz_sp;
/* painting context */
gint draw_xpos;
gint draw_ypos;
gint draw_pass;
};
static int GetDataBlock (GifContext *, unsigned char *);
static int GetCode (GifContext *, int, int);
static int LWZReadByte (GifContext *);
static int GetCode (GifContext *, int);
/* Returns TRUE if Read is OK,
* FALSE if more memory is needed. */
static int count = 0;
static int
ReadOK (GifContext *context, guchar *buffer, size_t len)
{
static int count = 0;
count += len;
g_print ("size :%d\tcount :%d\n", len, count);
if (context->file)
return fread(buffer, len, 1, context->file) != 0;
else {
if ((context->size - context->ptr) < len) {
gint retval, i;
if (context->file) {
count += len;
#ifdef IO_GIFDEBUG
g_print ("Fsize :%d\tcount :%d\t", len, count);
#endif
retval = (fread(buffer, len, 1, context->file) != 0);
#ifdef IO_GIFDEBUG
if (len < 100) {
for (i = 0; i < len; i++)
g_print ("%d ", buffer[i]);
}
g_print ("\n");
#endif
return retval;
} else {
#ifdef IO_GIFDEBUG
g_print ("\tlooking for %d bytes. size == %d, ptr == %d\n", len, context->size, context->ptr);
#endif
if ((context->size - context->ptr) >= len) {
count += len;
memcpy (buffer, context->buf + context->ptr, len);
context->ptr += len;
context->amount_needed = 0;
return 0;
#ifdef IO_GIFDEBUG
g_print ("Psize :%d\tcount :%d\t", len, count);
if (len < 100) {
for (i = 0; i < len; i++)
g_print ("%d ", buffer[i]);
}
g_print ("\n");
#endif
return TRUE;
}
context->amount_needed = len - (context->size - context->ptr);
}
return 1;
return 0;
}
/*
* Return vals.
* Unless otherwise specified, these are the return vals for most functions:
*
* for the gif_get* functions,
*-1 -> more bytes needed.
* 0 -> success
* 1 -> failure; abort the load
* 0 -> success
* -1 -> more bytes needed.
* -2 -> failure; abort the load
* -3 -> control needs to be passed back to the main loop
* \_ (most of the time returning 0 will get this, but not always)
*/
@@ -218,7 +257,7 @@ gif_get_colormap (GifContext *context)
* context->block_count is 0 before we read in the function.
*
* As a result, context->block_count MUST be 0 the first time the get_data_block is called
* within a context.
* within a context, and cannot be 0 the second time it's called.
*/
static int
@@ -234,10 +273,10 @@ get_data_block (GifContext *context,
}
if (context->block_count == 0)
if (empty_block)
if (empty_block) {
*empty_block = TRUE;
return 0;
return 0;
}
if (!ReadOK (context, buf, context->block_count)) {
return -1;
@@ -249,7 +288,6 @@ get_data_block (GifContext *context,
static void
gif_set_get_extension (GifContext *context)
{
g_print ("getting the extension woooooo\n");
context->state = GIF_GET_EXTENTION;
context->extension_flag = TRUE;
context->extension_label = '\000';
@@ -263,7 +301,6 @@ gif_get_extension (GifContext *context)
gint retval;
gint empty_block = FALSE;
g_print ("in gif_get_extension\n");
if (context->extension_flag) {
if (!context->extension_label) {
if (!ReadOK (context, &context->extension_label , 1)) {
@@ -289,6 +326,7 @@ gif_get_extension (GifContext *context)
}
/* Now we've successfully loaded this one, we continue on our way */
context->block_count = 0;
context->extension_flag = FALSE;
default:
/* Unhandled extension */
@@ -329,62 +367,132 @@ GetDataBlock (GifContext *context,
return context->block_count;
}
#if 0
static int
GetCode (GifContext *context,
int code_size,
int flag)
int code_size)
{
static unsigned char buf[280];
static int curbit, lastbit, done, last_byte;
int i, j, ret;
unsigned char count;
if (flag) {
curbit = 0;
lastbit = 0;
done = FALSE;
return 0;
}
if ((curbit + code_size) >= lastbit){
if (done) {
if (curbit >= lastbit) {
if ((context->code_curbit + code_size) >= context->code_lastbit){
if (context->code_done) {
if (context->code_curbit >= context->code_lastbit) {
/*g_message (_("GIF: ran off the end of by bits\n"));*/
return -1;
return -2;
}
return -1;
return -2;
}
buf[0] = buf[last_byte - 2];
buf[1] = buf[last_byte - 1];
context->block_buf[0] = context->block_buf[context->code_last_byte - 2];
context->block_buf[1] = context->block_buf[context->code_last_byte - 1];
if ((count = GetDataBlock (context, &buf[2])) == 0)
done = TRUE;
context->block_count = 0;
if ((count = GetDataBlock (context, &context->block_buf[2])) == 0)
context->code_done = TRUE;
last_byte = 2 + count;
curbit = (curbit - lastbit) + 16;
lastbit = (2 + count) * 8;
context->code_last_byte = 2 + count;
context->code_curbit = (context->code_curbit - context->code_lastbit) + 16;
context->code_lastbit = (2 + count) * 8;
}
ret = 0;
for (i = curbit, j = 0; j < code_size; ++i, ++j)
ret |= ((buf[i / 8] & (1 << (i % 8))) != 0) << j;
for (i = context->code_curbit, j = 0; j < code_size; ++i, ++j)
ret |= ((context->block_buf[i / 8] & (1 << (i % 8))) != 0) << j;
curbit += code_size;
context->code_curbit += code_size;
return ret;
}
#endif
static void
gif_set_lzw_fill_buffer (GifContext *context)
{
context->block_count = 0;
context->state = GIF_LZW_FILL_BUFFER;
}
static int
gif_lzw_fill_buffer (GifContext *context)
{
gint retval;
if (context->code_done) {
if (context->code_curbit >= context->code_lastbit) {
g_message ("GIF: ran off the end of by bits\n");
return -2;
}
g_message ("trying to read more data after we've done stuff\n");
return -2;
}
context->block_buf[0] = context->block_buf[context->code_last_byte - 2];
context->block_buf[1] = context->block_buf[context->code_last_byte - 1];
retval = get_data_block (context, &context->block_buf[2], NULL);
if (retval == -1)
return -1;
if (context->block_count == 0)
context->code_done = TRUE;
context->code_last_byte = 2 + context->block_count;
context->code_curbit = (context->code_curbit - context->code_lastbit) + 16;
context->code_lastbit = (2 + context->block_count) * 8;
context->state = GIF_GET_LZW;
return 0;
}
static int
GetCode (GifContext *context,
int code_size)
{
int i, j, ret;
if ((context->code_curbit + code_size) >= context->code_lastbit){
gif_set_lzw_fill_buffer (context);
return -3;
}
ret = 0;
for (i = context->code_curbit, j = 0; j < code_size; ++i, ++j)
ret |= ((context->block_buf[i / 8] & (1 << (i % 8))) != 0) << j;
context->code_curbit += code_size;
return ret;
}
/*
* For various reasons, this one is a little different from the others:
*
* returns -1 if it needs more bytes.
* returns -2 if there's an error.
* returns -3 if there is no error, but needs to go back to the main loop.
* returns the byte otherwise.
*/
static int
LWZReadByte (GifContext *context)
lwz_read_byte (GifContext *context)
{
int code, incode;
gint retval;
register int i;
if (context->lwz_fresh) {
context->lwz_fresh = FALSE;
do {
context->lwz_firstcode = context->lwz_oldcode = GetCode (context, context->lwz_code_size, FALSE);
retval = GetCode (context, context->lwz_code_size);
if (retval < 0) {
return retval;
}
context->lwz_firstcode = context->lwz_oldcode = retval;
} while (context->lwz_firstcode == context->lwz_clear_code);
return context->lwz_firstcode;
}
@@ -392,7 +500,7 @@ LWZReadByte (GifContext *context)
if (context->lwz_sp > context->lwz_stack)
return *--(context->lwz_sp);
while ((code = GetCode (context, context->lwz_code_size, FALSE)) >= 0) {
while ((code = GetCode (context, context->lwz_code_size)) >= 0) {
if (code == context->lwz_clear_code) {
for (i = 0; i < context->lwz_clear_code; ++i) {
context->lwz_table[0][i] = 0;
@@ -405,12 +513,14 @@ LWZReadByte (GifContext *context)
context->lwz_max_code = context->lwz_clear_code + 2;
context->lwz_sp = context->lwz_stack;
context->lwz_firstcode = context->lwz_oldcode =
GetCode (context, context->lwz_code_size, FALSE);
GetCode (context, context->lwz_code_size);
return context->lwz_firstcode;
} else if (code == context->lwz_end_code) {
int count;
unsigned char buf[260];
g_error (" DID WE EVER EVER GET HERE\n");
if (ZeroDataBlock)
return -2;
@@ -434,7 +544,7 @@ LWZReadByte (GifContext *context)
if (code == context->lwz_table[0][code]) {
/*g_message (_("GIF: circular table entry BIG ERROR\n"));*/
/*gimp_quit ();*/
return -1;
return -2;
}
code = context->lwz_table[0][code];
}
@@ -460,80 +570,106 @@ LWZReadByte (GifContext *context)
return code;
}
static void
gif_set_get_lzw (GifContext *context)
{
context->state = GIF_GET_LZW;
context->draw_xpos = 0;
context->draw_ypos = 0;
context->draw_pass = 0;
}
static int
gif_get_lzw (GifContext *context)
{
guchar *dest, *temp;
gint xpos = 0, ypos = 0, pass = 0;
gint v;
context->pixbuf = gdk_pixbuf_new (ART_PIX_RGB,
context->gif89.transparent,
8,
context->width,
context->height);
if (context->pixbuf == NULL) {
context->pixbuf = gdk_pixbuf_new (ART_PIX_RGB,
context->gif89.transparent,
8,
context->width,
context->height);
if (context->func)
(* context->func) (context->pixbuf, context->user_data);
}
dest = gdk_pixbuf_get_pixels (context->pixbuf);
while ((v = LWZReadByte ( context)) >= 0) {
while (TRUE) {
v = lwz_read_byte (context);
if (v < 0) {
return v;
}
if (context->gif89.transparent) {
temp = dest + ypos * gdk_pixbuf_get_rowstride (context->pixbuf) + xpos * 4;
temp = dest + context->draw_ypos * gdk_pixbuf_get_rowstride (context->pixbuf) + context->draw_xpos * 4;
*temp = context->color_map [0][(guchar) v];
*(temp+1) = context->color_map [1][(guchar) v];
*(temp+2) = context->color_map [2][(guchar) v];
*(temp+3) = (guchar) ((v == context->gif89.transparent) ? 0 : 65535);
} else {
temp = dest + ypos * gdk_pixbuf_get_rowstride (context->pixbuf) + xpos * 3;
temp = dest + context->draw_ypos * gdk_pixbuf_get_rowstride (context->pixbuf) + context->draw_xpos * 3;
*temp = context->color_map [0][(guchar) v];
*(temp+1) = context->color_map [1][(guchar) v];
*(temp+2) = context->color_map [2][(guchar) v];
}
xpos++;
if (xpos == context->frame_len) {
xpos = 0;
context->draw_xpos++;
if (context->draw_xpos == context->frame_len) {
context->draw_xpos = 0;
if (context->frame_interlace) {
switch (pass) {
switch (context->draw_pass) {
case 0:
case 1:
ypos += 8;
context->draw_ypos += 8;
break;
case 2:
ypos += 4;
context->draw_ypos += 4;
break;
case 3:
ypos += 2;
context->draw_ypos += 2;
break;
}
if (ypos >= context->frame_height) {
pass++;
switch (pass) {
if (context->draw_ypos >= context->frame_height) {
context->draw_pass++;
switch (context->draw_pass) {
case 1:
ypos = 4;
context->draw_ypos = 4;
break;
case 2:
ypos = 2;
context->draw_ypos = 2;
break;
case 3:
ypos = 1;
context->draw_ypos = 1;
break;
default:
goto done;
}
}
} else {
ypos++;
context->draw_ypos++;
}
}
if (ypos >= context->frame_height)
if (context->draw_ypos >= context->frame_height)
break;
}
done:
/* we got enough data. there may be more (ie, newer layers) but we can quit now */
context->state = GIF_DONE;
return 0;
}
static void
gif_set_prepare_lzw (GifContext *context)
{
context->state = GIF_PREPARE_LZW;
}
static int
gif_prepare_lzw (GifContext *context)
{
@@ -549,10 +685,10 @@ gif_prepare_lzw (GifContext *context)
context->lwz_end_code = context->lwz_clear_code + 1;
context->lwz_max_code_size = 2 * context->lwz_clear_code;
context->lwz_max_code = context->lwz_clear_code + 2;
GetCode (context, 0, TRUE);
context->lwz_fresh = TRUE;
context->code_curbit = 0;
context->code_lastbit = 0;
context->code_done = FALSE;
for (i = 0; i < context->lwz_clear_code; ++i) {
context->lwz_table[0][i] = 0;
@@ -562,6 +698,8 @@ gif_prepare_lzw (GifContext *context)
context->lwz_table[0][i] = context->lwz_table[1][0] = 0;
context->lwz_sp = context->lwz_stack;
gif_set_get_lzw (context);
return 0;
}
@@ -612,23 +750,58 @@ gif_init (GifContext *context)
return 0;
}
static void
gif_set_get_frame_info (GifContext *context)
{
context->state = GIF_GET_FRAME_INFO;
}
static gint
gif_get_frame_info (GifContext *context)
{
unsigned char buf[9];
if (!ReadOK (context, buf, 9)) {
return -1;
}
/* Okay, we got all the info we need. Lets record it */
context->frame_len = LM_to_uint (buf[4], buf[5]);
context->frame_height = LM_to_uint (buf[6], buf[7]);
if (context->frame_height > context->height) {
context->state = GIF_DONE;
return -2;
}
context->frame_interlace = BitSet (buf[8], INTERLACE);
if (BitSet (buf[8], LOCALCOLORMAP)) {
/* Does this frame have it's own colormap. */
/* really only relevant when looking at the first frame
* of an animated gif. */
/* if it does, we need to re-read in the colormap,
* the gray_scale, and the bit_pixel */
context->bit_pixel = 1 << ((buf[8] & 0x07) + 1);
gif_set_get_colormap2 (context);
return 0;
}
gif_set_prepare_lzw (context);
return 0;
}
static gint
gif_get_next_step (GifContext *context)
{
unsigned char buf[16];
unsigned char c;
for (;;) {
while (TRUE) {
if (!ReadOK (context, &c, 1)) {
return -1;
}
g_print ("c== %c\n", c);
if (c == ';') {
/* GIF terminator */
/* hmm. Not 100% sure what to do about this. Should
* i try to return a blank image instead? */
context->state = GIF_DONE;
return 1;
return -2;
}
if (c == '!') {
@@ -642,35 +815,10 @@ gif_get_next_step (GifContext *context)
/* Not a valid start character */
continue;
}
if (!ReadOK (context, buf, 9)) {
return -1;
}
/* Okay, we got all the info we need. Lets record it */
context->frame_len = LM_to_uint (buf[4], buf[5]);
context->frame_height = LM_to_uint (buf[6], buf[7]);
if (context->frame_height > context->height) {
context->state = GIF_DONE;
return 1;
}
context->frame_interlace = BitSet (buf[8], INTERLACE);
if (BitSet (buf[8], LOCALCOLORMAP)) {
/* Does this frame have it's own colormap. */
/* really only relevant when looking at the first frame
* of an animated gif. */
/* if it does, we need to re-read in the colormap,
* the gray_scale, and the bit_pixel */
context->bit_pixel = 1 << ((buf[8] & 0x07) + 1);
gif_set_get_colormap2 (context);
return 0;
}
context->state = GIF_PREPARE_LZW;
/* assume it's frame info */
gif_set_get_frame_info (context);
return 0;
}
g_assert_not_reached ();
return 1;
}
@@ -682,56 +830,53 @@ gif_main_loop (GifContext *context)
do {
switch (context->state) {
case GIF_START:
g_print ("in GIF_START\n");
retval = gif_init (context);
break;
case GIF_GET_COLORMAP:
g_print ("in GIF_GET_COLORMAP\n");
retval = gif_get_colormap (context);
if (retval == 0)
context->state = GIF_GET_NEXT_STEP;
break;
case GIF_GET_NEXT_STEP:
g_print ("in GIF_GET_NEXT_STEP\n");
retval = gif_get_next_step (context);
break;
case GIF_GET_FRAME_INFO:
retval = gif_get_frame_info (context);
break;
case GIF_GET_EXTENTION:
g_print ("in GIF_GET_EXTENTION\n");
retval = gif_get_extension (context);
if (retval == 0)
context->state = GIF_GET_NEXT_STEP;
break;
case GIF_GET_COLORMAP2:
g_print ("in GIF_GET_COLORMAP2\n");
retval = gif_get_colormap (context);
if (retval == 0)
context->state = GIF_PREPARE_LZW;
gif_set_prepare_lzw (context);
break;
case GIF_PREPARE_LZW:
g_print ("in GIF_PREPARE_LZW\n");
retval = gif_prepare_lzw (context);
if (retval == 0)
context->state = GIF_GET_LZW;
break;
case GIF_LZW_FILL_BUFFER:
retval = gif_lzw_fill_buffer (context);
break;
case GIF_GET_LZW:
g_print ("in GIF_GET_LZW\n");
gif_get_lzw (context);
retval = 0;
retval = gif_get_lzw (context);
break;
case GIF_DONE:
default:
g_print ("in GIF_DONE\n");
retval = 0;
goto done;
};
} while (retval == 0);
} while ((retval == 0) || (retval == -3));
done:
return retval;
}
@@ -745,10 +890,10 @@ image_load (FILE *file)
g_return_val_if_fail (file != NULL, NULL);
context = g_new (GifContext, 1);
context->lwz_fresh = FALSE;
context->file = file;
context->pixbuf = NULL;
context->state = GIF_START;
context->func = NULL;
gif_main_loop (context);
@@ -760,14 +905,15 @@ image_begin_load (ModulePreparedNotifyFunc func, gpointer user_data)
{
GifContext *context;
count = 0;
context = g_new (GifContext, 1);
context->func = func;
context->user_data = user_data;
context->lwz_fresh = FALSE;
context->file = NULL;
context->pixbuf = NULL;
context->state = GIF_START;
context->buf = NULL;
context->amount_needed = 0;
return (gpointer) context;
}
@@ -778,6 +924,7 @@ image_stop_load (gpointer data)
if (context->pixbuf)
gdk_pixbuf_unref (context->pixbuf);
// g_free (context->buf);
g_free (context);
}
@@ -787,17 +934,54 @@ image_load_increment (gpointer data, guchar *buf, guint size)
gint retval;
GifContext *context = (GifContext *) data;
return FALSE;
if (context->amount_needed == 0) {
/* we aren't looking for some bytes. */
/* we can use buf now, but we don't want to keep it around at all.
* it will be gone by the end of the call. */
context->buf = buf;
context->ptr = 0;
context->size = size;
}
} else {
/* we need some bytes */
if (size < context->amount_needed) {
context->amount_needed -= size;
/* copy it over and return */
memcpy (context->buf + context->size, buf, size);
context->size += size;
return TRUE;
} else if (size == context->amount_needed) {
memcpy (context->buf + context->size, buf, size);
context->size += size;
} else {
context->buf = g_realloc (context->buf, context->size + size);
memcpy (context->buf + context->size, buf, size);
context->size += size;
}
}
retval = gif_main_loop (context);
if (retval == 1)
if (retval == -2)
return FALSE;
if (retval == -1) {
/* we didn't have enough memory */
/* prepare for the next image_load_increment */
if (context->buf == buf) {
g_assert (context->size == size);
context->buf = (guchar *)g_new (guchar, context->amount_needed + (context->size - context->ptr));
memcpy (context->buf, buf + context->ptr, context->size - context->ptr);
} else {
/* copy the left overs to the begining of the buffer */
/* and realloc the memory */
memcpy (context->buf, context->buf, context->size - context->ptr);
context->buf = g_realloc (context->buf, context->amount_needed + (context->size - context->ptr));
}
context->size = context->size - context->ptr;
context->ptr = 0;
} else {
/* we are prolly all done */
if (context->buf == buf)
context->buf = NULL;
}
return TRUE;
}