summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Otte <otte@redhat.com>2011-03-20 19:19:42 +0100
committerBenjamin Otte <otte@redhat.com>2011-03-24 17:32:36 +0100
commit61d4dd6223e9249197c16f92413563116ea6be5a (patch)
treee1a89f19d5eba027bd99fba296441277e3134970
parent087b1381dde1fc9ef2c20bdbe827a6179c6021f6 (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.am2
-rw-r--r--pig/pig-loader-private.h72
-rw-r--r--pig/pig-loader.c124
-rw-r--r--pig/pig-loading.c348
-rw-r--r--pig/pig-loading.h2
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,