Blame gdk-pixbuf/io-ani.c

Packit a4058c
/* -*- mode: C; c-file-style: "linux" -*- */
Packit a4058c
/* GdkPixbuf library - ANI image loader
Packit a4058c
 *
Packit a4058c
 * Copyright (C) 2002 The Free Software Foundation
Packit a4058c
 *
Packit a4058c
 * Authors: Matthias Clasen <maclas@gmx.de>
Packit a4058c
 *
Packit a4058c
 * This library is free software; you can redistribute it and/or
Packit a4058c
 * modify it under the terms of the GNU Lesser General Public
Packit a4058c
 * License as published by the Free Software Foundation; either
Packit a4058c
 * version 2 of the License, or (at your option) any later version.
Packit a4058c
 *
Packit a4058c
 * This library is distributed in the hope that it will be useful,
Packit a4058c
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit a4058c
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit a4058c
 * Lesser General Public License for more details.
Packit a4058c
 *
Packit a4058c
 * You should have received a copy of the GNU Lesser General Public
Packit a4058c
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit a4058c
 */
Packit a4058c
Packit a4058c
#undef DEBUG_ANI
Packit a4058c
Packit a4058c
#include "config.h"
Packit a4058c
#include <stdlib.h>
Packit a4058c
#include <string.h>
Packit a4058c
#include "gdk-pixbuf-private.h"
Packit a4058c
#include "gdk-pixbuf-loader.h"
Packit a4058c
#include "io-ani-animation.h"
Packit a4058c
Packit a4058c
static int
Packit a4058c
lsb_32 (guchar *src)
Packit a4058c
{
Packit a4058c
	return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
Packit a4058c
}
Packit a4058c
Packit a4058c
#define MAKE_TAG(a,b,c,d) ( (guint32)d << 24 | \
Packit a4058c
			    (guint32)c << 16 | \
Packit a4058c
			    (guint32)b <<  8 | \
Packit a4058c
			    (guint32)a )
Packit a4058c
Packit a4058c
#define TAG_RIFF MAKE_TAG('R','I','F','F')
Packit a4058c
#define TAG_ACON MAKE_TAG('A','C','O','N')
Packit a4058c
#define TAG_LIST MAKE_TAG('L','I','S','T')
Packit a4058c
#define TAG_INAM MAKE_TAG('I','N','A','M')
Packit a4058c
#define TAG_IART MAKE_TAG('I','A','R','T')
Packit a4058c
#define TAG_anih MAKE_TAG('a','n','i','h')
Packit a4058c
#define TAG_seq  MAKE_TAG('s','e','q',' ')
Packit a4058c
#define TAG_rate MAKE_TAG('r','a','t','e')
Packit a4058c
#define TAG_icon MAKE_TAG('i','c','o','n')
Packit a4058c
Packit a4058c
typedef struct _AniLoaderContext 
Packit a4058c
{
Packit a4058c
        guint32 cp;
Packit a4058c
        
Packit a4058c
        guchar *buffer;
Packit a4058c
        guchar *byte;
Packit a4058c
        guint   n_bytes;
Packit a4058c
        guint   buffer_size;
Packit a4058c
        
Packit a4058c
        GdkPixbufModulePreparedFunc prepared_func;
Packit a4058c
        GdkPixbufModuleUpdatedFunc updated_func;
Packit a4058c
        gpointer user_data;
Packit a4058c
        
Packit a4058c
        guint32 data_size;
Packit a4058c
        
Packit a4058c
        guint32 HeaderSize;
Packit a4058c
        guint32 NumFrames; 
Packit a4058c
        guint32 NumSteps; 
Packit a4058c
        guint32 Width;   
Packit a4058c
        guint32 Height; 
Packit a4058c
        guint32 BitCount; 
Packit a4058c
        guint32 NumPlanes; 
Packit a4058c
        guint32 DisplayRate; 
Packit a4058c
        guint32 Flags;
Packit a4058c
        
Packit a4058c
        guint32 chunk_id;
Packit a4058c
        guint32 chunk_size;
Packit a4058c
        
Packit a4058c
        gchar  *title;
Packit a4058c
        gchar  *author;
Packit a4058c
Packit a4058c
        GdkPixbufAniAnim *animation;
Packit a4058c
	GdkPixbufLoader *loader;
Packit a4058c
Packit a4058c
        int     pos;
Packit a4058c
} AniLoaderContext;
Packit a4058c
Packit a4058c
Packit a4058c
#define BYTES_LEFT(context) \
Packit a4058c
        ((context)->n_bytes - ((context)->byte - (context)->buffer))
Packit a4058c
Packit a4058c
static void
Packit a4058c
read_int8 (AniLoaderContext *context,
Packit a4058c
           guchar     *data,
Packit a4058c
           int        count)
Packit a4058c
{
Packit a4058c
        int total = MIN (count, BYTES_LEFT (context));
Packit a4058c
        memcpy (data, context->byte, total);
Packit a4058c
        context->byte += total;
Packit a4058c
        context->cp += total;
Packit a4058c
}
Packit a4058c
Packit a4058c
Packit a4058c
static guint32
Packit a4058c
read_int32 (AniLoaderContext *context)
Packit a4058c
{
Packit a4058c
        guint32 result;
Packit a4058c
Packit a4058c
        read_int8 (context, (guchar*) &result, 4);
Packit a4058c
        return lsb_32 ((guchar *) &result);
Packit a4058c
}
Packit a4058c
Packit a4058c
static void
Packit a4058c
context_free (AniLoaderContext *context)
Packit a4058c
{
Packit a4058c
        if (!context)
Packit a4058c
                return;
Packit a4058c
Packit a4058c
	if (context->loader) 
Packit a4058c
	{
Packit a4058c
		gdk_pixbuf_loader_close (context->loader, NULL);
Packit a4058c
		g_object_unref (context->loader);
Packit a4058c
	}
Packit a4058c
        if (context->animation) 
Packit a4058c
		g_object_unref (context->animation);
Packit a4058c
        g_free (context->buffer);
Packit a4058c
        g_free (context->title);
Packit a4058c
        g_free (context->author);
Packit a4058c
        
Packit a4058c
        g_free (context);
Packit a4058c
}
Packit a4058c
Packit a4058c
static void
Packit a4058c
prepared_callback (GdkPixbufLoader *loader,
Packit a4058c
                   gpointer data)
Packit a4058c
{
Packit a4058c
	AniLoaderContext *context = (AniLoaderContext*)data;
Packit a4058c
Packit a4058c
#ifdef DEBUG_ANI
Packit a4058c
	g_print ("%d pixbuf prepared\n", context->pos);
Packit a4058c
#endif
Packit a4058c
Packit a4058c
	GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
Packit a4058c
        if (!pixbuf)
Packit a4058c
		return;
Packit a4058c
Packit a4058c
	if (gdk_pixbuf_get_width (pixbuf) > context->animation->width) 
Packit a4058c
		context->animation->width = gdk_pixbuf_get_width (pixbuf);
Packit a4058c
	
Packit a4058c
	if (gdk_pixbuf_get_height (pixbuf) > context->animation->height) 
Packit a4058c
		context->animation->height = gdk_pixbuf_get_height (pixbuf);
Packit a4058c
	
Packit a4058c
	if (context->title != NULL) 
Packit a4058c
		gdk_pixbuf_set_option (pixbuf, "Title", context->title);
Packit a4058c
	
Packit a4058c
	if (context->author != NULL) 
Packit a4058c
		gdk_pixbuf_set_option (pixbuf, "Author", context->author);
Packit a4058c
Packit a4058c
	g_object_ref (pixbuf);
Packit a4058c
	context->animation->pixbufs[context->pos] = pixbuf;
Packit a4058c
Packit a4058c
	if (context->pos == 0) 
Packit a4058c
	{
Packit a4058c
		if (context->prepared_func)
Packit a4058c
			(* context->prepared_func) (pixbuf, 
Packit a4058c
						    GDK_PIXBUF_ANIMATION (context->animation), 
Packit a4058c
						    context->user_data);
Packit a4058c
	}
Packit a4058c
	else {
Packit a4058c
		/* FIXME - this is necessary for nice display of loading 
Packit a4058c
		   animations because GtkImage ignores 
Packit a4058c
		   gdk_pixbuf_animation_iter_on_currently_loading_frame()
Packit a4058c
		   and always exposes the full frame */
Packit a4058c
		GdkPixbuf *last = context->animation->pixbufs[context->pos - 1];
Packit a4058c
		gint width = MIN (gdk_pixbuf_get_width (last), gdk_pixbuf_get_width (pixbuf));
Packit a4058c
		gint height = MIN (gdk_pixbuf_get_height (last), gdk_pixbuf_get_height (pixbuf));
Packit a4058c
		gdk_pixbuf_copy_area (last, 0, 0, width, height, pixbuf, 0, 0);
Packit a4058c
	}
Packit a4058c
Packit a4058c
	context->pos++;
Packit a4058c
}
Packit a4058c
Packit a4058c
static void
Packit a4058c
updated_callback (GdkPixbufLoader* loader,
Packit a4058c
		  gint x, gint y, gint width, gint height,
Packit a4058c
		  gpointer data)
Packit a4058c
{
Packit a4058c
	AniLoaderContext *context = (AniLoaderContext*)data;
Packit a4058c
Packit a4058c
	GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
Packit a4058c
	
Packit a4058c
	if (context->updated_func)
Packit a4058c
		(* context->updated_func) (pixbuf, 
Packit a4058c
					   x, y, width, height,
Packit a4058c
					   context->user_data);
Packit a4058c
}
Packit a4058c
Packit a4058c
static gboolean
Packit a4058c
ani_load_chunk (AniLoaderContext *context, GError **error)
Packit a4058c
{
Packit a4058c
        int i;
Packit a4058c
        
Packit a4058c
        if (context->chunk_id == 0x0) {
Packit a4058c
                if (BYTES_LEFT (context) < 8)
Packit a4058c
                        return FALSE;
Packit a4058c
                context->chunk_id = read_int32 (context);
Packit a4058c
                context->chunk_size = read_int32 (context);
Packit a4058c
		/* Pad it up to word length */
Packit a4058c
		if (context->chunk_size % 2)
Packit a4058c
			context->chunk_size += (2 - (context->chunk_size % 2));
Packit a4058c
        
Packit a4058c
        }
Packit a4058c
        
Packit a4058c
        while (context->chunk_id == TAG_LIST)
Packit a4058c
	{
Packit a4058c
		if (BYTES_LEFT (context) < 12)
Packit a4058c
			return FALSE;
Packit a4058c
                        
Packit a4058c
		read_int32 (context);
Packit a4058c
		context->chunk_id = read_int32 (context);
Packit a4058c
		context->chunk_size = read_int32 (context);
Packit a4058c
		/* Pad it up to word length */
Packit a4058c
		if (context->chunk_size % 2)
Packit a4058c
			context->chunk_size += (2 - (context->chunk_size % 2));
Packit a4058c
        
Packit a4058c
	}
Packit a4058c
        
Packit a4058c
	if (context->chunk_id == TAG_icon) 
Packit a4058c
	{
Packit a4058c
		GError *loader_error = NULL;
Packit a4058c
		guchar *data;
Packit a4058c
		guint32 towrite;
Packit a4058c
Packit a4058c
		if (context->loader == NULL) 
Packit a4058c
		{
Packit a4058c
			if (context->pos >= context->NumFrames) 
Packit a4058c
			{
Packit a4058c
				g_set_error_literal (error,
Packit a4058c
                                                     GDK_PIXBUF_ERROR,
Packit a4058c
                                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
                                                     _("Unexpected icon chunk in animation"));
Packit a4058c
				return FALSE; 
Packit a4058c
			}
Packit a4058c
Packit a4058c
#ifdef DEBUG_ANI
Packit a4058c
			g_print ("opening loader\n");
Packit a4058c
#endif
Packit a4058c
			context->loader = gdk_pixbuf_loader_new_with_type ("ico", &loader_error);
Packit a4058c
			if (loader_error) 
Packit a4058c
			{
Packit a4058c
				g_propagate_error (error, loader_error);
Packit a4058c
				return FALSE;
Packit a4058c
			}
Packit a4058c
			g_signal_connect (context->loader, "area_prepared",
Packit a4058c
					  G_CALLBACK (prepared_callback), context);
Packit a4058c
			g_signal_connect (context->loader, "area_updated",
Packit a4058c
					  G_CALLBACK (updated_callback), context);
Packit a4058c
		}
Packit a4058c
Packit a4058c
		towrite = MIN (context->chunk_size, BYTES_LEFT (context));
Packit a4058c
		data = context->byte;
Packit a4058c
		context->byte += towrite;
Packit a4058c
		context->cp += towrite;
Packit a4058c
#ifdef DEBUG_ANI
Packit a4058c
		g_print ("miss %d, get %d, leftover %d\n", context->chunk_size, towrite, BYTES_LEFT (context));
Packit a4058c
#endif
Packit a4058c
		context->chunk_size -= towrite;
Packit a4058c
		if (!gdk_pixbuf_loader_write (context->loader, data, towrite, &loader_error)) 
Packit a4058c
		{
Packit a4058c
			g_propagate_error (error, loader_error);
Packit a4058c
			gdk_pixbuf_loader_close (context->loader, NULL);
Packit a4058c
			g_object_unref (context->loader);
Packit a4058c
			context->loader = NULL;
Packit a4058c
			return FALSE; 
Packit a4058c
		}
Packit a4058c
		if (context->chunk_size == 0) 
Packit a4058c
		{
Packit a4058c
#ifdef DEBUG_ANI
Packit a4058c
			g_print ("closing loader\n");
Packit a4058c
#endif
Packit a4058c
			if (!gdk_pixbuf_loader_close (context->loader, &loader_error)) 
Packit a4058c
			{
Packit a4058c
				g_propagate_error (error, loader_error);
Packit a4058c
				g_object_unref (context->loader);
Packit a4058c
				context->loader = NULL;
Packit a4058c
				return FALSE;
Packit a4058c
			}
Packit a4058c
			g_object_unref (context->loader);
Packit a4058c
			context->loader = NULL;
Packit a4058c
			context->chunk_id = 0x0;
Packit a4058c
		}
Packit a4058c
		return BYTES_LEFT (context) > 0;
Packit a4058c
	}
Packit a4058c
Packit a4058c
        if (BYTES_LEFT (context) < context->chunk_size)
Packit a4058c
                return FALSE;
Packit a4058c
        
Packit a4058c
        if (context->chunk_id == TAG_anih) 
Packit a4058c
	{
Packit a4058c
		context->HeaderSize = read_int32 (context);
Packit a4058c
		context->NumFrames = read_int32 (context);
Packit a4058c
		context->NumSteps = read_int32 (context);
Packit a4058c
		context->Width = read_int32 (context);
Packit a4058c
		context->Height = read_int32 (context);
Packit a4058c
		context->BitCount = read_int32 (context);
Packit a4058c
		context->NumPlanes = read_int32 (context);
Packit a4058c
		context->DisplayRate = read_int32 (context);
Packit a4058c
		context->Flags = read_int32 (context);
Packit a4058c
                        
Packit a4058c
#ifdef DEBUG_ANI	  
Packit a4058c
		g_print ("HeaderSize \t%" G_GUINT32_FORMAT
Packit a4058c
			 "\nNumFrames \t%" G_GUINT32_FORMAT
Packit a4058c
			 "\nNumSteps \t%" G_GUINT32_FORMAT
Packit a4058c
			 "\nWidth    \t%" G_GUINT32_FORMAT
Packit a4058c
			 "\nHeight   \t%" G_GUINT32_FORMAT
Packit a4058c
			 "\nBitCount \t%" G_GUINT32_FORMAT
Packit a4058c
			 "\nNumPlanes \t%" G_GUINT32_FORMAT
Packit a4058c
			 "\nDisplayRate \t%" G_GUINT32_FORMAT
Packit a4058c
			 "\nSequenceFlag \t%d"
Packit a4058c
			 "\nIconFlag \t%d"
Packit a4058c
			 "\n",
Packit a4058c
			 context->HeaderSize, context->NumFrames, 
Packit a4058c
			 context->NumSteps, context->Width, 
Packit a4058c
			 context->Height, context->BitCount, 
Packit a4058c
			 context->NumPlanes, context->DisplayRate, 
Packit a4058c
			 (context->Flags & 0x2) != 0, 
Packit a4058c
			 (context->Flags & 0x1) != 0);
Packit a4058c
#endif
Packit a4058c
		if (!(context->Flags & 0x2))
Packit a4058c
			context->NumSteps = context->NumFrames;
Packit a4058c
		if (context->NumFrames == 0 || 
Packit a4058c
		    context->NumFrames >= 1024 || 
Packit a4058c
		    context->NumSteps == 0 || 
Packit a4058c
		    context->NumSteps >= 1024) 
Packit a4058c
		{
Packit a4058c
			g_set_error_literal (error,
Packit a4058c
                                             GDK_PIXBUF_ERROR,
Packit a4058c
                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
                                             _("Invalid header in animation"));
Packit a4058c
			return FALSE;
Packit a4058c
		}
Packit a4058c
      
Packit a4058c
		context->animation = g_object_new (GDK_TYPE_PIXBUF_ANI_ANIM, NULL);        
Packit a4058c
		if (!context->animation) 
Packit a4058c
		{
Packit a4058c
			g_set_error_literal (error,
Packit a4058c
                                             GDK_PIXBUF_ERROR,
Packit a4058c
                                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
Packit a4058c
                                             _("Not enough memory to load animation"));
Packit a4058c
			return FALSE;
Packit a4058c
		}
Packit a4058c
Packit a4058c
		context->animation->n_pixbufs = context->NumFrames;
Packit a4058c
		context->animation->n_frames = context->NumSteps;
Packit a4058c
Packit a4058c
		context->animation->total_time = context->NumSteps * (context->DisplayRate * 1000 / 60);
Packit a4058c
		context->animation->width = 0;
Packit a4058c
		context->animation->height = 0;
Packit a4058c
Packit a4058c
		context->animation->pixbufs = g_try_new0 (GdkPixbuf*, context->NumFrames);
Packit a4058c
		context->animation->delay = g_try_new (gint, context->NumSteps);
Packit a4058c
		context->animation->sequence = g_try_new (gint, context->NumSteps);
Packit a4058c
Packit a4058c
		if (!context->animation->pixbufs || 
Packit a4058c
		    !context->animation->delay || 
Packit a4058c
		    !context->animation->sequence) 
Packit a4058c
		{
Packit a4058c
			g_set_error_literal (error,
Packit a4058c
                                             GDK_PIXBUF_ERROR,
Packit a4058c
                                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
Packit a4058c
                                             _("Not enough memory to load animation"));
Packit a4058c
			return FALSE;
Packit a4058c
		}
Packit a4058c
Packit a4058c
		for (i = 0; i < context->NumSteps; i++) 
Packit a4058c
		{
Packit a4058c
			/* default values if the corresponding chunks are absent */
Packit a4058c
			context->animation->delay[i] = context->DisplayRate * 1000 / 60;
Packit a4058c
			context->animation->sequence[i] = MIN (i, context->NumFrames  - 1);	  
Packit a4058c
		}
Packit a4058c
	}
Packit a4058c
        else if (context->chunk_id == TAG_rate) 
Packit a4058c
	{
Packit a4058c
		if (context->chunk_size != 4 * context->NumSteps) 
Packit a4058c
		{
Packit a4058c
			g_set_error_literal (error,
Packit a4058c
                                             GDK_PIXBUF_ERROR,
Packit a4058c
                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
                                             _("Malformed chunk in animation"));
Packit a4058c
			return FALSE; 
Packit a4058c
		}
Packit a4058c
		if (!context->animation) 
Packit a4058c
		{
Packit a4058c
			g_set_error_literal (error,
Packit a4058c
                                             GDK_PIXBUF_ERROR,
Packit a4058c
                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
                                             _("Invalid header in animation"));
Packit a4058c
			return FALSE;
Packit a4058c
		}
Packit a4058c
Packit a4058c
		context->animation->total_time = 0;
Packit a4058c
		for (i = 0; i < context->NumSteps; i++) 
Packit a4058c
		{
Packit a4058c
			context->animation->delay[i] = read_int32 (context) * 1000 / 60;
Packit a4058c
			context->animation->total_time += context->animation->delay[i];
Packit a4058c
		}
Packit a4058c
	}
Packit a4058c
        else if (context->chunk_id == TAG_seq) 
Packit a4058c
	{
Packit a4058c
		if (context->chunk_size != 4 * context->NumSteps) 
Packit a4058c
		{
Packit a4058c
			g_set_error_literal (error,
Packit a4058c
                                             GDK_PIXBUF_ERROR,
Packit a4058c
                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
                                             _("Malformed chunk in animation"));
Packit a4058c
			return FALSE; 
Packit a4058c
		}
Packit a4058c
		if (!context->animation) 
Packit a4058c
		{
Packit a4058c
			g_set_error_literal (error,
Packit a4058c
                                             GDK_PIXBUF_ERROR,
Packit a4058c
                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
                                             _("Invalid header in animation"));
Packit a4058c
			return FALSE;
Packit a4058c
		}
Packit a4058c
		for (i = 0; i < context->NumSteps; i++) 
Packit a4058c
		{
Packit a4058c
			context->animation->sequence[i] = read_int32 (context);
Packit a4058c
			if (context->animation->sequence[i] >= context->NumFrames) 
Packit a4058c
			{
Packit a4058c
				g_set_error_literal (error,
Packit a4058c
                                                     GDK_PIXBUF_ERROR,
Packit a4058c
                                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
                                                     _("Malformed chunk in animation"));
Packit a4058c
				return FALSE; 
Packit a4058c
			}
Packit a4058c
		}
Packit a4058c
	}
Packit a4058c
        else if (context->chunk_id == TAG_INAM) 
Packit a4058c
	{
Packit a4058c
		if (!context->animation) 
Packit a4058c
		{
Packit a4058c
			g_set_error_literal (error,
Packit a4058c
                                             GDK_PIXBUF_ERROR,
Packit a4058c
                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
                                             _("Invalid header in animation"));
Packit a4058c
			return FALSE;
Packit a4058c
		}
Packit a4058c
		context->title = g_try_malloc (context->chunk_size + 1);
Packit a4058c
		if (!context->title) 
Packit a4058c
		{
Packit a4058c
			g_set_error_literal (error,
Packit a4058c
                                             GDK_PIXBUF_ERROR,
Packit a4058c
                                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
Packit a4058c
                                             _("Not enough memory to load animation"));
Packit a4058c
			return FALSE;
Packit a4058c
		}
Packit a4058c
		context->title[context->chunk_size] = 0;
Packit a4058c
		read_int8 (context, (guchar *)context->title, context->chunk_size);
Packit a4058c
#ifdef DEBUG_ANI
Packit a4058c
		g_print ("INAM %s\n", context->title);
Packit a4058c
#endif
Packit a4058c
		for (i = 0; i < context->pos; i++)
Packit a4058c
			gdk_pixbuf_set_option (context->animation->pixbufs[i], "Title", context->title);			
Packit a4058c
	}
Packit a4058c
        else if (context->chunk_id == TAG_IART) 
Packit a4058c
	{
Packit a4058c
		if (!context->animation) 
Packit a4058c
		{
Packit a4058c
			g_set_error_literal (error,
Packit a4058c
                                             GDK_PIXBUF_ERROR,
Packit a4058c
                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
                                             _("Invalid header in animation"));
Packit a4058c
			return FALSE;
Packit a4058c
		}
Packit a4058c
		context->author = g_try_malloc (context->chunk_size + 1);
Packit a4058c
		if (!context->author) 
Packit a4058c
		{
Packit a4058c
			g_set_error_literal (error,
Packit a4058c
                                             GDK_PIXBUF_ERROR,
Packit a4058c
                                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
Packit a4058c
                                             _("Not enough memory to load animation"));
Packit a4058c
			return FALSE;
Packit a4058c
		}
Packit a4058c
		context->author[context->chunk_size] = 0;
Packit a4058c
		read_int8 (context, (guchar *)context->author, context->chunk_size);
Packit a4058c
#ifdef DEBUG_ANI
Packit a4058c
		g_print ("IART %s\n", context->author);
Packit a4058c
#endif
Packit a4058c
		for (i = 0; i < context->pos; i++)
Packit a4058c
			gdk_pixbuf_set_option (context->animation->pixbufs[i], "Author", context->author);			
Packit a4058c
	}
Packit a4058c
Packit a4058c
#ifdef DEBUG_ANI
Packit a4058c
	{
Packit a4058c
		gint32 dummy = lsb_32 ((guchar *)&context->chunk_id);
Packit a4058c
        
Packit a4058c
		g_print ("Loaded chunk with ID '%c%c%c%c' and length %" G_GUINT32_FORMAT "\n",
Packit a4058c
			 ((char*)&dummy)[0], ((char*)&dummy)[1],
Packit a4058c
			 ((char*)&dummy)[2], ((char*)&dummy)[3], 
Packit a4058c
			 context->chunk_size);
Packit a4058c
	}
Packit a4058c
#endif 
Packit a4058c
		
Packit a4058c
	context->chunk_id = 0x0;
Packit a4058c
        return TRUE;
Packit a4058c
}
Packit a4058c
Packit a4058c
static gboolean
Packit a4058c
gdk_pixbuf__ani_image_load_increment (gpointer data,
Packit a4058c
				      const guchar *buf, guint size,
Packit a4058c
				      GError **error)
Packit a4058c
{
Packit a4058c
        AniLoaderContext *context = (AniLoaderContext *)data;
Packit a4058c
        
Packit a4058c
        if (context->n_bytes + size >= context->buffer_size) {
Packit a4058c
                int drop = context->byte - context->buffer;
Packit a4058c
                memmove (context->buffer, context->byte, context->n_bytes - drop);
Packit a4058c
                context->n_bytes -= drop;
Packit a4058c
                context->byte = context->buffer;
Packit a4058c
                if (context->n_bytes + size >= context->buffer_size) {
Packit a4058c
			guchar *tmp;
Packit a4058c
                        context->buffer_size = MAX (context->n_bytes + size, context->buffer_size + 4096);
Packit a4058c
#ifdef DEBUG_ANI
Packit a4058c
                        g_print ("growing buffer to %" G_GUINT32_FORMAT "\n", context->buffer_size);
Packit a4058c
#endif
Packit a4058c
                        tmp = g_try_realloc (context->buffer, context->buffer_size);
Packit a4058c
                        if (!tmp) 
Packit a4058c
			{
Packit a4058c
				g_set_error_literal (error,
Packit a4058c
                                                     GDK_PIXBUF_ERROR,
Packit a4058c
                                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
Packit a4058c
                                                     _("Not enough memory to load animation"));
Packit a4058c
				return FALSE;
Packit a4058c
			}
Packit a4058c
                        context->byte = context->buffer = tmp;
Packit a4058c
                }
Packit a4058c
        }
Packit a4058c
        memcpy (context->buffer + context->n_bytes, buf, size);
Packit a4058c
        context->n_bytes += size;
Packit a4058c
Packit a4058c
        if (context->data_size == 0) 
Packit a4058c
	{
Packit a4058c
		guint32 riff_id, chunk_id;
Packit a4058c
                        
Packit a4058c
		if (BYTES_LEFT (context) < 12)
Packit a4058c
			return TRUE;
Packit a4058c
                        
Packit a4058c
		riff_id = read_int32 (context);
Packit a4058c
		context->data_size = read_int32 (context);
Packit a4058c
		chunk_id = read_int32 (context);
Packit a4058c
                        
Packit a4058c
		if (riff_id != TAG_RIFF || 
Packit a4058c
		    context->data_size == 0 || 
Packit a4058c
		    chunk_id != TAG_ACON) 
Packit a4058c
		{
Packit a4058c
			g_set_error_literal (error,
Packit a4058c
                                             GDK_PIXBUF_ERROR,
Packit a4058c
                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
                                             _("Invalid header in animation"));
Packit a4058c
			return FALSE; 
Packit a4058c
		}
Packit a4058c
	}
Packit a4058c
        
Packit a4058c
        if (context->cp < context->data_size + 8) 
Packit a4058c
	{
Packit a4058c
		GError *chunk_error = NULL;
Packit a4058c
Packit a4058c
		while (ani_load_chunk (context, &chunk_error)) ;
Packit a4058c
		if (chunk_error) 
Packit a4058c
		{
Packit a4058c
			g_propagate_error (error, chunk_error);
Packit a4058c
			return FALSE;
Packit a4058c
		}
Packit a4058c
	}
Packit a4058c
Packit a4058c
        return TRUE;
Packit a4058c
}
Packit a4058c
Packit a4058c
static gpointer
Packit a4058c
gdk_pixbuf__ani_image_begin_load (GdkPixbufModuleSizeFunc size_func, 
Packit a4058c
                                  GdkPixbufModulePreparedFunc prepared_func, 
Packit a4058c
				  GdkPixbufModuleUpdatedFunc  updated_func,
Packit a4058c
				  gpointer user_data,
Packit a4058c
				  GError **error)
Packit a4058c
{
Packit a4058c
        AniLoaderContext *context;
Packit a4058c
        
Packit a4058c
        context = g_new0 (AniLoaderContext, 1);
Packit a4058c
        
Packit a4058c
        context->prepared_func = prepared_func;
Packit a4058c
        context->updated_func = updated_func;
Packit a4058c
        context->user_data = user_data;
Packit a4058c
        
Packit a4058c
        context->pos = 0;
Packit a4058c
        
Packit a4058c
        context->buffer_size = 4096;
Packit a4058c
        context->buffer = g_try_malloc (context->buffer_size);
Packit a4058c
        if (!context->buffer) 
Packit a4058c
	{
Packit a4058c
		context_free (context);
Packit a4058c
		g_set_error_literal (error,
Packit a4058c
                                     GDK_PIXBUF_ERROR,
Packit a4058c
                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
Packit a4058c
                                     _("Not enough memory to load animation"));
Packit a4058c
		return NULL;
Packit a4058c
	}
Packit a4058c
        
Packit a4058c
        context->byte = context->buffer;
Packit a4058c
        context->n_bytes = 0;
Packit a4058c
        
Packit a4058c
        return (gpointer) context;
Packit a4058c
}
Packit a4058c
Packit a4058c
static gboolean
Packit a4058c
gdk_pixbuf__ani_image_stop_load (gpointer data,
Packit a4058c
				 GError **error)
Packit a4058c
{
Packit a4058c
        AniLoaderContext *context = (AniLoaderContext *) data;
Packit a4058c
        gboolean retval;
Packit a4058c
Packit a4058c
	g_return_val_if_fail (context != NULL, TRUE);
Packit a4058c
        if (!context->animation) {
Packit a4058c
                g_set_error_literal (error,
Packit a4058c
                                     GDK_PIXBUF_ERROR,
Packit a4058c
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
                                     _("ANI image was truncated or incomplete."));
Packit a4058c
                retval = FALSE;
Packit a4058c
        }
Packit a4058c
        else {
Packit a4058c
                retval = TRUE;
Packit a4058c
        }
Packit a4058c
        context_free (context);
Packit a4058c
Packit a4058c
        return retval;
Packit a4058c
}
Packit a4058c
Packit a4058c
#ifndef INCLUDE_ani
Packit a4058c
#define MODULE_ENTRY(function) G_MODULE_EXPORT void function
Packit a4058c
#else
Packit a4058c
#define MODULE_ENTRY(function) void _gdk_pixbuf__ani_ ## function
Packit a4058c
#endif
Packit a4058c
Packit a4058c
MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
Packit a4058c
{
Packit a4058c
        module->begin_load = gdk_pixbuf__ani_image_begin_load;
Packit a4058c
        module->stop_load = gdk_pixbuf__ani_image_stop_load;
Packit a4058c
        module->load_increment = gdk_pixbuf__ani_image_load_increment;
Packit a4058c
}
Packit a4058c
Packit a4058c
MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
Packit a4058c
{
Packit a4058c
	static const GdkPixbufModulePattern signature[] = {
Packit a4058c
		{ "RIFF    ACON", "    xxxx    ", 100 },
Packit a4058c
		{ NULL, NULL, 0 }
Packit a4058c
	};
Packit a4058c
	static const gchar * mime_types[] = {
Packit a4058c
		"application/x-navi-animation",
Packit a4058c
		NULL
Packit a4058c
	};
Packit a4058c
	static const gchar * extensions[] = {
Packit a4058c
		"ani",
Packit a4058c
		NULL
Packit a4058c
	};
Packit a4058c
	
Packit a4058c
	info->name = "ani";
Packit a4058c
	info->signature = (GdkPixbufModulePattern *) signature;
Packit a4058c
	info->description = NC_("image format", "Windows animated cursor");
Packit a4058c
	info->mime_types = (gchar **) mime_types;
Packit a4058c
	info->extensions = (gchar **) extensions;
Packit a4058c
	info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
Packit a4058c
	info->license = "LGPL";
Packit a4058c
}
Packit a4058c
Packit a4058c
Packit a4058c
Packit a4058c