/* The PIKA -- an image manipulation program * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis * * pikafilloptions.c * Copyright (C) 2003 Simon Budig * * 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 #include "libpikabase/pikabase.h" #include "libpikacolor/pikacolor.h" #include "libpikaconfig/pikaconfig.h" #include "core-types.h" #include "operations/layer-modes/pika-layer-modes.h" #include "pika.h" #include "pika-palettes.h" #include "pikadrawable.h" #include "pikadrawable-fill.h" #include "pikaerror.h" #include "pikafilloptions.h" #include "pikaimage.h" #include "pikapattern.h" #include "pika-intl.h" enum { PROP_0, PROP_STYLE, PROP_CUSTOM_STYLE, PROP_ANTIALIAS, PROP_FEATHER, PROP_FEATHER_RADIUS, PROP_PATTERN_VIEW_TYPE, PROP_PATTERN_VIEW_SIZE }; typedef struct _PikaFillOptionsPrivate PikaFillOptionsPrivate; struct _PikaFillOptionsPrivate { PikaFillStyle style; PikaCustomStyle custom_style; gboolean antialias; gboolean feather; gdouble feather_radius; PikaViewType pattern_view_type; PikaViewSize pattern_view_size; const gchar *undo_desc; }; #define GET_PRIVATE(options) \ ((PikaFillOptionsPrivate *) pika_fill_options_get_instance_private ((PikaFillOptions *) (options))) static void pika_fill_options_config_init (PikaConfigInterface *iface); static void pika_fill_options_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void pika_fill_options_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static gboolean pika_fill_options_serialize (PikaConfig *config, PikaConfigWriter *writer, gpointer data); G_DEFINE_TYPE_WITH_CODE (PikaFillOptions, pika_fill_options, PIKA_TYPE_CONTEXT, G_ADD_PRIVATE (PikaFillOptions) G_IMPLEMENT_INTERFACE (PIKA_TYPE_CONFIG, pika_fill_options_config_init)) static void pika_fill_options_class_init (PikaFillOptionsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->set_property = pika_fill_options_set_property; object_class->get_property = pika_fill_options_get_property; PIKA_CONFIG_PROP_ENUM (object_class, PROP_STYLE, "style", _("Style"), NULL, PIKA_TYPE_FILL_STYLE, PIKA_FILL_STYLE_FG_COLOR, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_ENUM (object_class, PROP_CUSTOM_STYLE, "custom-style", _("Custom style"), NULL, PIKA_TYPE_CUSTOM_STYLE, PIKA_CUSTOM_STYLE_SOLID_COLOR, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_ANTIALIAS, "antialias", _("Antialiasing"), NULL, TRUE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_FEATHER, "feather", _("Feather edges"), _("Enable feathering of fill edges"), FALSE, PIKA_PARAM_STATIC_STRINGS); PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_FEATHER_RADIUS, "feather-radius", _("Radius"), _("Radius of feathering"), 0.0, 100.0, 10.0, PIKA_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_PATTERN_VIEW_TYPE, g_param_spec_enum ("pattern-view-type", NULL, NULL, PIKA_TYPE_VIEW_TYPE, PIKA_VIEW_TYPE_GRID, G_PARAM_CONSTRUCT | PIKA_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_PATTERN_VIEW_SIZE, g_param_spec_int ("pattern-view-size", NULL, NULL, PIKA_VIEW_SIZE_TINY, PIKA_VIEWABLE_MAX_BUTTON_SIZE, PIKA_VIEW_SIZE_SMALL, G_PARAM_CONSTRUCT | PIKA_PARAM_READWRITE)); } static void pika_fill_options_config_init (PikaConfigInterface *iface) { iface->serialize = pika_fill_options_serialize; } static void pika_fill_options_init (PikaFillOptions *options) { } static void pika_fill_options_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { PikaFillOptionsPrivate *private = GET_PRIVATE (object); switch (property_id) { case PROP_STYLE: private->style = g_value_get_enum (value); private->undo_desc = NULL; break; case PROP_CUSTOM_STYLE: private->custom_style = g_value_get_enum (value); private->undo_desc = NULL; break; case PROP_ANTIALIAS: private->antialias = g_value_get_boolean (value); break; case PROP_FEATHER: private->feather = g_value_get_boolean (value); break; case PROP_FEATHER_RADIUS: private->feather_radius = g_value_get_double (value); break; case PROP_PATTERN_VIEW_TYPE: private->pattern_view_type = g_value_get_enum (value); break; case PROP_PATTERN_VIEW_SIZE: private->pattern_view_size = g_value_get_int (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_fill_options_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { PikaFillOptionsPrivate *private = GET_PRIVATE (object); switch (property_id) { case PROP_STYLE: g_value_set_enum (value, private->style); break; case PROP_CUSTOM_STYLE: g_value_set_enum (value, private->custom_style); break; case PROP_ANTIALIAS: g_value_set_boolean (value, private->antialias); break; case PROP_FEATHER: g_value_set_boolean (value, private->feather); break; case PROP_FEATHER_RADIUS: g_value_set_double (value, private->feather_radius); break; case PROP_PATTERN_VIEW_TYPE: g_value_set_enum (value, private->pattern_view_type); break; case PROP_PATTERN_VIEW_SIZE: g_value_set_int (value, private->pattern_view_size); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static gboolean pika_fill_options_serialize (PikaConfig *config, PikaConfigWriter *writer, gpointer data) { return pika_config_serialize_properties (config, writer); } /* public functions */ PikaFillOptions * pika_fill_options_new (Pika *pika, PikaContext *context, gboolean use_context_color) { PikaFillOptions *options; g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL); g_return_val_if_fail (context == NULL || PIKA_IS_CONTEXT (context), NULL); g_return_val_if_fail (use_context_color == FALSE || context != NULL, NULL); options = g_object_new (PIKA_TYPE_FILL_OPTIONS, "pika", pika, NULL); if (use_context_color) { pika_context_define_properties (PIKA_CONTEXT (options), PIKA_CONTEXT_PROP_MASK_FOREGROUND | PIKA_CONTEXT_PROP_MASK_BACKGROUND | PIKA_CONTEXT_PROP_MASK_PATTERN, FALSE); pika_context_set_parent (PIKA_CONTEXT (options), context); } return options; } PikaFillStyle pika_fill_options_get_style (PikaFillOptions *options) { g_return_val_if_fail (PIKA_IS_FILL_OPTIONS (options), PIKA_FILL_STYLE_FG_COLOR); return GET_PRIVATE (options)->style; } void pika_fill_options_set_style (PikaFillOptions *options, PikaFillStyle style) { g_return_if_fail (PIKA_IS_FILL_OPTIONS (options)); g_object_set (options, "style", style, NULL); } PikaCustomStyle pika_fill_options_get_custom_style (PikaFillOptions *options) { g_return_val_if_fail (PIKA_IS_FILL_OPTIONS (options), PIKA_CUSTOM_STYLE_SOLID_COLOR); return GET_PRIVATE (options)->custom_style; } void pika_fill_options_set_custom_style (PikaFillOptions *options, PikaCustomStyle custom_style) { g_return_if_fail (PIKA_IS_FILL_OPTIONS (options)); g_object_set (options, "custom-style", custom_style, NULL); } gboolean pika_fill_options_get_antialias (PikaFillOptions *options) { g_return_val_if_fail (PIKA_IS_FILL_OPTIONS (options), FALSE); return GET_PRIVATE (options)->antialias; } void pika_fill_options_set_antialias (PikaFillOptions *options, gboolean antialias) { g_return_if_fail (PIKA_IS_FILL_OPTIONS (options)); g_object_set (options, "antialias", antialias, NULL); } gboolean pika_fill_options_get_feather (PikaFillOptions *options, gdouble *radius) { g_return_val_if_fail (PIKA_IS_FILL_OPTIONS (options), FALSE); if (radius) *radius = GET_PRIVATE (options)->feather_radius; return GET_PRIVATE (options)->feather; } void pika_fill_options_set_feather (PikaFillOptions *options, gboolean feather, gdouble radius) { g_return_if_fail (PIKA_IS_FILL_OPTIONS (options)); g_object_set (options, "feather", feather, NULL); g_object_set (options, "feather-radius", radius, NULL); } gboolean pika_fill_options_set_by_fill_type (PikaFillOptions *options, PikaContext *context, PikaFillType fill_type, GError **error) { PikaFillOptionsPrivate *private; PikaRGB color; const gchar *undo_desc; g_return_val_if_fail (PIKA_IS_FILL_OPTIONS (options), FALSE); g_return_val_if_fail (PIKA_IS_CONTEXT (context), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); private = GET_PRIVATE (options); private->undo_desc = NULL; switch (fill_type) { case PIKA_FILL_FOREGROUND: pika_context_get_foreground (context, &color); undo_desc = C_("undo-type", "Fill with Foreground Color"); break; case PIKA_FILL_BACKGROUND: pika_context_get_background (context, &color); undo_desc = C_("undo-type", "Fill with Background Color"); break; case PIKA_FILL_CIELAB_MIDDLE_GRAY: { const float cielab_pixel[3] = {50, 0, 0}; float pixel[3] = {0, 0, 0}; PikaImage *image = pika_context_get_image (context); PikaImageBaseType base_type; const Babl *format; base_type = pika_image_get_base_type (image); if (base_type == PIKA_INDEXED) base_type = PIKA_RGB; format = pika_image_get_format (image, base_type, PIKA_PRECISION_FLOAT_NON_LINEAR, FALSE, pika_image_get_layer_space (image)); babl_process (babl_fish (babl_format ("CIE Lab float"), format), cielab_pixel, pixel, 1); pika_rgba_set (&color, pixel[0], pixel[1], pixel[2], PIKA_OPACITY_OPAQUE); undo_desc = C_("undo-type", "Fill with Middle Gray (CIELAB) Color"); } break; case PIKA_FILL_WHITE: pika_rgba_set (&color, 1.0, 1.0, 1.0, PIKA_OPACITY_OPAQUE); undo_desc = C_("undo-type", "Fill with White"); break; case PIKA_FILL_TRANSPARENT: pika_context_get_background (context, &color); pika_context_set_paint_mode (PIKA_CONTEXT (options), PIKA_LAYER_MODE_ERASE); undo_desc = C_("undo-type", "Fill with Transparency"); break; case PIKA_FILL_PATTERN: { PikaPattern *pattern = pika_context_get_pattern (context); if (! pattern) { g_set_error_literal (error, PIKA_ERROR, PIKA_FAILED, _("No patterns available for this operation.")); return FALSE; } pika_fill_options_set_style (options, PIKA_FILL_STYLE_PATTERN); pika_context_set_pattern (PIKA_CONTEXT (options), pattern); private->undo_desc = C_("undo-type", "Fill with Pattern"); return TRUE; } break; default: g_warning ("%s: invalid fill_type %d", G_STRFUNC, fill_type); return FALSE; } pika_fill_options_set_style (options, PIKA_FILL_STYLE_FG_COLOR); pika_context_set_foreground (PIKA_CONTEXT (options), &color); private->undo_desc = undo_desc; return TRUE; } gboolean pika_fill_options_set_by_fill_mode (PikaFillOptions *options, PikaContext *context, PikaBucketFillMode fill_mode, GError **error) { PikaFillType fill_type; g_return_val_if_fail (PIKA_IS_FILL_OPTIONS (options), FALSE); g_return_val_if_fail (PIKA_IS_CONTEXT (context), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); switch (fill_mode) { default: case PIKA_BUCKET_FILL_FG: fill_type = PIKA_FILL_FOREGROUND; break; case PIKA_BUCKET_FILL_BG: fill_type = PIKA_FILL_BACKGROUND; break; case PIKA_BUCKET_FILL_PATTERN: fill_type = PIKA_FILL_PATTERN; break; } return pika_fill_options_set_by_fill_type (options, context, fill_type, error); } const gchar * pika_fill_options_get_undo_desc (PikaFillOptions *options) { PikaFillOptionsPrivate *private; g_return_val_if_fail (PIKA_IS_FILL_OPTIONS (options), NULL); private = GET_PRIVATE (options); if (private->undo_desc) return private->undo_desc; switch (private->style) { case PIKA_FILL_STYLE_FG_COLOR: return C_("undo-type", "Fill with Foreground Color"); case PIKA_FILL_STYLE_BG_COLOR: return C_("undo-type", "Fill with Background Color"); case PIKA_FILL_STYLE_PATTERN: return C_("undo-type", "Fill with Pattern"); } g_return_val_if_reached (NULL); } const Babl * pika_fill_options_get_format (PikaFillOptions *options, PikaDrawable *drawable) { PikaContext *context; g_return_val_if_fail (PIKA_IS_FILL_OPTIONS (options), NULL); g_return_val_if_fail (PIKA_IS_DRAWABLE (drawable), NULL); context = PIKA_CONTEXT (options); return pika_layer_mode_get_format (pika_context_get_paint_mode (context), PIKA_LAYER_COLOR_SPACE_AUTO, PIKA_LAYER_COLOR_SPACE_AUTO, pika_layer_mode_get_paint_composite_mode ( pika_context_get_paint_mode (context)), pika_drawable_get_format (drawable)); } GeglBuffer * pika_fill_options_create_buffer (PikaFillOptions *options, PikaDrawable *drawable, const GeglRectangle *rect, gint pattern_offset_x, gint pattern_offset_y) { GeglBuffer *buffer; g_return_val_if_fail (PIKA_IS_FILL_OPTIONS (options), NULL); g_return_val_if_fail (pika_fill_options_get_style (options) != PIKA_FILL_STYLE_PATTERN || pika_context_get_pattern (PIKA_CONTEXT (options)) != NULL, NULL); g_return_val_if_fail (PIKA_IS_DRAWABLE (drawable), NULL); g_return_val_if_fail (rect != NULL, NULL); buffer = gegl_buffer_new (rect, pika_fill_options_get_format (options, drawable)); pika_fill_options_fill_buffer (options, drawable, buffer, pattern_offset_x, pattern_offset_y); return buffer; } void pika_fill_options_fill_buffer (PikaFillOptions *options, PikaDrawable *drawable, GeglBuffer *buffer, gint pattern_offset_x, gint pattern_offset_y) { g_return_if_fail (PIKA_IS_FILL_OPTIONS (options)); g_return_if_fail (pika_fill_options_get_style (options) != PIKA_FILL_STYLE_PATTERN || pika_context_get_pattern (PIKA_CONTEXT (options)) != NULL); g_return_if_fail (PIKA_IS_DRAWABLE (drawable)); g_return_if_fail (GEGL_IS_BUFFER (buffer)); switch (pika_fill_options_get_style (options)) { case PIKA_FILL_STYLE_FG_COLOR: { PikaRGB color; pika_context_get_foreground (PIKA_CONTEXT (options), &color); pika_palettes_add_color_history (PIKA_CONTEXT (options)->pika, &color); pika_drawable_fill_buffer (drawable, buffer, &color, NULL, 0, 0); } break; case PIKA_FILL_STYLE_BG_COLOR: { PikaRGB color; pika_context_get_background (PIKA_CONTEXT (options), &color); pika_palettes_add_color_history (PIKA_CONTEXT (options)->pika, &color); pika_drawable_fill_buffer (drawable, buffer, &color, NULL, 0, 0); } break; case PIKA_FILL_STYLE_PATTERN: { PikaPattern *pattern; pattern = pika_context_get_pattern (PIKA_CONTEXT (options)); pika_drawable_fill_buffer (drawable, buffer, NULL, pattern, pattern_offset_x, pattern_offset_y); } break; } }