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

source code of gtk/subprojects/gdk-pixbuf/gdk-pixbuf/gdk-pixbuf-animation.c