[gnome-photos/wip/rishi/edit-mode: 10/29] Add an image editing pipeline



commit ee5bf0e9adbb108e44553b58a95837bc5cc3ec17
Author: Debarshi Ray <debarshir gnome org>
Date:   Thu Jan 29 15:55:48 2015 +0100

    Add an image editing pipeline

 src/Makefile.am        |    2 +
 src/photos-base-item.c |  127 ++++++++++++++++++-
 src/photos-base-item.h |   21 +++
 src/photos-pipeline.c  |  318 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/photos-pipeline.h  |   81 ++++++++++++
 5 files changed, 543 insertions(+), 6 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 4107d23..a3a5b80 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -132,6 +132,8 @@ gnome_photos_SOURCES = \
        photos-organize-collection-view.h \
        photos-overview-searchbar.c \
        photos-overview-searchbar.h \
+       photos-pipeline.c \
+       photos-pipeline.h \
        photos-preview-model.c \
        photos-preview-model.h \
        photos-preview-nav-buttons.c \
diff --git a/src/photos-base-item.c b/src/photos-base-item.c
index 4743eaf..89380c9 100644
--- a/src/photos-base-item.c
+++ b/src/photos-base-item.c
@@ -26,6 +26,7 @@
 
 #include "config.h"
 
+#include <stdarg.h>
 #include <string.h>
 
 #include <gdk/gdk.h>
@@ -42,6 +43,7 @@
 #include "photos-filterable.h"
 #include "photos-icons.h"
 #include "photos-local-item.h"
+#include "photos-pipeline.h"
 #include "photos-print-notification.h"
 #include "photos-print-operation.h"
 #include "photos-query.h"
@@ -56,13 +58,14 @@ struct _PhotosBaseItemPrivate
   cairo_surface_t *surface;
   GdkPixbuf *original_icon;
   GeglNode *graph;
-  GeglNode *node;
+  GeglNode *load;
   GeglRectangle bbox;
   GMutex mutex_download;
   GMutex mutex;
   GQuark equipment;
   GQuark flash;
   PhotosCollectionIconWatcher *watcher;
+  PhotosPipeline *pipeline;
   PhotosSelectionController *sel_cntrlr;
   TrackerSparqlCursor *cursor;
   gboolean collection;
@@ -736,6 +739,7 @@ static GeglNode *
 photos_base_item_load (PhotosBaseItem *self, GCancellable *cancellable, GError **error)
 {
   PhotosBaseItemPrivate *priv = self->priv;
+  GeglNode *output;
   GeglNode *ret_val = NULL;
   gchar *path = NULL;
 
@@ -743,10 +747,12 @@ photos_base_item_load (PhotosBaseItem *self, GCancellable *cancellable, GError *
   if (path == NULL)
     goto out;
 
-  gegl_node_set (priv->node, "path", path, NULL);
-  gegl_node_process (priv->node);
-  priv->bbox = gegl_node_get_bounding_box (priv->node);
-  ret_val = g_object_ref (priv->node);
+  gegl_node_set (priv->load, "path", path, NULL);
+  output = photos_pipeline_get_output (priv->pipeline);
+  gegl_node_process (output);
+  priv->bbox = gegl_node_get_bounding_box (output);
+
+  ret_val = g_object_ref (output);
 
  out:
   g_free (path);
@@ -782,6 +788,44 @@ photos_base_item_load_in_thread_func (GTask *task,
 
 
 static void
+photos_base_item_process (PhotosBaseItem *self, GCancellable *cancellable, GError **error)
+{
+  PhotosBaseItemPrivate *priv = self->priv;
+  GeglNode *output;
+
+  output = photos_pipeline_get_output (priv->pipeline);
+  gegl_node_process (output);
+  priv->bbox = gegl_node_get_bounding_box (output);
+}
+
+
+static void
+photos_base_item_process_in_thread_func (GTask *task,
+                                         gpointer source_object,
+                                         gpointer task_data,
+                                         GCancellable *cancellable)
+{
+  PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
+  PhotosBaseItemPrivate *priv = self->priv;
+  GError *error = NULL;
+
+  g_mutex_lock (&priv->mutex);
+
+  photos_base_item_process (self, cancellable, &error);
+  if (error != NULL)
+    {
+      g_task_return_error (task, error);
+      goto out;
+    }
+
+  g_task_return_boolean (task, TRUE);
+
+ out:
+  g_mutex_unlock (&priv->mutex);
+}
+
+
+static void
 photos_base_item_set_thumbnailing_icon (PhotosBaseItem *self)
 {
   if (thumbnailing_icon == NULL)
@@ -974,6 +1018,7 @@ photos_base_item_dispose (GObject *object)
   g_clear_object (&priv->graph);
   g_clear_object (&priv->original_icon);
   g_clear_object (&priv->watcher);
+  g_clear_object (&priv->pipeline);
   g_clear_object (&priv->sel_cntrlr);
   g_clear_object (&priv->cursor);
 
@@ -1397,8 +1442,15 @@ photos_base_item_load_async (PhotosBaseItem *self,
 
   if (priv->graph == NULL)
     {
+      GeglNode *graph;
+
       priv->graph = gegl_node_new ();
-      priv->node = gegl_node_new_child (priv->graph, "operation", "gegl:load", NULL);
+      priv->load = gegl_node_new_child (priv->graph, "operation", "gegl:load", NULL);
+
+      priv->pipeline = photos_pipeline_new (priv->graph);
+      graph = photos_pipeline_get_graph (priv->pipeline);
+
+      gegl_node_link_many (priv->load, graph, NULL);
     }
 
   task = g_task_new (self, cancellable, callback, user_data);
@@ -1430,6 +1482,38 @@ photos_base_item_open (PhotosBaseItem *self, GdkScreen *screen, guint32 timestam
 
 
 void
+photos_base_item_operation_add (PhotosBaseItem *self, const gchar *operation, const gchar 
*first_property_name, ...)
+{
+  va_list ap;
+
+  va_start (ap, first_property_name);
+  photos_pipeline_add (self->priv->pipeline, operation, first_property_name, ap);
+  va_end (ap);
+}
+
+
+gboolean
+photos_base_item_operation_get (PhotosBaseItem *self, const gchar *operation, const gchar 
*first_property_name, ...)
+{
+  gboolean ret_val;
+  va_list ap;
+
+  va_start (ap, first_property_name);
+  ret_val = photos_pipeline_get (self->priv->pipeline, operation, first_property_name, ap);
+  va_end (ap);
+
+  return ret_val;
+}
+
+
+gboolean
+photos_base_item_operation_undo (PhotosBaseItem *self)
+{
+  return photos_pipeline_undo (self->priv->pipeline);
+}
+
+
+void
 photos_base_item_print (PhotosBaseItem *self, GtkWidget *toplevel)
 {
   photos_base_item_load_async (self, NULL, photos_base_item_print_load, g_object_ref (toplevel));
@@ -1437,6 +1521,37 @@ photos_base_item_print (PhotosBaseItem *self, GtkWidget *toplevel)
 
 
 void
+photos_base_item_process_async (PhotosBaseItem *self,
+                                GCancellable *cancellable,
+                                GAsyncReadyCallback callback,
+                                gpointer user_data)
+{
+  GTask *task;
+
+  g_return_if_fail (PHOTOS_IS_BASE_ITEM (self));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, photos_base_item_process_async);
+
+  g_task_run_in_thread (task, photos_base_item_process_in_thread_func);
+  g_object_unref (task);
+}
+
+
+gboolean
+photos_base_item_process_finish (PhotosBaseItem *self, GAsyncResult *res, GError **error)
+{
+  GTask *task = G_TASK (res);
+
+  g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
+  g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_process_async, FALSE);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  return g_task_propagate_boolean (task, error);
+}
+
+
+void
 photos_base_item_refresh (PhotosBaseItem *self)
 {
   GApplication *app;
diff --git a/src/photos-base-item.h b/src/photos-base-item.h
index 456d11f..c796731 100644
--- a/src/photos-base-item.h
+++ b/src/photos-base-item.h
@@ -173,8 +173,29 @@ void                photos_base_item_open                    (PhotosBaseItem *se
                                                               GdkScreen *screen,
                                                               guint32 timestamp);
 
+void                photos_base_item_operation_add           (PhotosBaseItem *self,
+                                                              const gchar *operation,
+                                                              const gchar *first_property_name,
+                                                              ...) G_GNUC_NULL_TERMINATED;
+
+gboolean            photos_base_item_operation_get           (PhotosBaseItem *self,
+                                                              const gchar *operation,
+                                                              const gchar *first_property_name,
+                                                              ...) G_GNUC_NULL_TERMINATED 
G_GNUC_WARN_UNUSED_RESULT;
+
+gboolean            photos_base_item_operation_undo          (PhotosBaseItem *self);
+
 void                photos_base_item_print                   (PhotosBaseItem *self, GtkWidget *toplevel);
 
+void                photos_base_item_process_async           (PhotosBaseItem *self,
+                                                              GCancellable *cancellable,
+                                                              GAsyncReadyCallback callback,
+                                                              gpointer user_data);
+
+gboolean            photos_base_item_process_finish          (PhotosBaseItem *self,
+                                                              GAsyncResult *res,
+                                                              GError **error);
+
 void                photos_base_item_refresh                 (PhotosBaseItem *self);
 
 void                photos_base_item_set_default_app_name    (PhotosBaseItem *self, const gchar 
*default_app_name);
diff --git a/src/photos-pipeline.c b/src/photos-pipeline.c
new file mode 100644
index 0000000..7b2a2d1
--- /dev/null
+++ b/src/photos-pipeline.c
@@ -0,0 +1,318 @@
+/*
+ * Photos - access, organize and share your photos on GNOME
+ * Copyright © 2015 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+
+#include "config.h"
+
+#include <glib.h>
+
+#include "photos-debug.h"
+#include "photos-operation-insta-common.h"
+#include "photos-pipeline.h"
+
+
+struct _PhotosPipeline
+{
+  GObject parent_instance;
+  GeglNode *parent;
+  GHashTable *hash;
+  GQueue *history;
+  GeglNode *graph;
+};
+
+struct _PhotosPipelineClass
+{
+  GObjectClass parent_class;
+};
+
+enum
+{
+  PROP_0,
+  PROP_PARENT,
+};
+
+
+G_DEFINE_TYPE (PhotosPipeline, photos_pipeline, G_TYPE_OBJECT);
+
+
+static gchar *
+photos_pipeline_to_xml (PhotosPipeline *self)
+{
+  GeglNode *input;
+  GeglNode *last;
+  GeglNode *output;
+  gchar *ret_val;
+
+  /* PhotosPipeline can be connected to a gegl:buffer-source, in which
+   * case we will get a WARNING about trying to serialize the
+   * GeglBuffer. We work around that.
+   */
+
+  input = gegl_node_get_input_proxy (self->graph, "input");
+  output = gegl_node_get_output_proxy (self->graph, "output");
+
+  last = gegl_node_get_producer (input, "input", NULL);
+  if (last != NULL)
+    gegl_node_disconnect (input, "input");
+
+  ret_val = gegl_node_to_xml (output, "/");
+
+  if (last != NULL)
+    gegl_node_link (last, input);
+
+  return ret_val;
+}
+
+static void
+photos_pipeline_constructed (GObject *object)
+{
+  PhotosPipeline *self = PHOTOS_PIPELINE (object);
+  GeglNode *input;
+  GeglNode *output;
+
+  G_OBJECT_CLASS (photos_pipeline_parent_class)->constructed (object);
+
+  self->graph = gegl_node_new ();
+  gegl_node_add_child (self->parent, self->graph);
+  input = gegl_node_get_input_proxy (self->graph, "input");
+  output = gegl_node_get_output_proxy (self->graph, "output");
+  gegl_node_link (input, output);
+}
+
+
+static void
+photos_pipeline_dispose (GObject *object)
+{
+  PhotosPipeline *self = PHOTOS_PIPELINE (object);
+
+  g_clear_object (&self->graph);
+  g_clear_object (&self->parent);
+  g_clear_pointer (&self->hash, (GDestroyNotify) g_hash_table_unref);
+
+  G_OBJECT_CLASS (photos_pipeline_parent_class)->dispose (object);
+}
+
+
+static void
+photos_pipeline_finalize (GObject *object)
+{
+  PhotosPipeline *self = PHOTOS_PIPELINE (object);
+
+  g_queue_free (self->history);
+
+  G_OBJECT_CLASS (photos_pipeline_parent_class)->finalize (object);
+}
+
+
+static void
+photos_pipeline_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+  PhotosPipeline *self = PHOTOS_PIPELINE (object);
+
+  switch (prop_id)
+    {
+    case PROP_PARENT:
+      self->parent = GEGL_NODE (g_value_dup_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+
+static void
+photos_pipeline_init (PhotosPipeline *self)
+{
+  self->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+  self->history = g_queue_new ();
+}
+
+
+static void
+photos_pipeline_class_init (PhotosPipelineClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->constructed = photos_pipeline_constructed;
+  object_class->dispose = photos_pipeline_dispose;
+  object_class->finalize = photos_pipeline_finalize;
+  object_class->set_property = photos_pipeline_set_property;
+
+  g_object_class_install_property (object_class,
+                                   PROP_PARENT,
+                                   g_param_spec_object ("parent",
+                                                        "GeglNode object",
+                                                        "A GeglNode representing the parent graph",
+                                                        GEGL_TYPE_NODE,
+                                                        G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
+}
+
+
+PhotosPipeline *
+photos_pipeline_new (GeglNode *parent)
+{
+  return g_object_new (PHOTOS_TYPE_PIPELINE, "parent", parent, NULL);
+}
+
+
+void
+photos_pipeline_add (PhotosPipeline *self, const gchar *operation, const gchar *first_property_name, va_list 
ap)
+{
+  GeglNode *input;
+  GeglNode *last;
+  GeglNode *node;
+  GeglNode *output;
+  gchar *xml = NULL;
+
+  input = gegl_node_get_input_proxy (self->graph, "input");
+  output = gegl_node_get_output_proxy (self->graph, "output");
+  last = gegl_node_get_producer (output, "input", NULL);
+  if (last == input)
+    photos_pipeline_reset (self);
+
+  node = GEGL_NODE (g_hash_table_lookup (self->hash, operation));
+  if (node == NULL)
+    {
+      last = gegl_node_get_producer (output, "input", NULL);
+      node = gegl_node_new_child (self->graph, "operation", operation, NULL);
+      gegl_node_disconnect (output, "input");
+      gegl_node_link_many (last, node, output, NULL);
+      g_hash_table_insert (self->hash, g_strdup (operation), g_object_ref (node));
+    }
+
+  gegl_node_set_valist (node, first_property_name, ap);
+
+  xml = photos_pipeline_to_xml (self);
+  photos_debug (PHOTOS_DEBUG_GEGL, "Pipeline: %s", xml);
+
+  /* We want to remove the nodes from the graph too. */
+  g_queue_free_full (self->history, g_object_unref);
+  self->history = g_queue_new ();
+
+  g_free (xml);
+}
+
+
+gboolean
+photos_pipeline_get (PhotosPipeline *self, const gchar *operation, const gchar *first_property_name, va_list 
ap)
+{
+  GeglNode *node;
+  gboolean ret_val = FALSE;
+
+  node = GEGL_NODE (g_hash_table_lookup (self->hash, operation));
+  if (node == NULL)
+    goto out;
+
+  gegl_node_get_valist (node, first_property_name, ap);
+  ret_val = TRUE;
+
+ out:
+  return ret_val;
+}
+
+
+GeglNode *
+photos_pipeline_get_graph (PhotosPipeline *self)
+{
+  return self->graph;
+}
+
+
+GeglNode *
+photos_pipeline_get_output (PhotosPipeline *self)
+{
+  GeglNode *output;
+
+  output = gegl_node_get_output_proxy (self->graph, "output");
+  return output;
+}
+
+
+GeglProcessor *
+photos_pipeline_new_processor (PhotosPipeline *self)
+{
+  GeglNode *output;
+  GeglProcessor *processor;
+
+  output = gegl_node_get_output_proxy (self->graph, "output");
+  processor = gegl_node_new_processor (output, NULL);
+  return processor;
+}
+
+
+void
+photos_pipeline_reset (PhotosPipeline *self)
+{
+  GeglNode *input;
+  GeglNode *node;
+  GeglNode *output;
+
+  while (photos_pipeline_undo (self))
+    ;
+
+  input = gegl_node_get_input_proxy (self->graph, "input");
+  output = gegl_node_get_output_proxy (self->graph, "output");
+  node = gegl_node_new_child (self->graph,
+                              "operation", "photos:insta-filter",
+                              "preset", PHOTOS_OPERATION_INSTA_PRESET_NONE,
+                              NULL);
+  gegl_node_link_many (input, node, output, NULL);
+  g_hash_table_insert (self->hash, g_strdup ("photos:insta-filter"), g_object_ref (node));
+}
+
+
+gboolean
+photos_pipeline_undo (PhotosPipeline *self)
+{
+  GeglNode *input;
+  GeglNode *last;
+  GeglNode *last2;
+  GeglNode *output;
+  gboolean ret_val = FALSE;
+  gchar *operation = NULL;
+  gchar *xml = NULL;
+
+  input = gegl_node_get_input_proxy (self->graph, "input");
+  output = gegl_node_get_output_proxy (self->graph, "output");
+  last = gegl_node_get_producer (output, "input", NULL);
+  if (last == input)
+    goto out;
+
+  gegl_node_get (last, "operation", &operation, NULL);
+  g_hash_table_remove (self->hash, operation);
+  g_queue_push_head (self->history, last);
+
+  last2 = gegl_node_get_producer (last, "input", NULL);
+  gegl_node_disconnect (output, "input");
+  gegl_node_disconnect (last, "input");
+  gegl_node_link (last2, output);
+
+  xml = photos_pipeline_to_xml (self);
+  photos_debug (PHOTOS_DEBUG_GEGL, "Pipeline: %s", xml);
+
+  ret_val = TRUE;
+
+ out:
+  g_free (xml);
+  g_free (operation);
+  return ret_val;
+}
diff --git a/src/photos-pipeline.h b/src/photos-pipeline.h
new file mode 100644
index 0000000..e64e672
--- /dev/null
+++ b/src/photos-pipeline.h
@@ -0,0 +1,81 @@
+/*
+ * Photos - access, organize and share your photos on GNOME
+ * Copyright © 2015 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef PHOTOS_PIPELINE_H
+#define PHOTOS_PIPELINE_H
+
+#include <stdarg.h>
+
+#include <gegl.h>
+
+G_BEGIN_DECLS
+
+#define PHOTOS_TYPE_PIPELINE (photos_pipeline_get_type ())
+
+#define PHOTOS_PIPELINE(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+   PHOTOS_TYPE_PIPELINE, PhotosPipeline))
+
+#define PHOTOS_PIPELINE_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), \
+   PHOTOS_TYPE_PIPELINE, PhotosPipelineClass))
+
+#define PHOTOS_IS_PIPELINE(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+   PHOTOS_TYPE_PIPELINE))
+
+#define PHOTOS_IS_PIPELINE_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+   PHOTOS_TYPE_PIPELINE))
+
+#define PHOTOS_PIPELINE_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+   PHOTOS_TYPE_PIPELINE, PhotosPipelineClass))
+
+typedef struct _PhotosPipeline      PhotosPipeline;
+typedef struct _PhotosPipelineClass PhotosPipelineClass;
+
+GType                  photos_pipeline_get_type          (void) G_GNUC_CONST;
+
+PhotosPipeline        *photos_pipeline_new               (GeglNode *parent);
+
+void                   photos_pipeline_add               (PhotosPipeline *self,
+                                                          const gchar *operation,
+                                                          const gchar *first_property_name,
+                                                          va_list ap);
+
+gboolean               photos_pipeline_get               (PhotosPipeline *self,
+                                                          const gchar *operation,
+                                                          const gchar *first_property_name,
+                                                          va_list ap) G_GNUC_WARN_UNUSED_RESULT;
+
+GeglNode              *photos_pipeline_get_graph         (PhotosPipeline *self);
+
+GeglNode              *photos_pipeline_get_output        (PhotosPipeline *self);
+
+GeglProcessor         *photos_pipeline_new_processor     (PhotosPipeline *self);
+
+void                   photos_pipeline_reset             (PhotosPipeline *self);
+
+gboolean               photos_pipeline_undo              (PhotosPipeline *self);
+
+G_END_DECLS
+
+#endif /* PHOTOS_PIPELINE_H */


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