summaryrefslogtreecommitdiffstats
path: root/app/tools/gimpgegltool.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/tools/gimpgegltool.c')
-rw-r--r--app/tools/gimpgegltool.c559
1 files changed, 559 insertions, 0 deletions
diff --git a/app/tools/gimpgegltool.c b/app/tools/gimpgegltool.c
new file mode 100644
index 0000000..69a0eec
--- /dev/null
+++ b/app/tools/gimpgegltool.c
@@ -0,0 +1,559 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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 3 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gegl-plugin.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "tools-types.h"
+
+#include "widgets/gimphelp-ids.h"
+#include "widgets/gimppropwidgets.h"
+
+#include "gimpfilteroptions.h"
+#include "gimpgegltool.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ COLUMN_NAME,
+ COLUMN_LABEL,
+ COLUMN_ICON_NAME,
+ N_COLUMNS
+};
+
+
+/* local function prototypes */
+
+static void gimp_gegl_tool_control (GimpTool *tool,
+ GimpToolAction action,
+ GimpDisplay *display);
+
+static void gimp_gegl_tool_dialog (GimpFilterTool *filter_tool);
+
+static void gimp_gegl_tool_halt (GimpGeglTool *gegl_tool);
+
+static void gimp_gegl_tool_operation_changed (GtkWidget *widget,
+ GimpGeglTool *gegl_tool);
+
+
+G_DEFINE_TYPE (GimpGeglTool, gimp_gegl_tool, GIMP_TYPE_OPERATION_TOOL)
+
+#define parent_class gimp_gegl_tool_parent_class
+
+
+void
+gimp_gegl_tool_register (GimpToolRegisterCallback callback,
+ gpointer data)
+{
+ (* callback) (GIMP_TYPE_GEGL_TOOL,
+ GIMP_TYPE_FILTER_OPTIONS,
+ gimp_color_options_gui,
+ 0,
+ "gimp-gegl-tool",
+ _("GEGL Operation"),
+ _("GEGL Tool: Use an arbitrary GEGL operation"),
+ N_("_GEGL Operation..."), NULL,
+ NULL, GIMP_HELP_TOOL_GEGL,
+ GIMP_ICON_GEGL,
+ data);
+}
+
+static void
+gimp_gegl_tool_class_init (GimpGeglToolClass *klass)
+{
+ GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
+ GimpFilterToolClass *filter_tool_class = GIMP_FILTER_TOOL_CLASS (klass);
+
+ tool_class->control = gimp_gegl_tool_control;
+
+ filter_tool_class->dialog = gimp_gegl_tool_dialog;
+}
+
+static void
+gimp_gegl_tool_init (GimpGeglTool *tool)
+{
+}
+
+static void
+gimp_gegl_tool_control (GimpTool *tool,
+ GimpToolAction action,
+ GimpDisplay *display)
+{
+ GimpGeglTool *gegl_tool = GIMP_GEGL_TOOL (tool);
+
+ switch (action)
+ {
+ case GIMP_TOOL_ACTION_PAUSE:
+ case GIMP_TOOL_ACTION_RESUME:
+ break;
+
+ case GIMP_TOOL_ACTION_HALT:
+ gimp_gegl_tool_halt (gegl_tool);
+ break;
+
+ case GIMP_TOOL_ACTION_COMMIT:
+ break;
+ }
+
+ GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
+}
+
+static gboolean
+gimp_gegl_tool_operation_blacklisted (const gchar *name,
+ const gchar *categories_str)
+{
+ static const gchar * const category_blacklist[] =
+ {
+ "compositors",
+ "core",
+ "debug",
+ "display",
+ "hidden",
+ "input",
+ "output",
+ "programming",
+ "transform",
+ "video"
+ };
+ static const gchar * const name_blacklist[] =
+ {
+ /* these ops are already added to the menus via
+ * filters-actions or drawable-actions
+ */
+ "gegl:alien-map",
+ "gegl:antialias",
+ "gegl:apply-lens",
+ "gegl:bayer-matrix",
+ "gegl:bloom",
+ "gegl:bump-map",
+ "gegl:c2g",
+ "gegl:cartoon",
+ "gegl:cell-noise",
+ "gegl:channel-mixer",
+ "gegl:checkerboard",
+ "gegl:color",
+ "gegl:color-enhance",
+ "gegl:color-exchange",
+ "gegl:color-rotate",
+ "gegl:color-temperature",
+ "gegl:color-to-alpha",
+ "gegl:component-extract",
+ "gegl:convolution-matrix",
+ "gegl:cubism",
+ "gegl:deinterlace",
+ "gegl:difference-of-gaussians",
+ "gegl:diffraction-patterns",
+ "gegl:displace",
+ "gegl:distance-transform",
+ "gegl:dither",
+ "gegl:dropshadow",
+ "gegl:edge",
+ "gegl:edge-laplace",
+ "gegl:edge-neon",
+ "gegl:edge-sobel",
+ "gegl:emboss",
+ "gegl:engrave",
+ "gegl:exposure",
+ "gegl:fattal02",
+ "gegl:focus-blur",
+ "gegl:fractal-trace",
+ "gegl:gaussian-blur",
+ "gegl:gaussian-blur-selective",
+ "gegl:gegl",
+ "gegl:grid",
+ "gegl:high-pass",
+ "gegl:hue-chroma",
+ "gegl:illusion",
+ "gegl:image-gradient",
+ "gegl:invert-linear",
+ "gegl:invert-gamma",
+ "gegl:lens-blur",
+ "gegl:lens-distortion",
+ "gegl:lens-flare",
+ "gegl:linear-sinusoid",
+ "gegl:long-shadow",
+ "gegl:mantiuk06",
+ "gegl:maze",
+ "gegl:mean-curvature-blur",
+ "gegl:median-blur",
+ "gegl:mirrors",
+ "gegl:mono-mixer",
+ "gegl:mosaic",
+ "gegl:motion-blur-circular",
+ "gegl:motion-blur-linear",
+ "gegl:motion-blur-zoom",
+ "gegl:newsprint",
+ "gegl:noise-cie-lch",
+ "gegl:noise-hsv",
+ "gegl:noise-hurl",
+ "gegl:noise-pick",
+ "gegl:noise-reduction",
+ "gegl:noise-rgb",
+ "gegl:noise-slur",
+ "gegl:noise-solid",
+ "gegl:noise-spread",
+ "gegl:normal-map",
+ "gegl:oilify",
+ "gegl:panorama-projection",
+ "gegl:perlin-noise",
+ "gegl:photocopy",
+ "gegl:pixelize",
+ "gegl:plasma",
+ "gegl:polar-coordinates",
+ "gegl:recursive-transform",
+ "gegl:red-eye-removal",
+ "gegl:reinhard05",
+ "gegl:rgb-clip",
+ "gegl:ripple",
+ "gegl:saturation",
+ "gegl:sepia",
+ "gegl:shadows-highlights",
+ "gegl:shift",
+ "gegl:simplex-noise",
+ "gegl:sinus",
+ "gegl:slic",
+ "gegl:snn-mean",
+ "gegl:softglow",
+ "gegl:spherize",
+ "gegl:spiral",
+ "gegl:stereographic-projection",
+ "gegl:stretch-contrast",
+ "gegl:stretch-contrast-hsv",
+ "gegl:stress",
+ "gegl:supernova",
+ "gegl:texturize-canvas",
+ "gegl:tile-glass",
+ "gegl:tile-paper",
+ "gegl:tile-seamless",
+ "gegl:unsharp-mask",
+ "gegl:value-invert",
+ "gegl:value-propagate",
+ "gegl:variable-blur",
+ "gegl:video-degradation",
+ "gegl:vignette",
+ "gegl:waterpixels",
+ "gegl:wavelet-blur",
+ "gegl:waves",
+ "gegl:whirl-pinch",
+ "gegl:wind",
+
+ /* these ops are blacklisted for other reasons */
+ "gegl:contrast-curve",
+ "gegl:convert-format", /* pointless */
+ "gegl:ditto", /* pointless */
+ "gegl:fill-path",
+ "gegl:gray", /* we use gimp's op */
+ "gegl:hstack", /* pointless */
+ "gegl:introspect", /* pointless */
+ "gegl:layer", /* we use gimp's ops */
+ "gegl:lcms-from-profile", /* not usable here */
+ "gegl:linear-gradient", /* we use the blend tool */
+ "gegl:map-absolute", /* pointless */
+ "gegl:map-relative", /* pointless */
+ "gegl:matting-global", /* used in the foreground select tool */
+ "gegl:matting-levin", /* used in the foreground select tool */
+ "gegl:opacity", /* poinless */
+ "gegl:path",
+ "gegl:posterize", /* we use gimp's op */
+ "gegl:radial-gradient", /* we use the blend tool */
+ "gegl:rectangle", /* pointless */
+ "gegl:seamless-clone", /* used in the seamless clone tool */
+ "gegl:text", /* we use gimp's text rendering */
+ "gegl:threshold", /* we use gimp's op */
+ "gegl:tile", /* pointless */
+ "gegl:unpremul", /* pointless */
+ "gegl:vector-stroke",
+ };
+
+ gchar **categories;
+ gint i;
+
+ /* Operations with no name are abstract base classes */
+ if (! name)
+ return TRUE;
+
+ /* use this flag to include all ops for testing */
+ if (g_getenv ("GIMP_TESTING_NO_GEGL_BLACKLIST"))
+ return FALSE;
+
+ if (g_str_has_prefix (name, "gimp"))
+ return TRUE;
+
+ for (i = 0; i < G_N_ELEMENTS (name_blacklist); i++)
+ {
+ if (! strcmp (name, name_blacklist[i]))
+ return TRUE;
+ }
+
+ if (! categories_str)
+ return FALSE;
+
+ categories = g_strsplit (categories_str, ":", 0);
+
+ for (i = 0; i < G_N_ELEMENTS (category_blacklist); i++)
+ {
+ gint j;
+
+ for (j = 0; categories[j]; j++)
+ if (! strcmp (categories[j], category_blacklist[i]))
+ {
+ g_strfreev (categories);
+ return TRUE;
+ }
+ }
+
+ g_strfreev (categories);
+
+ return FALSE;
+}
+
+
+/* Builds a GList of the class structures of all subtypes of type.
+ */
+static GList *
+gimp_get_subtype_classes (GType type,
+ GList *classes)
+{
+ GeglOperationClass *klass;
+ GType *ops;
+ const gchar *categories;
+ guint n_ops;
+ gint i;
+
+ if (! type)
+ return classes;
+
+ klass = GEGL_OPERATION_CLASS (g_type_class_ref (type));
+ ops = g_type_children (type, &n_ops);
+
+ categories = gegl_operation_class_get_key (klass, "categories");
+
+ if (! gimp_gegl_tool_operation_blacklisted (klass->name, categories))
+ classes = g_list_prepend (classes, klass);
+
+ for (i = 0; i < n_ops; i++)
+ classes = gimp_get_subtype_classes (ops[i], classes);
+
+ if (ops)
+ g_free (ops);
+
+ return classes;
+}
+
+static gint
+gimp_gegl_tool_compare_operation_names (GeglOperationClass *a,
+ GeglOperationClass *b)
+{
+ const gchar *name_a = gegl_operation_class_get_key (a, "title");
+ const gchar *name_b = gegl_operation_class_get_key (b, "title");
+
+ if (! name_a) name_a = a->name;
+ if (! name_b) name_b = b->name;
+
+ return strcmp (name_a, name_b);
+}
+
+static GList *
+gimp_get_geglopclasses (void)
+{
+ GList *opclasses;
+
+ opclasses = gimp_get_subtype_classes (GEGL_TYPE_OPERATION, NULL);
+
+ opclasses = g_list_sort (opclasses,
+ (GCompareFunc)
+ gimp_gegl_tool_compare_operation_names);
+
+ return opclasses;
+}
+
+
+/*****************/
+/* Gegl dialog */
+/*****************/
+
+static void
+gimp_gegl_tool_dialog (GimpFilterTool *filter_tool)
+{
+ GimpGeglTool *tool = GIMP_GEGL_TOOL (filter_tool);
+ GimpOperationTool *o_tool = GIMP_OPERATION_TOOL (filter_tool);
+ GtkListStore *store;
+ GtkCellRenderer *cell;
+ GtkWidget *main_vbox;
+ GtkWidget *hbox;
+ GtkWidget *combo;
+ GtkWidget *options_gui;
+ GtkWidget *options_box;
+ GList *opclasses;
+ GList *iter;
+
+ GIMP_FILTER_TOOL_CLASS (parent_class)->dialog (filter_tool);
+
+ options_box = g_weak_ref_get (&o_tool->options_box_ref);
+ g_return_if_fail (options_box);
+
+ main_vbox = gimp_filter_tool_dialog_get_vbox (filter_tool);
+
+ /* The operation combo box */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
+ gtk_box_reorder_child (GTK_BOX (main_vbox), hbox, 0);
+ gtk_widget_show (hbox);
+
+ store = gtk_list_store_new (N_COLUMNS,
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+
+ opclasses = gimp_get_geglopclasses ();
+
+ for (iter = opclasses; iter; iter = iter->next)
+ {
+ GeglOperationClass *opclass = GEGL_OPERATION_CLASS (iter->data);
+ const gchar *icon_name = NULL;
+ const gchar *op_name = opclass->name;
+ const gchar *title;
+ gchar *label;
+
+ if (g_str_has_prefix (opclass->name, "gegl:"))
+ icon_name = GIMP_ICON_GEGL;
+
+ if (g_str_has_prefix (op_name, "gegl:"))
+ op_name += strlen ("gegl:");
+
+ title = gegl_operation_class_get_key (opclass, "title");
+
+ if (title)
+ label = g_strdup_printf ("%s (%s)", title, op_name);
+ else
+ label = g_strdup (op_name);
+
+ gtk_list_store_insert_with_values (store, NULL, -1,
+ COLUMN_NAME, opclass->name,
+ COLUMN_LABEL, label,
+ COLUMN_ICON_NAME, icon_name,
+ -1);
+
+ g_free (label);
+ }
+
+ g_list_free (opclasses);
+
+ combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
+ g_object_unref (store);
+ gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
+ gtk_widget_show (combo);
+
+ cell = gtk_cell_renderer_pixbuf_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE);
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), cell,
+ "icon-name", COLUMN_ICON_NAME);
+
+ cell = gtk_cell_renderer_text_new ();
+ g_object_set (cell,
+ "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
+ NULL);
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), cell,
+ "text", COLUMN_LABEL);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_gegl_tool_operation_changed),
+ tool);
+
+ tool->operation_combo = combo;
+
+ tool->description_label = gtk_label_new ("");
+ gtk_label_set_line_wrap (GTK_LABEL (tool->description_label), TRUE);
+ gtk_label_set_xalign (GTK_LABEL (tool->description_label), 0.0);
+ gtk_box_pack_start (GTK_BOX (main_vbox), tool->description_label,
+ FALSE, FALSE, 0);
+ gtk_box_reorder_child (GTK_BOX (main_vbox), tool->description_label, 1);
+
+ /* The options vbox */
+ options_gui = gtk_label_new (_("Select an operation from the list above"));
+ gimp_label_set_attributes (GTK_LABEL (options_gui),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+ gtk_misc_set_padding (GTK_MISC (options_gui), 0, 4);
+ gtk_container_add (GTK_CONTAINER (options_box), options_gui);
+ g_object_unref (options_box);
+ g_weak_ref_set (&o_tool->options_gui_ref, options_gui);
+ gtk_widget_show (options_gui);
+}
+
+static void
+gimp_gegl_tool_halt (GimpGeglTool *gegl_tool)
+{
+ GimpOperationTool *op_tool = GIMP_OPERATION_TOOL (gegl_tool);
+
+ gimp_operation_tool_set_operation (op_tool, NULL,
+ NULL, NULL, NULL, NULL, NULL);
+}
+
+static void
+gimp_gegl_tool_operation_changed (GtkWidget *widget,
+ GimpGeglTool *tool)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gchar *operation;
+
+ if (! gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter))
+ return;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_NAME, &operation,
+ -1);
+
+ if (operation)
+ {
+ const gchar *description;
+
+ description = gegl_operation_get_key (operation, "description");
+
+ if (description)
+ {
+ gtk_label_set_text (GTK_LABEL (tool->description_label), description);
+ gtk_widget_show (tool->description_label);
+ }
+ else
+ {
+ gtk_widget_hide (tool->description_label);
+ }
+
+ gimp_operation_tool_set_operation (GIMP_OPERATION_TOOL (tool),
+ operation,
+ _("GEGL Operation"),
+ _("GEGL Operation"),
+ NULL,
+ GIMP_ICON_GEGL,
+ GIMP_HELP_TOOL_GEGL);
+ g_free (operation);
+ }
+}