1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2 /* GdkPixbuf library - animated gif support
4 * Copyright (C) 1999 The Free Software Foundation
6 * Authors: Jonathan Blandford <jrb@redhat.com>
7 * Havoc Pennington <hp@redhat.com>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
27 #include "gdk-pixbuf-private.h"
28 #include "io-gif-animation.h"
30 static void gdk_pixbuf_gif_anim_class_init (GdkPixbufGifAnimClass *klass);
31 static void gdk_pixbuf_gif_anim_finalize (GObject *object);
33 static gboolean gdk_pixbuf_gif_anim_is_static_image (GdkPixbufAnimation *animation);
34 static GdkPixbuf* gdk_pixbuf_gif_anim_get_static_image (GdkPixbufAnimation *animation);
36 static void gdk_pixbuf_gif_anim_get_size (GdkPixbufAnimation *anim,
39 static GdkPixbufAnimationIter* gdk_pixbuf_gif_anim_get_iter (GdkPixbufAnimation *anim,
40 const GTimeVal *start_time);
45 static gpointer parent_class;
48 gdk_pixbuf_gif_anim_get_type (void)
50 static GType object_type = 0;
53 static const GTypeInfo object_info = {
54 sizeof (GdkPixbufGifAnimClass),
56 (GBaseFinalizeFunc) NULL,
57 (GClassInitFunc) gdk_pixbuf_gif_anim_class_init,
58 NULL, /* class_finalize */
59 NULL, /* class_data */
60 sizeof (GdkPixbufGifAnim),
62 (GInstanceInitFunc) NULL,
65 object_type = g_type_register_static (GDK_TYPE_PIXBUF_ANIMATION,
74 gdk_pixbuf_gif_anim_class_init (GdkPixbufGifAnimClass *klass)
76 GObjectClass *object_class = G_OBJECT_CLASS (klass);
77 GdkPixbufAnimationClass *anim_class = GDK_PIXBUF_ANIMATION_CLASS (klass);
79 parent_class = g_type_class_peek_parent (klass);
81 object_class->finalize = gdk_pixbuf_gif_anim_finalize;
83 anim_class->is_static_image = gdk_pixbuf_gif_anim_is_static_image;
84 anim_class->get_static_image = gdk_pixbuf_gif_anim_get_static_image;
85 anim_class->get_size = gdk_pixbuf_gif_anim_get_size;
86 anim_class->get_iter = gdk_pixbuf_gif_anim_get_iter;
90 gdk_pixbuf_gif_anim_finalize (GObject *object)
92 GdkPixbufGifAnim *gif_anim = GDK_PIXBUF_GIF_ANIM (object);
95 GdkPixbufFrame *frame;
97 for (l = gif_anim->frames; l; l = l->next) {
99 g_object_unref (frame->pixbuf);
100 if (frame->composited)
101 g_object_unref (frame->composited);
103 g_object_unref (frame->revert);
107 g_list_free (gif_anim->frames);
109 G_OBJECT_CLASS (parent_class)->finalize (object);
113 gdk_pixbuf_gif_anim_is_static_image (GdkPixbufAnimation *animation)
115 GdkPixbufGifAnim *gif_anim;
117 gif_anim = GDK_PIXBUF_GIF_ANIM (animation);
119 return (gif_anim->frames != NULL &&
120 gif_anim->frames->next == NULL);
124 gdk_pixbuf_gif_anim_get_static_image (GdkPixbufAnimation *animation)
126 GdkPixbufGifAnim *gif_anim;
128 gif_anim = GDK_PIXBUF_GIF_ANIM (animation);
130 if (gif_anim->frames == NULL)
133 return GDK_PIXBUF (((GdkPixbufFrame*)gif_anim->frames->data)->pixbuf);
137 gdk_pixbuf_gif_anim_get_size (GdkPixbufAnimation *anim,
141 GdkPixbufGifAnim *gif_anim;
143 gif_anim = GDK_PIXBUF_GIF_ANIM (anim);
146 *width = gif_anim->width;
149 *height = gif_anim->height;
154 iter_clear (GdkPixbufGifAnimIter *iter)
156 iter->current_frame = NULL;
160 iter_restart (GdkPixbufGifAnimIter *iter)
164 iter->current_frame = iter->gif_anim->frames;
167 static GdkPixbufAnimationIter*
168 gdk_pixbuf_gif_anim_get_iter (GdkPixbufAnimation *anim,
169 const GTimeVal *start_time)
171 GdkPixbufGifAnimIter *iter;
173 iter = g_object_new (GDK_TYPE_PIXBUF_GIF_ANIM_ITER, NULL);
175 iter->gif_anim = GDK_PIXBUF_GIF_ANIM (anim);
177 g_object_ref (iter->gif_anim);
181 iter->start_time = *start_time;
182 iter->current_time = *start_time;
184 return GDK_PIXBUF_ANIMATION_ITER (iter);
189 static void gdk_pixbuf_gif_anim_iter_class_init (GdkPixbufGifAnimIterClass *klass);
190 static void gdk_pixbuf_gif_anim_iter_finalize (GObject *object);
192 static int gdk_pixbuf_gif_anim_iter_get_delay_time (GdkPixbufAnimationIter *iter);
193 static GdkPixbuf* gdk_pixbuf_gif_anim_iter_get_pixbuf (GdkPixbufAnimationIter *iter);
194 static gboolean gdk_pixbuf_gif_anim_iter_on_currently_loading_frame (GdkPixbufAnimationIter *iter);
195 static gboolean gdk_pixbuf_gif_anim_iter_advance (GdkPixbufAnimationIter *iter,
196 const GTimeVal *current_time);
200 static gpointer iter_parent_class;
203 gdk_pixbuf_gif_anim_iter_get_type (void)
205 static GType object_type = 0;
208 static const GTypeInfo object_info = {
209 sizeof (GdkPixbufGifAnimIterClass),
210 (GBaseInitFunc) NULL,
211 (GBaseFinalizeFunc) NULL,
212 (GClassInitFunc) gdk_pixbuf_gif_anim_iter_class_init,
213 NULL, /* class_finalize */
214 NULL, /* class_data */
215 sizeof (GdkPixbufGifAnimIter),
217 (GInstanceInitFunc) NULL,
220 object_type = g_type_register_static (GDK_TYPE_PIXBUF_ANIMATION_ITER,
221 "GdkPixbufGifAnimIter",
229 gdk_pixbuf_gif_anim_iter_class_init (GdkPixbufGifAnimIterClass *klass)
231 GObjectClass *object_class = G_OBJECT_CLASS (klass);
232 GdkPixbufAnimationIterClass *anim_iter_class =
233 GDK_PIXBUF_ANIMATION_ITER_CLASS (klass);
235 iter_parent_class = g_type_class_peek_parent (klass);
237 object_class->finalize = gdk_pixbuf_gif_anim_iter_finalize;
239 anim_iter_class->get_delay_time = gdk_pixbuf_gif_anim_iter_get_delay_time;
240 anim_iter_class->get_pixbuf = gdk_pixbuf_gif_anim_iter_get_pixbuf;
241 anim_iter_class->on_currently_loading_frame = gdk_pixbuf_gif_anim_iter_on_currently_loading_frame;
242 anim_iter_class->advance = gdk_pixbuf_gif_anim_iter_advance;
246 gdk_pixbuf_gif_anim_iter_finalize (GObject *object)
248 GdkPixbufGifAnimIter *iter = GDK_PIXBUF_GIF_ANIM_ITER (object);
252 g_object_unref (iter->gif_anim);
254 G_OBJECT_CLASS (iter_parent_class)->finalize (object);
258 gdk_pixbuf_gif_anim_iter_advance (GdkPixbufAnimationIter *anim_iter,
259 const GTimeVal *current_time)
261 GdkPixbufGifAnimIter *iter;
267 iter = GDK_PIXBUF_GIF_ANIM_ITER (anim_iter);
269 iter->current_time = *current_time;
271 /* We use milliseconds for all times */
273 (((iter->current_time.tv_sec - iter->start_time.tv_sec) * G_USEC_PER_SEC +
274 iter->current_time.tv_usec - iter->start_time.tv_usec)) / 1000;
277 /* Try to compensate; probably the system clock
280 iter->start_time = iter->current_time;
284 g_assert (iter->gif_anim->total_time > 0);
286 /* See how many times we've already played the full animation,
287 * and subtract time for that.
290 loop = elapsed / iter->gif_anim->total_time;
291 elapsed = elapsed % iter->gif_anim->total_time;
293 iter->position = elapsed;
295 /* Now move to the proper frame */
296 if (iter->gif_anim->loop == 0 || loop < iter->gif_anim->loop)
297 tmp = iter->gif_anim->frames;
300 while (tmp != NULL) {
301 GdkPixbufFrame *frame = tmp->data;
303 if (iter->position >= frame->elapsed &&
304 iter->position < (frame->elapsed + frame->delay_time))
310 old = iter->current_frame;
312 iter->current_frame = tmp;
314 return iter->current_frame != old;
318 gdk_pixbuf_gif_anim_iter_get_delay_time (GdkPixbufAnimationIter *anim_iter)
320 GdkPixbufFrame *frame;
321 GdkPixbufGifAnimIter *iter;
323 iter = GDK_PIXBUF_GIF_ANIM_ITER (anim_iter);
325 if (iter->current_frame) {
326 frame = iter->current_frame->data;
329 g_print ("frame start: %d pos: %d frame len: %d frame remaining: %d\n",
333 frame->delay_time - (iter->position - frame->elapsed));
336 return frame->delay_time - (iter->position - frame->elapsed);
338 return -1; /* show last frame forever */
343 gdk_pixbuf_gif_anim_frame_composite (GdkPixbufGifAnim *gif_anim,
344 GdkPixbufFrame *frame)
349 link = g_list_find (gif_anim->frames, frame);
351 if (frame->need_recomposite || frame->composited == NULL) {
352 /* For now, to composite we start with the last
353 * composited frame and composite everything up to
357 /* Rewind to last composited frame. */
359 while (tmp != NULL) {
360 GdkPixbufFrame *f = tmp->data;
362 if (f->need_recomposite) {
364 g_object_unref (f->composited);
365 f->composited = NULL;
369 if (f->composited != NULL)
375 /* Go forward, compositing all frames up to the current frame */
377 tmp = gif_anim->frames;
379 while (tmp != NULL) {
380 GdkPixbufFrame *f = tmp->data;
382 if (f->need_recomposite) {
384 g_object_unref (f->composited);
385 f->composited = NULL;
389 if (f->composited != NULL)
392 if (tmp->prev == NULL) {
393 /* First frame may be smaller than the whole image;
394 * if so, we make the area outside it full alpha if the
395 * image has alpha, and background color otherwise.
396 * GIF spec doesn't actually say what to do about this.
398 f->composited = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
400 8, gif_anim->width, gif_anim->height);
402 /* alpha gets dumped if f->composited has no alpha */
404 gdk_pixbuf_fill (f->composited,
405 (gif_anim->bg_red << 24) |
406 (gif_anim->bg_green << 16) |
407 (gif_anim->bg_blue << 8));
409 gdk_pixbuf_composite (f->pixbuf,
413 gdk_pixbuf_get_width (f->pixbuf),
414 gdk_pixbuf_get_height (f->pixbuf),
415 f->x_offset, f->y_offset,
420 gdk_pixbuf_copy_area (f->pixbuf,
422 gdk_pixbuf_get_width (f->pixbuf),
423 gdk_pixbuf_get_height (f->pixbuf),
430 if (f->action == GDK_PIXBUF_FRAME_REVERT)
431 g_warning ("First frame of GIF has bad dispose mode, GIF loader should not have loaded this image");
433 f->need_recomposite = FALSE;
435 GdkPixbufFrame *prev_frame;
437 prev_frame = tmp->prev->data;
439 /* Init f->composited with what we should have after the previous
443 if (prev_frame->action == GDK_PIXBUF_FRAME_RETAIN) {
444 f->composited = gdk_pixbuf_copy (prev_frame->composited);
446 } else if (prev_frame->action == GDK_PIXBUF_FRAME_DISPOSE) {
449 f->composited = gdk_pixbuf_copy (prev_frame->composited);
451 /* Clear area of previous frame to background */
452 area = gdk_pixbuf_new_subpixbuf (f->composited,
453 prev_frame->x_offset,
454 prev_frame->y_offset,
455 gdk_pixbuf_get_width (prev_frame->pixbuf),
456 gdk_pixbuf_get_height (prev_frame->pixbuf));
458 gdk_pixbuf_fill (area,
459 (gif_anim->bg_red << 24) |
460 (gif_anim->bg_green << 16) |
461 (gif_anim->bg_blue << 8));
463 g_object_unref (area);
465 } else if (prev_frame->action == GDK_PIXBUF_FRAME_REVERT) {
466 f->composited = gdk_pixbuf_copy (prev_frame->composited);
468 /* Copy in the revert frame */
469 gdk_pixbuf_copy_area (prev_frame->revert,
471 gdk_pixbuf_get_width (prev_frame->revert),
472 gdk_pixbuf_get_height (prev_frame->revert),
474 prev_frame->x_offset,
475 prev_frame->y_offset);
477 g_warning ("Unknown revert action for GIF frame");
480 if (f->revert == NULL &&
481 f->action == GDK_PIXBUF_FRAME_REVERT) {
482 /* We need to save the contents before compositing */
485 area = gdk_pixbuf_new_subpixbuf (f->composited,
488 gdk_pixbuf_get_width (f->pixbuf),
489 gdk_pixbuf_get_height (f->pixbuf));
491 f->revert = gdk_pixbuf_copy (area);
493 g_object_unref (area);
496 /* Put current frame onto f->composited */
497 gdk_pixbuf_composite (f->pixbuf,
501 gdk_pixbuf_get_width (f->pixbuf),
502 gdk_pixbuf_get_height (f->pixbuf),
503 f->x_offset, f->y_offset,
508 f->need_recomposite = FALSE;
519 g_assert (frame->composited != NULL);
520 g_assert (gdk_pixbuf_get_width (frame->composited) == gif_anim->width);
521 g_assert (gdk_pixbuf_get_height (frame->composited) == gif_anim->height);
525 gdk_pixbuf_gif_anim_iter_get_pixbuf (GdkPixbufAnimationIter *anim_iter)
527 GdkPixbufGifAnimIter *iter;
528 GdkPixbufFrame *frame;
530 iter = GDK_PIXBUF_GIF_ANIM_ITER (anim_iter);
532 frame = iter->current_frame ? iter->current_frame->data : g_list_last (iter->gif_anim->frames)->data;
536 g_print ("current frame %d dispose mode %d %d x %d\n",
537 g_list_index (iter->gif_anim->frames,
540 gdk_pixbuf_get_width (frame->pixbuf),
541 gdk_pixbuf_get_height (frame->pixbuf));
547 gdk_pixbuf_gif_anim_frame_composite (iter->gif_anim, frame);
549 return frame->composited;
553 gdk_pixbuf_gif_anim_iter_on_currently_loading_frame (GdkPixbufAnimationIter *anim_iter)
555 GdkPixbufGifAnimIter *iter;
557 iter = GDK_PIXBUF_GIF_ANIM_ITER (anim_iter);
559 return iter->current_frame == NULL || iter->current_frame->next == NULL;