Add powerbox hook
[gtk-with-powerbox.git] / gdk-pixbuf / io-gif-animation.c
blob580b0575289ec41b0862b07beecadcc1363d3fc5
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.
25 #include "config.h"
26 #include <errno.h>
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,
37 int *width,
38 int *height);
39 static GdkPixbufAnimationIter* gdk_pixbuf_gif_anim_get_iter (GdkPixbufAnimation *anim,
40 const GTimeVal *start_time);
45 static gpointer parent_class;
47 GType
48 gdk_pixbuf_gif_anim_get_type (void)
50 static GType object_type = 0;
52 if (!object_type) {
53 static const GTypeInfo object_info = {
54 sizeof (GdkPixbufGifAnimClass),
55 (GBaseInitFunc) NULL,
56 (GBaseFinalizeFunc) NULL,
57 (GClassInitFunc) gdk_pixbuf_gif_anim_class_init,
58 NULL, /* class_finalize */
59 NULL, /* class_data */
60 sizeof (GdkPixbufGifAnim),
61 0, /* n_preallocs */
62 (GInstanceInitFunc) NULL,
65 object_type = g_type_register_static (GDK_TYPE_PIXBUF_ANIMATION,
66 g_intern_static_string ("GdkPixbufGifAnim"),
67 &object_info, 0);
70 return object_type;
73 static void
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;
89 static void
90 gdk_pixbuf_gif_anim_finalize (GObject *object)
92 GdkPixbufGifAnim *gif_anim = GDK_PIXBUF_GIF_ANIM (object);
94 GList *l;
95 GdkPixbufFrame *frame;
97 for (l = gif_anim->frames; l; l = l->next) {
98 frame = l->data;
99 g_object_unref (frame->pixbuf);
100 if (frame->composited)
101 g_object_unref (frame->composited);
102 if (frame->revert)
103 g_object_unref (frame->revert);
104 g_free (frame);
107 g_list_free (gif_anim->frames);
109 G_OBJECT_CLASS (parent_class)->finalize (object);
112 static gboolean
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);
123 static GdkPixbuf*
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)
131 return NULL;
132 else
133 return GDK_PIXBUF (((GdkPixbufFrame*)gif_anim->frames->data)->pixbuf);
136 static void
137 gdk_pixbuf_gif_anim_get_size (GdkPixbufAnimation *anim,
138 int *width,
139 int *height)
141 GdkPixbufGifAnim *gif_anim;
143 gif_anim = GDK_PIXBUF_GIF_ANIM (anim);
145 if (width)
146 *width = gif_anim->width;
148 if (height)
149 *height = gif_anim->height;
153 static void
154 iter_clear (GdkPixbufGifAnimIter *iter)
156 iter->current_frame = NULL;
159 static void
160 iter_restart (GdkPixbufGifAnimIter *iter)
162 iter_clear (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);
179 iter_restart (iter);
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;
203 GType
204 gdk_pixbuf_gif_anim_iter_get_type (void)
206 static GType object_type = 0;
208 if (!object_type) {
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),
217 0, /* n_preallocs */
218 (GInstanceInitFunc) NULL,
221 object_type = g_type_register_static (GDK_TYPE_PIXBUF_ANIMATION_ITER,
222 g_intern_static_string ("GdkPixbufGifAnimIter"),
223 &object_info, 0);
226 return object_type;
229 static void
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;
246 static void
247 gdk_pixbuf_gif_anim_iter_finalize (GObject *object)
249 GdkPixbufGifAnimIter *iter = GDK_PIXBUF_GIF_ANIM_ITER (object);
251 iter_clear (iter);
253 g_object_unref (iter->gif_anim);
255 G_OBJECT_CLASS (iter_parent_class)->finalize (object);
258 static gboolean
259 gdk_pixbuf_gif_anim_iter_advance (GdkPixbufAnimationIter *anim_iter,
260 const GTimeVal *current_time)
262 GdkPixbufGifAnimIter *iter;
263 gint elapsed;
264 gint loop;
265 GList *tmp;
266 GList *old;
268 iter = GDK_PIXBUF_GIF_ANIM_ITER (anim_iter);
270 iter->current_time = *current_time;
272 /* We use milliseconds for all times */
273 elapsed =
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;
277 if (elapsed < 0) {
278 /* Try to compensate; probably the system clock
279 * was set backwards
281 iter->start_time = iter->current_time;
282 elapsed = 0;
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)
292 loop = 0;
293 else {
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;
312 else
313 tmp = NULL;
314 while (tmp != NULL) {
315 GdkPixbufFrame *frame = tmp->data;
317 if (iter->position >= frame->elapsed &&
318 iter->position < (frame->elapsed + frame->delay_time))
319 break;
321 tmp = tmp->next;
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;
342 #if 0
343 g_print ("frame start: %d pos: %d frame len: %d frame remaining: %d\n",
344 frame->elapsed,
345 iter->position,
346 frame->delay_time,
347 frame->delay_time - (iter->position - frame->elapsed));
348 #endif
350 return frame->delay_time - (iter->position - frame->elapsed);
351 } else
352 return -1; /* show last frame forever */
355 void
356 gdk_pixbuf_gif_anim_frame_composite (GdkPixbufGifAnim *gif_anim,
357 GdkPixbufFrame *frame)
359 GList *link;
360 GList *tmp;
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
367 * here.
370 /* Rewind to last composited frame. */
371 tmp = link;
372 while (tmp != NULL) {
373 GdkPixbufFrame *f = tmp->data;
375 if (f->need_recomposite) {
376 if (f->composited) {
377 g_object_unref (f->composited);
378 f->composited = NULL;
382 if (f->composited != NULL)
383 break;
385 tmp = tmp->prev;
388 /* Go forward, compositing all frames up to the current frame */
389 if (tmp == NULL)
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)
397 return;
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) {
403 if (f->composited) {
404 g_object_unref (f->composited);
405 f->composited = NULL;
409 if (f->composited != NULL)
410 goto next;
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,
419 TRUE,
420 8, gif_anim->width, gif_anim->height);
422 if (f->composited == NULL)
423 return;
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,
434 f->composited,
435 f->x_offset,
436 f->y_offset,
437 clipped_width,
438 clipped_height,
439 f->x_offset, f->y_offset,
440 1.0, 1.0,
441 GDK_INTERP_BILINEAR,
442 255);
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;
448 } else {
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
459 * frame
462 if (prev_frame->action == GDK_PIXBUF_FRAME_RETAIN) {
463 f->composited = gdk_pixbuf_copy (prev_frame->composited);
465 if (f->composited == NULL)
466 return;
468 } else if (prev_frame->action == GDK_PIXBUF_FRAME_DISPOSE) {
469 f->composited = gdk_pixbuf_copy (prev_frame->composited);
471 if (f->composited == NULL)
472 return;
474 if (prev_clipped_width > 0 && prev_clipped_height > 0) {
475 /* Clear area of previous frame to background */
476 GdkPixbuf *area;
478 area = gdk_pixbuf_new_subpixbuf (f->composited,
479 prev_frame->x_offset,
480 prev_frame->y_offset,
481 prev_clipped_width,
482 prev_clipped_height);
484 if (area == NULL)
485 return;
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)
498 return;
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,
504 0, 0,
505 gdk_pixbuf_get_width (prev_frame->revert),
506 gdk_pixbuf_get_height (prev_frame->revert),
507 f->composited,
508 prev_frame->x_offset,
509 prev_frame->y_offset);
511 } else {
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 */
519 GdkPixbuf *area;
521 area = gdk_pixbuf_new_subpixbuf (f->composited,
522 f->x_offset,
523 f->y_offset,
524 clipped_width,
525 clipped_height);
527 if (area == NULL)
528 return;
530 f->revert = gdk_pixbuf_copy (area);
532 g_object_unref (area);
534 if (f->revert == NULL)
535 return;
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,
543 f->composited,
544 f->x_offset,
545 f->y_offset,
546 clipped_width,
547 clipped_height,
548 f->x_offset, f->y_offset,
549 1.0, 1.0,
550 GDK_INTERP_NEAREST,
551 255);
554 f->need_recomposite = FALSE;
557 next:
558 if (tmp == link)
559 break;
561 tmp = tmp->next;
566 GdkPixbuf*
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;
576 #if 0
577 if (FALSE && frame)
578 g_print ("current frame %d dispose mode %d %d x %d\n",
579 g_list_index (iter->gif_anim->frames,
580 frame),
581 frame->action,
582 gdk_pixbuf_get_width (frame->pixbuf),
583 gdk_pixbuf_get_height (frame->pixbuf));
584 #endif
586 if (frame == NULL)
587 return NULL;
589 gdk_pixbuf_gif_anim_frame_composite (iter->gif_anim, frame);
591 return frame->composited;
594 static gboolean
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;