diff options
author | Benjamin Otte <otte@redhat.com> | 2011-03-20 19:19:42 +0100 |
---|---|---|
committer | Benjamin Otte <otte@redhat.com> | 2011-03-24 17:32:36 +0100 |
commit | 61d4dd6223e9249197c16f92413563116ea6be5a (patch) | |
tree | e1a89f19d5eba027bd99fba296441277e3134970 | |
parent | 087b1381dde1fc9ef2c20bdbe827a6179c6021f6 (diff) |
Add PigLoader interface
This interface is supposed to be used by classes that support loading
images.
Also implement the interface in the pig loading functions.
-rw-r--r-- | pig/Makefile.am | 2 | ||||
-rw-r--r-- | pig/pig-loader-private.h | 72 | ||||
-rw-r--r-- | pig/pig-loader.c | 124 | ||||
-rw-r--r-- | pig/pig-loading.c | 348 | ||||
-rw-r--r-- | pig/pig-loading.h | 2 |
5 files changed, 540 insertions, 8 deletions
diff --git a/pig/Makefile.am b/pig/Makefile.am index f775a2b..0ea4843 100644 --- a/pig/Makefile.am +++ b/pig/Makefile.am @@ -2,6 +2,7 @@ lib_LTLIBRARIES = libpig-@PIG_MAJORMINOR@.la libpig_@PIG_MAJORMINOR@_la_SOURCES = \ pig-enums.c \ + pig-loader.c \ pig-loading.c \ pig-marshal.c \ pig-picture.c \ @@ -60,6 +61,7 @@ nodist_libpig_@PIG_MAJORMINOR@include_HEADERS = \ pig-version.h noinst_HEADERS = \ + pig-loader-private.h \ pig-marshal-private.h EXTRA_DIST = \ diff --git a/pig/pig-loader-private.h b/pig/pig-loader-private.h new file mode 100644 index 0000000..1c2a6c6 --- /dev/null +++ b/pig/pig-loader-private.h @@ -0,0 +1,72 @@ +/* Pig + * Copyright (C) 2011 Benjamin Otte <otte@gnome.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#if !defined (__PIG_H_INSIDE__) && !defined (PIG_COMPILATION) +#error "Only <pig/pig.h> can be included directly." +#endif + +#ifndef __PIG_LOADER_H__ +#define __PIG_LOADER_H__ + + +#include <pig/pig-picture.h> + + +G_BEGIN_DECLS + +#define PIG_TYPE_LOADER (_pig_loader_get_type ()) +#define PIG_LOADER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIG_TYPE_LOADER, PigLoader)) +#define PIG_LOADER_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), PIG_TYPE_LOADER, PigLoaderInterface)) +#define PIG_IS_LOADER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIG_TYPE_LOADER)) +#define PIG_LOADER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), PIG_TYPE_LOADER, PigLoaderInterface)) + +typedef struct _PigLoader PigLoader; /* Dummy typedef */ +typedef struct _PigLoaderInterface PigLoaderInterface; + +struct _PigLoaderInterface +{ + GTypeInterface g_iface; + + const char const ** mime_types; + + gboolean (* write) (PigLoader * loader, + const guchar * data, + gsize size, + GError ** error); + gboolean (* close) (PigLoader * loader, + GError ** error); +}; + + +GType _pig_loader_get_type (void) G_GNUC_CONST; + +PigPicture * _pig_loader_open (const char * content_type, + GError ** error); + +gboolean _pig_loader_write (PigLoader * loader, + const guchar *data, + gsize size, + GError ** error); +gboolean _pig_loader_close (PigLoader * loader, + GError ** error); + + +G_END_DECLS + +#endif /* __PIG_LOADER_H__ */ diff --git a/pig/pig-loader.c b/pig/pig-loader.c new file mode 100644 index 0000000..d208b18 --- /dev/null +++ b/pig/pig-loader.c @@ -0,0 +1,124 @@ +/* Pig + * Copyright (C) 2011 Benjamin Otte <otte@gnome.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#include "config.h" + +#include "pig-loader-private.h" + +#include <glib/gi18n-lib.h> + +#include "pig-loading.h" + +G_DEFINE_INTERFACE (PigLoader, _pig_loader, PIG_TYPE_PICTURE) + +static void +_pig_loader_default_init (PigLoaderInterface *iface) +{ +} + +typedef GType (* GetTypeFunc) (void); + +GetTypeFunc registered_loaders[] = { + NULL +}; + +PigPicture * +_pig_loader_open (const char *content_type, + GError ** error) +{ + PigPicture *picture = NULL; + char *description; + guint i, j; + + g_return_val_if_fail (content_type != NULL, FALSE); + + for (i = 0; registered_loaders[i]; i++) + { + PigLoaderInterface *iface; + gpointer klass; + GType loader_type; + + loader_type = registered_loaders[i] (); + klass = g_type_class_ref (loader_type); + iface = g_type_interface_peek (klass, PIG_TYPE_LOADER); + + for (j = 0; iface->mime_types[j]; j++) + { + char *iface_content_type; + gboolean equal; + + iface_content_type = g_content_type_from_mime_type (iface->mime_types[j]); + if (iface_content_type == NULL) + continue; + + equal = g_content_type_equals (content_type, iface_content_type); + g_free (iface_content_type); + + if (equal) + { + picture = g_object_new (loader_type, NULL); + g_type_class_unref (klass); + return picture; + } + } + + g_type_class_unref (klass); + } + + description = g_content_type_get_description (content_type); + g_set_error (error, PIG_ERROR, PIG_ERROR_UNKNOWN_TYPE, + _("No loader available for `%s'."), description); + g_free (description); + + return NULL; +} + +gboolean +_pig_loader_write (PigLoader * loader, + const guchar *data, + gsize size, + GError ** error) +{ + PigLoaderInterface *iface; + + g_return_val_if_fail (PIG_IS_LOADER (loader), FALSE); + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (size > 0, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + iface = PIG_LOADER_GET_INTERFACE (loader); + + return iface->write (loader, data, size, error); +} + +gboolean +_pig_loader_close (PigLoader *loader, + GError ** error) +{ + PigLoaderInterface *iface; + + g_return_val_if_fail (PIG_IS_LOADER (loader), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + iface = PIG_LOADER_GET_INTERFACE (loader); + + return iface->close (loader, error); +} + diff --git a/pig/pig-loading.c b/pig/pig-loading.c index 03faf80..f72d1dd 100644 --- a/pig/pig-loading.c +++ b/pig/pig-loading.c @@ -23,7 +23,10 @@ #include <glib/gi18n-lib.h> +#include "pig-loader-private.h" + #define PIG_READ_BUFFER_SIZE 4096 +#define PIG_READ_BUFFER_GUESS_SIZE 1024 /** * SECTION:loading @@ -69,9 +72,41 @@ pig_error_quark (void) return g_quark_from_static_string ("pig-error-quark"); } +static char * +pig_loader_sniff_mime_type (const guchar *data, + gsize size, + GError ** error) +{ + char *content_type; + char *mime_type; + + content_type = g_content_type_guess (NULL, data, size, NULL); + if (content_type == NULL) + { + g_set_error_literal (error, PIG_ERROR, PIG_ERROR_UNKNOWN_TYPE, + _("Cannot identify image type")); + return NULL; + } + + mime_type = g_content_type_get_mime_type (content_type); + + if (mime_type == NULL) + { + g_set_error (error, PIG_ERROR, PIG_ERROR_UNKNOWN_TYPE, + _("No mime type for content type `%s'"), content_type); + g_free (content_type); + return NULL; + } + + g_free (content_type); + + return mime_type; +} + /** * pig_picture_load_from_stream: * @stream: stream to load the picture from. + * @mime_type: %NULL or the mime type of the data from @stream * @flags: flags to use for loading * @cancellable: the cancellable to use or %NULL * @error: location to store a potential error or %NULL. Errors @@ -86,14 +121,86 @@ pig_error_quark (void) **/ PigPicture * pig_picture_load_from_stream (GInputStream * stream, + const char * mime_type, PigLoadFlags flags, GCancellable * cancellable, GError ** error) { - g_set_error_literal (error, PIG_ERROR, PIG_ERROR_UNKNOWN_TYPE, - _("Image loading is not supported yet.")); + PigPicture *loader; + gssize bytes_read; + guchar data[PIG_READ_BUFFER_SIZE]; + + g_return_val_if_fail (G_IS_INPUT_STREAM (stream), NULL); + g_return_val_if_fail ((flags & PIG_LOAD_INCREMENTAL) == 0, NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); - return NULL; + if (mime_type == NULL) + { + gsize bytes_read_unsigned; + char *sniffed_mime_type; + + /* need to read_all so we get enough data for typefinding */ + if (!g_input_stream_read_all (stream, + data, + PIG_READ_BUFFER_GUESS_SIZE, + &bytes_read_unsigned, + cancellable, + error)) + return NULL; + + sniffed_mime_type = pig_loader_sniff_mime_type (data, bytes_read_unsigned, error); + if (sniffed_mime_type == NULL) + return NULL; + + loader = _pig_loader_open (sniffed_mime_type, error); + + bytes_read = bytes_read_unsigned; + g_free (sniffed_mime_type); + + if (loader == NULL) + return NULL; + } + else + { + loader = _pig_loader_open (mime_type, error); + if (loader == NULL) + return NULL; + + bytes_read = g_input_stream_read (stream, + data, + sizeof (data), + cancellable, + error); + } + + while (bytes_read > 0) + { + if (!_pig_loader_write (PIG_LOADER (loader), + data, + bytes_read, + error)) + { + bytes_read = -1; + break; + } + + bytes_read = g_input_stream_read (stream, + data, + sizeof (data), + cancellable, + error); + } + + if (bytes_read != 0 || + !_pig_loader_close (PIG_LOADER (loader), error) || + !g_input_stream_close (stream, cancellable, error)) + { + g_object_unref (loader); + return NULL; + } + + return loader; } /** @@ -127,6 +234,7 @@ pig_picture_load_from_file (GFile * file, return NULL; picture = pig_picture_load_from_stream (G_INPUT_STREAM (stream), + NULL, flags, cancellable, error); @@ -173,9 +281,208 @@ pig_picture_load_from_filename (const char * filename, return picture; } +typedef struct _PigPictureLoadFromStreamData PigPictureLoadFromStreamData; +struct _PigPictureLoadFromStreamData +{ + char *mime_type; + PigLoadFlags flags; + int io_priority; + GCancellable *cancellable; + GSimpleAsyncResult *result; + guchar data[PIG_READ_BUFFER_SIZE]; + gsize n_read; + PigPicture *loader; +}; + +static PigPictureLoadFromStreamData * +pig_picture_load_from_stream_data_new (const char * mime_type, + PigLoadFlags flags, + int io_priority, + GCancellable * cancellable, + GSimpleAsyncResult *result) +{ + PigPictureLoadFromStreamData *data; + + data = g_slice_new (PigPictureLoadFromStreamData); + data->mime_type = g_strdup (mime_type); + data->flags = flags; + data->io_priority = io_priority; + if (cancellable) + g_object_ref (cancellable); + data->cancellable = cancellable; + data->result = result; + data->loader = NULL; + return data; +} + +static void +pig_picture_load_from_stream_data_free (PigPictureLoadFromStreamData *data) +{ + g_free (data->mime_type); + if (data->cancellable) + g_object_unref (data->cancellable); + if (data->loader) + g_object_unref (data->loader); + g_object_unref (data->result); + + g_slice_free (PigPictureLoadFromStreamData, data); +} + +static void +pig_picture_load_from_stream_data_complete (PigPictureLoadFromStreamData *data, + GError *error) +{ + if (error) + g_simple_async_result_take_error (data->result, error); + else + g_simple_async_result_set_op_res_gpointer (data->result, + g_object_ref (data->loader), + g_object_unref); + + g_simple_async_result_complete (data->result); + + pig_picture_load_from_stream_data_free (data); +} + +static void +pig_picture_load_from_stream_close_cb (GObject *stream, + GAsyncResult *result, + gpointer user_data) +{ + PigPictureLoadFromStreamData *data = user_data; + GError *error = NULL; + + if (!g_input_stream_close_finish (G_INPUT_STREAM (stream), + result, + &error)) + { + pig_picture_load_from_stream_data_complete (data, error); + return; + } + + pig_picture_load_from_stream_data_complete (data, NULL); +} + +static void +pig_picture_load_from_stream_read_cb (GObject *stream, + GAsyncResult *result, + gpointer user_data) +{ + PigPictureLoadFromStreamData *data = user_data; + gssize n_read; + GError *error = NULL; + + n_read = g_input_stream_read_finish (G_INPUT_STREAM (stream), + result, + &error); + + if (n_read > 0) + { + if (!_pig_loader_write (PIG_LOADER (data->loader), + data->data, + n_read, + &error)) + { + pig_picture_load_from_stream_data_complete (data, error); + return; + } + + g_input_stream_read_async (G_INPUT_STREAM (stream), + data->data, + sizeof (data->data), + data->io_priority, + data->cancellable, + pig_picture_load_from_stream_read_cb, + data); + return; + } + else if (n_read < 0) + { + pig_picture_load_from_stream_data_complete (data, error); + return; + } + + if (!_pig_loader_close (PIG_LOADER (data->loader), &error)) + { + pig_picture_load_from_stream_data_complete (data, error); + return; + } + + if (data->flags & PIG_LOAD_DO_NOT_CLOSE) + { + pig_picture_load_from_stream_data_complete (data, NULL); + return; + } + + g_input_stream_close_async (G_INPUT_STREAM (stream), + data->io_priority, + data->cancellable, + pig_picture_load_from_stream_close_cb, + data); +} + +static void +pig_picture_load_from_stream_initial_read_cb (GObject *stream, + GAsyncResult *result, + gpointer user_data) +{ + PigPictureLoadFromStreamData *data = user_data; + gssize n_read; + GError *error = NULL; + + n_read = g_input_stream_read_finish (G_INPUT_STREAM (stream), + result, + &error); + + if (n_read < 0) + { + pig_picture_load_from_stream_data_complete (data, error); + return; + } + + data->n_read += n_read; + if (n_read > 0 && data->n_read < PIG_READ_BUFFER_GUESS_SIZE) + { + g_input_stream_read_async (G_INPUT_STREAM (stream), + data->data + data->n_read, + sizeof (data->data) - data->n_read, + data->io_priority, + data->cancellable, + pig_picture_load_from_stream_initial_read_cb, + data); + return; + } + + if (data->mime_type == NULL) + { + data->mime_type = pig_loader_sniff_mime_type (data->data, data->n_read, &error); + if (data->mime_type == NULL) + { + pig_picture_load_from_stream_data_complete (data, error); + return; + } + } + data->loader = _pig_loader_open (data->mime_type, &error); + if (data->loader == NULL || + !_pig_loader_write (PIG_LOADER (data->loader), data->data, data->n_read, &error)) + { + pig_picture_load_from_stream_data_complete (data, error); + return; + } + + g_input_stream_read_async (G_INPUT_STREAM (stream), + data->data, + sizeof (data->data), + data->io_priority, + data->cancellable, + pig_picture_load_from_stream_read_cb, + data); +} + /** * pig_picture_load_from_stream_async: * @stream: stream to load the picture from. + * @mime_type: %NULL or the mime type describing the data in @stream * @flags: flags to use for loading * @io_priority: Main loop priority for the load * @cancellable: the cancellable to use or %NULL @@ -187,17 +494,36 @@ pig_picture_load_from_filename (const char * filename, **/ void pig_picture_load_from_stream_async (GInputStream * stream, + const char * mime_type, PigLoadFlags flags, int io_priority, GCancellable * cancellable, GAsyncReadyCallback callback, gpointer user_data) { - g_simple_async_report_error_in_idle (G_OBJECT (stream), - callback, - user_data, - PIG_ERROR, PIG_ERROR_UNKNOWN_TYPE, - _("Image loading is not supported yet.")); + PigPictureLoadFromStreamData *data; + GSimpleAsyncResult *result; + + g_return_if_fail (G_IS_INPUT_STREAM (stream)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + result = g_simple_async_result_new (G_OBJECT (stream), + callback, + user_data, + pig_picture_load_from_stream_async); + data = pig_picture_load_from_stream_data_new (mime_type, + flags, + io_priority, + cancellable, + result); + + g_input_stream_read_async (stream, + data->data, + sizeof (data->data), + io_priority, + cancellable, + pig_picture_load_from_stream_initial_read_cb, + data); } /** @@ -220,6 +546,7 @@ pig_picture_load_from_stream_finish (GInputStream *stream, g_return_val_if_fail (G_IS_INPUT_STREAM (stream), NULL); g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); simple = G_SIMPLE_ASYNC_RESULT (result); @@ -310,6 +637,7 @@ pig_picture_load_from_file_read_callback (GObject * object, } pig_picture_load_from_stream_async (G_INPUT_STREAM (stream), + NULL, data->flags, data->io_priority, data->cancellable, @@ -341,6 +669,9 @@ pig_picture_load_from_file_async (GFile * file, PigPictureLoadFromFileData *data; GSimpleAsyncResult *result; + g_return_if_fail (G_IS_FILE (file)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + result = g_simple_async_result_new (G_OBJECT (file), callback, user_data, @@ -378,6 +709,7 @@ pig_picture_load_from_file_finish (GFile * file, g_return_val_if_fail (G_IS_FILE (file), NULL); g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); simple = G_SIMPLE_ASYNC_RESULT (result); diff --git a/pig/pig-loading.h b/pig/pig-loading.h index b52c36d..457a843 100644 --- a/pig/pig-loading.h +++ b/pig/pig-loading.h @@ -69,6 +69,7 @@ GQuark pig_error_quark (void); PigPicture * pig_picture_load_from_stream (GInputStream * stream, + const char * mime_type, PigLoadFlags flags, GCancellable * cancellable, GError ** error); @@ -82,6 +83,7 @@ PigPicture * pig_picture_load_from_filename (const char * GError ** error); void pig_picture_load_from_stream_async (GInputStream * stream, + const char * mime_type, PigLoadFlags flags, int io_priority, GCancellable * cancellable, |