Commit e2d006d7 authored by Havoc Pennington's avatar Havoc Pennington Committed by Havoc Pennington

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
parent 607c245a
......@@ -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 \
......
#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
......@@ -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;
}
......@@ -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);
};
......
......@@ -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;
}
......@@ -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);
......
......@@ -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;
}
......@@ -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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment