1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2 /* GdkPixbuf library - Simple animation 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, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include "config.h"
24 #define GLIB_DISABLE_DEPRECATION_WARNINGS
25 #include <errno.h>
26 #include "gdk-pixbuf-private.h"
27 #include "gdk-pixbuf-animation.h"
28 #include "gdk-pixbuf-loader.h"
29 
30 #include <glib/gstdio.h>
31 
32 /**
33  * SECTION:animation
34  * @Short_description: Animated images.
35  * @Title: Animations
36  * @See_also: #GdkPixbufLoader.
37  *
38  * The GdkPixBuf library provides a simple mechanism to load and
39  * represent animations. An animation is conceptually a series of
40  * frames to be displayed over time. The animation may not be
41  * represented as a series of frames internally; for example, it may
42  * be stored as a sprite and instructions for moving the sprite around
43  * a background. To display an animation you don't need to understand
44  * its representation, however; you just ask GdkPixBuf what should
45  * be displayed at a given point in time.
46  */
47 
48 typedef struct _GdkPixbufNonAnim GdkPixbufNonAnim;
49 typedef struct _GdkPixbufNonAnimClass GdkPixbufNonAnimClass;
50 
51 #define GDK_TYPE_PIXBUF_NON_ANIM              (gdk_pixbuf_non_anim_get_type ())
52 #define GDK_PIXBUF_NON_ANIM(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_PIXBUF_NON_ANIM, GdkPixbufNonAnim))
53 #define GDK_IS_PIXBUF_NON_ANIM(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_PIXBUF_NON_ANIM))
54 
55 #define GDK_PIXBUF_NON_ANIM_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIXBUF_NON_ANIM, GdkPixbufNonAnimClass))
56 #define GDK_IS_PIXBUF_NON_ANIM_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIXBUF_NON_ANIM))
57 #define GDK_PIXBUF_NON_ANIM_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIXBUF_NON_ANIM, GdkPixbufNonAnimClass))
58 
59 /* Private part of the GdkPixbufNonAnim structure */
60 struct _GdkPixbufNonAnim {
61         GdkPixbufAnimation parent_instance;
62 
63         GdkPixbuf *pixbuf;
64 };
65 
66 struct _GdkPixbufNonAnimClass {
67         GdkPixbufAnimationClass parent_class;
68 };
69 
70 
71 typedef struct _GdkPixbufNonAnimIter GdkPixbufNonAnimIter;
72 typedef struct _GdkPixbufNonAnimIterClass GdkPixbufNonAnimIterClass;
73 
74 
75 #define GDK_TYPE_PIXBUF_NON_ANIM_ITER              (gdk_pixbuf_non_anim_iter_get_type ())
76 #define GDK_PIXBUF_NON_ANIM_ITER(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_PIXBUF_NON_ANIM_ITER, GdkPixbufNonAnimIter))
77 #define GDK_IS_PIXBUF_NON_ANIM_ITER(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_PIXBUF_NON_ANIM_ITER))
78 
79 #define GDK_PIXBUF_NON_ANIM_ITER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIXBUF_NON_ANIM_ITER, GdkPixbufNonAnimIterClass))
80 #define GDK_IS_PIXBUF_NON_ANIM_ITER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIXBUF_NON_ANIM_ITER))
81 #define GDK_PIXBUF_NON_ANIM_ITER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIXBUF_NON_ANIM_ITER, GdkPixbufNonAnimIterClass))
82 
83 struct _GdkPixbufNonAnimIter {
84         GdkPixbufAnimationIter parent_instance;
85 
86         GdkPixbufNonAnim   *non_anim;
87 };
88 
89 struct _GdkPixbufNonAnimIterClass {
90         GdkPixbufAnimationIterClass parent_class;
91 
92 };
93 
94 static GType gdk_pixbuf_non_anim_iter_get_type (void) G_GNUC_CONST;
95 
96 G_DEFINE_TYPE (GdkPixbufAnimation, gdk_pixbuf_animation, G_TYPE_OBJECT);
97 
98 static void
gdk_pixbuf_animation_class_init(GdkPixbufAnimationClass * klass)99 gdk_pixbuf_animation_class_init (GdkPixbufAnimationClass *klass)
100 {
101 }
102 
103 static void
gdk_pixbuf_animation_init(GdkPixbufAnimation * animation)104 gdk_pixbuf_animation_init (GdkPixbufAnimation *animation)
105 {
106 }
107 
108 static void
prepared_notify(GdkPixbuf * pixbuf,GdkPixbufAnimation * anim,gpointer user_data)109 prepared_notify (GdkPixbuf          *pixbuf,
110                  GdkPixbufAnimation *anim,
111                  gpointer            user_data)
112 {
113         if (anim != NULL)
114                 g_object_ref (anim);
115         else
116                 anim = gdk_pixbuf_non_anim_new (pixbuf);
117 
118         *((GdkPixbufAnimation **)user_data) = anim;
119 }
120 
121 /**
122  * gdk_pixbuf_animation_new_from_file:
123  * @filename: (type filename): Name of file to load, in the GLib file
124  *     name encoding
125  * @error: return location for error
126  *
127  * Creates a new animation by loading it from a file. The file format is
128  * detected automatically. If the file's format does not support multi-frame
129  * images, then an animation with a single frame will be created. Possible errors
130  * are in the #GDK_PIXBUF_ERROR and #G_FILE_ERROR domains.
131  *
132  * Return value: A newly-created animation with a reference count of 1, or %NULL
133  * if any of several error conditions ocurred:  the file could not be opened,
134  * there was no loader for the file's format, there was not enough memory to
135  * allocate the image buffer, or the image file contained invalid data.
136  */
137 GdkPixbufAnimation *
gdk_pixbuf_animation_new_from_file(const gchar * filename,GError ** error)138 gdk_pixbuf_animation_new_from_file (const gchar  *filename,
139                                     GError      **error)
140 {
141 	GdkPixbufAnimation *animation;
142 	int size;
143 	FILE *f;
144 	guchar buffer[SNIFF_BUFFER_SIZE];
145 	GdkPixbufModule *image_module;
146         gchar *display_name;
147 
148 	g_return_val_if_fail (filename != NULL, NULL);
149         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
150 
151         display_name = g_filename_display_name (filename);
152 	f = g_fopen (filename, "rb");
153 	if (!f) {
154                 gint save_errno = errno;
155                 g_set_error (error,
156                              G_FILE_ERROR,
157                              g_file_error_from_errno (save_errno),
158                              _("Failed to open file “%s”: %s"),
159                              display_name,
160                              g_strerror (save_errno));
161                 g_free (display_name);
162 		return NULL;
163         }
164 
165 	size = fread (&buffer, 1, sizeof (buffer), f);
166 
167 	if (size == 0) {
168                 g_set_error (error,
169                              GDK_PIXBUF_ERROR,
170                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
171                              _("Image file “%s” contains no data"),
172                              display_name);
173                 g_free (display_name);
174 		fclose (f);
175 		return NULL;
176 	}
177 
178 	image_module = _gdk_pixbuf_get_module (buffer, size, filename, error);
179 	if (!image_module) {
180                 g_free (display_name);
181 		fclose (f);
182 		return NULL;
183 	}
184 
185 	if (image_module->module == NULL)
186                 if (!_gdk_pixbuf_load_module (image_module, error)) {
187                         g_free (display_name);
188                         fclose (f);
189                         return NULL;
190                 }
191 
192 	if (image_module->load_animation != NULL) {
193 		fseek (f, 0, SEEK_SET);
194 		animation = (* image_module->load_animation) (f, error);
195 
196                 if (animation == NULL && error != NULL && *error == NULL) {
197                         /* I don't trust these crufty longjmp()'ing
198                          * image libs to maintain proper error
199                          * invariants, and I don't want user code to
200                          * segfault as a result. We need to maintain
201                          * the invariant that error gets set if NULL
202                          * is returned.
203                          */
204                         g_warning ("Bug! gdk-pixbuf loader '%s' didn't set an error on failure.",
205                                    image_module->module_name);
206                         g_set_error (error,
207                                      GDK_PIXBUF_ERROR,
208                                      GDK_PIXBUF_ERROR_FAILED,
209                                      _("Failed to load animation “%s”: reason not known, probably a corrupt animation file"),
210                                      display_name);
211                 }
212 
213 		fclose (f);
214         } else if (image_module->begin_load != NULL) {
215                 guchar buffer[4096];
216                 size_t length;
217                 gpointer context;
218                 gboolean success;
219 
220                 success = FALSE;
221                 animation = NULL;
222 		fseek (f, 0, SEEK_SET);
223 
224                 context = image_module->begin_load (NULL, prepared_notify, NULL, &animation, error);
225                 if (!context)
226                         goto fail_begin_load;
227 
228                 while (!feof (f) && !ferror (f)) {
229                         length = fread (buffer, 1, sizeof (buffer), f);
230                         if (length > 0) {
231                                 if (!image_module->load_increment (context, buffer, length, error)) {
232                                         error = NULL;
233                                         goto fail_load_increment;
234                                 }
235                         }
236                 }
237 
238                 success = TRUE;
239 
240 fail_load_increment:
241                 if (!image_module->stop_load (context, error))
242                         success = FALSE;
243 
244 fail_begin_load:
245 		fclose (f);
246 
247                 if (success) {
248                         /* If there was no error, there must be an animation that was successfully loaded */
249                         g_assert (animation);
250                 } else {
251                         if (animation) {
252                                 g_object_unref (animation);
253                                 animation = NULL;
254                         }
255                 }
256 	} else {
257 		GdkPixbuf *pixbuf;
258 
259 		/* Keep this logic in sync with gdk_pixbuf_new_from_file() */
260 
261 		fseek (f, 0, SEEK_SET);
262 		pixbuf = _gdk_pixbuf_generic_image_load (image_module, f, error);
263 		fclose (f);
264 
265                 if (pixbuf == NULL && error != NULL && *error == NULL) {
266                         /* I don't trust these crufty longjmp()'ing image libs
267                          * to maintain proper error invariants, and I don't
268                          * want user code to segfault as a result. We need to maintain
269                          * the invariant that error gets set if NULL is returned.
270                          */
271 
272                         g_warning ("Bug! gdk-pixbuf loader '%s' didn't set an error on failure.",
273                                    image_module->module_name);
274                         g_set_error (error,
275                                      GDK_PIXBUF_ERROR,
276                                      GDK_PIXBUF_ERROR_FAILED,
277                                      _("Failed to load image “%s”: reason not known, probably a corrupt image file"),
278                                      display_name);
279                 }
280 
281 		if (pixbuf == NULL) {
282                         g_free (display_name);
283                         animation = NULL;
284                         goto out;
285                 }
286 
287                 animation = gdk_pixbuf_non_anim_new (pixbuf);
288 
289                 g_object_unref (pixbuf);
290 	}
291 
292         g_free (display_name);
293 
294  out:
295 	return animation;
296 }
297 
298 #ifdef G_OS_WIN32
299 /**
300  * gdk_pixbuf_animation_new_from_file_utf8:
301  * @filename: (type filename): Name of file to load, in the GLib file name encoding
302  * @error: return location for error
303  *
304  * Same as gdk_pixbuf_animation_new_from_file()
305  *
306  * Return value: A newly-created animation with a reference count of 1, or %NULL
307  * if any of several error conditions ocurred:  the file could not be opened,
308  * there was no loader for the file's format, there was not enough memory to
309  * allocate the image buffer, or the image file contained invalid data.
310  */
311 GdkPixbufAnimation *
gdk_pixbuf_animation_new_from_file_utf8(const gchar * filename,GError ** error)312 gdk_pixbuf_animation_new_from_file_utf8 (const gchar  *filename,
313                                          GError      **error)
314 {
315     return gdk_pixbuf_animation_new_from_file (filename, error);
316 }
317 #endif
318 
319 /**
320  * gdk_pixbuf_animation_new_from_stream:
321  * @stream:  a #GInputStream to load the pixbuf from
322  * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore
323  * @error: Return location for an error
324  *
325  * Creates a new animation by loading it from an input stream.
326  *
327  * The file format is detected automatically. If %NULL is returned, then
328  * @error will be set. The @cancellable can be used to abort the operation
329  * from another thread. If the operation was cancelled, the error
330  * %G_IO_ERROR_CANCELLED will be returned. Other possible errors are in
331  * the #GDK_PIXBUF_ERROR and %G_IO_ERROR domains.
332  *
333  * The stream is not closed.
334  *
335  * Return value: A newly-created pixbuf, or %NULL if any of several error
336  * conditions occurred: the file could not be opened, the image format is
337  * not supported, there was not enough memory to allocate the image buffer,
338  * the stream contained invalid data, or the operation was cancelled.
339  *
340  * Since: 2.28
341  */
342 GdkPixbufAnimation *
gdk_pixbuf_animation_new_from_stream(GInputStream * stream,GCancellable * cancellable,GError ** error)343 gdk_pixbuf_animation_new_from_stream (GInputStream  *stream,
344                                       GCancellable  *cancellable,
345                                       GError       **error)
346 {
347         GdkPixbufAnimation *animation;
348         GdkPixbufLoader *loader;
349         gssize n_read;
350         guchar buffer[LOAD_BUFFER_SIZE];
351         gboolean res;
352 
353         g_return_val_if_fail (G_IS_INPUT_STREAM (stream), NULL);
354         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
355         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
356 
357         loader = gdk_pixbuf_loader_new ();
358 
359         res = TRUE;
360         while (1) {
361                 n_read = g_input_stream_read (stream, buffer, sizeof (buffer), cancellable, error);
362                 if (n_read < 0) {
363                         res = FALSE;
364                         error = NULL; /* Ignore further errors */
365                         break;
366                 }
367 
368                 if (n_read == 0)
369                         break;
370 
371                 if (!gdk_pixbuf_loader_write (loader, buffer, n_read, error)) {
372                         res = FALSE;
373                         error = NULL;
374                         break;
375                 }
376         }
377 
378         if (!gdk_pixbuf_loader_close (loader, error)) {
379                 res = FALSE;
380                 error = NULL;
381         }
382 
383         if (res) {
384                 animation = gdk_pixbuf_loader_get_animation (loader);
385                 if (animation)
386                         g_object_ref (animation);
387         } else {
388                 animation = NULL;
389         }
390 
391         g_object_unref (loader);
392 
393         return animation;
394 }
395 
396 static void
animation_new_from_stream_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)397 animation_new_from_stream_thread (GTask        *task,
398                                   gpointer      source_object,
399                                   gpointer      task_data,
400                                   GCancellable *cancellable)
401 {
402         GInputStream *stream = G_INPUT_STREAM (source_object);
403 	GdkPixbufAnimation *animation;
404 	GError *error = NULL;
405 
406 	animation = gdk_pixbuf_animation_new_from_stream (stream, cancellable, &error);
407 
408 	/* Set the new pixbuf as the result, or error out */
409 	if (animation == NULL) {
410                 g_task_return_error (task, error);
411 	} else {
412                 g_task_return_pointer (task, animation, g_object_unref);
413 	}
414 }
415 
416 /**
417  * gdk_pixbuf_animation_new_from_stream_async:
418  * @stream: a #GInputStream from which to load the animation
419  * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore
420  * @callback: a #GAsyncReadyCallback to call when the pixbuf is loaded
421  * @user_data: the data to pass to the callback function
422  *
423  * Creates a new animation by asynchronously loading an image from an input stream.
424  *
425  * For more details see gdk_pixbuf_new_from_stream(), which is the synchronous
426  * version of this function.
427  *
428  * When the operation is finished, @callback will be called in the main thread.
429  * You can then call gdk_pixbuf_animation_new_from_stream_finish() to get the
430  * result of the operation.
431  *
432  * Since: 2.28
433  **/
434 void
gdk_pixbuf_animation_new_from_stream_async(GInputStream * stream,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)435 gdk_pixbuf_animation_new_from_stream_async (GInputStream        *stream,
436                                             GCancellable        *cancellable,
437                                             GAsyncReadyCallback  callback,
438                                             gpointer             user_data)
439 {
440 	GTask *task;
441 
442 	g_return_if_fail (G_IS_INPUT_STREAM (stream));
443 	g_return_if_fail (callback != NULL);
444 	g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
445 
446 	task = g_task_new (G_OBJECT (stream), cancellable, callback, user_data);
447         g_task_set_source_tag (task, gdk_pixbuf_animation_new_from_stream_async);
448 	g_task_run_in_thread (task, animation_new_from_stream_thread);
449 	g_object_unref (task);
450 }
451 
452 /**
453  * gdk_pixbuf_animation_new_from_stream_finish:
454  * @async_result: a #GAsyncResult
455  * @error: a #GError, or %NULL
456  *
457  * Finishes an asynchronous pixbuf animation creation operation started with
458  * gdk_pixbuf_animation_new_from_stream_async().
459  *
460  * Return value: a #GdkPixbufAnimation or %NULL on error. Free the returned
461  * object with g_object_unref().
462  *
463  * Since: 2.28
464  **/
465 GdkPixbufAnimation *
gdk_pixbuf_animation_new_from_stream_finish(GAsyncResult * async_result,GError ** error)466 gdk_pixbuf_animation_new_from_stream_finish (GAsyncResult  *async_result,
467 			              	     GError       **error)
468 {
469         GTask *task = G_TASK (async_result);
470 
471 	g_return_val_if_fail (G_IS_TASK (async_result), NULL);
472 	g_return_val_if_fail (!error || (error && !*error), NULL);
473 	g_warn_if_fail (g_task_get_source_tag (task) == gdk_pixbuf_animation_new_from_stream_async);
474 
475 	return g_task_propagate_pointer (task, error);
476 }
477 
478 /**
479  * gdk_pixbuf_animation_new_from_resource:
480  * @resource_path: the path of the resource file
481  * @error: Return location for an error
482  *
483  * Creates a new pixbuf animation by loading an image from an resource.
484  *
485  * The file format is detected automatically. If %NULL is returned, then
486  * @error will be set.
487  *
488  * Return value: A newly-created animation, or %NULL if any of several error
489  * conditions occurred: the file could not be opened, the image format is
490  * not supported, there was not enough memory to allocate the image buffer,
491  * the stream contained invalid data, or the operation was cancelled.
492  *
493  * Since: 2.28
494  */
495 GdkPixbufAnimation *
gdk_pixbuf_animation_new_from_resource(const gchar * resource_path,GError ** error)496 gdk_pixbuf_animation_new_from_resource (const gchar  *resource_path,
497                                         GError      **error)
498 {
499 	GInputStream *stream;
500 	GdkPixbufAnimation *anim;
501 	GdkPixbuf *pixbuf;
502 
503         pixbuf = _gdk_pixbuf_new_from_resource_try_pixdata (resource_path);
504         if (pixbuf) {
505                 anim = gdk_pixbuf_non_anim_new (pixbuf);
506                 g_object_unref (pixbuf);
507                 return anim;
508         }
509 
510 	stream = g_resources_open_stream (resource_path, 0, error);
511 	if (stream == NULL)
512 		return NULL;
513 
514 	anim = gdk_pixbuf_animation_new_from_stream (stream, NULL, error);
515 	g_object_unref (stream);
516 	return anim;
517 }
518 
519 /**
520  * gdk_pixbuf_animation_ref: (skip)
521  * @animation: An animation.
522  *
523  * Adds a reference to an animation.
524  *
525  * Return value: The same as the @animation argument.
526  *
527  * Deprecated: 2.0: Use g_object_ref().
528  */
529 GdkPixbufAnimation *
gdk_pixbuf_animation_ref(GdkPixbufAnimation * animation)530 gdk_pixbuf_animation_ref (GdkPixbufAnimation *animation)
531 {
532         return (GdkPixbufAnimation*) g_object_ref (animation);
533 }
534 
535 /**
536  * gdk_pixbuf_animation_unref: (skip)
537  * @animation: An animation.
538  *
539  * Removes a reference from an animation.
540  *
541  * Deprecated: 2.0: Use g_object_unref().
542  */
543 void
gdk_pixbuf_animation_unref(GdkPixbufAnimation * animation)544 gdk_pixbuf_animation_unref (GdkPixbufAnimation *animation)
545 {
546         g_object_unref (animation);
547 }
548 
549 /**
550  * gdk_pixbuf_animation_is_static_image:
551  * @animation: a #GdkPixbufAnimation
552  *
553  * If you load a file with gdk_pixbuf_animation_new_from_file() and it
554  * turns out to be a plain, unanimated image, then this function will
555  * return %TRUE. Use gdk_pixbuf_animation_get_static_image() to retrieve
556  * the image.
557  *
558  * Return value: %TRUE if the "animation" was really just an image
559  */
560 gboolean
gdk_pixbuf_animation_is_static_image(GdkPixbufAnimation * animation)561 gdk_pixbuf_animation_is_static_image (GdkPixbufAnimation *animation)
562 {
563 	g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION (animation), FALSE);
564 
565         return GDK_PIXBUF_ANIMATION_GET_CLASS (animation)->is_static_image (animation);
566 }
567 
568 /**
569  * gdk_pixbuf_animation_get_static_image:
570  * @animation: a #GdkPixbufAnimation
571  *
572  * If an animation is really just a plain image (has only one frame),
573  * this function returns that image. If the animation is an animation,
574  * this function returns a reasonable thing to display as a static
575  * unanimated image, which might be the first frame, or something more
576  * sophisticated. If an animation hasn't loaded any frames yet, this
577  * function will return %NULL.
578  *
579  * Return value: (transfer none): unanimated image representing the animation
580  */
581 GdkPixbuf*
gdk_pixbuf_animation_get_static_image(GdkPixbufAnimation * animation)582 gdk_pixbuf_animation_get_static_image (GdkPixbufAnimation *animation)
583 {
584 	g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION (animation), NULL);
585 
586         return GDK_PIXBUF_ANIMATION_GET_CLASS (animation)->get_static_image (animation);
587 }
588 
589 /**
590  * gdk_pixbuf_animation_get_width:
591  * @animation: An animation.
592  *
593  * Queries the width of the bounding box of a pixbuf animation.
594  *
595  * Return value: Width of the bounding box of the animation.
596  */
597 gint
gdk_pixbuf_animation_get_width(GdkPixbufAnimation * animation)598 gdk_pixbuf_animation_get_width (GdkPixbufAnimation *animation)
599 {
600         gint width;
601 
602 	g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION (animation), 0);
603 
604         width = 0;
605         GDK_PIXBUF_ANIMATION_GET_CLASS (animation)->get_size (animation,
606                                                               &width, NULL);
607 
608 	return width;
609 }
610 
611 /**
612  * gdk_pixbuf_animation_get_height:
613  * @animation: An animation.
614  *
615  * Queries the height of the bounding box of a pixbuf animation.
616  *
617  * Return value: Height of the bounding box of the animation.
618  */
619 gint
gdk_pixbuf_animation_get_height(GdkPixbufAnimation * animation)620 gdk_pixbuf_animation_get_height (GdkPixbufAnimation *animation)
621 {
622         gint height;
623 
624 	g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION (animation), 0);
625 
626         height = 0;
627         GDK_PIXBUF_ANIMATION_GET_CLASS (animation)->get_size (animation,
628                                                               NULL, &height);
629 
630 	return height;
631 }
632 
633 
634 /**
635  * gdk_pixbuf_animation_get_iter:
636  * @animation: a #GdkPixbufAnimation
637  * @start_time: (allow-none): time when the animation starts playing
638  *
639  * Get an iterator for displaying an animation. The iterator provides
640  * the frames that should be displayed at a given time. It should be
641  * freed after use with g_object_unref().
642  *
643  * @start_time would normally come from g_get_current_time(), and marks
644  * the beginning of animation playback. After creating an iterator, you
645  * should immediately display the pixbuf returned by
646  * gdk_pixbuf_animation_iter_get_pixbuf(). Then, you should install
647  * a timeout (with g_timeout_add()) or by some other mechanism ensure
648  * that you'll update the image after
649  * gdk_pixbuf_animation_iter_get_delay_time() milliseconds. Each time
650  * the image is updated, you should reinstall the timeout with the new,
651  * possibly-changed delay time.
652  *
653  * As a shortcut, if @start_time is %NULL, the result of
654  * g_get_current_time() will be used automatically.
655  *
656  * To update the image (i.e. possibly change the result of
657  * gdk_pixbuf_animation_iter_get_pixbuf() to a new frame of the animation),
658  * call gdk_pixbuf_animation_iter_advance().
659  *
660  * If you're using #GdkPixbufLoader, in addition to updating the image
661  * after the delay time, you should also update it whenever you
662  * receive the area_updated signal and
663  * gdk_pixbuf_animation_iter_on_currently_loading_frame() returns
664  * %TRUE. In this case, the frame currently being fed into the loader
665  * has received new data, so needs to be refreshed. The delay time for
666  * a frame may also be modified after an area_updated signal, for
667  * example if the delay time for a frame is encoded in the data after
668  * the frame itself. So your timeout should be reinstalled after any
669  * area_updated signal.
670  *
671  * A delay time of -1 is possible, indicating "infinite."
672  *
673  * Return value: (transfer full): an iterator to move over the animation
674  */
675 GdkPixbufAnimationIter*
gdk_pixbuf_animation_get_iter(GdkPixbufAnimation * animation,const GTimeVal * start_time)676 gdk_pixbuf_animation_get_iter (GdkPixbufAnimation *animation,
677                                const GTimeVal     *start_time)
678 {
679         GTimeVal val;
680 
681         g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION (animation), NULL);
682 
683 
684         if (start_time)
685                 val = *start_time;
686         else
687                 g_get_current_time (&val);
688 
689         return GDK_PIXBUF_ANIMATION_GET_CLASS (animation)->get_iter (animation, &val);
690 }
691 
692 G_DEFINE_TYPE (GdkPixbufAnimationIter, gdk_pixbuf_animation_iter, G_TYPE_OBJECT);
693 
694 static void
gdk_pixbuf_animation_iter_class_init(GdkPixbufAnimationIterClass * klass)695 gdk_pixbuf_animation_iter_class_init (GdkPixbufAnimationIterClass *klass)
696 {
697 }
698 
699 static void
gdk_pixbuf_animation_iter_init(GdkPixbufAnimationIter * iter)700 gdk_pixbuf_animation_iter_init (GdkPixbufAnimationIter *iter)
701 {
702 }
703 
704 /**
705  * gdk_pixbuf_animation_iter_get_delay_time:
706  * @iter: an animation iterator
707  *
708  * Gets the number of milliseconds the current pixbuf should be displayed,
709  * or -1 if the current pixbuf should be displayed forever. g_timeout_add()
710  * conveniently takes a timeout in milliseconds, so you can use a timeout
711  * to schedule the next update.
712  *
713  * Note that some formats, like GIF, might clamp the timeout values in the
714  * image file to avoid updates that are just too quick. The minimum timeout
715  * for GIF images is currently 20 milliseconds.
716  *
717  * Return value: delay time in milliseconds (thousandths of a second)
718  */
719 gint
gdk_pixbuf_animation_iter_get_delay_time(GdkPixbufAnimationIter * iter)720 gdk_pixbuf_animation_iter_get_delay_time (GdkPixbufAnimationIter *iter)
721 {
722         g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION_ITER (iter), -1);
723         g_return_val_if_fail (GDK_PIXBUF_ANIMATION_ITER_GET_CLASS (iter)->get_delay_time, -1);
724 
725         return GDK_PIXBUF_ANIMATION_ITER_GET_CLASS (iter)->get_delay_time (iter);
726 }
727 
728 /**
729  * gdk_pixbuf_animation_iter_get_pixbuf:
730  * @iter: an animation iterator
731  *
732  * Gets the current pixbuf which should be displayed; the pixbuf might not
733  * be the same size as the animation itself
734  * (gdk_pixbuf_animation_get_width(), gdk_pixbuf_animation_get_height()).
735  * This pixbuf should be displayed for
736  * gdk_pixbuf_animation_iter_get_delay_time() milliseconds. The caller
737  * of this function does not own a reference to the returned pixbuf;
738  * the returned pixbuf will become invalid when the iterator advances
739  * to the next frame, which may happen anytime you call
740  * gdk_pixbuf_animation_iter_advance(). Copy the pixbuf to keep it
741  * (don't just add a reference), as it may get recycled as you advance
742  * the iterator.
743  *
744  * Return value: (transfer none): the pixbuf to be displayed
745  */
746 GdkPixbuf*
gdk_pixbuf_animation_iter_get_pixbuf(GdkPixbufAnimationIter * iter)747 gdk_pixbuf_animation_iter_get_pixbuf (GdkPixbufAnimationIter *iter)
748 {
749         g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION_ITER (iter), NULL);
750         g_return_val_if_fail (GDK_PIXBUF_ANIMATION_ITER_GET_CLASS (iter)->get_pixbuf, NULL);
751 
752         return GDK_PIXBUF_ANIMATION_ITER_GET_CLASS (iter)->get_pixbuf (iter);
753 }
754 
755 /**
756  * gdk_pixbuf_animation_iter_on_currently_loading_frame:
757  * @iter: a #GdkPixbufAnimationIter
758  *
759  * Used to determine how to respond to the area_updated signal on
760  * #GdkPixbufLoader when loading an animation. area_updated is emitted
761  * for an area of the frame currently streaming in to the loader. So if
762  * you're on the currently loading frame, you need to redraw the screen for
763  * the updated area.
764  *
765  * Return value: %TRUE if the frame we're on is partially loaded, or the last frame
766  */
767 gboolean
gdk_pixbuf_animation_iter_on_currently_loading_frame(GdkPixbufAnimationIter * iter)768 gdk_pixbuf_animation_iter_on_currently_loading_frame (GdkPixbufAnimationIter *iter)
769 {
770         g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION_ITER (iter), FALSE);
771         g_return_val_if_fail (GDK_PIXBUF_ANIMATION_ITER_GET_CLASS (iter)->on_currently_loading_frame, FALSE);
772 
773         return GDK_PIXBUF_ANIMATION_ITER_GET_CLASS (iter)->on_currently_loading_frame (iter);
774 }
775 
776 /**
777  * gdk_pixbuf_animation_iter_advance:
778  * @iter: a #GdkPixbufAnimationIter
779  * @current_time: (allow-none): current time
780  *
781  * Possibly advances an animation to a new frame. Chooses the frame based
782  * on the start time passed to gdk_pixbuf_animation_get_iter().
783  *
784  * @current_time would normally come from g_get_current_time(), and
785  * must be greater than or equal to the time passed to
786  * gdk_pixbuf_animation_get_iter(), and must increase or remain
787  * unchanged each time gdk_pixbuf_animation_iter_get_pixbuf() is
788  * called. That is, you can't go backward in time; animations only
789  * play forward.
790  *
791  * As a shortcut, pass %NULL for the current time and g_get_current_time()
792  * will be invoked on your behalf. So you only need to explicitly pass
793  * @current_time if you're doing something odd like playing the animation
794  * at double speed.
795  *
796  * If this function returns %FALSE, there's no need to update the animation
797  * display, assuming the display had been rendered prior to advancing;
798  * if %TRUE, you need to call gdk_pixbuf_animation_iter_get_pixbuf()
799  * and update the display with the new pixbuf.
800  *
801  * Returns: %TRUE if the image may need updating
802  */
803 gboolean
gdk_pixbuf_animation_iter_advance(GdkPixbufAnimationIter * iter,const GTimeVal * current_time)804 gdk_pixbuf_animation_iter_advance (GdkPixbufAnimationIter *iter,
805                                    const GTimeVal         *current_time)
806 {
807         GTimeVal val;
808 
809         g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION_ITER (iter), FALSE);
810         g_return_val_if_fail (GDK_PIXBUF_ANIMATION_ITER_GET_CLASS (iter)->advance, FALSE);
811 
812         if (current_time)
813                 val = *current_time;
814         else
815                 g_get_current_time (&val);
816 
817         return GDK_PIXBUF_ANIMATION_ITER_GET_CLASS (iter)->advance (iter, &val);
818 }
819 
820 static void                    gdk_pixbuf_non_anim_finalize         (GObject            *object);
821 static gboolean                gdk_pixbuf_non_anim_is_static_image  (GdkPixbufAnimation *animation);
822 static GdkPixbuf*              gdk_pixbuf_non_anim_get_static_image (GdkPixbufAnimation *animation);
823 static void                    gdk_pixbuf_non_anim_get_size         (GdkPixbufAnimation *anim,
824                                                                      gint               *width,
825                                                                      gint               *height);
826 static GdkPixbufAnimationIter* gdk_pixbuf_non_anim_get_iter         (GdkPixbufAnimation *anim,
827                                                                      const GTimeVal     *start_time);
828 
829 G_DEFINE_TYPE (GdkPixbufNonAnim, gdk_pixbuf_non_anim, GDK_TYPE_PIXBUF_ANIMATION);
830 
831 static void
gdk_pixbuf_non_anim_class_init(GdkPixbufNonAnimClass * klass)832 gdk_pixbuf_non_anim_class_init (GdkPixbufNonAnimClass *klass)
833 {
834         GObjectClass *object_class = G_OBJECT_CLASS (klass);
835         GdkPixbufAnimationClass *anim_class = GDK_PIXBUF_ANIMATION_CLASS (klass);
836 
837         object_class->finalize = gdk_pixbuf_non_anim_finalize;
838 
839         anim_class->is_static_image = gdk_pixbuf_non_anim_is_static_image;
840         anim_class->get_static_image = gdk_pixbuf_non_anim_get_static_image;
841         anim_class->get_size = gdk_pixbuf_non_anim_get_size;
842         anim_class->get_iter = gdk_pixbuf_non_anim_get_iter;
843 }
844 
845 static void
gdk_pixbuf_non_anim_init(GdkPixbufNonAnim * non_anim)846 gdk_pixbuf_non_anim_init (GdkPixbufNonAnim *non_anim)
847 {
848 }
849 
850 static void
gdk_pixbuf_non_anim_finalize(GObject * object)851 gdk_pixbuf_non_anim_finalize (GObject *object)
852 {
853         GdkPixbufNonAnim *non_anim = GDK_PIXBUF_NON_ANIM (object);
854 
855         if (non_anim->pixbuf)
856                 g_object_unref (non_anim->pixbuf);
857 
858         G_OBJECT_CLASS (gdk_pixbuf_non_anim_parent_class)->finalize (object);
859 }
860 
861 GdkPixbufAnimation*
gdk_pixbuf_non_anim_new(GdkPixbuf * pixbuf)862 gdk_pixbuf_non_anim_new (GdkPixbuf *pixbuf)
863 {
864         GdkPixbufNonAnim *non_anim;
865 
866         non_anim = g_object_new (GDK_TYPE_PIXBUF_NON_ANIM, NULL);
867 
868         non_anim->pixbuf = pixbuf;
869 
870         if (pixbuf)
871                 g_object_ref (pixbuf);
872 
873         return GDK_PIXBUF_ANIMATION (non_anim);
874 }
875 
876 static gboolean
gdk_pixbuf_non_anim_is_static_image(GdkPixbufAnimation * animation)877 gdk_pixbuf_non_anim_is_static_image (GdkPixbufAnimation *animation)
878 {
879 
880         return TRUE;
881 }
882 
883 static GdkPixbuf*
gdk_pixbuf_non_anim_get_static_image(GdkPixbufAnimation * animation)884 gdk_pixbuf_non_anim_get_static_image (GdkPixbufAnimation *animation)
885 {
886         GdkPixbufNonAnim *non_anim;
887 
888         non_anim = GDK_PIXBUF_NON_ANIM (animation);
889 
890         return non_anim->pixbuf;
891 }
892 
893 static void
gdk_pixbuf_non_anim_get_size(GdkPixbufAnimation * anim,gint * width,gint * height)894 gdk_pixbuf_non_anim_get_size (GdkPixbufAnimation *anim,
895                               gint               *width,
896                               gint               *height)
897 {
898         GdkPixbufNonAnim *non_anim;
899 
900         non_anim = GDK_PIXBUF_NON_ANIM (anim);
901 
902         if (width)
903                 *width = gdk_pixbuf_get_width (non_anim->pixbuf);
904 
905         if (height)
906                 *height = gdk_pixbuf_get_height (non_anim->pixbuf);
907 }
908 
909 static GdkPixbufAnimationIter*
gdk_pixbuf_non_anim_get_iter(GdkPixbufAnimation * anim,const GTimeVal * start_time)910 gdk_pixbuf_non_anim_get_iter (GdkPixbufAnimation *anim,
911                               const GTimeVal     *start_time)
912 {
913         GdkPixbufNonAnimIter *iter;
914 
915         iter = g_object_new (GDK_TYPE_PIXBUF_NON_ANIM_ITER, NULL);
916 
917         iter->non_anim = GDK_PIXBUF_NON_ANIM (anim);
918 
919         g_object_ref (iter->non_anim);
920 
921         return GDK_PIXBUF_ANIMATION_ITER (iter);
922 }
923 
924 static void       gdk_pixbuf_non_anim_iter_finalize                   (GObject                *object);
925 static gint       gdk_pixbuf_non_anim_iter_get_delay_time             (GdkPixbufAnimationIter *iter);
926 static GdkPixbuf* gdk_pixbuf_non_anim_iter_get_pixbuf                 (GdkPixbufAnimationIter *iter);
927 static gboolean   gdk_pixbuf_non_anim_iter_on_currently_loading_frame (GdkPixbufAnimationIter *iter);
928 static gboolean   gdk_pixbuf_non_anim_iter_advance                    (GdkPixbufAnimationIter *iter,
929                                                                        const GTimeVal         *current_time);
930 
G_DEFINE_TYPE(GdkPixbufNonAnimIter,gdk_pixbuf_non_anim_iter,GDK_TYPE_PIXBUF_ANIMATION_ITER)931 G_DEFINE_TYPE (GdkPixbufNonAnimIter, gdk_pixbuf_non_anim_iter, GDK_TYPE_PIXBUF_ANIMATION_ITER)
932 
933 static void
934 gdk_pixbuf_non_anim_iter_class_init (GdkPixbufNonAnimIterClass *klass)
935 {
936         GObjectClass *object_class = G_OBJECT_CLASS (klass);
937         GdkPixbufAnimationIterClass *anim_iter_class =
938                 GDK_PIXBUF_ANIMATION_ITER_CLASS (klass);
939 
940         object_class->finalize = gdk_pixbuf_non_anim_iter_finalize;
941 
942         anim_iter_class->get_delay_time = gdk_pixbuf_non_anim_iter_get_delay_time;
943         anim_iter_class->get_pixbuf = gdk_pixbuf_non_anim_iter_get_pixbuf;
944         anim_iter_class->on_currently_loading_frame = gdk_pixbuf_non_anim_iter_on_currently_loading_frame;
945         anim_iter_class->advance = gdk_pixbuf_non_anim_iter_advance;
946 }
947 
948 static void
gdk_pixbuf_non_anim_iter_init(GdkPixbufNonAnimIter * non_iter)949 gdk_pixbuf_non_anim_iter_init (GdkPixbufNonAnimIter *non_iter)
950 {
951 }
952 
953 static void
gdk_pixbuf_non_anim_iter_finalize(GObject * object)954 gdk_pixbuf_non_anim_iter_finalize (GObject *object)
955 {
956         GdkPixbufNonAnimIter *iter = GDK_PIXBUF_NON_ANIM_ITER (object);
957 
958         g_object_unref (iter->non_anim);
959 
960         G_OBJECT_CLASS (gdk_pixbuf_non_anim_iter_parent_class)->finalize (object);
961 }
962 
963 static gint
gdk_pixbuf_non_anim_iter_get_delay_time(GdkPixbufAnimationIter * iter)964 gdk_pixbuf_non_anim_iter_get_delay_time (GdkPixbufAnimationIter *iter)
965 {
966         return -1; /* show only frame forever */
967 }
968 
969 static GdkPixbuf*
gdk_pixbuf_non_anim_iter_get_pixbuf(GdkPixbufAnimationIter * iter)970 gdk_pixbuf_non_anim_iter_get_pixbuf (GdkPixbufAnimationIter *iter)
971 {
972         return GDK_PIXBUF_NON_ANIM_ITER (iter)->non_anim->pixbuf;
973 }
974 
975 
976 static gboolean
gdk_pixbuf_non_anim_iter_on_currently_loading_frame(GdkPixbufAnimationIter * iter)977 gdk_pixbuf_non_anim_iter_on_currently_loading_frame (GdkPixbufAnimationIter *iter)
978 {
979         return TRUE;
980 }
981 
982 static gboolean
gdk_pixbuf_non_anim_iter_advance(GdkPixbufAnimationIter * iter,const GTimeVal * current_time)983 gdk_pixbuf_non_anim_iter_advance (GdkPixbufAnimationIter *iter,
984                                   const GTimeVal         *current_time)
985 {
986 
987         /* Advancing never requires a refresh */
988         return FALSE;
989 }
990