]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/gdk-pixbuf-simple-anim.c
[quartz] Delete the typedef of GdkDevicePrivate
[~andy/gtk] / gdk-pixbuf / gdk-pixbuf-simple-anim.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2 /* GdkPixbuf library - Simple frame-based animations
3  *
4  * Copyright (C) Dom Lachowicz
5  *
6  * Authors: Dom Lachowicz <cinamod@hotmail.com>
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  * Based on code originally by:
24  *          Jonathan Blandford <jrb@redhat.com>
25  *          Havoc Pennington <hp@redhat.com>
26  */
27
28 #include "config.h"
29 #include <glib.h>
30
31 #define GDK_PIXBUF_C_COMPILATION
32 #include "gdk-pixbuf.h"
33 #include "gdk-pixbuf-private.h"
34 #include "gdk-pixbuf-io.h"
35 #include "gdk-pixbuf-simple-anim.h"
36 #include "gdk-pixbuf-alias.h"
37
38 struct _GdkPixbufSimpleAnimClass
39 {
40         GdkPixbufAnimationClass parent_class;
41 };
42
43 /* Private part of the GdkPixbufSimpleAnim structure */
44 struct _GdkPixbufSimpleAnim
45 {
46         GdkPixbufAnimation parent_instance;
47         
48         gint n_frames;
49         
50         gfloat rate;
51         gint total_time;
52         
53         GList *frames;
54         
55         gint width;
56         gint height;
57         
58         gboolean loop;
59 };
60
61
62 typedef struct _GdkPixbufSimpleAnimIter GdkPixbufSimpleAnimIter;
63 typedef struct _GdkPixbufSimpleAnimIterClass GdkPixbufSimpleAnimIterClass;
64
65 #define GDK_TYPE_PIXBUF_SIMPLE_ANIM_ITER              (gdk_pixbuf_simple_anim_iter_get_type ())
66 #define GDK_PIXBUF_SIMPLE_ANIM_ITER(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_PIXBUF_SIMPLE_ANIM_ITER, GdkPixbufSimpleAnimIter))
67 #define GDK_IS_PIXBUF_SIMPLE_ANIM_ITER(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_PIXBUF_SIMPLE_ANIM_ITER))
68
69 #define GDK_PIXBUF_SIMPLE_ANIM_ITER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIXBUF_SIMPLE_ANIM_ITER, GdkPixbufSimpleAnimIterClass))
70 #define GDK_IS_PIXBUF_SIMPLE_ANIM_ITER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIXBUF_SIMPLE_ANIM_ITER))
71 #define GDK_PIXBUF_SIMPLE_ANIM_ITER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIXBUF_SIMPLE_ANIM_ITER, GdkPixbufSimpleAnimIterClass))
72
73 GType gdk_pixbuf_simple_anim_iter_get_type (void) G_GNUC_CONST;
74
75
76 struct _GdkPixbufSimpleAnimIterClass
77 {
78         GdkPixbufAnimationIterClass parent_class;
79 };
80
81 struct _GdkPixbufSimpleAnimIter
82 {
83         GdkPixbufAnimationIter parent_instance;
84         
85         GdkPixbufSimpleAnim *simple_anim;
86         
87         GTimeVal start_time;
88         GTimeVal current_time;
89         
90         gint position;
91         
92         GList *current_frame;
93 };
94
95 typedef struct _GdkPixbufFrame GdkPixbufFrame;
96 struct _GdkPixbufFrame
97 {
98         GdkPixbuf *pixbuf;
99         gint delay_time;
100         gint elapsed;
101 };
102
103 static void gdk_pixbuf_simple_anim_finalize (GObject *object);
104
105 static gboolean   is_static_image  (GdkPixbufAnimation *animation);
106 static GdkPixbuf *get_static_image (GdkPixbufAnimation *animation);
107
108 static void       get_size         (GdkPixbufAnimation *anim,
109                                     gint               *width, 
110                                     gint               *height);
111 static GdkPixbufAnimationIter *get_iter (GdkPixbufAnimation *anim,
112                                          const GTimeVal     *start_time);
113
114
115 static void gdk_pixbuf_simple_anim_set_property (GObject        *object,
116                                                  guint           prop_id,
117                                                  const GValue   *value,
118                                                  GParamSpec     *pspec);
119 static void gdk_pixbuf_simple_anim_get_property (GObject        *object,
120                                                  guint           prop_id,
121                                                  GValue         *value,
122                                                  GParamSpec     *pspec);
123
124 enum
125 {
126         PROP_0,
127         PROP_LOOP
128 };
129
130 G_DEFINE_TYPE (GdkPixbufSimpleAnim, gdk_pixbuf_simple_anim, GDK_TYPE_PIXBUF_ANIMATION)
131
132 static void
133 gdk_pixbuf_simple_anim_init (GdkPixbufSimpleAnim *anim)
134 {
135 }
136
137 static void
138 gdk_pixbuf_simple_anim_class_init (GdkPixbufSimpleAnimClass *klass)
139 {
140         GObjectClass *object_class;
141         GdkPixbufAnimationClass *anim_class;
142
143         object_class = G_OBJECT_CLASS (klass);
144         anim_class = GDK_PIXBUF_ANIMATION_CLASS (klass);
145
146         object_class->set_property = gdk_pixbuf_simple_anim_set_property;
147         object_class->get_property = gdk_pixbuf_simple_anim_get_property;
148         object_class->finalize = gdk_pixbuf_simple_anim_finalize;
149         
150         anim_class->is_static_image = is_static_image;
151         anim_class->get_static_image = get_static_image;
152         anim_class->get_size = get_size;
153         anim_class->get_iter = get_iter;
154
155         /**
156          * GdkPixbufSimpleAnim:loop:
157          *
158          * Whether the animation should loop when it reaches the end.
159          *
160          * Since: 2.18
161          */
162         g_object_class_install_property (object_class,
163                                          PROP_LOOP,
164                                          g_param_spec_boolean ("loop",
165                                                                P_("Loop"),
166                                                                P_("Whether the animation should loop when it reaches the end"),
167                                                                FALSE,
168                                                                G_PARAM_READWRITE));
169 }
170
171 static void
172 gdk_pixbuf_simple_anim_finalize (GObject *object)
173 {
174         GdkPixbufSimpleAnim *anim;
175         GList *l;
176         GdkPixbufFrame *frame;
177         
178         anim = GDK_PIXBUF_SIMPLE_ANIM (object);        
179         
180         for (l = anim->frames; l; l = l->next) {
181                 frame = l->data;
182                 g_object_unref (frame->pixbuf);
183                 g_free (frame);
184         }
185         
186         g_list_free (anim->frames);
187         
188         G_OBJECT_CLASS (gdk_pixbuf_simple_anim_parent_class)->finalize (object);
189 }
190
191 static gboolean
192 is_static_image (GdkPixbufAnimation *animation)
193 {
194         GdkPixbufSimpleAnim *anim;
195         
196         anim = GDK_PIXBUF_SIMPLE_ANIM (animation);
197
198         return (anim->frames != NULL && anim->frames->next == NULL);
199 }
200
201 static GdkPixbuf *
202 get_static_image (GdkPixbufAnimation *animation)
203 {
204         GdkPixbufSimpleAnim *anim;
205         
206         anim = GDK_PIXBUF_SIMPLE_ANIM (animation);
207         
208         if (anim->frames == NULL)
209                 return NULL;
210         else
211                 return ((GdkPixbufFrame *)anim->frames->data)->pixbuf;
212 }
213
214 static void
215 get_size (GdkPixbufAnimation *animation,
216           gint               *width, 
217           gint               *height)
218 {
219         GdkPixbufSimpleAnim *anim;
220
221         anim = GDK_PIXBUF_SIMPLE_ANIM (animation);
222         
223         if (width)
224                 *width = anim->width;
225         
226         if (height)
227                 *height = anim->height;
228 }
229
230 static void
231 iter_clear (GdkPixbufSimpleAnimIter *iter)
232 {
233         iter->current_frame = NULL;
234 }
235
236 static void
237 iter_restart (GdkPixbufSimpleAnimIter *iter)
238 {
239         iter_clear (iter);
240         
241         iter->current_frame = iter->simple_anim->frames;
242 }
243
244 static GdkPixbufAnimationIter *
245 get_iter (GdkPixbufAnimation *anim,
246           const GTimeVal    *start_time)
247 {
248         GdkPixbufSimpleAnimIter *iter;
249         
250         iter = g_object_new (GDK_TYPE_PIXBUF_SIMPLE_ANIM_ITER, NULL);
251
252         iter->simple_anim = GDK_PIXBUF_SIMPLE_ANIM (anim);
253
254         g_object_ref (iter->simple_anim);
255         
256         iter_restart (iter);
257         
258         iter->start_time = *start_time;
259         iter->current_time = *start_time;
260         
261         return GDK_PIXBUF_ANIMATION_ITER (iter);
262 }
263
264 static void gdk_pixbuf_simple_anim_iter_finalize (GObject *object);
265
266 static gint       get_delay_time             (GdkPixbufAnimationIter *iter);
267 static GdkPixbuf *get_pixbuf                 (GdkPixbufAnimationIter *iter);
268 static gboolean   on_currently_loading_frame (GdkPixbufAnimationIter *iter);
269 static gboolean   advance                    (GdkPixbufAnimationIter *iter,
270                                               const GTimeVal         *current_time);
271
272 G_DEFINE_TYPE (GdkPixbufSimpleAnimIter, gdk_pixbuf_simple_anim_iter, GDK_TYPE_PIXBUF_ANIMATION_ITER)
273
274 static void
275 gdk_pixbuf_simple_anim_iter_init (GdkPixbufSimpleAnimIter *iter)
276 {
277 }
278
279 static void
280 gdk_pixbuf_simple_anim_iter_class_init (GdkPixbufSimpleAnimIterClass *klass)
281 {
282         GObjectClass *object_class;
283         GdkPixbufAnimationIterClass *anim_iter_class;
284
285         object_class = G_OBJECT_CLASS (klass);
286         anim_iter_class = GDK_PIXBUF_ANIMATION_ITER_CLASS (klass);
287         
288         object_class->finalize = gdk_pixbuf_simple_anim_iter_finalize;
289         
290         anim_iter_class->get_delay_time = get_delay_time;
291         anim_iter_class->get_pixbuf = get_pixbuf;
292         anim_iter_class->on_currently_loading_frame = on_currently_loading_frame;
293         anim_iter_class->advance = advance;
294 }
295
296 static void
297 gdk_pixbuf_simple_anim_iter_finalize (GObject *object)
298 {
299         GdkPixbufSimpleAnimIter *iter;
300         
301         iter = GDK_PIXBUF_SIMPLE_ANIM_ITER (object);
302         iter_clear (iter);
303         
304         g_object_unref (iter->simple_anim);
305         
306         G_OBJECT_CLASS (gdk_pixbuf_simple_anim_iter_parent_class)->finalize (object);
307 }
308
309 static gboolean
310 advance (GdkPixbufAnimationIter *anim_iter,
311          const GTimeVal         *current_time)
312 {
313         GdkPixbufSimpleAnimIter *iter;
314         gint elapsed;
315         gint loop_count;
316         GList *tmp;
317         GList *old;
318         
319         iter = GDK_PIXBUF_SIMPLE_ANIM_ITER (anim_iter);
320         
321         iter->current_time = *current_time;
322         
323         /* We use milliseconds for all times */
324         elapsed = (((iter->current_time.tv_sec - iter->start_time.tv_sec) * G_USEC_PER_SEC +
325                     iter->current_time.tv_usec - iter->start_time.tv_usec)) / 1000;
326         
327         if (elapsed < 0) {
328                 /* Try to compensate; probably the system clock
329                  * was set backwards
330                  */
331                 iter->start_time = iter->current_time;
332                 elapsed = 0;
333         }
334         
335         g_assert (iter->simple_anim->total_time > 0);
336         
337         /* See how many times we've already played the full animation,
338          * and subtract time for that.
339          */
340         loop_count = elapsed / iter->simple_anim->total_time;
341         elapsed = elapsed % iter->simple_anim->total_time;
342         
343         iter->position = elapsed;
344         
345         /* Now move to the proper frame */
346         if (loop_count < 1 || iter->simple_anim->loop)
347                 tmp = iter->simple_anim->frames;
348         else
349                 tmp = NULL;
350         
351         while (tmp != NULL) {
352                 GdkPixbufFrame *frame = tmp->data;
353                 
354                 if (iter->position >= frame->elapsed &&
355                     iter->position < (frame->elapsed + frame->delay_time))
356                         break;
357                 
358                 tmp = tmp->next;
359         }
360         
361         old = iter->current_frame;
362         
363         iter->current_frame = tmp;
364         
365         return iter->current_frame != old;
366 }
367
368 static gint
369 get_delay_time (GdkPixbufAnimationIter *anim_iter)
370 {
371         GdkPixbufFrame *frame;
372         GdkPixbufSimpleAnimIter *iter;
373
374         iter = GDK_PIXBUF_SIMPLE_ANIM_ITER (anim_iter);
375         
376         if (iter->current_frame) {
377                 frame = iter->current_frame->data;
378                 return frame->delay_time - (iter->position - frame->elapsed);
379         }
380         else {
381                 return -1;              /* show last frame forever */
382         }
383 }
384
385 static GdkPixbuf *
386 get_pixbuf (GdkPixbufAnimationIter *anim_iter)
387 {
388         GdkPixbufSimpleAnimIter *iter;
389         GdkPixbufFrame *frame;
390         
391         iter = GDK_PIXBUF_SIMPLE_ANIM_ITER (anim_iter);
392         
393         if (iter->current_frame)
394                 frame = iter->current_frame->data;
395         else if (g_list_length (iter->simple_anim->frames) > 0)
396                 frame = g_list_last (iter->simple_anim->frames)->data;
397         else
398                 frame = NULL;
399
400         if (frame == NULL)
401                 return NULL;
402         
403         return frame->pixbuf;
404 }
405
406 static gboolean
407 on_currently_loading_frame (GdkPixbufAnimationIter *anim_iter)
408 {
409   GdkPixbufSimpleAnimIter *iter;
410
411   iter = GDK_PIXBUF_SIMPLE_ANIM_ITER (anim_iter);
412
413   return iter->current_frame == NULL || iter->current_frame->next == NULL;
414 }
415
416 /**
417  * gdk_pixbuf_simple_anim_new:
418  * @width: the width of the animation
419  * @height: the height of the animation
420  * @rate: the speed of the animation, in frames per second
421  *
422  * Creates a new, empty animation.
423  *
424  * Returns: a newly allocated #GdkPixbufSimpleAnim 
425  *
426  * Since: 2.8
427  */
428 GdkPixbufSimpleAnim *
429 gdk_pixbuf_simple_anim_new (gint   width, 
430                             gint   height, 
431                             gfloat rate)
432 {
433   GdkPixbufSimpleAnim *anim;
434
435   anim = g_object_new (GDK_TYPE_PIXBUF_SIMPLE_ANIM, NULL);
436   anim->width = width;
437   anim->height = height;
438   anim->rate = rate;
439
440   return anim;
441 }
442
443 /**
444  * gdk_pixbuf_simple_anim_add_frame:
445  * @animation: a #GdkPixbufSimpleAnim
446  * @pixbuf: the pixbuf to add 
447  *
448  * Adds a new frame to @animation. The @pixbuf must
449  * have the dimensions specified when the animation 
450  * was constructed.
451  *
452  * Since: 2.8
453  */
454 void
455 gdk_pixbuf_simple_anim_add_frame (GdkPixbufSimpleAnim *animation,
456                                   GdkPixbuf           *pixbuf)
457 {
458   GdkPixbufFrame *frame;
459   int nframe = 0;
460   
461   g_return_if_fail (GDK_IS_PIXBUF_SIMPLE_ANIM (animation));
462   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
463   
464   nframe = g_list_length (animation->frames);
465   
466   frame = g_new0 (GdkPixbufFrame, 1);
467   frame->delay_time = (gint) (1000 / animation->rate);
468   frame->elapsed = (gint) (frame->delay_time * nframe);
469   animation->total_time += frame->delay_time;
470   frame->pixbuf = g_object_ref (pixbuf);
471
472   animation->frames = g_list_append (animation->frames, frame);
473 }
474
475 static void
476 gdk_pixbuf_simple_anim_get_property (GObject         *object,
477                                      guint            prop_id,
478                                      GValue          *value,
479                                      GParamSpec      *pspec)
480 {
481         GdkPixbufSimpleAnim *animation = GDK_PIXBUF_SIMPLE_ANIM (object);
482
483         switch (prop_id) {
484         case PROP_LOOP:
485                 g_value_set_boolean (value,
486                                      gdk_pixbuf_simple_anim_get_loop (animation));
487                 break;
488         default:
489                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
490                 break;
491         }
492 }
493
494 static void
495 gdk_pixbuf_simple_anim_set_property (GObject         *object,
496                                      guint            prop_id,
497                                      const GValue    *value,
498                                      GParamSpec      *pspec)
499 {
500         GdkPixbufSimpleAnim *animation = GDK_PIXBUF_SIMPLE_ANIM (object);
501
502         switch (prop_id) {
503         case PROP_LOOP:
504                 gdk_pixbuf_simple_anim_set_loop (animation,
505                                                  g_value_get_boolean (value));
506                 break;
507         default:
508                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
509                 break;
510         }
511 }
512
513 /**
514  * gdk_pixbuf_simple_anim_set_loop:
515  * @animation: a #GdkPixbufSimpleAnim
516  * @loop: whether to loop the animation
517  *
518  * Sets whether @animation should loop indefinitely when it reaches the end.
519  *
520  * Since: 2.18
521  **/
522 void
523 gdk_pixbuf_simple_anim_set_loop (GdkPixbufSimpleAnim *animation,
524                                  gboolean             loop)
525 {
526         g_return_if_fail (GDK_IS_PIXBUF_SIMPLE_ANIM (animation));
527
528         if (loop != animation->loop) {
529                 animation->loop = loop;
530                 g_object_notify (G_OBJECT (animation), "loop");
531         }
532 }
533
534 /**
535  * gdk_pixbuf_simple_anim_get_loop:
536  * @animation: a #GdkPixbufSimpleAnim
537  *
538  * Gets whether @animation should loop indefinitely when it reaches the end.
539  *
540  * Returns: %TRUE if the animation loops forever, %FALSE otherwise
541  *
542  * Since: 2.18
543  **/
544 gboolean
545 gdk_pixbuf_simple_anim_get_loop (GdkPixbufSimpleAnim *animation)
546 {
547         g_return_val_if_fail (GDK_IS_PIXBUF_SIMPLE_ANIM (animation), FALSE);
548
549         return animation->loop;
550 }
551
552 #define __GDK_PIXBUF_SIMPLE_ANIM_C__
553 #include "gdk-pixbuf-aliasdef.c"