diff options
author | Matthias Clasen <maclas@gmx.de> | 2004-01-07 01:57:42 +0000 |
---|---|---|
committer | Matthias Clasen <matthiasc@src.gnome.org> | 2004-01-07 01:57:42 +0000 |
commit | 03376d79faf862ada7fca924aed681156398d574 (patch) | |
tree | 914249bd1d2864773bec4eadf2f3f533f9523fac /gdk-pixbuf | |
parent | 5b26e695a1fc70c99b58f2e9c4c922412fbd1a08 (diff) | |
download | gtk+-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/ChangeLog | 26 | ||||
-rw-r--r-- | gdk-pixbuf/gdk-pixbuf-io.c | 429 | ||||
-rw-r--r-- | gdk-pixbuf/gdk-pixbuf-io.h | 11 | ||||
-rw-r--r-- | gdk-pixbuf/gdk-pixbuf-loader.c | 1 | ||||
-rw-r--r-- | gdk-pixbuf/gdk-pixbuf.h | 39 | ||||
-rw-r--r-- | gdk-pixbuf/gdk_pixbuf.def | 5 | ||||
-rw-r--r-- | gdk-pixbuf/io-jpeg.c | 138 | ||||
-rw-r--r-- | gdk-pixbuf/io-png.c | 76 |
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 |