]> Pileus Git - ~andy/gtk/blob - demos/testanimation.c
at Oct 20 19:38:16 2001 Owen Taylor <otaylor@redhat.com>
[~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 (G_OBJECT (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 (G_OBJECT (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 (G_OBJECT (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 (G_OBJECT (lc->pixbuf_loader));
275           lc->pixbuf_loader = NULL;
276         }
277       
278       lc->pixbuf_loader = gdk_pixbuf_loader_new ();
279       
280       g_signal_connect (G_OBJECT (lc->pixbuf_loader), "area_prepared",
281                         G_CALLBACK (progressive_prepared_callback), image);
282       g_signal_connect (G_OBJECT (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 = g_timeout_add (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   gtk_widget_show_all (window);
361
362   return window;
363 }
364
365 static void
366 do_nonprogressive (const gchar *filename)
367 {
368   GtkWidget *frame;
369   GtkWidget *vbox;
370   GtkWidget *image;
371   GtkWidget *label;
372   GtkWidget *align;
373   GtkWidget *window;
374   gchar *str, *escaped;
375   
376   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
377   gtk_window_set_title (GTK_WINDOW (window), "Animation");
378
379   gtk_container_set_border_width (GTK_CONTAINER (window), 8);
380
381   vbox = gtk_vbox_new (FALSE, 8);
382   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
383   gtk_container_add (GTK_CONTAINER (window), vbox);
384
385   label = gtk_label_new (NULL);
386   gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
387   escaped = g_markup_escape_text (filename, -1);
388   str = g_strdup_printf ("Loaded from file: <b>%s</b>", escaped);
389   gtk_label_set_markup (GTK_LABEL (label),
390                         str);
391   g_free (escaped);
392   g_free (str);
393   
394   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
395       
396   frame = gtk_frame_new (NULL);
397   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
398   /* The alignment keeps the frame from growing when users resize
399    * the window
400    */
401   align = gtk_alignment_new (0.5, 0.5, 0, 0);
402   gtk_container_add (GTK_CONTAINER (align), frame);
403   gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0);      
404
405   image = gtk_image_new_from_file (filename);
406   gtk_container_add (GTK_CONTAINER (frame), image);
407
408   gtk_widget_show_all (window);
409 }
410
411 int
412 main (int    argc,
413       char **argv)
414 {
415   gint i;
416   
417   gtk_init (&argc, &argv);
418
419   i = 1;
420   while (i < argc)
421     {
422       do_image (argv[i]);
423       do_nonprogressive (argv[i]);
424       
425       ++i;
426     }
427
428   gtk_main ();
429   
430   return 0;
431 }
432
433