1 /* Images
2 *
3 * GtkImage is used to display an image; the image can be in a number of formats.
4 * Typically, you load an image into a GdkPixbuf, then display the pixbuf.
5 *
6 * This demo code shows some of the more obscure cases, in the simple
7 * case a call to gtk_image_new_from_file() is all you need.
8 *
9 * If you want to put image data in your program as a C variable,
10 * use the make-inline-pixbuf program that comes with GTK+.
11 * This way you won't need to depend on loading external files, your
12 * application binary can be self-contained.
13 */
14
15 #include <gtk/gtk.h>
16 #include <glib/gstdio.h>
17 #include <stdio.h>
18 #include <errno.h>
19 #include "demo-common.h"
20
21 static GtkWidget *window = NULL;
22 static GdkPixbufLoader *pixbuf_loader = NULL;
23 static guint load_timeout = 0;
24 static FILE* image_stream = NULL;
25
26 static void
progressive_prepared_callback(GdkPixbufLoader * loader,gpointer data)27 progressive_prepared_callback (GdkPixbufLoader *loader,
28 gpointer data)
29 {
30 GdkPixbuf *pixbuf;
31 GtkWidget *image;
32
33 image = GTK_WIDGET (data);
34
35 pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
36
37 /* Avoid displaying random memory contents, since the pixbuf
38 * isn't filled in yet.
39 */
40 gdk_pixbuf_fill (pixbuf, 0xaaaaaaff);
41
42 gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf);
43 }
44
45 static void
progressive_updated_callback(GdkPixbufLoader * loader,gint x,gint y,gint width,gint height,gpointer data)46 progressive_updated_callback (GdkPixbufLoader *loader,
47 gint x,
48 gint y,
49 gint width,
50 gint height,
51 gpointer data)
52 {
53 GtkWidget *image;
54
55 image = GTK_WIDGET (data);
56
57 /* We know the pixbuf inside the GtkImage has changed, but the image
58 * itself doesn't know this; so queue a redraw. If we wanted to be
59 * really efficient, we could use a drawing area or something
60 * instead of a GtkImage, so we could control the exact position of
61 * the pixbuf on the display, then we could queue a draw for only
62 * the updated area of the image.
63 */
64
65 gtk_widget_queue_draw (image);
66 }
67
68 static gint
progressive_timeout(gpointer data)69 progressive_timeout (gpointer data)
70 {
71 GtkWidget *image;
72
73 image = GTK_WIDGET (data);
74
75 /* This shows off fully-paranoid error handling, so looks scary.
76 * You could factor out the error handling code into a nice separate
77 * function to make things nicer.
78 */
79
80 if (image_stream)
81 {
82 size_t bytes_read;
83 guchar buf[256];
84 GError *error = NULL;
85
86 bytes_read = fread (buf, 1, 256, image_stream);
87
88 if (ferror (image_stream))
89 {
90 GtkWidget *dialog;
91
92 dialog = gtk_message_dialog_new (GTK_WINDOW (window),
93 GTK_DIALOG_DESTROY_WITH_PARENT,
94 GTK_MESSAGE_ERROR,
95 GTK_BUTTONS_CLOSE,
96 "Failure reading image file 'alphatest.png': %s",
97 g_strerror (errno));
98
99 g_signal_connect (dialog, "response",
100 G_CALLBACK (gtk_widget_destroy), NULL);
101
102 fclose (image_stream);
103 image_stream = NULL;
104
105 gtk_widget_show (dialog);
106
107 load_timeout = 0;
108
109 return FALSE; /* uninstall the timeout */
110 }
111
112 if (!gdk_pixbuf_loader_write (pixbuf_loader,
113 buf, bytes_read,
114 &error))
115 {
116 GtkWidget *dialog;
117
118 dialog = gtk_message_dialog_new (GTK_WINDOW (window),
119 GTK_DIALOG_DESTROY_WITH_PARENT,
120 GTK_MESSAGE_ERROR,
121 GTK_BUTTONS_CLOSE,
122 "Failed to load image: %s",
123 error->message);
124
125 g_error_free (error);
126
127 g_signal_connect (dialog, "response",
128 G_CALLBACK (gtk_widget_destroy), NULL);
129
130 fclose (image_stream);
131 image_stream = NULL;
132
133 gtk_widget_show (dialog);
134
135 load_timeout = 0;
136
137 return FALSE; /* uninstall the timeout */
138 }
139
140 if (feof (image_stream))
141 {
142 fclose (image_stream);
143 image_stream = NULL;
144
145 /* Errors can happen on close, e.g. if the image
146 * file was truncated we'll know on close that
147 * it was incomplete.
148 */
149 error = NULL;
150 if (!gdk_pixbuf_loader_close (pixbuf_loader,
151 &error))
152 {
153 GtkWidget *dialog;
154
155 dialog = gtk_message_dialog_new (GTK_WINDOW (window),
156 GTK_DIALOG_DESTROY_WITH_PARENT,
157 GTK_MESSAGE_ERROR,
158 GTK_BUTTONS_CLOSE,
159 "Failed to load image: %s",
160 error->message);
161
162 g_error_free (error);
163
164 g_signal_connect (dialog, "response",
165 G_CALLBACK (gtk_widget_destroy), NULL);
166
167 gtk_widget_show (dialog);
168
169 g_object_unref (pixbuf_loader);
170 pixbuf_loader = NULL;
171
172 load_timeout = 0;
173
174 return FALSE; /* uninstall the timeout */
175 }
176
177 g_object_unref (pixbuf_loader);
178 pixbuf_loader = NULL;
179 }
180 }
181 else
182 {
183 gchar *filename;
184 gchar *error_message = NULL;
185 GError *error = NULL;
186
187 /* demo_find_file() looks in the current directory first,
188 * so you can run gtk-demo without installing GTK, then looks
189 * in the location where the file is installed.
190 */
191 filename = demo_find_file ("alphatest.png", &error);
192 if (error)
193 {
194 error_message = g_strdup (error->message);
195 g_error_free (error);
196 }
197 else
198 {
199 image_stream = g_fopen (filename, "rb");
200 g_free (filename);
201
202 if (!image_stream)
203 error_message = g_strdup_printf ("Unable to open image file 'alphatest.png': %s",
204 g_strerror (errno));
205 }
206
207 if (image_stream == NULL)
208 {
209 GtkWidget *dialog;
210
211 dialog = gtk_message_dialog_new (GTK_WINDOW (window),
212 GTK_DIALOG_DESTROY_WITH_PARENT,
213 GTK_MESSAGE_ERROR,
214 GTK_BUTTONS_CLOSE,
215 "%s", error_message);
216 g_free (error_message);
217
218 g_signal_connect (dialog, "response",
219 G_CALLBACK (gtk_widget_destroy), NULL);
220
221 gtk_widget_show (dialog);
222
223 load_timeout = 0;
224
225 return FALSE; /* uninstall the timeout */
226 }
227
228 if (pixbuf_loader)
229 {
230 gdk_pixbuf_loader_close (pixbuf_loader, NULL);
231 g_object_unref (pixbuf_loader);
232 pixbuf_loader = NULL;
233 }
234
235 pixbuf_loader = gdk_pixbuf_loader_new ();
236
237 g_signal_connect (pixbuf_loader, "area-prepared",
238 G_CALLBACK (progressive_prepared_callback), image);
239
240 g_signal_connect (pixbuf_loader, "area-updated",
241 G_CALLBACK (progressive_updated_callback), image);
242 }
243
244 /* leave timeout installed */
245 return TRUE;
246 }
247
248 static void
start_progressive_loading(GtkWidget * image)249 start_progressive_loading (GtkWidget *image)
250 {
251 /* This is obviously totally contrived (we slow down loading
252 * on purpose to show how incremental loading works).
253 * The real purpose of incremental loading is the case where
254 * you are reading data from a slow source such as the network.
255 * The timeout simply simulates a slow data source by inserting
256 * pauses in the reading process.
257 */
258 load_timeout = gdk_threads_add_timeout (150,
259 progressive_timeout,
260 image);
261 }
262
263 static void
cleanup_callback(GtkObject * object,gpointer data)264 cleanup_callback (GtkObject *object,
265 gpointer data)
266 {
267 if (load_timeout)
268 {
269 g_source_remove (load_timeout);
270 load_timeout = 0;
271 }
272
273 if (pixbuf_loader)
274 {
275 gdk_pixbuf_loader_close (pixbuf_loader, NULL);
276 g_object_unref (pixbuf_loader);
277 pixbuf_loader = NULL;
278 }
279
280 if (image_stream)
281 fclose (image_stream);
282 image_stream = NULL;
283 }
284
285 static void
toggle_sensitivity_callback(GtkWidget * togglebutton,gpointer user_data)286 toggle_sensitivity_callback (GtkWidget *togglebutton,
287 gpointer user_data)
288 {
289 GtkContainer *container = user_data;
290 GList *list;
291 GList *tmp;
292
293 list = gtk_container_get_children (container);
294
295 tmp = list;
296 while (tmp != NULL)
297 {
298 /* don't disable our toggle */
299 if (GTK_WIDGET (tmp->data) != togglebutton)
300 gtk_widget_set_sensitive (GTK_WIDGET (tmp->data),
301 !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (togglebutton)));
302
303 tmp = tmp->next;
304 }
305
306 g_list_free (list);
307 }
308
309
310 GtkWidget *
do_images(GtkWidget * do_widget)311 do_images (GtkWidget *do_widget)
312 {
313 GtkWidget *frame;
314 GtkWidget *vbox;
315 GtkWidget *image;
316 GtkWidget *label;
317 GtkWidget *align;
318 GtkWidget *button;
319 GdkPixbuf *pixbuf;
320 GError *error = NULL;
321 char *filename;
322
323 if (!window)
324 {
325 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
326 gtk_window_set_screen (GTK_WINDOW (window),
327 gtk_widget_get_screen (do_widget));
328 gtk_window_set_title (GTK_WINDOW (window), "Images");
329
330 g_signal_connect (window, "destroy",
331 G_CALLBACK (gtk_widget_destroyed), &window);
332 g_signal_connect (window, "destroy",
333 G_CALLBACK (cleanup_callback), NULL);
334
335 gtk_container_set_border_width (GTK_CONTAINER (window), 8);
336
337 vbox = gtk_vbox_new (FALSE, 8);
338 gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
339 gtk_container_add (GTK_CONTAINER (window), vbox);
340
341 label = gtk_label_new (NULL);
342 gtk_label_set_markup (GTK_LABEL (label),
343 "<u>Image loaded from a file</u>");
344 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
345
346 frame = gtk_frame_new (NULL);
347 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
348 /* The alignment keeps the frame from growing when users resize
349 * the window
350 */
351 align = gtk_alignment_new (0.5, 0.5, 0, 0);
352 gtk_container_add (GTK_CONTAINER (align), frame);
353 gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0);
354
355 /* demo_find_file() looks in the current directory first,
356 * so you can run gtk-demo without installing GTK, then looks
357 * in the location where the file is installed.
358 */
359 pixbuf = NULL;
360 filename = demo_find_file ("gtk-logo-rgb.gif", &error);
361 if (filename)
362 {
363 pixbuf = gdk_pixbuf_new_from_file (filename, &error);
364 g_free (filename);
365 }
366
367 if (error)
368 {
369 /* This code shows off error handling. You can just use
370 * gtk_image_new_from_file() instead if you don't want to report
371 * errors to the user. If the file doesn't load when using
372 * gtk_image_new_from_file(), a "missing image" icon will
373 * be displayed instead.
374 */
375 GtkWidget *dialog;
376
377 dialog = gtk_message_dialog_new (GTK_WINDOW (window),
378 GTK_DIALOG_DESTROY_WITH_PARENT,
379 GTK_MESSAGE_ERROR,
380 GTK_BUTTONS_CLOSE,
381 "Unable to open image file 'gtk-logo-rgb.gif': %s",
382 error->message);
383 g_error_free (error);
384
385 g_signal_connect (dialog, "response",
386 G_CALLBACK (gtk_widget_destroy), NULL);
387
388 gtk_widget_show (dialog);
389 }
390
391 image = gtk_image_new_from_pixbuf (pixbuf);
392
393 gtk_container_add (GTK_CONTAINER (frame), image);
394
395
396 /* Animation */
397
398 label = gtk_label_new (NULL);
399 gtk_label_set_markup (GTK_LABEL (label),
400 "<u>Animation loaded from a file</u>");
401 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
402
403 frame = gtk_frame_new (NULL);
404 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
405 /* The alignment keeps the frame from growing when users resize
406 * the window
407 */
408 align = gtk_alignment_new (0.5, 0.5, 0, 0);
409 gtk_container_add (GTK_CONTAINER (align), frame);
410 gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0);
411
412 filename = demo_find_file ("floppybuddy.gif", NULL);
413 image = gtk_image_new_from_file (filename);
414 g_free (filename);
415
416 gtk_container_add (GTK_CONTAINER (frame), image);
417
418
419 /* Progressive */
420
421
422 label = gtk_label_new (NULL);
423 gtk_label_set_markup (GTK_LABEL (label),
424 "<u>Progressive image loading</u>");
425 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
426
427 frame = gtk_frame_new (NULL);
428 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
429 /* The alignment keeps the frame from growing when users resize
430 * the window
431 */
432 align = gtk_alignment_new (0.5, 0.5, 0, 0);
433 gtk_container_add (GTK_CONTAINER (align), frame);
434 gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0);
435
436 /* Create an empty image for now; the progressive loader
437 * will create the pixbuf and fill it in.
438 */
439 image = gtk_image_new_from_pixbuf (NULL);
440 gtk_container_add (GTK_CONTAINER (frame), image);
441
442 start_progressive_loading (image);
443
444 /* Sensitivity control */
445 button = gtk_toggle_button_new_with_mnemonic ("_Insensitive");
446 gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
447
448 g_signal_connect (button, "toggled",
449 G_CALLBACK (toggle_sensitivity_callback),
450 vbox);
451 }
452
453 if (!gtk_widget_get_visible (window))
454 {
455 gtk_widget_show_all (window);
456 }
457 else
458 {
459 gtk_widget_destroy (window);
460 window = NULL;
461 }
462
463 return window;
464 }
465