[gnome-photos/wip/rishi/thumbnailer: 13/20] Add an out-of-process custom thumbnailer
- From: Debarshi Ray <debarshir src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-photos/wip/rishi/thumbnailer: 13/20] Add an out-of-process custom thumbnailer
- Date: Tue, 14 Feb 2017 10:39:06 +0000 (UTC)
commit 5c652895e3beacf24169a691f0451d303851392d
Author: Debarshi Ray <debarshir gnome org>
Date: Thu Feb 9 20:24:37 2017 +0100
Add an out-of-process custom thumbnailer
src/Makefile.am | 82 +++++
src/photos-thumbnailer-dbus.xml | 38 ++
src/photos-thumbnailer-main.c | 70 ++++
src/photos-thumbnailer.c | 758 +++++++++++++++++++++++++++++++++++++++
src/photos-thumbnailer.h | 35 ++
5 files changed, 983 insertions(+), 0 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 75a9264..4051b05 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,7 @@
bin_PROGRAMS = gnome-photos
+libexec_PROGRAMS = gnome-photos-thumbnailer
+
gnome_photos_built_sources = \
photos-about-data.c \
photos-about-data.h \
@@ -267,8 +269,63 @@ gnome_photos_SOURCES = \
photos-main.c \
$(NULL)
+gnome_photos_thumbnailer_built_sources = \
+ photos-thumbnailer-dbus.c \
+ photos-thumbnailer-dbus.h \
+ photos-enums.c \
+ photos-enums.h \
+ $(NULL)
+
+nodist_gnome_photos_thumbnailer_SOURCES = \
+ $(gnome_photos_thumbnailer_built_sources) \
+ $(NULL)
+
+gnome_photos_thumbnailer_SOURCES = \
+ egg-counter.c \
+ egg-counter.h \
+ photos-debug.c \
+ photos-debug.h \
+ photos-error.c \
+ photos-error.h \
+ photos-gegl.c \
+ photos-gegl.h \
+ photos-glib.c \
+ photos-glib.h \
+ photos-jpeg-count.c \
+ photos-jpeg-count.h \
+ photos-operation-insta-common.h \
+ photos-operation-insta-curve.c \
+ photos-operation-insta-curve.h \
+ photos-operation-insta-filter.c \
+ photos-operation-insta-filter.h \
+ photos-operation-insta-hefe.c \
+ photos-operation-insta-hefe.h \
+ photos-operation-insta-hefe-curve.c \
+ photos-operation-insta-hefe-curve.h \
+ photos-operation-insta-hefe-vignette.c \
+ photos-operation-insta-hefe-vignette.h \
+ photos-operation-jpg-guess-sizes.c \
+ photos-operation-jpg-guess-sizes.h \
+ photos-operation-png-guess-sizes.c \
+ photos-operation-png-guess-sizes.h \
+ photos-operation-saturation.c \
+ photos-operation-saturation.h \
+ photos-pipeline.c \
+ photos-pipeline.h \
+ photos-pixbuf.c \
+ photos-pixbuf.h \
+ photos-png-count.c \
+ photos-png-count.h \
+ photos-quarks.c \
+ photos-quarks.h \
+ photos-thumbnailer.c \
+ photos-thumbnailer.h \
+ photos-thumbnailer-main.c \
+ $(NULL)
+
BUILT_SOURCES = \
$(gnome_photos_built_sources) \
+ $(gnome_photos_thumbnailer_built_sources) \
$(NULL)
EXTRA_DIST = \
@@ -287,6 +344,7 @@ EXTRA_DIST = \
photos-selection-toolbar.ui \
photos-share-dialog.ui \
photos-gom-miner.xml \
+ photos-thumbnailer-dbus.xml \
photos-tracker-extract-priority.xml \
photos-tracker-resources.xml \
photos-dleyna-renderer-device.xml \
@@ -349,6 +407,22 @@ gnome_photos_LDADD = \
$(top_builddir)/libgd/libgd.la \
$(NULL)
+gnome_photos_thumbnailer_LDFLAGS = \
+ $(WARN_LDFLAGS) \
+ $(NULL)
+
+gnome_photos_thumbnailer_LDADD = \
+ $(BABL_LIBS) \
+ $(GEGL_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(GIO_LIBS) \
+ $(GLIB_LIBS) \
+ $(JPEG_LIBS) \
+ $(PNG_LIBS) \
+ $(LIBM) \
+ $(LIBRT) \
+ $(NULL)
+
CLEANFILES = \
$(BUILT_SOURCES) \
stamp-photos-about-data.h \
@@ -495,6 +569,14 @@ photos-shell-search-provider2.h photos-shell-search-provider2.c: org.gnome.Shell
--interface-prefix org.gnome.Shell. \
$<
+photos-thumbnailer-dbus.h photos-thumbnailer-dbus.c: photos-thumbnailer-dbus.xml
+ $(AM_V_GEN)gdbus-codegen \
+ --c-namespace Photos \
+ --generate-c-code photos-thumbnailer-dbus \
+ --interface-prefix org.gnome.Photos. \
+ --annotate "org.gnome.Photos.Thumbnailer" org.gtk.GDBus.C.Name ThumbnailerDBus \
+ $<
+
photos-tracker-extract-priority.h photos-tracker-extract-priority.c: photos-tracker-extract-priority.xml
$(AM_V_GEN)gdbus-codegen \
--c-namespace Tracker \
diff --git a/src/photos-thumbnailer-dbus.xml b/src/photos-thumbnailer-dbus.xml
new file mode 100644
index 0000000..6f9b30d
--- /dev/null
+++ b/src/photos-thumbnailer-dbus.xml
@@ -0,0 +1,38 @@
+<!DOCTYPE node PUBLIC
+"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+
+<!--
+ Photos - access, organize and share your photos on GNOME
+ Copyright © 2017 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.
+-->
+
+<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
+ <interface name="org.gnome.Photos.Thumbnailer">
+ <method name="GenerateThumbnail">
+ <arg name="uri" type="s" direction="in" />
+ <arg name="mime_type" type="s" direction="in" />
+ <arg name="orientation" type="s" direction="in" />
+ <arg name="original_height" type="x" direction="in" />
+ <arg name="original_width" type="x" direction="in" />
+ <arg name="pipeline_uri" type="s" direction="in" />
+ <arg name="thumbnail_path" type="s" direction="in" />
+ <arg name="thumbnail_size" type="i" direction="in" />
+ </method>
+ </interface>
+</node>
diff --git a/src/photos-thumbnailer-main.c b/src/photos-thumbnailer-main.c
new file mode 100644
index 0000000..624f86a
--- /dev/null
+++ b/src/photos-thumbnailer-main.c
@@ -0,0 +1,70 @@
+/*
+ * Photos - access, organize and share your photos on GNOME
+ * Copyright © 2017 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 <locale.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "egg-counter.h"
+#include "photos-debug.h"
+#include "photos-thumbnailer.h"
+
+
+static void
+photos_thumbnailer_main_counter_arena_foreach (EggCounter *counter, gpointer user_data)
+{
+ gint64 count;
+
+ count = egg_counter_get (counter);
+ photos_debug (PHOTOS_DEBUG_MEMORY, "%s.%s = %" G_GINT64_FORMAT, counter->category, counter->name, count);
+}
+
+
+gint
+main (gint argc, gchar *argv[])
+{
+ EggCounterArena *counter_arena;
+ GApplication *app;
+ gint exit_status = 0;
+
+ setlocale (LC_ALL, "");
+ g_setenv ("G_DEBUG", "fatal-criticals", TRUE);
+
+ photos_debug_init ();
+
+ bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+
+ g_set_prgname (PACKAGE_TARNAME "-thumbnailer");
+
+ app = photos_thumbnailer_new ();
+ exit_status = g_application_run (app, argc, argv);
+ g_object_unref (app);
+
+ counter_arena = egg_counter_arena_get_default ();
+ egg_counter_arena_foreach (counter_arena, photos_thumbnailer_main_counter_arena_foreach, NULL);
+
+ return exit_status;
+}
diff --git a/src/photos-thumbnailer.c b/src/photos-thumbnailer.c
new file mode 100644
index 0000000..ea65965
--- /dev/null
+++ b/src/photos-thumbnailer.c
@@ -0,0 +1,758 @@
+/*
+ * Photos - access, organize and share your photos on GNOME
+ * Copyright © 2017 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 <stdlib.h>
+
+#include <babl/babl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+#include <glib/gi18n.h>
+
+#include "photos-debug.h"
+#include "photos-error.h"
+#include "photos-gegl.h"
+#include "photos-glib.h"
+#include "photos-pipeline.h"
+#include "photos-pixbuf.h"
+#include "photos-thumbnailer.h"
+#include "photos-thumbnailer-dbus.h"
+
+
+struct _PhotosThumbnailer
+{
+ GApplication parent_instance;
+ GDBusConnection *connection;
+ PhotosThumbnailerDBus *skeleton;
+ gchar *address;
+};
+
+
+G_DEFINE_TYPE (PhotosThumbnailer, photos_thumbnailer, G_TYPE_APPLICATION);
+
+
+typedef struct _PhotosThumbnailerGenerateData PhotosThumbnailerGenerateData;
+
+struct _PhotosThumbnailerGenerateData
+{
+ GFile *file;
+ GFileOutputStream *stream;
+ GQuark orientation;
+ GdkPixbuf *pixbuf_thumbnail;
+ GeglNode *graph;
+ PhotosPipeline *pipeline;
+ gchar *thumbnail_path;
+ gint thumbnail_size;
+ gint64 original_height;
+ gint64 original_width;
+};
+
+enum
+{
+ INACTIVITY_TIMEOUT = 12000 /* ms */
+};
+
+static const GOptionEntry COMMAND_LINE_OPTIONS[] =
+{
+ { "address", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, NULL, N_("D-Bus address to use"), NULL},
+ { NULL }
+};
+
+static const gchar *THUMBNAILER_PATH = "/org/gnome/Photos/Thumbnailer";
+
+
+static void
+photos_thumbnailer_generate_data_free (PhotosThumbnailerGenerateData *data)
+{
+ g_free (data->thumbnail_path);
+ g_clear_object (&data->file);
+ g_clear_object (&data->graph);
+ g_clear_object (&data->pipeline);
+ g_clear_object (&data->pixbuf_thumbnail);
+ g_clear_object (&data->stream);
+ g_slice_free (PhotosThumbnailerGenerateData, data);
+}
+
+
+static PhotosThumbnailerGenerateData *
+photos_thumbnailer_generate_data_new (GFile *file,
+ GQuark orientation,
+ gint64 original_height,
+ gint64 original_width,
+ const gchar *thumbnail_path,
+ gint thumbnail_size,
+ GeglNode *graph)
+{
+ PhotosThumbnailerGenerateData *data;
+
+ data = g_slice_new0 (PhotosThumbnailerGenerateData);
+ data->file = g_object_ref (file);
+ data->orientation = orientation;
+ data->original_height = original_height;
+ data->original_width = original_width;
+ data->thumbnail_path = g_strdup (thumbnail_path);
+ data->thumbnail_size = thumbnail_size;
+ data->graph = g_object_ref (graph);
+
+ return data;
+}
+
+
+static gboolean
+photos_thumbnailer_authorize_authenticated_peer (PhotosThumbnailer *self,
+ GIOStream *iostream,
+ GCredentials *credentials)
+{
+ GCredentials *own_credentials = NULL;
+ GError *error;
+ gboolean ret_val = FALSE;
+
+ if (credentials == NULL)
+ goto out;
+
+ own_credentials = g_credentials_new ();
+
+ error = NULL;
+ if (!g_credentials_is_same_user (credentials, own_credentials, &error))
+ {
+ g_warning ("Unable to authorize peer: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ ret_val = TRUE;
+
+ out:
+ g_clear_object (&own_credentials);
+ return ret_val;
+}
+
+
+static void
+photos_thumbnailer_generate_thumbnail_stream_close (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ GOutputStream *stream = G_OUTPUT_STREAM (source_object);
+ GTask *task = G_TASK (user_data);
+
+ error = NULL;
+ if (!g_output_stream_close_finish (stream, res, &error))
+ {
+ g_task_return_error (task, error);
+ goto out;
+ }
+
+ g_task_return_boolean (task, TRUE);
+
+ out:
+ g_object_unref (task);
+}
+
+
+static void
+photos_thumbnailer_generate_thumbnail_save_to_stream (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GCancellable *cancellable;
+ GError *error;
+ GTask *task = G_TASK (user_data);
+ PhotosThumbnailerGenerateData *data;
+
+ cancellable = g_task_get_cancellable (task);
+ data = g_task_get_task_data (task);
+
+ error = NULL;
+ if (!gdk_pixbuf_save_to_stream_finish (res, &error))
+ {
+ g_task_return_error (task, error);
+ goto out;
+ }
+
+ g_output_stream_close_async (G_OUTPUT_STREAM (data->stream),
+ G_PRIORITY_DEFAULT,
+ cancellable,
+ photos_thumbnailer_generate_thumbnail_stream_close,
+ g_object_ref (task));
+
+ out:
+ g_object_unref (task);
+}
+
+
+static void
+photos_thumbnailer_generate_thumbnail_replace (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+ GCancellable *cancellable;
+ GError *error;
+ GFile *thumbnail_file = G_FILE (source_object);
+ GFileOutputStream *stream = NULL;
+ GTask *task = G_TASK (user_data);
+ PhotosThumbnailerGenerateData *data;
+ const gchar *prgname;
+ gchar *original_height_str = NULL;
+ gchar *original_width_str = NULL;
+ gchar *uri = NULL;
+
+ cancellable = g_task_get_cancellable (task);
+ data = g_task_get_task_data (task);
+
+ error = NULL;
+ stream = g_file_replace_finish (thumbnail_file, res, &error);
+ if (error != NULL)
+ {
+ g_task_return_error (task, error);
+ goto out;
+ }
+
+ g_assert_null (data->stream);
+ data->stream = g_object_ref (stream);
+
+ original_height_str = g_strdup_printf ("%" G_GINT64_FORMAT, data->original_height);
+ original_width_str = g_strdup_printf ("%" G_GINT64_FORMAT, data->original_width);
+ prgname = g_get_prgname ();
+ uri = g_file_get_uri (data->file);
+ gdk_pixbuf_save_to_stream_async (data->pixbuf_thumbnail,
+ G_OUTPUT_STREAM (stream),
+ "png",
+ cancellable,
+ photos_thumbnailer_generate_thumbnail_save_to_stream,
+ g_object_ref (task),
+ "tEXt::Software", prgname,
+ "tEXt::Thumb::URI", uri,
+ "tEXt::Thumb::Image::Height", original_height_str,
+ "tEXt::Thumb::Image::Width", original_width_str,
+ NULL);
+
+ out:
+ g_free (original_height_str);
+ g_free (original_width_str);
+ g_free (uri);
+ g_clear_object (&stream);
+ g_object_unref (task);
+}
+
+
+static void
+photos_thumbnailer_generate_thumbnail_process (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+ GCancellable *cancellable;
+ GError *error;
+ GFile *thumbnail_dir = NULL;
+ GFile *thumbnail_file = NULL;
+ GTask *task = G_TASK (user_data);
+ GeglProcessor *processor = GEGL_PROCESSOR (source_object);
+ PhotosThumbnailerGenerateData *data;
+ gdouble zoom = 0.0;
+ gint pixbuf_height;
+ gint pixbuf_width;
+ gint pixbuf_zoomed_height;
+ gint pixbuf_zoomed_width;
+
+ cancellable = g_task_get_cancellable (task);
+ data = g_task_get_task_data (task);
+
+ error = NULL;
+ if (!photos_gegl_processor_process_finish (processor, res, &error))
+ {
+ g_task_return_error (task, error);
+ goto out;
+ }
+
+ pixbuf_height = gdk_pixbuf_get_height (data->pixbuf_thumbnail);
+ pixbuf_width = gdk_pixbuf_get_width (data->pixbuf_thumbnail);
+
+ if (pixbuf_height > pixbuf_width && pixbuf_height > data->thumbnail_size)
+ {
+ zoom = (gdouble) pixbuf_height / (gdouble) data->thumbnail_size;
+ pixbuf_zoomed_height = data->thumbnail_size;
+ pixbuf_zoomed_width = (gint) (zoom * (gdouble) pixbuf_width + 0.5);
+ }
+ else if (pixbuf_height <= pixbuf_width && pixbuf_width > data->thumbnail_size)
+ {
+ zoom = (gdouble) pixbuf_width / (gdouble) data->thumbnail_size;
+ pixbuf_zoomed_height = (gint) (zoom * (gdouble) pixbuf_height + 0.5);
+ pixbuf_zoomed_width = data->thumbnail_size;
+ }
+
+ if (zoom > 0.0)
+ {
+ GdkPixbuf *pixbuf_scaled = NULL;
+
+ pixbuf_scaled = gdk_pixbuf_scale_simple (data->pixbuf_thumbnail,
+ pixbuf_zoomed_width,
+ pixbuf_zoomed_height,
+ GDK_INTERP_BILINEAR);
+
+ g_set_object (&data->pixbuf_thumbnail, pixbuf_scaled);
+ g_object_unref (pixbuf_scaled);
+ }
+
+ thumbnail_file = g_file_new_for_path (data->thumbnail_path);
+ thumbnail_dir = g_file_get_parent (thumbnail_file);
+ g_assert_true (G_IS_FILE (thumbnail_dir));
+
+ error = NULL;
+ if (!photos_glib_make_directory_with_parents (thumbnail_dir, cancellable, &error))
+ {
+ g_task_return_error (task, error);
+ goto out;
+ }
+
+ photos_debug (PHOTOS_DEBUG_THUMBNAILER, "Saving thumbnail to %s", data->thumbnail_path);
+ g_file_replace_async (thumbnail_file,
+ NULL,
+ FALSE,
+ G_FILE_CREATE_PRIVATE | G_FILE_CREATE_REPLACE_DESTINATION,
+ G_PRIORITY_DEFAULT,
+ cancellable,
+ photos_thumbnailer_generate_thumbnail_replace,
+ g_object_ref (task));
+
+ out:
+ g_clear_object (&thumbnail_dir);
+ g_clear_object (&thumbnail_file);
+ g_object_unref (task);
+}
+
+
+static void
+photos_thumbnailer_generate_thumbnail_pixbuf (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+ GCancellable *cancellable;
+ GError *error;
+ GTask *task = G_TASK (user_data);
+ GdkPixbuf *pixbuf = NULL;
+ GeglNode *orientation;
+ GeglNode *pipeline_node;
+ GeglNode *pixbuf_source;
+ GeglNode *save_pixbuf;
+ GeglProcessor *processor = NULL;
+ PhotosThumbnailerGenerateData *data;
+
+ cancellable = g_task_get_cancellable (task);
+ data = g_task_get_task_data (task);
+
+ error = NULL;
+ pixbuf = photos_pixbuf_new_from_file_at_size_finish (res, &error);
+ if (error != NULL)
+ {
+ g_task_return_error (task, error);
+ goto out;
+ }
+
+ g_assert_null (data->pixbuf_thumbnail);
+
+ pixbuf_source = gegl_node_new_child (data->graph, "operation", "gegl:pixbuf", "pixbuf", pixbuf, NULL);
+ orientation = photos_gegl_create_orientation_node (data->graph, data->orientation);
+ pipeline_node = photos_pipeline_get_graph (data->pipeline);
+ save_pixbuf = gegl_node_new_child (data->graph,
+ "operation", "gegl:save-pixbuf",
+ "pixbuf", &data->pixbuf_thumbnail,
+ NULL);
+
+ gegl_node_link_many (pixbuf_source, orientation, pipeline_node, save_pixbuf, NULL);
+
+ processor = gegl_node_new_processor (save_pixbuf, NULL);
+ photos_gegl_processor_process_async (processor,
+ cancellable,
+ photos_thumbnailer_generate_thumbnail_process,
+ g_object_ref (task));
+
+ out:
+ g_clear_object (&pixbuf);
+ g_clear_object (&processor);
+ g_object_unref (task);
+}
+
+
+static void
+photos_thumbnailer_generate_thumbnail_pipeline (GObject *source_object, GAsyncResult *res, gpointer
user_data)
+{
+ GCancellable *cancellable;
+ GError *error;
+ GTask *task = G_TASK (user_data);
+ PhotosPipeline *pipeline = NULL;
+ PhotosThumbnailerGenerateData *data;
+ gchar *path = NULL;
+ gdouble height;
+ gdouble width;
+ gdouble x;
+ gdouble y;
+ gint load_height;
+ gint load_width;
+
+ cancellable = g_task_get_cancellable (task);
+ data = g_task_get_task_data (task);
+
+ error = NULL;
+ pipeline = photos_pipeline_new_finish (res, &error);
+ if (error != NULL)
+ {
+ g_task_return_error (task, error);
+ goto out;
+ }
+
+ g_assert_null (data->pipeline);
+ data->pipeline = g_object_ref (pipeline);
+
+ if (photos_pipeline_get (pipeline, "gegl:crop", "height", &height, "width", &width, "x", &x, "y", &y,
NULL))
+ {
+ if (height < 0.0 || width < 0.0 || x < 0.0 || y < 0.0)
+ {
+ gchar *uri = NULL;
+
+ uri = g_file_get_uri (data->file);
+ g_warning ("Unable to crop the thumbnail for %s: Invalid parameters", uri);
+
+ photos_pipeline_remove (pipeline, "gegl:crop");
+ load_height = data->thumbnail_size;
+ load_width = data->thumbnail_size;
+
+ g_free (uri);
+ }
+ else
+ {
+ load_height = (gint64) data->original_height;
+ load_width = (gint64) data->original_width;
+ }
+ }
+ else
+ {
+ load_height = data->thumbnail_size;
+ load_width = data->thumbnail_size;
+ }
+
+ path = g_file_get_path (data->file);
+ photos_pixbuf_new_from_file_at_size_async (path,
+ load_width,
+ load_height,
+ cancellable,
+ photos_thumbnailer_generate_thumbnail_pixbuf,
+ g_object_ref (task));
+
+ out:
+ g_free (path);
+ g_clear_object (&pipeline);
+ g_object_unref (task);
+}
+
+
+static void
+photos_thumbnailer_generate_thumbnail_async (PhotosThumbnailer *self,
+ const gchar *uri,
+ const gchar *mime_type,
+ const gchar *orientation,
+ gint64 original_height,
+ gint64 original_width,
+ const gchar *pipeline_uri,
+ const gchar *thumbnail_path,
+ gint thumbnail_size,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GFile *file = NULL;
+ GTask *task = NULL;
+ GQuark orientation_quark;
+ GeglNode *graph;
+ PhotosThumbnailerGenerateData *data;
+
+ g_return_if_fail (PHOTOS_IS_THUMBNAILER (self));
+ g_return_if_fail (uri != NULL && uri[0] != '\0');
+ g_return_if_fail (mime_type != NULL && mime_type[0] != '\0');
+ g_return_if_fail (orientation != NULL && orientation[0] != '\0');
+ g_return_if_fail (thumbnail_path != NULL && thumbnail_path[0] != '\0');
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+ file = g_file_new_for_uri (uri);
+ orientation_quark = g_quark_from_string (orientation);
+ graph = gegl_node_new ();
+ data = photos_thumbnailer_generate_data_new (file,
+ orientation_quark,
+ original_height,
+ original_width,
+ thumbnail_path,
+ thumbnail_size,
+ graph);
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, photos_thumbnailer_generate_thumbnail_async);
+ g_task_set_task_data (task, data, (GDestroyNotify) photos_thumbnailer_generate_data_free);
+
+ photos_pipeline_new_async (graph,
+ pipeline_uri,
+ cancellable,
+ photos_thumbnailer_generate_thumbnail_pipeline,
+ g_object_ref (task));
+
+ g_object_unref (file);
+ g_object_unref (graph);
+ g_object_unref (task);
+}
+
+
+static gboolean
+photos_thumbnailer_generate_thumbnail_finish (PhotosThumbnailer *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_thumbnailer_generate_thumbnail_async, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ return g_task_propagate_boolean (task, error);
+}
+
+
+static void
+photos_thumbnailer_handle_generate_thumbnail_generate_thumbnail (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ PhotosThumbnailer *self = PHOTOS_THUMBNAILER (source_object);
+ GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (user_data);
+ GError *error;
+
+ error = NULL;
+ if (!photos_thumbnailer_generate_thumbnail_finish (self, res, &error))
+ {
+ g_dbus_method_invocation_take_error (invocation, error);
+ goto out;
+ }
+
+ photos_thumbnailer_dbus_complete_generate_thumbnail (self->skeleton, invocation);
+
+ out:
+ photos_debug (PHOTOS_DEBUG_THUMBNAILER, "Completed GenerateThumbnail");
+ g_application_release (G_APPLICATION (self));
+ g_object_unref (invocation);
+}
+
+
+static gboolean
+photos_thumbnailer_handle_generate_thumbnail (PhotosThumbnailer *self,
+ GDBusMethodInvocation *invocation,
+ const gchar *uri,
+ const gchar *mime_type,
+ const gchar *orientation,
+ gint64 original_height,
+ gint64 original_width,
+ const gchar *pipeline_uri,
+ const gchar *thumbnail_path,
+ gint thumbnail_size)
+{
+ g_return_val_if_fail (PHOTOS_IS_THUMBNAILER (self), FALSE);
+ g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), FALSE);
+ g_return_val_if_fail (uri != NULL && uri[0] != '\0', FALSE);
+ g_return_val_if_fail (mime_type != NULL && mime_type[0] != '\0', FALSE);
+ g_return_val_if_fail (orientation != NULL && orientation[0] != '\0', FALSE);
+ g_return_val_if_fail (thumbnail_path != NULL, FALSE);
+ g_return_val_if_fail (thumbnail_path != NULL && thumbnail_path[0] != '\0', FALSE);
+
+ photos_debug (PHOTOS_DEBUG_THUMBNAILER, "Handling GenerateThumbnail for %s", uri);
+
+ if (pipeline_uri[0] == '\0')
+ pipeline_uri = NULL;
+
+ g_application_hold (G_APPLICATION (self));
+ photos_thumbnailer_generate_thumbnail_async (self,
+ uri,
+ mime_type,
+ orientation,
+ original_height,
+ original_width,
+ pipeline_uri,
+ thumbnail_path,
+ thumbnail_size,
+ NULL,
+
photos_thumbnailer_handle_generate_thumbnail_generate_thumbnail,
+ g_object_ref (invocation));
+
+ return TRUE;
+}
+
+
+static gboolean
+photos_thumbnailer_dbus_register (GApplication *application,
+ GDBusConnection *connection,
+ const gchar *object_path,
+ GError **error)
+{
+ PhotosThumbnailer *self = PHOTOS_THUMBNAILER (application);
+ GDBusAuthObserver *observer = NULL;
+ gboolean ret_val = FALSE;
+
+ if (!G_APPLICATION_CLASS (photos_thumbnailer_parent_class)->dbus_register (application,
+ connection,
+ object_path,
+ error))
+ goto out;
+
+ observer = g_dbus_auth_observer_new ();
+ g_signal_connect_swapped (observer,
+ "authorize-authenticated-peer",
+ G_CALLBACK (photos_thumbnailer_authorize_authenticated_peer),
+ self);
+
+ self->connection = g_dbus_connection_new_for_address_sync (self->address,
+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
+ observer,
+ NULL,
+ error);
+ if (self->connection == NULL)
+ goto out;
+
+ if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self->skeleton),
+ self->connection,
+ THUMBNAILER_PATH,
+ error))
+ {
+ g_clear_object (&self->skeleton);
+ goto out;
+ }
+
+ ret_val = TRUE;
+
+ out:
+ g_clear_object (&observer);
+ return ret_val;
+}
+
+
+static void
+photos_thumbnailer_dbus_unregister (GApplication *application,
+ GDBusConnection *connection,
+ const gchar *object_path)
+{
+ PhotosThumbnailer *self = PHOTOS_THUMBNAILER (application);
+
+ if (self->skeleton != NULL)
+ {
+ g_dbus_interface_skeleton_unexport_from_connection (G_DBUS_INTERFACE_SKELETON (self->skeleton),
+ self->connection);
+ g_clear_object (&self->skeleton);
+ }
+
+ G_APPLICATION_CLASS (photos_thumbnailer_parent_class)->dbus_unregister (application, connection,
object_path);
+}
+
+
+static gint
+photos_thumbnailer_handle_local_options (GApplication *application, GVariantDict *options)
+{
+ PhotosThumbnailer *self = PHOTOS_THUMBNAILER (application);
+ gint ret_val = EXIT_FAILURE;
+
+ if (g_variant_dict_lookup (options, "address", "s", &self->address))
+ ret_val = -1;
+
+ return ret_val;
+}
+
+
+static void
+photos_thumbnailer_shutdown (GApplication *application)
+{
+ photos_debug (PHOTOS_DEBUG_THUMBNAILER, "Thumbnailer exiting");
+
+ G_APPLICATION_CLASS (photos_thumbnailer_parent_class)->shutdown (application);
+}
+
+
+static void
+photos_thumbnailer_startup (GApplication *application)
+{
+ G_APPLICATION_CLASS (photos_thumbnailer_parent_class)->startup (application);
+
+ gegl_init (NULL, NULL);
+ photos_debug (PHOTOS_DEBUG_THUMBNAILER, "Thumbnailer ready");
+}
+
+
+static void
+photos_thumbnailer_dispose (GObject *object)
+{
+ PhotosThumbnailer *self = PHOTOS_THUMBNAILER (object);
+
+ g_clear_object (&self->connection);
+ g_clear_object (&self->skeleton);
+
+ G_OBJECT_CLASS (photos_thumbnailer_parent_class)->dispose (object);
+}
+
+
+static void
+photos_thumbnailer_finalize (GObject *object)
+{
+ PhotosThumbnailer *self = PHOTOS_THUMBNAILER (object);
+
+ g_free (self->address);
+
+ if (g_application_get_is_registered (G_APPLICATION (self)))
+ gegl_exit ();
+
+ G_OBJECT_CLASS (photos_thumbnailer_parent_class)->finalize (object);
+}
+
+
+static void
+photos_thumbnailer_init (PhotosThumbnailer *self)
+{
+ photos_gegl_ensure_builtins ();
+
+ self->skeleton = photos_thumbnailer_dbus_skeleton_new ();
+ g_signal_connect_swapped (self->skeleton,
+ "handle-generate-thumbnail",
+ G_CALLBACK (photos_thumbnailer_handle_generate_thumbnail),
+ self);
+
+ g_application_add_main_option_entries (G_APPLICATION (self), COMMAND_LINE_OPTIONS);
+}
+
+
+static void
+photos_thumbnailer_class_init (PhotosThumbnailerClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GApplicationClass *application_class = G_APPLICATION_CLASS (class);
+
+ object_class->dispose = photos_thumbnailer_dispose;
+ object_class->finalize = photos_thumbnailer_finalize;
+ application_class->dbus_register = photos_thumbnailer_dbus_register;
+ application_class->dbus_unregister = photos_thumbnailer_dbus_unregister;
+ application_class->handle_local_options = photos_thumbnailer_handle_local_options;
+ application_class->shutdown = photos_thumbnailer_shutdown;
+ application_class->startup = photos_thumbnailer_startup;
+}
+
+
+GApplication *
+photos_thumbnailer_new (void)
+{
+ return g_object_new (PHOTOS_TYPE_THUMBNAILER,
+ "flags", G_APPLICATION_IS_SERVICE | G_APPLICATION_NON_UNIQUE,
+ "inactivity-timeout", INACTIVITY_TIMEOUT,
+ NULL);
+}
diff --git a/src/photos-thumbnailer.h b/src/photos-thumbnailer.h
new file mode 100644
index 0000000..f4d287a
--- /dev/null
+++ b/src/photos-thumbnailer.h
@@ -0,0 +1,35 @@
+/*
+ * Photos - access, organize and share your photos on GNOME
+ * Copyright © 2017 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_THUMBNAILER_H
+#define PHOTOS_THUMBNAILER_H
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define PHOTOS_TYPE_THUMBNAILER (photos_thumbnailer_get_type ())
+G_DECLARE_FINAL_TYPE (PhotosThumbnailer, photos_thumbnailer, PHOTOS, THUMBNAILER, GApplication);
+
+GApplication *photos_thumbnailer_new (void);
+
+G_END_DECLS
+
+#endif /* PHOTOS_THUMBNAILER_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]