Gimp/app/paint/gimppaintoptions.c
Jehan d493f0537f Issue #8900 and #9923: reimplementing GimpUnit as a proper class.
This fixes all our GObject Introspection issues with GimpUnit which was
both an enum and an int-derived type of user-defined units *completing*
the enum values. GIR clearly didn't like this!

Now GimpUnit is a proper class and units are unique objects, allowing to
compare them with an identity test (i.e. `unit == gimp_unit_pixel ()`
tells us if unit is the pixel unit or not), which makes it easy to use,
just like with int, yet adding also methods, making for nicer
introspected API.

As an aside, this also fixes #10738, by having all the built-in units
retrievable even if libgimpbase had not been properly initialized with
gimp_base_init().
I haven't checked in details how GIR works to introspect, but it looks
like it loads the library to inspect and runs functions, hence
triggering some CRITICALS because virtual methods (supposed to be
initialized with gimp_base_init() run by libgimp) are not set. This new
code won't trigger any critical because the vtable method are now not
necessary, at least for all built-in units.

Note that GimpUnit is still in libgimpbase. It could have been moved to
libgimp in order to avoid any virtual method table (since we need to
keep core and libgimp side's units in sync, PDB is required), but too
many libgimpwidgets widgets were already using GimpUnit. And technically
most of GimpUnit logic doesn't require PDB (only the creation/sync
part). This is one of the reasons why user-created GimpUnit list is
handled and stored differently from other types of objects.

Globally this simplifies the code a lot too and we don't need separate
implementations of various utils for core and libgimp, which means less
prone to errors.
2024-08-02 10:46:38 +02:00

1379 lines
47 KiB
C

/* GIMP - The GNU Image Manipulation Program
* 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 "libgimpbase/gimpbase.h"
#include "libgimpmath/gimpmath.h"
#include "libgimpconfig/gimpconfig.h"
#include "paint-types.h"
#include "core/gimp.h"
#include "core/gimpbrush-header.h"
#include "core/gimpbrushgenerated.h"
#include "core/gimpimage.h"
#include "core/gimpdynamics.h"
#include "core/gimpdynamicsoutput.h"
#include "core/gimpgradient.h"
#include "core/gimppaintinfo.h"
#include "gimpbrushcore.h"
#include "gimppaintoptions.h"
#include "gimp-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 GIMP_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 GIMP_REPEAT_NONE
#define DEFAULT_FADE_UNIT gimp_unit_pixel ()
#define DEFAULT_GRADIENT_REVERSE FALSE
#define DEFAULT_GRADIENT_BLEND_SPACE GIMP_GRADIENT_BLEND_RGB_PERCEPTUAL
#define DEFAULT_GRADIENT_REPEAT GIMP_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 GIMP_FILL_TRANSPARENT
#define DEFAULT_EXPAND_MASK_FILL_TYPE GIMP_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 gimp_paint_options_config_iface_init (GimpConfigInterface *config_iface);
static void gimp_paint_options_dispose (GObject *object);
static void gimp_paint_options_finalize (GObject *object);
static void gimp_paint_options_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_paint_options_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_paint_options_brush_changed (GimpContext *context,
GimpBrush *brush);
static void gimp_paint_options_brush_notify (GimpBrush *brush,
const GParamSpec *pspec,
GimpPaintOptions *options);
static GimpConfig * gimp_paint_options_duplicate (GimpConfig *config);
static gboolean gimp_paint_options_copy (GimpConfig *src,
GimpConfig *dest,
GParamFlags flags);
static void gimp_paint_options_reset (GimpConfig *config);
G_DEFINE_TYPE_WITH_CODE (GimpPaintOptions, gimp_paint_options,
GIMP_TYPE_TOOL_OPTIONS,
G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,
gimp_paint_options_config_iface_init))
#define parent_class gimp_paint_options_parent_class
static GimpConfigInterface *parent_config_iface = NULL;
static void
gimp_paint_options_class_init (GimpPaintOptionsClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GimpContextClass *context_class = GIMP_CONTEXT_CLASS (klass);
object_class->dispose = gimp_paint_options_dispose;
object_class->finalize = gimp_paint_options_finalize;
object_class->set_property = gimp_paint_options_set_property;
object_class->get_property = gimp_paint_options_get_property;
context_class->brush_changed = gimp_paint_options_brush_changed;
g_object_class_install_property (object_class, PROP_PAINT_INFO,
g_param_spec_object ("paint-info",
NULL, NULL,
GIMP_TYPE_PAINT_INFO,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, PROP_USE_APPLICATOR,
g_param_spec_boolean ("use-applicator",
"Use GimpApplicator",
NULL,
FALSE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_BRUSH_SIZE,
"brush-size",
_("Size"),
_("Brush Size"),
1.0, GIMP_BRUSH_MAX_SIZE, DEFAULT_BRUSH_SIZE,
GIMP_PARAM_STATIC_STRINGS);
GIMP_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,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_BRUSH_ANGLE,
"brush-angle",
_("Angle"),
_("Brush Angle"),
-180.0, 180.0, DEFAULT_BRUSH_ANGLE,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_BRUSH_SPACING,
"brush-spacing",
_("Spacing"),
_("Brush Spacing"),
0.01, 50.0, DEFAULT_BRUSH_SPACING,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_BRUSH_HARDNESS,
"brush-hardness",
_("Hardness"),
_("Brush Hardness"),
0.0, 1.0, DEFAULT_BRUSH_HARDNESS,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_BRUSH_FORCE,
"brush-force",
_("Force"),
_("Brush Force"),
0.0, 1.0, DEFAULT_BRUSH_FORCE,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_BRUSH_LINK_SIZE,
"brush-link-size",
_("Link Size"),
_("Link brush size to brush native"),
DEFAULT_BRUSH_LINK_SIZE,
GIMP_PARAM_STATIC_STRINGS);
GIMP_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,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_BRUSH_LINK_ANGLE,
"brush-link-angle",
_("Link Angle"),
_("Link brush angle to brush native"),
DEFAULT_BRUSH_LINK_ANGLE,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_BRUSH_LINK_SPACING,
"brush-link-spacing",
_("Link Spacing"),
_("Link brush spacing to brush native"),
DEFAULT_BRUSH_LINK_SPACING,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_BRUSH_LINK_HARDNESS,
"brush-link-hardness",
_("Link Hardness"),
_("Link brush hardness to brush native"),
DEFAULT_BRUSH_LINK_HARDNESS,
GIMP_PARAM_STATIC_STRINGS);
GIMP_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,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_ENUM (object_class, PROP_APPLICATION_MODE,
"application-mode",
_("Incremental"),
_("Every stamp has its own opacity"),
GIMP_TYPE_PAINT_APPLICATION_MODE,
DEFAULT_APPLICATION_MODE,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_HARD,
"hard",
_("Hard edge"),
_("Ignore fuzziness of the current brush"),
DEFAULT_HARD,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_JITTER,
"use-jitter",
_("Apply Jitter"),
_("Scatter brush as you paint"),
DEFAULT_USE_JITTER,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_EXPAND_USE,
"expand-use",
_("Expand Layers"),
_("Expand active layer as you paint"),
DEFAULT_EXPAND_USE,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_EXPAND_AMOUNT,
"expand-amount",
_("Amount"),
_("Amount of expansion"),
1.0, 1000.0, DEFAULT_EXPAND_AMOUNT,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_ENUM (object_class, PROP_EXPAND_FILL_TYPE,
"expand-fill-type",
_("Fill With"),
_("Fill layer with"),
GIMP_TYPE_FILL_TYPE,
DEFAULT_EXPAND_FILL_TYPE,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_ENUM (object_class, PROP_EXPAND_MASK_FILL_TYPE,
"expand-mask-fill-type",
_("Fill Mask With"),
_("Fill layer mask with"),
GIMP_TYPE_ADD_MASK_TYPE,
DEFAULT_EXPAND_MASK_FILL_TYPE,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_DYNAMICS_ENABLED,
"dynamics-enabled",
_("Enable dynamics"),
_("Apply dynamics curves to paint settings"),
DEFAULT_DYNAMICS_ENABLED,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_JITTER_AMOUNT,
"jitter-amount",
_("Amount"),
_("Distance of scattering"),
0.0, 50.0, DEFAULT_JITTER_AMOUNT,
GIMP_PARAM_STATIC_STRINGS);
GIMP_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,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_UNIT (object_class, PROP_FADE_UNIT,
"fade-unit",
NULL, NULL,
TRUE, TRUE, DEFAULT_FADE_UNIT,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_FADE_REVERSE,
"fade-reverse",
_("Reverse"),
_("Reverse direction of fading"),
DEFAULT_FADE_REVERSE,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_ENUM (object_class, PROP_FADE_REPEAT,
"fade-repeat",
_("Repeat"),
_("How fade is repeated as you paint"),
GIMP_TYPE_REPEAT_MODE,
DEFAULT_FADE_REPEAT,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_GRADIENT_REVERSE,
"gradient-reverse",
NULL, NULL,
DEFAULT_GRADIENT_REVERSE,
GIMP_PARAM_STATIC_STRINGS);
GIMP_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"),
GIMP_TYPE_GRADIENT_BLEND_COLOR_SPACE,
DEFAULT_GRADIENT_BLEND_SPACE,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_ENUM (object_class, PROP_GRADIENT_REPEAT,
"gradient-repeat",
_("Repeat"),
NULL,
GIMP_TYPE_REPEAT_MODE,
DEFAULT_GRADIENT_REPEAT,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_ENUM (object_class, PROP_BRUSH_VIEW_TYPE,
"brush-view-type",
NULL, NULL,
GIMP_TYPE_VIEW_TYPE,
GIMP_VIEW_TYPE_GRID,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_INT (object_class, PROP_BRUSH_VIEW_SIZE,
"brush-view-size",
NULL, NULL,
GIMP_VIEW_SIZE_TINY,
GIMP_VIEWABLE_MAX_BUTTON_SIZE,
GIMP_VIEW_SIZE_SMALL,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_ENUM (object_class, PROP_DYNAMICS_VIEW_TYPE,
"dynamics-view-type",
NULL, NULL,
GIMP_TYPE_VIEW_TYPE,
GIMP_VIEW_TYPE_LIST,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_INT (object_class, PROP_DYNAMICS_VIEW_SIZE,
"dynamics-view-size",
NULL, NULL,
GIMP_VIEW_SIZE_TINY,
GIMP_VIEWABLE_MAX_BUTTON_SIZE,
GIMP_VIEW_SIZE_SMALL,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_ENUM (object_class, PROP_PATTERN_VIEW_TYPE,
"pattern-view-type",
NULL, NULL,
GIMP_TYPE_VIEW_TYPE,
GIMP_VIEW_TYPE_GRID,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_INT (object_class, PROP_PATTERN_VIEW_SIZE,
"pattern-view-size",
NULL, NULL,
GIMP_VIEW_SIZE_TINY,
GIMP_VIEWABLE_MAX_BUTTON_SIZE,
GIMP_VIEW_SIZE_SMALL,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_ENUM (object_class, PROP_GRADIENT_VIEW_TYPE,
"gradient-view-type",
NULL, NULL,
GIMP_TYPE_VIEW_TYPE,
GIMP_VIEW_TYPE_LIST,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_INT (object_class, PROP_GRADIENT_VIEW_SIZE,
"gradient-view-size",
NULL, NULL,
GIMP_VIEW_SIZE_TINY,
GIMP_VIEWABLE_MAX_BUTTON_SIZE,
GIMP_VIEW_SIZE_LARGE,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_SMOOTHING,
"use-smoothing",
_("Smooth stroke"),
_("Paint smoother strokes"),
FALSE,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_INT (object_class, PROP_SMOOTHING_QUALITY,
"smoothing-quality",
_("Quality"),
_("Depth of smoothing"),
1, 100, DEFAULT_SMOOTHING_QUALITY,
GIMP_PARAM_STATIC_STRINGS);
GIMP_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,
GIMP_PARAM_STATIC_STRINGS);
}
static void
gimp_paint_options_config_iface_init (GimpConfigInterface *config_iface)
{
parent_config_iface = g_type_interface_peek_parent (config_iface);
if (! parent_config_iface)
parent_config_iface = g_type_default_interface_peek (GIMP_TYPE_CONFIG);
config_iface->duplicate = gimp_paint_options_duplicate;
config_iface->copy = gimp_paint_options_copy;
config_iface->reset = gimp_paint_options_reset;
}
static void
gimp_paint_options_init (GimpPaintOptions *options)
{
options->application_mode_save = DEFAULT_APPLICATION_MODE;
options->jitter_options = g_slice_new0 (GimpJitterOptions);
options->fade_options = g_slice_new0 (GimpFadeOptions);
options->gradient_options = g_slice_new0 (GimpGradientPaintOptions);
options->smoothing_options = g_slice_new0 (GimpSmoothingOptions);
}
static void
gimp_paint_options_dispose (GObject *object)
{
GimpPaintOptions *options = GIMP_PAINT_OPTIONS (object);
g_clear_object (&options->paint_info);
if (options->brush)
{
g_signal_handlers_disconnect_by_func (options->brush,
gimp_paint_options_brush_notify,
options);
g_clear_weak_pointer (&options->brush);
}
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gimp_paint_options_finalize (GObject *object)
{
GimpPaintOptions *options = GIMP_PAINT_OPTIONS (object);
g_slice_free (GimpJitterOptions, options->jitter_options);
g_slice_free (GimpFadeOptions, options->fade_options);
g_slice_free (GimpGradientPaintOptions, options->gradient_options);
g_slice_free (GimpSmoothingOptions, options->smoothing_options);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_paint_options_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpPaintOptions *options = GIMP_PAINT_OPTIONS (object);
GimpFadeOptions *fade_options = options->fade_options;
GimpJitterOptions *jitter_options = options->jitter_options;
GimpGradientPaintOptions *gradient_options = options->gradient_options;
GimpSmoothingOptions *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_object (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
gimp_paint_options_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpPaintOptions *options = GIMP_PAINT_OPTIONS (object);
GimpFadeOptions *fade_options = options->fade_options;
GimpJitterOptions *jitter_options = options->jitter_options;
GimpGradientPaintOptions *gradient_options = options->gradient_options;
GimpSmoothingOptions *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_object (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
gimp_paint_options_brush_changed (GimpContext *context,
GimpBrush *brush)
{
GimpPaintOptions *options = GIMP_PAINT_OPTIONS (context);
if (options->paint_info &&
g_type_is_a (options->paint_info->paint_type,
GIMP_TYPE_BRUSH_CORE))
{
if (options->brush)
{
g_signal_handlers_disconnect_by_func (options->brush,
gimp_paint_options_brush_notify,
options);
}
g_set_weak_pointer (&options->brush, brush);
if (options->brush)
{
g_signal_connect_object (options->brush, "notify",
G_CALLBACK (gimp_paint_options_brush_notify),
options, 0);
gimp_paint_options_brush_notify (options->brush, NULL, options);
}
}
}
static void
gimp_paint_options_brush_notify (GimpBrush *brush,
const GParamSpec *pspec,
GimpPaintOptions *options)
{
if (gimp_tool_options_get_gui_mode (GIMP_TOOL_OPTIONS (options)))
{
#define IS_PSPEC(p,n) (p == NULL || ! strcmp (n, p->name))
if (options->brush_link_size && IS_PSPEC (pspec, "radius"))
gimp_paint_options_set_default_brush_size (options, brush);
if (options->brush_link_aspect_ratio && IS_PSPEC (pspec, "aspect-ratio"))
gimp_paint_options_set_default_brush_aspect_ratio (options, brush);
if (options->brush_link_angle && IS_PSPEC (pspec, "angle"))
gimp_paint_options_set_default_brush_angle (options, brush);
if (options->brush_link_spacing && IS_PSPEC (pspec, "spacing"))
gimp_paint_options_set_default_brush_spacing (options, brush);
if (options->brush_link_hardness && IS_PSPEC (pspec, "hardness"))
gimp_paint_options_set_default_brush_hardness (options, brush);
#undef IS_SPEC
}
}
static GimpConfig *
gimp_paint_options_duplicate (GimpConfig *config)
{
return parent_config_iface->duplicate (config);
}
static gboolean
gimp_paint_options_copy (GimpConfig *src,
GimpConfig *dest,
GParamFlags flags)
{
return parent_config_iface->copy (src, dest, flags);
}
static void
gimp_paint_options_reset (GimpConfig *config)
{
GimpBrush *brush = gimp_context_get_brush (GIMP_CONTEXT (config));
parent_config_iface->reset (config);
if (brush)
{
gimp_paint_options_set_default_brush_size (GIMP_PAINT_OPTIONS (config),
brush);
gimp_paint_options_set_default_brush_hardness (GIMP_PAINT_OPTIONS (config),
brush);
gimp_paint_options_set_default_brush_aspect_ratio (GIMP_PAINT_OPTIONS (config),
brush);
gimp_paint_options_set_default_brush_angle (GIMP_PAINT_OPTIONS (config),
brush);
gimp_paint_options_set_default_brush_spacing (GIMP_PAINT_OPTIONS (config),
brush);
}
}
GimpPaintOptions *
gimp_paint_options_new (GimpPaintInfo *paint_info)
{
GimpPaintOptions *options;
g_return_val_if_fail (GIMP_IS_PAINT_INFO (paint_info), NULL);
options = g_object_new (paint_info->paint_options_type,
"gimp", paint_info->gimp,
"name", gimp_object_get_name (paint_info),
"paint-info", paint_info,
NULL);
return options;
}
gdouble
gimp_paint_options_get_fade (GimpPaintOptions *paint_options,
GimpImage *image,
gdouble pixel_dist)
{
GimpFadeOptions *fade_options;
gdouble z = -1.0;
gdouble fade_out = 0.0;
gdouble unit_factor;
gdouble pos;
g_return_val_if_fail (GIMP_IS_PAINT_OPTIONS (paint_options),
DYNAMIC_MAX_VALUE);
g_return_val_if_fail (GIMP_IS_IMAGE (image), DYNAMIC_MAX_VALUE);
fade_options = paint_options->fade_options;
if (fade_options->fade_unit == gimp_unit_pixel ())
{
fade_out = fade_options->fade_length;
}
else if (fade_options->fade_unit == gimp_unit_percent ())
{
fade_out = (MAX (gimp_image_get_width (image),
gimp_image_get_height (image)) *
fade_options->fade_length / 100);
}
else
{
gdouble xres;
gdouble yres;
gimp_image_get_resolution (image, &xres, &yres);
unit_factor = gimp_unit_get_factor (fade_options->fade_unit);
fade_out = (fade_options->fade_length *
MAX (xres, yres) / unit_factor);
}
/* 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 == GIMP_REPEAT_NONE && pos >= DYNAMIC_MAX_VALUE)
pos = DYNAMIC_MAX_VALUE - 0.0000001;
if (((gint) pos & 1) &&
fade_options->fade_repeat != GIMP_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
gimp_paint_options_get_jitter (GimpPaintOptions *paint_options,
GimpImage *image)
{
GimpJitterOptions *jitter_options;
g_return_val_if_fail (GIMP_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
gimp_paint_options_get_gradient_color (GimpPaintOptions *paint_options,
GimpImage *image,
gdouble grad_point,
gdouble pixel_dist,
GeglColor **color)
{
GimpDynamics *dynamics;
g_return_val_if_fail (GIMP_IS_PAINT_OPTIONS (paint_options), FALSE);
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
g_return_val_if_fail (color != NULL && *color == NULL, FALSE);
dynamics = gimp_context_get_dynamics (GIMP_CONTEXT (paint_options));
if (gimp_dynamics_is_output_enabled (dynamics, GIMP_DYNAMICS_OUTPUT_COLOR))
{
GimpGradientPaintOptions *gradient_options = paint_options->gradient_options;
GimpGradient *gradient;
gradient = gimp_context_get_gradient (GIMP_CONTEXT (paint_options));
gimp_gradient_get_color_at (gradient, GIMP_CONTEXT (paint_options),
NULL, grad_point,
gradient_options->gradient_reverse,
gradient_options->gradient_blend_color_space,
color);
return TRUE;
}
return FALSE;
}
GimpBrushApplicationMode
gimp_paint_options_get_brush_mode (GimpPaintOptions *paint_options)
{
GimpDynamics *dynamics;
gboolean dynamic_force = FALSE;
g_return_val_if_fail (GIMP_IS_PAINT_OPTIONS (paint_options), GIMP_BRUSH_SOFT);
if (paint_options->hard)
return GIMP_BRUSH_HARD;
dynamics = gimp_context_get_dynamics (GIMP_CONTEXT (paint_options));
dynamic_force = gimp_dynamics_is_output_enabled (dynamics,
GIMP_DYNAMICS_OUTPUT_FORCE);
if (dynamic_force || (paint_options->brush_force != 0.5))
return GIMP_BRUSH_PRESSURE;
return GIMP_BRUSH_SOFT;
}
void
gimp_paint_options_set_default_brush_size (GimpPaintOptions *paint_options,
GimpBrush *brush)
{
g_return_if_fail (GIMP_IS_PAINT_OPTIONS (paint_options));
g_return_if_fail (brush == NULL || GIMP_IS_BRUSH (brush));
if (! brush)
brush = gimp_context_get_brush (GIMP_CONTEXT (paint_options));
if (brush)
{
gint height;
gint width;
gimp_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
gimp_paint_options_set_default_brush_angle (GimpPaintOptions *paint_options,
GimpBrush *brush)
{
g_return_if_fail (GIMP_IS_PAINT_OPTIONS (paint_options));
g_return_if_fail (brush == NULL || GIMP_IS_BRUSH (brush));
if (! brush)
brush = gimp_context_get_brush (GIMP_CONTEXT (paint_options));
if (GIMP_IS_BRUSH_GENERATED (brush))
{
GimpBrushGenerated *generated_brush = GIMP_BRUSH_GENERATED (brush);
g_object_set (paint_options,
"brush-angle", (gdouble) gimp_brush_generated_get_angle (generated_brush),
NULL);
}
else
{
g_object_set (paint_options,
"brush-angle", DEFAULT_BRUSH_ANGLE,
NULL);
}
}
void
gimp_paint_options_set_default_brush_aspect_ratio (GimpPaintOptions *paint_options,
GimpBrush *brush)
{
g_return_if_fail (GIMP_IS_PAINT_OPTIONS (paint_options));
g_return_if_fail (brush == NULL || GIMP_IS_BRUSH (brush));
if (! brush)
brush = gimp_context_get_brush (GIMP_CONTEXT (paint_options));
if (GIMP_IS_BRUSH_GENERATED (brush))
{
GimpBrushGenerated *generated_brush = GIMP_BRUSH_GENERATED (brush);
gdouble ratio;
ratio = gimp_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
gimp_paint_options_set_default_brush_spacing (GimpPaintOptions *paint_options,
GimpBrush *brush)
{
g_return_if_fail (GIMP_IS_PAINT_OPTIONS (paint_options));
g_return_if_fail (brush == NULL || GIMP_IS_BRUSH (brush));
if (! brush)
brush = gimp_context_get_brush (GIMP_CONTEXT (paint_options));
if (brush)
{
g_object_set (paint_options,
"brush-spacing", (gdouble) gimp_brush_get_spacing (brush) / 100.0,
NULL);
}
}
void
gimp_paint_options_set_default_brush_hardness (GimpPaintOptions *paint_options,
GimpBrush *brush)
{
g_return_if_fail (GIMP_IS_PAINT_OPTIONS (paint_options));
g_return_if_fail (brush == NULL || GIMP_IS_BRUSH (brush));
if (! brush)
brush = gimp_context_get_brush (GIMP_CONTEXT (paint_options));
if (GIMP_IS_BRUSH_GENERATED (brush))
{
GimpBrushGenerated *generated_brush = GIMP_BRUSH_GENERATED (brush);
g_object_set (paint_options,
"brush-hardness", (gdouble) gimp_brush_generated_get_hardness (generated_brush),
NULL);
}
else
{
g_object_set (paint_options,
"brush-hardness", DEFAULT_BRUSH_HARDNESS,
NULL);
}
}
gboolean
gimp_paint_options_are_dynamics_enabled (GimpPaintOptions *paint_options)
{
return paint_options->dynamics_enabled;
}
void
gimp_paint_options_enable_dynamics (GimpPaintOptions *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
gimp_paint_options_is_prop (const gchar *prop_name,
GimpContextPropMask prop_mask)
{
gint i;
g_return_val_if_fail (prop_name != NULL, FALSE);
if (prop_mask & GIMP_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 & GIMP_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 & GIMP_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 & GIMP_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
gimp_paint_options_copy_props (GimpPaintOptions *src,
GimpPaintOptions *dest,
GimpContextPropMask prop_mask)
{
const gchar *names[max_n_props];
GValue values[max_n_props];
gint n_props = 0;
gint i;
g_return_if_fail (GIMP_IS_PAINT_OPTIONS (src));
g_return_if_fail (GIMP_IS_PAINT_OPTIONS (dest));
if (prop_mask & GIMP_CONTEXT_PROP_MASK_BRUSH)
{
for (i = 0; i < G_N_ELEMENTS (brush_props); i++)
names[n_props++] = brush_props[i];
}
if (prop_mask & GIMP_CONTEXT_PROP_MASK_DYNAMICS)
{
for (i = 0; i < G_N_ELEMENTS (dynamics_props); i++)
names[n_props++] = dynamics_props[i];
}
if (prop_mask & GIMP_CONTEXT_PROP_MASK_GRADIENT)
{
for (i = 0; i < G_N_ELEMENTS (gradient_props); i++)
names[n_props++] = gradient_props[i];
}
if (prop_mask & GIMP_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]);
}
}