PIKApp/app/paint/pikapaintoptions.c

1388 lines
47 KiB
C
Raw Permalink Normal View History

2023-09-26 00:35:21 +02:00
/* 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 <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#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
2023-12-02 20:03:24 +01:00
#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
2023-09-26 00:35:21 +02:00
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,
2023-12-02 20:03:24 +01:00
PROP_SMOOTHING_FACTOR,
PROP_EXPAND_USE,
PROP_EXPAND_AMOUNT,
PROP_EXPAND_FILL_TYPE,
PROP_EXPAND_MASK_FILL_TYPE
2023-09-26 00:35:21 +02:00
};
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);
2023-12-02 20:03:24 +01:00
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);
2023-09-26 00:35:21 +02:00
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;
2023-12-02 20:03:24 +01:00
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;
2023-09-26 00:35:21 +02:00
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;
2023-12-02 20:03:24 +01:00
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;
2023-09-26 00:35:21 +02:00
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"
};
2023-12-02 20:03:24 +01:00
static const gchar *expand_props[] =
{
"expand-use",
"expand-amount",
"expand-fill-type",
"expand-mask-fill-type",
};
2023-09-26 00:35:21 +02:00
static const gint max_n_props = (G_N_ELEMENTS (brush_props) +
G_N_ELEMENTS (dynamics_props) +
2023-12-02 20:03:24 +01:00
G_N_ELEMENTS (gradient_props) +
G_N_ELEMENTS (expand_props));
2023-09-26 00:35:21 +02:00
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;
}
2023-12-02 20:03:24 +01:00
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;
}
2023-09-26 00:35:21 +02:00
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];
}
2023-12-02 20:03:24 +01:00
if (prop_mask & PIKA_CONTEXT_PROP_MASK_EXPAND)
{
for (i = 0; i < G_N_ELEMENTS (expand_props); i++)
names[n_props++] = expand_props[i];
}
2023-09-26 00:35:21 +02:00
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]);
}
}