]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-ani.c
Use explicit G_MODULE_EXPORT decoration when building the loader in
[~andy/gtk] / gdk-pixbuf / io-ani.c
1 /* -*- mode: C; c-file-style: "linux" -*- */
2 /* GdkPixbuf library - ANI image loader
3  *
4  * Copyright (C) 2002 The Free Software Foundation
5  *
6  * Authors: Matthias Clasen <maclas@gmx.de>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #undef DEBUG_ANI
25
26 #include <config.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include "gdk-pixbuf-private.h"
30 #include "gdk-pixbuf-io.h"
31 #include "io-ani-animation.h"
32
33 static int
34 lsb_32 (guchar *src)
35 {
36         return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
37 }
38
39 #define MAKE_TAG(a,b,c,d) ( (guint32)d << 24 | \
40                             (guint32)c << 16 | \
41                             (guint32)b <<  8 | \
42                             (guint32)a )
43
44 #define TAG_RIFF MAKE_TAG('R','I','F','F')
45 #define TAG_ACON MAKE_TAG('A','C','O','N')
46 #define TAG_LIST MAKE_TAG('L','I','S','T')
47 #define TAG_INAM MAKE_TAG('I','N','A','M')
48 #define TAG_IART MAKE_TAG('I','A','R','T')
49 #define TAG_anih MAKE_TAG('a','n','i','h')
50 #define TAG_seq  MAKE_TAG('s','e','q',' ')
51 #define TAG_rate MAKE_TAG('r','a','t','e')
52 #define TAG_icon MAKE_TAG('i','c','o','n')
53
54 typedef struct _AniLoaderContext 
55 {
56         guint32 cp;
57         
58         guchar *buffer;
59         guchar *byte;
60         guint   n_bytes;
61         guint   buffer_size;
62         
63         GdkPixbufModulePreparedFunc prepared_func;
64         GdkPixbufModuleUpdatedFunc updated_func;
65         gpointer user_data;
66         
67         guint32 data_size;
68         
69         guint32 HeaderSize;
70         guint32 NumFrames; 
71         guint32 NumSteps; 
72         guint32 Width;   
73         guint32 Height; 
74         guint32 BitCount; 
75         guint32 NumPlanes; 
76         guint32 DisplayRate; 
77         guint32 Flags;
78         
79         guint32 chunk_id;
80         guint32 chunk_size;
81         
82         gchar  *title;
83         gchar  *author;
84
85         GdkPixbufAniAnim *animation;
86         GdkPixbufLoader *loader;
87
88         int     pos;
89 } AniLoaderContext;
90
91
92 #define BYTES_LEFT(context) \
93         ((context)->n_bytes - ((context)->byte - (context)->buffer))
94
95 static void
96 read_int8 (AniLoaderContext *context,
97            guchar     *data,
98            int        count)
99 {
100         int total = MIN (count, BYTES_LEFT (context));
101         memcpy (data, context->byte, total);
102         context->byte += total;
103         context->cp += total;
104 }
105
106
107 static guint32
108 read_int32 (AniLoaderContext *context)
109 {
110         guint32 result;
111
112         read_int8 (context, (guchar*) &result, 4);
113         return lsb_32 ((guchar *) &result);
114 }
115
116 static void
117 context_free (AniLoaderContext *context)
118 {
119         if (!context)
120                 return;
121
122         if (context->loader) 
123         {
124                 gdk_pixbuf_loader_close (context->loader, NULL);
125                 g_object_unref (context->loader);
126         }
127         if (context->animation) 
128                 g_object_unref (context->animation);
129         g_free (context->buffer);
130         g_free (context->title);
131         g_free (context->author);
132         
133         g_free (context);
134 }
135
136 static void
137 prepared_callback (GdkPixbufLoader *loader,
138                    gpointer data)
139 {
140         AniLoaderContext *context = (AniLoaderContext*)data;
141
142 #ifdef DEBUG_ANI
143         g_print ("%d pixbuf prepared\n", context->pos);
144 #endif
145
146         GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
147         if (!pixbuf)
148                 return;
149
150         if (gdk_pixbuf_get_width (pixbuf) > context->animation->width) 
151                 context->animation->width = gdk_pixbuf_get_width (pixbuf);
152         
153         if (gdk_pixbuf_get_height (pixbuf) > context->animation->height) 
154                 context->animation->height = gdk_pixbuf_get_height (pixbuf);
155         
156         if (context->title != NULL) 
157                 gdk_pixbuf_set_option (pixbuf, "Title", context->title);
158         
159         if (context->author != NULL) 
160                 gdk_pixbuf_set_option (pixbuf, "Author", context->author);
161
162         g_object_ref (pixbuf);
163         context->animation->pixbufs[context->pos] = pixbuf;
164
165         if (context->pos == 0) 
166         {
167                 if (context->prepared_func)
168                         (* context->prepared_func) (pixbuf, 
169                                                     GDK_PIXBUF_ANIMATION (context->animation), 
170                                                     context->user_data);
171         }
172         else {
173                 /* FIXME - this is necessary for nice display of loading 
174                    animations because GtkImage ignores 
175                    gdk_pixbuf_animation_iter_on_currently_loading_frame()
176                    and always exposes the full frame */
177                 GdkPixbuf *last = context->animation->pixbufs[context->pos - 1];
178                 gint width = MIN (gdk_pixbuf_get_width (last), gdk_pixbuf_get_width (pixbuf));
179                 gint height = MIN (gdk_pixbuf_get_height (last), gdk_pixbuf_get_height (pixbuf));
180                 gdk_pixbuf_copy_area (last, 0, 0, width, height, pixbuf, 0, 0);
181         }
182
183         context->pos++;
184 }
185
186 static void
187 updated_callback (GdkPixbufLoader* loader,
188                   gint x, gint y, gint width, gint height,
189                   gpointer data)
190 {
191         AniLoaderContext *context = (AniLoaderContext*)data;
192
193         GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
194         
195         if (context->updated_func)
196                 (* context->updated_func) (pixbuf, 
197                                            x, y, width, height,
198                                            context->user_data);
199 }
200
201 static gboolean
202 ani_load_chunk (AniLoaderContext *context, GError **error)
203 {
204         int i;
205         
206         if (context->chunk_id == 0x0) {
207                 if (BYTES_LEFT (context) < 8)
208                         return FALSE;
209                 context->chunk_id = read_int32 (context);
210                 context->chunk_size = read_int32 (context);
211                 /* Pad it up to word length */
212                 if (context->chunk_size % 2)
213                         context->chunk_size += (2 - (context->chunk_size % 2));
214         
215         }
216         
217         while (context->chunk_id == TAG_LIST)
218         {
219                 if (BYTES_LEFT (context) < 12)
220                         return FALSE;
221                         
222                 read_int32 (context);
223                 context->chunk_id = read_int32 (context);
224                 context->chunk_size = read_int32 (context);
225                 /* Pad it up to word length */
226                 if (context->chunk_size % 2)
227                         context->chunk_size += (2 - (context->chunk_size % 2));
228         
229         }
230         
231         if (context->chunk_id == TAG_icon) 
232         {
233                 GError *loader_error = NULL;
234                 guchar *data;
235                 guint32 towrite;
236
237                 if (context->loader == NULL) 
238                 {
239                         if (context->pos >= context->NumFrames) 
240                         {
241                                 g_set_error (error,
242                                              GDK_PIXBUF_ERROR,
243                                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
244                                              _("Unexpected icon chunk in animation"));
245                                 return FALSE; 
246                         }
247
248 #ifdef DEBUG_ANI
249                         g_print ("opening loader\n");
250 #endif
251                         context->loader = gdk_pixbuf_loader_new_with_type ("ico", &loader_error);
252                         if (loader_error) 
253                         {
254                                 g_propagate_error (error, loader_error);
255                                 return FALSE;
256                         }
257                         g_signal_connect (context->loader, "area_prepared",
258                                           G_CALLBACK (prepared_callback), context);
259                         g_signal_connect (context->loader, "area_updated",
260                                           G_CALLBACK (updated_callback), context);
261                 }
262
263                 towrite = MIN (context->chunk_size, BYTES_LEFT (context));
264                 data = context->byte;
265                 context->byte += towrite;
266                 context->cp += towrite;
267 #ifdef DEBUG_ANI
268                 g_print ("miss %d, get %d, leftover %d\n", context->chunk_size, towrite, BYTES_LEFT (context));
269 #endif
270                 context->chunk_size -= towrite;
271                 if (!gdk_pixbuf_loader_write (context->loader, data, towrite, &loader_error)) 
272                 {
273                         g_propagate_error (error, loader_error);
274                         gdk_pixbuf_loader_close (context->loader, NULL);
275                         g_object_unref (context->loader);
276                         context->loader = NULL;
277                         return FALSE; 
278                 }
279                 if (context->chunk_size == 0) 
280                 {
281 #ifdef DEBUG_ANI
282                         g_print ("closing loader\n");
283 #endif
284                         if (!gdk_pixbuf_loader_close (context->loader, &loader_error)) 
285                         {
286                                 g_propagate_error (error, loader_error);
287                                 g_object_unref (context->loader);
288                                 context->loader = NULL;
289                                 return FALSE;
290                         }
291                         g_object_unref (context->loader);
292                         context->loader = NULL;
293                         context->chunk_id = 0x0;
294                 }
295                 return BYTES_LEFT (context) > 0;
296         }
297
298         if (BYTES_LEFT (context) < context->chunk_size)
299                 return FALSE;
300         
301         if (context->chunk_id == TAG_anih) 
302         {
303                 context->HeaderSize = read_int32 (context);
304                 context->NumFrames = read_int32 (context);
305                 context->NumSteps = read_int32 (context);
306                 context->Width = read_int32 (context);
307                 context->Height = read_int32 (context);
308                 context->BitCount = read_int32 (context);
309                 context->NumPlanes = read_int32 (context);
310                 context->DisplayRate = read_int32 (context);
311                 context->Flags = read_int32 (context);
312                         
313 #ifdef DEBUG_ANI          
314                 g_print ("HeaderSize \t%" G_GUINT32_FORMAT
315                          "\nNumFrames \t%" G_GUINT32_FORMAT
316                          "\nNumSteps \t%" G_GUINT32_FORMAT
317                          "\nWidth    \t%" G_GUINT32_FORMAT
318                          "\nHeight   \t%" G_GUINT32_FORMAT
319                          "\nBitCount \t%" G_GUINT32_FORMAT
320                          "\nNumPlanes \t%" G_GUINT32_FORMAT
321                          "\nDisplayRate \t%" G_GUINT32_FORMAT
322                          "\nSequenceFlag \t%d"
323                          "\nIconFlag \t%d"
324                          "\n",
325                          context->HeaderSize, context->NumFrames, 
326                          context->NumSteps, context->Width, 
327                          context->Height, context->BitCount, 
328                          context->NumPlanes, context->DisplayRate, 
329                          (context->Flags & 0x2) != 0, 
330                          (context->Flags & 0x1) != 0);
331 #endif
332                 if (!context->Flags & 0x2) 
333                 {
334                         g_set_error (error,
335                                      GDK_PIXBUF_ERROR,
336                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
337                                      _("Unsupported animation type"));
338                         return FALSE; 
339                 }
340                 if (context->NumFrames == 0 || 
341                     context->NumFrames >= 1024 || 
342                     context->NumSteps == 0 || 
343                     context->NumSteps >= 1024) 
344                 {
345                         g_set_error (error,
346                                      GDK_PIXBUF_ERROR,
347                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
348                                      _("Invalid header in animation"));
349                         return FALSE;
350                 }
351       
352                 context->animation = g_object_new (GDK_TYPE_PIXBUF_ANI_ANIM, NULL);        
353                 if (!context->animation) 
354                 {
355                         g_set_error (error,
356                                      GDK_PIXBUF_ERROR,
357                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
358                                      _("Not enough memory to load animation"));
359                         return FALSE;
360                 }
361
362                 context->animation->n_pixbufs = context->NumFrames;
363                 context->animation->n_frames = context->NumSteps;
364
365                 context->animation->total_time = context->NumSteps * (context->DisplayRate * 1000 / 60);
366                 context->animation->width = 0;
367                 context->animation->height = 0;
368
369                 context->animation->pixbufs = g_try_new0 (GdkPixbuf*, context->NumFrames);
370                 context->animation->delay = g_try_new (gint, context->NumSteps);
371                 context->animation->sequence = g_try_new (gint, context->NumSteps);
372
373                 if (!context->animation->pixbufs || 
374                     !context->animation->delay || 
375                     !context->animation->sequence) 
376                 {
377                         g_set_error (error,
378                                      GDK_PIXBUF_ERROR,
379                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
380                                      _("Not enough memory to load animation"));
381                         return FALSE;
382                 }
383
384                 for (i = 0; i < context->NumSteps; i++) 
385                 {
386                         /* default values if the corresponding chunks are absent */
387                         context->animation->delay[i] = context->DisplayRate * 1000 / 60;
388                         context->animation->sequence[i] = MIN (i, context->NumFrames  - 1);       
389                 }
390         }
391         else if (context->chunk_id == TAG_rate) 
392         {
393                 if (context->chunk_size != 4 * context->NumSteps) 
394                 {
395                         g_set_error (error,
396                                      GDK_PIXBUF_ERROR,
397                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
398                                      _("Malformed chunk in animation"));
399                         return FALSE; 
400                 }
401                 if (!context->animation) 
402                 {
403                         g_set_error (error,
404                                      GDK_PIXBUF_ERROR,
405                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
406                                      _("Invalid header in animation"));
407                         return FALSE;
408                 }
409
410                 context->animation->total_time = 0;
411                 for (i = 0; i < context->NumSteps; i++) 
412                 {
413                         context->animation->delay[i] = read_int32 (context) * 1000 / 60;
414                         context->animation->total_time += context->animation->delay[i];
415                 }
416         }
417         else if (context->chunk_id == TAG_seq) 
418         {
419                 if (context->chunk_size != 4 * context->NumSteps) 
420                 {
421                         g_set_error (error,
422                                      GDK_PIXBUF_ERROR,
423                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
424                                      _("Malformed chunk in animation"));
425                         return FALSE; 
426                 }
427                 if (!context->animation) 
428                 {
429                         g_set_error (error,
430                                      GDK_PIXBUF_ERROR,
431                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
432                                      _("Invalid header in animation"));
433                         return FALSE;
434                 }
435                 for (i = 0; i < context->NumSteps; i++) 
436                 {
437                         context->animation->sequence[i] = read_int32 (context);
438                         if (context->animation->sequence[i] >= context->NumFrames) 
439                         {
440                                 g_set_error (error,
441                                              GDK_PIXBUF_ERROR,
442                                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
443                                              _("Malformed chunk in animation"));
444                                 return FALSE; 
445                         }
446                 }
447         }
448         else if (context->chunk_id == TAG_INAM) 
449         {
450                 if (!context->animation) 
451                 {
452                         g_set_error (error,
453                                      GDK_PIXBUF_ERROR,
454                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
455                                      _("Invalid header in animation"));
456                         return FALSE;
457                 }
458                 context->title = g_try_malloc (context->chunk_size + 1);
459                 if (!context->title) 
460                 {
461                         g_set_error (error,
462                                      GDK_PIXBUF_ERROR,
463                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
464                                      _("Not enough memory to load animation"));
465                         return FALSE;
466                 }
467                 context->title[context->chunk_size] = 0;
468                 read_int8 (context, (guchar *)context->title, context->chunk_size);
469 #ifdef DEBUG_ANI
470                 g_print ("INAM %s\n", context->title);
471 #endif
472                 for (i = 0; i < context->pos; i++)
473                         gdk_pixbuf_set_option (context->animation->pixbufs[i], "Title", context->title);                        
474         }
475         else if (context->chunk_id == TAG_IART) 
476         {
477                 if (!context->animation) 
478                 {
479                         g_set_error (error,
480                                      GDK_PIXBUF_ERROR,
481                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
482                                      _("Invalid header in animation"));
483                         return FALSE;
484                 }
485                 context->author = g_try_malloc (context->chunk_size + 1);
486                 if (!context->author) 
487                 {
488                         g_set_error (error,
489                                      GDK_PIXBUF_ERROR,
490                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
491                                      _("Not enough memory to load animation"));
492                         return FALSE;
493                 }
494                 context->author[context->chunk_size] = 0;
495                 read_int8 (context, (guchar *)context->author, context->chunk_size);
496 #ifdef DEBUG_ANI
497                 g_print ("IART %s\n", context->author);
498 #endif
499                 for (i = 0; i < context->pos; i++)
500                         gdk_pixbuf_set_option (context->animation->pixbufs[i], "Author", context->author);                      
501         }
502
503 #ifdef DEBUG_ANI
504         {
505                 gint32 dummy = lsb_32 ((guchar *)&context->chunk_id);
506         
507                 g_print ("Loaded chunk with ID '%c%c%c%c' and length %" G_GUINT32_FORMAT "\n",
508                          ((char*)&dummy)[0], ((char*)&dummy)[1],
509                          ((char*)&dummy)[2], ((char*)&dummy)[3], 
510                          context->chunk_size);
511         }
512 #endif 
513                 
514         context->chunk_id = 0x0;
515         return TRUE;
516 }
517
518 static gboolean
519 gdk_pixbuf__ani_image_load_increment (gpointer data,
520                                       const guchar *buf, guint size,
521                                       GError **error)
522 {
523         AniLoaderContext *context = (AniLoaderContext *)data;
524         
525         if (context->n_bytes + size >= context->buffer_size) {
526                 int drop = context->byte - context->buffer;
527                 memmove (context->buffer, context->byte, context->n_bytes - drop);
528                 context->n_bytes -= drop;
529                 context->byte = context->buffer;
530                 if (context->n_bytes + size >= context->buffer_size) {
531                         guchar *tmp;
532                         context->buffer_size = MAX (context->n_bytes + size, context->buffer_size + 4096);
533 #ifdef DEBUG_ANI
534                         g_print ("growing buffer to %" G_GUINT32_FORMAT "\n", context->buffer_size);
535 #endif
536                         tmp = g_try_realloc (context->buffer, context->buffer_size);
537                         if (!tmp) 
538                         {
539                                 g_set_error (error,
540                                              GDK_PIXBUF_ERROR,
541                                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
542                                              _("Not enough memory to load animation"));
543                                 return FALSE;
544                         }
545                         context->byte = context->buffer = tmp;
546                 }
547         }
548         memcpy (context->buffer + context->n_bytes, buf, size);
549         context->n_bytes += size;
550
551         if (context->data_size == 0) 
552         {
553                 guint32 riff_id, chunk_id;
554                         
555                 if (BYTES_LEFT (context) < 12)
556                         return TRUE;
557                         
558                 riff_id = read_int32 (context);
559                 context->data_size = read_int32 (context);
560                 chunk_id = read_int32 (context);
561                         
562                 if (riff_id != TAG_RIFF || 
563                     context->data_size == 0 || 
564                     chunk_id != TAG_ACON) 
565                 {
566                         g_set_error (error,
567                                      GDK_PIXBUF_ERROR,
568                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
569                                      _("Invalid header in animation"));
570                         return FALSE; 
571                 }
572         }
573         
574         if (context->cp < context->data_size + 8) 
575         {
576                 GError *chunk_error = NULL;
577
578                 while (ani_load_chunk (context, &chunk_error)) ;
579                 if (chunk_error) 
580                 {
581                         g_propagate_error (error, chunk_error);
582                         return FALSE;
583                 }
584         }
585
586         return TRUE;
587 }
588
589 static gpointer
590 gdk_pixbuf__ani_image_begin_load (GdkPixbufModuleSizeFunc size_func, 
591                                   GdkPixbufModulePreparedFunc prepared_func, 
592                                   GdkPixbufModuleUpdatedFunc  updated_func,
593                                   gpointer user_data,
594                                   GError **error)
595 {
596         AniLoaderContext *context;
597         
598         context = g_new0 (AniLoaderContext, 1);
599         
600         context->prepared_func = prepared_func;
601         context->updated_func = updated_func;
602         context->user_data = user_data;
603         
604         context->pos = 0;
605         
606         context->buffer_size = 4096;
607         context->buffer = g_try_malloc (context->buffer_size);
608         if (!context->buffer) 
609         {
610                 context_free (context);
611                 g_set_error (error,
612                              GDK_PIXBUF_ERROR,
613                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
614                              _("Not enough memory to load animation"));
615                 return NULL;
616         }
617         
618         context->byte = context->buffer;
619         context->n_bytes = 0;
620         
621         return (gpointer) context;
622 }
623
624 static gboolean
625 gdk_pixbuf__ani_image_stop_load (gpointer data,
626                                  GError **error)
627 {
628         AniLoaderContext *context = (AniLoaderContext *) data;
629         
630         g_return_val_if_fail (context != NULL, TRUE);
631         context_free (context);
632         
633         return TRUE;
634 }
635
636 static void
637 prepared_notify (GdkPixbuf *pixbuf, 
638                  GdkPixbufAnimation *anim, 
639                  gpointer user_data)
640 {
641         if (anim != NULL)
642                 g_object_ref (anim);
643         *((GdkPixbufAnimation **)user_data) = anim;
644 }
645
646 static GdkPixbufAnimation *
647 gdk_pixbuf__ani_image_load_animation (FILE *f, GError **error)
648 {
649         guchar buffer[4096];
650         size_t length;
651         GdkPixbufAnimation *anim = NULL;
652         gpointer context;
653
654         context = gdk_pixbuf__ani_image_begin_load (NULL, prepared_notify, 
655                                                     NULL, &anim, error);
656         
657         if (!context)
658                 return NULL;
659         
660         while (!feof (f) && !ferror (f)) {
661                 length = fread (buffer, 1, sizeof (buffer), f);
662                 if (length > 0)
663                         if (!gdk_pixbuf__ani_image_load_increment (context, buffer, length, error)) {
664                                 gdk_pixbuf__ani_image_stop_load (context, NULL);
665                                 if (anim != NULL)
666                                         g_object_unref (anim);
667                                 return NULL;
668                         }
669         }
670
671         if (!gdk_pixbuf__ani_image_stop_load (context, error)) {
672                 if (anim != NULL)
673                         g_object_unref (anim);
674                 return NULL;
675         }
676         
677         return anim;
678 }
679
680 #ifndef INCLUDE_ani
681 #define MODULE_ENTRY(function) G_MODULE_EXPORT void function
682 #else
683 #define MODULE_ENTRY(function) void _gdk_pixbuf__ani_ ## function
684 #endif
685
686 MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
687 {
688         module->load_animation = gdk_pixbuf__ani_image_load_animation;
689         module->begin_load = gdk_pixbuf__ani_image_begin_load;
690         module->stop_load = gdk_pixbuf__ani_image_stop_load;
691         module->load_increment = gdk_pixbuf__ani_image_load_increment;
692 }
693
694 MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
695 {
696         static GdkPixbufModulePattern signature[] = {
697                 { "RIFF    ACON", "    xxxx    ", 100 },
698                 { NULL, NULL, 0 }
699         };
700         static gchar * mime_types[] = {
701                 "application/x-navi-animation",
702                 NULL
703         };
704         static gchar * extensions[] = {
705                 "ani",
706                 NULL
707         };
708         
709         info->name = "ani";
710         info->signature = signature;
711         info->description = N_("The ANI image format");
712         info->mime_types = mime_types;
713         info->extensions = extensions;
714         info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
715         info->license = "LGPL";
716 }
717
718
719
720