/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * 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 . */ #include "config.h" #include #include #include "libpikabase/pikabase.h" #include "libpikaconfig/pikaconfig.h" #include "libpikawidgets/pikawidgets.h" #include "libpikawidgets/pikawidgets-private.h" #include "tools-types.h" #include "config/pikacoreconfig.h" #include "config/pikadialogconfig.h" #include "core/pika.h" #include "core/pikacontainer.h" #include "core/pikadatafactory.h" #include "core/pikadrawable.h" #include "core/pikaimage.h" #include "core/pikapaintinfo.h" #include "core/pikastrokeoptions.h" #include "core/pikatoolinfo.h" #include "display/pikadisplay.h" #include "paint/pikasourceoptions.h" #include "widgets/pikacontainercombobox.h" #include "widgets/pikacontainertreestore.h" #include "widgets/pikacontainerview.h" #include "widgets/pikapropwidgets.h" #include "widgets/pikaviewablebox.h" #include "widgets/pikaviewrenderer.h" #include "widgets/pikawidgets-utils.h" #include "pikabucketfilloptions.h" #include "pikapaintoptions-gui.h" #include "pika-intl.h" enum { PROP_0, PROP_FILL_MODE, PROP_FILL_AREA, PROP_FILL_TRANSPARENT, PROP_SAMPLE_MERGED, PROP_DIAGONAL_NEIGHBORS, PROP_ANTIALIAS, PROP_FEATHER, PROP_FEATHER_RADIUS, PROP_THRESHOLD, PROP_LINE_ART_SOURCE, PROP_LINE_ART_THRESHOLD, PROP_LINE_ART_MAX_GROW, PROP_LINE_ART_STROKE, PROP_LINE_ART_STROKE_TOOL, PROP_LINE_ART_AUTOMATIC_CLOSURE, PROP_LINE_ART_MAX_GAP_LENGTH, PROP_FILL_CRITERION, PROP_FILL_COLOR_AS_LINE_ART, PROP_FILL_COLOR_AS_LINE_ART_THRESHOLD, }; struct _PikaBucketFillOptionsPrivate { GtkWidget *diagonal_neighbors_checkbox; GtkWidget *threshold_scale; GtkWidget *similar_color_frame; GtkWidget *line_art_settings; GtkWidget *fill_as_line_art_frame; GtkWidget *line_art_detect_opacity; }; static void pika_bucket_fill_options_config_iface_init (PikaConfigInterface *config_iface); static void pika_bucket_fill_options_finalize (GObject *object); static void pika_bucket_fill_options_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void pika_bucket_fill_options_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static gboolean pika_bucket_fill_options_select_stroke_tool (PikaContainerView *view, GList *items, GList *paths, PikaBucketFillOptions *options); static void pika_bucket_fill_options_tool_cell_renderer (GtkCellLayout *layout, GtkCellRenderer *cell, GtkTreeModel *model, GtkTreeIter *iter, gpointer data); static void pika_bucket_fill_options_image_changed (PikaContext *context, PikaImage *image, PikaBucketFillOptions *options); static void pika_bucket_fill_options_reset (PikaConfig *config); static void pika_bucket_fill_options_update_area (PikaBucketFillOptions *options); G_DEFINE_TYPE_WITH_CODE (PikaBucketFillOptions, pika_bucket_fill_options, PIKA_TYPE_PAINT_OPTIONS, G_ADD_PRIVATE (PikaBucketFillOptions) G_IMPLEMENT_INTERFACE (PIKA_TYPE_CONFIG, pika_bucket_fill_options_config_iface_init)) #define parent_class pika_bucket_fill_options_parent_class static PikaConfigInterface *parent_config_iface = NULL; static void pika_bucket_fill_options_class_init (PikaBucketFillOptionsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = pika_bucket_fill_options_finalize; object_class->set_property = pika_bucket_fill_options_set_property; object_class->get_property = pika_bucket_fill_options_get_property; PIKA_CONFIG_PROP_ENUM (object_class, PROP_FILL_MODE, "fill-mode", _("Fill type"), NULL, PIKA_TYPE_BUCKET_FILL_MODE, PIKA_BUCKET_FILL_FG, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_ENUM (object_class, PROP_FILL_AREA, "fill-area", _("Fill selection"), _("Which area will be filled"), PIKA_TYPE_BUCKET_FILL_AREA, PIKA_BUCKET_FILL_SIMILAR_COLORS, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_FILL_TRANSPARENT, "fill-transparent", _("Fill transparent areas"), _("Allow completely transparent regions " "to be filled"), TRUE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_SAMPLE_MERGED, "sample-merged", _("Sample merged"), _("Base filled area on all visible layers"), FALSE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_DIAGONAL_NEIGHBORS, "diagonal-neighbors", _("Diagonal neighbors"), _("Treat diagonally neighboring pixels as " "connected"), FALSE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_ANTIALIAS, "antialias", _("Antialiasing"), _("Base fill opacity on color difference from " "the clicked pixel (see threshold) or on line " " art borders. Disable antialiasing to fill " "the entire area uniformly."), TRUE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_FEATHER, "feather", _("Feather edges"), _("Enable feathering of fill edges"), FALSE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_FEATHER_RADIUS, "feather-radius", _("Radius"), _("Radius of feathering"), 0.0, 100.0, 10.0, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_THRESHOLD, "threshold", _("Threshold"), _("Maximum color difference"), 0.0, 255.0, 15.0, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_ENUM (object_class, PROP_LINE_ART_SOURCE, "line-art-source", _("Source"), _("Source image for line art computation"), PIKA_TYPE_LINE_ART_SOURCE, PIKA_LINE_ART_SOURCE_SAMPLE_MERGED, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_FILL_COLOR_AS_LINE_ART, "fill-color-as-line-art", _("Manual closure in fill layer"), _("Consider pixels of selected layer and filled with the fill color as line art closure"), FALSE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_FILL_COLOR_AS_LINE_ART_THRESHOLD, "fill-color-as-line-art-threshold", _("Threshold"), _("Maximum color difference"), 0.0, 255.0, 15.0, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_LINE_ART_THRESHOLD, "line-art-threshold", _("Line art detection threshold"), _("Threshold to detect contour (higher values will include more pixels)"), 0.0, 1.0, 0.92, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_INT (object_class, PROP_LINE_ART_MAX_GROW, "line-art-max-grow", _("Maximum growing size"), _("Maximum number of pixels grown under the line art"), 1, 100, 3, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_LINE_ART_STROKE, "line-art-stroke-border", _("Stroke borders"), _("Stroke fill borders with last stroke options"), FALSE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_STRING (object_class, PROP_LINE_ART_STROKE_TOOL, "line-art-stroke-tool", _("Stroke tool"), _("The tool to stroke the fill borders with"), "pika-pencil", PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_LINE_ART_AUTOMATIC_CLOSURE, "line-art-automatic-closure", _("Automatic closure"), _("Geometric analysis of the stroke contours to close line arts by splines/segments"), TRUE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_INT (object_class, PROP_LINE_ART_MAX_GAP_LENGTH, "line-art-max-gap-length", _("Maximum gap length"), _("Maximum gap (in pixels) in line art which can be closed"), 0, 1000, 100, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_ENUM (object_class, PROP_FILL_CRITERION, "fill-criterion", _("Fill by"), _("Criterion used for determining color similarity"), PIKA_TYPE_SELECT_CRITERION, PIKA_SELECT_CRITERION_COMPOSITE, PIKA_PARAM_STATIC_STRINGS); } static void pika_bucket_fill_options_config_iface_init (PikaConfigInterface *config_iface) { parent_config_iface = g_type_interface_peek_parent (config_iface); config_iface->reset = pika_bucket_fill_options_reset; } static void pika_bucket_fill_options_init (PikaBucketFillOptions *options) { options->priv = pika_bucket_fill_options_get_instance_private (options); options->line_art_stroke_tool = NULL; } static void pika_bucket_fill_options_finalize (GObject *object) { PikaBucketFillOptions *options = PIKA_BUCKET_FILL_OPTIONS (object); g_clear_object (&options->stroke_options); g_clear_pointer (&options->line_art_stroke_tool, g_free); G_OBJECT_CLASS (parent_class)->finalize (object); } static void pika_bucket_fill_options_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { PikaBucketFillOptions *options = PIKA_BUCKET_FILL_OPTIONS (object); PikaToolOptions *tool_options = PIKA_TOOL_OPTIONS (object); switch (property_id) { case PROP_FILL_MODE: options->fill_mode = g_value_get_enum (value); pika_bucket_fill_options_update_area (options); break; case PROP_FILL_AREA: options->fill_area = g_value_get_enum (value); pika_bucket_fill_options_update_area (options); break; case PROP_FILL_TRANSPARENT: options->fill_transparent = g_value_get_boolean (value); break; case PROP_SAMPLE_MERGED: options->sample_merged = g_value_get_boolean (value); break; case PROP_DIAGONAL_NEIGHBORS: options->diagonal_neighbors = g_value_get_boolean (value); break; case PROP_ANTIALIAS: options->antialias = g_value_get_boolean (value); break; case PROP_FEATHER: options->feather = g_value_get_boolean (value); break; case PROP_FEATHER_RADIUS: options->feather_radius = g_value_get_double (value); break; case PROP_THRESHOLD: options->threshold = g_value_get_double (value); break; case PROP_LINE_ART_SOURCE: options->line_art_source = g_value_get_enum (value); pika_bucket_fill_options_update_area (options); break; case PROP_LINE_ART_THRESHOLD: options->line_art_threshold = g_value_get_double (value); break; case PROP_LINE_ART_MAX_GROW: options->line_art_max_grow = g_value_get_int (value); break; case PROP_LINE_ART_STROKE: options->line_art_stroke = g_value_get_boolean (value); break; case PROP_LINE_ART_STROKE_TOOL: g_clear_pointer (&options->line_art_stroke_tool, g_free); options->line_art_stroke_tool = g_value_dup_string (value); if (options->stroke_options) { PikaPaintInfo *paint_info = NULL; Pika *pika = tool_options->tool_info->pika; if (! options->line_art_stroke_tool) options->line_art_stroke_tool = g_strdup ("pika-pencil"); paint_info = PIKA_PAINT_INFO (pika_container_get_child_by_name (pika->paint_info_list, options->line_art_stroke_tool)); if (! paint_info && ! pika_container_is_empty (pika->paint_info_list)) paint_info = PIKA_PAINT_INFO (pika_container_get_child_by_index (pika->paint_info_list, 0)); g_object_set (options->stroke_options, "paint-info", paint_info, NULL); } break; case PROP_LINE_ART_AUTOMATIC_CLOSURE: options->line_art_automatic_closure = g_value_get_boolean (value); break; case PROP_LINE_ART_MAX_GAP_LENGTH: options->line_art_max_gap_length = g_value_get_int (value); break; case PROP_FILL_CRITERION: options->fill_criterion = g_value_get_enum (value); break; case PROP_FILL_COLOR_AS_LINE_ART: options->fill_as_line_art = g_value_get_boolean (value); break; case PROP_FILL_COLOR_AS_LINE_ART_THRESHOLD: options->fill_as_line_art_threshold = g_value_get_double (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_bucket_fill_options_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { PikaBucketFillOptions *options = PIKA_BUCKET_FILL_OPTIONS (object); switch (property_id) { case PROP_FILL_MODE: g_value_set_enum (value, options->fill_mode); break; case PROP_FILL_AREA: g_value_set_enum (value, options->fill_area); break; case PROP_FILL_TRANSPARENT: g_value_set_boolean (value, options->fill_transparent); break; case PROP_SAMPLE_MERGED: g_value_set_boolean (value, options->sample_merged); break; case PROP_DIAGONAL_NEIGHBORS: g_value_set_boolean (value, options->diagonal_neighbors); break; case PROP_ANTIALIAS: g_value_set_boolean (value, options->antialias); break; case PROP_FEATHER: g_value_set_boolean (value, options->feather); break; case PROP_FEATHER_RADIUS: g_value_set_double (value, options->feather_radius); break; case PROP_THRESHOLD: g_value_set_double (value, options->threshold); break; case PROP_LINE_ART_SOURCE: g_value_set_enum (value, options->line_art_source); break; case PROP_LINE_ART_THRESHOLD: g_value_set_double (value, options->line_art_threshold); break; case PROP_LINE_ART_MAX_GROW: g_value_set_int (value, options->line_art_max_grow); break; case PROP_LINE_ART_STROKE: g_value_set_boolean (value, options->line_art_stroke); break; case PROP_LINE_ART_STROKE_TOOL: g_value_set_string (value, options->line_art_stroke_tool); break; case PROP_LINE_ART_AUTOMATIC_CLOSURE: g_value_set_boolean (value, options->line_art_automatic_closure); break; case PROP_LINE_ART_MAX_GAP_LENGTH: g_value_set_int (value, options->line_art_max_gap_length); break; case PROP_FILL_CRITERION: g_value_set_enum (value, options->fill_criterion); break; case PROP_FILL_COLOR_AS_LINE_ART: g_value_set_boolean (value, options->fill_as_line_art); break; case PROP_FILL_COLOR_AS_LINE_ART_THRESHOLD: g_value_set_double (value, options->fill_as_line_art_threshold); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static gboolean pika_bucket_fill_options_select_stroke_tool (PikaContainerView *view, GList *items, GList *paths, PikaBucketFillOptions *options) { GList *iter; for (iter = items; iter; iter = iter->next) { g_object_set (options, "line-art-stroke-tool", iter->data ? pika_object_get_name (iter->data) : NULL, NULL); break; } return TRUE; } static void pika_bucket_fill_options_tool_cell_renderer (GtkCellLayout *layout, GtkCellRenderer *cell, GtkTreeModel *model, GtkTreeIter *iter, gpointer data) { PikaViewRenderer *renderer; gtk_tree_model_get (model, iter, PIKA_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer, -1); if (renderer->viewable) { PikaPaintInfo *info = PIKA_PAINT_INFO (renderer->viewable); if (PIKA_IS_SOURCE_OPTIONS (info->paint_options)) gtk_tree_store_set (GTK_TREE_STORE (model), iter, PIKA_CONTAINER_TREE_STORE_COLUMN_NAME_SENSITIVE, FALSE, -1); } g_object_unref (renderer); } static void pika_bucket_fill_options_image_changed (PikaContext *context, PikaImage *image, PikaBucketFillOptions *options) { PikaImage *prev_image; prev_image = g_object_get_data (G_OBJECT (options), "pika-bucket-fill-options-image"); if (image != prev_image) { if (prev_image) g_signal_handlers_disconnect_by_func (prev_image, G_CALLBACK (pika_bucket_fill_options_update_area), options); if (image) { g_signal_connect_object (image, "selected-channels-changed", G_CALLBACK (pika_bucket_fill_options_update_area), options, G_CONNECT_SWAPPED); g_signal_connect_object (image, "selected-layers-changed", G_CALLBACK (pika_bucket_fill_options_update_area), options, G_CONNECT_SWAPPED); } g_object_set_data (G_OBJECT (options), "pika-bucket-fill-options-image", image); pika_bucket_fill_options_update_area (options); } } static void pika_bucket_fill_options_reset (PikaConfig *config) { PikaToolOptions *tool_options = PIKA_TOOL_OPTIONS (config); GParamSpec *pspec; pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (config), "threshold"); if (pspec) G_PARAM_SPEC_DOUBLE (pspec)->default_value = tool_options->tool_info->pika->config->default_threshold; parent_config_iface->reset (config); } static void pika_bucket_fill_options_update_area (PikaBucketFillOptions *options) { PikaImage *image; GList *drawables = NULL; const gchar *tooltip = _("Opaque pixels will be considered as line art " "instead of low luminance pixels"); image = pika_context_get_image (pika_get_user_context (PIKA_CONTEXT (options)->pika)); /* GUI not created yet. */ if (! options->priv->threshold_scale) return; if (image) drawables = pika_image_get_selected_drawables (image); switch (options->fill_area) { case PIKA_BUCKET_FILL_LINE_ART: gtk_widget_hide (options->priv->similar_color_frame); gtk_widget_show (options->priv->line_art_settings); if ((options->fill_mode == PIKA_BUCKET_FILL_FG || options->fill_mode == PIKA_BUCKET_FILL_BG) && (options->line_art_source == PIKA_LINE_ART_SOURCE_LOWER_LAYER || options->line_art_source == PIKA_LINE_ART_SOURCE_UPPER_LAYER)) gtk_widget_set_sensitive (options->priv->fill_as_line_art_frame, TRUE); else gtk_widget_set_sensitive (options->priv->fill_as_line_art_frame, FALSE); if (image != NULL && options->line_art_source != PIKA_LINE_ART_SOURCE_SAMPLE_MERGED && g_list_length (drawables) == 1) { PikaDrawable *source = NULL; PikaItem *parent; PikaContainer *container; gint index; parent = pika_item_get_parent (PIKA_ITEM (drawables->data)); if (parent) container = pika_viewable_get_children (PIKA_VIEWABLE (parent)); else container = pika_image_get_layers (image); index = pika_item_get_index (PIKA_ITEM (drawables->data)); if (options->line_art_source == PIKA_LINE_ART_SOURCE_ACTIVE_LAYER) source = drawables->data; else if (options->line_art_source == PIKA_LINE_ART_SOURCE_LOWER_LAYER) source = PIKA_DRAWABLE (pika_container_get_child_by_index (container, index + 1)); else if (options->line_art_source == PIKA_LINE_ART_SOURCE_UPPER_LAYER) source = PIKA_DRAWABLE (pika_container_get_child_by_index (container, index - 1)); gtk_widget_set_sensitive (options->priv->line_art_detect_opacity, source != NULL && pika_drawable_has_alpha (source)); if (source == NULL) tooltip = _("No valid source drawable selected"); else if (! pika_drawable_has_alpha (source)) tooltip = _("The source drawable has no alpha channel"); } else { gtk_widget_set_sensitive (options->priv->line_art_detect_opacity, TRUE); } gtk_widget_set_tooltip_text (options->priv->line_art_detect_opacity, tooltip); break; case PIKA_BUCKET_FILL_SIMILAR_COLORS: gtk_widget_show (options->priv->similar_color_frame); gtk_widget_hide (options->priv->line_art_settings); break; default: gtk_widget_hide (options->priv->similar_color_frame); gtk_widget_hide (options->priv->line_art_settings); break; } g_list_free (drawables); } GtkWidget * pika_bucket_fill_options_gui (PikaToolOptions *tool_options) { PikaBucketFillOptions *options = PIKA_BUCKET_FILL_OPTIONS (tool_options); GObject *config = G_OBJECT (tool_options); Pika *pika = tool_options->tool_info->pika; GtkWidget *vbox = pika_paint_options_gui (tool_options); GtkWidget *box2; GtkWidget *frame; GtkWidget *hbox; GtkWidget *widget; GtkWidget *scale; GtkWidget *combo; gchar *str; gboolean bold; GdkModifierType extend_mask = pika_get_extend_selection_mask (); GdkModifierType toggle_mask = GDK_MOD1_MASK; /* fill type */ str = g_strdup_printf (_("Fill Type (%s)"), pika_get_mod_string (toggle_mask)), frame = pika_prop_enum_radio_frame_new (config, "fill-mode", str, 0, 0); gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); g_free (str); hbox = pika_prop_pattern_box_new (NULL, PIKA_CONTEXT (tool_options), NULL, 2, "pattern-view-type", "pattern-view-size"); pika_enum_radio_frame_add (GTK_FRAME (frame), hbox, PIKA_BUCKET_FILL_PATTERN, TRUE); /* fill selection */ str = g_strdup_printf (_("Affected Area (%s)"), pika_get_mod_string (extend_mask)); frame = pika_prop_enum_radio_frame_new (config, "fill-area", str, 0, 0); gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); g_free (str); /* Similar color frame */ frame = pika_frame_new (_("Finding Similar Colors")); gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); options->priv->similar_color_frame = frame; gtk_widget_show (frame); box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_container_add (GTK_CONTAINER (frame), box2); gtk_widget_show (box2); /* the fill transparent areas toggle */ widget = pika_prop_check_button_new (config, "fill-transparent", NULL); gtk_box_pack_start (GTK_BOX (box2), widget, FALSE, FALSE, 0); /* the sample merged toggle */ widget = pika_prop_check_button_new (config, "sample-merged", NULL); gtk_box_pack_start (GTK_BOX (box2), widget, FALSE, FALSE, 0); /* the diagonal neighbors toggle */ widget = pika_prop_check_button_new (config, "diagonal-neighbors", NULL); gtk_box_pack_start (GTK_BOX (box2), widget, FALSE, FALSE, 0); options->priv->diagonal_neighbors_checkbox = widget; /* the antialias toggle */ widget = pika_prop_check_button_new (config, "antialias", NULL); gtk_box_pack_start (GTK_BOX (box2), widget, FALSE, FALSE, 0); /* the threshold scale */ scale = pika_prop_spin_scale_new (config, "threshold", 1.0, 16.0, 1); gtk_box_pack_start (GTK_BOX (box2), scale, FALSE, FALSE, 0); options->priv->threshold_scale = scale; /* the fill criterion combo */ combo = pika_prop_enum_combo_box_new (config, "fill-criterion", 0, 0); pika_int_combo_box_set_label (PIKA_INT_COMBO_BOX (combo), _("Fill by")); gtk_box_pack_start (GTK_BOX (box2), combo, FALSE, FALSE, 0); /* Line art settings */ options->priv->line_art_settings = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); gtk_box_pack_start (GTK_BOX (vbox), options->priv->line_art_settings, FALSE, FALSE, 0); pika_widget_set_identifier (options->priv->line_art_settings, "line-art-settings"); frame = pika_frame_new (NULL); gtk_box_pack_start (GTK_BOX (options->priv->line_art_settings), frame, FALSE, FALSE, 0); gtk_widget_show (frame); /* Line art: label widget */ box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3); gtk_frame_set_label_widget (GTK_FRAME (frame), box2); gtk_widget_show (box2); widget = gtk_label_new (_("Line Art Detection")); gtk_box_pack_start (GTK_BOX (box2), widget, FALSE, FALSE, 0); gtk_widget_style_get (GTK_WIDGET (frame), "label-bold", &bold, NULL); pika_label_set_attributes (GTK_LABEL (widget), PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD, -1); gtk_widget_show (widget); options->line_art_busy_box = pika_busy_box_new (_("(computing...)")); gtk_box_pack_start (GTK_BOX (box2), options->line_art_busy_box, FALSE, FALSE, 0); box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_container_add (GTK_CONTAINER (frame), box2); gtk_widget_show (box2); /* Line Art: source combo (replace sample merged!) */ combo = pika_prop_enum_combo_box_new (config, "line-art-source", 0, 0); pika_int_combo_box_set_label (PIKA_INT_COMBO_BOX (combo), _("Source")); gtk_box_pack_start (GTK_BOX (box2), combo, FALSE, FALSE, 0); /* the fill transparent areas toggle */ widget = pika_prop_check_button_new (config, "fill-transparent", _("Detect opacity rather than grayscale")); options->priv->line_art_detect_opacity = widget; gtk_box_pack_start (GTK_BOX (box2), widget, FALSE, FALSE, 0); /* Line Art: stroke threshold */ scale = pika_prop_spin_scale_new (config, "line-art-threshold", 0.05, 0.1, 2); gtk_box_pack_start (GTK_BOX (box2), scale, FALSE, FALSE, 0); /* Line Art Closure frame */ frame = pika_frame_new (NULL); gtk_box_pack_start (GTK_BOX (options->priv->line_art_settings), frame, FALSE, FALSE, 0); gtk_widget_show (frame); /* Line Art Closure: frame label */ widget = gtk_label_new (_("Line Art Closure")); gtk_frame_set_label_widget (GTK_FRAME (frame), widget); gtk_widget_show (widget); box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_container_add (GTK_CONTAINER (frame), box2); gtk_widget_show (box2); /* Line Art Closure: max gap length */ scale = pika_prop_spin_scale_new (config, "line-art-max-gap-length", 1, 5, 0); frame = pika_prop_expanding_frame_new (config, "line-art-automatic-closure", NULL, scale, NULL); gtk_box_pack_start (GTK_BOX (box2), frame, FALSE, FALSE, 0); /* Line Art Closure: manual line art closure */ scale = pika_prop_spin_scale_new (config, "fill-color-as-line-art-threshold", 1.0, 16.0, 1); frame = pika_prop_expanding_frame_new (config, "fill-color-as-line-art", NULL, scale, NULL); gtk_box_pack_start (GTK_BOX (box2), frame, FALSE, FALSE, 0); options->priv->fill_as_line_art_frame = frame; /* Line Art Borders frame */ frame = pika_frame_new (NULL); gtk_box_pack_start (GTK_BOX (options->priv->line_art_settings), frame, FALSE, FALSE, 0); gtk_widget_show (frame); /* Line Art Borders: frame label */ widget = gtk_label_new (_("Fill borders")); gtk_frame_set_label_widget (GTK_FRAME (frame), widget); gtk_widget_show (widget); box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_container_add (GTK_CONTAINER (frame), box2); gtk_widget_show (box2); /* Line Art Borders: max growing size */ scale = pika_prop_spin_scale_new (config, "line-art-max-grow", 1, 5, 0); gtk_box_pack_start (GTK_BOX (box2), scale, FALSE, FALSE, 0); /* Line Art Borders: feather radius scale */ scale = pika_prop_spin_scale_new (config, "feather-radius", 1.0, 10.0, 1); frame = pika_prop_expanding_frame_new (config, "feather", NULL, scale, NULL); gtk_box_pack_start (GTK_BOX (box2), frame, FALSE, FALSE, 0); /* Line Art Borders: stroke border with paint brush */ options->stroke_options = pika_stroke_options_new (pika, pika_get_user_context (pika), TRUE); pika_config_sync (G_OBJECT (PIKA_DIALOG_CONFIG (pika->config)->stroke_options), G_OBJECT (options->stroke_options), 0); widget = pika_container_combo_box_new (pika->paint_info_list, PIKA_CONTEXT (options->stroke_options), 16, 0); gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (widget), PIKA_CONTAINER_COMBO_BOX (widget)->viewable_renderer, pika_bucket_fill_options_tool_cell_renderer, options, NULL); g_signal_connect (widget, "select-items", G_CALLBACK (pika_bucket_fill_options_select_stroke_tool), options); frame = pika_prop_expanding_frame_new (config, "line-art-stroke-border", NULL, widget, NULL); gtk_box_pack_start (GTK_BOX (box2), frame, FALSE, FALSE, 0); gtk_widget_show (widget); pika_bucket_fill_options_update_area (options); g_signal_connect (pika_get_user_context (PIKA_CONTEXT (tool_options)->pika), "image-changed", G_CALLBACK (pika_bucket_fill_options_image_changed), tool_options); return vbox; }