]> Pileus Git - ~andy/gtk/blob - demos/testanimation.c
Replace a lot of idle and timeout calls by the new gdk_threads api.
[~andy/gtk] / demos / testanimation.c
1
2 /* testpixbuf -- test program for gdk-pixbuf code
3  * Copyright (C) 1999 Mark Crichton, Larry Ewing
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <gtk/gtk.h>
28
29 typedef struct _LoadContext LoadContext;
30
31 struct _LoadContext
32 {
33   gchar *filename;
34   GtkWidget *window;
35   GdkPixbufLoader *pixbuf_loader;
36   guint load_timeout;
37   FILE* image_stream;
38 };
39
40 static void
41 destroy_context (gpointer data)
42 {
43   LoadContext *lc = data;
44
45   g_free (lc->filename);
46   
47   if (lc->load_timeout)
48     g_source_remove (lc->load_timeout);
49
50   if (lc->image_stream)
51     fclose (lc->image_stream);
52
53   if (lc->pixbuf_loader)
54     {
55       gdk_pixbuf_loader_close (lc->pixbuf_loader, NULL);
56       g_object_unref (lc->pixbuf_loader);
57     }
58   
59   g_free (lc);
60 }
61
62 static LoadContext*
63 get_load_context (GtkWidget *image)
64 {
65   LoadContext *lc;
66
67   lc = g_object_get_data (G_OBJECT (image), "lc");
68
69   if (lc == NULL)
70     {
71       lc = g_new0 (LoadContext, 1);
72
73       g_object_set_data_full (G_OBJECT (image),        
74                               "lc",
75                               lc,
76                               destroy_context);
77     }
78
79   return lc;
80 }
81
82 static void
83 progressive_prepared_callback (GdkPixbufLoader* loader,
84                                gpointer         data)
85 {
86   GdkPixbuf* pixbuf;
87   GtkWidget* image;
88
89   image = GTK_WIDGET (data);
90     
91   pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
92
93   /* Avoid displaying random memory contents, since the pixbuf
94    * isn't filled in yet.
95    */
96   gdk_pixbuf_fill (pixbuf, 0xaaaaaaff);
97
98   /* Could set the pixbuf instead, if we only wanted to display
99    * static images.
100    */
101   gtk_image_set_from_animation (GTK_IMAGE (image),
102                                 gdk_pixbuf_loader_get_animation (loader));
103 }
104
105 static void
106 progressive_updated_callback (GdkPixbufLoader* loader,
107                               gint x, gint y, gint width, gint height,
108                               gpointer data)
109 {
110   GtkWidget* image;
111   
112   image = GTK_WIDGET (data);
113
114   /* We know the pixbuf inside the GtkImage has changed, but the image
115    * itself doesn't know this; so queue a redraw.  If we wanted to be
116    * really efficient, we could use a drawing area or something
117    * instead of a GtkImage, so we could control the exact position of
118    * the pixbuf on the display, then we could queue a draw for only
119    * the updated area of the image.
120    */
121
122   /* We only really need to redraw if the image's animation iterator
123    * is gdk_pixbuf_animation_iter_on_currently_loading_frame(), but
124    * who cares.
125    */
126   
127   gtk_widget_queue_draw (image);
128 }
129
130 static gint
131 progressive_timeout (gpointer data)
132 {
133   GtkWidget *image;
134   LoadContext *lc;
135   
136   image = GTK_WIDGET (data);
137   lc = get_load_context (image);
138   
139   /* This shows off fully-paranoid error handling, so looks scary.
140    * You could factor out the error handling code into a nice separate
141    * function to make things nicer.
142    */
143   
144   if (lc->image_stream)
145     {
146       size_t bytes_read;
147       guchar buf[256];
148       GError *error = NULL;
149       
150       bytes_read = fread (buf, 1, 256, lc->image_stream);
151
152       if (ferror (lc->image_stream))
153         {
154           GtkWidget *dialog;
155           
156           dialog = gtk_message_dialog_new (GTK_WINDOW (lc->window),
157                                            GTK_DIALOG_DESTROY_WITH_PARENT,
158                                            GTK_MESSAGE_ERROR,
159                                            GTK_BUTTONS_CLOSE,
160                                            "Failure reading image file 'alphatest.png': %s",
161                                            g_strerror (errno));
162
163           g_signal_connect (dialog, "response",
164                             G_CALLBACK (gtk_widget_destroy), NULL);
165
166           fclose (lc->image_stream);
167           lc->image_stream = NULL;
168
169           gtk_widget_show (dialog);
170           
171           lc->load_timeout = 0;
172
173           return FALSE; /* uninstall the timeout */
174         }
175
176       if (!gdk_pixbuf_loader_write (lc->pixbuf_loader,
177                                     buf, bytes_read,
178                                     &error))
179         {
180           GtkWidget *dialog;
181           
182           dialog = gtk_message_dialog_new (GTK_WINDOW (lc->window),
183                                            GTK_DIALOG_DESTROY_WITH_PARENT,
184                                            GTK_MESSAGE_ERROR,
185                                            GTK_BUTTONS_CLOSE,
186                                            "Failed to load image: %s",
187                                            error->message);
188
189           g_error_free (error);
190           
191           g_signal_connect (dialog, "response",
192                             G_CALLBACK (gtk_widget_destroy), NULL);
193
194           fclose (lc->image_stream);
195           lc->image_stream = NULL;
196           
197           gtk_widget_show (dialog);
198
199           lc->load_timeout = 0;
200
201           return FALSE; /* uninstall the timeout */
202         }
203
204       if (feof (lc->image_stream))
205         {
206           fclose (lc->image_stream);
207           lc->image_stream = NULL;
208
209           /* Errors can happen on close, e.g. if the image
210            * file was truncated we'll know on close that
211            * it was incomplete.
212            */
213           error = NULL;
214           if (!gdk_pixbuf_loader_close (lc->pixbuf_loader,
215                                         &error))
216             {
217               GtkWidget *dialog;
218               
219               dialog = gtk_message_dialog_new (GTK_WINDOW (lc->window),
220                                                GTK_DIALOG_DESTROY_WITH_PARENT,
221                                                GTK_MESSAGE_ERROR,
222                                                GTK_BUTTONS_CLOSE,
223                                                "Failed to load image: %s",
224                                                error->message);
225               
226               g_error_free (error);
227               
228               g_signal_connect (dialog, "response",
229                                 G_CALLBACK (gtk_widget_destroy), NULL);
230               
231               gtk_widget_show (dialog);
232
233               g_object_unref (lc->pixbuf_loader);
234               lc->pixbuf_loader = NULL;
235               
236               lc->load_timeout = 0;
237               
238               return FALSE; /* uninstall the timeout */
239             }
240           
241           g_object_unref (lc->pixbuf_loader);
242           lc->pixbuf_loader = NULL;
243         }
244     }
245   else
246     {
247       lc->image_stream = fopen (lc->filename, "r");
248
249       if (lc->image_stream == NULL)
250         {
251           GtkWidget *dialog;
252           
253           dialog = gtk_message_dialog_new (GTK_WINDOW (lc->window),
254                                            GTK_DIALOG_DESTROY_WITH_PARENT,
255                                            GTK_MESSAGE_ERROR,
256                                            GTK_BUTTONS_CLOSE,
257                                            "Unable to open image file '%s': %s",
258                                            lc->filename,
259                                            g_strerror (errno));
260
261           g_signal_connect (dialog, "response",
262                             G_CALLBACK (gtk_widget_destroy), NULL);
263           
264           gtk_widget_show (dialog);
265
266           lc->load_timeout = 0;
267
268           return FALSE; /* uninstall the timeout */
269         }
270
271       if (lc->pixbuf_loader)
272         {
273           gdk_pixbuf_loader_close (lc->pixbuf_loader, NULL);
274           g_object_unref (lc->pixbuf_loader);
275           lc->pixbuf_loader = NULL;
276         }
277       
278       lc->pixbuf_loader = gdk_pixbuf_loader_new ();
279       
280       g_signal_connect (lc->pixbuf_loader, "area_prepared",
281                         G_CALLBACK (progressive_prepared_callback), image);
282       g_signal_connect (lc->pixbuf_loader, "area_updated",
283                         G_CALLBACK (progressive_updated_callback), image);
284     }
285
286   /* leave timeout installed */
287   return TRUE;
288 }
289
290 static void
291 start_progressive_loading (GtkWidget *image)
292 {
293   LoadContext *lc;
294
295   lc = get_load_context (image);
296   
297   /* This is obviously totally contrived (we slow down loading
298    * on purpose to show how incremental loading works).
299    * The real purpose of incremental loading is the case where
300    * you are reading data from a slow source such as the network.
301    * The timeout simply simulates a slow data source by inserting
302    * pauses in the reading process.
303    */
304   lc->load_timeout = gdk_threads_add_timeout (100,
305                                     progressive_timeout,
306                                     image);
307 }
308
309 static GtkWidget *
310 do_image (const char *filename)
311 {
312   GtkWidget *frame;
313   GtkWidget *vbox;
314   GtkWidget *image;
315   GtkWidget *label;
316   GtkWidget *align;
317   GtkWidget *window;
318   gchar *str, *escaped;
319   LoadContext *lc;
320   
321   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
322   gtk_window_set_title (GTK_WINDOW (window), "Image Loading");
323
324   gtk_container_set_border_width (GTK_CONTAINER (window), 8);
325
326   vbox = gtk_vbox_new (FALSE, 8);
327   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
328   gtk_container_add (GTK_CONTAINER (window), vbox);
329
330   label = gtk_label_new (NULL);
331   gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
332   escaped = g_markup_escape_text (filename, -1);
333   str = g_strdup_printf ("Progressively loading: <b>%s</b>", escaped);
334   gtk_label_set_markup (GTK_LABEL (label),
335                         str);
336   g_free (escaped);
337   g_free (str);
338   
339   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
340       
341   frame = gtk_frame_new (NULL);
342   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
343   /* The alignment keeps the frame from growing when users resize
344    * the window
345    */
346   align = gtk_alignment_new (0.5, 0.5, 0, 0);
347   gtk_container_add (GTK_CONTAINER (align), frame);
348   gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0);      
349
350   image = gtk_image_new_from_pixbuf (NULL);
351   gtk_container_add (GTK_CONTAINER (frame), image);
352
353   lc = get_load_context (image);
354
355   lc->window = window;
356   lc->filename = g_strdup (filename);
357   
358   start_progressive_loading (image);
359
360   g_signal_connect (window, "destroy",
361                     G_CALLBACK (gtk_main_quit), NULL);
362   
363   g_signal_connect (window, "delete_event",
364                     G_CALLBACK (gtk_main_quit), NULL);
365
366   gtk_widget_show_all (window);
367
368   return window;
369 }
370
371 static void
372 do_nonprogressive (const gchar *filename)
373 {
374   GtkWidget *frame;
375   GtkWidget *vbox;
376   GtkWidget *image;
377   GtkWidget *label;
378   GtkWidget *align;
379   GtkWidget *window;
380   gchar *str, *escaped;
381   
382   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
383   gtk_window_set_title (GTK_WINDOW (window), "Animation");
384
385   gtk_container_set_border_width (GTK_CONTAINER (window), 8);
386
387   vbox = gtk_vbox_new (FALSE, 8);
388   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
389   gtk_container_add (GTK_CONTAINER (window), vbox);
390
391   label = gtk_label_new (NULL);
392   gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
393   escaped = g_markup_escape_text (filename, -1);
394   str = g_strdup_printf ("Loaded from file: <b>%s</b>", escaped);
395   gtk_label_set_markup (GTK_LABEL (label),
396                         str);
397   g_free (escaped);
398   g_free (str);
399   
400   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
401       
402   frame = gtk_frame_new (NULL);
403   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
404   /* The alignment keeps the frame from growing when users resize
405    * the window
406    */
407   align = gtk_alignment_new (0.5, 0.5, 0, 0);
408   gtk_container_add (GTK_CONTAINER (align), frame);
409   gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0);      
410
411   image = gtk_image_new_from_file (filename);
412   gtk_container_add (GTK_CONTAINER (frame), image);
413
414   g_signal_connect (window, "destroy",
415                     G_CALLBACK (gtk_main_quit), NULL);
416   
417   g_signal_connect (window, "delete_event",
418                     G_CALLBACK (gtk_main_quit), NULL);
419
420   gtk_widget_show_all (window);
421 }
422
423 int
424 main (int    argc,
425       char **argv)
426 {
427   gint i;
428   
429   gtk_init (&argc, &argv);
430
431   i = 1;
432   while (i < argc)
433     {
434       do_image (argv[i]);
435       do_nonprogressive (argv[i]);
436       
437       ++i;
438     }
439
440   gtk_main ();
441   
442   return 0;
443 }
444