/* 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-1999 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 "libpikamath/pikamath.h" #include "libpikaconfig/pikaconfig.h" #include "paint-types.h" #include "core/pika.h" #include "core/pikabrush-header.h" #include "core/pikabrushgenerated.h" #include "core/pikaimage.h" #include "core/pikadynamics.h" #include "core/pikadynamicsoutput.h" #include "core/pikagradient.h" #include "core/pikapaintinfo.h" #include "pikabrushcore.h" #include "pikapaintoptions.h" #include "pika-intl.h" #define DEFAULT_BRUSH_SIZE 20.0 #define DEFAULT_BRUSH_ASPECT_RATIO 0.0 #define DEFAULT_BRUSH_ANGLE 0.0 #define DEFAULT_BRUSH_SPACING 0.1 #define DEFAULT_BRUSH_HARDNESS 1.0 /* Generated brushes have their own */ #define DEFAULT_BRUSH_FORCE 0.5 #define DEFAULT_BRUSH_LINK_SIZE TRUE #define DEFAULT_BRUSH_LINK_ASPECT_RATIO TRUE #define DEFAULT_BRUSH_LINK_ANGLE TRUE #define DEFAULT_BRUSH_LINK_SPACING TRUE #define DEFAULT_BRUSH_LINK_HARDNESS TRUE #define DEFAULT_BRUSH_LOCK_TO_VIEW FALSE #define DEFAULT_APPLICATION_MODE PIKA_PAINT_CONSTANT #define DEFAULT_HARD FALSE #define DEFAULT_USE_JITTER FALSE #define DEFAULT_JITTER_AMOUNT 0.2 #define DEFAULT_DYNAMICS_ENABLED TRUE #define DEFAULT_FADE_LENGTH 100.0 #define DEFAULT_FADE_REVERSE FALSE #define DEFAULT_FADE_REPEAT PIKA_REPEAT_NONE #define DEFAULT_FADE_UNIT PIKA_UNIT_PIXEL #define DEFAULT_GRADIENT_REVERSE FALSE #define DEFAULT_GRADIENT_BLEND_SPACE PIKA_GRADIENT_BLEND_RGB_PERCEPTUAL #define DEFAULT_GRADIENT_REPEAT PIKA_REPEAT_NONE #define DYNAMIC_MAX_VALUE 1.0 #define DYNAMIC_MIN_VALUE 0.0 #define DEFAULT_SMOOTHING_QUALITY 20 #define DEFAULT_SMOOTHING_FACTOR 50 #define DEFAULT_EXPAND_USE FALSE #define DEFAULT_EXPAND_AMOUNT 100.0 #define DEFAULT_EXPAND_FILL_TYPE PIKA_FILL_TRANSPARENT #define DEFAULT_EXPAND_MASK_FILL_TYPE PIKA_ADD_MASK_WHITE enum { PROP_0, PROP_PAINT_INFO, PROP_USE_APPLICATOR, /* temp debug */ PROP_BRUSH_SIZE, PROP_BRUSH_ASPECT_RATIO, PROP_BRUSH_ANGLE, PROP_BRUSH_SPACING, PROP_BRUSH_HARDNESS, PROP_BRUSH_FORCE, PROP_BRUSH_LINK_SIZE, PROP_BRUSH_LINK_ASPECT_RATIO, PROP_BRUSH_LINK_ANGLE, PROP_BRUSH_LINK_SPACING, PROP_BRUSH_LINK_HARDNESS, PROP_BRUSH_LOCK_TO_VIEW, PROP_APPLICATION_MODE, PROP_HARD, PROP_USE_JITTER, PROP_JITTER_AMOUNT, PROP_DYNAMICS_ENABLED, PROP_FADE_LENGTH, PROP_FADE_REVERSE, PROP_FADE_REPEAT, PROP_FADE_UNIT, PROP_GRADIENT_REVERSE, PROP_GRADIENT_BLEND_COLOR_SPACE, PROP_GRADIENT_REPEAT, PROP_BRUSH_VIEW_TYPE, PROP_BRUSH_VIEW_SIZE, PROP_DYNAMICS_VIEW_TYPE, PROP_DYNAMICS_VIEW_SIZE, PROP_PATTERN_VIEW_TYPE, PROP_PATTERN_VIEW_SIZE, PROP_GRADIENT_VIEW_TYPE, PROP_GRADIENT_VIEW_SIZE, PROP_USE_SMOOTHING, PROP_SMOOTHING_QUALITY, PROP_SMOOTHING_FACTOR, PROP_EXPAND_USE, PROP_EXPAND_AMOUNT, PROP_EXPAND_FILL_TYPE, PROP_EXPAND_MASK_FILL_TYPE }; static void pika_paint_options_config_iface_init (PikaConfigInterface *config_iface); static void pika_paint_options_dispose (GObject *object); static void pika_paint_options_finalize (GObject *object); static void pika_paint_options_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void pika_paint_options_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void pika_paint_options_brush_changed (PikaContext *context, PikaBrush *brush); static void pika_paint_options_brush_notify (PikaBrush *brush, const GParamSpec *pspec, PikaPaintOptions *options); static PikaConfig * pika_paint_options_duplicate (PikaConfig *config); static gboolean pika_paint_options_copy (PikaConfig *src, PikaConfig *dest, GParamFlags flags); static void pika_paint_options_reset (PikaConfig *config); G_DEFINE_TYPE_WITH_CODE (PikaPaintOptions, pika_paint_options, PIKA_TYPE_TOOL_OPTIONS, G_IMPLEMENT_INTERFACE (PIKA_TYPE_CONFIG, pika_paint_options_config_iface_init)) #define parent_class pika_paint_options_parent_class static PikaConfigInterface *parent_config_iface = NULL; static void pika_paint_options_class_init (PikaPaintOptionsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); PikaContextClass *context_class = PIKA_CONTEXT_CLASS (klass); object_class->dispose = pika_paint_options_dispose; object_class->finalize = pika_paint_options_finalize; object_class->set_property = pika_paint_options_set_property; object_class->get_property = pika_paint_options_get_property; context_class->brush_changed = pika_paint_options_brush_changed; g_object_class_install_property (object_class, PROP_PAINT_INFO, g_param_spec_object ("paint-info", NULL, NULL, PIKA_TYPE_PAINT_INFO, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_USE_APPLICATOR, g_param_spec_boolean ("use-applicator", "Use PikaApplicator", NULL, FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_BRUSH_SIZE, "brush-size", _("Size"), _("Brush Size"), 1.0, PIKA_BRUSH_MAX_SIZE, DEFAULT_BRUSH_SIZE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_BRUSH_ASPECT_RATIO, "brush-aspect-ratio", _("Aspect Ratio"), _("Brush Aspect Ratio"), -20.0, 20.0, DEFAULT_BRUSH_ASPECT_RATIO, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_BRUSH_ANGLE, "brush-angle", _("Angle"), _("Brush Angle"), -180.0, 180.0, DEFAULT_BRUSH_ANGLE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_BRUSH_SPACING, "brush-spacing", _("Spacing"), _("Brush Spacing"), 0.01, 50.0, DEFAULT_BRUSH_SPACING, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_BRUSH_HARDNESS, "brush-hardness", _("Hardness"), _("Brush Hardness"), 0.0, 1.0, DEFAULT_BRUSH_HARDNESS, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_BRUSH_FORCE, "brush-force", _("Force"), _("Brush Force"), 0.0, 1.0, DEFAULT_BRUSH_FORCE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_BRUSH_LINK_SIZE, "brush-link-size", _("Link Size"), _("Link brush size to brush native"), DEFAULT_BRUSH_LINK_SIZE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_BRUSH_LINK_ASPECT_RATIO, "brush-link-aspect-ratio", _("Link Aspect Ratio"), _("Link brush aspect ratio to brush native"), DEFAULT_BRUSH_LINK_ASPECT_RATIO, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_BRUSH_LINK_ANGLE, "brush-link-angle", _("Link Angle"), _("Link brush angle to brush native"), DEFAULT_BRUSH_LINK_ANGLE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_BRUSH_LINK_SPACING, "brush-link-spacing", _("Link Spacing"), _("Link brush spacing to brush native"), DEFAULT_BRUSH_LINK_SPACING, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_BRUSH_LINK_HARDNESS, "brush-link-hardness", _("Link Hardness"), _("Link brush hardness to brush native"), DEFAULT_BRUSH_LINK_HARDNESS, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_BRUSH_LOCK_TO_VIEW, "brush-lock-to-view", _("Lock brush to view"), _("Keep brush appearance fixed relative to the view"), DEFAULT_BRUSH_LOCK_TO_VIEW, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_ENUM (object_class, PROP_APPLICATION_MODE, "application-mode", _("Incremental"), _("Every stamp has its own opacity"), PIKA_TYPE_PAINT_APPLICATION_MODE, DEFAULT_APPLICATION_MODE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_HARD, "hard", _("Hard edge"), _("Ignore fuzziness of the current brush"), DEFAULT_HARD, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_JITTER, "use-jitter", _("Apply Jitter"), _("Scatter brush as you paint"), DEFAULT_USE_JITTER, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_EXPAND_USE, "expand-use", _("Expand Layers"), _("Expand active layer as you paint"), DEFAULT_EXPAND_USE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_EXPAND_AMOUNT, "expand-amount", _("Amount"), _("Amount of expansion"), 1.0, 1000.0, DEFAULT_EXPAND_AMOUNT, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_ENUM (object_class, PROP_EXPAND_FILL_TYPE, "expand-fill-type", _("Fill With"), _("Fill layer with"), PIKA_TYPE_FILL_TYPE, DEFAULT_EXPAND_FILL_TYPE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_ENUM (object_class, PROP_EXPAND_MASK_FILL_TYPE, "expand-mask-fill-type", _("Fill Mask With"), _("Fill layer mask with"), PIKA_TYPE_ADD_MASK_TYPE, DEFAULT_EXPAND_MASK_FILL_TYPE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_DYNAMICS_ENABLED, "dynamics-enabled", _("Enable dynamics"), _("Apply dynamics curves to paint settings"), DEFAULT_DYNAMICS_ENABLED, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_JITTER_AMOUNT, "jitter-amount", _("Amount"), _("Distance of scattering"), 0.0, 50.0, DEFAULT_JITTER_AMOUNT, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_FADE_LENGTH, "fade-length", _("Fade length"), _("Distance over which strokes fade out"), 0.0, 32767.0, DEFAULT_FADE_LENGTH, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_UNIT (object_class, PROP_FADE_UNIT, "fade-unit", NULL, NULL, TRUE, TRUE, DEFAULT_FADE_UNIT, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_FADE_REVERSE, "fade-reverse", _("Reverse"), _("Reverse direction of fading"), DEFAULT_FADE_REVERSE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_ENUM (object_class, PROP_FADE_REPEAT, "fade-repeat", _("Repeat"), _("How fade is repeated as you paint"), PIKA_TYPE_REPEAT_MODE, DEFAULT_FADE_REPEAT, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_GRADIENT_REVERSE, "gradient-reverse", NULL, NULL, DEFAULT_GRADIENT_REVERSE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_ENUM (object_class, PROP_GRADIENT_BLEND_COLOR_SPACE, "gradient-blend-color-space", _("Blend Color Space"), _("Which color space to use when blending RGB gradient segments"), PIKA_TYPE_GRADIENT_BLEND_COLOR_SPACE, DEFAULT_GRADIENT_BLEND_SPACE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_ENUM (object_class, PROP_GRADIENT_REPEAT, "gradient-repeat", _("Repeat"), NULL, PIKA_TYPE_REPEAT_MODE, DEFAULT_GRADIENT_REPEAT, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_ENUM (object_class, PROP_BRUSH_VIEW_TYPE, "brush-view-type", NULL, NULL, PIKA_TYPE_VIEW_TYPE, PIKA_VIEW_TYPE_GRID, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_INT (object_class, PROP_BRUSH_VIEW_SIZE, "brush-view-size", NULL, NULL, PIKA_VIEW_SIZE_TINY, PIKA_VIEWABLE_MAX_BUTTON_SIZE, PIKA_VIEW_SIZE_SMALL, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_ENUM (object_class, PROP_DYNAMICS_VIEW_TYPE, "dynamics-view-type", NULL, NULL, PIKA_TYPE_VIEW_TYPE, PIKA_VIEW_TYPE_LIST, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_INT (object_class, PROP_DYNAMICS_VIEW_SIZE, "dynamics-view-size", NULL, NULL, PIKA_VIEW_SIZE_TINY, PIKA_VIEWABLE_MAX_BUTTON_SIZE, PIKA_VIEW_SIZE_SMALL, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_ENUM (object_class, PROP_PATTERN_VIEW_TYPE, "pattern-view-type", NULL, NULL, PIKA_TYPE_VIEW_TYPE, PIKA_VIEW_TYPE_GRID, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_INT (object_class, PROP_PATTERN_VIEW_SIZE, "pattern-view-size", NULL, NULL, PIKA_VIEW_SIZE_TINY, PIKA_VIEWABLE_MAX_BUTTON_SIZE, PIKA_VIEW_SIZE_SMALL, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_ENUM (object_class, PROP_GRADIENT_VIEW_TYPE, "gradient-view-type", NULL, NULL, PIKA_TYPE_VIEW_TYPE, PIKA_VIEW_TYPE_LIST, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_INT (object_class, PROP_GRADIENT_VIEW_SIZE, "gradient-view-size", NULL, NULL, PIKA_VIEW_SIZE_TINY, PIKA_VIEWABLE_MAX_BUTTON_SIZE, PIKA_VIEW_SIZE_LARGE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_SMOOTHING, "use-smoothing", _("Smooth stroke"), _("Paint smoother strokes"), FALSE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_INT (object_class, PROP_SMOOTHING_QUALITY, "smoothing-quality", _("Quality"), _("Depth of smoothing"), 1, 100, DEFAULT_SMOOTHING_QUALITY, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_SMOOTHING_FACTOR, "smoothing-factor", _("Weight"), _("Gravity of the pen"), /* Max velocity is set to 3; allowing for * smoothing factor to be less than velcoty * results in numeric instablility */ 3.0, 1000.0, DEFAULT_SMOOTHING_FACTOR, PIKA_PARAM_STATIC_STRINGS); } static void pika_paint_options_config_iface_init (PikaConfigInterface *config_iface) { parent_config_iface = g_type_interface_peek_parent (config_iface); if (! parent_config_iface) parent_config_iface = g_type_default_interface_peek (PIKA_TYPE_CONFIG); config_iface->duplicate = pika_paint_options_duplicate; config_iface->copy = pika_paint_options_copy; config_iface->reset = pika_paint_options_reset; } static void pika_paint_options_init (PikaPaintOptions *options) { options->application_mode_save = DEFAULT_APPLICATION_MODE; options->jitter_options = g_slice_new0 (PikaJitterOptions); options->fade_options = g_slice_new0 (PikaFadeOptions); options->gradient_options = g_slice_new0 (PikaGradientPaintOptions); options->smoothing_options = g_slice_new0 (PikaSmoothingOptions); } static void pika_paint_options_dispose (GObject *object) { PikaPaintOptions *options = PIKA_PAINT_OPTIONS (object); g_clear_object (&options->paint_info); if (options->brush) { g_signal_handlers_disconnect_by_func (options->brush, pika_paint_options_brush_notify, options); g_clear_weak_pointer (&options->brush); } G_OBJECT_CLASS (parent_class)->dispose (object); } static void pika_paint_options_finalize (GObject *object) { PikaPaintOptions *options = PIKA_PAINT_OPTIONS (object); g_slice_free (PikaJitterOptions, options->jitter_options); g_slice_free (PikaFadeOptions, options->fade_options); g_slice_free (PikaGradientPaintOptions, options->gradient_options); g_slice_free (PikaSmoothingOptions, options->smoothing_options); G_OBJECT_CLASS (parent_class)->finalize (object); } static void pika_paint_options_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { PikaPaintOptions *options = PIKA_PAINT_OPTIONS (object); PikaFadeOptions *fade_options = options->fade_options; PikaJitterOptions *jitter_options = options->jitter_options; PikaGradientPaintOptions *gradient_options = options->gradient_options; PikaSmoothingOptions *smoothing_options = options->smoothing_options; switch (property_id) { case PROP_PAINT_INFO: options->paint_info = g_value_dup_object (value); break; case PROP_USE_APPLICATOR: options->use_applicator = g_value_get_boolean (value); break; case PROP_BRUSH_SIZE: options->brush_size = g_value_get_double (value); break; case PROP_BRUSH_ASPECT_RATIO: options->brush_aspect_ratio = g_value_get_double (value); break; case PROP_BRUSH_ANGLE: options->brush_angle = - 1.0 * g_value_get_double (value) / 360.0; /* let's make the angle mathematically correct */ break; case PROP_BRUSH_SPACING: options->brush_spacing = g_value_get_double (value); break; case PROP_BRUSH_HARDNESS: options->brush_hardness = g_value_get_double (value); break; case PROP_BRUSH_FORCE: options->brush_force = g_value_get_double (value); break; case PROP_BRUSH_LINK_SIZE: options->brush_link_size = g_value_get_boolean (value); break; case PROP_BRUSH_LINK_ASPECT_RATIO: options->brush_link_aspect_ratio = g_value_get_boolean (value); break; case PROP_BRUSH_LINK_ANGLE: options->brush_link_angle = g_value_get_boolean (value); break; case PROP_BRUSH_LINK_SPACING: options->brush_link_spacing = g_value_get_boolean (value); break; case PROP_BRUSH_LINK_HARDNESS: options->brush_link_hardness = g_value_get_boolean (value); break; case PROP_BRUSH_LOCK_TO_VIEW: options->brush_lock_to_view = g_value_get_boolean (value); break; case PROP_APPLICATION_MODE: options->application_mode = g_value_get_enum (value); break; case PROP_HARD: options->hard = g_value_get_boolean (value); break; case PROP_USE_JITTER: jitter_options->use_jitter = g_value_get_boolean (value); break; case PROP_JITTER_AMOUNT: jitter_options->jitter_amount = g_value_get_double (value); break; case PROP_DYNAMICS_ENABLED: options->dynamics_enabled = g_value_get_boolean (value); break; case PROP_FADE_LENGTH: fade_options->fade_length = g_value_get_double (value); break; case PROP_FADE_REVERSE: fade_options->fade_reverse = g_value_get_boolean (value); break; case PROP_FADE_REPEAT: fade_options->fade_repeat = g_value_get_enum (value); break; case PROP_FADE_UNIT: fade_options->fade_unit = g_value_get_int (value); break; case PROP_GRADIENT_REVERSE: gradient_options->gradient_reverse = g_value_get_boolean (value); break; case PROP_GRADIENT_BLEND_COLOR_SPACE: gradient_options->gradient_blend_color_space = g_value_get_enum (value); break; case PROP_GRADIENT_REPEAT: gradient_options->gradient_repeat = g_value_get_enum (value); break; case PROP_BRUSH_VIEW_TYPE: options->brush_view_type = g_value_get_enum (value); break; case PROP_BRUSH_VIEW_SIZE: options->brush_view_size = g_value_get_int (value); break; case PROP_DYNAMICS_VIEW_TYPE: options->dynamics_view_type = g_value_get_enum (value); break; case PROP_DYNAMICS_VIEW_SIZE: options->dynamics_view_size = g_value_get_int (value); break; case PROP_PATTERN_VIEW_TYPE: options->pattern_view_type = g_value_get_enum (value); break; case PROP_PATTERN_VIEW_SIZE: options->pattern_view_size = g_value_get_int (value); break; case PROP_GRADIENT_VIEW_TYPE: options->gradient_view_type = g_value_get_enum (value); break; case PROP_GRADIENT_VIEW_SIZE: options->gradient_view_size = g_value_get_int (value); break; case PROP_USE_SMOOTHING: smoothing_options->use_smoothing = g_value_get_boolean (value); break; case PROP_SMOOTHING_QUALITY: smoothing_options->smoothing_quality = g_value_get_int (value); break; case PROP_SMOOTHING_FACTOR: smoothing_options->smoothing_factor = g_value_get_double (value); break; case PROP_EXPAND_USE: options->expand_use = g_value_get_boolean (value); break; case PROP_EXPAND_AMOUNT: options->expand_amount = g_value_get_double (value); break; case PROP_EXPAND_FILL_TYPE: options->expand_fill_type = g_value_get_enum (value); break; case PROP_EXPAND_MASK_FILL_TYPE: options->expand_mask_fill_type = g_value_get_enum (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_paint_options_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { PikaPaintOptions *options = PIKA_PAINT_OPTIONS (object); PikaFadeOptions *fade_options = options->fade_options; PikaJitterOptions *jitter_options = options->jitter_options; PikaGradientPaintOptions *gradient_options = options->gradient_options; PikaSmoothingOptions *smoothing_options = options->smoothing_options; switch (property_id) { case PROP_PAINT_INFO: g_value_set_object (value, options->paint_info); break; case PROP_USE_APPLICATOR: g_value_set_boolean (value, options->use_applicator); break; case PROP_BRUSH_SIZE: g_value_set_double (value, options->brush_size); break; case PROP_BRUSH_ASPECT_RATIO: g_value_set_double (value, options->brush_aspect_ratio); break; case PROP_BRUSH_ANGLE: g_value_set_double (value, - 1.0 * options->brush_angle * 360.0); /* mathematically correct -> intuitively correct */ break; case PROP_BRUSH_SPACING: g_value_set_double (value, options->brush_spacing); break; case PROP_BRUSH_HARDNESS: g_value_set_double (value, options->brush_hardness); break; case PROP_BRUSH_FORCE: g_value_set_double (value, options->brush_force); break; case PROP_BRUSH_LINK_SIZE: g_value_set_boolean (value, options->brush_link_size); break; case PROP_BRUSH_LINK_ASPECT_RATIO: g_value_set_boolean (value, options->brush_link_aspect_ratio); break; case PROP_BRUSH_LINK_ANGLE: g_value_set_boolean (value, options->brush_link_angle); break; case PROP_BRUSH_LINK_SPACING: g_value_set_boolean (value, options->brush_link_spacing); break; case PROP_BRUSH_LINK_HARDNESS: g_value_set_boolean (value, options->brush_link_hardness); break; case PROP_BRUSH_LOCK_TO_VIEW: g_value_set_boolean (value, options->brush_lock_to_view); break; case PROP_APPLICATION_MODE: g_value_set_enum (value, options->application_mode); break; case PROP_HARD: g_value_set_boolean (value, options->hard); break; case PROP_USE_JITTER: g_value_set_boolean (value, jitter_options->use_jitter); break; case PROP_JITTER_AMOUNT: g_value_set_double (value, jitter_options->jitter_amount); break; case PROP_DYNAMICS_ENABLED: g_value_set_boolean (value, options->dynamics_enabled); break; case PROP_FADE_LENGTH: g_value_set_double (value, fade_options->fade_length); break; case PROP_FADE_REVERSE: g_value_set_boolean (value, fade_options->fade_reverse); break; case PROP_FADE_REPEAT: g_value_set_enum (value, fade_options->fade_repeat); break; case PROP_FADE_UNIT: g_value_set_int (value, fade_options->fade_unit); break; case PROP_GRADIENT_REVERSE: g_value_set_boolean (value, gradient_options->gradient_reverse); break; case PROP_GRADIENT_BLEND_COLOR_SPACE: g_value_set_enum (value, gradient_options->gradient_blend_color_space); break; case PROP_GRADIENT_REPEAT: g_value_set_enum (value, gradient_options->gradient_repeat); break; case PROP_BRUSH_VIEW_TYPE: g_value_set_enum (value, options->brush_view_type); break; case PROP_BRUSH_VIEW_SIZE: g_value_set_int (value, options->brush_view_size); break; case PROP_DYNAMICS_VIEW_TYPE: g_value_set_enum (value, options->dynamics_view_type); break; case PROP_DYNAMICS_VIEW_SIZE: g_value_set_int (value, options->dynamics_view_size); break; case PROP_PATTERN_VIEW_TYPE: g_value_set_enum (value, options->pattern_view_type); break; case PROP_PATTERN_VIEW_SIZE: g_value_set_int (value, options->pattern_view_size); break; case PROP_GRADIENT_VIEW_TYPE: g_value_set_enum (value, options->gradient_view_type); break; case PROP_GRADIENT_VIEW_SIZE: g_value_set_int (value, options->gradient_view_size); break; case PROP_USE_SMOOTHING: g_value_set_boolean (value, smoothing_options->use_smoothing); break; case PROP_SMOOTHING_QUALITY: g_value_set_int (value, smoothing_options->smoothing_quality); break; case PROP_SMOOTHING_FACTOR: g_value_set_double (value, smoothing_options->smoothing_factor); break; case PROP_EXPAND_USE: g_value_set_boolean (value, options->expand_use); break; case PROP_EXPAND_AMOUNT: g_value_set_double (value, options->expand_amount); break; case PROP_EXPAND_FILL_TYPE: g_value_set_enum (value, options->expand_fill_type); break; case PROP_EXPAND_MASK_FILL_TYPE: g_value_set_enum (value, options->expand_mask_fill_type); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_paint_options_brush_changed (PikaContext *context, PikaBrush *brush) { PikaPaintOptions *options = PIKA_PAINT_OPTIONS (context); if (options->paint_info && g_type_is_a (options->paint_info->paint_type, PIKA_TYPE_BRUSH_CORE)) { if (options->brush) { g_signal_handlers_disconnect_by_func (options->brush, pika_paint_options_brush_notify, options); } g_set_weak_pointer (&options->brush, brush); if (options->brush) { g_signal_connect_object (options->brush, "notify", G_CALLBACK (pika_paint_options_brush_notify), options, 0); pika_paint_options_brush_notify (options->brush, NULL, options); } } } static void pika_paint_options_brush_notify (PikaBrush *brush, const GParamSpec *pspec, PikaPaintOptions *options) { if (pika_tool_options_get_gui_mode (PIKA_TOOL_OPTIONS (options))) { #define IS_PSPEC(p,n) (p == NULL || ! strcmp (n, p->name)) if (options->brush_link_size && IS_PSPEC (pspec, "radius")) pika_paint_options_set_default_brush_size (options, brush); if (options->brush_link_aspect_ratio && IS_PSPEC (pspec, "aspect-ratio")) pika_paint_options_set_default_brush_aspect_ratio (options, brush); if (options->brush_link_angle && IS_PSPEC (pspec, "angle")) pika_paint_options_set_default_brush_angle (options, brush); if (options->brush_link_spacing && IS_PSPEC (pspec, "spacing")) pika_paint_options_set_default_brush_spacing (options, brush); if (options->brush_link_hardness && IS_PSPEC (pspec, "hardness")) pika_paint_options_set_default_brush_hardness (options, brush); #undef IS_SPEC } } static PikaConfig * pika_paint_options_duplicate (PikaConfig *config) { return parent_config_iface->duplicate (config); } static gboolean pika_paint_options_copy (PikaConfig *src, PikaConfig *dest, GParamFlags flags) { return parent_config_iface->copy (src, dest, flags); } static void pika_paint_options_reset (PikaConfig *config) { PikaBrush *brush = pika_context_get_brush (PIKA_CONTEXT (config)); parent_config_iface->reset (config); if (brush) { pika_paint_options_set_default_brush_size (PIKA_PAINT_OPTIONS (config), brush); pika_paint_options_set_default_brush_hardness (PIKA_PAINT_OPTIONS (config), brush); pika_paint_options_set_default_brush_aspect_ratio (PIKA_PAINT_OPTIONS (config), brush); pika_paint_options_set_default_brush_angle (PIKA_PAINT_OPTIONS (config), brush); pika_paint_options_set_default_brush_spacing (PIKA_PAINT_OPTIONS (config), brush); } } PikaPaintOptions * pika_paint_options_new (PikaPaintInfo *paint_info) { PikaPaintOptions *options; g_return_val_if_fail (PIKA_IS_PAINT_INFO (paint_info), NULL); options = g_object_new (paint_info->paint_options_type, "pika", paint_info->pika, "name", pika_object_get_name (paint_info), "paint-info", paint_info, NULL); return options; } gdouble pika_paint_options_get_fade (PikaPaintOptions *paint_options, PikaImage *image, gdouble pixel_dist) { PikaFadeOptions *fade_options; gdouble z = -1.0; gdouble fade_out = 0.0; gdouble unit_factor; gdouble pos; g_return_val_if_fail (PIKA_IS_PAINT_OPTIONS (paint_options), DYNAMIC_MAX_VALUE); g_return_val_if_fail (PIKA_IS_IMAGE (image), DYNAMIC_MAX_VALUE); fade_options = paint_options->fade_options; switch (fade_options->fade_unit) { case PIKA_UNIT_PIXEL: fade_out = fade_options->fade_length; break; case PIKA_UNIT_PERCENT: fade_out = (MAX (pika_image_get_width (image), pika_image_get_height (image)) * fade_options->fade_length / 100); break; default: { gdouble xres; gdouble yres; pika_image_get_resolution (image, &xres, &yres); unit_factor = pika_unit_get_factor (fade_options->fade_unit); fade_out = (fade_options->fade_length * MAX (xres, yres) / unit_factor); } break; } /* factor in the fade out value */ if (fade_out > 0.0) { pos = pixel_dist / fade_out; } else pos = DYNAMIC_MAX_VALUE; /* for no repeat, set pos close to 1.0 after the first chunk */ if (fade_options->fade_repeat == PIKA_REPEAT_NONE && pos >= DYNAMIC_MAX_VALUE) pos = DYNAMIC_MAX_VALUE - 0.0000001; if (((gint) pos & 1) && fade_options->fade_repeat != PIKA_REPEAT_SAWTOOTH) pos = DYNAMIC_MAX_VALUE - (pos - (gint) pos); else pos = pos - (gint) pos; z = pos; if (fade_options->fade_reverse) z = 1.0 - z; return z; /* ln (1/255) */ } gdouble pika_paint_options_get_jitter (PikaPaintOptions *paint_options, PikaImage *image) { PikaJitterOptions *jitter_options; g_return_val_if_fail (PIKA_IS_PAINT_OPTIONS (paint_options), 0.0); jitter_options = paint_options->jitter_options; if (jitter_options->use_jitter) { return jitter_options->jitter_amount; } return 0.0; } gboolean pika_paint_options_get_gradient_color (PikaPaintOptions *paint_options, PikaImage *image, gdouble grad_point, gdouble pixel_dist, PikaRGB *color) { PikaDynamics *dynamics; g_return_val_if_fail (PIKA_IS_PAINT_OPTIONS (paint_options), FALSE); g_return_val_if_fail (PIKA_IS_IMAGE (image), FALSE); g_return_val_if_fail (color != NULL, FALSE); dynamics = pika_context_get_dynamics (PIKA_CONTEXT (paint_options)); if (pika_dynamics_is_output_enabled (dynamics, PIKA_DYNAMICS_OUTPUT_COLOR)) { PikaGradientPaintOptions *gradient_options = paint_options->gradient_options; PikaGradient *gradient; gradient = pika_context_get_gradient (PIKA_CONTEXT (paint_options)); pika_gradient_get_color_at (gradient, PIKA_CONTEXT (paint_options), NULL, grad_point, gradient_options->gradient_reverse, gradient_options->gradient_blend_color_space, color); return TRUE; } return FALSE; } PikaBrushApplicationMode pika_paint_options_get_brush_mode (PikaPaintOptions *paint_options) { PikaDynamics *dynamics; gboolean dynamic_force = FALSE; g_return_val_if_fail (PIKA_IS_PAINT_OPTIONS (paint_options), PIKA_BRUSH_SOFT); if (paint_options->hard) return PIKA_BRUSH_HARD; dynamics = pika_context_get_dynamics (PIKA_CONTEXT (paint_options)); dynamic_force = pika_dynamics_is_output_enabled (dynamics, PIKA_DYNAMICS_OUTPUT_FORCE); if (dynamic_force || (paint_options->brush_force != 0.5)) return PIKA_BRUSH_PRESSURE; return PIKA_BRUSH_SOFT; } void pika_paint_options_set_default_brush_size (PikaPaintOptions *paint_options, PikaBrush *brush) { g_return_if_fail (PIKA_IS_PAINT_OPTIONS (paint_options)); g_return_if_fail (brush == NULL || PIKA_IS_BRUSH (brush)); if (! brush) brush = pika_context_get_brush (PIKA_CONTEXT (paint_options)); if (brush) { gint height; gint width; pika_brush_transform_size (brush, 1.0, 0.0, 0.0, FALSE, &width, &height); g_object_set (paint_options, "brush-size", (gdouble) MAX (height, width), NULL); } } void pika_paint_options_set_default_brush_angle (PikaPaintOptions *paint_options, PikaBrush *brush) { g_return_if_fail (PIKA_IS_PAINT_OPTIONS (paint_options)); g_return_if_fail (brush == NULL || PIKA_IS_BRUSH (brush)); if (! brush) brush = pika_context_get_brush (PIKA_CONTEXT (paint_options)); if (PIKA_IS_BRUSH_GENERATED (brush)) { PikaBrushGenerated *generated_brush = PIKA_BRUSH_GENERATED (brush); g_object_set (paint_options, "brush-angle", (gdouble) pika_brush_generated_get_angle (generated_brush), NULL); } else { g_object_set (paint_options, "brush-angle", DEFAULT_BRUSH_ANGLE, NULL); } } void pika_paint_options_set_default_brush_aspect_ratio (PikaPaintOptions *paint_options, PikaBrush *brush) { g_return_if_fail (PIKA_IS_PAINT_OPTIONS (paint_options)); g_return_if_fail (brush == NULL || PIKA_IS_BRUSH (brush)); if (! brush) brush = pika_context_get_brush (PIKA_CONTEXT (paint_options)); if (PIKA_IS_BRUSH_GENERATED (brush)) { PikaBrushGenerated *generated_brush = PIKA_BRUSH_GENERATED (brush); gdouble ratio; ratio = pika_brush_generated_get_aspect_ratio (generated_brush); ratio = (ratio - 1.0) * 20.0 / 19.0; g_object_set (paint_options, "brush-aspect-ratio", ratio, NULL); } else { g_object_set (paint_options, "brush-aspect-ratio", DEFAULT_BRUSH_ASPECT_RATIO, NULL); } } void pika_paint_options_set_default_brush_spacing (PikaPaintOptions *paint_options, PikaBrush *brush) { g_return_if_fail (PIKA_IS_PAINT_OPTIONS (paint_options)); g_return_if_fail (brush == NULL || PIKA_IS_BRUSH (brush)); if (! brush) brush = pika_context_get_brush (PIKA_CONTEXT (paint_options)); if (brush) { g_object_set (paint_options, "brush-spacing", (gdouble) pika_brush_get_spacing (brush) / 100.0, NULL); } } void pika_paint_options_set_default_brush_hardness (PikaPaintOptions *paint_options, PikaBrush *brush) { g_return_if_fail (PIKA_IS_PAINT_OPTIONS (paint_options)); g_return_if_fail (brush == NULL || PIKA_IS_BRUSH (brush)); if (! brush) brush = pika_context_get_brush (PIKA_CONTEXT (paint_options)); if (PIKA_IS_BRUSH_GENERATED (brush)) { PikaBrushGenerated *generated_brush = PIKA_BRUSH_GENERATED (brush); g_object_set (paint_options, "brush-hardness", (gdouble) pika_brush_generated_get_hardness (generated_brush), NULL); } else { g_object_set (paint_options, "brush-hardness", DEFAULT_BRUSH_HARDNESS, NULL); } } gboolean pika_paint_options_are_dynamics_enabled (PikaPaintOptions *paint_options) { return paint_options->dynamics_enabled; } void pika_paint_options_enable_dynamics (PikaPaintOptions *paint_options, gboolean enable) { if (paint_options->dynamics_enabled != enable) { g_object_set (paint_options, "dynamics-enabled", enable, NULL); } } static const gchar *brush_props[] = { "brush-size", "brush-angle", "brush-aspect-ratio", "brush-spacing", "brush-hardness", "brush-force", "brush-link-size", "brush-link-angle", "brush-link-aspect-ratio", "brush-link-spacing", "brush-link-hardness", "brush-lock-to-view" }; static const gchar *dynamics_props[] = { "dynamics-enabled", "fade-reverse", "fade-length", "fade-unit", "fade-repeat" }; static const gchar *gradient_props[] = { "gradient-reverse", "gradient-blend-color-space", "gradient-repeat" }; static const gchar *expand_props[] = { "expand-use", "expand-amount", "expand-fill-type", "expand-mask-fill-type", }; static const gint max_n_props = (G_N_ELEMENTS (brush_props) + G_N_ELEMENTS (dynamics_props) + G_N_ELEMENTS (gradient_props) + G_N_ELEMENTS (expand_props)); gboolean pika_paint_options_is_prop (const gchar *prop_name, PikaContextPropMask prop_mask) { gint i; g_return_val_if_fail (prop_name != NULL, FALSE); if (prop_mask & PIKA_CONTEXT_PROP_MASK_BRUSH) { for (i = 0; i < G_N_ELEMENTS (brush_props); i++) if (! strcmp (prop_name, brush_props[i])) return TRUE; } if (prop_mask & PIKA_CONTEXT_PROP_MASK_DYNAMICS) { for (i = 0; i < G_N_ELEMENTS (dynamics_props); i++) if (! strcmp (prop_name, dynamics_props[i])) return TRUE; } if (prop_mask & PIKA_CONTEXT_PROP_MASK_GRADIENT) { for (i = 0; i < G_N_ELEMENTS (gradient_props); i++) if (! strcmp (prop_name, gradient_props[i])) return TRUE; } if (prop_mask & PIKA_CONTEXT_PROP_MASK_EXPAND) { for (i = 0; i < G_N_ELEMENTS (expand_props); i++) if (! strcmp (prop_name, expand_props[i])) return TRUE; } return FALSE; } void pika_paint_options_copy_props (PikaPaintOptions *src, PikaPaintOptions *dest, PikaContextPropMask prop_mask) { const gchar *names[max_n_props]; GValue values[max_n_props]; gint n_props = 0; gint i; g_return_if_fail (PIKA_IS_PAINT_OPTIONS (src)); g_return_if_fail (PIKA_IS_PAINT_OPTIONS (dest)); if (prop_mask & PIKA_CONTEXT_PROP_MASK_BRUSH) { for (i = 0; i < G_N_ELEMENTS (brush_props); i++) names[n_props++] = brush_props[i]; } if (prop_mask & PIKA_CONTEXT_PROP_MASK_DYNAMICS) { for (i = 0; i < G_N_ELEMENTS (dynamics_props); i++) names[n_props++] = dynamics_props[i]; } if (prop_mask & PIKA_CONTEXT_PROP_MASK_GRADIENT) { for (i = 0; i < G_N_ELEMENTS (gradient_props); i++) names[n_props++] = gradient_props[i]; } if (prop_mask & PIKA_CONTEXT_PROP_MASK_EXPAND) { for (i = 0; i < G_N_ELEMENTS (expand_props); i++) names[n_props++] = expand_props[i]; } if (n_props > 0) { g_object_getv (G_OBJECT (src), n_props, names, values); g_object_setv (G_OBJECT (dest), n_props, names, values); while (n_props--) g_value_unset (&values[n_props]); } }