pixbuf saving



Hi,

I went back to the list archives and got David's pixbuf saving patch
and revised it for GTK+ HEAD (not committed yet).

I made some small changes:

 - used the key-value varargs list instead of a struct for the 
   save options, Owen requested this (the struct wouldn't work
   really - it at minimum would need to come with a bitfield
   in the way GdkWindowAttributes does)

 - added GError support, though I didn't yet sort out how to get
   from the setjmp/longjmp mess to actually setting the GError
   (I want to do this when I add error handling for loading, 
   see below)

 - reformatting/reindentation

 - renamed save_to_file to be save_to_stream() since it saves to 
   a FILE*

That's it I think, patch appended.

Federico, I believe this change is unsuitable for gdk-pixbuf 1.0,
since it involves API additions and requires GError.

After merging the bugfixes to gdk-pixbuf 1.0 into HEAD, I want to add
GError support to the loader; this change is definitely for HEAD only,
because it breaks the new_from_* APIs.

If there are no objections I'll commit this shortly.

Havoc

Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/gtk+/ChangeLog,v
retrieving revision 1.1416
diff -u -u -r1.1416 ChangeLog
--- ChangeLog	2000/10/05 01:04:57	1.1416
+++ ChangeLog	2000/10/06 00:34:00
@@ -1,3 +1,9 @@
+2000-10-05  Havoc Pennington  <hp redhat com>
+
+	* demos/testpixbuf-save.c: add pixbuf save test
+
+	* demos/Makefile.am: add testpixbuf-save.c
+
 2000-10-04    <jrb redhat com>
 
 	* gtk/gtk{tree,cell}?*.[ch]: Checked in initial draft of the new
Index: demos/Makefile.am
===================================================================
RCS file: /cvs/gnome/gtk+/demos/Makefile.am,v
retrieving revision 1.6
diff -u -u -r1.6 Makefile.am
--- demos/Makefile.am	2000/10/04 16:39:43	1.6
+++ demos/Makefile.am	2000/10/06 00:34:00
@@ -34,6 +34,7 @@
 	testpixbuf 		\
 	testpixbuf-drawable 	\
 	testanimation 		\
+	testpixbuf-save		\
 	testpixbuf-scale 	\
 	pixbuf-demo
 
@@ -44,18 +45,21 @@
 
 testpixbuf_DEPENDENCIES = $(DEPS)
 testpixbuf_drawable_DEPENDENCIES = $(DEPS)
+testpixbuf_save_DEPENDENCIES = $(DEPS)
 testpixbuf_scale_DEPENDENCIES = $(DEPS)
 testanimation_DEPENDENCIES = $(DEPS)
 pixbuf_demo_DEPENDENCIES = $(DEPS)
 
 testpixbuf_LDADD = $(LDADDS)
 testpixbuf_drawable_LDADD = $(LDADDS)
+testpixbuf_save_LDADD = $(LDADDS)
 testpixbuf_scale_LDADD = $(LDADDS)
 testanimation_LDADD = $(LDADDS)
 pixbuf_demo_LDADD = $(LDADDS)
 
 testpixbuf_SOURCES = testpixbuf.c pixbuf-init.c
 testpixbuf_drawable_SOURCES = testpixbuf-drawable.c pixbuf-init.c
+testpixbuf_save_SOURCES = testpixbuf-save.c
 testpixbuf_scale_SOURCES = testpixbuf-scale.c pixbuf-init.c
 testanimation_SOURCES = testanimation.c pixbuf-init.c
 pixbuf_demo_SOURCES = pixbuf-demo.c pixbuf-init.c
Index: demos/testpixbuf-save.c
===================================================================
RCS file: testpixbuf-save.c
diff -N testpixbuf-save.c
--- /dev/null	Tue May  5 16:32:27 1998
+++ testpixbuf-save.c	Thu Oct  5 20:34:00 2000
@@ -0,0 +1,153 @@
+
+#include <config.h>
+/* if building outside GTK, remove /x11 part */
+#include <gdk/x11/gdkx.h>
+#include <gtk/gtk.h>
+
+
+void
+keypress_check (GtkWidget *widget, GdkEventKey *evt, gpointer data)
+{
+        GdkPixbuf *pixbuf;
+        GtkDrawingArea *da = (GtkDrawingArea*)data;
+        GError *err = NULL;
+        
+        pixbuf = (GdkPixbuf *) gtk_object_get_data (GTK_OBJECT (da), "pixbuf");
+
+        if (evt->keyval == 'q')
+                gtk_main_quit ();
+        if (evt->keyval == 's') {
+                if (pixbuf == NULL) {
+                        fprintf (stderr, "PIXBUF NULL\n");
+                        return;
+                }	
+
+                if (!gdk_pixbuf_save (pixbuf, "foo.jpg", "jpeg",
+                                      &err,
+                                      "quality", "100",
+                                      NULL)) {
+                        fprintf (stderr, "%s", err->message);
+                        g_error_free (err);
+                }
+                
+        } else if (evt->keyval == 'p') {
+                if (pixbuf == NULL) {
+                        fprintf (stderr, "PIXBUF NULL\n");
+                        return;
+                }
+
+                if (!gdk_pixbuf_save (pixbuf, "foo.png", "png", &err, NULL)) {
+                        fprintf (stderr, "%s", err->message);
+                        g_error_free (err);
+                }
+        }
+}
+
+
+int
+close_app (GtkWidget *widget, gpointer data)
+{
+        gtk_main_quit ();
+        return TRUE;
+}
+
+int
+expose_cb (GtkWidget *drawing_area, GdkEventExpose *evt, gpointer data)
+{
+        GdkPixbuf *pixbuf;
+         
+        pixbuf = (GdkPixbuf *) gtk_object_get_data (GTK_OBJECT (drawing_area),
+                                                    "pixbuf");
+        if (gdk_pixbuf_get_has_alpha (pixbuf)) {
+                gdk_draw_rgb_32_image (drawing_area->window,
+                                       drawing_area->style->black_gc,
+                                       evt->area.x, evt->area.y,
+                                       evt->area.width,
+                                       evt->area.height,
+                                       GDK_RGB_DITHER_MAX,
+                                       gdk_pixbuf_get_pixels (pixbuf) +
+                                       (evt->area.y * gdk_pixbuf_get_rowstride (pixbuf)) +
+                                       (evt->area.x * gdk_pixbuf_get_n_channels (pixbuf)),
+                                       gdk_pixbuf_get_rowstride (pixbuf));
+        } else {
+                gdk_draw_rgb_image (drawing_area->window, 
+                                    drawing_area->style->black_gc, 
+                                    evt->area.x, evt->area.y,
+                                    evt->area.width,
+                                    evt->area.height,  
+                                    GDK_RGB_DITHER_NORMAL,
+                                    gdk_pixbuf_get_pixels (pixbuf) +
+                                    (evt->area.y * gdk_pixbuf_get_rowstride (pixbuf)) +
+                                    (evt->area.x * gdk_pixbuf_get_n_channels (pixbuf)),
+                                    gdk_pixbuf_get_rowstride (pixbuf));
+        }
+        return FALSE;
+}
+
+int
+configure_cb (GtkWidget *drawing_area, GdkEventConfigure *evt, gpointer data)
+{
+        GdkPixbuf *pixbuf;
+                           
+        pixbuf = (GdkPixbuf *) gtk_object_get_data (GTK_OBJECT (drawing_area),   
+                                                    "pixbuf");
+    
+        g_print ("X:%d Y:%d\n", evt->width, evt->height);
+        if (evt->width != gdk_pixbuf_get_width (pixbuf) || evt->height != gdk_pixbuf_get_height (pixbuf)) {
+                GdkWindow *root;
+                GdkPixbuf *new_pixbuf;
+
+                root = GDK_ROOT_PARENT ();
+                new_pixbuf = gdk_pixbuf_get_from_drawable (NULL, root, NULL,
+                                                           0, 0, 0, 0, evt->width, evt->height);
+                gtk_object_set_data (GTK_OBJECT (drawing_area), "pixbuf", new_pixbuf);
+                gdk_pixbuf_unref (pixbuf);
+        }
+
+        return FALSE;
+}
+
+int
+main (int argc, char **argv)
+{   
+        GdkWindow     *root;
+        GtkWidget     *window;
+        GtkWidget     *vbox;
+        GtkWidget     *drawing_area;
+        GdkPixbuf     *pixbuf;    
+   
+        gtk_init (&argc, &argv);   
+
+        gtk_widget_set_default_colormap (gdk_rgb_get_cmap ());
+
+        root = GDK_ROOT_PARENT ();
+        pixbuf = gdk_pixbuf_get_from_drawable (NULL, root, NULL,
+                                               0, 0, 0, 0, 150, 160);
+   
+        window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+        gtk_signal_connect (GTK_OBJECT (window), "delete_event",
+                            GTK_SIGNAL_FUNC (close_app), NULL);
+        gtk_signal_connect (GTK_OBJECT (window), "destroy",   
+                            GTK_SIGNAL_FUNC (close_app), NULL);
+   
+        vbox = gtk_vbox_new (FALSE, 0);
+        gtk_container_add (GTK_CONTAINER (window), vbox);  
+   
+        drawing_area = gtk_drawing_area_new ();
+        gtk_drawing_area_size (GTK_DRAWING_AREA (drawing_area),
+                               gdk_pixbuf_get_width (pixbuf),
+                               gdk_pixbuf_get_height (pixbuf));
+        gtk_signal_connect (GTK_OBJECT (drawing_area), "expose_event",
+                            GTK_SIGNAL_FUNC (expose_cb), NULL);
+
+        gtk_signal_connect (GTK_OBJECT (drawing_area), "configure_event",
+                            GTK_SIGNAL_FUNC (configure_cb), NULL);
+        gtk_signal_connect (GTK_OBJECT (window), "key_press_event", 
+                            GTK_SIGNAL_FUNC (keypress_check), drawing_area);    
+        gtk_object_set_data (GTK_OBJECT (drawing_area), "pixbuf", pixbuf);
+        gtk_box_pack_start (GTK_BOX (vbox), drawing_area, TRUE, TRUE, 0);
+   
+        gtk_widget_show_all (window);
+        gtk_main ();
+        return 0;
+}
Index: gdk-pixbuf/ChangeLog
===================================================================
RCS file: /cvs/gnome/gtk+/gdk-pixbuf/ChangeLog,v
retrieving revision 1.251
diff -u -u -r1.251 ChangeLog
--- gdk-pixbuf/ChangeLog	2000/10/05 21:40:37	1.251
+++ gdk-pixbuf/ChangeLog	2000/10/06 00:34:00
@@ -1,3 +1,25 @@
+2000-10-05  Havoc Pennington  <hp redhat com>
+
+	* Makefile.am (GDK_PIXBUF_LIBS): add INTLLIBS
+	(libgdk_pixbuf_1_3_la_SOURCES): add gdk-pixbuf-i18n.h
+
+	* gdk-pixbuf-i18n.h: Add _() to gdk-pixbuf
+
+	* io-png.c (gdk_pixbuf__png_image_save): PNG save routine.
+
+	* io-jpeg.c (gdk_pixbuf__jpeg_image_save): JPEG save routine.
+
+	* gdk-pixbuf-io.c (gdk_pixbuf_save): 
+	(gdk_pixbuf_save_to_stream): Implement pixbuf saving routines
+
+	* gdk-pixbuf.c (gdk_pixbuf_error_quark): pixbuf error quark
+	function
+
+	* gdk-pixbuf.h: Add public save routines; add pixbuf error 
+	types
+
+	* gdk-pixbuf-io.h: Add save function to GdkPixbufModule
+
 2000-10-05  Dan Winship  <danw helixcode com>
 
 	* io-png.c, io-tiff.c, io-xpm.c: Fix comments to not claim that
Index: gdk-pixbuf/Makefile.am
===================================================================
RCS file: /cvs/gnome/gtk+/gdk-pixbuf/Makefile.am,v
retrieving revision 1.76
diff -u -u -r1.76 Makefile.am
--- gdk-pixbuf/Makefile.am	2000/09/26 20:22:17	1.76
+++ gdk-pixbuf/Makefile.am	2000/10/06 00:34:00
@@ -154,7 +154,7 @@
 
 make_inline_pixbuf_LDADD = $(LDADDS)
 
-GDK_PIXBUF_LIBS = $(GLIB_LIBS)
+GDK_PIXBUF_LIBS = $(GLIB_LIBS) $(INTLLIBS)
 
 #
 # The GdkPixBuf library
@@ -163,6 +163,7 @@
 libgdk_pixbufincludedir = $(includedir)/gtk-2.0/gdk-pixbuf
 
 libgdk_pixbuf_1_3_la_SOURCES = 	\
+	gdk-pixbuf-i18n.h	\
 	gdk-pixbuf.c		\
 	gdk-pixbuf-animation.c	\
 	gdk-pixbuf-data.c	\
Index: gdk-pixbuf/gdk-pixbuf-i18n.h
===================================================================
RCS file: gdk-pixbuf-i18n.h
diff -N gdk-pixbuf-i18n.h
--- /dev/null	Tue May  5 16:32:27 1998
+++ gdk-pixbuf-i18n.h	Thu Oct  5 20:34:00 2000
@@ -0,0 +1,24 @@
+#ifndef __GDKPIXBUFINTL_H__
+#define __GDKPIXBUFINTL_H__
+
+#include "config.h"
+
+#ifdef ENABLE_NLS
+#include<libintl.h>
+#define _(String) dgettext(GETTEXT_PACKAGE,String)
+#ifdef gettext_noop
+#define N_(String) gettext_noop(String)
+#else
+#define N_(String) (String)
+#endif
+#else /* NLS is disabled */
+#define _(String) (String)
+#define N_(String) (String)
+#define textdomain(String) (String)
+#define gettext(String) (String)
+#define dgettext(Domain,String) (String)
+#define dcgettext(Domain,String,Type) (String)
+#define bindtextdomain(Domain,Directory) (Domain) 
+#endif
+
+#endif
Index: gdk-pixbuf/gdk-pixbuf-io.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk-pixbuf/gdk-pixbuf-io.c,v
retrieving revision 1.44
diff -u -u -r1.44 gdk-pixbuf-io.c
--- gdk-pixbuf/gdk-pixbuf-io.c	2000/07/28 00:16:17	1.44
+++ gdk-pixbuf/gdk-pixbuf-io.c	2000/10/06 00:34:00
@@ -24,6 +24,7 @@
 #include <config.h>
 #include <string.h>
 #include <glib.h>
+#include <errno.h>
 #include "gdk-pixbuf-private.h"
 #include "gdk-pixbuf-io.h"
 
@@ -185,17 +186,17 @@
 }
 
 static GdkPixbufModule file_formats [] = {
-	{ "png",  pixbuf_check_png, NULL,  NULL, NULL, NULL, NULL, NULL },
-	{ "jpeg", pixbuf_check_jpeg, NULL, NULL, NULL, NULL, NULL, NULL },
-	{ "tiff", pixbuf_check_tiff, NULL, NULL, NULL, NULL, NULL, NULL },
-	{ "gif",  pixbuf_check_gif, NULL,  NULL, NULL, NULL, NULL, NULL },
+	{ "png",  pixbuf_check_png, NULL,  NULL, NULL, NULL, NULL, NULL, NULL, },
+	{ "jpeg", pixbuf_check_jpeg, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+	{ "tiff", pixbuf_check_tiff, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+	{ "gif",  pixbuf_check_gif, NULL,  NULL, NULL, NULL, NULL, NULL, NULL },
 #define XPM_FILE_FORMAT_INDEX 4
-	{ "xpm",  pixbuf_check_xpm, NULL,  NULL, NULL, NULL, NULL, NULL },
-	{ "pnm",  pixbuf_check_pnm, NULL,  NULL, NULL, NULL, NULL, NULL },
-	{ "ras",  pixbuf_check_sunras, NULL,  NULL, NULL, NULL, NULL, NULL },
-	{ "ico",  pixbuf_check_ico, NULL,  NULL, NULL, NULL, NULL, NULL },
-	{ "bmp",  pixbuf_check_bmp, NULL,  NULL, NULL, NULL, NULL, NULL },
-	{ "wbmp", pixbuf_check_wbmp, NULL, NULL, NULL, NULL, NULL, NULL },
+	{ "xpm",  pixbuf_check_xpm, NULL,  NULL, NULL, NULL, NULL, NULL, NULL },
+	{ "pnm",  pixbuf_check_pnm, NULL,  NULL, NULL, NULL, NULL, NULL, NULL },
+	{ "ras",  pixbuf_check_sunras, NULL,  NULL, NULL, NULL, NULL, NULL, NULL },
+	{ "ico",  pixbuf_check_ico, NULL,  NULL, NULL, NULL, NULL, NULL, NULL },
+	{ "bmp",  pixbuf_check_bmp, NULL,  NULL, NULL, NULL, NULL, NULL, NULL },
+	{ "wbmp", pixbuf_check_wbmp, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
 	{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
@@ -281,6 +282,7 @@
 	char *path;
 	GModule *module;
 	gpointer load_sym;
+        gpointer save_sym;
 	char *name;
 	
         g_return_if_fail (image_module->module == NULL);
@@ -333,6 +335,9 @@
 
         if (pixbuf_module_symbol (module, name, "image_load_animation", &load_sym))
 		image_module->load_animation = load_sym;
+
+        if (pixbuf_module_symbol (module, name, "image_save", &save_sym))
+          image_module->save = save_sym;        
 }
 #else
 
@@ -354,6 +359,7 @@
 m_begin_load (png);
 m_load_increment (png);
 m_stop_load (png);
+m_save (png);
 /* BMP */
 m_load (bmp);
 m_begin_load (bmp);
@@ -380,6 +386,7 @@
 m_begin_load (jpeg);
 m_load_increment (jpeg);
 m_stop_load (jpeg);
+m_save (jpeg);
 /* PNM */
 m_load (pnm);
 m_begin_load (pnm);
@@ -409,6 +416,7 @@
 		image_module->begin_load     = mname (png,begin_load);
 		image_module->load_increment = mname (png,load_increment);
 		image_module->stop_load      = mname (png,stop_load);
+                image_module->save           = mname (png,save);
 		return;
 	}
 
@@ -450,6 +458,7 @@
 		image_module->begin_load     = mname (jpeg,begin_load);
 		image_module->load_increment = mname (jpeg,load_increment);
 		image_module->stop_load      = mname (jpeg,stop_load);
+                image_module->save           = mname (jpeg,save);
 		return;
 	}
 	if (strcmp (image_module->module_name, "pnm") == 0){
@@ -595,4 +604,205 @@
 
 	pixbuf = (* load_xpm_data) (data);
 	return pixbuf;
+}
+
+static void
+collect_save_options (va_list   opts,
+                      gchar  ***keys,
+                      gchar  ***vals)
+{
+  gchar *key;
+  gchar *val;
+  gchar *next;
+  gint count;
+
+  count = 0;
+  *keys = NULL;
+  *vals = NULL;
+  
+  next = va_arg (opts, gchar*);
+  while (next)
+    {
+      key = next;
+      val = va_arg (opts, gchar*);
+
+      ++count;
+
+      *keys = g_realloc (*keys, sizeof(gchar*) * (count + 1));
+      *vals = g_realloc (*vals, sizeof(gchar*) * (count + 1));
+      
+      (*keys)[count-1] = g_strdup (key);
+      (*vals)[count-1] = g_strdup (val);
+
+      (*keys)[count] = NULL;
+      (*vals)[count] = NULL;
+      
+      next = va_arg (opts, gchar*);
+    }
+}
+
+static gboolean
+gdk_pixbuf_real_save (GdkPixbuf     *pixbuf, 
+                      FILE          *filehandle, 
+                      const char    *format, 
+                      gchar        **keys,
+                      gchar        **values,
+                      GError       **error)
+{
+       int i;
+       GdkPixbufModule *image_module = NULL;       
+       
+       for (i = 0; file_formats[i].module_name; i++) {
+               if (!strcmp (file_formats[i].module_name, format))
+                       image_module = &(file_formats[i]);
+       }
+       
+       g_return_val_if_fail (image_module != NULL, FALSE);
+       if (!image_module) {
+               g_warning ("gdk-pixbuf does not support the format: %s", format);
+               fclose (filehandle);
+               return FALSE;
+       }
+       
+       if (image_module->module == NULL)
+               gdk_pixbuf_load_module (image_module);
+       
+       g_return_val_if_fail (image_module->save != NULL, FALSE);
+
+       
+       
+       return (* image_module->save) (filehandle, pixbuf,
+                                      keys, values,
+                                      error);
+}
+
+/**
+ * gdk_pixbuf_save_to_stream:
+ * @pixbuf: pointer to GdkPixbuf.
+ * @filehandle: FILE* to save to.
+ * @format: name of file format.
+ * @error: error return location, or NULL
+ * @Varargs: key-value pairs of properties affecting the save
+ *
+ * Saves pixbuf to a filehandle in @format. @format can be "png" or "jpeg".
+ * @error is a return location for an error while saving. The variable
+ * argument list should be NULL-terminated; if not empty, it should contain
+ * pairs of strings that modify the save parameters. For example:
+ *
+ * <programlisting>
+ * gdk_pixbuf_save_to_file (pixbuf, handle, "jpeg", &error,
+ *                          "quality", "100", NULL);
+ * </programlisting>
+ *
+ * The only save parameter that currently exists is the "quality" field
+ * for JPEG images; its value should be in the range [0,100].
+ *
+ * Return value: TRUE on success, FALSE on failure. 
+ **/
+
+gboolean 
+gdk_pixbuf_save_to_stream (GdkPixbuf     *pixbuf, 
+                           FILE          *filehandle, 
+                           const char    *format,
+                           GError       **error,
+                           ...)
+{
+       gchar **keys = NULL;
+       gchar **values = NULL;
+       va_list args;
+       gboolean result;
+       
+       g_return_val_if_fail (filehandle != NULL, FALSE);
+       g_return_val_if_fail (format != NULL, FALSE);
+
+       va_start (args, error);
+
+       collect_save_options (args, &keys, &values);
+
+       va_end (args);
+
+       result = gdk_pixbuf_real_save (pixbuf, filehandle, format,
+                                      keys, values, error);
+
+       g_strfreev (keys);
+       g_strfreev (values);
+
+       return result;
+}
+
+
+/**
+ * gdk_pixbuf_save:
+ * @pixbuf: pointer to GdkPixbuf.
+ * @filename: Name of file to save.
+ * @format: name of file format.
+ * @error: return location for error, or NULL
+ * @Varargs: list of key-value save options
+ *
+ * Saves pixbuf to a file in @format, which is currently "jpeg" or "png".
+ * If @error is set, FALSE will be returned. The variable argument list
+ * should be NULL-terminated and should be key-value pairs setting
+ * options for the save. See gdk_pixbuf_save_to_stream() for more
+ * details.
+ *
+ * Return value: whether an error was set
+ **/
+
+
+gboolean
+gdk_pixbuf_save (GdkPixbuf  *pixbuf, 
+                 const char *filename, 
+                 const char *format, 
+                 GError    **error,
+                 ...)
+{
+        FILE *f = NULL;
+        gchar **keys = NULL;
+        gchar **values = NULL;
+        va_list args;
+        gboolean result;
+        
+       
+        g_return_val_if_fail (filename != NULL, FALSE);
+        g_return_val_if_fail (format != NULL, FALSE);
+       
+        f = fopen (filename, "w");
+        
+        if (f == NULL) {
+                g_set_error (error,
+                             GDK_PIXBUF_ERROR,
+                             GDK_PIXBUF_ERROR_IO,
+                             _("Failed to open '%s' for writing: %s"),
+                             filename, g_strerror (errno));
+                return FALSE;
+        }
+
+       va_start (args, error);
+       
+       collect_save_options (args, &keys, &values);
+       
+       va_end (args);
+       
+       result = gdk_pixbuf_real_save (pixbuf, f, format,
+                                      keys, values, error);
+
+       g_strfreev (keys);
+       g_strfreev (values);
+       
+       if (!result) {
+               g_return_val_if_fail (error == NULL || *error != NULL, FALSE);
+               fclose (f);
+               return FALSE;
+       }
+
+       if (fclose (f) < 0) {
+               g_set_error (error,
+                            GDK_PIXBUF_ERROR,
+                            GDK_PIXBUF_ERROR_IO,
+                            _("Failed to close '%s' while writing image, all data may not have been saved: %s"),
+                            filename, g_strerror (errno));
+               return FALSE;
+       }
+       
+       return TRUE;
 }
Index: gdk-pixbuf/gdk-pixbuf-io.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk-pixbuf/gdk-pixbuf-io.h,v
retrieving revision 1.20
diff -u -u -r1.20 gdk-pixbuf-io.h
--- gdk-pixbuf/gdk-pixbuf-io.h	2000/07/28 00:09:35	1.20
+++ gdk-pixbuf/gdk-pixbuf-io.h	2000/10/06 00:34:00
@@ -31,6 +31,7 @@
 #include <gmodule.h>
 #include <stdio.h>
 #include "gdk-pixbuf.h"
+#include "gdk-pixbuf-i18n.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -64,11 +65,19 @@
 				 ModuleFrameDoneNotifyFunc frame_done_func,
 				 ModuleAnimationDoneNotifyFunc anim_done_func,
 				 gpointer user_data);
-        void (* stop_load) (gpointer context);
-        gboolean (* load_increment) (gpointer context, const guchar *buf, guint size);
+        void (* stop_load)          (gpointer context);
+        gboolean (* load_increment) (gpointer      context,
+                                     const guchar *buf,
+                                     guint         size);
 
 	/* Animation loading */
 	GdkPixbufAnimation *(* load_animation) (FILE *f);
+
+        gboolean (* save) (FILE      *f,
+                           GdkPixbuf *pixbuf,
+                           gchar    **param_keys,
+                           gchar    **param_values,
+                           GError   **err);
 };
 
 
Index: gdk-pixbuf/gdk-pixbuf.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk-pixbuf/gdk-pixbuf.c,v
retrieving revision 1.35
diff -u -u -r1.35 gdk-pixbuf.c
--- gdk-pixbuf/gdk-pixbuf.c	2000/07/26 11:32:39	1.35
+++ gdk-pixbuf/gdk-pixbuf.c	2000/10/06 00:34:00
@@ -361,3 +361,14 @@
         gdk_pixbuf_preinit (NULL, NULL);
         gdk_pixbuf_postinit (NULL, NULL);
 }
+
+/* Error quark */
+GQuark
+gdk_pixbuf_error_quark (void)
+{
+  static GQuark q = 0;
+  if (q == 0)
+    q = g_quark_from_static_string ("gdk-pixbuf-error-quark");
+
+  return q;
+}
Index: gdk-pixbuf/gdk-pixbuf.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk-pixbuf/gdk-pixbuf.h,v
retrieving revision 1.46
diff -u -u -r1.46 gdk-pixbuf.h
--- gdk-pixbuf/gdk-pixbuf.h	2000/09/26 20:22:17	1.46
+++ gdk-pixbuf/gdk-pixbuf.h	2000/10/06 00:34:00
@@ -29,6 +29,7 @@
 #include <glib.h>
 #include <gdk-pixbuf/gdk-pixbuf-features.h>
 #include <gobject/gobject.h>
+#include <stdio.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -61,6 +62,18 @@
 /* Handler that must free the pixel array */
 typedef void (* GdkPixbufDestroyNotify) (guchar *pixels, gpointer data);
 
+#define GDK_PIXBUF_ERROR gdk_pixbuf_error_quark ()
+
+typedef enum {
+        /* some kind of failure reading or writing files */
+        GDK_PIXBUF_ERROR_IO,
+        /* bad option passed to save routine */
+        GDK_PIXBUF_ERROR_BAD_OPTION,
+        GDK_PIXBUF_ERROR_FAILED
+} GdkPixbufErrorType;
+
+GQuark gdk_pixbuf_error_quark () G_GNUC_CONST;
+
 
 
 GType gdk_pixbuf_get_type (void) G_GNUC_CONST;
@@ -110,6 +123,21 @@
 GdkPixbuf *gdk_pixbuf_new_from_inline   (const guchar *inline_pixbuf,
                                          gboolean      copy_pixels,
                                          int           length);
+
+
+/* Saving */
+
+
+gboolean gdk_pixbuf_save_to_stream (GdkPixbuf     *pixbuf, 
+                                    FILE          *filehandle, 
+                                    const char    *format,
+                                    GError       **error,
+                                    ...);
+gboolean gdk_pixbuf_save           (GdkPixbuf  *pixbuf, 
+                                    const char *filename, 
+                                    const char *format, 
+                                    GError    **err,
+                                    ...);
 
 /* Adding an alpha channel */
 GdkPixbuf *gdk_pixbuf_add_alpha (const GdkPixbuf *pixbuf, gboolean substitute_color,
Index: gdk-pixbuf/io-jpeg.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk-pixbuf/io-jpeg.c,v
retrieving revision 1.31
diff -u -u -r1.31 io-jpeg.c
--- gdk-pixbuf/io-jpeg.c	2000/07/26 11:32:40	1.31
+++ gdk-pixbuf/io-jpeg.c	2000/10/06 00:34:00
@@ -565,3 +565,114 @@
 
 	return TRUE;
 }
+
+gboolean
+gdk_pixbuf__jpeg_image_save (FILE          *f, 
+                             GdkPixbuf     *pixbuf, 
+                             gchar        **keys,
+                             gchar        **values,
+                             GError       **error)
+{
+        /* FIXME error handling is broken */
+        
+       struct jpeg_compress_struct cinfo;
+       guchar *buf = NULL;
+       guchar *ptr;
+       guchar *pixels = NULL;
+       JSAMPROW *jbuf;
+       int y = 0;
+       int quality = 75; /* default; must be between 0 and 100 */
+       int i, j;
+       int w, h = 0;
+       int rowstride = 0;
+       struct error_handler_data jerr;
+
+       if (keys && *keys) {
+               gchar **kiter = keys;
+               gchar **viter = values;
+
+               while (*kiter) {
+                       if (strcmp (*kiter, "quality") == 0) {
+                               quality = atoi (*viter);
+                               if (quality < 0 ||
+                                   quality > 100) {
+                                       /* This is a user-visible error;
+                                        * lets people skip the range-checking
+                                        * in their app.
+                                        */
+                                       g_set_error (error,
+                                                    GDK_PIXBUF_ERROR,
+                                                    GDK_PIXBUF_ERROR_BAD_OPTION,
+                                                    _("JPEG quality must be a value between 0 and 100; value '%d' is not allowed."),
+                                                    quality);
+
+                                       return FALSE;
+                               }
+                       } else {
+                               g_warning ("Bad option name '%s' passed to JPEG saver",
+                                          *kiter);
+                               return FALSE;
+                       }
+               
+                       ++kiter;
+                       ++viter;
+               }
+       }
+       
+       rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+
+       w = gdk_pixbuf_get_width (pixbuf);
+       h = gdk_pixbuf_get_height (pixbuf);
+
+       /* no image data? abort */
+       pixels = gdk_pixbuf_get_pixels (pixbuf);
+       g_return_val_if_fail (pixels != NULL, FALSE);
+
+       /* allocate a small buffer to convert image data */
+       buf = malloc (w * 3 * sizeof (guchar));
+       g_return_val_if_fail (buf != NULL, FALSE);
+
+       /* set up error handling */
+       jerr.pub.error_exit = fatal_error_handler;
+
+       cinfo.err = jpeg_std_error (&(jerr.pub));
+       if (sigsetjmp (jerr.setjmp_buffer, 1)) {
+               jpeg_destroy_compress (&cinfo);
+               free (buf);
+               return FALSE;
+       }
+
+       /* setup compress params */
+       jpeg_create_compress (&cinfo);
+       jpeg_stdio_dest (&cinfo, f);
+       cinfo.image_width      = w;
+       cinfo.image_height     = h;
+       cinfo.input_components = 3; 
+       cinfo.in_color_space   = JCS_RGB;
+
+       /* set up jepg compression parameters */
+       jpeg_set_defaults (&cinfo);
+       jpeg_set_quality (&cinfo, quality, TRUE);
+       jpeg_start_compress (&cinfo, TRUE);
+       /* get the start pointer */
+       ptr = pixels;
+       /* go one scanline at a time... and save */
+       i = 0;
+       while (cinfo.next_scanline < cinfo.image_height) {
+               /* convert scanline from ARGB to RGB packed */
+               for (j = 0; j < w; j++)
+                       memcpy (&(buf[j*3]), &(ptr[i*rowstride + j*3]), 3);
+
+               /* write scanline */
+               jbuf = (JSAMPROW *)(&buf);
+               jpeg_write_scanlines (&cinfo, jbuf, 1);
+               i++;
+               y++;
+
+       }
+       
+       /* finish off */
+       jpeg_finish_compress (&cinfo);   
+       free (buf);
+       return TRUE;
+}
Index: gdk-pixbuf/io-png.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk-pixbuf/io-png.c,v
retrieving revision 1.32
diff -u -u -r1.32 io-png.c
--- gdk-pixbuf/io-png.c	2000/10/05 21:40:37	1.32
+++ gdk-pixbuf/io-png.c	2000/10/06 00:34:00
@@ -537,5 +537,112 @@
 }
 
 
+/* Save */
+gboolean
+gdk_pixbuf__png_image_save (FILE          *f, 
+                            GdkPixbuf     *pixbuf, 
+                            gchar        **keys,
+                            gchar        **values,
+                            GError       **error)
+{
+        /* FIXME error handling is broken */
+        
+       png_structp png_ptr;
+       png_infop info_ptr;
+       guchar *ptr;
+       guchar *pixels;
+       int x, y, j;
+       png_bytep row_ptr, data = NULL;
+       png_color_8 sig_bit;
+       int w, h, rowstride;
+       int has_alpha;
+       int bpc;
+
+       if (keys && *keys) {
+               g_warning ("Bad option name '%s' passed to JPEG saver",
+                          *keys);
+               return FALSE;
+#if 0
+               gchar **kiter = keys;
+               gchar **viter = values;
+
+               
+               while (*kiter) {
+                       
+                       ++kiter;
+                       ++viter;
+               }
+#endif
+       }
+       
+       bpc = gdk_pixbuf_get_bits_per_sample (pixbuf);
+       w = gdk_pixbuf_get_width (pixbuf);
+       h = gdk_pixbuf_get_height (pixbuf);
+       rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+       has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
+       pixels = gdk_pixbuf_get_pixels (pixbuf);
+
+       png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
+                                          NULL, NULL, NULL);
+
+       g_return_val_if_fail (png_ptr != NULL, FALSE);
+
+       info_ptr = png_create_info_struct (png_ptr);
+       if (info_ptr == NULL) {
+               png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
+               return FALSE;
+       }
+       if (setjmp (png_ptr->jmpbuf)) {
+               png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
+               return FALSE;
+       }
+       png_init_io (png_ptr, f);
+       if (has_alpha) {
+               png_set_IHDR (png_ptr, info_ptr, w, h, bpc,
+                            PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
+                            PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+#ifdef WORDS_BIGENDIAN
+               png_set_swap_alpha (png_ptr);
+#else
+               png_set_bgr (png_ptr);
+#endif
+       } else {
+               png_set_IHDR (png_ptr, info_ptr, w, h, bpc,
+                            PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
+                            PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+               data = malloc (w * 3 * sizeof (char));
+       }
+       sig_bit.red = bpc;
+       sig_bit.green = bpc;
+       sig_bit.blue = bpc;
+       sig_bit.alpha = bpc;
+       png_set_sBIT (png_ptr, info_ptr, &sig_bit);
+       png_write_info (png_ptr, info_ptr);
+       png_set_shift (png_ptr, &sig_bit);
+       png_set_packing (png_ptr);
+
+       ptr = pixels;
+       for (y = 0; y < h; y++) {
+               if (has_alpha)
+                       row_ptr = (png_bytep)ptr;
+               else {
+                       for (j = 0, x = 0; x < w; x++)
+                               memcpy (&(data[x*3]), &(ptr[x*3]), 3);
+
+                       row_ptr = (png_bytep)data;
+               }
+               png_write_rows (png_ptr, &row_ptr, 1);
+               ptr += rowstride;
+       }
+
+       if (data)
+               free (data);
+
+       png_write_end (png_ptr, info_ptr);
+       png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
+
+       return TRUE;
+}
+
 
 




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]