]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-gif-animation.c
Pass TRUE for search_sensitive - prevents a problem where after hitting
[~andy/gtk] / gdk-pixbuf / io-gif-animation.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2 /* GdkPixbuf library - animated gif support
3  *
4  * Copyright (C) 1999 The Free Software Foundation
5  *
6  * Authors: Jonathan Blandford <jrb@redhat.com>
7  *          Havoc Pennington <hp@redhat.com>
8  *
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.
13  *
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.
18  *
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.
23  */
24
25 #include <config.h>
26 #include <errno.h>
27 #include "gdk-pixbuf-private.h"
28 #include "io-gif-animation.h"
29
30 static void gdk_pixbuf_gif_anim_class_init (GdkPixbufGifAnimClass *klass);
31 static void gdk_pixbuf_gif_anim_finalize   (GObject        *object);
32
33 static gboolean                gdk_pixbuf_gif_anim_is_static_image  (GdkPixbufAnimation *animation);
34 static GdkPixbuf*              gdk_pixbuf_gif_anim_get_static_image (GdkPixbufAnimation *animation);
35
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);
41
42
43 \f
44
45 static gpointer parent_class;
46
47 GType
48 gdk_pixbuf_gif_anim_get_type (void)
49 {
50         static GType object_type = 0;
51
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,
63                 };
64                 
65                 object_type = g_type_register_static (GDK_TYPE_PIXBUF_ANIMATION,
66                                                       "GdkPixbufGifAnim",
67                                                       &object_info, 0);
68         }
69         
70         return object_type;
71 }
72
73 static void
74 gdk_pixbuf_gif_anim_class_init (GdkPixbufGifAnimClass *klass)
75 {
76         GObjectClass *object_class = G_OBJECT_CLASS (klass);
77         GdkPixbufAnimationClass *anim_class = GDK_PIXBUF_ANIMATION_CLASS (klass);
78         
79         parent_class = g_type_class_peek_parent (klass);
80         
81         object_class->finalize = gdk_pixbuf_gif_anim_finalize;
82
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;
87 }
88
89 static void
90 gdk_pixbuf_gif_anim_finalize (GObject *object)
91 {
92         GdkPixbufGifAnim *gif_anim = GDK_PIXBUF_GIF_ANIM (object);
93
94         GList *l;
95         GdkPixbufFrame *frame;
96         
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);
105         }
106         
107         g_list_free (gif_anim->frames);
108         
109         G_OBJECT_CLASS (parent_class)->finalize (object);
110 }
111
112 static gboolean
113 gdk_pixbuf_gif_anim_is_static_image  (GdkPixbufAnimation *animation)
114 {
115         GdkPixbufGifAnim *gif_anim;
116
117         gif_anim = GDK_PIXBUF_GIF_ANIM (animation);
118
119         return (gif_anim->frames != NULL &&
120                 gif_anim->frames->next == NULL);
121 }
122
123 static GdkPixbuf*
124 gdk_pixbuf_gif_anim_get_static_image (GdkPixbufAnimation *animation)
125 {
126         GdkPixbufGifAnim *gif_anim;
127
128         gif_anim = GDK_PIXBUF_GIF_ANIM (animation);
129
130         if (gif_anim->frames == NULL)
131                 return NULL;
132         else
133                 return GDK_PIXBUF (((GdkPixbufFrame*)gif_anim->frames->data)->pixbuf);        
134 }
135
136 static void
137 gdk_pixbuf_gif_anim_get_size (GdkPixbufAnimation *anim,
138                               int                *width,
139                               int                *height)
140 {
141         GdkPixbufGifAnim *gif_anim;
142
143         gif_anim = GDK_PIXBUF_GIF_ANIM (anim);
144
145         if (width)
146                 *width = gif_anim->width;
147
148         if (height)
149                 *height = gif_anim->height;
150 }
151
152
153 static void
154 iter_clear (GdkPixbufGifAnimIter *iter)
155 {
156         iter->current_frame = NULL;
157 }
158
159 static void
160 iter_restart (GdkPixbufGifAnimIter *iter)
161 {
162         iter_clear (iter);
163   
164         iter->current_frame = iter->gif_anim->frames;
165 }
166
167 static GdkPixbufAnimationIter*
168 gdk_pixbuf_gif_anim_get_iter (GdkPixbufAnimation *anim,
169                               const GTimeVal     *start_time)
170 {
171         GdkPixbufGifAnimIter *iter;
172
173         iter = g_object_new (GDK_TYPE_PIXBUF_GIF_ANIM_ITER, NULL);
174
175         iter->gif_anim = GDK_PIXBUF_GIF_ANIM (anim);
176
177         g_object_ref (iter->gif_anim);
178         
179         iter_restart (iter);
180
181         iter->start_time = *start_time;
182         iter->current_time = *start_time;
183         
184         return GDK_PIXBUF_ANIMATION_ITER (iter);
185 }
186
187 \f
188
189 static void gdk_pixbuf_gif_anim_iter_class_init (GdkPixbufGifAnimIterClass *klass);
190 static void gdk_pixbuf_gif_anim_iter_finalize   (GObject                   *object);
191
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);
197
198 \f
199
200 static gpointer iter_parent_class;
201
202 GType
203 gdk_pixbuf_gif_anim_iter_get_type (void)
204 {
205         static GType object_type = 0;
206
207         if (!object_type) {
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),
216                         0,              /* n_preallocs */
217                         (GInstanceInitFunc) NULL,
218                 };
219                 
220                 object_type = g_type_register_static (GDK_TYPE_PIXBUF_ANIMATION_ITER,
221                                                       "GdkPixbufGifAnimIter",
222                                                       &object_info, 0);
223         }
224         
225         return object_type;
226 }
227
228 static void
229 gdk_pixbuf_gif_anim_iter_class_init (GdkPixbufGifAnimIterClass *klass)
230 {
231         GObjectClass *object_class = G_OBJECT_CLASS (klass);
232         GdkPixbufAnimationIterClass *anim_iter_class =
233                 GDK_PIXBUF_ANIMATION_ITER_CLASS (klass);
234         
235         iter_parent_class = g_type_class_peek_parent (klass);
236         
237         object_class->finalize = gdk_pixbuf_gif_anim_iter_finalize;
238
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;
243 }
244
245 static void
246 gdk_pixbuf_gif_anim_iter_finalize (GObject *object)
247 {
248         GdkPixbufGifAnimIter *iter = GDK_PIXBUF_GIF_ANIM_ITER (object);
249
250         iter_clear (iter);
251
252         g_object_unref (iter->gif_anim);
253         
254         G_OBJECT_CLASS (iter_parent_class)->finalize (object);
255 }
256
257 static gboolean
258 gdk_pixbuf_gif_anim_iter_advance (GdkPixbufAnimationIter *anim_iter,
259                                   const GTimeVal         *current_time)
260 {
261         GdkPixbufGifAnimIter *iter;
262         gint elapsed;
263         gint loop;
264         GList *tmp;
265         GList *old;
266         
267         iter = GDK_PIXBUF_GIF_ANIM_ITER (anim_iter);
268         
269         iter->current_time = *current_time;
270
271         /* We use milliseconds for all times */
272         elapsed =
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;
275
276         if (elapsed < 0) {
277                 /* Try to compensate; probably the system clock
278                  * was set backwards
279                  */
280                 iter->start_time = iter->current_time;
281                 elapsed = 0;
282         }
283
284         g_assert (iter->gif_anim->total_time > 0);
285         
286         /* See how many times we've already played the full animation,
287          * and subtract time for that.
288          */
289
290         loop = elapsed / iter->gif_anim->total_time;
291         elapsed = elapsed % iter->gif_anim->total_time;
292
293         iter->position = elapsed;
294
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;
298         else 
299                 tmp = NULL;
300         while (tmp != NULL) {
301                 GdkPixbufFrame *frame = tmp->data;
302                 
303                 if (iter->position >= frame->elapsed &&
304                     iter->position < (frame->elapsed + frame->delay_time))
305                         break;
306                 
307                 tmp = tmp->next;
308         }
309
310         old = iter->current_frame;
311         
312         iter->current_frame = tmp;
313
314         return iter->current_frame != old;
315 }
316
317 int
318 gdk_pixbuf_gif_anim_iter_get_delay_time (GdkPixbufAnimationIter *anim_iter)
319 {
320         GdkPixbufFrame *frame;
321         GdkPixbufGifAnimIter *iter;
322   
323         iter = GDK_PIXBUF_GIF_ANIM_ITER (anim_iter);
324
325         if (iter->current_frame) {
326                 frame = iter->current_frame->data;
327
328 #if 0
329                 g_print ("frame start: %d pos: %d frame len: %d frame remaining: %d\n",
330                          frame->elapsed,
331                          iter->position,
332                          frame->delay_time,
333                          frame->delay_time - (iter->position - frame->elapsed));
334 #endif
335                 
336                 return frame->delay_time - (iter->position - frame->elapsed);
337         } else {
338                 return -1; /* show last frame forever */
339         }
340 }
341
342 void
343 gdk_pixbuf_gif_anim_frame_composite (GdkPixbufGifAnim *gif_anim,
344                                      GdkPixbufFrame   *frame)
345 {  
346         GList *link;
347         GList *tmp;
348         
349         link = g_list_find (gif_anim->frames, frame);
350         
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
354                  * here.
355                  */
356
357                 /* Rewind to last composited frame. */
358                 tmp = link;
359                 while (tmp != NULL) {
360                         GdkPixbufFrame *f = tmp->data;
361                         
362                         if (f->need_recomposite) {
363                                 if (f->composited) {
364                                         g_object_unref (f->composited);
365                                         f->composited = NULL;
366                                 }
367                         }
368
369                         if (f->composited != NULL)
370                                 break;
371                         
372                         tmp = tmp->prev;
373                 }
374
375                 /* Go forward, compositing all frames up to the current frame */
376                 if (tmp == NULL)
377                         tmp = gif_anim->frames;
378                 
379                 while (tmp != NULL) {
380                         GdkPixbufFrame *f = tmp->data;
381
382                         if (f->need_recomposite) {
383                                 if (f->composited) {
384                                         g_object_unref (f->composited);
385                                         f->composited = NULL;
386                                 }
387                         }
388                         
389                         if (f->composited != NULL)
390                                 goto next;
391
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.
397                                  */
398                                 f->composited = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
399                                                                 TRUE,
400                                                                 8, gif_anim->width, gif_anim->height);
401
402                                 /* alpha gets dumped if f->composited has no alpha */
403                                 
404                                 gdk_pixbuf_fill (f->composited,
405                                                  (gif_anim->bg_red << 24) |
406                                                  (gif_anim->bg_green << 16) |
407                                                  (gif_anim->bg_blue << 8) |
408                                                  (f->bg_transparent ? 0 : 255));
409
410                                 gdk_pixbuf_composite (f->pixbuf,
411                                                       f->composited,
412                                                       f->x_offset,
413                                                       f->y_offset,
414                                                       gdk_pixbuf_get_width (f->pixbuf),
415                                                       gdk_pixbuf_get_height (f->pixbuf),
416                                                       f->x_offset, f->y_offset,
417                                                       1.0, 1.0,
418                                                       GDK_INTERP_BILINEAR,
419                                                       255);
420 #if 0                                
421                                 gdk_pixbuf_copy_area (f->pixbuf,
422                                                       0, 0,
423                                                       gdk_pixbuf_get_width (f->pixbuf),
424                                                       gdk_pixbuf_get_height (f->pixbuf),
425                                                       f->composited,
426                                                       f->x_offset,
427                                                       f->y_offset);
428                                 
429 #endif
430                                 
431                                 if (f->action == GDK_PIXBUF_FRAME_REVERT)
432                                         g_warning ("First frame of GIF has bad dispose mode, GIF loader should not have loaded this image");
433
434                                 f->need_recomposite = FALSE;
435                         } else {
436                                 GdkPixbufFrame *prev_frame;
437                                 
438                                 prev_frame = tmp->prev->data;
439
440                                 /* Init f->composited with what we should have after the previous
441                                  * frame
442                                  */
443                                 
444                                 if (prev_frame->action == GDK_PIXBUF_FRAME_RETAIN) {
445                                         f->composited = gdk_pixbuf_copy (prev_frame->composited);
446                                         
447                                 } else if (prev_frame->action == GDK_PIXBUF_FRAME_DISPOSE) {
448                                         GdkPixbuf *area;
449                                         
450                                         f->composited = gdk_pixbuf_copy (prev_frame->composited);
451
452                                         /* Clear area of previous frame to background */
453                                         area = gdk_pixbuf_new_subpixbuf (f->composited,
454                                                                          prev_frame->x_offset,
455                                                                          prev_frame->y_offset,
456                                                                          gdk_pixbuf_get_width (prev_frame->pixbuf),
457                                                                          gdk_pixbuf_get_height (prev_frame->pixbuf));
458
459                                         gdk_pixbuf_fill (area,
460                                                          (gif_anim->bg_red << 24) |
461                                                          (gif_anim->bg_green << 16) |
462                                                          (gif_anim->bg_blue << 8) |
463                                                          prev_frame->bg_transparent ? 0 : 255);
464
465                                         g_object_unref (area);
466                                         
467                                 } else if (prev_frame->action == GDK_PIXBUF_FRAME_REVERT) {
468                                         f->composited = gdk_pixbuf_copy (prev_frame->composited);
469
470                                         /* Copy in the revert frame */
471                                         gdk_pixbuf_copy_area (prev_frame->revert,
472                                                               0, 0,
473                                                               gdk_pixbuf_get_width (prev_frame->revert),
474                                                               gdk_pixbuf_get_height (prev_frame->revert),
475                                                               f->composited,
476                                                               prev_frame->x_offset,
477                                                               prev_frame->y_offset);
478                                 } else {
479                                         g_warning ("Unknown revert action for GIF frame");
480                                 }
481
482                                 if (f->revert == NULL &&
483                                     f->action == GDK_PIXBUF_FRAME_REVERT) {
484                                         /* We need to save the contents before compositing */
485                                         GdkPixbuf *area;
486
487                                         area = gdk_pixbuf_new_subpixbuf (f->composited,
488                                                                          f->x_offset,
489                                                                          f->y_offset,
490                                                                          gdk_pixbuf_get_width (f->pixbuf),
491                                                                          gdk_pixbuf_get_height (f->pixbuf));
492
493                                         f->revert = gdk_pixbuf_copy (area);
494                                         
495                                         g_object_unref (area);
496                                 }
497
498                                 /* Put current frame onto f->composited */
499                                 gdk_pixbuf_composite (f->pixbuf,
500                                                       f->composited,
501                                                       f->x_offset,
502                                                       f->y_offset,
503                                                       gdk_pixbuf_get_width (f->pixbuf),
504                                                       gdk_pixbuf_get_height (f->pixbuf),
505                                                       f->x_offset, f->y_offset,
506                                                       1.0, 1.0,
507                                                       GDK_INTERP_NEAREST,
508                                                       255);
509                         
510                                 f->need_recomposite = FALSE;
511                         }
512                         
513                 next:
514                         if (tmp == link)
515                                 break;
516                         
517                         tmp = tmp->next;
518                 }
519         }
520
521         g_assert (frame->composited != NULL);
522         g_assert (gdk_pixbuf_get_width (frame->composited) == gif_anim->width);
523         g_assert (gdk_pixbuf_get_height (frame->composited) == gif_anim->height);
524 }
525
526 GdkPixbuf*
527 gdk_pixbuf_gif_anim_iter_get_pixbuf (GdkPixbufAnimationIter *anim_iter)
528 {
529         GdkPixbufGifAnimIter *iter;
530         GdkPixbufFrame *frame;
531         
532         iter = GDK_PIXBUF_GIF_ANIM_ITER (anim_iter);
533
534         frame = iter->current_frame ? iter->current_frame->data : g_list_last (iter->gif_anim->frames)->data;
535
536 #if 0
537         if (FALSE && frame)
538           g_print ("current frame %d dispose mode %d  %d x %d\n",
539                    g_list_index (iter->gif_anim->frames,
540                                  frame),
541                    frame->action,
542                    gdk_pixbuf_get_width (frame->pixbuf),
543                    gdk_pixbuf_get_height (frame->pixbuf));
544 #endif
545         
546         if (frame == NULL)
547                 return NULL;
548
549         gdk_pixbuf_gif_anim_frame_composite (iter->gif_anim, frame);
550         
551         return frame->composited;
552 }
553
554 static gboolean
555 gdk_pixbuf_gif_anim_iter_on_currently_loading_frame (GdkPixbufAnimationIter *anim_iter)
556 {
557         GdkPixbufGifAnimIter *iter;
558   
559         iter = GDK_PIXBUF_GIF_ANIM_ITER (anim_iter);
560
561         return iter->current_frame == NULL || iter->current_frame->next == NULL;  
562 }