]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/gdk-pixbuf-io.c
gtk/gtkmain.c Use the new
[~andy/gtk] / gdk-pixbuf / gdk-pixbuf-io.c
1 /* GdkPixbuf library - Main loading interface.
2  *
3  * Copyright (C) 1999 The Free Software Foundation
4  *
5  * Authors: Miguel de Icaza <miguel@gnu.org>
6  *          Federico Mena-Quintero <federico@gimp.org>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #include <config.h>
25 #include <string.h>
26 #include <glib.h>
27 #include <errno.h>
28 #include "gdk-pixbuf-private.h"
29 #include "gdk-pixbuf-io.h"
30
31 #ifdef G_OS_WIN32
32 #define STRICT
33 #include <windows.h>
34 #endif
35
36 \f
37
38 static gboolean
39 pixbuf_check_png (guchar *buffer, int size)
40 {
41         if (size < 28)
42                 return FALSE;
43
44         if (buffer [0] != 0x89 ||
45             buffer [1] != 'P' ||
46             buffer [2] != 'N' ||
47             buffer [3] != 'G' ||
48             buffer [4] != 0x0d ||
49             buffer [5] != 0x0a ||
50             buffer [6] != 0x1a ||
51             buffer [7] != 0x0a)
52                 return FALSE;
53
54         return TRUE;
55 }
56
57 static gboolean
58 pixbuf_check_jpeg (guchar *buffer, int size)
59 {
60         if (size < 10)
61                 return FALSE;
62
63         if (buffer [0] != 0xff || buffer [1] != 0xd8)
64                 return FALSE;
65
66         return TRUE;
67 }
68
69 static gboolean
70 pixbuf_check_tiff (guchar *buffer, int size)
71 {
72         if (size < 10)
73                 return FALSE;
74
75         if (buffer [0] == 'M' &&
76             buffer [1] == 'M' &&
77             buffer [2] == 0   &&
78             buffer [3] == 0x2a)
79                 return TRUE;
80
81         if (buffer [0] == 'I' &&
82             buffer [1] == 'I' &&
83             buffer [2] == 0x2a &&
84             buffer [3] == 0)
85                 return TRUE;
86
87         return FALSE;
88 }
89
90 static gboolean
91 pixbuf_check_gif (guchar *buffer, int size)
92 {
93         if (size < 20)
94                 return FALSE;
95
96         if (strncmp (buffer, "GIF8", 4) == 0)
97                 return TRUE;
98
99         return FALSE;
100 }
101
102 static gboolean
103 pixbuf_check_xpm (guchar *buffer, int size)
104 {
105         if (size < 20)
106                 return FALSE;
107
108         if (strncmp (buffer, "/* XPM */", 9) == 0)
109                 return TRUE;
110
111         return FALSE;
112 }
113
114 static gboolean
115 pixbuf_check_pnm (guchar *buffer, int size)
116 {
117         if (size < 20)
118                 return FALSE;
119
120         if (buffer [0] == 'P') {
121                 if (buffer [1] == '1' ||
122                     buffer [1] == '2' ||
123                     buffer [1] == '3' ||
124                     buffer [1] == '4' ||
125                     buffer [1] == '5' ||
126                     buffer [1] == '6')
127                         return TRUE;
128         }
129         return FALSE;
130 }
131 static gboolean
132 pixbuf_check_sunras (guchar *buffer, int size)
133 {
134         if (size < 32)
135                 return FALSE;
136
137         if (buffer [0] != 0x59 ||
138             buffer [1] != 0xA6 ||
139             buffer [2] != 0x6A ||
140             buffer [3] != 0x95)
141                 return FALSE;
142
143         return TRUE;
144 }
145
146 static gboolean
147 pixbuf_check_ico (guchar *buffer, int size)
148 {
149         /* Note that this may cause false positives, but .ico's don't
150            have a magic number.*/
151         if (size < 6)
152                 return FALSE;
153         if (buffer [0] != 0x0 ||
154             buffer [1] != 0x0 ||
155             ((buffer [2] != 0x1)&&(buffer[2]!=0x2)) ||
156             buffer [3] != 0x0 ||
157             buffer [5] != 0x0 )
158                 return FALSE;
159
160         return TRUE;
161 }
162
163
164 static gboolean
165 pixbuf_check_bmp (guchar *buffer, int size)
166 {
167         if (size < 20)
168                 return FALSE;
169
170         if (buffer [0] != 'B' || buffer [1] != 'M')
171                 return FALSE;
172
173         return TRUE;
174 }
175
176 static gboolean
177 pixbuf_check_wbmp (guchar *buffer, int size)
178 {
179   if(size < 10) /* at least */
180     return FALSE;
181
182   if(buffer[0] == '\0' /* We only handle type 0 wbmp's for now */)
183     return TRUE;
184
185   return FALSE;
186 }
187
188 static GdkPixbufModule file_formats [] = {
189         { "png",  pixbuf_check_png, NULL,  NULL, NULL, NULL, NULL, NULL, NULL, },
190         { "jpeg", pixbuf_check_jpeg, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
191         { "tiff", pixbuf_check_tiff, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
192         { "gif",  pixbuf_check_gif, NULL,  NULL, NULL, NULL, NULL, NULL, NULL },
193 #define XPM_FILE_FORMAT_INDEX 4
194         { "xpm",  pixbuf_check_xpm, NULL,  NULL, NULL, NULL, NULL, NULL, NULL },
195         { "pnm",  pixbuf_check_pnm, NULL,  NULL, NULL, NULL, NULL, NULL, NULL },
196         { "ras",  pixbuf_check_sunras, NULL,  NULL, NULL, NULL, NULL, NULL, NULL },
197         { "ico",  pixbuf_check_ico, NULL,  NULL, NULL, NULL, NULL, NULL, NULL },
198         { "bmp",  pixbuf_check_bmp, NULL,  NULL, NULL, NULL, NULL, NULL, NULL },
199         { "wbmp", pixbuf_check_wbmp, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
200         { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
201 };
202
203 #ifdef USE_GMODULE 
204 static gboolean
205 pixbuf_module_symbol (GModule *module, const char *module_name, const char *symbol_name, gpointer *symbol)
206 {
207         char *full_symbol_name = g_strconcat ("gdk_pixbuf__", module_name, "_", symbol_name, NULL);
208         gboolean return_value;
209
210         return_value = g_module_symbol (module, full_symbol_name, symbol);
211         g_free (full_symbol_name);
212         
213         return return_value;
214 }
215
216 #ifdef G_OS_WIN32
217
218 static char *
219 get_libdir (void)
220 {
221   static char *libdir = NULL;
222
223   if (libdir == NULL)
224     libdir = g_win32_get_package_installation_subdirectory
225       (GETTEXT_PACKAGE,
226        g_strdup_printf ("gdk_pixbug-%d.%d.dll",
227                         GDK_PIXBUF_MAJOR, GDK_PIXBUF_MINOR),
228        "loaders");
229
230   return libdir;
231 }
232
233 #define PIXBUF_LIBDIR get_libdir ()
234
235 #endif
236
237 /* actually load the image handler - gdk_pixbuf_get_module only get a */
238 /* reference to the module to load, it doesn't actually load it       */
239 /* perhaps these actions should be combined in one function           */
240 gboolean
241 gdk_pixbuf_load_module (GdkPixbufModule *image_module,
242                         GError         **error)
243 {
244         char *module_name;
245         char *path;
246         GModule *module;
247         gpointer load_sym;
248         gpointer save_sym;
249         char *name;
250         
251         g_return_val_if_fail (image_module->module == NULL, FALSE);
252
253         name = image_module->module_name;
254         
255         module_name = g_strconcat ("pixbufloader-", name, NULL);
256         path = g_module_build_path (PIXBUF_LIBDIR, module_name);
257
258         module = g_module_open (path, G_MODULE_BIND_LAZY);
259         if (!module) {
260                 /* Debug feature, check in GDK_PIXBUF_MODULEDIR, or working directory */
261                 char *dir = g_getenv ("GDK_PIXBUF_MODULEDIR");
262                 if (!dir)
263                         dir = "";
264           
265                 g_free (path);
266                 path = g_module_build_path (dir, module_name);
267                 module = g_module_open (path, G_MODULE_BIND_LAZY);
268
269                 if (!module) {
270                         g_set_error (error,
271                                      GDK_PIXBUF_ERROR,
272                                      GDK_PIXBUF_ERROR_FAILED,
273                                      _("Unable to load image-loading module: %s: %s"),
274                                      path, g_module_error ());
275                         g_free (module_name);
276                         g_free (path);
277                         return FALSE;
278                 }
279                 g_free (path);
280         } else {
281                 g_free (path);
282         }
283
284         g_free (module_name);
285
286         image_module->module = module;
287
288         if (pixbuf_module_symbol (module, name, "image_load", &load_sym))
289                 image_module->load = load_sym;
290
291         if (pixbuf_module_symbol (module, name, "image_load_xpm_data", &load_sym))
292                 image_module->load_xpm_data = load_sym;
293
294         if (pixbuf_module_symbol (module, name, "image_begin_load", &load_sym))
295                 image_module->begin_load = load_sym;
296
297         if (pixbuf_module_symbol (module, name, "image_stop_load", &load_sym))
298                 image_module->stop_load = load_sym;
299
300         if (pixbuf_module_symbol (module, name, "image_load_increment", &load_sym))
301                 image_module->load_increment = load_sym;
302
303         if (pixbuf_module_symbol (module, name, "image_load_animation", &load_sym))
304                 image_module->load_animation = load_sym;
305
306         if (pixbuf_module_symbol (module, name, "image_save", &save_sym))
307           image_module->save = save_sym;
308
309         return TRUE;
310 }
311 #else
312
313 #define mname(type,fn) gdk_pixbuf__ ## type ## _image_ ##fn
314 #define m_load(type)  extern GdkPixbuf * mname(type,load) (FILE *f, GError **error);
315 #define m_load_xpm_data(type)  extern GdkPixbuf * mname(type,load_xpm_data) (const char **data);
316 #define m_begin_load(type)  \
317    extern gpointer mname(type,begin_load) (ModulePreparedNotifyFunc prepare_func, \
318                                  ModuleUpdatedNotifyFunc update_func, \
319                                  ModuleFrameDoneNotifyFunc frame_done_func,\
320                                  ModuleAnimationDoneNotifyFunc anim_done_func,\
321                                  gpointer user_data,\
322                                  GError **error);
323 #define m_stop_load(type)  extern void mname(type,stop_load) (gpointer context);
324 #define m_load_increment(type)  extern gboolean mname(type,load_increment) (gpointer context, const guchar *buf, guint size, GError **error);
325 #define m_load_animation(type)  extern GdkPixbufAnimation * mname(type,load_animation) (FILE *f, GError **error);
326 #define m_save(type) \
327    extern gboolean mname(type,save) (FILE          *f, \
328                                      GdkPixbuf     *pixbuf, \
329                                      gchar        **keys, \
330                                      gchar        **values, \
331                                      GError       **error);
332
333 /* PNG */
334 m_load (png);
335 m_begin_load (png);
336 m_load_increment (png);
337 m_stop_load (png);
338 m_save (png);
339 /* BMP */
340 m_load (bmp);
341 m_begin_load (bmp);
342 m_load_increment (bmp);
343 m_stop_load (bmp);
344 /* WBMP */
345 m_load (wbmp);
346 m_begin_load (wbmp);
347 m_load_increment (wbmp);
348 m_stop_load (wbmp);
349 /* GIF */
350 m_load (gif);
351 m_begin_load (gif);
352 m_load_increment (gif);
353 m_stop_load (gif);
354 m_load_animation (gif);
355 /* ICO */
356 m_load (ico);
357 m_begin_load (ico);
358 m_load_increment (ico);
359 m_stop_load (ico);
360 /* JPEG */
361 m_load (jpeg);
362 m_begin_load (jpeg);
363 m_load_increment (jpeg);
364 m_stop_load (jpeg);
365 m_save (jpeg);
366 /* PNM */
367 m_load (pnm);
368 m_begin_load (pnm);
369 m_load_increment (pnm);
370 m_stop_load (pnm);
371 /* RAS */
372 m_load (ras);
373 m_begin_load (ras);
374 m_load_increment (ras);
375 m_stop_load (ras);
376 /* TIFF */
377 m_load (tiff);
378 m_begin_load (tiff);
379 m_load_increment (tiff);
380 m_stop_load (tiff);
381 /* XPM */
382 m_load (xpm);
383 m_load_xpm_data (xpm);
384
385 gboolean
386 gdk_pixbuf_load_module (GdkPixbufModule *image_module,
387                         GError         **error)
388 {
389         image_module->module = (void *) 1;
390
391 #ifdef INCLUDE_png      
392         if (strcmp (image_module->module_name, "png") == 0){
393                 image_module->load           = mname (png,load);
394                 image_module->begin_load     = mname (png,begin_load);
395                 image_module->load_increment = mname (png,load_increment);
396                 image_module->stop_load      = mname (png,stop_load);
397                 image_module->save           = mname (png,save);
398                 return TRUE;
399         }
400 #endif
401
402 #ifdef INCLUDE_bmp      
403         if (strcmp (image_module->module_name, "bmp") == 0){
404                 image_module->load           = mname (bmp,load);
405                 image_module->begin_load     = mname (bmp,begin_load);
406                 image_module->load_increment = mname (bmp,load_increment);
407                 image_module->stop_load      = mname (bmp,stop_load);
408                 return TRUE;
409         }
410 #endif
411
412 #ifdef INCLUDE_wbmp
413         if (strcmp (image_module->module_name, "wbmp") == 0){
414                 image_module->load           = mname (wbmp,load);
415                 image_module->begin_load     = mname (wbmp,begin_load);
416                 image_module->load_increment = mname (wbmp,load_increment);
417                 image_module->stop_load      = mname (wbmp,stop_load);
418                 return TRUE;
419         }
420 #endif
421
422 #ifdef INCLUDE_gif
423         if (strcmp (image_module->module_name, "gif") == 0){
424                 image_module->load           = mname (gif,load);
425                 image_module->begin_load     = mname (gif,begin_load);
426                 image_module->load_increment = mname (gif,load_increment);
427                 image_module->stop_load      = mname (gif,stop_load);
428                 image_module->load_animation = mname (gif,load_animation);
429                 return TRUE;
430         }
431 #endif
432
433 #ifdef INCLUDE_ico
434         if (strcmp (image_module->module_name, "ico") == 0){
435                 image_module->load           = mname (ico,load);
436                 image_module->begin_load     = mname (ico,begin_load);
437                 image_module->load_increment = mname (ico,load_increment);
438                 image_module->stop_load      = mname (ico,stop_load);
439                 return TRUE;
440         }
441 #endif
442
443 #ifdef INCLUDE_jpeg
444         if (strcmp (image_module->module_name, "jpeg") == 0){
445                 image_module->load           = mname (jpeg,load);
446                 image_module->begin_load     = mname (jpeg,begin_load);
447                 image_module->load_increment = mname (jpeg,load_increment);
448                 image_module->stop_load      = mname (jpeg,stop_load);
449                 image_module->save           = mname (jpeg,save);
450                 return TRUE;
451         }
452 #endif
453
454 #ifdef INCLUDE_pnm
455         if (strcmp (image_module->module_name, "pnm") == 0){
456                 image_module->load           = mname (pnm,load);
457                 image_module->begin_load     = mname (pnm,begin_load);
458                 image_module->load_increment = mname (pnm,load_increment);
459                 image_module->stop_load      = mname (pnm,stop_load);
460                 return TRUE;
461         }
462 #endif
463
464 #ifdef INCLUDE_ras
465         if (strcmp (image_module->module_name, "ras") == 0){
466                 image_module->load           = mname (ras,load);
467                 image_module->begin_load     = mname (ras,begin_load);
468                 image_module->load_increment = mname (ras,load_increment);
469                 image_module->stop_load      = mname (ras,stop_load);
470                 return TRUE;
471         }
472 #endif
473
474 #ifdef INCLUDE_tiff
475         if (strcmp (image_module->module_name, "tiff") == 0){
476                 image_module->load           = mname (tiff,load);
477                 image_module->begin_load     = mname (tiff,begin_load);
478                 image_module->load_increment = mname (tiff,load_increment);
479                 image_module->stop_load      = mname (tiff,stop_load);
480                 return TRUE;
481         }
482 #endif
483
484 #ifdef INCLUDE_xpm
485         if (strcmp (image_module->module_name, "xpm") == 0){
486                 image_module->load           = mname (xpm,load);
487                 image_module->load_xpm_data  = mname (xpm,load_xpm_data);
488                 return TRUE;
489         }
490 #endif
491
492         g_set_error (error,
493                      GDK_PIXBUF_ERROR,
494                      GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
495                      _("Image type '%s' is not supported"),
496                      image_module->module_name);
497
498         return FALSE;
499 }
500
501
502 #endif
503
504 \f
505
506 GdkPixbufModule *
507 gdk_pixbuf_get_named_module (const char *name,
508                              GError **error)
509 {
510         int i;
511
512         for (i = 0; file_formats [i].module_name; i++) {
513                 if (!strcmp(name, file_formats[i].module_name))
514                         return &(file_formats[i]);
515         }
516
517         g_set_error (error,
518                      GDK_PIXBUF_ERROR,
519                      GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
520                      _("Image type '%s' is not supported"),
521                      name);
522         
523         return NULL;
524 }
525
526 GdkPixbufModule *
527 gdk_pixbuf_get_module (guchar *buffer, guint size,
528                        const gchar *filename,
529                        GError **error)
530 {
531         int i;
532
533         for (i = 0; file_formats [i].module_name; i++) {
534                 if ((* file_formats [i].format_check) (buffer, size))
535                         return &(file_formats[i]);
536         }
537
538         if (filename)
539                 g_set_error (error,
540                              GDK_PIXBUF_ERROR,
541                              GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
542                              _("Couldn't recognize the image file format for file '%s'"),
543                              filename);        
544         else
545                 g_set_error (error,
546                              GDK_PIXBUF_ERROR,
547                              GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
548                              _("Unrecognized image file format"));
549
550         
551         return NULL;
552 }
553
554 /**
555  * gdk_pixbuf_new_from_file:
556  * @filename: Name of file to load.
557  * @error: Return location for an error
558  *
559  * Creates a new pixbuf by loading an image from a file.  The file format is
560  * detected automatically. If NULL is returned, then @error will be set.
561  * Possible errors are in the #GDK_PIXBUF_ERROR and #G_FILE_ERROR domains.
562  *
563  * Return value: A newly-created pixbuf with a reference count of 1, or NULL if
564  * any of several error conditions occurred:  the file could not be opened,
565  * there was no loader for the file's format, there was not enough memory to
566  * allocate the image buffer, or the image file contained invalid data.
567  **/
568 GdkPixbuf *
569 gdk_pixbuf_new_from_file (const char *filename,
570                           GError    **error)
571 {
572         GdkPixbuf *pixbuf;
573         int size;
574         FILE *f;
575         guchar buffer [128];
576         GdkPixbufModule *image_module;
577
578         g_return_val_if_fail (filename != NULL, NULL);
579
580         f = fopen (filename, "rb");
581         if (!f) {
582                 g_set_error (error,
583                              G_FILE_ERROR,
584                              g_file_error_from_errno (errno),
585                              _("Failed to open file '%s': %s"),
586                              filename, g_strerror (errno));
587                 return NULL;
588         }
589
590         size = fread (&buffer, 1, sizeof (buffer), f);
591         if (size == 0) {
592                 g_set_error (error,
593                              GDK_PIXBUF_ERROR,
594                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
595                              _("Image file '%s' contains no data"),
596                              filename);
597                 
598                 fclose (f);
599                 return NULL;
600         }
601
602         image_module = gdk_pixbuf_get_module (buffer, size, filename, error);
603         if (image_module == NULL) {
604                 fclose (f);
605                 return NULL;
606         }
607
608         if (image_module->module == NULL)
609                 if (!gdk_pixbuf_load_module (image_module, error)) {
610                         fclose (f);
611                         return NULL;
612                 }
613
614         if (image_module->load == NULL) {
615                 g_set_error (error,
616                              GDK_PIXBUF_ERROR,
617                              GDK_PIXBUF_ERROR_UNSUPPORTED_OPERATION,
618                              _("Don't know how to load the image in file '%s'"),
619                              filename);
620                 
621                 fclose (f);
622                 return NULL;
623         }
624
625         fseek (f, 0, SEEK_SET);
626         pixbuf = (* image_module->load) (f, error);
627         fclose (f);
628
629         if (pixbuf == NULL && error != NULL && *error == NULL) {
630                 /* I don't trust these crufty longjmp()'ing image libs
631                  * to maintain proper error invariants, and I don't
632                  * want user code to segfault as a result. We need to maintain
633                  * the invariant that error gets set if NULL is returned.
634                  */
635                 
636                 g_warning ("Bug! gdk-pixbuf loader '%s' didn't set an error on failure.", image_module->module_name);
637                 g_set_error (error,
638                              GDK_PIXBUF_ERROR,
639                              GDK_PIXBUF_ERROR_FAILED,
640                              _("Failed to load image '%s': reason not known, probably a corrupt image file"),
641                              filename);
642                 
643         } else if (error != NULL && *error != NULL) {
644
645           /* Add the filename to the error message */
646           GError *e = *error;
647           gchar *old;
648           
649           old = e->message;
650
651           e->message = g_strdup_printf (_("Failed to load image '%s': %s"),
652                                         filename, old);
653
654           g_free (old);
655         }
656                 
657         return pixbuf;
658 }
659
660 /**
661  * gdk_pixbuf_new_from_xpm_data:
662  * @data: Pointer to inline XPM data.
663  *
664  * Creates a new pixbuf by parsing XPM data in memory.  This data is commonly
665  * the result of including an XPM file into a program's C source.
666  *
667  * Return value: A newly-created pixbuf with a reference count of 1.
668  **/
669 GdkPixbuf *
670 gdk_pixbuf_new_from_xpm_data (const char **data)
671 {
672         GdkPixbuf *(* load_xpm_data) (const char **data);
673         GdkPixbuf *pixbuf;
674
675         if (file_formats[XPM_FILE_FORMAT_INDEX].module == NULL)
676                 gdk_pixbuf_load_module (&file_formats[XPM_FILE_FORMAT_INDEX], NULL);
677
678         if (file_formats[XPM_FILE_FORMAT_INDEX].module == NULL) {
679                 g_warning ("Can't find gdk-pixbuf module for parsing inline XPM data");
680                 return NULL;
681         } else if (file_formats[XPM_FILE_FORMAT_INDEX].load_xpm_data == NULL) {
682                 g_warning ("gdk-pixbuf XPM module lacks XPM data capability");
683                 return NULL;
684         } else
685                 load_xpm_data = file_formats[XPM_FILE_FORMAT_INDEX].load_xpm_data;
686
687         pixbuf = (* load_xpm_data) (data);
688         return pixbuf;
689 }
690
691 static void
692 collect_save_options (va_list   opts,
693                       gchar  ***keys,
694                       gchar  ***vals)
695 {
696   gchar *key;
697   gchar *val;
698   gchar *next;
699   gint count;
700
701   count = 0;
702   *keys = NULL;
703   *vals = NULL;
704   
705   next = va_arg (opts, gchar*);
706   while (next)
707     {
708       key = next;
709       val = va_arg (opts, gchar*);
710
711       ++count;
712
713       /* woo, slow */
714       *keys = g_realloc (*keys, sizeof(gchar*) * (count + 1));
715       *vals = g_realloc (*vals, sizeof(gchar*) * (count + 1));
716       
717       (*keys)[count-1] = g_strdup (key);
718       (*vals)[count-1] = g_strdup (val);
719
720       (*keys)[count] = NULL;
721       (*vals)[count] = NULL;
722       
723       next = va_arg (opts, gchar*);
724     }
725 }
726
727 static gboolean
728 gdk_pixbuf_real_save (GdkPixbuf     *pixbuf, 
729                       FILE          *filehandle, 
730                       const char    *type, 
731                       gchar        **keys,
732                       gchar        **values,
733                       GError       **error)
734 {
735        GdkPixbufModule *image_module = NULL;       
736
737        image_module = gdk_pixbuf_get_named_module (type, error);
738
739        if (image_module == NULL)
740                return FALSE;
741        
742        if (image_module->module == NULL)
743                if (!gdk_pixbuf_load_module (image_module, error))
744                        return FALSE;
745
746        if (image_module->save == NULL) {
747                g_set_error (error,
748                             GDK_PIXBUF_ERROR,
749                             GDK_PIXBUF_ERROR_UNSUPPORTED_OPERATION,
750                             _("This build of gdk-pixbuf does not support saving the image format: %s"),
751                             type);
752                return FALSE;
753        }
754                
755        return (* image_module->save) (filehandle, pixbuf,
756                                       keys, values,
757                                       error);
758 }
759
760  
761 /**
762  * gdk_pixbuf_save:
763  * @pixbuf: pointer to GdkPixbuf.
764  * @filename: Name of file to save.
765  * @type: name of file format.
766  * @error: return location for error, or NULL
767  * @Varargs: list of key-value save options
768  *
769  * Saves pixbuf to a file in @type, which is currently "jpeg" or
770  * "png".  If @error is set, FALSE will be returned. Possible errors include those
771  * in the #GDK_PIXBUF_ERROR domain and those in the #G_FILE_ERROR domain.
772  *
773  * The variable argument list should be NULL-terminated; if not empty,
774  * it should contain pairs of strings that modify the save
775  * parameters. For example:
776  *
777  * <programlisting>
778  * gdk_pixbuf_save (pixbuf, handle, "jpeg", &error,
779  *                  "quality", "100", NULL);
780  * </programlisting>
781  *
782  * The only save parameter that currently exists is the "quality" field
783  * for JPEG images; its value should be in the range [0,100].
784  *
785  * Return value: whether an error was set
786  **/
787
788 gboolean
789 gdk_pixbuf_save (GdkPixbuf  *pixbuf, 
790                  const char *filename, 
791                  const char *type, 
792                  GError    **error,
793                  ...)
794 {
795         gchar **keys = NULL;
796         gchar **values = NULL;
797         va_list args;
798         gboolean result;
799         
800         va_start (args, error);
801         
802         collect_save_options (args, &keys, &values);
803         
804         va_end (args);
805
806         result = gdk_pixbuf_savev (pixbuf, filename, type,
807                                    keys, values,
808                                    error);
809
810         g_strfreev (keys);
811         g_strfreev (values);
812
813         return result;
814 }
815
816 /**
817  * gdk_pixbuf_savev:
818  * @pixbuf: pointer to GdkPixbuf.
819  * @filename: Name of file to save.
820  * @type: name of file format.
821  * @option_keys: name of options to set, NULL-terminated
822  * @option_values: values for named options
823  * @error: return location for error, or NULL
824  *
825  * Saves pixbuf to a file in @type, which is currently "jpeg" or "png".
826  * If @error is set, FALSE will be returned. See gdk_pixbuf_save () for more
827  * details.
828  *
829  * Return value: whether an error was set
830  **/
831
832 gboolean
833 gdk_pixbuf_savev (GdkPixbuf  *pixbuf, 
834                   const char *filename, 
835                   const char *type,
836                   char      **option_keys,
837                   char      **option_values,
838                   GError    **error)
839 {
840         FILE *f = NULL;
841         gboolean result;
842         
843        
844         g_return_val_if_fail (filename != NULL, FALSE);
845         g_return_val_if_fail (type != NULL, FALSE);
846        
847         f = fopen (filename, "wb");
848         
849         if (f == NULL) {
850                 g_set_error (error,
851                              G_FILE_ERROR,
852                              g_file_error_from_errno (errno),
853                              _("Failed to open '%s' for writing: %s"),
854                              filename, g_strerror (errno));
855                 return FALSE;
856         }
857
858        
859        result = gdk_pixbuf_real_save (pixbuf, f, type,
860                                       option_keys, option_values,
861                                       error);
862        
863        
864        if (!result) {
865                g_return_val_if_fail (error == NULL || *error != NULL, FALSE);
866                fclose (f);
867                return FALSE;
868        }
869
870        if (fclose (f) < 0) {
871                g_set_error (error,
872                             G_FILE_ERROR,
873                             g_file_error_from_errno (errno),
874                             _("Failed to close '%s' while writing image, all data may not have been saved: %s"),
875                             filename, g_strerror (errno));
876                return FALSE;
877        }
878        
879        return TRUE;
880 }