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
,
66 g_intern_static_string ("GdkPixbufGifAnim"),
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
;
183 iter
->first_loop_slowness
= 0;
185 return GDK_PIXBUF_ANIMATION_ITER (iter
);
190 static void gdk_pixbuf_gif_anim_iter_class_init (GdkPixbufGifAnimIterClass
*klass
);
191 static void gdk_pixbuf_gif_anim_iter_finalize (GObject
*object
);
193 static int gdk_pixbuf_gif_anim_iter_get_delay_time (GdkPixbufAnimationIter
*iter
);
194 static GdkPixbuf
* gdk_pixbuf_gif_anim_iter_get_pixbuf (GdkPixbufAnimationIter
*iter
);
195 static gboolean
gdk_pixbuf_gif_anim_iter_on_currently_loading_frame (GdkPixbufAnimationIter
*iter
);
196 static gboolean
gdk_pixbuf_gif_anim_iter_advance (GdkPixbufAnimationIter
*iter
,
197 const GTimeVal
*current_time
);
201 static gpointer iter_parent_class
;
204 gdk_pixbuf_gif_anim_iter_get_type (void)
206 static GType object_type
= 0;
209 static const GTypeInfo object_info
= {
210 sizeof (GdkPixbufGifAnimIterClass
),
211 (GBaseInitFunc
) NULL
,
212 (GBaseFinalizeFunc
) NULL
,
213 (GClassInitFunc
) gdk_pixbuf_gif_anim_iter_class_init
,
214 NULL
, /* class_finalize */
215 NULL
, /* class_data */
216 sizeof (GdkPixbufGifAnimIter
),
218 (GInstanceInitFunc
) NULL
,
221 object_type
= g_type_register_static (GDK_TYPE_PIXBUF_ANIMATION_ITER
,
222 g_intern_static_string ("GdkPixbufGifAnimIter"),
230 gdk_pixbuf_gif_anim_iter_class_init (GdkPixbufGifAnimIterClass
*klass
)
232 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
233 GdkPixbufAnimationIterClass
*anim_iter_class
=
234 GDK_PIXBUF_ANIMATION_ITER_CLASS (klass
);
236 iter_parent_class
= g_type_class_peek_parent (klass
);
238 object_class
->finalize
= gdk_pixbuf_gif_anim_iter_finalize
;
240 anim_iter_class
->get_delay_time
= gdk_pixbuf_gif_anim_iter_get_delay_time
;
241 anim_iter_class
->get_pixbuf
= gdk_pixbuf_gif_anim_iter_get_pixbuf
;
242 anim_iter_class
->on_currently_loading_frame
= gdk_pixbuf_gif_anim_iter_on_currently_loading_frame
;
243 anim_iter_class
->advance
= gdk_pixbuf_gif_anim_iter_advance
;
247 gdk_pixbuf_gif_anim_iter_finalize (GObject
*object
)
249 GdkPixbufGifAnimIter
*iter
= GDK_PIXBUF_GIF_ANIM_ITER (object
);
253 g_object_unref (iter
->gif_anim
);
255 G_OBJECT_CLASS (iter_parent_class
)->finalize (object
);
259 gdk_pixbuf_gif_anim_iter_advance (GdkPixbufAnimationIter
*anim_iter
,
260 const GTimeVal
*current_time
)
262 GdkPixbufGifAnimIter
*iter
;
268 iter
= GDK_PIXBUF_GIF_ANIM_ITER (anim_iter
);
270 iter
->current_time
= *current_time
;
272 /* We use milliseconds for all times */
274 (((iter
->current_time
.tv_sec
- iter
->start_time
.tv_sec
) * G_USEC_PER_SEC
+
275 iter
->current_time
.tv_usec
- iter
->start_time
.tv_usec
)) / 1000;
278 /* Try to compensate; probably the system clock
281 iter
->start_time
= iter
->current_time
;
285 g_assert (iter
->gif_anim
->total_time
> 0);
287 /* See how many times we've already played the full animation,
288 * and subtract time for that.
291 if (iter
->gif_anim
->loading
)
294 /* If current_frame is NULL at this point, we have loaded the
295 * animation from a source which fell behind the speed of the
296 * display. We remember how much slower the first loop was due
297 * to this and correct the position calculation in order to not
298 * jump in the middle of the second loop.
300 if (iter
->current_frame
== NULL
)
301 iter
->first_loop_slowness
= MAX(0, elapsed
- iter
->gif_anim
->total_time
);
303 loop
= (elapsed
- iter
->first_loop_slowness
) / iter
->gif_anim
->total_time
;
304 elapsed
= (elapsed
- iter
->first_loop_slowness
) % iter
->gif_anim
->total_time
;
307 iter
->position
= elapsed
;
309 /* Now move to the proper frame */
310 if (iter
->gif_anim
->loop
== 0 || loop
< iter
->gif_anim
->loop
)
311 tmp
= iter
->gif_anim
->frames
;
314 while (tmp
!= NULL
) {
315 GdkPixbufFrame
*frame
= tmp
->data
;
317 if (iter
->position
>= frame
->elapsed
&&
318 iter
->position
< (frame
->elapsed
+ frame
->delay_time
))
324 old
= iter
->current_frame
;
326 iter
->current_frame
= tmp
;
328 return iter
->current_frame
!= old
;
332 gdk_pixbuf_gif_anim_iter_get_delay_time (GdkPixbufAnimationIter
*anim_iter
)
334 GdkPixbufFrame
*frame
;
335 GdkPixbufGifAnimIter
*iter
;
337 iter
= GDK_PIXBUF_GIF_ANIM_ITER (anim_iter
);
339 if (iter
->current_frame
) {
340 frame
= iter
->current_frame
->data
;
343 g_print ("frame start: %d pos: %d frame len: %d frame remaining: %d\n",
347 frame
->delay_time
- (iter
->position
- frame
->elapsed
));
350 return frame
->delay_time
- (iter
->position
- frame
->elapsed
);
352 return -1; /* show last frame forever */
356 gdk_pixbuf_gif_anim_frame_composite (GdkPixbufGifAnim
*gif_anim
,
357 GdkPixbufFrame
*frame
)
362 link
= g_list_find (gif_anim
->frames
, frame
);
364 if (frame
->need_recomposite
|| frame
->composited
== NULL
) {
365 /* For now, to composite we start with the last
366 * composited frame and composite everything up to
370 /* Rewind to last composited frame. */
372 while (tmp
!= NULL
) {
373 GdkPixbufFrame
*f
= tmp
->data
;
375 if (f
->need_recomposite
) {
377 g_object_unref (f
->composited
);
378 f
->composited
= NULL
;
382 if (f
->composited
!= NULL
)
388 /* Go forward, compositing all frames up to the current frame */
390 tmp
= gif_anim
->frames
;
392 while (tmp
!= NULL
) {
393 GdkPixbufFrame
*f
= tmp
->data
;
394 gint clipped_width
, clipped_height
;
396 if (f
->pixbuf
== NULL
)
399 clipped_width
= MIN (gif_anim
->width
- f
->x_offset
, gdk_pixbuf_get_width (f
->pixbuf
));
400 clipped_height
= MIN (gif_anim
->height
- f
->y_offset
, gdk_pixbuf_get_height (f
->pixbuf
));
402 if (f
->need_recomposite
) {
404 g_object_unref (f
->composited
);
405 f
->composited
= NULL
;
409 if (f
->composited
!= NULL
)
412 if (tmp
->prev
== NULL
) {
413 /* First frame may be smaller than the whole image;
414 * if so, we make the area outside it full alpha if the
415 * image has alpha, and background color otherwise.
416 * GIF spec doesn't actually say what to do about this.
418 f
->composited
= gdk_pixbuf_new (GDK_COLORSPACE_RGB
,
420 8, gif_anim
->width
, gif_anim
->height
);
422 if (f
->composited
== NULL
)
425 /* alpha gets dumped if f->composited has no alpha */
427 gdk_pixbuf_fill (f
->composited
,
428 (gif_anim
->bg_red
<< 24) |
429 (gif_anim
->bg_green
<< 16) |
430 (gif_anim
->bg_blue
<< 8));
432 if (clipped_width
> 0 && clipped_height
> 0)
433 gdk_pixbuf_composite (f
->pixbuf
,
439 f
->x_offset
, f
->y_offset
,
444 if (f
->action
== GDK_PIXBUF_FRAME_REVERT
)
445 g_warning ("First frame of GIF has bad dispose mode, GIF loader should not have loaded this image");
447 f
->need_recomposite
= FALSE
;
449 GdkPixbufFrame
*prev_frame
;
450 gint prev_clipped_width
;
451 gint prev_clipped_height
;
453 prev_frame
= tmp
->prev
->data
;
455 prev_clipped_width
= MIN (gif_anim
->width
- prev_frame
->x_offset
, gdk_pixbuf_get_width (prev_frame
->pixbuf
));
456 prev_clipped_height
= MIN (gif_anim
->height
- prev_frame
->y_offset
, gdk_pixbuf_get_height (prev_frame
->pixbuf
));
458 /* Init f->composited with what we should have after the previous
462 if (prev_frame
->action
== GDK_PIXBUF_FRAME_RETAIN
) {
463 f
->composited
= gdk_pixbuf_copy (prev_frame
->composited
);
465 if (f
->composited
== NULL
)
468 } else if (prev_frame
->action
== GDK_PIXBUF_FRAME_DISPOSE
) {
469 f
->composited
= gdk_pixbuf_copy (prev_frame
->composited
);
471 if (f
->composited
== NULL
)
474 if (prev_clipped_width
> 0 && prev_clipped_height
> 0) {
475 /* Clear area of previous frame to background */
478 area
= gdk_pixbuf_new_subpixbuf (f
->composited
,
479 prev_frame
->x_offset
,
480 prev_frame
->y_offset
,
482 prev_clipped_height
);
487 gdk_pixbuf_fill (area
,
488 (gif_anim
->bg_red
<< 24) |
489 (gif_anim
->bg_green
<< 16) |
490 (gif_anim
->bg_blue
<< 8));
492 g_object_unref (area
);
494 } else if (prev_frame
->action
== GDK_PIXBUF_FRAME_REVERT
) {
495 f
->composited
= gdk_pixbuf_copy (prev_frame
->composited
);
497 if (f
->composited
== NULL
)
500 if (prev_frame
->revert
!= NULL
&&
501 prev_clipped_width
> 0 && prev_clipped_height
> 0) {
502 /* Copy in the revert frame */
503 gdk_pixbuf_copy_area (prev_frame
->revert
,
505 gdk_pixbuf_get_width (prev_frame
->revert
),
506 gdk_pixbuf_get_height (prev_frame
->revert
),
508 prev_frame
->x_offset
,
509 prev_frame
->y_offset
);
512 g_warning ("Unknown revert action for GIF frame");
515 if (f
->revert
== NULL
&&
516 f
->action
== GDK_PIXBUF_FRAME_REVERT
) {
517 if (clipped_width
> 0 && clipped_height
> 0) {
518 /* We need to save the contents before compositing */
521 area
= gdk_pixbuf_new_subpixbuf (f
->composited
,
530 f
->revert
= gdk_pixbuf_copy (area
);
532 g_object_unref (area
);
534 if (f
->revert
== NULL
)
539 if (clipped_width
> 0 && clipped_height
> 0 &&
540 f
->pixbuf
!= NULL
&& f
->composited
!= NULL
) {
541 /* Put current frame onto f->composited */
542 gdk_pixbuf_composite (f
->pixbuf
,
548 f
->x_offset
, f
->y_offset
,
554 f
->need_recomposite
= FALSE
;
567 gdk_pixbuf_gif_anim_iter_get_pixbuf (GdkPixbufAnimationIter
*anim_iter
)
569 GdkPixbufGifAnimIter
*iter
;
570 GdkPixbufFrame
*frame
;
572 iter
= GDK_PIXBUF_GIF_ANIM_ITER (anim_iter
);
574 frame
= iter
->current_frame
? iter
->current_frame
->data
: g_list_last (iter
->gif_anim
->frames
)->data
;
578 g_print ("current frame %d dispose mode %d %d x %d\n",
579 g_list_index (iter
->gif_anim
->frames
,
582 gdk_pixbuf_get_width (frame
->pixbuf
),
583 gdk_pixbuf_get_height (frame
->pixbuf
));
589 gdk_pixbuf_gif_anim_frame_composite (iter
->gif_anim
, frame
);
591 return frame
->composited
;
595 gdk_pixbuf_gif_anim_iter_on_currently_loading_frame (GdkPixbufAnimationIter
*anim_iter
)
597 GdkPixbufGifAnimIter
*iter
;
599 iter
= GDK_PIXBUF_GIF_ANIM_ITER (anim_iter
);
601 return iter
->current_frame
== NULL
|| iter
->current_frame
->next
== NULL
;