summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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,