summaryrefslogtreecommitdiff
path: root/gdk-pixbuf
diff options
context:
space:
mode:
authorMatthias Clasen <maclas@gmx.de>2004-01-07 01:57:42 +0000
committerMatthias Clasen <matthiasc@src.gnome.org>2004-01-07 01:57:42 +0000
commit03376d79faf862ada7fca924aed681156398d574 (patch)
tree914249bd1d2864773bec4eadf2f3f533f9523fac /gdk-pixbuf
parent5b26e695a1fc70c99b58f2e9c4c922412fbd1a08 (diff)
downloadgtk+-03376d79faf862ada7fca924aed681156398d574.tar.gz
New public API to save pixbufs to non-file locations. (#82203, Tim Evans)
Wed Jan 7 02:41:14 2004 Matthias Clasen <maclas@gmx.de> * gdk-pixbuf-io.c (gdk_pixbuf_save_to_bufferv): * gdk-pixbuf-io.c (gdk_pixbuf_save_to_buffer): * gdk-pixbuf-io.c (gdk_pixbuf_save_to_callbackv): * gdk-pixbuf-io.c (gdk_pixbuf_save_to_callback): New public API to save pixbufs to non-file locations. (#82203, Tim Evans) * gdk-pixbuf.h: * gdk_pixbuf.def: Declare the new public API. * gdk-pixbuf-io.h: Add save_to_callback to the module interface. * io-jpeg.c: * io-png.c: Implement save_to_callback. * gdk-pixbuf-io.c (gdk_pixbuf_savev): * gdk-pixbuf-io.c (gdk_pixbuf_save): Update documentation. * gdk-pixbuf-io.c (gdk_pixbuf_real_save): Support saving via image_module->save_to_callback. * gdk-pixbuf-io.c (gdk_pixbuf_real_save_to_callback): New generic save_to_callback function which falls back to image_module->save on a temp file.
Diffstat (limited to 'gdk-pixbuf')
-rw-r--r--gdk-pixbuf/ChangeLog26
-rw-r--r--gdk-pixbuf/gdk-pixbuf-io.c429
-rw-r--r--gdk-pixbuf/gdk-pixbuf-io.h11
-rw-r--r--gdk-pixbuf/gdk-pixbuf-loader.c1
-rw-r--r--gdk-pixbuf/gdk-pixbuf.h39
-rw-r--r--gdk-pixbuf/gdk_pixbuf.def5
-rw-r--r--gdk-pixbuf/io-jpeg.c138
-rw-r--r--gdk-pixbuf/io-png.c76
8 files changed, 698 insertions, 27 deletions
diff --git a/gdk-pixbuf/ChangeLog b/gdk-pixbuf/ChangeLog
index 796b17833c..c09fdd5c47 100644
--- a/gdk-pixbuf/ChangeLog
+++ b/gdk-pixbuf/ChangeLog
@@ -1,3 +1,29 @@
+Wed Jan 7 02:41:14 2004 Matthias Clasen <maclas@gmx.de>
+
+ * gdk-pixbuf-io.c (gdk_pixbuf_save_to_bufferv):
+ * gdk-pixbuf-io.c (gdk_pixbuf_save_to_buffer):
+ * gdk-pixbuf-io.c (gdk_pixbuf_save_to_callbackv):
+ * gdk-pixbuf-io.c (gdk_pixbuf_save_to_callback): New public API
+ to save pixbufs to non-file locations. (#82203, Tim Evans)
+
+ * gdk-pixbuf.h:
+ * gdk-pixbuf.def: Declare the new public API.
+
+ * gdk-pixbuf-io.h: Add save_to_callback to the module interface.
+
+ * io-jpeg.c:
+ * io-png.c: Implement save_to_callback.
+
+ * gdk-pixbuf-io.c (gdk_pixbuf_savev):
+ * gdk-pixbuf-io.c (gdk_pixbuf_save): Update documentation.
+
+ * gdk-pixbuf-io.c (gdk_pixbuf_real_save): Support saving via
+ image_module->save_to_callback.
+
+ * gdk-pixbuf-io.c (gdk_pixbuf_real_save_to_callback): New generic
+ save_to_callback function which falls back to image_module->save
+ on a temp file.
+
Wed Jan 7 01:17:36 2004 Matthias Clasen <maclas@gmx.de>
* gdk-pixbuf-loader.c (gdk_pixbuf_loader_size_func):
diff --git a/gdk-pixbuf/gdk-pixbuf-io.c b/gdk-pixbuf/gdk-pixbuf-io.c
index dc5dc51431..e67564c223 100644
--- a/gdk-pixbuf/gdk-pixbuf-io.c
+++ b/gdk-pixbuf/gdk-pixbuf-io.c
@@ -1074,6 +1074,27 @@ collect_save_options (va_list opts,
}
static gboolean
+save_to_file_callback (const gchar *buf,
+ gsize count,
+ GError **error,
+ gpointer data)
+{
+ FILE *filehandle = data;
+ gsize n;
+
+ n = fwrite (buf, 1, count, filehandle);
+ if (n != count) {
+ g_set_error (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ _("Error writing to image file: %s"),
+ g_strerror (errno));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
gdk_pixbuf_real_save (GdkPixbuf *pixbuf,
FILE *filehandle,
const char *type,
@@ -1092,7 +1113,137 @@ gdk_pixbuf_real_save (GdkPixbuf *pixbuf,
if (!_gdk_pixbuf_load_module (image_module, error))
return FALSE;
- if (image_module->save == NULL) {
+ if (image_module->save) {
+ /* save normally */
+ return (* image_module->save) (filehandle, pixbuf,
+ keys, values,
+ error);
+ }
+ else if (image_module->save_to_callback) {
+ /* save with simple callback */
+ return (* image_module->save_to_callback) (save_to_file_callback,
+ filehandle, pixbuf,
+ keys, values,
+ error);
+ }
+ else {
+ /* can't save */
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_UNSUPPORTED_OPERATION,
+ _("This build of gdk-pixbuf does not support saving the image format: %s"),
+ type);
+ return FALSE;
+ }
+}
+
+#define TMP_FILE_BUF_SIZE 4096
+
+static gboolean
+save_to_callback_with_tmp_file (GdkPixbufModule *image_module,
+ GdkPixbuf *pixbuf,
+ GdkPixbufSaveFunc save_func,
+ gpointer user_data,
+ gchar **keys,
+ gchar **values,
+ GError **error)
+{
+ int fd;
+ FILE *f = NULL;
+ gboolean retval = FALSE;
+ gchar *buf = NULL;
+ gsize n;
+ gchar *filename = NULL;
+
+ buf = g_try_malloc (TMP_FILE_BUF_SIZE);
+ if (buf == NULL) {
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Insufficient memory to save image to callback"));
+ goto end;
+ }
+
+ fd = g_file_open_tmp ("gdkpixbuf-save-tmp.XXXXXX", &filename, error);
+ if (fd == -1)
+ goto end;
+ f = fdopen (fd, "wb+");
+ if (f == NULL) {
+ g_set_error (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ _("Failed to open temporary file"));
+ goto end;
+ }
+ if (!(* image_module->save) (f, pixbuf, keys, values, error))
+ goto end;
+ rewind (f);
+ for (;;) {
+ n = fread (buf, 1, TMP_FILE_BUF_SIZE, f);
+ if (n > 0) {
+ if (!save_func (buf, n, error, user_data))
+ goto end;
+ }
+ if (n != TMP_FILE_BUF_SIZE)
+ break;
+ }
+ if (ferror (f)) {
+ g_set_error (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ _("Failed to read from temporary file"));
+ goto end;
+ }
+ retval = TRUE;
+
+ end:
+ /* cleanup and return retval */
+ if (f)
+ fclose (f);
+ if (filename) {
+ unlink (filename);
+ g_free (filename);
+ }
+ g_free (buf);
+
+ return retval;
+}
+
+static gboolean
+gdk_pixbuf_real_save_to_callback (GdkPixbuf *pixbuf,
+ GdkPixbufSaveFunc save_func,
+ gpointer user_data,
+ const char *type,
+ gchar **keys,
+ gchar **values,
+ GError **error)
+{
+ GdkPixbufModule *image_module = NULL;
+
+ image_module = _gdk_pixbuf_get_named_module (type, error);
+
+ if (image_module == NULL)
+ return FALSE;
+
+ if (image_module->module == NULL)
+ if (!_gdk_pixbuf_load_module (image_module, error))
+ return FALSE;
+
+ if (image_module->save_to_callback) {
+ /* save normally */
+ return (* image_module->save_to_callback) (save_func, user_data,
+ pixbuf, keys, values,
+ error);
+ }
+ else if (image_module->save) {
+ /* use a temporary file */
+ return save_to_callback_with_tmp_file (image_module, pixbuf,
+ save_func, user_data,
+ keys, values,
+ error);
+ }
+ else {
+ /* can't save */
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_UNSUPPORTED_OPERATION,
@@ -1100,10 +1251,6 @@ gdk_pixbuf_real_save (GdkPixbuf *pixbuf,
type);
return FALSE;
}
-
- return (* image_module->save) (filehandle, pixbuf,
- keys, values,
- error);
}
@@ -1115,8 +1262,25 @@ gdk_pixbuf_real_save (GdkPixbuf *pixbuf,
* @error: return location for error, or %NULL
* @Varargs: list of key-value save options
*
- * Saves pixbuf to a file in @type, which is currently "jpeg", "png" or
- * "ico". If @error is set, %FALSE will be returned. Possible errors include
+ * Saves pixbuf to a file in format @type. By default, "jpeg", "png" and
+ * "ico" are possible file formats to save in, but more formats may be
+ * installed. The list of all writable formats can be determined in the
+ * following way:
+ *
+ * <informalexample><programlisting>
+ * void add_if_writable (GdkPixbufFormat *data, GSList **list)
+ * {
+ * if (gdk_pixbuf_format_is_writable (data))
+ * *list = g_slist_prepend (*list, data);
+ * }
+ *
+ * GSList *formats = gdk_pixbuf_get_formats (<!-- -->);
+ * GSList *writable_formats = NULL;
+ * g_slist_foreach (formats, add_if_writable, &writable_formats);
+ * g_slist_free (formats);
+ * </programlisting></informalexample>
+ *
+ * If @error is set, %FALSE will be returned. Possible errors include
* those in the #GDK_PIXBUF_ERROR domain and those in the #G_FILE_ERROR domain.
*
* The variable argument list should be %NULL-terminated; if not empty,
@@ -1178,9 +1342,9 @@ gdk_pixbuf_save (GdkPixbuf *pixbuf,
* @option_values: values for named options
* @error: return location for error, or %NULL
*
- * Saves pixbuf to a file in @type, which is currently "jpeg" or "png".
- * If @error is set, %FALSE will be returned. See gdk_pixbuf_save () for more
- * details.
+ * Saves pixbuf to a file in @type, which is currently "jpeg", "png" or "ico".
+ * If @error is set, %FALSE will be returned.
+ * See gdk_pixbuf_save () for more details.
*
* Return value: whether an error was set
**/
@@ -1237,6 +1401,251 @@ gdk_pixbuf_savev (GdkPixbuf *pixbuf,
}
/**
+ * gdk_pixbuf_save_to_callback:
+ * @pixbuf: a #GdkPixbuf.
+ * @save_func: a function that is called to save each block of data that
+ * the save routine generates.
+ * @user_data: user data to pass to the save function.
+ * @type: name of file format.
+ * @error: return location for error, or %NULL
+ * @Varargs: list of key-value save options
+ *
+ * Saves pixbuf in format @type by feeding the produced data to a
+ * callback. Can be used when you want to store the image to something
+ * other than a file, such as an in-memory buffer or a socket.
+ * If @error is set, %FALSE will be returned. Possible errors
+ * include those in the #GDK_PIXBUF_ERROR domain and whatever the save
+ * function generates.
+ *
+ * See gdk_pixbuf_save() for more details.
+ *
+ * Return value: whether an error was set
+ *
+ * Since: 2.4
+ **/
+gboolean
+gdk_pixbuf_save_to_callback (GdkPixbuf *pixbuf,
+ GdkPixbufSaveFunc save_func,
+ gpointer user_data,
+ const char *type,
+ GError **error,
+ ...)
+{
+ gchar **keys = NULL;
+ gchar **values = NULL;
+ va_list args;
+ gboolean result;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ va_start (args, error);
+
+ collect_save_options (args, &keys, &values);
+
+ va_end (args);
+
+ result = gdk_pixbuf_save_to_callbackv (pixbuf, save_func, user_data,
+ type, keys, values,
+ error);
+
+ g_strfreev (keys);
+ g_strfreev (values);
+
+ return result;
+}
+
+/**
+ * gdk_pixbuf_save_to_callbackv:
+ * @pixbuf: a #GdkPixbuf.
+ * @save_func: a function that is called to save each block of data that
+ * the save routine generates.
+ * @user_data: user data to pass to the save function.
+ * @type: 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 callback in format @type, which is currently "jpeg",
+ * "png" or "ico". If @error is set, %FALSE will be returned. See
+ * gdk_pixbuf_save_to_callback () for more details.
+ *
+ * Return value: whether an error was set
+ *
+ * Since: 2.4
+ **/
+gboolean
+gdk_pixbuf_save_to_callbackv (GdkPixbuf *pixbuf,
+ GdkPixbufSaveFunc save_func,
+ gpointer user_data,
+ const char *type,
+ char **option_keys,
+ char **option_values,
+ GError **error)
+{
+ gboolean result;
+
+
+ g_return_val_if_fail (save_func != NULL, FALSE);
+ g_return_val_if_fail (type != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ result = gdk_pixbuf_real_save_to_callback (pixbuf,
+ save_func, user_data, type,
+ option_keys, option_values,
+ error);
+
+ if (!result) {
+ g_return_val_if_fail (error == NULL || *error != NULL, FALSE);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gdk_pixbuf_save_to_buffer:
+ * @pixbuf: a #GdkPixbuf.
+ * @buffer: location to receive a pointer to the new buffer.
+ * @buffer_size: location to receive the size of the new buffer.
+ * @type: name of file format.
+ * @error: return location for error, or %NULL
+ * @Varargs: list of key-value save options
+ *
+ * Saves pixbuf to a new buffer in format @type, which is currently "jpeg",
+ * "png" or "ico". This is a convenience function that uses
+ * gdk_pixbuf_save_to_callback() to do the real work. Note that the buffer
+ * is not nul-terminated and may contain embedded nuls.
+ * If @error is set, %FALSE will be returned and @string will be set to
+ * %NULL. Possible errors include those in the #GDK_PIXBUF_ERROR
+ * domain.
+ *
+ * See gdk_pixbuf_save() for more details.
+ *
+ * Return value: whether an error was set
+ *
+ * Since: 2.4
+ **/
+gboolean
+gdk_pixbuf_save_to_buffer (GdkPixbuf *pixbuf,
+ gchar **buffer,
+ gsize *buffer_size,
+ const char *type,
+ GError **error,
+ ...)
+{
+ gchar **keys = NULL;
+ gchar **values = NULL;
+ va_list args;
+ gboolean result;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ va_start (args, error);
+
+ collect_save_options (args, &keys, &values);
+
+ va_end (args);
+
+ result = gdk_pixbuf_save_to_bufferv (pixbuf, buffer, buffer_size,
+ type, keys, values,
+ error);
+
+ g_strfreev (keys);
+ g_strfreev (values);
+
+ return result;
+}
+
+struct SaveToBufferData {
+ gchar *buffer;
+ gsize len, max;
+};
+
+static gboolean
+save_to_buffer_callback (const gchar *data,
+ gsize count,
+ GError **error,
+ gpointer user_data)
+{
+ struct SaveToBufferData *sdata = user_data;
+ gchar *new_buffer;
+ gsize new_max;
+
+ if (sdata->len + count > sdata->max) {
+ new_max = MAX (sdata->max*2, sdata->len + count);
+ new_buffer = g_try_realloc (sdata->buffer, new_max);
+ if (!new_buffer) {
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Insufficient memory to save image into a buffer"));
+ return FALSE;
+ }
+ sdata->buffer = new_buffer;
+ sdata->max = new_max;
+ }
+ memcpy (sdata->buffer + sdata->len, data, count);
+ sdata->len += count;
+ return TRUE;
+}
+
+/**
+ * gdk_pixbuf_save_to_bufferv:
+ * @pixbuf: a #GdkPixbuf.
+ * @buffer: location to receive a pointer to the new buffer.
+ * @buffer_size: location to receive the size of the new buffer.
+ * @type: 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 new buffer in format @type, which is currently "jpeg",
+ * "png" or "ico". See gdk_pixbuf_save_to_buffer() for more details.
+ *
+ * Return value: whether an error was set
+ *
+ * Since: 2.4
+ **/
+gboolean
+gdk_pixbuf_save_to_bufferv (GdkPixbuf *pixbuf,
+ gchar **buffer,
+ gsize *buffer_size,
+ const char *type,
+ char **option_keys,
+ char **option_values,
+ GError **error)
+{
+ static const gint initial_max = 1024;
+ struct SaveToBufferData sdata;
+
+ *buffer = NULL;
+ *buffer_size = 0;
+
+ sdata.buffer = g_try_malloc (initial_max);
+ sdata.max = initial_max;
+ sdata.len = 0;
+ if (!sdata.buffer) {
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Insufficient memory to save image into a buffer"));
+ return FALSE;
+ }
+
+ if (!gdk_pixbuf_save_to_callbackv (pixbuf,
+ save_to_buffer_callback, &sdata,
+ type, option_keys, option_values,
+ error)) {
+ g_free (sdata.buffer);
+ return FALSE;
+ }
+
+ *buffer = sdata.buffer;
+ *buffer_size = sdata.len;
+ return TRUE;
+}
+
+/**
* gdk_pixbuf_format_get_name:
* @format: a #GdkPixbufFormat
*
diff --git a/gdk-pixbuf/gdk-pixbuf-io.h b/gdk-pixbuf/gdk-pixbuf-io.h
index d62669af20..b4fadce99b 100644
--- a/gdk-pixbuf/gdk-pixbuf-io.h
+++ b/gdk-pixbuf/gdk-pixbuf-io.h
@@ -89,11 +89,19 @@ struct _GdkPixbufModule {
GdkPixbufAnimation *(* load_animation) (FILE *f,
GError **error);
+ /* Saving */
gboolean (* save) (FILE *f,
GdkPixbuf *pixbuf,
gchar **param_keys,
gchar **param_values,
GError **error);
+
+ gboolean (*save_to_callback) (GdkPixbufSaveFunc save_func,
+ gpointer user_data,
+ GdkPixbuf *pixbuf,
+ gchar **option_keys,
+ gchar **option_values,
+ GError **error);
/*< private >*/
void (*_reserved1) (void);
@@ -101,7 +109,6 @@ struct _GdkPixbufModule {
void (*_reserved3) (void);
void (*_reserved4) (void);
void (*_reserved5) (void);
- void (*_reserved6) (void);
};
@@ -116,7 +123,7 @@ gboolean gdk_pixbuf_set_option (GdkPixbuf *pixbuf,
typedef enum /*< skip >*/
{
- GDK_PIXBUF_FORMAT_WRITABLE = 1 << 0
+ GDK_PIXBUF_FORMAT_WRITABLE = 1 << 0,
} GdkPixbufFormatFlags;
struct _GdkPixbufFormat {
diff --git a/gdk-pixbuf/gdk-pixbuf-loader.c b/gdk-pixbuf/gdk-pixbuf-loader.c
index d980eea97b..54eff3280d 100644
--- a/gdk-pixbuf/gdk-pixbuf-loader.c
+++ b/gdk-pixbuf/gdk-pixbuf-loader.c
@@ -225,7 +225,6 @@ gdk_pixbuf_loader_finalize (GObject *object)
if (!priv->closed)
g_warning ("GdkPixbufLoader finalized without calling gdk_pixbuf_loader_close() - this is not allowed. You must explicitly end the data stream to the loader before dropping the last reference.");
-
if (priv->animation)
g_object_unref (priv->animation);
diff --git a/gdk-pixbuf/gdk-pixbuf.h b/gdk-pixbuf/gdk-pixbuf.h
index 234f88310e..a4054cc078 100644
--- a/gdk-pixbuf/gdk-pixbuf.h
+++ b/gdk-pixbuf/gdk-pixbuf.h
@@ -170,6 +170,45 @@ gboolean gdk_pixbuf_savev (GdkPixbuf *pixbuf,
char **option_values,
GError **error);
+/* Saving to a callback function */
+
+typedef gboolean (*GdkPixbufSaveFunc) (const gchar *buf,
+ gsize count,
+ GError **error,
+ gpointer data);
+
+gboolean gdk_pixbuf_save_to_callback (GdkPixbuf *pixbuf,
+ GdkPixbufSaveFunc save_func,
+ gpointer user_data,
+ const char *type,
+ GError **error,
+ ...);
+
+gboolean gdk_pixbuf_save_to_callbackv (GdkPixbuf *pixbuf,
+ GdkPixbufSaveFunc save_func,
+ gpointer user_data,
+ const char *type,
+ char **option_keys,
+ char **option_values,
+ GError **error);
+
+/* Saving into a newly allocated char array */
+
+gboolean gdk_pixbuf_save_to_buffer (GdkPixbuf *pixbuf,
+ gchar **buffer,
+ gsize *buffer_size,
+ const char *type,
+ GError **error,
+ ...);
+
+gboolean gdk_pixbuf_save_to_bufferv (GdkPixbuf *pixbuf,
+ gchar **buffer,
+ gsize *buffer_size,
+ const char *type,
+ char **option_keys,
+ char **option_values,
+ GError **error);
+
/* 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/gdk_pixbuf.def b/gdk-pixbuf/gdk_pixbuf.def
index c7cd424649..c5042f94f3 100644
--- a/gdk-pixbuf/gdk_pixbuf.def
+++ b/gdk-pixbuf/gdk_pixbuf.def
@@ -33,6 +33,7 @@ EXPORTS
gdk_pixbuf_from_pixdata
gdk_pixbuf_get_bits_per_sample
gdk_pixbuf_get_colorspace
+ gdk_pixbuf_get_file_info
gdk_pixbuf_get_formats
gdk_pixbuf_get_has_alpha
gdk_pixbuf_get_height
@@ -60,6 +61,10 @@ EXPORTS
gdk_pixbuf_ref
gdk_pixbuf_saturate_and_pixelate
gdk_pixbuf_save
+ gdk_pixbuf_save_to_callback
+ gdk_pixbuf_save_to_callbackv
+ gdk_pixbuf_save_to_buffer
+ gdk_pixbuf_save_to_bufferv
gdk_pixbuf_savev
gdk_pixbuf_scale
gdk_pixbuf_scale_simple
diff --git a/gdk-pixbuf/io-jpeg.c b/gdk-pixbuf/io-jpeg.c
index 85d75dfe60..4dd5f8f2a5 100644
--- a/gdk-pixbuf/io-jpeg.c
+++ b/gdk-pixbuf/io-jpeg.c
@@ -780,12 +780,85 @@ gdk_pixbuf__jpeg_image_load_increment (gpointer data,
return TRUE;
}
+/* Save */
+
+#define TO_FUNCTION_BUF_SIZE 4096
+
+typedef struct {
+ struct jpeg_destination_mgr pub;
+ JOCTET *buffer;
+ GdkPixbufSaveFunc save_func;
+ gpointer user_data;
+ GError **error;
+} ToFunctionDestinationManager;
+
+void
+to_callback_init (j_compress_ptr cinfo)
+{
+ ToFunctionDestinationManager *destmgr;
+
+ destmgr = (ToFunctionDestinationManager*) cinfo->dest;
+ destmgr->pub.next_output_byte = destmgr->buffer;
+ destmgr->pub.free_in_buffer = TO_FUNCTION_BUF_SIZE;
+}
+
+static void
+to_callback_do_write (j_compress_ptr cinfo, gsize length)
+{
+ ToFunctionDestinationManager *destmgr;
+
+ destmgr = (ToFunctionDestinationManager*) cinfo->dest;
+ if (!destmgr->save_func (destmgr->buffer,
+ length,
+ destmgr->error,
+ destmgr->user_data)) {
+ struct error_handler_data *errmgr;
+
+ errmgr = (struct error_handler_data *) cinfo->err;
+ /* Use a default error message if the callback didn't set one,
+ * which it should have.
+ */
+ if (errmgr->error && *errmgr->error == NULL) {
+ g_set_error (errmgr->error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ "write function failed");
+ }
+ siglongjmp (errmgr->setjmp_buffer, 1);
+ g_assert_not_reached ();
+ }
+}
+
+static guchar
+to_callback_empty_output_buffer (j_compress_ptr cinfo)
+{
+ ToFunctionDestinationManager *destmgr;
+
+ destmgr = (ToFunctionDestinationManager*) cinfo->dest;
+ to_callback_do_write (cinfo, TO_FUNCTION_BUF_SIZE);
+ destmgr->pub.next_output_byte = destmgr->buffer;
+ destmgr->pub.free_in_buffer = TO_FUNCTION_BUF_SIZE;
+ return TRUE;
+}
+
+void
+to_callback_terminate (j_compress_ptr cinfo)
+{
+ ToFunctionDestinationManager *destmgr;
+
+ destmgr = (ToFunctionDestinationManager*) cinfo->dest;
+ to_callback_do_write (cinfo, TO_FUNCTION_BUF_SIZE - destmgr->pub.free_in_buffer);
+}
+
static gboolean
-gdk_pixbuf__jpeg_image_save (FILE *f,
- GdkPixbuf *pixbuf,
- gchar **keys,
- gchar **values,
- GError **error)
+real_save_jpeg (GdkPixbuf *pixbuf,
+ gchar **keys,
+ gchar **values,
+ GError **error,
+ gboolean to_callback,
+ FILE *f,
+ GdkPixbufSaveFunc save_func,
+ gpointer user_data)
{
/* FIXME error handling is broken */
@@ -800,6 +873,9 @@ gdk_pixbuf__jpeg_image_save (FILE *f,
int w, h = 0;
int rowstride = 0;
struct error_handler_data jerr;
+ ToFunctionDestinationManager to_callback_destmgr;
+
+ to_callback_destmgr.buffer = NULL;
if (keys && *keys) {
gchar **kiter = keys;
@@ -854,7 +930,9 @@ gdk_pixbuf__jpeg_image_save (FILE *f,
pixels = gdk_pixbuf_get_pixels (pixbuf);
g_return_val_if_fail (pixels != NULL, FALSE);
- /* allocate a small buffer to convert image data */
+ /* Allocate a small buffer to convert image data,
+ * and a larger buffer if doing to_callback save.
+ */
buf = g_try_malloc (w * 3 * sizeof (guchar));
if (!buf) {
g_set_error (error,
@@ -863,6 +941,16 @@ gdk_pixbuf__jpeg_image_save (FILE *f,
_("Couldn't allocate memory for loading JPEG file"));
return FALSE;
}
+ if (to_callback) {
+ to_callback_destmgr.buffer = g_try_malloc (TO_FUNCTION_BUF_SIZE);
+ if (!to_callback_destmgr.buffer) {
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Couldn't allocate memory for loading JPEG file"));
+ return FALSE;
+ }
+ }
/* set up error handling */
jerr.pub.error_exit = fatal_error_handler;
@@ -873,12 +961,23 @@ gdk_pixbuf__jpeg_image_save (FILE *f,
if (sigsetjmp (jerr.setjmp_buffer, 1)) {
jpeg_destroy_compress (&cinfo);
g_free (buf);
+ g_free (to_callback_destmgr.buffer);
return FALSE;
}
/* setup compress params */
jpeg_create_compress (&cinfo);
- jpeg_stdio_dest (&cinfo, f);
+ if (to_callback) {
+ to_callback_destmgr.pub.init_destination = to_callback_init;
+ to_callback_destmgr.pub.empty_output_buffer = to_callback_empty_output_buffer;
+ to_callback_destmgr.pub.term_destination = to_callback_terminate;
+ to_callback_destmgr.error = error;
+ to_callback_destmgr.save_func = save_func;
+ to_callback_destmgr.user_data = user_data;
+ cinfo.dest = (struct jpeg_destination_mgr*) &to_callback_destmgr;
+ } else {
+ jpeg_stdio_dest (&cinfo, f);
+ }
cinfo.image_width = w;
cinfo.image_height = h;
cinfo.input_components = 3;
@@ -909,9 +1008,33 @@ gdk_pixbuf__jpeg_image_save (FILE *f,
jpeg_finish_compress (&cinfo);
jpeg_destroy_compress(&cinfo);
g_free (buf);
+ g_free (to_callback_destmgr.buffer);
return TRUE;
}
+static gboolean
+gdk_pixbuf__jpeg_image_save (FILE *f,
+ GdkPixbuf *pixbuf,
+ gchar **keys,
+ gchar **values,
+ GError **error)
+{
+ return real_save_jpeg (pixbuf, keys, values, error,
+ FALSE, f, NULL, NULL);
+}
+
+static gboolean
+gdk_pixbuf__jpeg_image_save_to_callback (GdkPixbufSaveFunc save_func,
+ gpointer user_data,
+ GdkPixbuf *pixbuf,
+ gchar **keys,
+ gchar **values,
+ GError **error)
+{
+ return real_save_jpeg (pixbuf, keys, values, error,
+ TRUE, NULL, save_func, user_data);
+}
+
void
MODULE_ENTRY (jpeg, fill_vtable) (GdkPixbufModule *module)
{
@@ -920,6 +1043,7 @@ MODULE_ENTRY (jpeg, fill_vtable) (GdkPixbufModule *module)
module->stop_load = gdk_pixbuf__jpeg_image_stop_load;
module->load_increment = gdk_pixbuf__jpeg_image_load_increment;
module->save = gdk_pixbuf__jpeg_image_save;
+ module->save_to_callback = gdk_pixbuf__jpeg_image_save_to_callback;
}
void
diff --git a/gdk-pixbuf/io-png.c b/gdk-pixbuf/io-png.c
index fa35734a76..669cb7bfc5 100644
--- a/gdk-pixbuf/io-png.c
+++ b/gdk-pixbuf/io-png.c
@@ -741,12 +741,40 @@ png_warning_callback(png_structp png_read_ptr,
/* Save */
-static gboolean
-gdk_pixbuf__png_image_save (FILE *f,
- GdkPixbuf *pixbuf,
- gchar **keys,
- gchar **values,
- GError **error)
+typedef struct {
+ GdkPixbufSaveFunc save_func;
+ gpointer user_data;
+ GError **error;
+} SaveToFunctionIoPtr;
+
+static void
+png_save_to_callback_write_func (png_structp png_ptr,
+ png_bytep data,
+ png_size_t length)
+{
+ SaveToFunctionIoPtr *ioptr = png_get_io_ptr (png_ptr);
+
+ if (!ioptr->save_func (data, length, ioptr->error, ioptr->user_data)) {
+ /* If save_func has already set an error, which it
+ should have done, this won't overwrite it. */
+ png_error (png_ptr, "write function failed");
+ }
+}
+
+static void
+png_save_to_callback_flush_func (png_structp png_ptr)
+{
+ ;
+}
+
+static gboolean real_save_png (GdkPixbuf *pixbuf,
+ gchar **keys,
+ gchar **values,
+ GError **error,
+ gboolean to_callback,
+ FILE *f,
+ GdkPixbufSaveFunc save_func,
+ gpointer user_data)
{
png_structp png_ptr;
png_infop info_ptr;
@@ -762,6 +790,7 @@ gdk_pixbuf__png_image_save (FILE *f,
int bpc;
int num_keys;
gboolean success = TRUE;
+ SaveToFunctionIoPtr to_callback_ioptr;
num_keys = 0;
@@ -860,7 +889,16 @@ gdk_pixbuf__png_image_save (FILE *f,
png_set_text (png_ptr, info_ptr, text_ptr, num_keys);
}
- png_init_io (png_ptr, f);
+ if (to_callback) {
+ to_callback_ioptr.save_func = save_func;
+ to_callback_ioptr.user_data = user_data;
+ to_callback_ioptr.error = error;
+ png_set_write_fn (png_ptr, &to_callback_ioptr,
+ png_save_to_callback_write_func,
+ png_save_to_callback_flush_func);
+ } else {
+ png_init_io (png_ptr, f);
+ }
if (has_alpha) {
png_set_IHDR (png_ptr, info_ptr, w, h, bpc,
@@ -901,6 +939,29 @@ cleanup:
return success;
}
+static gboolean
+gdk_pixbuf__png_image_save (FILE *f,
+ GdkPixbuf *pixbuf,
+ gchar **keys,
+ gchar **values,
+ GError **error)
+{
+ return real_save_png (pixbuf, keys, values, error,
+ FALSE, f, NULL, NULL);
+}
+
+static gboolean
+gdk_pixbuf__png_image_save_to_callback (GdkPixbufSaveFunc save_func,
+ gpointer user_data,
+ GdkPixbuf *pixbuf,
+ gchar **keys,
+ gchar **values,
+ GError **error)
+{
+ return real_save_png (pixbuf, keys, values, error,
+ TRUE, NULL, save_func, user_data);
+}
+
void
MODULE_ENTRY (png, fill_vtable) (GdkPixbufModule *module)
{
@@ -909,6 +970,7 @@ MODULE_ENTRY (png, fill_vtable) (GdkPixbufModule *module)
module->stop_load = gdk_pixbuf__png_image_stop_load;
module->load_increment = gdk_pixbuf__png_image_load_increment;
module->save = gdk_pixbuf__png_image_save;
+ module->save_to_callback = gdk_pixbuf__png_image_save_to_callback;
}
void