diff options
author | Havoc Pennington <hp@redhat.com> | 2000-10-06 18:19:18 +0000 |
---|---|---|
committer | Havoc Pennington <hp@src.gnome.org> | 2000-10-06 18:19:18 +0000 |
commit | 6b9f9072708e6f1b41f9f75acd8d70191ad245ea (patch) | |
tree | 2a46c20b581be14650eb258c30ded238359a3c15 | |
parent | a7e20093e9d6623433e764bb2aac9e40693c783d (diff) | |
download | gtk+-6b9f9072708e6f1b41f9f75acd8d70191ad245ea.tar.gz |
Pixbuf saving, patch from David Welton.
2000-10-05 Havoc Pennington <hp@redhat.com>
Pixbuf saving, patch from David Welton.
* 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_savev): 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 Havoc Pennington <hp@redhat.com>
* demos/testpixbuf-save.c: add pixbuf save test
* demos/Makefile.am: add testpixbuf-save.c
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | ChangeLog.pre-2-0 | 6 | ||||
-rw-r--r-- | ChangeLog.pre-2-10 | 6 | ||||
-rw-r--r-- | ChangeLog.pre-2-2 | 6 | ||||
-rw-r--r-- | ChangeLog.pre-2-4 | 6 | ||||
-rw-r--r-- | ChangeLog.pre-2-6 | 6 | ||||
-rw-r--r-- | ChangeLog.pre-2-8 | 6 | ||||
-rw-r--r-- | demos/Makefile.am | 4 | ||||
-rw-r--r-- | demos/testpixbuf-save.c | 154 | ||||
-rw-r--r-- | gdk-pixbuf/ChangeLog | 24 | ||||
-rw-r--r-- | gdk-pixbuf/Makefile.am | 3 | ||||
-rw-r--r-- | gdk-pixbuf/gdk-pixbuf-i18n.h | 24 | ||||
-rw-r--r-- | gdk-pixbuf/gdk-pixbuf-io.c | 223 | ||||
-rw-r--r-- | gdk-pixbuf/gdk-pixbuf-io.h | 13 | ||||
-rw-r--r-- | gdk-pixbuf/gdk-pixbuf.c | 11 | ||||
-rw-r--r-- | gdk-pixbuf/gdk-pixbuf.h | 28 | ||||
-rw-r--r-- | gdk-pixbuf/io-jpeg.c | 123 | ||||
-rw-r--r-- | gdk-pixbuf/io-png.c | 107 |
18 files changed, 743 insertions, 13 deletions
@@ -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-05 Jonathan Blandford <jrb@redhat.com> * gtk/gtktree.h (GTK_TREE_SELECTION): Replaced with diff --git a/ChangeLog.pre-2-0 b/ChangeLog.pre-2-0 index dee05f90c2..e2562f508a 100644 --- a/ChangeLog.pre-2-0 +++ b/ChangeLog.pre-2-0 @@ -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-05 Jonathan Blandford <jrb@redhat.com> * gtk/gtktree.h (GTK_TREE_SELECTION): Replaced with diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index dee05f90c2..e2562f508a 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -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-05 Jonathan Blandford <jrb@redhat.com> * gtk/gtktree.h (GTK_TREE_SELECTION): Replaced with diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2 index dee05f90c2..e2562f508a 100644 --- a/ChangeLog.pre-2-2 +++ b/ChangeLog.pre-2-2 @@ -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-05 Jonathan Blandford <jrb@redhat.com> * gtk/gtktree.h (GTK_TREE_SELECTION): Replaced with diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index dee05f90c2..e2562f508a 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -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-05 Jonathan Blandford <jrb@redhat.com> * gtk/gtktree.h (GTK_TREE_SELECTION): Replaced with diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index dee05f90c2..e2562f508a 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -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-05 Jonathan Blandford <jrb@redhat.com> * gtk/gtktree.h (GTK_TREE_SELECTION): Replaced with diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index dee05f90c2..e2562f508a 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -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-05 Jonathan Blandford <jrb@redhat.com> * gtk/gtktree.h (GTK_TREE_SELECTION): Replaced with diff --git a/demos/Makefile.am b/demos/Makefile.am index 13d3b26367..3aa45b4223 100644 --- a/demos/Makefile.am +++ b/demos/Makefile.am @@ -34,6 +34,7 @@ noinst_PROGRAMS = \ testpixbuf \ testpixbuf-drawable \ testanimation \ + testpixbuf-save \ testpixbuf-scale \ pixbuf-demo @@ -44,18 +45,21 @@ test-inline-pixbufs.h: $(top_builddir)/gdk-pixbuf/make-inline-pixbuf apple-red.p 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 diff --git a/demos/testpixbuf-save.c b/demos/testpixbuf-save.c new file mode 100644 index 0000000000..802f984946 --- /dev/null +++ b/demos/testpixbuf-save.c @@ -0,0 +1,154 @@ + +#include <config.h> +/* if building outside GTK, remove /x11 part */ +#include <gdk/x11/gdkx.h> +#include <gtk/gtk.h> +#include <stdio.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; +} diff --git a/gdk-pixbuf/ChangeLog b/gdk-pixbuf/ChangeLog index 9fcf2d5ac0..a60d5b6f09 100644 --- a/gdk-pixbuf/ChangeLog +++ b/gdk-pixbuf/ChangeLog @@ -1,3 +1,27 @@ +2000-10-05 Havoc Pennington <hp@redhat.com> + + Pixbuf saving, patch from David Welton. + + * 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_savev): 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 diff --git a/gdk-pixbuf/Makefile.am b/gdk-pixbuf/Makefile.am index 2247e657e3..b709dc6ce8 100644 --- a/gdk-pixbuf/Makefile.am +++ b/gdk-pixbuf/Makefile.am @@ -154,7 +154,7 @@ make_inline_pixbuf_SOURCES=make-inline-pixbuf.c make_inline_pixbuf_LDADD = $(LDADDS) -GDK_PIXBUF_LIBS = $(GLIB_LIBS) +GDK_PIXBUF_LIBS = $(GLIB_LIBS) $(INTLLIBS) # # The GdkPixBuf library @@ -163,6 +163,7 @@ GDK_PIXBUF_LIBS = $(GLIB_LIBS) 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 \ diff --git a/gdk-pixbuf/gdk-pixbuf-i18n.h b/gdk-pixbuf/gdk-pixbuf-i18n.h new file mode 100644 index 0000000000..69121d005c --- /dev/null +++ b/gdk-pixbuf/gdk-pixbuf-i18n.h @@ -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 diff --git a/gdk-pixbuf/gdk-pixbuf-io.c b/gdk-pixbuf/gdk-pixbuf-io.c index a063f4d41e..0e3ca13f53 100644 --- a/gdk-pixbuf/gdk-pixbuf-io.c +++ b/gdk-pixbuf/gdk-pixbuf-io.c @@ -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 @@ pixbuf_check_wbmp (guchar *buffer, int size) } 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 @@ gdk_pixbuf_load_module (GdkPixbufModule *image_module) char *path; GModule *module; gpointer load_sym; + gpointer save_sym; char *name; g_return_if_fail (image_module->module == NULL); @@ -333,6 +335,9 @@ gdk_pixbuf_load_module (GdkPixbufModule *image_module) 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_load (png); 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_load (jpeg); 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 @@ gdk_pixbuf_load_module (GdkPixbufModule *image_module) 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 @@ gdk_pixbuf_load_module (GdkPixbufModule *image_module) 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){ @@ -596,3 +605,197 @@ gdk_pixbuf_new_from_xpm_data (const char **data) 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; + + /* woo, slow */ + *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]); + break; + } + } + + if (!image_module) { + g_warning ("gdk-pixbuf does not support the format: %s", format); + 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: + * @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. Possible errors include those + * from #GdkPixbufErrorType and those from #GFileErrorType. + * + * 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 (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: whether an error was set + **/ + +gboolean +gdk_pixbuf_save (GdkPixbuf *pixbuf, + const char *filename, + const char *format, + GError **error, + ...) +{ + gchar **keys = NULL; + gchar **values = NULL; + va_list args; + gboolean result; + + va_start (args, error); + + collect_save_options (args, &keys, &values); + + va_end (args); + + result = gdk_pixbuf_savev (pixbuf, filename, format, + keys, values, + error); + + g_strfreev (keys); + g_strfreev (values); + + return result; +} + +/** + * gdk_pixbuf_savev: + * @pixbuf: pointer to GdkPixbuf. + * @filename: Name of file to save. + * @format: name of file format. + * @option_keys: name of options to set, NULL-terminated + * @option_values: values for named options + * @error: return location for error, or NULL + * + * Saves pixbuf to a file in @format, which is currently "jpeg" or "png". + * If @error is set, FALSE will be returned. See gdk_pixbuf_save () for more + * details. Possible errors include those from #GdkPixbufErrorType and + * those from #GFileErrorType. + * + * Return value: whether an error was set + **/ + +gboolean +gdk_pixbuf_savev (GdkPixbuf *pixbuf, + const char *filename, + const char *format, + char **option_keys, + char **option_values, + GError **error) +{ + FILE *f = NULL; + 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; + } + + + result = gdk_pixbuf_real_save (pixbuf, f, format, + option_keys, option_values, + error); + + + 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; +} diff --git a/gdk-pixbuf/gdk-pixbuf-io.h b/gdk-pixbuf/gdk-pixbuf-io.h index af9fb40b8e..3ce4d2f733 100644 --- a/gdk-pixbuf/gdk-pixbuf-io.h +++ b/gdk-pixbuf/gdk-pixbuf-io.h @@ -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 @@ struct _GdkPixbufModule { 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); }; diff --git a/gdk-pixbuf/gdk-pixbuf.c b/gdk-pixbuf/gdk-pixbuf.c index 91b45b04a3..a515ea55b0 100644 --- a/gdk-pixbuf/gdk-pixbuf.c +++ b/gdk-pixbuf/gdk-pixbuf.c @@ -361,3 +361,14 @@ gdk_pixbuf_init (void) 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; +} diff --git a/gdk-pixbuf/gdk-pixbuf.h b/gdk-pixbuf/gdk-pixbuf.h index aaaad7fda2..b7c0a6b886 100644 --- a/gdk-pixbuf/gdk-pixbuf.h +++ b/gdk-pixbuf/gdk-pixbuf.h @@ -61,6 +61,18 @@ typedef struct _GdkPixbufFrame GdkPixbufFrame; /* 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 value passed to save routine */ + GDK_PIXBUF_ERROR_BAD_OPTION_VALUE, + GDK_PIXBUF_ERROR_FAILED +} GdkPixbufErrorType; + +GQuark gdk_pixbuf_error_quark () G_GNUC_CONST; + GType gdk_pixbuf_get_type (void) G_GNUC_CONST; @@ -111,6 +123,22 @@ GdkPixbuf *gdk_pixbuf_new_from_inline (const guchar *inline_pixbuf, gboolean copy_pixels, int length); + +/* Saving */ + +gboolean gdk_pixbuf_save (GdkPixbuf *pixbuf, + const char *filename, + const char *format, + GError **err, + ...); + +gboolean gdk_pixbuf_savev (GdkPixbuf *pixbuf, + const char *filename, + const char *format, + char **option_keys, + char **option_values, + GError **err); + /* Adding an alpha channel */ GdkPixbuf *gdk_pixbuf_add_alpha (const GdkPixbuf *pixbuf, gboolean substitute_color, guchar r, guchar g, guchar b); diff --git a/gdk-pixbuf/io-jpeg.c b/gdk-pixbuf/io-jpeg.c index 06deb42f6c..89ca8d2015 100644 --- a/gdk-pixbuf/io-jpeg.c +++ b/gdk-pixbuf/io-jpeg.c @@ -565,3 +565,126 @@ gdk_pixbuf__jpeg_image_load_increment (gpointer data, guchar *buf, guint size) 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) { + char *endptr = NULL; + quality = strtol (*viter, &endptr, 10); + + if (endptr == *viter) { + g_set_error (error, + GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_BAD_OPTION_VALUE, + _("JPEG quality must be a value between 0 and 100; value '%s' could not be parsed."), + *viter); + + return FALSE; + } + + 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_VALUE, + _("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; +} diff --git a/gdk-pixbuf/io-png.c b/gdk-pixbuf/io-png.c index 1371970fd5..33fa2cf68f 100644 --- a/gdk-pixbuf/io-png.c +++ b/gdk-pixbuf/io-png.c @@ -537,5 +537,112 @@ png_warning_callback(png_structp png_read_ptr, } +/* 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 PNG 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; +} + |