[gnome-photos/wip/rishi/edit-mode: 9/27] Add an image editing pipeline
- From: Debarshi Ray <debarshir src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-photos/wip/rishi/edit-mode: 9/27] Add an image editing pipeline
- Date: Wed, 24 Jun 2015 08:06:23 +0000 (UTC)
commit ef926f398951c54ff81c3e9e3cfeb538cf908f91
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 | 128 ++++++++++++++++++-
src/photos-base-item.h | 21 +++
src/photos-pipeline.c | 324 ++++++++++++++++++++++++++++++++++++++++++++++++
src/photos-pipeline.h | 83 ++++++++++++
5 files changed, 552 insertions(+), 6 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index f32e9b3..b1c0c2f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -160,6 +160,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 092c58c..1fce175 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;
@@ -729,6 +732,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;
@@ -736,10 +740,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);
@@ -775,6 +781,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_pointer (task, NULL, NULL);
+
+ out:
+ g_mutex_unlock (&priv->mutex);
+}
+
+
+static void
photos_base_item_set_thumbnailing_icon (PhotosBaseItem *self)
{
if (thumbnailing_icon == NULL)
@@ -967,6 +1011,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);
@@ -1391,8 +1436,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);
@@ -1425,6 +1477,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;
+}
+
+
+void
+photos_base_item_operation_undo (PhotosBaseItem *self)
+{
+ 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));
@@ -1432,6 +1516,38 @@ 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_check_cancellable (task, TRUE);
+ 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);
+}
+
+
+void
+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), NULL);
+ g_return_val_if_fail (g_task_get_source_tag (task) == photos_base_item_process_async, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ g_task_propagate_pointer (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..eb70b82 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;
+
+void 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);
+
+void 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..435d270
--- /dev/null
+++ b/src/photos-pipeline.c
@@ -0,0 +1,324 @@
+/*
+ * 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)
+ {
+ node = gegl_node_new_child (self->graph, "operation", operation, NULL);
+ last = gegl_node_get_producer (output, "input", 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_redo (PhotosPipeline *self)
+{
+}
+
+
+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..1083df5
--- /dev/null
+++ b/src/photos-pipeline.h
@@ -0,0 +1,83 @@
+/*
+ * 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_redo (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]