[gegl] webp: Port webp-load operation to GIO



commit df44954d8b0f137f5d127076e9e2f0e55e4170cb
Author: Martin Blanchard <tchaik gmx com>
Date:   Wed Feb 3 22:08:43 2016 +0100

    webp: Port webp-load operation to GIO
    
    Make use of gegl_gio_open_input_stream() service. New 'uri'
    property (along the existing 'path' one); benefit is that files
    can be opened from distant locations now.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=761521

 operations/external/webp-load.c |  292 +++++++++++++++++++++++++++++++++------
 1 files changed, 248 insertions(+), 44 deletions(-)
---
diff --git a/operations/external/webp-load.c b/operations/external/webp-load.c
index c661cfd..72412a4 100644
--- a/operations/external/webp-load.c
+++ b/operations/external/webp-load.c
@@ -23,86 +23,246 @@
 #ifdef GEGL_PROPERTIES
 
 property_file_path (path, _("File"), "")
-    description(_("Path of file to load."))
+  description (_("Path of file to load"))
+property_uri (uri, _("URI"), "")
+  description (_("URI for file to load"))
 
 #else
 
 #define GEGL_OP_SOURCE
 #define GEGL_OP_C_SOURCE webp-load.c
 
-#include "gegl-op.h"
+#include <gegl-op.h>
+#include <gegl-gio-private.h>
 #include <webp/decode.h>
 
-static gboolean
-read_webp (const gchar *path, GeglBuffer *buf, GeglRectangle *bounds_out, const Babl **format_out)
+#define IO_BUFFER_SIZE 4096
+
+typedef struct
 {
-  GMappedFile *map = g_mapped_file_new (path, FALSE, NULL);
+  GFile *file;
+  GInputStream *stream;
+
+  WebPDecoderConfig *config;
+  WebPIDecoder *decoder;
 
-  const gpointer data = g_mapped_file_get_contents (map);
-  gsize data_size = g_mapped_file_get_length (map);
+  const Babl *format;
 
-  const Babl* format;
-  GeglRectangle bounds = {0, };
+  gint width;
+  gint height;
+} Priv;
 
-  WebPDecoderConfig config;
-  if (!WebPInitDecoderConfig (&config) ||
-      WebPGetFeatures (data, data_size, &config.input) != VP8_STATUS_OK)
+static void
+cleanup(GeglOperation *operation)
+{
+  GeglProperties *o = GEGL_PROPERTIES (operation);
+  Priv *p = (Priv*) o->user_data;
+
+  if (p != NULL)
     {
-      g_mapped_file_unref (map);
-      return FALSE;
+      if (p->decoder != NULL)
+        WebPIDelete (p->decoder);
+      p->decoder = NULL;
+      if (p->config != NULL)
+        WebPFreeDecBuffer (&p->config->output);
+      if (p->config != NULL)
+        g_free (p->config);
+      p->config = NULL;
+
+      if (p->stream != NULL)
+        g_input_stream_close (G_INPUT_STREAM (p->stream), NULL, NULL);
+      if (p->stream != NULL)
+        g_clear_object (&p->stream);
+
+      if (p->file != NULL)
+        g_clear_object (&p->file);
+
+      p->width = p->height = 0;
+      p->format = NULL;
     }
+}
+
+static gsize
+read_from_stream (GInputStream *stream,
+                  guchar **buffer,
+                  gsize size)
+{
+  GError *error = NULL;
+  gboolean success;
+  gsize read;
+
+  *buffer = g_try_new (guchar, size);
 
-  bounds.width  = config.input.width;
-  bounds.height = config.input.height;
+  g_assert (*buffer != NULL);
 
-  if (config.input.has_alpha)
+  success = g_input_stream_read_all (G_INPUT_STREAM (stream),
+                                     (void *) *buffer, size, &read,
+                                     NULL, &error);
+  if (!success || error != NULL)
     {
-      config.output.colorspace = MODE_RGBA;
-      format = babl_format ("R'G'B'A u8");
+      g_warning (error->message);
+      g_error_free (error);
+      return -1;
+    }
+
+  return read;
+}
+
+static gsize
+decode_from_stream (GInputStream *stream,
+                    WebPIDecoder *decoder)
+{
+  GError *error = NULL;
+  const gsize size = IO_BUFFER_SIZE;
+  guchar *buffer;
+  gsize read, total = 0;
+  VP8StatusCode status;
+  gboolean success;
+
+  buffer = g_try_new (guchar, size);
+
+  g_assert (buffer != NULL);
+
+  do
+    {
+      success = g_input_stream_read_all (G_INPUT_STREAM (stream),
+                                         (void *) buffer, size, &read,
+                                         NULL, &error);
+      if (!success || error != NULL)
+        {
+          g_warning (error->message);
+          g_error_free (error);
+          return -1;
+        }
+      else if (read > 0)
+        {
+          total += read;
+
+          status = WebPIAppend (decoder, buffer, read);
+          if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED)
+            return -1;
+          else if (status == VP8_STATUS_OK)
+            break;
+        }
+    }
+  while (success && read > 0);
+
+  return total;
+}
+
+static gboolean
+query_webp (GeglOperation *operation)
+{
+  GeglProperties *o = GEGL_PROPERTIES (operation);
+  Priv *p = (Priv*) o->user_data;
+
+  g_return_val_if_fail (p->config != NULL, FALSE);
+
+  if (p->config->input.has_alpha)
+    {
+      p->config->output.colorspace = MODE_RGBA;
+      p->format = babl_format ("R'G'B'A u8");
     }
   else
     {
-      config.output.colorspace = MODE_RGB;
-      format = babl_format ("R'G'B' u8");
+      p->config->output.colorspace = MODE_RGB;
+      p->format = babl_format ("R'G'B' u8");
     }
 
-  if (buf)
+  p->height = p->config->input.height;
+  p->width = p->config->input.width;
+
+  return TRUE;
+}
+
+static void
+prepare (GeglOperation *operation)
+{
+  GeglProperties *o = GEGL_PROPERTIES (operation);
+  Priv *p = (o->user_data) ? o->user_data : g_new0 (Priv, 1);
+  GError *error = NULL;
+  GFile *file = NULL;
+  guchar *buffer;
+  gsize read;
+
+  g_assert (p != NULL);
+
+  if (p->file != NULL && (o->uri || o->path))
     {
-      if (WebPDecode (data, data_size, &config) != VP8_STATUS_OK)
+      if (o->uri && strlen (o->uri) > 0)
+        file = g_file_new_for_uri (o->uri);
+      else if (o->path && strlen (o->path) > 0)
+        file = g_file_new_for_path (o->path);
+      if (file != NULL)
         {
-          WebPFreeDecBuffer (&config.output);
-          g_mapped_file_unref (map);
-          return FALSE;
+          if (!g_file_equal (p->file, file))
+            cleanup (operation);
+          g_object_unref (file);
         }
+    }
 
-      gegl_buffer_set (buf, &bounds, 0, format, config.output.u.RGBA.rgba,
-                       config.output.u.RGBA.stride);
+  o->user_data = (void*) p;
 
-      WebPFreeDecBuffer (&config.output);
-    }
+  if (p->config == NULL)
+    {
+      p->stream = gegl_gio_open_input_stream (o->uri, o->path, &p->file, &error);
+      if (p->stream == NULL)
+        {
+          g_warning (error->message);
+          g_error_free (error);
+          cleanup (operation);
+          return;
+        }
+
+      p->config = g_try_new (WebPDecoderConfig, 1);
+      p->decoder = WebPINewDecoder (&p->config->output);
+
+      g_assert (p->config != NULL);
 
-  if (bounds_out)
-    *bounds_out = bounds;
+      if (!WebPInitDecoderConfig (p->config))
+        {
+          g_warning ("could not initialise WebP decoder configuration");
+          cleanup (operation);
+          return;
+        }
+
+      read = read_from_stream (p->stream, &buffer, IO_BUFFER_SIZE);
+      if (WebPGetFeatures (buffer, read, &p->config->input) != VP8_STATUS_OK)
+        {
+          g_warning ("failed reading WebP image file");
+          cleanup (operation);
+          g_free (buffer);
+          return;
+        }
 
-  if (format_out)
-    *format_out = format;
+      if (!query_webp (operation))
+        {
+          g_warning ("could not query WebP image file");
+          cleanup (operation);
+          g_free (buffer);
+          return;
+        }
 
-  g_mapped_file_unref (map);
+       WebPIAppend (p->decoder, buffer, read);
 
-  return TRUE;
+      g_free (buffer);
+    }
+
+  gegl_operation_set_format (operation, "output", p->format);
 }
 
 static GeglRectangle
 get_bounding_box (GeglOperation *operation)
 {
-  GeglProperties   *o = GEGL_PROPERTIES (operation);
-  GeglRectangle result = {0,0,0,0};
-  const Babl   *format = NULL;
-
-  read_webp (o->path, NULL, &result, &format);
+  GeglProperties *o = GEGL_PROPERTIES (operation);
+  GeglRectangle result = { 0, 0, 0, 0 };
+  Priv *p = (Priv*) o->user_data;
 
-  if (format)
-    gegl_operation_set_format (operation, "output", format);
+  if (p->config != NULL)
+    {
+      result.width = p->width;
+      result.height = p->height;
+    }
 
   return result;
 }
@@ -114,7 +274,32 @@ process (GeglOperation       *operation,
          gint                 level)
 {
   GeglProperties *o = GEGL_PROPERTIES (operation);
-  return read_webp (o->path, output, NULL, NULL);
+  Priv *p = (Priv*) o->user_data;
+
+  if (p->config != NULL)
+    {
+      if (p->decoder != NULL)
+        {
+          if (decode_from_stream (p->stream, p->decoder) < 0)
+            {
+              g_warning ("failed decoding WebP image file");
+              cleanup (operation);
+              return FALSE;
+            }
+
+          g_input_stream_close (G_INPUT_STREAM (p->stream), NULL, NULL);
+          g_clear_object (&p->stream);
+
+          WebPIDelete (p->decoder);
+          p->decoder = NULL;
+        }
+
+      gegl_buffer_set (output, result, 0, p->format,
+                       p->config->output.u.RGBA.rgba,
+                       p->config->output.u.RGBA.stride);
+    }
+
+  return FALSE;
 }
 
 static GeglRectangle
@@ -125,15 +310,34 @@ get_cached_region (GeglOperation       *operation,
 }
 
 static void
+finalize(GObject *object)
+{
+  GeglProperties *o = GEGL_PROPERTIES (object);
+
+  if (o->user_data != NULL)
+    {
+      cleanup (GEGL_OPERATION (object));
+      if (o->user_data != NULL)
+        g_free (o->user_data);
+      o->user_data = NULL;
+    }
+
+  G_OBJECT_CLASS (gegl_op_parent_class)->finalize (object);
+}
+
+static void
 gegl_op_class_init (GeglOpClass *klass)
 {
   GeglOperationClass       *operation_class;
   GeglOperationSourceClass *source_class;
 
+  G_OBJECT_CLASS(klass)->finalize = finalize;
+
   operation_class = GEGL_OPERATION_CLASS (klass);
   source_class    = GEGL_OPERATION_SOURCE_CLASS (klass);
 
   source_class->process = process;
+  operation_class->prepare = prepare;
   operation_class->get_bounding_box = get_bounding_box;
   operation_class->get_cached_region = get_cached_region;
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]