mirror of https://github.com/GNOME/gimp.git
Bug 648776 - mirror symmetries.
You can now set any paint tool to mirror painting relatively horizontal/vertical axis or a central point (any combination of these 3 symmetries). This has been implemented as a new multi-stroke core, where every stroke is actually handled as a multi-stroke (default of size 1). This is also the first usage of custom guides for symmetry guiding. Current version has to be activated in the playground.
This commit is contained in:
parent
b8fadf3ad7
commit
76f573c981
|
@ -61,6 +61,12 @@ const GimpStringActionEntry dialogs_dockable_actions[] =
|
|||
"gimp-device-status",
|
||||
GIMP_HELP_DEVICE_STATUS_DIALOG },
|
||||
|
||||
{ "dialogs-symmetry", GIMP_STOCK_SYMMETRY,
|
||||
NC_("dialogs-action", "_Symmetry painting"), NULL,
|
||||
NC_("dialogs-action", "Open the symmetry dialog"),
|
||||
"gimp-symmetry-editor",
|
||||
GIMP_HELP_SYMMETRY_DIALOG },
|
||||
|
||||
{ "dialogs-layers", GIMP_STOCK_LAYERS,
|
||||
NC_("dialogs-action", "_Layers"), "<primary>L",
|
||||
NC_("dialogs-action", "Open the layers dialog"),
|
||||
|
|
|
@ -83,6 +83,7 @@ enum
|
|||
PROP_PLAYGROUND_NPD_TOOL,
|
||||
PROP_PLAYGROUND_HANDLE_TRANSFORM_TOOL,
|
||||
PROP_PLAYGROUND_SEAMLESS_CLONE_TOOL,
|
||||
PROP_PLAYGROUND_SYMMETRY,
|
||||
|
||||
PROP_HIDE_DOCKS,
|
||||
PROP_SINGLE_WINDOW_MODE,
|
||||
|
@ -300,6 +301,13 @@ gimp_gui_config_class_init (GimpGuiConfigClass *klass)
|
|||
FALSE,
|
||||
GIMP_PARAM_STATIC_STRINGS |
|
||||
GIMP_CONFIG_PARAM_RESTART);
|
||||
GIMP_CONFIG_INSTALL_PROP_BOOLEAN (object_class,
|
||||
PROP_PLAYGROUND_SYMMETRY,
|
||||
"playground-symmetry",
|
||||
PLAYGROUND_SYMMETRY_BLURB,
|
||||
FALSE,
|
||||
GIMP_PARAM_STATIC_STRINGS |
|
||||
GIMP_CONFIG_PARAM_RESTART);
|
||||
GIMP_CONFIG_INSTALL_PROP_BOOLEAN (object_class,
|
||||
PROP_PLAYGROUND_SEAMLESS_CLONE_TOOL,
|
||||
"playground-seamless-clone-tool",
|
||||
|
@ -528,6 +536,9 @@ gimp_gui_config_set_property (GObject *object,
|
|||
case PROP_PLAYGROUND_HANDLE_TRANSFORM_TOOL:
|
||||
gui_config->playground_handle_transform_tool = g_value_get_boolean (value);
|
||||
break;
|
||||
case PROP_PLAYGROUND_SYMMETRY:
|
||||
gui_config->playground_symmetry = g_value_get_boolean (value);
|
||||
break;
|
||||
case PROP_PLAYGROUND_SEAMLESS_CLONE_TOOL:
|
||||
gui_config->playground_seamless_clone_tool = g_value_get_boolean (value);
|
||||
break;
|
||||
|
@ -678,6 +689,9 @@ gimp_gui_config_get_property (GObject *object,
|
|||
case PROP_PLAYGROUND_HANDLE_TRANSFORM_TOOL:
|
||||
g_value_set_boolean (value, gui_config->playground_handle_transform_tool);
|
||||
break;
|
||||
case PROP_PLAYGROUND_SYMMETRY:
|
||||
g_value_set_boolean (value, gui_config->playground_symmetry);
|
||||
break;
|
||||
case PROP_PLAYGROUND_SEAMLESS_CLONE_TOOL:
|
||||
g_value_set_boolean (value, gui_config->playground_seamless_clone_tool);
|
||||
break;
|
||||
|
|
|
@ -83,6 +83,7 @@ struct _GimpGuiConfig
|
|||
gboolean playground_npd_tool;
|
||||
gboolean playground_handle_transform_tool;
|
||||
gboolean playground_seamless_clone_tool;
|
||||
gboolean playground_symmetry;
|
||||
|
||||
/* saved in sessionrc */
|
||||
gboolean hide_docks;
|
||||
|
|
|
@ -390,6 +390,9 @@ _("Enable the N-Point Deformation tool.")
|
|||
#define PLAYGROUND_HANDLE_TRANSFORM_TOOL_BLURB \
|
||||
_("Enable the Handle Transform tool.")
|
||||
|
||||
#define PLAYGROUND_SYMMETRY_BLURB \
|
||||
_("Enable symmetry on painting.")
|
||||
|
||||
#define PLAYGROUND_MYBRUSH_TOOL_BLURB \
|
||||
_("Enable the MyPaint Brush tool.")
|
||||
|
||||
|
|
|
@ -259,6 +259,8 @@ libappcore_a_sources = \
|
|||
gimpimage-scale.h \
|
||||
gimpimage-snap.c \
|
||||
gimpimage-snap.h \
|
||||
gimpimage-symmetry.c \
|
||||
gimpimage-symmetry.h \
|
||||
gimpimage-undo.c \
|
||||
gimpimage-undo.h \
|
||||
gimpimage-undo-push.c \
|
||||
|
@ -367,6 +369,10 @@ libappcore_a_sources = \
|
|||
gimpstrokeoptions.h \
|
||||
gimpsubprogress.c \
|
||||
gimpsubprogress.h \
|
||||
gimpsymmetry.c \
|
||||
gimpsymmetry.h \
|
||||
gimpsymmetry-mirror.c \
|
||||
gimpsymmetry-mirror.h \
|
||||
gimptag.c \
|
||||
gimptag.h \
|
||||
gimptagcache.c \
|
||||
|
|
|
@ -175,6 +175,11 @@ typedef struct _GimpUndoStack GimpUndoStack;
|
|||
typedef struct _GimpUndoAccumulator GimpUndoAccumulator;
|
||||
|
||||
|
||||
/* Symmetry transformations */
|
||||
|
||||
typedef struct _GimpSymmetry GimpSymmetry;
|
||||
typedef struct _GimpMirror GimpMirror;
|
||||
|
||||
/* misc objects */
|
||||
|
||||
typedef struct _GimpBuffer GimpBuffer;
|
||||
|
|
|
@ -39,7 +39,7 @@ gimp_brush_transform_boundary_exact (GimpBrush *brush,
|
|||
{
|
||||
const GimpTempBuf *mask;
|
||||
|
||||
mask = gimp_brush_transform_mask (brush,
|
||||
mask = gimp_brush_transform_mask (brush, NULL,
|
||||
scale, aspect_ratio, angle, hardness);
|
||||
|
||||
if (mask)
|
||||
|
|
|
@ -313,12 +313,12 @@ gimp_brush_get_new_preview (GimpViewable *viewable,
|
|||
{
|
||||
GimpBrushGenerated *gen_brush = GIMP_BRUSH_GENERATED (brush);
|
||||
|
||||
mask_buf = gimp_brush_transform_mask (brush, scale,
|
||||
mask_buf = gimp_brush_transform_mask (brush, NULL, scale,
|
||||
0.0, 0.0,
|
||||
gimp_brush_generated_get_hardness (gen_brush));
|
||||
}
|
||||
else
|
||||
mask_buf = gimp_brush_transform_mask (brush, scale,
|
||||
mask_buf = gimp_brush_transform_mask (brush, NULL, scale,
|
||||
0.0, 0.0, 1.0);
|
||||
|
||||
if (! mask_buf)
|
||||
|
@ -332,7 +332,7 @@ gimp_brush_get_new_preview (GimpViewable *viewable,
|
|||
}
|
||||
|
||||
if (pixmap_buf)
|
||||
pixmap_buf = gimp_brush_transform_pixmap (brush, scale,
|
||||
pixmap_buf = gimp_brush_transform_pixmap (brush, NULL, scale,
|
||||
0.0, 0.0, 1.0);
|
||||
|
||||
mask_width = gimp_temp_buf_get_width (mask_buf);
|
||||
|
@ -611,6 +611,7 @@ gimp_brush_transform_size (GimpBrush *brush,
|
|||
|
||||
const GimpTempBuf *
|
||||
gimp_brush_transform_mask (GimpBrush *brush,
|
||||
GeglNode *op,
|
||||
gdouble scale,
|
||||
gdouble aspect_ratio,
|
||||
gdouble angle,
|
||||
|
@ -628,7 +629,7 @@ gimp_brush_transform_mask (GimpBrush *brush,
|
|||
&width, &height);
|
||||
|
||||
mask = gimp_brush_cache_get (brush->priv->mask_cache,
|
||||
width, height,
|
||||
op, width, height,
|
||||
scale, aspect_ratio, angle, hardness);
|
||||
|
||||
if (! mask)
|
||||
|
@ -649,9 +650,31 @@ gimp_brush_transform_mask (GimpBrush *brush,
|
|||
hardness);
|
||||
}
|
||||
|
||||
if (op)
|
||||
{
|
||||
GeglNode *graph, *source, *target;
|
||||
GeglBuffer *buffer = gimp_temp_buf_create_buffer ((GimpTempBuf *) mask);
|
||||
|
||||
graph = gegl_node_new ();
|
||||
source = gegl_node_new_child (graph,
|
||||
"operation", "gegl:buffer-source",
|
||||
"buffer", buffer,
|
||||
NULL);
|
||||
gegl_node_add_child (graph, op);
|
||||
target = gegl_node_new_child (graph,
|
||||
"operation", "gegl:write-buffer",
|
||||
"buffer", buffer,
|
||||
NULL);
|
||||
|
||||
gegl_node_link_many (source, op, target, NULL);
|
||||
gegl_node_process (target);
|
||||
|
||||
g_object_unref (graph);
|
||||
g_object_unref (buffer);
|
||||
}
|
||||
gimp_brush_cache_add (brush->priv->mask_cache,
|
||||
(gpointer) mask,
|
||||
width, height,
|
||||
op, width, height,
|
||||
scale, aspect_ratio, angle, hardness);
|
||||
}
|
||||
|
||||
|
@ -660,6 +683,7 @@ gimp_brush_transform_mask (GimpBrush *brush,
|
|||
|
||||
const GimpTempBuf *
|
||||
gimp_brush_transform_pixmap (GimpBrush *brush,
|
||||
GeglNode *op,
|
||||
gdouble scale,
|
||||
gdouble aspect_ratio,
|
||||
gdouble angle,
|
||||
|
@ -678,7 +702,7 @@ gimp_brush_transform_pixmap (GimpBrush *brush,
|
|||
&width, &height);
|
||||
|
||||
pixmap = gimp_brush_cache_get (brush->priv->pixmap_cache,
|
||||
width, height,
|
||||
op, width, height,
|
||||
scale, aspect_ratio, angle, hardness);
|
||||
|
||||
if (! pixmap)
|
||||
|
@ -699,9 +723,31 @@ gimp_brush_transform_pixmap (GimpBrush *brush,
|
|||
hardness);
|
||||
}
|
||||
|
||||
if (op)
|
||||
{
|
||||
GeglNode *graph, *source, *target;
|
||||
GeglBuffer *buffer = gimp_temp_buf_create_buffer ((GimpTempBuf *) pixmap);
|
||||
|
||||
graph = gegl_node_new ();
|
||||
source = gegl_node_new_child (graph,
|
||||
"operation", "gegl:buffer-source",
|
||||
"buffer", buffer,
|
||||
NULL);
|
||||
gegl_node_add_child (graph, op);
|
||||
target = gegl_node_new_child (graph,
|
||||
"operation", "gegl:write-buffer",
|
||||
"buffer", buffer,
|
||||
NULL);
|
||||
|
||||
gegl_node_link_many (source, op, target, NULL);
|
||||
gegl_node_process (target);
|
||||
|
||||
g_object_unref (graph);
|
||||
g_object_unref (buffer);
|
||||
}
|
||||
gimp_brush_cache_add (brush->priv->pixmap_cache,
|
||||
(gpointer) pixmap,
|
||||
width, height,
|
||||
op, width, height,
|
||||
scale, aspect_ratio, angle, hardness);
|
||||
}
|
||||
|
||||
|
@ -729,7 +775,7 @@ gimp_brush_transform_boundary (GimpBrush *brush,
|
|||
width, height);
|
||||
|
||||
boundary = gimp_brush_cache_get (brush->priv->boundary_cache,
|
||||
*width, *height,
|
||||
NULL, *width, *height,
|
||||
scale, aspect_ratio, angle, hardness);
|
||||
|
||||
if (! boundary)
|
||||
|
@ -751,7 +797,7 @@ gimp_brush_transform_boundary (GimpBrush *brush,
|
|||
if (boundary)
|
||||
gimp_brush_cache_add (brush->priv->boundary_cache,
|
||||
(gpointer) boundary,
|
||||
*width, *height,
|
||||
NULL, *width, *height,
|
||||
scale, aspect_ratio, angle, hardness);
|
||||
}
|
||||
|
||||
|
|
|
@ -109,11 +109,13 @@ void gimp_brush_transform_size (GimpBrush *brush,
|
|||
gint *width,
|
||||
gint *height);
|
||||
const GimpTempBuf * gimp_brush_transform_mask (GimpBrush *brush,
|
||||
GeglNode *op,
|
||||
gdouble scale,
|
||||
gdouble aspect_ratio,
|
||||
gdouble angle,
|
||||
gdouble hardness);
|
||||
const GimpTempBuf * gimp_brush_transform_pixmap (GimpBrush *brush,
|
||||
GeglNode *op,
|
||||
gdouble scale,
|
||||
gdouble aspect_ratio,
|
||||
gdouble angle,
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "gimp-log.h"
|
||||
#include "gimp-intl.h"
|
||||
|
||||
#define MAX_CACHED_DATA 20
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -36,6 +37,20 @@ enum
|
|||
PROP_DATA_DESTROY
|
||||
};
|
||||
|
||||
typedef struct _GimpBrushCacheUnit GimpBrushCacheUnit;
|
||||
|
||||
struct _GimpBrushCacheUnit
|
||||
{
|
||||
gpointer data;
|
||||
|
||||
gint width;
|
||||
gint height;
|
||||
gdouble scale;
|
||||
gdouble aspect_ratio;
|
||||
gdouble angle;
|
||||
gdouble hardness;
|
||||
GeglNode *op;
|
||||
};
|
||||
|
||||
static void gimp_brush_cache_constructed (GObject *object);
|
||||
static void gimp_brush_cache_finalize (GObject *object);
|
||||
|
@ -91,10 +106,18 @@ gimp_brush_cache_finalize (GObject *object)
|
|||
{
|
||||
GimpBrushCache *cache = GIMP_BRUSH_CACHE (object);
|
||||
|
||||
if (cache->last_data)
|
||||
if (cache->cached_units)
|
||||
{
|
||||
cache->data_destroy (cache->last_data);
|
||||
cache->last_data = NULL;
|
||||
GList *iter;
|
||||
|
||||
for (iter = cache->cached_units; iter; iter = g_list_next (iter))
|
||||
{
|
||||
GimpBrushCacheUnit *unit = iter->data;
|
||||
|
||||
cache->data_destroy (unit->data);
|
||||
}
|
||||
g_list_free_full (cache->cached_units, g_free);
|
||||
cache->cached_units = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
|
@ -167,15 +190,24 @@ gimp_brush_cache_clear (GimpBrushCache *cache)
|
|||
{
|
||||
g_return_if_fail (GIMP_IS_BRUSH_CACHE (cache));
|
||||
|
||||
if (cache->last_data)
|
||||
if (cache->cached_units)
|
||||
{
|
||||
cache->data_destroy (cache->last_data);
|
||||
cache->last_data = NULL;
|
||||
GList *iter;
|
||||
|
||||
for (iter = cache->cached_units; iter; iter = g_list_next (iter))
|
||||
{
|
||||
GimpBrushCacheUnit *unit = iter->data;
|
||||
|
||||
cache->data_destroy (unit->data);
|
||||
}
|
||||
g_list_free_full (cache->cached_units, g_free);
|
||||
cache->cached_units = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
gconstpointer
|
||||
gimp_brush_cache_get (GimpBrushCache *cache,
|
||||
GeglNode *op,
|
||||
gint width,
|
||||
gint height,
|
||||
gdouble scale,
|
||||
|
@ -183,20 +215,34 @@ gimp_brush_cache_get (GimpBrushCache *cache,
|
|||
gdouble angle,
|
||||
gdouble hardness)
|
||||
{
|
||||
GList *iter;
|
||||
|
||||
g_return_val_if_fail (GIMP_IS_BRUSH_CACHE (cache), NULL);
|
||||
|
||||
if (cache->last_data &&
|
||||
cache->last_width == width &&
|
||||
cache->last_height == height &&
|
||||
cache->last_scale == scale &&
|
||||
cache->last_aspect_ratio == aspect_ratio &&
|
||||
cache->last_angle == angle &&
|
||||
cache->last_hardness == hardness)
|
||||
for (iter = cache->cached_units; iter; iter = g_list_next (iter))
|
||||
{
|
||||
if (gimp_log_flags & GIMP_LOG_BRUSH_CACHE)
|
||||
g_printerr ("%c", cache->debug_hit);
|
||||
GimpBrushCacheUnit *unit = iter->data;
|
||||
|
||||
return (gconstpointer) cache->last_data;
|
||||
if (unit->data &&
|
||||
unit->width == width &&
|
||||
unit->height == height &&
|
||||
unit->scale == scale &&
|
||||
unit->aspect_ratio == aspect_ratio &&
|
||||
unit->angle == angle &&
|
||||
unit->hardness == hardness &&
|
||||
unit->op == op)
|
||||
{
|
||||
if (gimp_log_flags & GIMP_LOG_BRUSH_CACHE)
|
||||
g_printerr ("%c", cache->debug_hit);
|
||||
|
||||
/* Make the returned cached brush first in the list. */
|
||||
cache->cached_units = g_list_remove_link (cache->cached_units, iter);
|
||||
iter->next = cache->cached_units;
|
||||
if (cache->cached_units)
|
||||
cache->cached_units->prev = iter;
|
||||
cache->cached_units = iter;
|
||||
return (gconstpointer) unit->data;
|
||||
}
|
||||
}
|
||||
|
||||
if (gimp_log_flags & GIMP_LOG_BRUSH_CACHE)
|
||||
|
@ -208,6 +254,7 @@ gimp_brush_cache_get (GimpBrushCache *cache,
|
|||
void
|
||||
gimp_brush_cache_add (GimpBrushCache *cache,
|
||||
gpointer data,
|
||||
GeglNode *op,
|
||||
gint width,
|
||||
gint height,
|
||||
gdouble scale,
|
||||
|
@ -215,20 +262,38 @@ gimp_brush_cache_add (GimpBrushCache *cache,
|
|||
gdouble angle,
|
||||
gdouble hardness)
|
||||
{
|
||||
GList *iter;
|
||||
GimpBrushCacheUnit *unit;
|
||||
|
||||
g_return_if_fail (GIMP_IS_BRUSH_CACHE (cache));
|
||||
g_return_if_fail (data != NULL);
|
||||
|
||||
if (data == cache->last_data)
|
||||
return;
|
||||
for (iter = cache->cached_units; iter; iter = g_list_next (iter))
|
||||
{
|
||||
unit = iter->data;
|
||||
if (data == unit->data)
|
||||
return;
|
||||
}
|
||||
|
||||
if (cache->last_data)
|
||||
cache->data_destroy (cache->last_data);
|
||||
if (g_list_length (cache->cached_units) > MAX_CACHED_DATA &&
|
||||
(iter = g_list_last (cache->cached_units)))
|
||||
{
|
||||
unit = iter->data;
|
||||
|
||||
cache->last_data = data;
|
||||
cache->last_width = width;
|
||||
cache->last_height = height;
|
||||
cache->last_scale = scale;
|
||||
cache->last_aspect_ratio = aspect_ratio;
|
||||
cache->last_angle = angle;
|
||||
cache->last_hardness = hardness;
|
||||
cache->data_destroy (unit->data);
|
||||
cache->cached_units = g_list_delete_link (cache->cached_units, iter);
|
||||
}
|
||||
|
||||
unit = g_new (GimpBrushCacheUnit, 1);
|
||||
|
||||
unit->data = data;
|
||||
unit->width = width;
|
||||
unit->height = height;
|
||||
unit->scale = scale;
|
||||
unit->aspect_ratio = aspect_ratio;
|
||||
unit->angle = angle;
|
||||
unit->hardness = hardness;
|
||||
unit->op = op;
|
||||
|
||||
cache->cached_units = g_list_prepend (cache->cached_units, unit);
|
||||
}
|
||||
|
|
|
@ -41,13 +41,7 @@ struct _GimpBrushCache
|
|||
|
||||
GDestroyNotify data_destroy;
|
||||
|
||||
gpointer last_data;
|
||||
gint last_width;
|
||||
gint last_height;
|
||||
gdouble last_scale;
|
||||
gdouble last_aspect_ratio;
|
||||
gdouble last_angle;
|
||||
gdouble last_hardness;
|
||||
GList *cached_units;
|
||||
|
||||
gchar debug_hit;
|
||||
gchar debug_miss;
|
||||
|
@ -68,6 +62,7 @@ GimpBrushCache * gimp_brush_cache_new (GDestroyNotify data_destory,
|
|||
void gimp_brush_cache_clear (GimpBrushCache *cache);
|
||||
|
||||
gconstpointer gimp_brush_cache_get (GimpBrushCache *cache,
|
||||
GeglNode *op,
|
||||
gint width,
|
||||
gint height,
|
||||
gdouble scale,
|
||||
|
@ -76,6 +71,7 @@ gconstpointer gimp_brush_cache_get (GimpBrushCache *cache,
|
|||
gdouble hardness);
|
||||
void gimp_brush_cache_add (GimpBrushCache *cache,
|
||||
gpointer data,
|
||||
GeglNode *op,
|
||||
gint width,
|
||||
gint height,
|
||||
gdouble scale,
|
||||
|
|
|
@ -87,6 +87,9 @@ struct _GimpImagePrivate
|
|||
GeglNode *graph; /* GEGL projection graph */
|
||||
GeglNode *visible_mask; /* component visibility node */
|
||||
|
||||
GList *symmetries; /* Painting symmetries */
|
||||
GimpSymmetry *selected_symmetry; /* Selected symmetry */
|
||||
|
||||
GList *guides; /* guides */
|
||||
GimpGrid *grid; /* grid */
|
||||
GList *sample_points; /* color sample points */
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
/* GIMP - The GNU Image Manipulation Program
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* gimpimage-symmetry.c
|
||||
* Copyright (C) 2015 Jehan <jehan@gimp.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gegl.h>
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
|
||||
#include "core-types.h"
|
||||
|
||||
#include "gimpsymmetry.h"
|
||||
#include "gimpimage.h"
|
||||
#include "gimpimage-private.h"
|
||||
#include "gimpimage-symmetry.h"
|
||||
#include "gimpsymmetry-mirror.h"
|
||||
|
||||
/**
|
||||
* gimp_image_symmetry_list:
|
||||
*
|
||||
* Returns a list of #GType of all existing symmetries.
|
||||
**/
|
||||
GList *
|
||||
gimp_image_symmetry_list (void)
|
||||
{
|
||||
GList *list = NULL;
|
||||
|
||||
list = g_list_prepend (list, GINT_TO_POINTER (GIMP_TYPE_MIRROR));
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* gimp_image_symmetry_new:
|
||||
* @image: the #GimpImage
|
||||
* @type: the #GType of the symmetry
|
||||
*
|
||||
* Creates a new #GimpSymmetry of @type attached to @image.
|
||||
*
|
||||
* Returns: the new #GimpSymmetry.
|
||||
**/
|
||||
GimpSymmetry *
|
||||
gimp_image_symmetry_new (GimpImage *image,
|
||||
GType type)
|
||||
{
|
||||
GimpSymmetry *sym = NULL;
|
||||
|
||||
if (type != G_TYPE_NONE)
|
||||
{
|
||||
sym = g_object_new (type,
|
||||
"image", image,
|
||||
NULL);
|
||||
sym->type = type;
|
||||
}
|
||||
|
||||
return sym;
|
||||
}
|
||||
|
||||
/**
|
||||
* gimp_image_symmetry_add:
|
||||
* @image: the #GimpImage
|
||||
* @type: the #GType of the symmetry
|
||||
*
|
||||
* Add a symmetry of type @type to @image and make it the
|
||||
* selected transformation.
|
||||
**/
|
||||
void
|
||||
gimp_image_symmetry_add (GimpImage *image,
|
||||
GimpSymmetry *sym)
|
||||
{
|
||||
GimpImagePrivate *private;
|
||||
|
||||
g_return_if_fail (GIMP_IS_IMAGE (image));
|
||||
g_return_if_fail (GIMP_IS_SYMMETRY (sym));
|
||||
|
||||
private = GIMP_IMAGE_GET_PRIVATE (image);
|
||||
|
||||
private->symmetries = g_list_prepend (private->symmetries,
|
||||
sym);
|
||||
}
|
||||
|
||||
/**
|
||||
* gimp_image_symmetry_remove:
|
||||
* @image: the #GimpImage
|
||||
* @sym: the #GimpSymmetry
|
||||
*
|
||||
* Remove @sym from the list of symmetries of @image.
|
||||
* If it was the selected transformation, unselect it first.
|
||||
**/
|
||||
void
|
||||
gimp_image_symmetry_remove (GimpImage *image,
|
||||
GimpSymmetry *sym)
|
||||
{
|
||||
GimpImagePrivate *private;
|
||||
|
||||
g_return_if_fail (GIMP_IS_SYMMETRY (sym));
|
||||
g_return_if_fail (GIMP_IS_IMAGE (image));
|
||||
|
||||
private = GIMP_IMAGE_GET_PRIVATE (image);
|
||||
|
||||
if (private->selected_symmetry == sym)
|
||||
gimp_image_symmetry_select (image, G_TYPE_NONE);
|
||||
private->symmetries = g_list_remove (private->symmetries,
|
||||
sym);
|
||||
g_object_unref (sym);
|
||||
}
|
||||
|
||||
/**
|
||||
* gimp_image_symmetry_get:
|
||||
* @image: the #GimpImage
|
||||
*
|
||||
* Returns: the list of #GimpSymmetry set on @image.
|
||||
* The returned list belongs to @image and should not be freed.
|
||||
**/
|
||||
GList *
|
||||
gimp_image_symmetry_get (GimpImage *image)
|
||||
{
|
||||
GimpImagePrivate *private;
|
||||
|
||||
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
|
||||
|
||||
private = GIMP_IMAGE_GET_PRIVATE (image);
|
||||
|
||||
return private->symmetries;
|
||||
}
|
||||
|
||||
/**
|
||||
* gimp_image_symmetry_select:
|
||||
* @image: the #GimpImage
|
||||
* @type: the #GType of the symmetry
|
||||
*
|
||||
* Select the symmetry of type @type.
|
||||
* Using the GType allows to select a transformation without
|
||||
* knowing whether one of the same @type was already created.
|
||||
*
|
||||
* Returns TRUE on success, FALSE if no such symmetry was found.
|
||||
**/
|
||||
gboolean
|
||||
gimp_image_symmetry_select (GimpImage *image,
|
||||
GType type)
|
||||
{
|
||||
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
|
||||
|
||||
g_object_set (image,
|
||||
"symmetry", type,
|
||||
NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gimp_image_symmetry_selected:
|
||||
* @image: the #GimpImage
|
||||
*
|
||||
* Returns the #GimpSymmetry transformation selected on @image.
|
||||
**/
|
||||
GimpSymmetry *
|
||||
gimp_image_symmetry_selected (GimpImage *image)
|
||||
{
|
||||
static GimpImage *last_image = NULL;
|
||||
static GimpSymmetry *identity = NULL;
|
||||
GimpImagePrivate *private;
|
||||
|
||||
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
|
||||
|
||||
if (last_image != image)
|
||||
{
|
||||
if (identity)
|
||||
g_object_unref (identity);
|
||||
identity = gimp_image_symmetry_new (image,
|
||||
GIMP_TYPE_SYMMETRY);
|
||||
}
|
||||
|
||||
private = GIMP_IMAGE_GET_PRIVATE (image);
|
||||
|
||||
return private->selected_symmetry ? private->selected_symmetry : identity;
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/* GIMP - The GNU Image Manipulation Program
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* gimpimage-symmetry.h
|
||||
* Copyright (C) 2015 Jehan <jehan@gimp.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __GIMP_IMAGE_SYMMETRY_H__
|
||||
#define __GIMP_IMAGE_SYMMETRY_H__
|
||||
|
||||
GList * gimp_image_symmetry_list (void);
|
||||
|
||||
GimpSymmetry * gimp_image_symmetry_new (GimpImage *image,
|
||||
GType type);
|
||||
void gimp_image_symmetry_add (GimpImage *image,
|
||||
GimpSymmetry *sym);
|
||||
void gimp_image_symmetry_remove (GimpImage *image,
|
||||
GimpSymmetry *sym);
|
||||
GList * gimp_image_symmetry_get (GimpImage *image);
|
||||
|
||||
gboolean gimp_image_symmetry_select (GimpImage *image,
|
||||
GType type);
|
||||
GimpSymmetry * gimp_image_symmetry_selected (GimpImage *image);
|
||||
|
||||
#endif /* __GIMP_IMAGE_SYMMETRY_H__ */
|
|
@ -56,6 +56,7 @@
|
|||
#include "gimpimage-preview.h"
|
||||
#include "gimpimage-private.h"
|
||||
#include "gimpimage-quick-mask.h"
|
||||
#include "gimpimage-symmetry.h"
|
||||
#include "gimpimage-undo.h"
|
||||
#include "gimpimage-undo-push.h"
|
||||
#include "gimpitemtree.h"
|
||||
|
@ -69,6 +70,7 @@
|
|||
#include "gimpprojection.h"
|
||||
#include "gimpsamplepoint.h"
|
||||
#include "gimpselection.h"
|
||||
#include "gimpsymmetry.h"
|
||||
#include "gimptempbuf.h"
|
||||
#include "gimptemplate.h"
|
||||
#include "gimpundostack.h"
|
||||
|
@ -130,7 +132,8 @@ enum
|
|||
PROP_BASE_TYPE,
|
||||
PROP_PRECISION,
|
||||
PROP_METADATA,
|
||||
PROP_BUFFER
|
||||
PROP_BUFFER,
|
||||
PROP_SYMMETRY
|
||||
};
|
||||
|
||||
|
||||
|
@ -624,6 +627,11 @@ gimp_image_class_init (GimpImageClass *klass)
|
|||
|
||||
g_object_class_override_property (object_class, PROP_BUFFER, "buffer");
|
||||
|
||||
g_object_class_install_property (object_class, PROP_SYMMETRY,
|
||||
g_param_spec_int ("symmetry",
|
||||
NULL, _("Symmetry"),
|
||||
G_TYPE_NONE, G_MAXINT, G_TYPE_NONE,
|
||||
GIMP_PARAM_READWRITE));
|
||||
g_type_class_add_private (klass, sizeof (GimpImagePrivate));
|
||||
}
|
||||
|
||||
|
@ -696,6 +704,9 @@ gimp_image_init (GimpImage *image)
|
|||
|
||||
private->projection = gimp_projection_new (GIMP_PROJECTABLE (image));
|
||||
|
||||
private->symmetries = NULL;
|
||||
private->selected_symmetry = NULL;
|
||||
|
||||
private->guides = NULL;
|
||||
private->grid = NULL;
|
||||
private->sample_points = NULL;
|
||||
|
@ -859,6 +870,42 @@ gimp_image_set_property (GObject *object,
|
|||
break;
|
||||
case PROP_METADATA:
|
||||
case PROP_BUFFER:
|
||||
break;
|
||||
case PROP_SYMMETRY:
|
||||
{
|
||||
GType type = g_value_get_int (value);
|
||||
|
||||
if (private->selected_symmetry)
|
||||
g_object_set (private->selected_symmetry,
|
||||
"active", FALSE,
|
||||
NULL);
|
||||
private->selected_symmetry = NULL;
|
||||
|
||||
if (type != G_TYPE_NONE)
|
||||
{
|
||||
GList *iter;
|
||||
|
||||
for (iter = private->symmetries; iter; iter = g_list_next (iter))
|
||||
{
|
||||
GimpSymmetry *sym = iter->data;
|
||||
if (g_type_is_a (sym->type, type))
|
||||
private->selected_symmetry = iter->data;
|
||||
}
|
||||
|
||||
if (private->selected_symmetry == NULL)
|
||||
{
|
||||
GimpSymmetry *sym;
|
||||
|
||||
sym = gimp_image_symmetry_new (image, type);
|
||||
gimp_image_symmetry_add (image, sym);
|
||||
private->selected_symmetry = sym;
|
||||
}
|
||||
g_object_set (private->selected_symmetry,
|
||||
"active", TRUE,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
|
@ -900,6 +947,11 @@ gimp_image_get_property (GObject *object,
|
|||
case PROP_BUFFER:
|
||||
g_value_set_object (value, gimp_image_get_buffer (GIMP_PICKABLE (image)));
|
||||
break;
|
||||
case PROP_SYMMETRY:
|
||||
g_value_set_int (value,
|
||||
private->selected_symmetry ?
|
||||
private->selected_symmetry->type : G_TYPE_NONE);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
|
@ -1050,6 +1102,12 @@ gimp_image_finalize (GObject *object)
|
|||
private->guides = NULL;
|
||||
}
|
||||
|
||||
if (private->symmetries)
|
||||
{
|
||||
g_list_free_full (private->symmetries, g_object_unref);
|
||||
private->symmetries = NULL;
|
||||
}
|
||||
|
||||
if (private->grid)
|
||||
{
|
||||
g_object_unref (private->grid);
|
||||
|
|
|
@ -0,0 +1,701 @@
|
|||
/* GIMP - The GNU Image Manipulation Program
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* gimpsymmetry-mirror.c
|
||||
* Copyright (C) 2015 Jehan <jehan@gimp.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <cairo.h>
|
||||
#include <gegl.h>
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
|
||||
#include "libgimpconfig/gimpconfig.h"
|
||||
|
||||
#include "core-types.h"
|
||||
|
||||
#include "gimp.h"
|
||||
#include "gimp-cairo.h"
|
||||
#include "gimpbrush.h"
|
||||
#include "gimpguide.h"
|
||||
#include "gimpimage.h"
|
||||
#include "gimpimage-guides.h"
|
||||
#include "gimpimage-symmetry.h"
|
||||
#include "gimpitem.h"
|
||||
#include "gimpsymmetry-mirror.h"
|
||||
|
||||
#include "gimp-intl.h"
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
|
||||
PROP_HORIZONTAL_SYMMETRY,
|
||||
PROP_VERTICAL_SYMMETRY,
|
||||
PROP_POINT_SYMMETRY,
|
||||
PROP_DISABLE_TRANSFORMATION,
|
||||
PROP_HORIZONTAL_POSITION,
|
||||
PROP_VERTICAL_POSITION
|
||||
};
|
||||
|
||||
/* Local function prototypes */
|
||||
|
||||
static void gimp_mirror_finalize (GObject *object);
|
||||
static void gimp_mirror_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec);
|
||||
static void gimp_mirror_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
|
||||
static void gimp_mirror_update_strokes (GimpSymmetry *mirror,
|
||||
GimpDrawable *drawable,
|
||||
GimpCoords *origin);
|
||||
static void gimp_mirror_prepare_operations (GimpMirror *mirror,
|
||||
gint paint_width,
|
||||
gint paint_height);
|
||||
static GeglNode * gimp_mirror_get_operation (GimpSymmetry *mirror,
|
||||
gint stroke,
|
||||
gint paint_width,
|
||||
gint paint_height);
|
||||
static void gimp_mirror_reset (GimpMirror *mirror);
|
||||
static void gimp_mirror_add_guide (GimpMirror *mirror,
|
||||
GimpOrientationType orientation);
|
||||
static void gimp_mirror_remove_guide (GimpMirror *mirror,
|
||||
GimpOrientationType orientation);
|
||||
static void gimp_mirror_guide_removed_cb (GObject *object,
|
||||
GimpMirror *mirror);
|
||||
static void gimp_mirror_guide_position_cb (GObject *object,
|
||||
GParamSpec *pspec,
|
||||
GimpMirror *mirror);
|
||||
static GParamSpec ** gimp_mirror_get_settings (GimpSymmetry *sym,
|
||||
gint *n_settings);
|
||||
static void gimp_mirror_active_changed (GimpSymmetry *sym);
|
||||
static void gimp_mirror_set_horizontal_symmetry (GimpMirror *mirror,
|
||||
gboolean active);
|
||||
static void gimp_mirror_set_vertical_symmetry (GimpMirror *mirror,
|
||||
gboolean active);
|
||||
static void gimp_mirror_set_point_symmetry (GimpMirror *mirror,
|
||||
gboolean active);
|
||||
|
||||
G_DEFINE_TYPE (GimpMirror, gimp_mirror, GIMP_TYPE_SYMMETRY)
|
||||
|
||||
#define parent_class gimp_mirror_parent_class
|
||||
|
||||
static void
|
||||
gimp_mirror_class_init (GimpMirrorClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GimpSymmetryClass *symmetry_class = GIMP_SYMMETRY_CLASS (klass);
|
||||
|
||||
object_class->finalize = gimp_mirror_finalize;
|
||||
object_class->set_property = gimp_mirror_set_property;
|
||||
object_class->get_property = gimp_mirror_get_property;
|
||||
|
||||
symmetry_class->label = _("Mirror");
|
||||
symmetry_class->update_strokes = gimp_mirror_update_strokes;
|
||||
symmetry_class->get_operation = gimp_mirror_get_operation;
|
||||
symmetry_class->get_settings = gimp_mirror_get_settings;
|
||||
symmetry_class->active_changed = gimp_mirror_active_changed;
|
||||
|
||||
/* Properties for user settings */
|
||||
GIMP_CONFIG_INSTALL_PROP_BOOLEAN (object_class, PROP_HORIZONTAL_SYMMETRY,
|
||||
"horizontal-symmetry",
|
||||
_("Horizontal Mirror"),
|
||||
FALSE,
|
||||
GIMP_PARAM_STATIC_STRINGS);
|
||||
GIMP_CONFIG_INSTALL_PROP_BOOLEAN (object_class, PROP_VERTICAL_SYMMETRY,
|
||||
"vertical-symmetry",
|
||||
_("Vertical Mirror"),
|
||||
FALSE,
|
||||
GIMP_PARAM_STATIC_STRINGS);
|
||||
GIMP_CONFIG_INSTALL_PROP_BOOLEAN (object_class, PROP_POINT_SYMMETRY,
|
||||
"point-symmetry",
|
||||
_("Central Symmetry"),
|
||||
FALSE,
|
||||
GIMP_PARAM_STATIC_STRINGS);
|
||||
GIMP_CONFIG_INSTALL_PROP_BOOLEAN (object_class, PROP_DISABLE_TRANSFORMATION,
|
||||
"disable-transformation",
|
||||
_("Disable Brush Transformation (faster)"),
|
||||
FALSE,
|
||||
GIMP_PARAM_STATIC_STRINGS);
|
||||
|
||||
/* Properties for XCF serialization only */
|
||||
GIMP_CONFIG_INSTALL_PROP_DOUBLE (object_class, PROP_HORIZONTAL_POSITION,
|
||||
"horizontal-position",
|
||||
_("Horizontal guide position"),
|
||||
0.0, G_MAXDOUBLE, 0.0,
|
||||
GIMP_PARAM_STATIC_STRINGS);
|
||||
GIMP_CONFIG_INSTALL_PROP_DOUBLE (object_class, PROP_VERTICAL_POSITION,
|
||||
"vertical-position",
|
||||
_("Vertical guide position"),
|
||||
0.0, G_MAXDOUBLE, 0.0,
|
||||
GIMP_PARAM_STATIC_STRINGS);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_mirror_init (GimpMirror *mirror)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_mirror_finalize (GObject *object)
|
||||
{
|
||||
GimpMirror *mirror = GIMP_MIRROR (object);
|
||||
|
||||
if (mirror->horizontal_guide)
|
||||
g_object_unref (mirror->horizontal_guide);
|
||||
mirror->horizontal_guide = NULL;
|
||||
|
||||
if (mirror->vertical_guide)
|
||||
g_object_unref (mirror->vertical_guide);
|
||||
mirror->vertical_guide = NULL;
|
||||
|
||||
if (mirror->horizontal_op)
|
||||
g_object_unref (mirror->horizontal_op);
|
||||
mirror->horizontal_op = NULL;
|
||||
|
||||
if (mirror->vertical_op)
|
||||
g_object_unref (mirror->vertical_op);
|
||||
mirror->vertical_op = NULL;
|
||||
|
||||
if (mirror->central_op)
|
||||
g_object_unref (mirror->central_op);
|
||||
mirror->central_op = NULL;
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_mirror_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GimpMirror *mirror = GIMP_MIRROR (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_HORIZONTAL_SYMMETRY:
|
||||
gimp_mirror_set_horizontal_symmetry (mirror,
|
||||
g_value_get_boolean (value));
|
||||
break;
|
||||
case PROP_VERTICAL_SYMMETRY:
|
||||
gimp_mirror_set_vertical_symmetry (mirror,
|
||||
g_value_get_boolean (value));
|
||||
break;
|
||||
case PROP_POINT_SYMMETRY:
|
||||
gimp_mirror_set_point_symmetry (mirror,
|
||||
g_value_get_boolean (value));
|
||||
break;
|
||||
case PROP_DISABLE_TRANSFORMATION:
|
||||
mirror->disable_transformation = g_value_get_boolean (value);
|
||||
break;
|
||||
case PROP_HORIZONTAL_POSITION:
|
||||
mirror->horizontal_position = g_value_get_double (value);
|
||||
if (mirror->horizontal_guide)
|
||||
gimp_guide_set_position (mirror->horizontal_guide,
|
||||
mirror->horizontal_position);
|
||||
break;
|
||||
case PROP_VERTICAL_POSITION:
|
||||
mirror->vertical_position = g_value_get_double (value);
|
||||
if (mirror->vertical_guide)
|
||||
gimp_guide_set_position (mirror->vertical_guide,
|
||||
mirror->vertical_position);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_mirror_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GimpMirror *mirror = GIMP_MIRROR (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_HORIZONTAL_SYMMETRY:
|
||||
g_value_set_boolean (value, mirror->horizontal_mirror);
|
||||
break;
|
||||
case PROP_VERTICAL_SYMMETRY:
|
||||
g_value_set_boolean (value, mirror->vertical_mirror);
|
||||
break;
|
||||
case PROP_POINT_SYMMETRY:
|
||||
g_value_set_boolean (value, mirror->point_symmetry);
|
||||
break;
|
||||
case PROP_DISABLE_TRANSFORMATION:
|
||||
g_value_set_boolean (value, mirror->disable_transformation);
|
||||
break;
|
||||
case PROP_HORIZONTAL_POSITION:
|
||||
g_value_set_double (value, mirror->horizontal_position);
|
||||
break;
|
||||
case PROP_VERTICAL_POSITION:
|
||||
g_value_set_double (value, mirror->vertical_position);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_mirror_update_strokes (GimpSymmetry *sym,
|
||||
GimpDrawable *drawable,
|
||||
GimpCoords *origin)
|
||||
{
|
||||
GList *strokes = NULL;
|
||||
GimpMirror *mirror = GIMP_MIRROR (sym);
|
||||
GimpCoords *coords;
|
||||
|
||||
g_list_free_full (sym->strokes, g_free);
|
||||
strokes = g_list_prepend (strokes,
|
||||
g_memdup (origin, sizeof (GimpCoords)));
|
||||
|
||||
if (mirror->horizontal_mirror)
|
||||
{
|
||||
coords = g_memdup (origin, sizeof (GimpCoords));
|
||||
coords->y = 2.0 * mirror->horizontal_position - origin->y;
|
||||
strokes = g_list_prepend (strokes, coords);
|
||||
}
|
||||
if (mirror->vertical_mirror)
|
||||
{
|
||||
coords = g_memdup (origin, sizeof (GimpCoords));
|
||||
coords->x = 2.0 * mirror->vertical_position - origin->x;
|
||||
strokes = g_list_prepend (strokes, coords);
|
||||
}
|
||||
if (mirror->point_symmetry)
|
||||
{
|
||||
coords = g_memdup (origin, sizeof (GimpCoords));
|
||||
coords->x = 2.0 * mirror->vertical_position - origin->x;
|
||||
coords->y = 2.0 * mirror->horizontal_position - origin->y;
|
||||
strokes = g_list_prepend (strokes, coords);
|
||||
}
|
||||
sym->strokes = g_list_reverse (strokes);
|
||||
|
||||
g_signal_emit_by_name (sym, "strokes-updated", sym->image);
|
||||
}
|
||||
|
||||
static void gimp_mirror_prepare_operations (GimpMirror *mirror,
|
||||
gint paint_width,
|
||||
gint paint_height)
|
||||
{
|
||||
if (paint_width == mirror->last_paint_width &&
|
||||
paint_height == mirror->last_paint_height)
|
||||
return;
|
||||
|
||||
mirror->last_paint_width = paint_width;
|
||||
mirror->last_paint_height = paint_height;
|
||||
|
||||
if (mirror->horizontal_op)
|
||||
g_object_unref (mirror->horizontal_op);
|
||||
|
||||
mirror->horizontal_op = gegl_node_new_child (NULL,
|
||||
"operation", "gegl:reflect",
|
||||
"origin-x", 0.0,
|
||||
"origin-y",
|
||||
(gdouble) paint_height / 2.0,
|
||||
"x",
|
||||
1.0,
|
||||
"y",
|
||||
0.0,
|
||||
NULL);
|
||||
|
||||
if (mirror->vertical_op)
|
||||
g_object_unref (mirror->vertical_op);
|
||||
|
||||
mirror->vertical_op = gegl_node_new_child (NULL,
|
||||
"operation", "gegl:reflect",
|
||||
"origin-x",
|
||||
(gdouble) paint_width / 2.0,
|
||||
"origin-y", 0.0,
|
||||
"x",
|
||||
0.0,
|
||||
"y",
|
||||
1.0,
|
||||
NULL);
|
||||
|
||||
if (mirror->central_op)
|
||||
g_object_unref (mirror->central_op);
|
||||
|
||||
mirror->central_op = gegl_node_new_child (NULL,
|
||||
"operation", "gegl:rotate",
|
||||
"origin-x",
|
||||
(gdouble) paint_width / 2.0,
|
||||
"origin-y",
|
||||
(gdouble) paint_height / 2.0,
|
||||
"degrees",
|
||||
180.0,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static GeglNode *
|
||||
gimp_mirror_get_operation (GimpSymmetry *sym,
|
||||
gint stroke,
|
||||
gint paint_width,
|
||||
gint paint_height)
|
||||
{
|
||||
GimpMirror *mirror = GIMP_MIRROR (sym);
|
||||
GeglNode *op;
|
||||
|
||||
g_return_val_if_fail (stroke >= 0 &&
|
||||
stroke < g_list_length (sym->strokes), NULL);
|
||||
|
||||
gimp_mirror_prepare_operations (mirror, paint_width, paint_height);
|
||||
|
||||
if (mirror->disable_transformation || stroke == 0 ||
|
||||
paint_width == 0 || paint_height == 0)
|
||||
op = NULL;
|
||||
else if (stroke == 1 && mirror->horizontal_mirror)
|
||||
op = g_object_ref (mirror->horizontal_op);
|
||||
else if ((stroke == 2 && mirror->horizontal_mirror &&
|
||||
mirror->vertical_mirror) ||
|
||||
(stroke == 1 && mirror->vertical_mirror &&
|
||||
! mirror->horizontal_mirror))
|
||||
op = g_object_ref (mirror->vertical_op);
|
||||
else
|
||||
op = g_object_ref (mirror->central_op);
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_mirror_reset (GimpMirror *mirror)
|
||||
{
|
||||
GimpSymmetry *sym;
|
||||
|
||||
g_return_if_fail (GIMP_IS_MIRROR (mirror));
|
||||
|
||||
sym = GIMP_SYMMETRY (mirror);
|
||||
|
||||
if (sym->origin)
|
||||
{
|
||||
gimp_symmetry_set_origin (sym, sym->drawable,
|
||||
sym->origin);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_mirror_add_guide (GimpMirror *mirror,
|
||||
GimpOrientationType orientation)
|
||||
{
|
||||
static GimpRGB normal_fg = { 1.0, 1.0, 1.0, 1.0 };
|
||||
static GimpRGB normal_bg = { 0.0, 1.0, 0.0, 1.0 };
|
||||
static GimpRGB active_fg = { 0.0, 1.0, 0.0, 1.0 };
|
||||
static GimpRGB active_bg = { 1.0, 0.0, 0.0, 1.0 };
|
||||
GimpSymmetry *sym;
|
||||
GimpImage *image;
|
||||
Gimp *gimp;
|
||||
GimpGuide *guide;
|
||||
gint position;
|
||||
|
||||
g_return_if_fail (GIMP_IS_MIRROR (mirror));
|
||||
|
||||
sym = GIMP_SYMMETRY (mirror);
|
||||
image = sym->image;
|
||||
gimp = GIMP (image->gimp);
|
||||
|
||||
guide = gimp_guide_custom_new (orientation,
|
||||
gimp->next_guide_ID++,
|
||||
&normal_fg, &normal_bg,
|
||||
&active_fg, &active_bg,
|
||||
1.0);
|
||||
|
||||
if (orientation == GIMP_ORIENTATION_HORIZONTAL)
|
||||
{
|
||||
mirror->horizontal_guide = guide;
|
||||
|
||||
/* Mirror guide position at first activation is at canvas middle. */
|
||||
if (mirror->horizontal_position < 1.0)
|
||||
mirror->horizontal_position = (gdouble) gimp_image_get_height (image) / 2.0;
|
||||
position = (gint) mirror->horizontal_position;
|
||||
}
|
||||
else
|
||||
{
|
||||
mirror->vertical_guide = guide;
|
||||
|
||||
/* Mirror guide position at first activation is at canvas middle. */
|
||||
if (mirror->vertical_position < 1.0)
|
||||
mirror->vertical_position = (gdouble) gimp_image_get_width (image) / 2.0;
|
||||
position = (gint) mirror->vertical_position;
|
||||
}
|
||||
g_signal_connect (G_OBJECT (guide), "removed",
|
||||
G_CALLBACK (gimp_mirror_guide_removed_cb),
|
||||
mirror);
|
||||
|
||||
gimp_image_add_guide (image, guide,
|
||||
(gint) position);
|
||||
|
||||
g_signal_connect (G_OBJECT (guide), "notify::position",
|
||||
G_CALLBACK (gimp_mirror_guide_position_cb),
|
||||
mirror);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_mirror_remove_guide (GimpMirror *mirror,
|
||||
GimpOrientationType orientation)
|
||||
{
|
||||
GimpSymmetry *sym;
|
||||
GimpImage *image;
|
||||
GimpGuide *guide;
|
||||
|
||||
g_return_if_fail (GIMP_IS_MIRROR (mirror));
|
||||
|
||||
sym = GIMP_SYMMETRY (mirror);
|
||||
image = sym->image;
|
||||
guide = (orientation == GIMP_ORIENTATION_HORIZONTAL) ?
|
||||
mirror->horizontal_guide : mirror->vertical_guide;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (G_OBJECT (guide),
|
||||
gimp_mirror_guide_removed_cb,
|
||||
mirror);
|
||||
g_signal_handlers_disconnect_by_func (G_OBJECT (guide),
|
||||
gimp_mirror_guide_position_cb,
|
||||
mirror);
|
||||
gimp_image_remove_guide (image, guide, FALSE);
|
||||
g_object_unref (guide);
|
||||
|
||||
if (orientation == GIMP_ORIENTATION_HORIZONTAL)
|
||||
mirror->horizontal_guide = NULL;
|
||||
else
|
||||
mirror->vertical_guide = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_mirror_guide_removed_cb (GObject *object,
|
||||
GimpMirror *mirror)
|
||||
{
|
||||
GimpSymmetry *symmetry = GIMP_SYMMETRY (mirror);
|
||||
|
||||
g_signal_handlers_disconnect_by_func (object,
|
||||
gimp_mirror_guide_removed_cb,
|
||||
mirror);
|
||||
g_signal_handlers_disconnect_by_func (object,
|
||||
gimp_mirror_guide_position_cb,
|
||||
mirror);
|
||||
if (GIMP_GUIDE (object) == mirror->horizontal_guide)
|
||||
{
|
||||
g_object_unref (mirror->horizontal_guide);
|
||||
mirror->horizontal_guide = NULL;
|
||||
|
||||
mirror->horizontal_mirror = FALSE;
|
||||
mirror->point_symmetry = FALSE;
|
||||
mirror->horizontal_position = 0.0;
|
||||
|
||||
if (mirror->vertical_guide &&
|
||||
! mirror->vertical_mirror)
|
||||
{
|
||||
g_signal_handlers_disconnect_by_func (G_OBJECT (mirror->vertical_guide),
|
||||
gimp_mirror_guide_removed_cb,
|
||||
mirror);
|
||||
g_signal_handlers_disconnect_by_func (G_OBJECT (mirror->vertical_guide),
|
||||
gimp_mirror_guide_position_cb,
|
||||
mirror);
|
||||
gimp_image_remove_guide (symmetry->image,
|
||||
mirror->vertical_guide,
|
||||
FALSE);
|
||||
g_clear_object (&mirror->vertical_guide);
|
||||
}
|
||||
}
|
||||
else if (GIMP_GUIDE (object) == mirror->vertical_guide)
|
||||
{
|
||||
g_object_unref (mirror->vertical_guide);
|
||||
mirror->vertical_guide = NULL;
|
||||
|
||||
mirror->vertical_mirror = FALSE;
|
||||
mirror->point_symmetry = FALSE;
|
||||
mirror->vertical_position = 0.0;
|
||||
|
||||
if (mirror->horizontal_guide &&
|
||||
! mirror->horizontal_mirror)
|
||||
{
|
||||
g_signal_handlers_disconnect_by_func (G_OBJECT (mirror->horizontal_guide),
|
||||
gimp_mirror_guide_removed_cb,
|
||||
mirror);
|
||||
g_signal_handlers_disconnect_by_func (G_OBJECT (mirror->horizontal_guide),
|
||||
gimp_mirror_guide_position_cb,
|
||||
mirror);
|
||||
gimp_image_remove_guide (symmetry->image,
|
||||
mirror->horizontal_guide,
|
||||
FALSE);
|
||||
g_clear_object (&mirror->horizontal_guide);
|
||||
}
|
||||
}
|
||||
|
||||
if (mirror->horizontal_guide == NULL &&
|
||||
mirror->vertical_guide == NULL)
|
||||
{
|
||||
gimp_image_symmetry_remove (symmetry->image,
|
||||
GIMP_SYMMETRY (mirror));
|
||||
}
|
||||
else
|
||||
{
|
||||
gimp_mirror_reset (mirror);
|
||||
g_signal_emit_by_name (mirror, "update-ui",
|
||||
GIMP_SYMMETRY (mirror)->image);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_mirror_guide_position_cb (GObject *object,
|
||||
GParamSpec *pspec,
|
||||
GimpMirror *mirror)
|
||||
{
|
||||
GimpGuide *guide;
|
||||
|
||||
guide = GIMP_GUIDE (object);
|
||||
|
||||
if (guide == mirror->horizontal_guide)
|
||||
{
|
||||
mirror->horizontal_position = (gdouble) gimp_guide_get_position (guide);
|
||||
}
|
||||
else if (guide == mirror->vertical_guide)
|
||||
{
|
||||
mirror->vertical_position = (gdouble) gimp_guide_get_position (guide);
|
||||
}
|
||||
}
|
||||
|
||||
static GParamSpec **
|
||||
gimp_mirror_get_settings (GimpSymmetry *sym,
|
||||
gint *n_settings)
|
||||
{
|
||||
GParamSpec **pspecs;
|
||||
|
||||
*n_settings = 5;
|
||||
pspecs = g_new (GParamSpec*, 5);
|
||||
|
||||
pspecs[0] = g_object_class_find_property (G_OBJECT_GET_CLASS (sym),
|
||||
"horizontal-symmetry");
|
||||
pspecs[1] = g_object_class_find_property (G_OBJECT_GET_CLASS (sym),
|
||||
"vertical-symmetry");
|
||||
pspecs[2] = g_object_class_find_property (G_OBJECT_GET_CLASS (sym),
|
||||
"point-symmetry");
|
||||
pspecs[3] = NULL;
|
||||
pspecs[4] = g_object_class_find_property (G_OBJECT_GET_CLASS (sym),
|
||||
"disable-transformation");
|
||||
|
||||
return pspecs;
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_mirror_active_changed (GimpSymmetry *sym)
|
||||
{
|
||||
GimpMirror *mirror = GIMP_MIRROR (sym);
|
||||
|
||||
if (sym->active)
|
||||
{
|
||||
if ((mirror->horizontal_mirror || mirror->point_symmetry) &&
|
||||
! mirror->horizontal_guide)
|
||||
gimp_mirror_add_guide (mirror, GIMP_ORIENTATION_HORIZONTAL);
|
||||
if ((mirror->vertical_mirror || mirror->point_symmetry) &&
|
||||
! mirror->vertical_guide)
|
||||
gimp_mirror_add_guide (mirror, GIMP_ORIENTATION_VERTICAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mirror->horizontal_guide)
|
||||
gimp_mirror_remove_guide (mirror, GIMP_ORIENTATION_HORIZONTAL);
|
||||
if (mirror->vertical_guide)
|
||||
gimp_mirror_remove_guide (mirror, GIMP_ORIENTATION_VERTICAL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_mirror_set_horizontal_symmetry (GimpMirror *mirror,
|
||||
gboolean active)
|
||||
{
|
||||
g_return_if_fail (GIMP_IS_MIRROR (mirror));
|
||||
|
||||
if (active == mirror->horizontal_mirror)
|
||||
return;
|
||||
|
||||
mirror->horizontal_mirror = active;
|
||||
|
||||
if (active)
|
||||
{
|
||||
if (! mirror->horizontal_guide)
|
||||
gimp_mirror_add_guide (mirror, GIMP_ORIENTATION_HORIZONTAL);
|
||||
}
|
||||
else if (! mirror->point_symmetry)
|
||||
gimp_mirror_remove_guide (mirror, GIMP_ORIENTATION_HORIZONTAL);
|
||||
|
||||
gimp_mirror_reset (mirror);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_mirror_set_vertical_symmetry (GimpMirror *mirror,
|
||||
gboolean active)
|
||||
{
|
||||
g_return_if_fail (GIMP_IS_MIRROR (mirror));
|
||||
|
||||
if (active == mirror->vertical_mirror)
|
||||
return;
|
||||
|
||||
mirror->vertical_mirror = active;
|
||||
|
||||
if (active)
|
||||
{
|
||||
if (! mirror->vertical_guide)
|
||||
gimp_mirror_add_guide (mirror, GIMP_ORIENTATION_VERTICAL);
|
||||
}
|
||||
else if (! mirror->point_symmetry)
|
||||
gimp_mirror_remove_guide (mirror, GIMP_ORIENTATION_VERTICAL);
|
||||
|
||||
gimp_mirror_reset (mirror);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_mirror_set_point_symmetry (GimpMirror *mirror,
|
||||
gboolean active)
|
||||
{
|
||||
g_return_if_fail (GIMP_IS_MIRROR (mirror));
|
||||
|
||||
if (active == mirror->point_symmetry)
|
||||
return;
|
||||
|
||||
mirror->point_symmetry = active;
|
||||
|
||||
if (active)
|
||||
{
|
||||
/* Show the horizontal guide unless already shown */
|
||||
if (! mirror->horizontal_guide)
|
||||
gimp_mirror_add_guide (mirror, GIMP_ORIENTATION_HORIZONTAL);
|
||||
|
||||
/* Show the vertical guide unless already shown */
|
||||
if (! mirror->vertical_guide)
|
||||
gimp_mirror_add_guide (mirror, GIMP_ORIENTATION_VERTICAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Remove the horizontal guide unless needed by horizontal mirror */
|
||||
if (! mirror->horizontal_mirror)
|
||||
gimp_mirror_remove_guide (mirror, GIMP_ORIENTATION_HORIZONTAL);
|
||||
/* Remove the vertical guide unless needed by vertical mirror */
|
||||
if (! mirror->vertical_mirror)
|
||||
gimp_mirror_remove_guide (mirror, GIMP_ORIENTATION_VERTICAL);
|
||||
}
|
||||
|
||||
gimp_mirror_reset (mirror);
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/* GIMP - The GNU Image Manipulation Program
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* gimpsymmetry-mirror.h
|
||||
* Copyright (C) 2015 Jehan <jehan@gimp.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __GIMP_MIRROR_H__
|
||||
#define __GIMP_MIRROR_H__
|
||||
|
||||
|
||||
#include "gimpsymmetry.h"
|
||||
|
||||
|
||||
#define GIMP_TYPE_MIRROR (gimp_mirror_get_type ())
|
||||
#define GIMP_MIRROR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_MIRROR, GimpMirror))
|
||||
#define GIMP_MIRROR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_MIRROR, GimpMirrorClass))
|
||||
#define GIMP_IS_MIRROR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_MIRROR))
|
||||
#define GIMP_IS_MIRROR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_MIRROR))
|
||||
#define GIMP_MIRROR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_MIRROR, GimpMirrorClass))
|
||||
|
||||
typedef struct _GimpMirrorClass GimpMirrorClass;
|
||||
|
||||
struct _GimpMirror
|
||||
{
|
||||
GimpSymmetry parent_instance;
|
||||
|
||||
gboolean horizontal_mirror;
|
||||
gboolean vertical_mirror;
|
||||
gboolean point_symmetry;
|
||||
gboolean disable_transformation;
|
||||
|
||||
gdouble horizontal_position;
|
||||
gdouble vertical_position;
|
||||
GimpGuide *horizontal_guide;
|
||||
GimpGuide *vertical_guide;
|
||||
|
||||
/* Cached data */
|
||||
gint last_paint_width;
|
||||
gint last_paint_height;
|
||||
GeglNode *horizontal_op;
|
||||
GeglNode *vertical_op;
|
||||
GeglNode *central_op;
|
||||
};
|
||||
|
||||
struct _GimpMirrorClass
|
||||
{
|
||||
GimpSymmetryClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
GType gimp_mirror_get_type (void) G_GNUC_CONST;
|
||||
|
||||
#endif /* __GIMP_MIRROR_H__ */
|
|
@ -0,0 +1,451 @@
|
|||
/* GIMP - The GNU Image Manipulation Program
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* gimpsymmetry.c
|
||||
* Copyright (C) 2015 Jehan <jehan@gimp.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <gegl.h>
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
|
||||
#include "libgimpbase/gimpbase.h"
|
||||
#include "libgimpconfig/gimpconfig.h"
|
||||
|
||||
#include "core-types.h"
|
||||
|
||||
#include "gimpdrawable.h"
|
||||
#include "gimpimage.h"
|
||||
#include "gimpimage-symmetry.h"
|
||||
#include "gimpitem.h"
|
||||
#include "gimpsymmetry.h"
|
||||
|
||||
#include "gimp-intl.h"
|
||||
|
||||
enum
|
||||
{
|
||||
STROKES_UPDATED,
|
||||
UPDATE_UI,
|
||||
ACTIVE_CHANGED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_IMAGE,
|
||||
PROP_ACTIVE,
|
||||
};
|
||||
|
||||
/* Local function prototypes */
|
||||
|
||||
static void gimp_symmetry_finalize (GObject *object);
|
||||
static void gimp_symmetry_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec);
|
||||
static void gimp_symmetry_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
|
||||
static void
|
||||
gimp_symmetry_real_update_strokes (GimpSymmetry *sym,
|
||||
GimpDrawable *drawable,
|
||||
GimpCoords *origin);
|
||||
static GeglNode *
|
||||
gimp_symmetry_real_get_op (GimpSymmetry *sym,
|
||||
gint stroke,
|
||||
gint paint_width,
|
||||
gint paint_height);
|
||||
static GParamSpec **
|
||||
gimp_symmetry_real_get_settings (GimpSymmetry *sym,
|
||||
gint *n_properties);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GimpSymmetry, gimp_symmetry, GIMP_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, NULL))
|
||||
|
||||
|
||||
#define parent_class gimp_symmetry_parent_class
|
||||
|
||||
static guint gimp_symmetry_signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void
|
||||
gimp_symmetry_class_init (GimpSymmetryClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
/* This signal should likely be emitted at the end of update_strokes()
|
||||
* if stroke coordinates were changed. */
|
||||
gimp_symmetry_signals[STROKES_UPDATED] =
|
||||
g_signal_new ("strokes-updated",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_FIRST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
NULL,
|
||||
G_TYPE_NONE,
|
||||
1, GIMP_TYPE_IMAGE);
|
||||
/* This signal should be emitted when you request a change in
|
||||
* the settings UI. For instance adding some settings (therefore having a
|
||||
* dynamic UI), or changing scale min/max extremes, etc. */
|
||||
gimp_symmetry_signals[UPDATE_UI] =
|
||||
g_signal_new ("update-ui",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_FIRST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
NULL,
|
||||
G_TYPE_NONE,
|
||||
1, GIMP_TYPE_IMAGE);
|
||||
|
||||
gimp_symmetry_signals[ACTIVE_CHANGED] =
|
||||
g_signal_new ("active-changed",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_FIRST,
|
||||
G_STRUCT_OFFSET (GimpSymmetryClass, active_changed),
|
||||
NULL, NULL,
|
||||
NULL,
|
||||
G_TYPE_NONE, 0);
|
||||
|
||||
object_class->finalize = gimp_symmetry_finalize;
|
||||
object_class->set_property = gimp_symmetry_set_property;
|
||||
object_class->get_property = gimp_symmetry_get_property;
|
||||
|
||||
klass->label = _("None");
|
||||
klass->update_strokes = gimp_symmetry_real_update_strokes;
|
||||
klass->get_operation = gimp_symmetry_real_get_op;
|
||||
klass->get_settings = gimp_symmetry_real_get_settings;
|
||||
klass->active_changed = NULL;
|
||||
|
||||
g_object_class_install_property (object_class, PROP_IMAGE,
|
||||
g_param_spec_object ("image",
|
||||
NULL, NULL,
|
||||
GIMP_TYPE_IMAGE,
|
||||
GIMP_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY));
|
||||
GIMP_CONFIG_INSTALL_PROP_BOOLEAN (object_class, PROP_ACTIVE,
|
||||
"active",
|
||||
_("Activate symmetry painting"),
|
||||
FALSE,
|
||||
GIMP_PARAM_STATIC_STRINGS);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gimp_symmetry_init (GimpSymmetry *sym)
|
||||
{
|
||||
sym->type = G_TYPE_NONE;
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_symmetry_finalize (GObject *object)
|
||||
{
|
||||
GimpSymmetry *sym = GIMP_SYMMETRY (object);
|
||||
|
||||
if (sym->drawable)
|
||||
g_object_unref (sym->drawable);
|
||||
|
||||
g_free (sym->origin);
|
||||
sym->origin = NULL;
|
||||
|
||||
g_list_free_full (sym->strokes, g_free);
|
||||
sym->strokes = NULL;
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_symmetry_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GimpSymmetry *sym = GIMP_SYMMETRY (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_IMAGE:
|
||||
sym->image = g_value_get_object (value);
|
||||
break;
|
||||
case PROP_ACTIVE:
|
||||
sym->active = g_value_get_boolean (value);
|
||||
g_signal_emit (sym, gimp_symmetry_signals[ACTIVE_CHANGED], 0,
|
||||
sym->active);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_symmetry_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GimpSymmetry *sym = GIMP_SYMMETRY (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_IMAGE:
|
||||
g_value_set_object (value, sym->image);
|
||||
break;
|
||||
case PROP_ACTIVE:
|
||||
g_value_set_boolean (value, sym->active);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_symmetry_real_update_strokes (GimpSymmetry *sym,
|
||||
GimpDrawable *drawable,
|
||||
GimpCoords *origin)
|
||||
{
|
||||
/* The basic symmetry just uses the origin as is. */
|
||||
sym->strokes = g_list_prepend (sym->strokes,
|
||||
g_memdup (origin, sizeof (GimpCoords)));
|
||||
}
|
||||
|
||||
static GeglNode *
|
||||
gimp_symmetry_real_get_op (GimpSymmetry *sym,
|
||||
gint stroke,
|
||||
gint paint_width,
|
||||
gint paint_height)
|
||||
{
|
||||
/* The basic symmetry just returns NULL, since no transformation of the
|
||||
* brush painting happen. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GParamSpec **
|
||||
gimp_symmetry_real_get_settings (GimpSymmetry *sym,
|
||||
gint *n_properties)
|
||||
{
|
||||
*n_properties = 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/***** Public Functions *****/
|
||||
|
||||
/**
|
||||
* gimp_symmetry_set_origin:
|
||||
* @sym: the #GimpSymmetry
|
||||
* @drawable: the #GimpDrawable where painting will happen
|
||||
* @origin: new base coordinates.
|
||||
*
|
||||
* Set the symmetry to new origin coordinates and drawable.
|
||||
**/
|
||||
void
|
||||
gimp_symmetry_set_origin (GimpSymmetry *sym,
|
||||
GimpDrawable *drawable,
|
||||
GimpCoords *origin)
|
||||
{
|
||||
g_return_if_fail (GIMP_IS_SYMMETRY (sym));
|
||||
g_return_if_fail (GIMP_IS_DRAWABLE (drawable));
|
||||
g_return_if_fail (gimp_item_get_image (GIMP_ITEM (drawable)) == sym->image);
|
||||
|
||||
if (drawable != sym->drawable)
|
||||
{
|
||||
if (sym->drawable)
|
||||
g_object_unref (sym->drawable);
|
||||
sym->drawable = g_object_ref (drawable);
|
||||
}
|
||||
|
||||
if (origin != sym->origin)
|
||||
{
|
||||
g_free (sym->origin);
|
||||
sym->origin = g_memdup (origin, sizeof (GimpCoords));
|
||||
}
|
||||
|
||||
g_list_free_full (sym->strokes, g_free);
|
||||
sym->strokes = NULL;
|
||||
|
||||
GIMP_SYMMETRY_GET_CLASS (sym)->update_strokes (sym,
|
||||
drawable,
|
||||
origin);
|
||||
}
|
||||
|
||||
/**
|
||||
* gimp_symmetry_get_origin:
|
||||
* @sym: the #GimpSymmetry
|
||||
*
|
||||
* Returns: the origin stroke coordinates.
|
||||
* The returned value is owned by the #GimpSymmetry and must not be freed.
|
||||
**/
|
||||
GimpCoords *
|
||||
gimp_symmetry_get_origin (GimpSymmetry *sym)
|
||||
{
|
||||
g_return_val_if_fail (GIMP_IS_SYMMETRY (sym), NULL);
|
||||
|
||||
return sym->origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* gimp_symmetry_get_size:
|
||||
* @sym: the #GimpSymmetry
|
||||
*
|
||||
* Returns: the total number of strokes.
|
||||
**/
|
||||
gint
|
||||
gimp_symmetry_get_size (GimpSymmetry *sym)
|
||||
{
|
||||
g_return_val_if_fail (GIMP_IS_SYMMETRY (sym), 0);
|
||||
|
||||
return g_list_length (sym->strokes);
|
||||
}
|
||||
|
||||
/**
|
||||
* gimp_symmetry_get_coords:
|
||||
* @sym: the #GimpSymmetry
|
||||
* @stroke: the stroke number
|
||||
*
|
||||
* Returns: the coordinates of the stroke number @stroke.
|
||||
* The returned value is owned by the #GimpSymmetry and must not be freed.
|
||||
**/
|
||||
GimpCoords *
|
||||
gimp_symmetry_get_coords (GimpSymmetry *sym,
|
||||
gint stroke)
|
||||
{
|
||||
g_return_val_if_fail (GIMP_IS_SYMMETRY (sym), NULL);
|
||||
|
||||
return g_list_nth_data (sym->strokes, stroke);
|
||||
}
|
||||
|
||||
/**
|
||||
* gimp_symmetry_get_operation:
|
||||
* @sym: the #GimpSymmetry
|
||||
* @stroke: the stroke number
|
||||
* @paint_width: the width of the painting area
|
||||
* @paint_height: the height of the painting area
|
||||
*
|
||||
* Returns: the operation to apply to the paint buffer for stroke number @stroke.
|
||||
* NULL means to copy the original stroke as-is.
|
||||
**/
|
||||
GeglNode *
|
||||
gimp_symmetry_get_operation (GimpSymmetry *sym,
|
||||
gint stroke,
|
||||
gint paint_width,
|
||||
gint paint_height)
|
||||
{
|
||||
g_return_val_if_fail (GIMP_IS_SYMMETRY (sym), NULL);
|
||||
|
||||
return GIMP_SYMMETRY_GET_CLASS (sym)->get_operation (sym,
|
||||
stroke,
|
||||
paint_width,
|
||||
paint_height);
|
||||
}
|
||||
|
||||
/**
|
||||
* gimp_symmetry_get_settings:
|
||||
* @sym: the #GimpSymmetry
|
||||
* @n_properties: the number of properties in the returned array
|
||||
*
|
||||
* Returns: an array of the symmetry properties which are supposed to
|
||||
* be settable by the user.
|
||||
* The returned array must be freed by the caller.
|
||||
**/
|
||||
GParamSpec **
|
||||
gimp_symmetry_get_settings (GimpSymmetry *sym,
|
||||
gint *n_properties)
|
||||
{
|
||||
g_return_val_if_fail (GIMP_IS_SYMMETRY (sym), NULL);
|
||||
|
||||
return GIMP_SYMMETRY_GET_CLASS (sym)->get_settings (sym,
|
||||
n_properties);
|
||||
}
|
||||
|
||||
/*
|
||||
* gimp_symmetry_parasite_name:
|
||||
* @type: the #GimpSymmetry's #GType
|
||||
*
|
||||
* Returns: a newly allocated string.
|
||||
*/
|
||||
gchar *
|
||||
gimp_symmetry_parasite_name (GType type)
|
||||
{
|
||||
GimpSymmetryClass *klass;
|
||||
|
||||
klass = g_type_class_ref (type);
|
||||
|
||||
return g_strconcat ("gimp-image-symmetry:", klass->label, NULL);
|
||||
}
|
||||
|
||||
GimpParasite *
|
||||
gimp_symmetry_to_parasite (const GimpSymmetry *sym)
|
||||
{
|
||||
GimpParasite *parasite;
|
||||
gchar *parasite_name;
|
||||
gchar *str;
|
||||
|
||||
g_return_val_if_fail (GIMP_IS_SYMMETRY (sym), NULL);
|
||||
|
||||
str = gimp_config_serialize_to_string (GIMP_CONFIG (sym), NULL);
|
||||
g_return_val_if_fail (str != NULL, NULL);
|
||||
|
||||
parasite_name = gimp_symmetry_parasite_name (sym->type);
|
||||
parasite = gimp_parasite_new (parasite_name,
|
||||
GIMP_PARASITE_PERSISTENT,
|
||||
strlen (str) + 1, str);
|
||||
g_free (parasite_name);
|
||||
g_free (str);
|
||||
|
||||
return parasite;
|
||||
}
|
||||
|
||||
GimpSymmetry *
|
||||
gimp_symmetry_from_parasite (const GimpParasite *parasite,
|
||||
GimpImage *image,
|
||||
GType type)
|
||||
{
|
||||
GimpSymmetry *symmetry;
|
||||
gchar *parasite_name;
|
||||
const gchar *str;
|
||||
GError *error = NULL;
|
||||
|
||||
parasite_name = gimp_symmetry_parasite_name (type);
|
||||
|
||||
g_return_val_if_fail (parasite != NULL, NULL);
|
||||
g_return_val_if_fail (strcmp (gimp_parasite_name (parasite),
|
||||
parasite_name) == 0,
|
||||
NULL);
|
||||
g_free (parasite_name);
|
||||
|
||||
str = gimp_parasite_data (parasite);
|
||||
g_return_val_if_fail (str != NULL, NULL);
|
||||
|
||||
symmetry = gimp_image_symmetry_new (image, type);
|
||||
|
||||
if (! gimp_config_deserialize_string (GIMP_CONFIG (symmetry),
|
||||
str,
|
||||
gimp_parasite_data_size (parasite),
|
||||
NULL,
|
||||
&error))
|
||||
{
|
||||
g_warning ("Failed to deserialize symmetry parasite: %s", error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
return symmetry;
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/* GIMP - The GNU Image Manipulation Program
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* gimpsymmetry.h
|
||||
* Copyright (C) 2015 Jehan <jehan@gimp.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __GIMP_SYMMETRY_H__
|
||||
#define __GIMP_SYMMETRY_H__
|
||||
|
||||
|
||||
#include "gimpobject.h"
|
||||
|
||||
|
||||
#define GIMP_TYPE_SYMMETRY (gimp_symmetry_get_type ())
|
||||
#define GIMP_SYMMETRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_SYMMETRY, GimpSymmetry))
|
||||
#define GIMP_SYMMETRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_SYMMETRY, GimpSymmetryClass))
|
||||
#define GIMP_IS_SYMMETRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_SYMMETRY))
|
||||
#define GIMP_IS_SYMMETRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_SYMMETRY))
|
||||
#define GIMP_SYMMETRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_SYMMETRY, GimpSymmetryClass))
|
||||
|
||||
typedef struct _GimpSymmetryClass GimpSymmetryClass;
|
||||
|
||||
struct _GimpSymmetry
|
||||
{
|
||||
GimpObject parent_instance;
|
||||
|
||||
GimpImage *image;
|
||||
GimpDrawable *drawable;
|
||||
GimpCoords *origin;
|
||||
gboolean active;
|
||||
|
||||
GList *strokes;
|
||||
|
||||
GType type;
|
||||
};
|
||||
|
||||
struct _GimpSymmetryClass
|
||||
{
|
||||
GimpObjectClass parent_class;
|
||||
|
||||
const gchar * label;
|
||||
|
||||
/* Virtual functions */
|
||||
void (* update_strokes) (GimpSymmetry *symmetry,
|
||||
GimpDrawable *drawable,
|
||||
GimpCoords *origin);
|
||||
GeglNode * (* get_operation) (GimpSymmetry *symmetry,
|
||||
gint stroke,
|
||||
gint paint_width,
|
||||
gint paint_height);
|
||||
GParamSpec **
|
||||
(* get_settings) (GimpSymmetry *symmetry,
|
||||
gint *n_properties);
|
||||
void (* active_changed) (GimpSymmetry *symmetry);
|
||||
};
|
||||
|
||||
|
||||
GType gimp_symmetry_get_type (void) G_GNUC_CONST;
|
||||
|
||||
void gimp_symmetry_set_origin (GimpSymmetry *symmetry,
|
||||
GimpDrawable *drawable,
|
||||
GimpCoords *origin);
|
||||
|
||||
GimpCoords * gimp_symmetry_get_origin (GimpSymmetry *symmetry);
|
||||
gint gimp_symmetry_get_size (GimpSymmetry *symmetry);
|
||||
GimpCoords * gimp_symmetry_get_coords (GimpSymmetry *symmetry,
|
||||
gint stroke);
|
||||
GeglNode * gimp_symmetry_get_operation (GimpSymmetry *symmetry,
|
||||
gint stroke,
|
||||
gint paint_width,
|
||||
gint paint_height);
|
||||
GParamSpec ** gimp_symmetry_get_settings (GimpSymmetry *symmetry,
|
||||
gint *n_properties);
|
||||
|
||||
gchar * gimp_symmetry_parasite_name (GType type);
|
||||
GimpParasite * gimp_symmetry_to_parasite (const GimpSymmetry *symmetry);
|
||||
GimpSymmetry * gimp_symmetry_from_parasite (const GimpParasite *parasite,
|
||||
GimpImage *image,
|
||||
GType type);
|
||||
#endif /* __GIMP_SYMMETRY_H__ */
|
|
@ -53,6 +53,7 @@
|
|||
#include "widgets/gimppatternfactoryview.h"
|
||||
#include "widgets/gimpsamplepointeditor.h"
|
||||
#include "widgets/gimpselectioneditor.h"
|
||||
#include "widgets/gimpsymmetryeditor.h"
|
||||
#include "widgets/gimptemplateview.h"
|
||||
#include "widgets/gimptoolbox.h"
|
||||
#include "widgets/gimptooloptionseditor.h"
|
||||
|
@ -736,6 +737,17 @@ dialogs_selection_editor_new (GimpDialogFactory *factory,
|
|||
return gimp_selection_editor_new (gimp_dialog_factory_get_menu_factory (factory));
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
dialogs_symmetry_editor_new (GimpDialogFactory *factory,
|
||||
GimpContext *context,
|
||||
GimpUIManager *ui_manager,
|
||||
gint view_size)
|
||||
{
|
||||
return gimp_symmetry_editor_new (context->gimp,
|
||||
gimp_context_get_image (context),
|
||||
gimp_dialog_factory_get_menu_factory (factory));
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
dialogs_undo_editor_new (GimpDialogFactory *factory,
|
||||
GimpContext *context,
|
||||
|
|
|
@ -244,6 +244,10 @@ GtkWidget * dialogs_selection_editor_new (GimpDialogFactory *factory,
|
|||
GimpContext *context,
|
||||
GimpUIManager *ui_manager,
|
||||
gint view_size);
|
||||
GtkWidget * dialogs_symmetry_editor_new (GimpDialogFactory *factory,
|
||||
GimpContext *context,
|
||||
GimpUIManager *ui_manager,
|
||||
gint view_size);
|
||||
GtkWidget * dialogs_undo_editor_new (GimpDialogFactory *factory,
|
||||
GimpContext *context,
|
||||
GimpUIManager *ui_manager,
|
||||
|
|
|
@ -385,6 +385,10 @@ static const GimpDialogFactoryEntry entries[] =
|
|||
N_("Selection"), N_("Selection Editor"), GIMP_STOCK_SELECTION,
|
||||
GIMP_HELP_SELECTION_DIALOG,
|
||||
dialogs_selection_editor_new, 0, FALSE),
|
||||
DOCKABLE ("gimp-symmetry-editor",
|
||||
N_("Symmetry Painting"), NULL, GIMP_STOCK_SYMMETRY,
|
||||
GIMP_HELP_SYMMETRY_DIALOG,
|
||||
dialogs_symmetry_editor_new, 0, FALSE),
|
||||
DOCKABLE ("gimp-undo-history",
|
||||
N_("Undo"), N_("Undo History"), GIMP_STOCK_UNDO_HISTORY,
|
||||
GIMP_HELP_UNDO_DIALOG,
|
||||
|
|
|
@ -1601,6 +1601,9 @@ prefs_dialog_new (Gimp *gimp,
|
|||
button = prefs_check_button_add (object, "playground-seamless-clone-tool",
|
||||
_("_Seamless Clone tool"),
|
||||
GTK_BOX (vbox2));
|
||||
button = prefs_check_button_add (object, "playground-symmetry",
|
||||
_("_Symmetry Painting"),
|
||||
GTK_BOX (vbox2));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -228,6 +228,37 @@ gimp_zoom_focus_get_type (void)
|
|||
return type;
|
||||
}
|
||||
|
||||
GType
|
||||
gimp_guide_style_get_type (void)
|
||||
{
|
||||
static const GEnumValue values[] =
|
||||
{
|
||||
{ GIMP_GUIDE_STYLE_NONE, "GIMP_GUIDE_STYLE_NONE", "none" },
|
||||
{ GIMP_GUIDE_STYLE_NORMAL, "GIMP_GUIDE_STYLE_NORMAL", "normal" },
|
||||
{ GIMP_GUIDE_STYLE_MIRROR, "GIMP_GUIDE_STYLE_MIRROR", "mirror" },
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
|
||||
static const GimpEnumDesc descs[] =
|
||||
{
|
||||
{ GIMP_GUIDE_STYLE_NONE, "GIMP_GUIDE_STYLE_NONE", NULL },
|
||||
{ GIMP_GUIDE_STYLE_NORMAL, "GIMP_GUIDE_STYLE_NORMAL", NULL },
|
||||
{ GIMP_GUIDE_STYLE_MIRROR, "GIMP_GUIDE_STYLE_MIRROR", NULL },
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
|
||||
static GType type = 0;
|
||||
|
||||
if (G_UNLIKELY (! type))
|
||||
{
|
||||
type = g_enum_register_static ("GimpGuideStyle", values);
|
||||
gimp_type_set_translation_context (type, "guide-style");
|
||||
gimp_enum_set_value_descriptions (type, descs);
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
/* Generated data ends here */
|
||||
|
||||
|
|
|
@ -117,5 +117,15 @@ typedef enum
|
|||
|
||||
} GimpZoomFocus;
|
||||
|
||||
#define GIMP_TYPE_GUIDE_STYLE (gimp_guide_style_get_type ())
|
||||
|
||||
GType gimp_guide_style_get_type (void) G_GNUC_CONST;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GIMP_GUIDE_STYLE_NONE,
|
||||
GIMP_GUIDE_STYLE_NORMAL,
|
||||
GIMP_GUIDE_STYLE_MIRROR
|
||||
} GimpGuideStyle;
|
||||
|
||||
#endif /* __DISPLAY_ENUMS_H__ */
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "core/gimpdynamics.h"
|
||||
#include "core/gimpgradient.h"
|
||||
#include "core/gimpimage.h"
|
||||
#include "core/gimpsymmetry.h"
|
||||
|
||||
#include "gimpairbrush.h"
|
||||
#include "gimpairbrushoptions.h"
|
||||
|
@ -40,13 +41,13 @@ static void gimp_airbrush_finalize (GObject *object);
|
|||
static void gimp_airbrush_paint (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time);
|
||||
static void gimp_airbrush_motion (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords);
|
||||
GimpSymmetry *sym);
|
||||
static gboolean gimp_airbrush_timeout (gpointer data);
|
||||
|
||||
|
||||
|
@ -82,6 +83,7 @@ static void
|
|||
gimp_airbrush_init (GimpAirbrush *airbrush)
|
||||
{
|
||||
airbrush->timeout_id = 0;
|
||||
airbrush->sym = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -95,6 +97,9 @@ gimp_airbrush_finalize (GObject *object)
|
|||
airbrush->timeout_id = 0;
|
||||
}
|
||||
|
||||
if (airbrush->sym)
|
||||
g_object_unref (airbrush->sym);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
|
@ -102,7 +107,7 @@ static void
|
|||
gimp_airbrush_paint (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time)
|
||||
{
|
||||
|
@ -121,7 +126,7 @@ gimp_airbrush_paint (GimpPaintCore *paint_core,
|
|||
|
||||
GIMP_PAINT_CORE_CLASS (parent_class)->paint (paint_core, drawable,
|
||||
paint_options,
|
||||
coords,
|
||||
sym,
|
||||
paint_state, time);
|
||||
break;
|
||||
|
||||
|
@ -132,14 +137,15 @@ gimp_airbrush_paint (GimpPaintCore *paint_core,
|
|||
airbrush->timeout_id = 0;
|
||||
}
|
||||
|
||||
gimp_airbrush_motion (paint_core, drawable, paint_options, coords);
|
||||
gimp_airbrush_motion (paint_core, drawable, paint_options, sym);
|
||||
|
||||
if ((options->rate != 0.0) && (!options->motion_only))
|
||||
{
|
||||
GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable));
|
||||
gdouble fade_point;
|
||||
gdouble dynamic_rate;
|
||||
gint timeout;
|
||||
GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable));
|
||||
gdouble fade_point;
|
||||
gdouble dynamic_rate;
|
||||
gint timeout;
|
||||
GimpCoords *coords;
|
||||
|
||||
fade_point = gimp_paint_options_get_fade (paint_options, image,
|
||||
paint_core->pixel_dist);
|
||||
|
@ -147,6 +153,13 @@ gimp_airbrush_paint (GimpPaintCore *paint_core,
|
|||
airbrush->drawable = drawable;
|
||||
airbrush->paint_options = paint_options;
|
||||
|
||||
if (airbrush->sym)
|
||||
g_object_unref (airbrush->sym);
|
||||
airbrush->sym = g_object_ref (sym);
|
||||
|
||||
/* Base our timeout on the original stroke. */
|
||||
coords = gimp_symmetry_get_origin (sym);
|
||||
|
||||
dynamic_rate = gimp_dynamics_get_linear_value (dynamics,
|
||||
GIMP_DYNAMICS_OUTPUT_RATE,
|
||||
coords,
|
||||
|
@ -170,7 +183,7 @@ gimp_airbrush_paint (GimpPaintCore *paint_core,
|
|||
|
||||
GIMP_PAINT_CORE_CLASS (parent_class)->paint (paint_core, drawable,
|
||||
paint_options,
|
||||
coords,
|
||||
sym,
|
||||
paint_state, time);
|
||||
break;
|
||||
}
|
||||
|
@ -180,7 +193,7 @@ static void
|
|||
gimp_airbrush_motion (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords)
|
||||
GimpSymmetry *sym)
|
||||
|
||||
{
|
||||
GimpAirbrushOptions *options = GIMP_AIRBRUSH_OPTIONS (paint_options);
|
||||
|
@ -188,10 +201,13 @@ gimp_airbrush_motion (GimpPaintCore *paint_core,
|
|||
GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable));
|
||||
gdouble opacity;
|
||||
gdouble fade_point;
|
||||
GimpCoords *coords;
|
||||
|
||||
fade_point = gimp_paint_options_get_fade (paint_options, image,
|
||||
paint_core->pixel_dist);
|
||||
|
||||
coords = gimp_symmetry_get_origin (sym);
|
||||
|
||||
opacity = (options->flow / 100.0 *
|
||||
gimp_dynamics_get_linear_value (dynamics,
|
||||
GIMP_DYNAMICS_OUTPUT_FLOW,
|
||||
|
@ -199,21 +215,19 @@ gimp_airbrush_motion (GimpPaintCore *paint_core,
|
|||
paint_options,
|
||||
fade_point));
|
||||
|
||||
_gimp_paintbrush_motion (paint_core, drawable, paint_options, coords, opacity);
|
||||
_gimp_paintbrush_motion (paint_core, drawable, paint_options,
|
||||
sym, opacity);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gimp_airbrush_timeout (gpointer data)
|
||||
{
|
||||
GimpAirbrush *airbrush = GIMP_AIRBRUSH (data);
|
||||
GimpCoords coords;
|
||||
|
||||
gimp_paint_core_get_current_coords (GIMP_PAINT_CORE (airbrush), &coords);
|
||||
|
||||
gimp_airbrush_paint (GIMP_PAINT_CORE (airbrush),
|
||||
airbrush->drawable,
|
||||
airbrush->paint_options,
|
||||
&coords,
|
||||
airbrush->sym,
|
||||
GIMP_PAINT_STATE_MOTION, 0);
|
||||
|
||||
gimp_image_flush (gimp_item_get_image (GIMP_ITEM (airbrush->drawable)));
|
||||
|
|
|
@ -37,6 +37,8 @@ struct _GimpAirbrush
|
|||
GimpPaintbrush parent_instance;
|
||||
|
||||
guint timeout_id;
|
||||
|
||||
GimpSymmetry *sym;
|
||||
GimpDrawable *drawable;
|
||||
GimpPaintOptions *paint_options;
|
||||
};
|
||||
|
|
|
@ -84,7 +84,9 @@ static GeglBuffer * gimp_brush_core_get_paint_buffer(GimpPaintCore *paint_cor
|
|||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
gint *paint_buffer_x,
|
||||
gint *paint_buffer_y);
|
||||
gint *paint_buffer_y,
|
||||
gint *paint_width,
|
||||
gint *paint_height);
|
||||
|
||||
static void gimp_brush_core_real_set_brush (GimpBrushCore *core,
|
||||
GimpBrush *brush);
|
||||
|
@ -109,10 +111,12 @@ static const GimpTempBuf *
|
|||
gdouble y);
|
||||
static const GimpTempBuf *
|
||||
gimp_brush_core_transform_mask (GimpBrushCore *core,
|
||||
GimpBrush *brush);
|
||||
GimpBrush *brush,
|
||||
GeglNode *op);
|
||||
static const GimpTempBuf *
|
||||
gimp_brush_core_transform_pixmap (GimpBrushCore *core,
|
||||
GimpBrush *brush);
|
||||
GimpBrush *brush,
|
||||
GeglNode *op);
|
||||
|
||||
static void gimp_brush_core_invalidate_cache (GimpBrush *brush,
|
||||
GimpBrushCore *core);
|
||||
|
@ -811,7 +815,9 @@ gimp_brush_core_get_paint_buffer (GimpPaintCore *paint_core,
|
|||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
gint *paint_buffer_x,
|
||||
gint *paint_buffer_y)
|
||||
gint *paint_buffer_y,
|
||||
gint *paint_width,
|
||||
gint *paint_height)
|
||||
{
|
||||
GimpBrushCore *core = GIMP_BRUSH_CORE (paint_core);
|
||||
gint x, y;
|
||||
|
@ -819,18 +825,15 @@ gimp_brush_core_get_paint_buffer (GimpPaintCore *paint_core,
|
|||
gint drawable_width, drawable_height;
|
||||
gint brush_width, brush_height;
|
||||
|
||||
if (GIMP_BRUSH_CORE_GET_CLASS (core)->handles_transforming_brush)
|
||||
{
|
||||
gimp_brush_core_eval_transform_dynamics (core,
|
||||
drawable,
|
||||
paint_options,
|
||||
coords);
|
||||
}
|
||||
|
||||
gimp_brush_transform_size (core->brush,
|
||||
core->scale, core->aspect_ratio, core->angle,
|
||||
&brush_width, &brush_height);
|
||||
|
||||
if (paint_width)
|
||||
*paint_width = brush_width;
|
||||
if (paint_height)
|
||||
*paint_height = brush_height;
|
||||
|
||||
/* adjust the x and y coordinates to the upper left corner of the brush */
|
||||
x = (gint) floor (coords->x) - (brush_width / 2);
|
||||
y = (gint) floor (coords->y) - (brush_height / 2);
|
||||
|
@ -960,11 +963,12 @@ gimp_brush_core_paste_canvas (GimpBrushCore *core,
|
|||
GimpLayerModeEffects paint_mode,
|
||||
GimpBrushApplicationMode brush_hardness,
|
||||
gdouble dynamic_force,
|
||||
GimpPaintApplicationMode mode)
|
||||
GimpPaintApplicationMode mode,
|
||||
GeglNode *op)
|
||||
{
|
||||
const GimpTempBuf *brush_mask;
|
||||
|
||||
brush_mask = gimp_brush_core_get_brush_mask (core, coords,
|
||||
brush_mask = gimp_brush_core_get_brush_mask (core, coords, op,
|
||||
brush_hardness,
|
||||
dynamic_force);
|
||||
|
||||
|
@ -1003,11 +1007,12 @@ gimp_brush_core_replace_canvas (GimpBrushCore *core,
|
|||
gdouble image_opacity,
|
||||
GimpBrushApplicationMode brush_hardness,
|
||||
gdouble dynamic_force,
|
||||
GimpPaintApplicationMode mode)
|
||||
GimpPaintApplicationMode mode,
|
||||
GeglNode *op)
|
||||
{
|
||||
const GimpTempBuf *brush_mask;
|
||||
|
||||
brush_mask = gimp_brush_core_get_brush_mask (core, coords,
|
||||
brush_mask = gimp_brush_core_get_brush_mask (core, coords, op,
|
||||
brush_hardness,
|
||||
dynamic_force);
|
||||
|
||||
|
@ -1407,7 +1412,8 @@ gimp_brush_core_solidify_mask (GimpBrushCore *core,
|
|||
|
||||
static const GimpTempBuf *
|
||||
gimp_brush_core_transform_mask (GimpBrushCore *core,
|
||||
GimpBrush *brush)
|
||||
GimpBrush *brush,
|
||||
GeglNode *op)
|
||||
{
|
||||
const GimpTempBuf *mask;
|
||||
|
||||
|
@ -1415,6 +1421,7 @@ gimp_brush_core_transform_mask (GimpBrushCore *core,
|
|||
return NULL;
|
||||
|
||||
mask = gimp_brush_transform_mask (brush,
|
||||
op,
|
||||
core->scale,
|
||||
core->aspect_ratio,
|
||||
core->angle,
|
||||
|
@ -1432,7 +1439,8 @@ gimp_brush_core_transform_mask (GimpBrushCore *core,
|
|||
|
||||
static const GimpTempBuf *
|
||||
gimp_brush_core_transform_pixmap (GimpBrushCore *core,
|
||||
GimpBrush *brush)
|
||||
GimpBrush *brush,
|
||||
GeglNode *op)
|
||||
{
|
||||
const GimpTempBuf *pixmap;
|
||||
|
||||
|
@ -1440,6 +1448,7 @@ gimp_brush_core_transform_pixmap (GimpBrushCore *core,
|
|||
return NULL;
|
||||
|
||||
pixmap = gimp_brush_transform_pixmap (brush,
|
||||
op,
|
||||
core->scale,
|
||||
core->aspect_ratio,
|
||||
core->angle,
|
||||
|
@ -1457,12 +1466,13 @@ gimp_brush_core_transform_pixmap (GimpBrushCore *core,
|
|||
const GimpTempBuf *
|
||||
gimp_brush_core_get_brush_mask (GimpBrushCore *core,
|
||||
const GimpCoords *coords,
|
||||
GeglNode *op,
|
||||
GimpBrushApplicationMode brush_hardness,
|
||||
gdouble dynamic_force)
|
||||
{
|
||||
const GimpTempBuf *mask;
|
||||
|
||||
mask = gimp_brush_core_transform_mask (core, core->brush);
|
||||
mask = gimp_brush_core_transform_mask (core, core->brush, op);
|
||||
|
||||
if (! mask)
|
||||
return NULL;
|
||||
|
@ -1591,6 +1601,7 @@ void
|
|||
gimp_brush_core_color_area_with_pixmap (GimpBrushCore *core,
|
||||
GimpDrawable *drawable,
|
||||
const GimpCoords *coords,
|
||||
GeglNode *op,
|
||||
GeglBuffer *area,
|
||||
gint area_x,
|
||||
gint area_y,
|
||||
|
@ -1609,13 +1620,13 @@ gimp_brush_core_color_area_with_pixmap (GimpBrushCore *core,
|
|||
g_return_if_fail (gimp_brush_get_pixmap (core->brush) != NULL);
|
||||
|
||||
/* scale the brushes */
|
||||
pixmap_mask = gimp_brush_core_transform_pixmap (core, core->brush);
|
||||
pixmap_mask = gimp_brush_core_transform_pixmap (core, core->brush, op);
|
||||
|
||||
if (! pixmap_mask)
|
||||
return;
|
||||
|
||||
if (mode != GIMP_BRUSH_HARD)
|
||||
brush_mask = gimp_brush_core_transform_mask (core, core->brush);
|
||||
brush_mask = gimp_brush_core_transform_mask (core, core->brush, op);
|
||||
else
|
||||
brush_mask = NULL;
|
||||
|
||||
|
|
|
@ -107,7 +107,8 @@ void gimp_brush_core_paste_canvas (GimpBrushCore *core,
|
|||
GimpLayerModeEffects paint_mode,
|
||||
GimpBrushApplicationMode brush_hardness,
|
||||
gdouble dynamic_hardness,
|
||||
GimpPaintApplicationMode mode);
|
||||
GimpPaintApplicationMode mode,
|
||||
GeglNode *op);
|
||||
void gimp_brush_core_replace_canvas (GimpBrushCore *core,
|
||||
GimpDrawable *drawable,
|
||||
const GimpCoords *coords,
|
||||
|
@ -115,12 +116,14 @@ void gimp_brush_core_replace_canvas (GimpBrushCore *core,
|
|||
gdouble image_opacity,
|
||||
GimpBrushApplicationMode brush_hardness,
|
||||
gdouble dynamic_hardness,
|
||||
GimpPaintApplicationMode mode);
|
||||
GimpPaintApplicationMode mode,
|
||||
GeglNode *op);
|
||||
|
||||
void gimp_brush_core_color_area_with_pixmap
|
||||
(GimpBrushCore *core,
|
||||
GimpDrawable *drawable,
|
||||
const GimpCoords *coords,
|
||||
GeglNode *op,
|
||||
GeglBuffer *area,
|
||||
gint area_x,
|
||||
gint area_y,
|
||||
|
@ -129,6 +132,7 @@ void gimp_brush_core_color_area_with_pixmap
|
|||
const GimpTempBuf * gimp_brush_core_get_brush_mask
|
||||
(GimpBrushCore *core,
|
||||
const GimpCoords *coords,
|
||||
GeglNode *op,
|
||||
GimpBrushApplicationMode brush_hardness,
|
||||
gdouble dynamic_hardness);
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "core/gimpimage.h"
|
||||
#include "core/gimppattern.h"
|
||||
#include "core/gimppickable.h"
|
||||
#include "core/gimpsymmetry.h"
|
||||
|
||||
#include "gimpclone.h"
|
||||
#include "gimpcloneoptions.h"
|
||||
|
@ -51,6 +52,7 @@ static void gimp_clone_motion (GimpSourceCore *source_core,
|
|||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GeglNode *op,
|
||||
gdouble opacity,
|
||||
GimpPickable *src_pickable,
|
||||
GeglBuffer *src_buffer,
|
||||
|
@ -137,6 +139,7 @@ gimp_clone_motion (GimpSourceCore *source_core,
|
|||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GeglNode *op,
|
||||
gdouble opacity,
|
||||
GimpPickable *src_pickable,
|
||||
GeglBuffer *src_buffer,
|
||||
|
@ -173,6 +176,26 @@ gimp_clone_motion (GimpSourceCore *source_core,
|
|||
GEGL_RECTANGLE (paint_area_offset_x,
|
||||
paint_area_offset_y,
|
||||
0, 0));
|
||||
if (op)
|
||||
{
|
||||
GeglNode *graph, *source, *target;
|
||||
|
||||
graph = gegl_node_new ();
|
||||
source = gegl_node_new_child (graph,
|
||||
"operation", "gegl:buffer-source",
|
||||
"buffer", paint_buffer,
|
||||
NULL);
|
||||
gegl_node_add_child (graph, op);
|
||||
target = gegl_node_new_child (graph,
|
||||
"operation", "gegl:write-buffer",
|
||||
"buffer", paint_buffer,
|
||||
NULL);
|
||||
|
||||
gegl_node_link_many (source, op, target, NULL);
|
||||
gegl_node_process (target);
|
||||
|
||||
g_object_unref (graph);
|
||||
}
|
||||
}
|
||||
else if (options->clone_type == GIMP_CLONE_PATTERN)
|
||||
{
|
||||
|
@ -223,7 +246,8 @@ gimp_clone_motion (GimpSourceCore *source_core,
|
|||
*/
|
||||
source_options->align_mode ==
|
||||
GIMP_SOURCE_ALIGN_FIXED ?
|
||||
GIMP_PAINT_INCREMENTAL : GIMP_PAINT_CONSTANT);
|
||||
GIMP_PAINT_INCREMENTAL : GIMP_PAINT_CONSTANT,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "core/gimpdynamics.h"
|
||||
#include "core/gimpimage.h"
|
||||
#include "core/gimppickable.h"
|
||||
#include "core/gimpsymmetry.h"
|
||||
#include "core/gimptempbuf.h"
|
||||
|
||||
#include "gimpconvolve.h"
|
||||
|
@ -48,13 +49,13 @@
|
|||
static void gimp_convolve_paint (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time);
|
||||
static void gimp_convolve_motion (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords);
|
||||
GimpSymmetry *sym);
|
||||
|
||||
static void gimp_convolve_calculate_matrix (GimpConvolve *convolve,
|
||||
GimpConvolveType type,
|
||||
|
@ -102,14 +103,14 @@ static void
|
|||
gimp_convolve_paint (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time)
|
||||
{
|
||||
switch (paint_state)
|
||||
{
|
||||
case GIMP_PAINT_STATE_MOTION:
|
||||
gimp_convolve_motion (paint_core, drawable, paint_options, coords);
|
||||
gimp_convolve_motion (paint_core, drawable, paint_options, sym);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -121,7 +122,7 @@ static void
|
|||
gimp_convolve_motion (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords)
|
||||
GimpSymmetry *sym)
|
||||
{
|
||||
GimpConvolve *convolve = GIMP_CONVOLVE (paint_core);
|
||||
GimpBrushCore *brush_core = GIMP_BRUSH_CORE (paint_core);
|
||||
|
@ -137,10 +138,16 @@ gimp_convolve_motion (GimpPaintCore *paint_core,
|
|||
gdouble fade_point;
|
||||
gdouble opacity;
|
||||
gdouble rate;
|
||||
const GimpCoords *coords;
|
||||
GeglNode *op;
|
||||
gint paint_width, paint_height;
|
||||
gint n_strokes;
|
||||
gint i;
|
||||
|
||||
fade_point = gimp_paint_options_get_fade (paint_options, image,
|
||||
paint_core->pixel_dist);
|
||||
|
||||
coords = gimp_symmetry_get_origin (sym);
|
||||
opacity = gimp_dynamics_get_linear_value (dynamics,
|
||||
GIMP_DYNAMICS_OUTPUT_OPACITY,
|
||||
coords,
|
||||
|
@ -149,61 +156,77 @@ gimp_convolve_motion (GimpPaintCore *paint_core,
|
|||
if (opacity == 0.0)
|
||||
return;
|
||||
|
||||
paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable,
|
||||
paint_options, coords,
|
||||
&paint_buffer_x,
|
||||
&paint_buffer_y);
|
||||
if (! paint_buffer)
|
||||
return;
|
||||
gimp_brush_core_eval_transform_dynamics (GIMP_BRUSH_CORE (paint_core),
|
||||
drawable,
|
||||
paint_options,
|
||||
coords);
|
||||
n_strokes = gimp_symmetry_get_size (sym);
|
||||
for (i = 0; i < n_strokes; i++)
|
||||
{
|
||||
coords = gimp_symmetry_get_coords (sym, i);
|
||||
|
||||
rate = (options->rate *
|
||||
gimp_dynamics_get_linear_value (dynamics,
|
||||
GIMP_DYNAMICS_OUTPUT_RATE,
|
||||
coords,
|
||||
paint_options,
|
||||
fade_point));
|
||||
paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable,
|
||||
paint_options, coords,
|
||||
&paint_buffer_x,
|
||||
&paint_buffer_y,
|
||||
&paint_width,
|
||||
&paint_height);
|
||||
if (! paint_buffer)
|
||||
continue;
|
||||
|
||||
gimp_convolve_calculate_matrix (convolve, options->type,
|
||||
gimp_brush_get_width (brush_core->brush) / 2,
|
||||
gimp_brush_get_height (brush_core->brush) / 2,
|
||||
rate);
|
||||
op = gimp_symmetry_get_operation (sym, i,
|
||||
paint_width,
|
||||
paint_height);
|
||||
|
||||
/* need a linear buffer for gimp_gegl_convolve() */
|
||||
temp_buf = gimp_temp_buf_new (gegl_buffer_get_width (paint_buffer),
|
||||
gegl_buffer_get_height (paint_buffer),
|
||||
gegl_buffer_get_format (paint_buffer));
|
||||
convolve_buffer = gimp_temp_buf_create_buffer (temp_buf);
|
||||
gimp_temp_buf_unref (temp_buf);
|
||||
rate = (options->rate *
|
||||
gimp_dynamics_get_linear_value (dynamics,
|
||||
GIMP_DYNAMICS_OUTPUT_RATE,
|
||||
coords,
|
||||
paint_options,
|
||||
fade_point));
|
||||
|
||||
gegl_buffer_copy (gimp_drawable_get_buffer (drawable),
|
||||
GEGL_RECTANGLE (paint_buffer_x,
|
||||
paint_buffer_y,
|
||||
gegl_buffer_get_width (paint_buffer),
|
||||
gegl_buffer_get_height (paint_buffer)),
|
||||
GEGL_ABYSS_NONE,
|
||||
convolve_buffer,
|
||||
GEGL_RECTANGLE (0, 0, 0, 0));
|
||||
gimp_convolve_calculate_matrix (convolve, options->type,
|
||||
gimp_brush_get_width (brush_core->brush) / 2,
|
||||
gimp_brush_get_height (brush_core->brush) / 2,
|
||||
rate);
|
||||
|
||||
gimp_gegl_convolve (convolve_buffer,
|
||||
GEGL_RECTANGLE (0, 0,
|
||||
gegl_buffer_get_width (convolve_buffer),
|
||||
gegl_buffer_get_height (convolve_buffer)),
|
||||
paint_buffer,
|
||||
GEGL_RECTANGLE (0, 0,
|
||||
gegl_buffer_get_width (paint_buffer),
|
||||
gegl_buffer_get_height (paint_buffer)),
|
||||
convolve->matrix, 3, convolve->matrix_divisor,
|
||||
GIMP_NORMAL_CONVOL, TRUE);
|
||||
/* need a linear buffer for gimp_gegl_convolve() */
|
||||
temp_buf = gimp_temp_buf_new (gegl_buffer_get_width (paint_buffer),
|
||||
gegl_buffer_get_height (paint_buffer),
|
||||
gegl_buffer_get_format (paint_buffer));
|
||||
convolve_buffer = gimp_temp_buf_create_buffer (temp_buf);
|
||||
gimp_temp_buf_unref (temp_buf);
|
||||
|
||||
g_object_unref (convolve_buffer);
|
||||
gegl_buffer_copy (gimp_drawable_get_buffer (drawable),
|
||||
GEGL_RECTANGLE (paint_buffer_x,
|
||||
paint_buffer_y,
|
||||
gegl_buffer_get_width (paint_buffer),
|
||||
gegl_buffer_get_height (paint_buffer)),
|
||||
GEGL_ABYSS_NONE,
|
||||
convolve_buffer,
|
||||
GEGL_RECTANGLE (0, 0, 0, 0));
|
||||
|
||||
gimp_brush_core_replace_canvas (brush_core, drawable,
|
||||
coords,
|
||||
MIN (opacity, GIMP_OPACITY_OPAQUE),
|
||||
gimp_context_get_opacity (context),
|
||||
gimp_paint_options_get_brush_mode (paint_options),
|
||||
1.0,
|
||||
GIMP_PAINT_INCREMENTAL);
|
||||
gimp_gegl_convolve (convolve_buffer,
|
||||
GEGL_RECTANGLE (0, 0,
|
||||
gegl_buffer_get_width (convolve_buffer),
|
||||
gegl_buffer_get_height (convolve_buffer)),
|
||||
paint_buffer,
|
||||
GEGL_RECTANGLE (0, 0,
|
||||
gegl_buffer_get_width (paint_buffer),
|
||||
gegl_buffer_get_height (paint_buffer)),
|
||||
convolve->matrix, 3, convolve->matrix_divisor,
|
||||
GIMP_NORMAL_CONVOL, TRUE);
|
||||
|
||||
g_object_unref (convolve_buffer);
|
||||
|
||||
gimp_brush_core_replace_canvas (brush_core, drawable,
|
||||
coords,
|
||||
MIN (opacity, GIMP_OPACITY_OPAQUE),
|
||||
gimp_context_get_opacity (context),
|
||||
gimp_paint_options_get_brush_mode (paint_options),
|
||||
1.0,
|
||||
GIMP_PAINT_INCREMENTAL, op);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "core/gimpdrawable.h"
|
||||
#include "core/gimpdynamics.h"
|
||||
#include "core/gimpimage.h"
|
||||
#include "core/gimpsymmetry.h"
|
||||
|
||||
#include "gimpdodgeburn.h"
|
||||
#include "gimpdodgeburnoptions.h"
|
||||
|
@ -41,13 +42,13 @@
|
|||
static void gimp_dodge_burn_paint (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time);
|
||||
static void gimp_dodge_burn_motion (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords);
|
||||
GimpSymmetry *sym);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (GimpDodgeBurn, gimp_dodge_burn, GIMP_TYPE_BRUSH_CORE)
|
||||
|
@ -87,7 +88,7 @@ static void
|
|||
gimp_dodge_burn_paint (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time)
|
||||
{
|
||||
|
@ -97,7 +98,7 @@ gimp_dodge_burn_paint (GimpPaintCore *paint_core,
|
|||
break;
|
||||
|
||||
case GIMP_PAINT_STATE_MOTION:
|
||||
gimp_dodge_burn_motion (paint_core, drawable, paint_options, coords);
|
||||
gimp_dodge_burn_motion (paint_core, drawable, paint_options, sym);
|
||||
break;
|
||||
|
||||
case GIMP_PAINT_STATE_FINISH:
|
||||
|
@ -109,7 +110,7 @@ static void
|
|||
gimp_dodge_burn_motion (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords)
|
||||
GimpSymmetry *sym)
|
||||
{
|
||||
GimpDodgeBurnOptions *options = GIMP_DODGE_BURN_OPTIONS (paint_options);
|
||||
GimpContext *context = GIMP_CONTEXT (paint_options);
|
||||
|
@ -121,10 +122,16 @@ gimp_dodge_burn_motion (GimpPaintCore *paint_core,
|
|||
gdouble fade_point;
|
||||
gdouble opacity;
|
||||
gdouble force;
|
||||
const GimpCoords *coords;
|
||||
GeglNode *op;
|
||||
gint paint_width, paint_height;
|
||||
gint n_strokes;
|
||||
gint i;
|
||||
|
||||
fade_point = gimp_paint_options_get_fade (paint_options, image,
|
||||
paint_core->pixel_dist);
|
||||
|
||||
coords = gimp_symmetry_get_origin (sym);
|
||||
opacity = gimp_dynamics_get_linear_value (dynamics,
|
||||
GIMP_DYNAMICS_OUTPUT_OPACITY,
|
||||
coords,
|
||||
|
@ -133,40 +140,56 @@ gimp_dodge_burn_motion (GimpPaintCore *paint_core,
|
|||
if (opacity == 0.0)
|
||||
return;
|
||||
|
||||
paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable,
|
||||
paint_options, coords,
|
||||
&paint_buffer_x,
|
||||
&paint_buffer_y);
|
||||
if (! paint_buffer)
|
||||
return;
|
||||
gimp_brush_core_eval_transform_dynamics (GIMP_BRUSH_CORE (paint_core),
|
||||
drawable,
|
||||
paint_options,
|
||||
coords);
|
||||
n_strokes = gimp_symmetry_get_size (sym);
|
||||
for (i = 0; i < n_strokes; i++)
|
||||
{
|
||||
coords = gimp_symmetry_get_coords (sym, i);
|
||||
|
||||
/* DodgeBurn the region */
|
||||
gimp_gegl_dodgeburn (gimp_paint_core_get_orig_image (paint_core),
|
||||
GEGL_RECTANGLE (paint_buffer_x,
|
||||
paint_buffer_y,
|
||||
gegl_buffer_get_width (paint_buffer),
|
||||
gegl_buffer_get_height (paint_buffer)),
|
||||
paint_buffer,
|
||||
GEGL_RECTANGLE (0, 0, 0, 0),
|
||||
options->exposure / 100.0,
|
||||
options->type,
|
||||
options->mode);
|
||||
paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable,
|
||||
paint_options, coords,
|
||||
&paint_buffer_x,
|
||||
&paint_buffer_y,
|
||||
&paint_width,
|
||||
&paint_height);
|
||||
if (! paint_buffer)
|
||||
continue;
|
||||
|
||||
if (gimp_dynamics_is_output_enabled (dynamics, GIMP_DYNAMICS_OUTPUT_FORCE))
|
||||
force = gimp_dynamics_get_linear_value (dynamics,
|
||||
GIMP_DYNAMICS_OUTPUT_FORCE,
|
||||
coords,
|
||||
paint_options,
|
||||
fade_point);
|
||||
else
|
||||
force = paint_options->brush_force;
|
||||
op = gimp_symmetry_get_operation (sym, i,
|
||||
paint_width,
|
||||
paint_height);
|
||||
|
||||
/* Replace the newly dodgedburned area (paint_area) to the image */
|
||||
gimp_brush_core_replace_canvas (GIMP_BRUSH_CORE (paint_core), drawable,
|
||||
coords,
|
||||
MIN (opacity, GIMP_OPACITY_OPAQUE),
|
||||
gimp_context_get_opacity (context),
|
||||
gimp_paint_options_get_brush_mode (paint_options),
|
||||
force,
|
||||
GIMP_PAINT_CONSTANT);
|
||||
/* DodgeBurn the region */
|
||||
gimp_gegl_dodgeburn (gimp_paint_core_get_orig_image (paint_core),
|
||||
GEGL_RECTANGLE (paint_buffer_x,
|
||||
paint_buffer_y,
|
||||
gegl_buffer_get_width (paint_buffer),
|
||||
gegl_buffer_get_height (paint_buffer)),
|
||||
paint_buffer,
|
||||
GEGL_RECTANGLE (0, 0, 0, 0),
|
||||
options->exposure / 100.0,
|
||||
options->type,
|
||||
options->mode);
|
||||
|
||||
if (gimp_dynamics_is_output_enabled (dynamics, GIMP_DYNAMICS_OUTPUT_FORCE))
|
||||
force = gimp_dynamics_get_linear_value (dynamics,
|
||||
GIMP_DYNAMICS_OUTPUT_FORCE,
|
||||
coords,
|
||||
paint_options,
|
||||
fade_point);
|
||||
else
|
||||
force = paint_options->brush_force;
|
||||
|
||||
/* Replace the newly dodgedburned area (paint_area) to the image */
|
||||
gimp_brush_core_replace_canvas (GIMP_BRUSH_CORE (paint_core), drawable,
|
||||
coords,
|
||||
MIN (opacity, GIMP_OPACITY_OPAQUE),
|
||||
gimp_context_get_opacity (context),
|
||||
gimp_paint_options_get_brush_mode (paint_options),
|
||||
force,
|
||||
GIMP_PAINT_CONSTANT, op);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "core/gimpdrawable.h"
|
||||
#include "core/gimpdynamics.h"
|
||||
#include "core/gimpimage.h"
|
||||
#include "core/gimpsymmetry.h"
|
||||
|
||||
#include "gimperaser.h"
|
||||
#include "gimperaseroptions.h"
|
||||
|
@ -39,13 +40,13 @@
|
|||
static void gimp_eraser_paint (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time);
|
||||
static void gimp_eraser_motion (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords);
|
||||
GimpSymmetry *sym);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (GimpEraser, gimp_eraser, GIMP_TYPE_BRUSH_CORE)
|
||||
|
@ -83,7 +84,7 @@ static void
|
|||
gimp_eraser_paint (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time)
|
||||
{
|
||||
|
@ -106,7 +107,7 @@ gimp_eraser_paint (GimpPaintCore *paint_core,
|
|||
}
|
||||
break;
|
||||
case GIMP_PAINT_STATE_MOTION:
|
||||
gimp_eraser_motion (paint_core, drawable, paint_options, coords);
|
||||
gimp_eraser_motion (paint_core, drawable, paint_options, sym);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -118,7 +119,7 @@ static void
|
|||
gimp_eraser_motion (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords)
|
||||
GimpSymmetry *sym)
|
||||
{
|
||||
GimpEraserOptions *options = GIMP_ERASER_OPTIONS (paint_options);
|
||||
GimpContext *context = GIMP_CONTEXT (paint_options);
|
||||
|
@ -133,10 +134,16 @@ gimp_eraser_motion (GimpPaintCore *paint_core,
|
|||
GimpRGB background;
|
||||
GeglColor *color;
|
||||
gdouble force;
|
||||
const GimpCoords *coords;
|
||||
GeglNode *op;
|
||||
gint n_strokes;
|
||||
gint paint_width, paint_height;
|
||||
gint i;
|
||||
|
||||
fade_point = gimp_paint_options_get_fade (paint_options, image,
|
||||
paint_core->pixel_dist);
|
||||
|
||||
coords = gimp_symmetry_get_origin (sym);
|
||||
opacity = gimp_dynamics_get_linear_value (dynamics,
|
||||
GIMP_DYNAMICS_OUTPUT_OPACITY,
|
||||
coords,
|
||||
|
@ -145,19 +152,9 @@ gimp_eraser_motion (GimpPaintCore *paint_core,
|
|||
if (opacity == 0.0)
|
||||
return;
|
||||
|
||||
paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable,
|
||||
paint_options, coords,
|
||||
&paint_buffer_x,
|
||||
&paint_buffer_y);
|
||||
if (! paint_buffer)
|
||||
return;
|
||||
|
||||
gimp_context_get_background (context, &background);
|
||||
color = gimp_gegl_color_new (&background);
|
||||
|
||||
gegl_buffer_set_color (paint_buffer, NULL, color);
|
||||
g_object_unref (color);
|
||||
|
||||
if (options->anti_erase)
|
||||
paint_mode = GIMP_ANTI_ERASE_MODE;
|
||||
else if (gimp_drawable_has_alpha (drawable))
|
||||
|
@ -165,21 +162,50 @@ gimp_eraser_motion (GimpPaintCore *paint_core,
|
|||
else
|
||||
paint_mode = GIMP_NORMAL_MODE;
|
||||
|
||||
if (gimp_dynamics_is_output_enabled (dynamics, GIMP_DYNAMICS_OUTPUT_FORCE))
|
||||
force = gimp_dynamics_get_linear_value (dynamics,
|
||||
GIMP_DYNAMICS_OUTPUT_FORCE,
|
||||
coords,
|
||||
paint_options,
|
||||
fade_point);
|
||||
else
|
||||
force = paint_options->brush_force;
|
||||
gimp_brush_core_eval_transform_dynamics (GIMP_BRUSH_CORE (paint_core),
|
||||
drawable,
|
||||
paint_options,
|
||||
coords);
|
||||
|
||||
gimp_brush_core_paste_canvas (GIMP_BRUSH_CORE (paint_core), drawable,
|
||||
coords,
|
||||
MIN (opacity, GIMP_OPACITY_OPAQUE),
|
||||
gimp_context_get_opacity (context),
|
||||
paint_mode,
|
||||
gimp_paint_options_get_brush_mode (paint_options),
|
||||
force,
|
||||
paint_options->application_mode);
|
||||
n_strokes = gimp_symmetry_get_size (sym);
|
||||
for (i = 0; i < n_strokes; i++)
|
||||
{
|
||||
coords = gimp_symmetry_get_coords (sym, i);
|
||||
|
||||
if (gimp_dynamics_is_output_enabled (dynamics, GIMP_DYNAMICS_OUTPUT_FORCE))
|
||||
force = gimp_dynamics_get_linear_value (dynamics,
|
||||
GIMP_DYNAMICS_OUTPUT_FORCE,
|
||||
coords,
|
||||
paint_options,
|
||||
fade_point);
|
||||
else
|
||||
force = paint_options->brush_force;
|
||||
|
||||
|
||||
paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable,
|
||||
paint_options, coords,
|
||||
&paint_buffer_x,
|
||||
&paint_buffer_y,
|
||||
&paint_width,
|
||||
&paint_height);
|
||||
if (! paint_buffer)
|
||||
continue;
|
||||
|
||||
op = gimp_symmetry_get_operation (sym, i,
|
||||
paint_width,
|
||||
paint_height);
|
||||
|
||||
gegl_buffer_set_color (paint_buffer, NULL, color);
|
||||
|
||||
gimp_brush_core_paste_canvas (GIMP_BRUSH_CORE (paint_core), drawable,
|
||||
coords,
|
||||
MIN (opacity, GIMP_OPACITY_OPAQUE),
|
||||
gimp_context_get_opacity (context),
|
||||
paint_mode,
|
||||
gimp_paint_options_get_brush_mode (paint_options),
|
||||
force,
|
||||
paint_options->application_mode, op);
|
||||
}
|
||||
|
||||
g_object_unref (color);
|
||||
}
|
||||
|
|
|
@ -74,6 +74,7 @@ static void gimp_heal_motion (GimpSourceCore *source_core,
|
|||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GeglNode *op,
|
||||
gdouble opacity,
|
||||
GimpPickable *src_pickable,
|
||||
GeglBuffer *src_buffer,
|
||||
|
@ -461,6 +462,7 @@ gimp_heal_motion (GimpSourceCore *source_core,
|
|||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GeglNode *op,
|
||||
gdouble opacity,
|
||||
GimpPickable *src_pickable,
|
||||
GeglBuffer *src_buffer,
|
||||
|
@ -500,7 +502,7 @@ gimp_heal_motion (GimpSourceCore *source_core,
|
|||
force = paint_options->brush_force;
|
||||
|
||||
mask_buf = gimp_brush_core_get_brush_mask (GIMP_BRUSH_CORE (source_core),
|
||||
coords,
|
||||
coords, op,
|
||||
GIMP_BRUSH_HARD,
|
||||
force);
|
||||
|
||||
|
@ -549,6 +551,27 @@ gimp_heal_motion (GimpSourceCore *source_core,
|
|||
mask_off_y = (y < 0) ? -y : 0;
|
||||
}
|
||||
|
||||
if (op)
|
||||
{
|
||||
GeglNode *graph, *source, *target;
|
||||
|
||||
graph = gegl_node_new ();
|
||||
source = gegl_node_new_child (graph,
|
||||
"operation", "gegl:buffer-source",
|
||||
"buffer", src_copy,
|
||||
NULL);
|
||||
gegl_node_add_child (graph, op);
|
||||
target = gegl_node_new_child (graph,
|
||||
"operation", "gegl:write-buffer",
|
||||
"buffer", src_copy,
|
||||
NULL);
|
||||
|
||||
gegl_node_link_many (source, op, target, NULL);
|
||||
gegl_node_process (target);
|
||||
|
||||
g_object_unref (graph);
|
||||
}
|
||||
|
||||
gimp_heal (src_copy,
|
||||
GEGL_RECTANGLE (0, 0,
|
||||
gegl_buffer_get_width (src_copy),
|
||||
|
@ -573,5 +596,5 @@ gimp_heal_motion (GimpSourceCore *source_core,
|
|||
gimp_context_get_opacity (context),
|
||||
gimp_paint_options_get_brush_mode (paint_options),
|
||||
force,
|
||||
GIMP_PAINT_INCREMENTAL);
|
||||
GIMP_PAINT_INCREMENTAL, NULL);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "core/gimpdrawable.h"
|
||||
#include "core/gimpimage.h"
|
||||
#include "core/gimpimage-undo.h"
|
||||
#include "core/gimpsymmetry.h"
|
||||
#include "core/gimptempbuf.h"
|
||||
|
||||
#include "gimpinkoptions.h"
|
||||
|
@ -52,7 +53,7 @@ static void gimp_ink_finalize (GObject *object);
|
|||
static void gimp_ink_paint (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time);
|
||||
static GeglBuffer * gimp_ink_get_paint_buffer (GimpPaintCore *paint_core,
|
||||
|
@ -60,7 +61,9 @@ static GeglBuffer * gimp_ink_get_paint_buffer (GimpPaintCore *paint_core,
|
|||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
gint *paint_buffer_x,
|
||||
gint *paint_buffer_y);
|
||||
gint *paint_buffer_y,
|
||||
gint *paint_width,
|
||||
gint *paint_height);
|
||||
static GimpUndo * gimp_ink_push_undo (GimpPaintCore *core,
|
||||
GimpImage *image,
|
||||
const gchar *undo_desc);
|
||||
|
@ -68,7 +71,7 @@ static GimpUndo * gimp_ink_push_undo (GimpPaintCore *core,
|
|||
static void gimp_ink_motion (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
guint32 time);
|
||||
|
||||
static GimpBlob * ink_pen_ellipse (GimpInkOptions *options,
|
||||
|
@ -124,16 +127,16 @@ gimp_ink_finalize (GObject *object)
|
|||
{
|
||||
GimpInk *ink = GIMP_INK (object);
|
||||
|
||||
if (ink->start_blob)
|
||||
if (ink->start_blobs)
|
||||
{
|
||||
g_free (ink->start_blob);
|
||||
ink->start_blob = NULL;
|
||||
g_list_free_full (ink->start_blobs, g_free);
|
||||
ink->start_blobs = NULL;
|
||||
}
|
||||
|
||||
if (ink->last_blob)
|
||||
if (ink->last_blobs)
|
||||
{
|
||||
g_free (ink->last_blob);
|
||||
ink->last_blob = NULL;
|
||||
g_list_free_full (ink->last_blobs, g_free);
|
||||
ink->last_blobs = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
|
@ -143,14 +146,16 @@ static void
|
|||
gimp_ink_paint (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time)
|
||||
{
|
||||
GimpInk *ink = GIMP_INK (paint_core);
|
||||
GimpCoords last_coords;
|
||||
GimpInk *ink = GIMP_INK (paint_core);
|
||||
GimpCoords *cur_coords;
|
||||
GimpCoords last_coords;
|
||||
|
||||
gimp_paint_core_get_last_coords (paint_core, &last_coords);
|
||||
cur_coords = gimp_symmetry_get_origin (sym);
|
||||
|
||||
switch (paint_state)
|
||||
{
|
||||
|
@ -163,37 +168,48 @@ gimp_ink_paint (GimpPaintCore *paint_core,
|
|||
gimp_palettes_add_color_history (context->gimp,
|
||||
&foreground);
|
||||
|
||||
if (coords->x == last_coords.x &&
|
||||
coords->y == last_coords.y)
|
||||
if (cur_coords->x == last_coords.x &&
|
||||
cur_coords->y == last_coords.y)
|
||||
{
|
||||
/* start with new blobs if we're not interpolating */
|
||||
|
||||
if (ink->start_blob)
|
||||
if (ink->start_blobs)
|
||||
{
|
||||
g_free (ink->start_blob);
|
||||
ink->start_blob = NULL;
|
||||
g_list_free_full (ink->start_blobs, g_free);
|
||||
ink->start_blobs = NULL;
|
||||
}
|
||||
|
||||
if (ink->last_blob)
|
||||
if (ink->last_blobs)
|
||||
{
|
||||
g_free (ink->last_blob);
|
||||
ink->last_blob = NULL;
|
||||
g_list_free_full (ink->last_blobs, g_free);
|
||||
ink->last_blobs = NULL;
|
||||
}
|
||||
}
|
||||
else if (ink->last_blob)
|
||||
else if (ink->last_blobs)
|
||||
{
|
||||
/* save the start blob of the line for undo otherwise */
|
||||
GimpBlob *last_blob;
|
||||
GList *iter;
|
||||
gint i;
|
||||
|
||||
if (ink->start_blob)
|
||||
g_free (ink->start_blob);
|
||||
if (ink->start_blobs)
|
||||
{
|
||||
g_list_free_full (ink->start_blobs, g_free);
|
||||
ink->start_blobs = NULL;
|
||||
}
|
||||
|
||||
ink->start_blob = gimp_blob_duplicate (ink->last_blob);
|
||||
/* save the start blobs of each stroke for undo otherwise */
|
||||
for (iter = ink->last_blobs, i = 0; iter; iter = g_list_next (iter), i++)
|
||||
{
|
||||
last_blob = g_list_nth_data (ink->last_blobs, i);
|
||||
|
||||
ink->start_blobs = g_list_prepend (ink->start_blobs,
|
||||
gimp_blob_duplicate (last_blob));
|
||||
}
|
||||
ink->start_blobs = g_list_reverse (ink->start_blobs);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GIMP_PAINT_STATE_MOTION:
|
||||
gimp_ink_motion (paint_core, drawable, paint_options, coords, time);
|
||||
gimp_ink_motion (paint_core, drawable, paint_options, sym, time);
|
||||
break;
|
||||
|
||||
case GIMP_PAINT_STATE_FINISH:
|
||||
|
@ -207,7 +223,9 @@ gimp_ink_get_paint_buffer (GimpPaintCore *paint_core,
|
|||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
gint *paint_buffer_x,
|
||||
gint *paint_buffer_y)
|
||||
gint *paint_buffer_y,
|
||||
gint *paint_width,
|
||||
gint *paint_height)
|
||||
{
|
||||
GimpInk *ink = GIMP_INK (paint_core);
|
||||
gint x, y;
|
||||
|
@ -225,6 +243,11 @@ gimp_ink_get_paint_buffer (GimpPaintCore *paint_core,
|
|||
x2 = CLAMP ((x + width) / SUBSAMPLE + 2, 0, dwidth);
|
||||
y2 = CLAMP ((y + height) / SUBSAMPLE + 2, 0, dheight);
|
||||
|
||||
if (paint_width)
|
||||
*paint_width = width / SUBSAMPLE + 3;
|
||||
if (paint_height)
|
||||
*paint_height = height / SUBSAMPLE + 3;
|
||||
|
||||
/* configure the canvas buffer */
|
||||
if ((x2 - x1) && (y2 - y1))
|
||||
{
|
||||
|
@ -271,93 +294,137 @@ static void
|
|||
gimp_ink_motion (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
guint32 time)
|
||||
{
|
||||
GimpInk *ink = GIMP_INK (paint_core);
|
||||
GimpInkOptions *options = GIMP_INK_OPTIONS (paint_options);
|
||||
GimpContext *context = GIMP_CONTEXT (paint_options);
|
||||
GimpBlob *blob_union = NULL;
|
||||
GimpBlob *blob_to_render;
|
||||
GimpInk *ink = GIMP_INK (paint_core);
|
||||
GimpInkOptions *options = GIMP_INK_OPTIONS (paint_options);
|
||||
GimpContext *context = GIMP_CONTEXT (paint_options);
|
||||
GList *blob_unions = NULL;
|
||||
GList *blobs_to_render = NULL;
|
||||
GeglBuffer *paint_buffer;
|
||||
gint paint_buffer_x;
|
||||
gint paint_buffer_y;
|
||||
GimpRGB foreground;
|
||||
GeglColor *color;
|
||||
GimpBlob *last_blob;
|
||||
GimpCoords *coords;
|
||||
gint n_strokes;
|
||||
gint i;
|
||||
|
||||
if (! ink->last_blob)
|
||||
n_strokes = gimp_symmetry_get_size (sym);
|
||||
|
||||
if (ink->last_blobs &&
|
||||
g_list_length (ink->last_blobs) != n_strokes)
|
||||
{
|
||||
ink->last_blob = ink_pen_ellipse (options,
|
||||
coords->x,
|
||||
coords->y,
|
||||
coords->pressure,
|
||||
coords->xtilt,
|
||||
coords->ytilt,
|
||||
100);
|
||||
g_list_free_full (ink->last_blobs, g_free);
|
||||
ink->last_blobs = NULL;
|
||||
}
|
||||
|
||||
if (ink->start_blob)
|
||||
g_free (ink->start_blob);
|
||||
if (! ink->last_blobs)
|
||||
{
|
||||
if (ink->start_blobs)
|
||||
{
|
||||
g_list_free_full (ink->start_blobs, g_free);
|
||||
ink->start_blobs = NULL;
|
||||
}
|
||||
|
||||
ink->start_blob = gimp_blob_duplicate (ink->last_blob);
|
||||
for (i = 0; i < n_strokes; i++)
|
||||
{
|
||||
coords = gimp_symmetry_get_coords (sym, i);
|
||||
|
||||
blob_to_render = ink->last_blob;
|
||||
last_blob = ink_pen_ellipse (options,
|
||||
coords->x,
|
||||
coords->y,
|
||||
coords->pressure,
|
||||
coords->xtilt,
|
||||
coords->ytilt,
|
||||
100);
|
||||
|
||||
ink->last_blobs = g_list_prepend (ink->last_blobs,
|
||||
last_blob);
|
||||
ink->start_blobs = g_list_prepend (ink->start_blobs,
|
||||
gimp_blob_duplicate (last_blob));
|
||||
blobs_to_render = g_list_prepend (blobs_to_render, last_blob);
|
||||
}
|
||||
ink->start_blobs = g_list_reverse (ink->start_blobs);
|
||||
ink->last_blobs = g_list_reverse (ink->last_blobs);
|
||||
blobs_to_render = g_list_reverse (blobs_to_render);
|
||||
}
|
||||
else
|
||||
{
|
||||
GimpBlob *blob = ink_pen_ellipse (options,
|
||||
coords->x,
|
||||
coords->y,
|
||||
coords->pressure,
|
||||
coords->xtilt,
|
||||
coords->ytilt,
|
||||
coords->velocity * 100);
|
||||
for (i = 0; i < n_strokes; i++)
|
||||
{
|
||||
GimpBlob *blob;
|
||||
GimpBlob *blob_union = NULL;
|
||||
|
||||
blob_union = gimp_blob_convex_union (ink->last_blob, blob);
|
||||
coords = gimp_symmetry_get_coords (sym, i);
|
||||
blob = ink_pen_ellipse (options,
|
||||
coords->x,
|
||||
coords->y,
|
||||
coords->pressure,
|
||||
coords->xtilt,
|
||||
coords->ytilt,
|
||||
coords->velocity * 100);
|
||||
|
||||
g_free (ink->last_blob);
|
||||
ink->last_blob = blob;
|
||||
last_blob = g_list_nth_data (ink->last_blobs, i);
|
||||
blob_union = gimp_blob_convex_union (last_blob, blob);
|
||||
|
||||
blob_to_render = blob_union;
|
||||
g_free (last_blob);
|
||||
g_list_nth (ink->last_blobs, i)->data = blob;
|
||||
|
||||
blobs_to_render = g_list_prepend (blobs_to_render, blob_union);
|
||||
blob_unions = g_list_prepend (blob_unions, blob_union);
|
||||
}
|
||||
blobs_to_render = g_list_reverse (blobs_to_render);
|
||||
}
|
||||
|
||||
/* Get the buffer */
|
||||
ink->cur_blob = blob_to_render;
|
||||
paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable,
|
||||
paint_options, coords,
|
||||
&paint_buffer_x,
|
||||
&paint_buffer_y);
|
||||
ink->cur_blob = NULL;
|
||||
for (i = 0; i < n_strokes; i++)
|
||||
{
|
||||
GimpBlob *blob_to_render = g_list_nth_data (blobs_to_render, i);
|
||||
|
||||
if (! paint_buffer)
|
||||
return;
|
||||
coords = gimp_symmetry_get_coords (sym, i);
|
||||
|
||||
gimp_context_get_foreground (context, &foreground);
|
||||
color = gimp_gegl_color_new (&foreground);
|
||||
ink->cur_blob = blob_to_render;
|
||||
paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable,
|
||||
paint_options, coords,
|
||||
&paint_buffer_x,
|
||||
&paint_buffer_y,
|
||||
NULL, NULL);
|
||||
ink->cur_blob = NULL;
|
||||
|
||||
gegl_buffer_set_color (paint_buffer, NULL, color);
|
||||
g_object_unref (color);
|
||||
if (! paint_buffer)
|
||||
continue;
|
||||
|
||||
/* draw the blob directly to the canvas_buffer */
|
||||
render_blob (paint_core->canvas_buffer,
|
||||
GEGL_RECTANGLE (paint_core->paint_buffer_x,
|
||||
paint_core->paint_buffer_y,
|
||||
gegl_buffer_get_width (paint_core->paint_buffer),
|
||||
gegl_buffer_get_height (paint_core->paint_buffer)),
|
||||
blob_to_render);
|
||||
gimp_context_get_foreground (context, &foreground);
|
||||
color = gimp_gegl_color_new (&foreground);
|
||||
|
||||
/* draw the paint_area using the just rendered canvas_buffer as mask */
|
||||
gimp_paint_core_paste (paint_core,
|
||||
NULL,
|
||||
paint_core->paint_buffer_x,
|
||||
paint_core->paint_buffer_y,
|
||||
drawable,
|
||||
GIMP_OPACITY_OPAQUE,
|
||||
gimp_context_get_opacity (context),
|
||||
gimp_context_get_paint_mode (context),
|
||||
GIMP_PAINT_CONSTANT);
|
||||
gegl_buffer_set_color (paint_buffer, NULL, color);
|
||||
g_object_unref (color);
|
||||
|
||||
if (blob_union)
|
||||
g_free (blob_union);
|
||||
/* draw the blob directly to the canvas_buffer */
|
||||
render_blob (paint_core->canvas_buffer,
|
||||
GEGL_RECTANGLE (paint_core->paint_buffer_x,
|
||||
paint_core->paint_buffer_y,
|
||||
gegl_buffer_get_width (paint_core->paint_buffer),
|
||||
gegl_buffer_get_height (paint_core->paint_buffer)),
|
||||
blob_to_render);
|
||||
|
||||
/* draw the paint_area using the just rendered canvas_buffer as mask */
|
||||
gimp_paint_core_paste (paint_core,
|
||||
NULL,
|
||||
paint_core->paint_buffer_x,
|
||||
paint_core->paint_buffer_y,
|
||||
drawable,
|
||||
GIMP_OPACITY_OPAQUE,
|
||||
gimp_context_get_opacity (context),
|
||||
gimp_context_get_paint_mode (context),
|
||||
GIMP_PAINT_CONSTANT);
|
||||
|
||||
}
|
||||
|
||||
g_list_free_full (blob_unions, g_free);
|
||||
}
|
||||
|
||||
static GimpBlob *
|
||||
|
|
|
@ -37,10 +37,10 @@ struct _GimpInk
|
|||
{
|
||||
GimpPaintCore parent_instance;
|
||||
|
||||
GimpBlob *start_blob; /* starting blob (for undo) */
|
||||
GList *start_blobs; /* starting blobs per stroke (for undo) */
|
||||
|
||||
GimpBlob *cur_blob; /* current blob */
|
||||
GimpBlob *last_blob; /* blob for last cursor position */
|
||||
GimpBlob *cur_blob; /* current blob */
|
||||
GList *last_blobs; /* blobs for last stroke positions */
|
||||
};
|
||||
|
||||
struct _GimpInkClass
|
||||
|
|
|
@ -58,6 +58,7 @@ gimp_ink_undo_class_init (GimpInkUndoClass *klass)
|
|||
static void
|
||||
gimp_ink_undo_init (GimpInkUndo *undo)
|
||||
{
|
||||
undo->last_blobs = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -72,8 +73,20 @@ gimp_ink_undo_constructed (GObject *object)
|
|||
|
||||
ink = GIMP_INK (GIMP_PAINT_CORE_UNDO (ink_undo)->paint_core);
|
||||
|
||||
if (ink->start_blob)
|
||||
ink_undo->last_blob = gimp_blob_duplicate (ink->start_blob);
|
||||
if (ink->start_blobs)
|
||||
{
|
||||
gint i;
|
||||
GimpBlob *blob;
|
||||
|
||||
for (i = 0; i < g_list_length (ink->start_blobs); i++)
|
||||
{
|
||||
blob = g_list_nth_data (ink->start_blobs, i);
|
||||
|
||||
ink_undo->last_blobs = g_list_prepend (ink_undo->last_blobs,
|
||||
gimp_blob_duplicate (blob));
|
||||
}
|
||||
ink_undo->last_blobs = g_list_reverse (ink_undo->last_blobs);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -88,12 +101,11 @@ gimp_ink_undo_pop (GimpUndo *undo,
|
|||
if (GIMP_PAINT_CORE_UNDO (ink_undo)->paint_core)
|
||||
{
|
||||
GimpInk *ink = GIMP_INK (GIMP_PAINT_CORE_UNDO (ink_undo)->paint_core);
|
||||
GimpBlob *tmp_blob;
|
||||
|
||||
tmp_blob = ink->last_blob;
|
||||
ink->last_blob = ink_undo->last_blob;
|
||||
ink_undo->last_blob = tmp_blob;
|
||||
GList *tmp_blobs;
|
||||
|
||||
tmp_blobs = ink->last_blobs;
|
||||
ink->last_blobs = ink_undo->last_blobs;
|
||||
ink_undo->last_blobs = tmp_blobs;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,10 +115,10 @@ gimp_ink_undo_free (GimpUndo *undo,
|
|||
{
|
||||
GimpInkUndo *ink_undo = GIMP_INK_UNDO (undo);
|
||||
|
||||
if (ink_undo->last_blob)
|
||||
if (ink_undo->last_blobs)
|
||||
{
|
||||
g_free (ink_undo->last_blob);
|
||||
ink_undo->last_blob = NULL;
|
||||
g_list_free_full (ink_undo->last_blobs, g_free);
|
||||
ink_undo->last_blobs = NULL;
|
||||
}
|
||||
|
||||
GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode);
|
||||
|
|
|
@ -36,7 +36,7 @@ struct _GimpInkUndo
|
|||
{
|
||||
GimpPaintCoreUndo parent_instance;
|
||||
|
||||
GimpBlob *last_blob;
|
||||
GList *last_blobs;
|
||||
};
|
||||
|
||||
struct _GimpInkUndoClass
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "core/gimpdrawable.h"
|
||||
#include "core/gimperror.h"
|
||||
#include "core/gimpmybrush.h"
|
||||
#include "core/gimpsymmetry.h"
|
||||
|
||||
#include "gimpmybrushcore.h"
|
||||
#include "gimpmybrushsurface.h"
|
||||
|
@ -49,7 +50,7 @@ struct _GimpMybrushCorePrivate
|
|||
{
|
||||
GimpMybrush *mybrush;
|
||||
GimpMybrushSurface *surface;
|
||||
MyPaintBrush *brush;
|
||||
GList *brushes;
|
||||
gboolean synthetic;
|
||||
gint64 last_time;
|
||||
};
|
||||
|
@ -57,6 +58,8 @@ struct _GimpMybrushCorePrivate
|
|||
|
||||
/* local function prototypes */
|
||||
|
||||
static void gimp_mybrush_core_finalize (GObject *object);
|
||||
|
||||
static gboolean gimp_mybrush_core_start (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
|
@ -69,13 +72,13 @@ static void gimp_mybrush_core_interpolate (GimpPaintCore *paint_core,
|
|||
static void gimp_mybrush_core_paint (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time);
|
||||
static void gimp_mybrush_core_motion (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
guint32 time);
|
||||
|
||||
|
||||
|
@ -99,8 +102,11 @@ gimp_mybrush_core_register (Gimp *gimp,
|
|||
static void
|
||||
gimp_mybrush_core_class_init (GimpMybrushCoreClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GimpPaintCoreClass *paint_core_class = GIMP_PAINT_CORE_CLASS (klass);
|
||||
|
||||
object_class->finalize = gimp_mybrush_core_finalize;
|
||||
|
||||
paint_core_class->start = gimp_mybrush_core_start;
|
||||
paint_core_class->paint = gimp_mybrush_core_paint;
|
||||
paint_core_class->interpolate = gimp_mybrush_core_interpolate;
|
||||
|
@ -116,6 +122,21 @@ gimp_mybrush_core_init (GimpMybrushCore *mybrush)
|
|||
GimpMybrushCorePrivate);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_mybrush_core_finalize (GObject *object)
|
||||
{
|
||||
GimpMybrushCore *core = GIMP_MYBRUSH_CORE (object);
|
||||
|
||||
if (core->private->brushes)
|
||||
{
|
||||
g_list_free_full (core->private->brushes,
|
||||
(GDestroyNotify) mypaint_brush_unref);
|
||||
core->private->brushes = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gimp_mybrush_core_start (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
|
@ -172,7 +193,7 @@ static void
|
|||
gimp_mybrush_core_paint (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time)
|
||||
{
|
||||
|
@ -182,6 +203,8 @@ gimp_mybrush_core_paint (GimpPaintCore *paint_core,
|
|||
const gchar *brush_data;
|
||||
GimpRGB fg;
|
||||
GimpHSV hsv;
|
||||
gint n_strokes;
|
||||
gint i;
|
||||
|
||||
switch (paint_state)
|
||||
{
|
||||
|
@ -195,53 +218,69 @@ gimp_mybrush_core_paint (GimpPaintCore *paint_core,
|
|||
paint_core->mask_x_offset,
|
||||
paint_core->mask_y_offset);
|
||||
|
||||
mybrush->private->brush = mypaint_brush_new ();
|
||||
mypaint_brush_from_defaults (mybrush->private->brush);
|
||||
brush_data = gimp_mybrush_get_brush_json (mybrush->private->mybrush);
|
||||
if (brush_data)
|
||||
mypaint_brush_from_string (mybrush->private->brush, brush_data);
|
||||
|
||||
gimp_rgb_to_hsv (&fg, &hsv);
|
||||
|
||||
mypaint_brush_set_base_value (mybrush->private->brush,
|
||||
MYPAINT_BRUSH_SETTING_COLOR_H,
|
||||
hsv.h);
|
||||
mypaint_brush_set_base_value (mybrush->private->brush,
|
||||
MYPAINT_BRUSH_SETTING_COLOR_S,
|
||||
hsv.s);
|
||||
mypaint_brush_set_base_value (mybrush->private->brush,
|
||||
MYPAINT_BRUSH_SETTING_COLOR_V,
|
||||
hsv.v);
|
||||
if (mybrush->private->brushes)
|
||||
{
|
||||
g_list_free_full (mybrush->private->brushes,
|
||||
(GDestroyNotify) mypaint_brush_unref);
|
||||
mybrush->private->brushes = NULL;
|
||||
}
|
||||
|
||||
mypaint_brush_set_base_value (mybrush->private->brush,
|
||||
MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC,
|
||||
options->radius);
|
||||
mypaint_brush_set_base_value (mybrush->private->brush,
|
||||
MYPAINT_BRUSH_SETTING_OPAQUE,
|
||||
options->opaque * gimp_context_get_opacity (context));
|
||||
mypaint_brush_set_base_value (mybrush->private->brush,
|
||||
MYPAINT_BRUSH_SETTING_HARDNESS,
|
||||
options->hardness);
|
||||
mypaint_brush_set_base_value (mybrush->private->brush,
|
||||
MYPAINT_BRUSH_SETTING_ERASER,
|
||||
options->eraser ? 1.0f : 0.0f);
|
||||
n_strokes = gimp_symmetry_get_size (sym);
|
||||
for (i = 0; i < n_strokes; i++)
|
||||
{
|
||||
MyPaintBrush *brush = mypaint_brush_new ();
|
||||
|
||||
mypaint_brush_new_stroke (mybrush->private->brush);
|
||||
mypaint_brush_from_defaults (brush);
|
||||
brush_data = gimp_mybrush_get_brush_json (mybrush->private->mybrush);
|
||||
if (brush_data)
|
||||
mypaint_brush_from_string (brush, brush_data);
|
||||
|
||||
mypaint_brush_set_base_value (brush,
|
||||
MYPAINT_BRUSH_SETTING_COLOR_H,
|
||||
hsv.h);
|
||||
mypaint_brush_set_base_value (brush,
|
||||
MYPAINT_BRUSH_SETTING_COLOR_S,
|
||||
hsv.s);
|
||||
mypaint_brush_set_base_value (brush,
|
||||
MYPAINT_BRUSH_SETTING_COLOR_V,
|
||||
hsv.v);
|
||||
|
||||
mypaint_brush_set_base_value (brush,
|
||||
MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC,
|
||||
options->radius);
|
||||
mypaint_brush_set_base_value (brush,
|
||||
MYPAINT_BRUSH_SETTING_OPAQUE,
|
||||
options->opaque * gimp_context_get_opacity (context));
|
||||
mypaint_brush_set_base_value (brush,
|
||||
MYPAINT_BRUSH_SETTING_HARDNESS,
|
||||
options->hardness);
|
||||
mypaint_brush_set_base_value (brush,
|
||||
MYPAINT_BRUSH_SETTING_ERASER,
|
||||
options->eraser ? 1.0f : 0.0f);
|
||||
|
||||
mypaint_brush_new_stroke (brush);
|
||||
|
||||
mybrush->private->brushes = g_list_prepend (mybrush->private->brushes, brush);
|
||||
}
|
||||
mybrush->private->brushes = g_list_reverse (mybrush->private->brushes);
|
||||
mybrush->private->last_time = -1;
|
||||
mybrush->private->synthetic = FALSE;
|
||||
break;
|
||||
|
||||
case GIMP_PAINT_STATE_MOTION:
|
||||
gimp_mybrush_core_motion (paint_core, drawable, paint_options,
|
||||
coords, time);
|
||||
sym, time);
|
||||
break;
|
||||
|
||||
case GIMP_PAINT_STATE_FINISH:
|
||||
mypaint_surface_unref ((MyPaintSurface *) mybrush->private->surface);
|
||||
mybrush->private->surface = NULL;
|
||||
|
||||
mypaint_brush_unref (mybrush->private->brush);
|
||||
mybrush->private->brush = NULL;
|
||||
g_list_free_full (mybrush->private->brushes,
|
||||
(GDestroyNotify) mypaint_brush_unref);
|
||||
mybrush->private->brushes = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -250,27 +289,93 @@ static void
|
|||
gimp_mybrush_core_motion (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
guint32 time)
|
||||
{
|
||||
GimpMybrushCore *mybrush = GIMP_MYBRUSH_CORE (paint_core);
|
||||
GimpContext *context = GIMP_CONTEXT (paint_options);
|
||||
MyPaintBrush *brush;
|
||||
GimpCoords *coords;
|
||||
MyPaintRectangle rect;
|
||||
gdouble pressure;
|
||||
gdouble dt = 0.0;
|
||||
gint n_strokes;
|
||||
gint i;
|
||||
GList *iter;
|
||||
|
||||
n_strokes = gimp_symmetry_get_size (sym);
|
||||
|
||||
/* Number of strokes may change during a motion, depending on the type
|
||||
* of symmetry. When that happens, we reset the brushes. */
|
||||
if (g_list_length (mybrush->private->brushes) != n_strokes)
|
||||
{
|
||||
const gchar *brush_data;
|
||||
GimpMybrushOptions *options = GIMP_MYBRUSH_OPTIONS (paint_options);
|
||||
GimpRGB fg;
|
||||
GimpHSV hsv;
|
||||
|
||||
gimp_context_get_foreground (context, &fg);
|
||||
gimp_rgb_to_hsv (&fg, &hsv);
|
||||
|
||||
g_list_free_full (mybrush->private->brushes,
|
||||
(GDestroyNotify) mypaint_brush_unref);
|
||||
mybrush->private->brushes = NULL;
|
||||
for (i = 0; i < n_strokes; i++)
|
||||
{
|
||||
brush = mypaint_brush_new ();
|
||||
|
||||
mypaint_brush_from_defaults (brush);
|
||||
brush_data = gimp_mybrush_get_brush_json (mybrush->private->mybrush);
|
||||
if (brush_data)
|
||||
mypaint_brush_from_string (brush, brush_data);
|
||||
|
||||
mypaint_brush_set_base_value (brush,
|
||||
MYPAINT_BRUSH_SETTING_COLOR_H,
|
||||
hsv.h);
|
||||
mypaint_brush_set_base_value (brush,
|
||||
MYPAINT_BRUSH_SETTING_COLOR_S,
|
||||
hsv.s);
|
||||
mypaint_brush_set_base_value (brush,
|
||||
MYPAINT_BRUSH_SETTING_COLOR_V,
|
||||
hsv.v);
|
||||
|
||||
mypaint_brush_set_base_value (brush,
|
||||
MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC,
|
||||
options->radius);
|
||||
mypaint_brush_set_base_value (brush,
|
||||
MYPAINT_BRUSH_SETTING_OPAQUE,
|
||||
options->opaque * gimp_context_get_opacity (context));
|
||||
mypaint_brush_set_base_value (brush,
|
||||
MYPAINT_BRUSH_SETTING_HARDNESS,
|
||||
options->hardness);
|
||||
mypaint_brush_set_base_value (brush,
|
||||
MYPAINT_BRUSH_SETTING_ERASER,
|
||||
options->eraser ? 1.0f : 0.0f);
|
||||
|
||||
mypaint_brush_new_stroke (brush);
|
||||
mybrush->private->brushes = g_list_prepend (mybrush->private->brushes, brush);
|
||||
}
|
||||
mybrush->private->brushes = g_list_reverse (mybrush->private->brushes);
|
||||
}
|
||||
|
||||
mypaint_surface_begin_atomic ((MyPaintSurface *) mybrush->private->surface);
|
||||
|
||||
if (mybrush->private->last_time < 0)
|
||||
{
|
||||
/* First motion, so we need a zero pressure event to start the stroke */
|
||||
mypaint_brush_stroke_to (mybrush->private->brush,
|
||||
(MyPaintSurface *) mybrush->private->surface,
|
||||
coords->x,
|
||||
coords->y,
|
||||
0.0f,
|
||||
coords->xtilt,
|
||||
coords->ytilt,
|
||||
1.0f /* Pretend the cursor hasn't moved in a while */);
|
||||
/* First motion, so we need zero pressure events to start the strokes */
|
||||
for (iter = mybrush->private->brushes, i = 0; iter ; iter = g_list_next (iter), i++)
|
||||
{
|
||||
brush = iter->data;
|
||||
coords = gimp_symmetry_get_coords (sym, i);
|
||||
mypaint_brush_stroke_to (brush,
|
||||
(MyPaintSurface *) mybrush->private->surface,
|
||||
coords->x,
|
||||
coords->y,
|
||||
0.0f,
|
||||
coords->xtilt,
|
||||
coords->ytilt,
|
||||
1.0f /* Pretend the cursor hasn't moved in a while */);
|
||||
}
|
||||
dt = 0.015;
|
||||
}
|
||||
else if (mybrush->private->synthetic)
|
||||
|
@ -283,20 +388,25 @@ gimp_mybrush_core_motion (GimpPaintCore *paint_core,
|
|||
dt = (time - mybrush->private->last_time) * 0.001;
|
||||
}
|
||||
|
||||
pressure = coords->pressure;
|
||||
for (iter = mybrush->private->brushes, i = 0; iter ; iter = g_list_next (iter), i++)
|
||||
{
|
||||
brush = iter->data;
|
||||
coords = gimp_symmetry_get_coords (sym, i);
|
||||
pressure = coords->pressure;
|
||||
|
||||
/* libmypaint expects non-extended devices to default to 0.5 pressure */
|
||||
if (! coords->extended)
|
||||
pressure = 0.5f;
|
||||
/* libmypaint expects non-extended devices to default to 0.5 pressure */
|
||||
if (! coords->extended)
|
||||
pressure = 0.5f;
|
||||
|
||||
mypaint_brush_stroke_to (mybrush->private->brush,
|
||||
(MyPaintSurface *) mybrush->private->surface,
|
||||
coords->x,
|
||||
coords->y,
|
||||
pressure,
|
||||
coords->xtilt,
|
||||
coords->ytilt,
|
||||
dt);
|
||||
mypaint_brush_stroke_to (brush,
|
||||
(MyPaintSurface *) mybrush->private->surface,
|
||||
coords->x,
|
||||
coords->y,
|
||||
pressure,
|
||||
coords->xtilt,
|
||||
coords->ytilt,
|
||||
dt);
|
||||
}
|
||||
|
||||
mybrush->private->last_time = time;
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "core/gimpdynamics.h"
|
||||
#include "core/gimpgradient.h"
|
||||
#include "core/gimpimage.h"
|
||||
#include "core/gimpsymmetry.h"
|
||||
#include "core/gimptempbuf.h"
|
||||
|
||||
#include "gimppaintbrush.h"
|
||||
|
@ -47,7 +48,7 @@
|
|||
static void gimp_paintbrush_paint (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time);
|
||||
|
||||
|
@ -87,7 +88,7 @@ static void
|
|||
gimp_paintbrush_paint (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time)
|
||||
{
|
||||
|
@ -116,8 +117,8 @@ gimp_paintbrush_paint (GimpPaintCore *paint_core,
|
|||
}
|
||||
break;
|
||||
case GIMP_PAINT_STATE_MOTION:
|
||||
_gimp_paintbrush_motion (paint_core, drawable, paint_options, coords,
|
||||
GIMP_OPACITY_OPAQUE);
|
||||
_gimp_paintbrush_motion (paint_core, drawable, paint_options,
|
||||
sym, GIMP_OPACITY_OPAQUE);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -129,7 +130,7 @@ void
|
|||
_gimp_paintbrush_motion (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
gdouble opacity)
|
||||
{
|
||||
GimpBrushCore *brush_core = GIMP_BRUSH_CORE (paint_core);
|
||||
|
@ -144,12 +145,18 @@ _gimp_paintbrush_motion (GimpPaintCore *paint_core,
|
|||
gdouble fade_point;
|
||||
gdouble grad_point;
|
||||
gdouble force;
|
||||
const GimpCoords *coords;
|
||||
GeglNode *op;
|
||||
gint n_strokes;
|
||||
gint i;
|
||||
|
||||
image = gimp_item_get_image (GIMP_ITEM (drawable));
|
||||
|
||||
fade_point = gimp_paint_options_get_fade (paint_options, image,
|
||||
paint_core->pixel_dist);
|
||||
|
||||
coords = gimp_symmetry_get_origin (sym);
|
||||
/* Some settings are based on the original stroke. */
|
||||
opacity *= gimp_dynamics_get_linear_value (dynamics,
|
||||
GIMP_DYNAMICS_OUTPUT_OPACITY,
|
||||
coords,
|
||||
|
@ -158,13 +165,6 @@ _gimp_paintbrush_motion (GimpPaintCore *paint_core,
|
|||
if (opacity == 0.0)
|
||||
return;
|
||||
|
||||
paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable,
|
||||
paint_options, coords,
|
||||
&paint_buffer_x,
|
||||
&paint_buffer_y);
|
||||
if (! paint_buffer)
|
||||
return;
|
||||
|
||||
paint_appl_mode = paint_options->application_mode;
|
||||
|
||||
grad_point = gimp_dynamics_get_linear_value (dynamics,
|
||||
|
@ -173,69 +173,98 @@ _gimp_paintbrush_motion (GimpPaintCore *paint_core,
|
|||
paint_options,
|
||||
fade_point);
|
||||
|
||||
if (gimp_paint_options_get_gradient_color (paint_options, image,
|
||||
grad_point,
|
||||
paint_core->pixel_dist,
|
||||
&gradient_color))
|
||||
|
||||
if (GIMP_BRUSH_CORE_GET_CLASS (brush_core)->handles_transforming_brush)
|
||||
{
|
||||
/* optionally take the color from the current gradient */
|
||||
|
||||
GeglColor *color;
|
||||
|
||||
opacity *= gradient_color.a;
|
||||
gimp_rgb_set_alpha (&gradient_color, GIMP_OPACITY_OPAQUE);
|
||||
|
||||
color = gimp_gegl_color_new (&gradient_color);
|
||||
|
||||
gegl_buffer_set_color (paint_buffer, NULL, color);
|
||||
g_object_unref (color);
|
||||
|
||||
paint_appl_mode = GIMP_PAINT_INCREMENTAL;
|
||||
}
|
||||
else if (brush_core->brush && gimp_brush_get_pixmap (brush_core->brush))
|
||||
{
|
||||
/* otherwise check if the brush has a pixmap and use that to
|
||||
* color the area
|
||||
*/
|
||||
gimp_brush_core_color_area_with_pixmap (brush_core, drawable,
|
||||
coords,
|
||||
paint_buffer,
|
||||
paint_buffer_x,
|
||||
paint_buffer_y,
|
||||
gimp_paint_options_get_brush_mode (paint_options));
|
||||
|
||||
paint_appl_mode = GIMP_PAINT_INCREMENTAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* otherwise fill the area with the foreground color */
|
||||
|
||||
GimpRGB foreground;
|
||||
GeglColor *color;
|
||||
|
||||
gimp_context_get_foreground (context, &foreground);
|
||||
color = gimp_gegl_color_new (&foreground);
|
||||
|
||||
gegl_buffer_set_color (paint_buffer, NULL, color);
|
||||
g_object_unref (color);
|
||||
gimp_brush_core_eval_transform_dynamics (brush_core,
|
||||
drawable,
|
||||
paint_options,
|
||||
coords);
|
||||
}
|
||||
|
||||
if (gimp_dynamics_is_output_enabled (dynamics, GIMP_DYNAMICS_OUTPUT_FORCE))
|
||||
force = gimp_dynamics_get_linear_value (dynamics,
|
||||
GIMP_DYNAMICS_OUTPUT_FORCE,
|
||||
coords,
|
||||
paint_options,
|
||||
fade_point);
|
||||
else
|
||||
force = paint_options->brush_force;
|
||||
n_strokes = gimp_symmetry_get_size (sym);
|
||||
for (i = 0; i < n_strokes; i++)
|
||||
{
|
||||
gint paint_width, paint_height;
|
||||
|
||||
/* finally, let the brush core paste the colored area on the canvas */
|
||||
gimp_brush_core_paste_canvas (brush_core, drawable,
|
||||
coords,
|
||||
MIN (opacity, GIMP_OPACITY_OPAQUE),
|
||||
gimp_context_get_opacity (context),
|
||||
gimp_context_get_paint_mode (context),
|
||||
gimp_paint_options_get_brush_mode (paint_options),
|
||||
force,
|
||||
paint_appl_mode);
|
||||
coords = gimp_symmetry_get_coords (sym, i);
|
||||
|
||||
paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable,
|
||||
paint_options, coords,
|
||||
&paint_buffer_x,
|
||||
&paint_buffer_y,
|
||||
&paint_width,
|
||||
&paint_height);
|
||||
if (! paint_buffer)
|
||||
continue;
|
||||
|
||||
op = gimp_symmetry_get_operation (sym, i,
|
||||
paint_width,
|
||||
paint_height);
|
||||
if (gimp_paint_options_get_gradient_color (paint_options, image,
|
||||
grad_point,
|
||||
paint_core->pixel_dist,
|
||||
&gradient_color))
|
||||
{
|
||||
/* optionally take the color from the current gradient */
|
||||
|
||||
GeglColor *color;
|
||||
|
||||
opacity *= gradient_color.a;
|
||||
gimp_rgb_set_alpha (&gradient_color, GIMP_OPACITY_OPAQUE);
|
||||
|
||||
color = gimp_gegl_color_new (&gradient_color);
|
||||
|
||||
gegl_buffer_set_color (paint_buffer, NULL, color);
|
||||
g_object_unref (color);
|
||||
|
||||
paint_appl_mode = GIMP_PAINT_INCREMENTAL;
|
||||
}
|
||||
else if (brush_core->brush && gimp_brush_get_pixmap (brush_core->brush))
|
||||
{
|
||||
/* otherwise check if the brush has a pixmap and use that to
|
||||
* color the area
|
||||
*/
|
||||
gimp_brush_core_color_area_with_pixmap (brush_core, drawable,
|
||||
coords, op,
|
||||
paint_buffer,
|
||||
paint_buffer_x,
|
||||
paint_buffer_y,
|
||||
gimp_paint_options_get_brush_mode (paint_options));
|
||||
|
||||
paint_appl_mode = GIMP_PAINT_INCREMENTAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* otherwise fill the area with the foreground color */
|
||||
|
||||
GimpRGB foreground;
|
||||
GeglColor *color;
|
||||
|
||||
gimp_context_get_foreground (context, &foreground);
|
||||
color = gimp_gegl_color_new (&foreground);
|
||||
|
||||
gegl_buffer_set_color (paint_buffer, NULL, color);
|
||||
g_object_unref (color);
|
||||
}
|
||||
|
||||
if (gimp_dynamics_is_output_enabled (dynamics, GIMP_DYNAMICS_OUTPUT_FORCE))
|
||||
force = gimp_dynamics_get_linear_value (dynamics,
|
||||
GIMP_DYNAMICS_OUTPUT_FORCE,
|
||||
coords,
|
||||
paint_options,
|
||||
fade_point);
|
||||
else
|
||||
force = paint_options->brush_force;
|
||||
|
||||
/* finally, let the brush core paste the colored area on the canvas */
|
||||
gimp_brush_core_paste_canvas (brush_core, drawable,
|
||||
coords,
|
||||
MIN (opacity, GIMP_OPACITY_OPAQUE),
|
||||
gimp_context_get_opacity (context),
|
||||
gimp_context_get_paint_mode (context),
|
||||
gimp_paint_options_get_brush_mode (paint_options),
|
||||
force,
|
||||
paint_appl_mode, op);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ GType gimp_paintbrush_get_type (void) G_GNUC_CONST;
|
|||
void _gimp_paintbrush_motion (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
gdouble opacity);
|
||||
|
||||
|
||||
|
|
|
@ -39,14 +39,20 @@ combine_paint_mask_to_canvas_mask (const GimpTempBuf *paint_mask,
|
|||
GeglRectangle roi;
|
||||
GeglBufferIterator *iter;
|
||||
|
||||
const gint mask_stride = gimp_temp_buf_get_width (paint_mask);
|
||||
const gint mask_start_offset = mask_y_offset * mask_stride + mask_x_offset;
|
||||
const Babl *mask_format = gimp_temp_buf_get_format (paint_mask);
|
||||
const gint mask_stride = gimp_temp_buf_get_width (paint_mask);
|
||||
const gint mask_start_offset = mask_y_offset * mask_stride + mask_x_offset;
|
||||
const Babl *mask_format = gimp_temp_buf_get_format (paint_mask);
|
||||
GimpTempBuf *modified_mask = gimp_temp_buf_copy (paint_mask);
|
||||
gint width;
|
||||
gint height;
|
||||
|
||||
width = gimp_temp_buf_get_width (modified_mask);
|
||||
height = gimp_temp_buf_get_height (modified_mask);
|
||||
|
||||
roi.x = x_offset;
|
||||
roi.y = y_offset;
|
||||
roi.width = gimp_temp_buf_get_width (paint_mask) - mask_x_offset;
|
||||
roi.height = gimp_temp_buf_get_height (paint_mask) - mask_y_offset;
|
||||
roi.width = width - mask_x_offset;
|
||||
roi.height = height - mask_y_offset;
|
||||
|
||||
iter = gegl_buffer_iterator_new (canvas_buffer, &roi, 0,
|
||||
babl_format ("Y float"),
|
||||
|
@ -56,7 +62,7 @@ combine_paint_mask_to_canvas_mask (const GimpTempBuf *paint_mask,
|
|||
{
|
||||
if (mask_format == babl_format ("Y u8"))
|
||||
{
|
||||
const guint8 *mask_data = (const guint8 *) gimp_temp_buf_get_data (paint_mask);
|
||||
const guint8 *mask_data = (const guint8 *) gimp_temp_buf_get_data (modified_mask);
|
||||
mask_data += mask_start_offset;
|
||||
|
||||
while (gegl_buffer_iterator_next (iter))
|
||||
|
@ -81,7 +87,7 @@ combine_paint_mask_to_canvas_mask (const GimpTempBuf *paint_mask,
|
|||
}
|
||||
else if (mask_format == babl_format ("Y float"))
|
||||
{
|
||||
const gfloat *mask_data = (const gfloat *) gimp_temp_buf_get_data (paint_mask);
|
||||
const gfloat *mask_data = (const gfloat *) gimp_temp_buf_get_data (modified_mask);
|
||||
mask_data += mask_start_offset;
|
||||
|
||||
while (gegl_buffer_iterator_next (iter))
|
||||
|
@ -113,7 +119,7 @@ combine_paint_mask_to_canvas_mask (const GimpTempBuf *paint_mask,
|
|||
{
|
||||
if (mask_format == babl_format ("Y u8"))
|
||||
{
|
||||
const guint8 *mask_data = (const guint8 *) gimp_temp_buf_get_data (paint_mask);
|
||||
const guint8 *mask_data = (const guint8 *) gimp_temp_buf_get_data (modified_mask);
|
||||
mask_data += mask_start_offset;
|
||||
|
||||
while (gegl_buffer_iterator_next (iter))
|
||||
|
@ -139,7 +145,7 @@ combine_paint_mask_to_canvas_mask (const GimpTempBuf *paint_mask,
|
|||
}
|
||||
else if (mask_format == babl_format ("Y float"))
|
||||
{
|
||||
const gfloat *mask_data = (const gfloat *) gimp_temp_buf_get_data (paint_mask);
|
||||
const gfloat *mask_data = (const gfloat *) gimp_temp_buf_get_data (modified_mask);
|
||||
mask_data += mask_start_offset;
|
||||
|
||||
while (gegl_buffer_iterator_next (iter))
|
||||
|
@ -168,6 +174,7 @@ combine_paint_mask_to_canvas_mask (const GimpTempBuf *paint_mask,
|
|||
g_warning("Mask format not supported: %s", babl_get_name (mask_format));
|
||||
}
|
||||
}
|
||||
gimp_temp_buf_unref (modified_mask);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -37,9 +37,12 @@
|
|||
#include "core/gimp-utils.h"
|
||||
#include "core/gimpchannel.h"
|
||||
#include "core/gimpimage.h"
|
||||
#include "core/gimpimage-guides.h"
|
||||
#include "core/gimpimage-symmetry.h"
|
||||
#include "core/gimpimage-undo.h"
|
||||
#include "core/gimppickable.h"
|
||||
#include "core/gimpprojection.h"
|
||||
#include "core/gimpsymmetry.h"
|
||||
#include "core/gimptempbuf.h"
|
||||
|
||||
#include "gimppaintcore.h"
|
||||
|
@ -86,7 +89,7 @@ static gboolean gimp_paint_core_real_pre_paint (GimpPaintCore *core,
|
|||
static void gimp_paint_core_real_paint (GimpPaintCore *core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time);
|
||||
static void gimp_paint_core_real_post_paint (GimpPaintCore *core,
|
||||
|
@ -104,7 +107,9 @@ static GeglBuffer *
|
|||
GimpPaintOptions *options,
|
||||
const GimpCoords *coords,
|
||||
gint *paint_buffer_x,
|
||||
gint *paint_buffer_y);
|
||||
gint *paint_buffer_y,
|
||||
gint *paint_width,
|
||||
gint *paint_height);
|
||||
static GimpUndo* gimp_paint_core_real_push_undo (GimpPaintCore *core,
|
||||
GimpImage *image,
|
||||
const gchar *undo_desc);
|
||||
|
@ -231,7 +236,7 @@ static void
|
|||
gimp_paint_core_real_paint (GimpPaintCore *core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time)
|
||||
{
|
||||
|
@ -264,7 +269,9 @@ gimp_paint_core_real_get_paint_buffer (GimpPaintCore *core,
|
|||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
gint *paint_buffer_x,
|
||||
gint *paint_buffer_y)
|
||||
gint *paint_buffer_y,
|
||||
gint *paint_width,
|
||||
gint *paint_height)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
@ -304,6 +311,12 @@ gimp_paint_core_paint (GimpPaintCore *core,
|
|||
paint_options,
|
||||
paint_state, time))
|
||||
{
|
||||
GimpSymmetry *sym;
|
||||
GimpImage *image;
|
||||
GimpItem *item;
|
||||
|
||||
item = GIMP_ITEM (drawable);
|
||||
image = gimp_item_get_image (item);
|
||||
|
||||
if (paint_state == GIMP_PAINT_STATE_MOTION)
|
||||
{
|
||||
|
@ -312,10 +325,13 @@ gimp_paint_core_paint (GimpPaintCore *core,
|
|||
core->last_paint.y = core->cur_coords.y;
|
||||
}
|
||||
|
||||
sym = g_object_ref (gimp_image_symmetry_selected (image));
|
||||
gimp_symmetry_set_origin (sym, drawable, &core->cur_coords);
|
||||
|
||||
core_class->paint (core, drawable,
|
||||
paint_options,
|
||||
&core->cur_coords,
|
||||
paint_state, time);
|
||||
sym, paint_state, time);
|
||||
g_object_unref (sym);
|
||||
|
||||
core_class->post_paint (core, drawable,
|
||||
paint_options,
|
||||
|
@ -749,7 +765,9 @@ gimp_paint_core_get_paint_buffer (GimpPaintCore *core,
|
|||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
gint *paint_buffer_x,
|
||||
gint *paint_buffer_y)
|
||||
gint *paint_buffer_y,
|
||||
gint *paint_width,
|
||||
gint *paint_height)
|
||||
{
|
||||
GeglBuffer *paint_buffer;
|
||||
|
||||
|
@ -766,7 +784,9 @@ gimp_paint_core_get_paint_buffer (GimpPaintCore *core,
|
|||
paint_options,
|
||||
coords,
|
||||
paint_buffer_x,
|
||||
paint_buffer_y);
|
||||
paint_buffer_y,
|
||||
paint_width,
|
||||
paint_height);
|
||||
|
||||
core->paint_buffer_x = *paint_buffer_x;
|
||||
core->paint_buffer_y = *paint_buffer_y;
|
||||
|
@ -818,8 +838,9 @@ gimp_paint_core_paste (GimpPaintCore *core,
|
|||
*/
|
||||
if (paint_mask != NULL)
|
||||
{
|
||||
GeglBuffer *paint_mask_buffer =
|
||||
gimp_temp_buf_create_buffer ((GimpTempBuf *) paint_mask);
|
||||
GimpTempBuf *modified_mask = gimp_temp_buf_copy (paint_mask);
|
||||
GeglBuffer *paint_mask_buffer =
|
||||
gimp_temp_buf_create_buffer ((GimpTempBuf *) modified_mask);
|
||||
|
||||
gimp_gegl_combine_mask_weird (paint_mask_buffer,
|
||||
GEGL_RECTANGLE (paint_mask_offset_x,
|
||||
|
@ -833,6 +854,7 @@ gimp_paint_core_paste (GimpPaintCore *core,
|
|||
GIMP_IS_AIRBRUSH (core));
|
||||
|
||||
g_object_unref (paint_mask_buffer);
|
||||
gimp_temp_buf_unref (modified_mask);
|
||||
}
|
||||
|
||||
gimp_gegl_apply_mask (core->canvas_buffer,
|
||||
|
|
|
@ -93,7 +93,7 @@ struct _GimpPaintCoreClass
|
|||
void (* paint) (GimpPaintCore *core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time);
|
||||
void (* post_paint) (GimpPaintCore *core,
|
||||
|
@ -112,7 +112,9 @@ struct _GimpPaintCoreClass
|
|||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
gint *paint_buffer_x,
|
||||
gint *paint_buffer_y);
|
||||
gint *paint_buffer_y,
|
||||
gint *paint_width,
|
||||
gint *paint_height);
|
||||
|
||||
GimpUndo * (* push_undo) (GimpPaintCore *core,
|
||||
GimpImage *image,
|
||||
|
@ -168,7 +170,9 @@ GeglBuffer * gimp_paint_core_get_paint_buffer (GimpPaintCore *core,
|
|||
GimpPaintOptions *options,
|
||||
const GimpCoords *coords,
|
||||
gint *paint_buffer_x,
|
||||
gint *paint_buffer_y);
|
||||
gint *paint_buffer_y,
|
||||
gint *paint_width,
|
||||
gint *paint_height);
|
||||
|
||||
GeglBuffer * gimp_paint_core_get_orig_image (GimpPaintCore *core);
|
||||
GeglBuffer * gimp_paint_core_get_orig_proj (GimpPaintCore *core);
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "core/gimpimage.h"
|
||||
#include "core/gimppattern.h"
|
||||
#include "core/gimppickable.h"
|
||||
#include "core/gimpsymmetry.h"
|
||||
|
||||
#include "gimpperspectiveclone.h"
|
||||
#include "gimpperspectivecloneoptions.h"
|
||||
|
@ -48,7 +49,7 @@
|
|||
static void gimp_perspective_clone_paint (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time);
|
||||
|
||||
|
@ -120,7 +121,7 @@ static void
|
|||
gimp_perspective_clone_paint (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time)
|
||||
{
|
||||
|
@ -129,6 +130,10 @@ gimp_perspective_clone_paint (GimpPaintCore *paint_core,
|
|||
GimpContext *context = GIMP_CONTEXT (paint_options);
|
||||
GimpCloneOptions *clone_options = GIMP_CLONE_OPTIONS (paint_options);
|
||||
GimpSourceOptions *options = GIMP_SOURCE_OPTIONS (paint_options);
|
||||
const GimpCoords *coords;
|
||||
|
||||
/* The source is based on the original stroke */
|
||||
coords = gimp_symmetry_get_origin (sym);
|
||||
|
||||
switch (paint_state)
|
||||
{
|
||||
|
@ -284,36 +289,44 @@ gimp_perspective_clone_paint (GimpPaintCore *paint_core,
|
|||
|
||||
gint dest_x;
|
||||
gint dest_y;
|
||||
gint n_strokes;
|
||||
gint i;
|
||||
|
||||
dest_x = coords->x;
|
||||
dest_y = coords->y;
|
||||
|
||||
if (options->align_mode == GIMP_SOURCE_ALIGN_REGISTERED)
|
||||
n_strokes = gimp_symmetry_get_size (sym);
|
||||
for (i = 0; i < n_strokes; i++)
|
||||
{
|
||||
source_core->offset_x = 0;
|
||||
source_core->offset_y = 0;
|
||||
}
|
||||
else if (options->align_mode == GIMP_SOURCE_ALIGN_FIXED)
|
||||
{
|
||||
source_core->offset_x = source_core->src_x - dest_x;
|
||||
source_core->offset_y = source_core->src_y - dest_y;
|
||||
}
|
||||
else if (source_core->first_stroke)
|
||||
{
|
||||
source_core->offset_x = source_core->src_x - dest_x;
|
||||
source_core->offset_y = source_core->src_y - dest_y;
|
||||
coords = gimp_symmetry_get_coords (sym, i);
|
||||
|
||||
/* get destination coordinates in front view perspective */
|
||||
gimp_matrix3_transform_point (&clone->transform_inv,
|
||||
dest_x,
|
||||
dest_y,
|
||||
&clone->dest_x_fv,
|
||||
&clone->dest_y_fv);
|
||||
dest_x = coords->x;
|
||||
dest_y = coords->y;
|
||||
|
||||
source_core->first_stroke = FALSE;
|
||||
if (options->align_mode == GIMP_SOURCE_ALIGN_REGISTERED)
|
||||
{
|
||||
source_core->offset_x = 0;
|
||||
source_core->offset_y = 0;
|
||||
}
|
||||
else if (options->align_mode == GIMP_SOURCE_ALIGN_FIXED)
|
||||
{
|
||||
source_core->offset_x = source_core->src_x - dest_x;
|
||||
source_core->offset_y = source_core->src_y - dest_y;
|
||||
}
|
||||
else if (source_core->first_stroke)
|
||||
{
|
||||
source_core->offset_x = source_core->src_x - dest_x;
|
||||
source_core->offset_y = source_core->src_y - dest_y;
|
||||
|
||||
/* get destination coordinates in front view perspective */
|
||||
gimp_matrix3_transform_point (&clone->transform_inv,
|
||||
dest_x,
|
||||
dest_y,
|
||||
&clone->dest_x_fv,
|
||||
&clone->dest_y_fv);
|
||||
|
||||
source_core->first_stroke = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
gimp_source_core_motion (source_core, drawable, paint_options, coords);
|
||||
gimp_source_core_motion (source_core, drawable, paint_options, sym);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "core/gimpdynamics.h"
|
||||
#include "core/gimpimage.h"
|
||||
#include "core/gimppickable.h"
|
||||
#include "core/gimpsymmetry.h"
|
||||
#include "core/gimptempbuf.h"
|
||||
|
||||
#include "gimpsmudge.h"
|
||||
|
@ -45,20 +46,21 @@ static void gimp_smudge_finalize (GObject *object);
|
|||
static void gimp_smudge_paint (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time);
|
||||
static gboolean gimp_smudge_start (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords);
|
||||
GimpSymmetry *sym);
|
||||
static void gimp_smudge_motion (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords);
|
||||
GimpSymmetry *sym);
|
||||
|
||||
static void gimp_smudge_accumulator_coords (GimpPaintCore *paint_core,
|
||||
const GimpCoords *coords,
|
||||
gint stroke,
|
||||
gint *x,
|
||||
gint *y);
|
||||
|
||||
|
@ -110,10 +112,16 @@ gimp_smudge_finalize (GObject *object)
|
|||
{
|
||||
GimpSmudge *smudge = GIMP_SMUDGE (object);
|
||||
|
||||
if (smudge->accum_buffer)
|
||||
if (smudge->accum_buffers)
|
||||
{
|
||||
g_object_unref (smudge->accum_buffer);
|
||||
smudge->accum_buffer = NULL;
|
||||
GList *iter;
|
||||
|
||||
for (iter = smudge->accum_buffers; iter; iter = g_list_next (iter))
|
||||
{
|
||||
if (iter->data)
|
||||
g_object_unref (iter->data);
|
||||
smudge->accum_buffers = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
|
@ -123,7 +131,7 @@ static void
|
|||
gimp_smudge_paint (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time)
|
||||
{
|
||||
|
@ -135,17 +143,23 @@ gimp_smudge_paint (GimpPaintCore *paint_core,
|
|||
/* initialization fails if the user starts outside the drawable */
|
||||
if (! smudge->initialized)
|
||||
smudge->initialized = gimp_smudge_start (paint_core, drawable,
|
||||
paint_options, coords);
|
||||
paint_options, sym);
|
||||
|
||||
if (smudge->initialized)
|
||||
gimp_smudge_motion (paint_core, drawable, paint_options, coords);
|
||||
gimp_smudge_motion (paint_core, drawable, paint_options, sym);
|
||||
break;
|
||||
|
||||
case GIMP_PAINT_STATE_FINISH:
|
||||
if (smudge->accum_buffer)
|
||||
if (smudge->accum_buffers)
|
||||
{
|
||||
g_object_unref (smudge->accum_buffer);
|
||||
smudge->accum_buffer = NULL;
|
||||
GList *iter;
|
||||
|
||||
for (iter = smudge->accum_buffers; iter; iter = g_list_next (iter))
|
||||
{
|
||||
if (iter->data)
|
||||
g_object_unref (iter->data);
|
||||
smudge->accum_buffers = NULL;
|
||||
}
|
||||
}
|
||||
smudge->initialized = FALSE;
|
||||
break;
|
||||
|
@ -159,72 +173,91 @@ static gboolean
|
|||
gimp_smudge_start (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords)
|
||||
GimpSymmetry *sym)
|
||||
{
|
||||
GimpSmudge *smudge = GIMP_SMUDGE (paint_core);
|
||||
GeglBuffer *paint_buffer;
|
||||
GimpCoords *coords;
|
||||
gint paint_buffer_x;
|
||||
gint paint_buffer_y;
|
||||
gint accum_size;
|
||||
gint n_strokes;
|
||||
gint i;
|
||||
gint x, y;
|
||||
|
||||
paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable,
|
||||
paint_options, coords,
|
||||
&paint_buffer_x,
|
||||
&paint_buffer_y);
|
||||
if (! paint_buffer)
|
||||
return FALSE;
|
||||
coords = gimp_symmetry_get_origin (sym);
|
||||
gimp_brush_core_eval_transform_dynamics (GIMP_BRUSH_CORE (paint_core),
|
||||
drawable,
|
||||
paint_options,
|
||||
coords);
|
||||
|
||||
gimp_smudge_accumulator_size (paint_options, coords, &accum_size);
|
||||
|
||||
/* Allocate the accumulation buffer */
|
||||
smudge->accum_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
|
||||
accum_size,
|
||||
accum_size),
|
||||
babl_format ("RGBA float"));
|
||||
|
||||
/* adjust the x and y coordinates to the upper left corner of the
|
||||
* accumulator
|
||||
*/
|
||||
gimp_smudge_accumulator_coords (paint_core, coords, &x, &y);
|
||||
|
||||
/* If clipped, prefill the smudge buffer with the color at the
|
||||
* brush position.
|
||||
*/
|
||||
if (x != paint_buffer_x ||
|
||||
y != paint_buffer_y ||
|
||||
accum_size != gegl_buffer_get_width (paint_buffer) ||
|
||||
accum_size != gegl_buffer_get_height (paint_buffer))
|
||||
n_strokes = gimp_symmetry_get_size (sym);
|
||||
for (i = 0; i < n_strokes; i++)
|
||||
{
|
||||
GimpRGB pixel;
|
||||
GeglColor *color;
|
||||
GeglBuffer *accum_buffer;
|
||||
|
||||
gimp_pickable_get_color_at (GIMP_PICKABLE (drawable),
|
||||
CLAMP ((gint) coords->x,
|
||||
0,
|
||||
gimp_item_get_width (GIMP_ITEM (drawable)) - 1),
|
||||
CLAMP ((gint) coords->y,
|
||||
0,
|
||||
gimp_item_get_height (GIMP_ITEM (drawable)) - 1),
|
||||
&pixel);
|
||||
coords = gimp_symmetry_get_coords (sym, i);
|
||||
paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable,
|
||||
paint_options, coords,
|
||||
&paint_buffer_x,
|
||||
&paint_buffer_y,
|
||||
NULL, NULL);
|
||||
if (! paint_buffer)
|
||||
return FALSE;
|
||||
|
||||
color = gimp_gegl_color_new (&pixel);
|
||||
gegl_buffer_set_color (smudge->accum_buffer, NULL, color);
|
||||
g_object_unref (color);
|
||||
gimp_smudge_accumulator_size (paint_options, coords, &accum_size);
|
||||
|
||||
/* Allocate the accumulation buffer */
|
||||
accum_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
|
||||
accum_size,
|
||||
accum_size),
|
||||
babl_format ("RGBA float"));
|
||||
smudge->accum_buffers = g_list_prepend (smudge->accum_buffers,
|
||||
accum_buffer);
|
||||
|
||||
/* adjust the x and y coordinates to the upper left corner of the
|
||||
* accumulator
|
||||
*/
|
||||
gimp_smudge_accumulator_coords (paint_core, coords, i, &x, &y);
|
||||
|
||||
/* If clipped, prefill the smudge buffer with the color at the
|
||||
* brush position.
|
||||
*/
|
||||
if (x != paint_buffer_x ||
|
||||
y != paint_buffer_y ||
|
||||
accum_size != gegl_buffer_get_width (paint_buffer) ||
|
||||
accum_size != gegl_buffer_get_height (paint_buffer))
|
||||
{
|
||||
GimpRGB pixel;
|
||||
GeglColor *color;
|
||||
|
||||
gimp_pickable_get_color_at (GIMP_PICKABLE (drawable),
|
||||
CLAMP ((gint) coords->x,
|
||||
0,
|
||||
gimp_item_get_width (GIMP_ITEM (drawable)) - 1),
|
||||
CLAMP ((gint) coords->y,
|
||||
0,
|
||||
gimp_item_get_height (GIMP_ITEM (drawable)) - 1),
|
||||
&pixel);
|
||||
|
||||
color = gimp_gegl_color_new (&pixel);
|
||||
gegl_buffer_set_color (accum_buffer, NULL, color);
|
||||
g_object_unref (color);
|
||||
}
|
||||
|
||||
/* copy the region under the original painthit. */
|
||||
gegl_buffer_copy (gimp_drawable_get_buffer (drawable),
|
||||
GEGL_RECTANGLE (paint_buffer_x,
|
||||
paint_buffer_y,
|
||||
gegl_buffer_get_width (paint_buffer),
|
||||
gegl_buffer_get_height (paint_buffer)),
|
||||
GEGL_ABYSS_NONE,
|
||||
accum_buffer,
|
||||
GEGL_RECTANGLE (paint_buffer_x - x,
|
||||
paint_buffer_y - y,
|
||||
0, 0));
|
||||
}
|
||||
|
||||
/* copy the region under the original painthit. */
|
||||
gegl_buffer_copy (gimp_drawable_get_buffer (drawable),
|
||||
GEGL_RECTANGLE (paint_buffer_x,
|
||||
paint_buffer_y,
|
||||
gegl_buffer_get_width (paint_buffer),
|
||||
gegl_buffer_get_height (paint_buffer)),
|
||||
GEGL_ABYSS_NONE,
|
||||
smudge->accum_buffer,
|
||||
GEGL_RECTANGLE (paint_buffer_x - x,
|
||||
paint_buffer_y - y,
|
||||
0, 0));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -232,28 +265,35 @@ static void
|
|||
gimp_smudge_motion (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords)
|
||||
GimpSymmetry *sym)
|
||||
{
|
||||
GimpSmudge *smudge = GIMP_SMUDGE (paint_core);
|
||||
GimpSmudgeOptions *options = GIMP_SMUDGE_OPTIONS (paint_options);
|
||||
GimpContext *context = GIMP_CONTEXT (paint_options);
|
||||
GimpDynamics *dynamics = GIMP_BRUSH_CORE (paint_core)->dynamics;
|
||||
GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable));
|
||||
GeglBuffer *paint_buffer;
|
||||
gint paint_buffer_x;
|
||||
gint paint_buffer_y;
|
||||
gint paint_buffer_width;
|
||||
gint paint_buffer_height;
|
||||
gdouble fade_point;
|
||||
gdouble opacity;
|
||||
gdouble rate;
|
||||
gdouble dynamic_rate;
|
||||
gint x, y;
|
||||
gdouble force;
|
||||
GimpSmudge *smudge = GIMP_SMUDGE (paint_core);
|
||||
GimpSmudgeOptions *options = GIMP_SMUDGE_OPTIONS (paint_options);
|
||||
GimpContext *context = GIMP_CONTEXT (paint_options);
|
||||
GimpDynamics *dynamics = GIMP_BRUSH_CORE (paint_core)->dynamics;
|
||||
GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable));
|
||||
GeglBuffer *paint_buffer;
|
||||
gint paint_buffer_x;
|
||||
gint paint_buffer_y;
|
||||
gint paint_buffer_width;
|
||||
gint paint_buffer_height;
|
||||
gdouble fade_point;
|
||||
gdouble opacity;
|
||||
gdouble rate;
|
||||
gdouble dynamic_rate;
|
||||
gint x, y;
|
||||
gdouble force;
|
||||
GeglBuffer *accum_buffer;
|
||||
GimpCoords *coords;
|
||||
GeglNode *op;
|
||||
gint paint_width, paint_height;
|
||||
gint n_strokes;
|
||||
gint i;
|
||||
|
||||
fade_point = gimp_paint_options_get_fade (paint_options, image,
|
||||
paint_core->pixel_dist);
|
||||
|
||||
coords = gimp_symmetry_get_origin (sym);
|
||||
opacity = gimp_dynamics_get_linear_value (dynamics,
|
||||
GIMP_DYNAMICS_OUTPUT_OPACITY,
|
||||
coords,
|
||||
|
@ -262,90 +302,111 @@ gimp_smudge_motion (GimpPaintCore *paint_core,
|
|||
if (opacity == 0.0)
|
||||
return;
|
||||
|
||||
paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable,
|
||||
paint_options, coords,
|
||||
&paint_buffer_x,
|
||||
&paint_buffer_y);
|
||||
if (! paint_buffer)
|
||||
return;
|
||||
gimp_brush_core_eval_transform_dynamics (GIMP_BRUSH_CORE (paint_core),
|
||||
drawable,
|
||||
paint_options,
|
||||
coords);
|
||||
|
||||
paint_buffer_width = gegl_buffer_get_width (paint_buffer);
|
||||
paint_buffer_height = gegl_buffer_get_height (paint_buffer);
|
||||
n_strokes = gimp_symmetry_get_size (sym);
|
||||
for (i = 0; i < n_strokes; i++)
|
||||
{
|
||||
coords = gimp_symmetry_get_coords (sym, i);
|
||||
|
||||
/* Get the unclipped acumulator coordinates */
|
||||
gimp_smudge_accumulator_coords (paint_core, coords, &x, &y);
|
||||
paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable,
|
||||
paint_options, coords,
|
||||
&paint_buffer_x,
|
||||
&paint_buffer_y,
|
||||
&paint_width,
|
||||
&paint_height);
|
||||
if (! paint_buffer)
|
||||
continue;
|
||||
|
||||
/* Enable dynamic rate */
|
||||
dynamic_rate = gimp_dynamics_get_linear_value (dynamics,
|
||||
GIMP_DYNAMICS_OUTPUT_RATE,
|
||||
coords,
|
||||
paint_options,
|
||||
fade_point);
|
||||
op = gimp_symmetry_get_operation (sym, i,
|
||||
paint_width,
|
||||
paint_height);
|
||||
|
||||
rate = (options->rate / 100.0) * dynamic_rate;
|
||||
paint_buffer_width = gegl_buffer_get_width (paint_buffer);
|
||||
paint_buffer_height = gegl_buffer_get_height (paint_buffer);
|
||||
|
||||
/* Smudge uses the buffer Accum.
|
||||
* For each successive painthit Accum is built like this
|
||||
* Accum = rate*Accum + (1-rate)*I.
|
||||
* where I is the pixels under the current painthit.
|
||||
* Then the paint area (paint_area) is built as
|
||||
* (Accum,1) (if no alpha),
|
||||
*/
|
||||
/* Get the unclipped acumulator coordinates */
|
||||
gimp_smudge_accumulator_coords (paint_core, coords, i, &x, &y);
|
||||
|
||||
gimp_gegl_smudge_blend (smudge->accum_buffer,
|
||||
GEGL_RECTANGLE (paint_buffer_x - x,
|
||||
paint_buffer_y - y,
|
||||
paint_buffer_width,
|
||||
paint_buffer_height),
|
||||
gimp_drawable_get_buffer (drawable),
|
||||
GEGL_RECTANGLE (paint_buffer_x,
|
||||
paint_buffer_y,
|
||||
paint_buffer_width,
|
||||
paint_buffer_height),
|
||||
smudge->accum_buffer,
|
||||
GEGL_RECTANGLE (paint_buffer_x - x,
|
||||
paint_buffer_y - y,
|
||||
paint_buffer_width,
|
||||
paint_buffer_height),
|
||||
rate);
|
||||
/* Enable dynamic rate */
|
||||
dynamic_rate = gimp_dynamics_get_linear_value (dynamics,
|
||||
GIMP_DYNAMICS_OUTPUT_RATE,
|
||||
coords,
|
||||
paint_options,
|
||||
fade_point);
|
||||
|
||||
gegl_buffer_copy (smudge->accum_buffer,
|
||||
GEGL_RECTANGLE (paint_buffer_x - x,
|
||||
paint_buffer_y - y,
|
||||
paint_buffer_width,
|
||||
paint_buffer_height),
|
||||
GEGL_ABYSS_NONE,
|
||||
paint_buffer,
|
||||
GEGL_RECTANGLE (0, 0, 0, 0));
|
||||
rate = (options->rate / 100.0) * dynamic_rate;
|
||||
|
||||
if (gimp_dynamics_is_output_enabled (dynamics, GIMP_DYNAMICS_OUTPUT_FORCE))
|
||||
force = gimp_dynamics_get_linear_value (dynamics,
|
||||
GIMP_DYNAMICS_OUTPUT_FORCE,
|
||||
coords,
|
||||
paint_options,
|
||||
fade_point);
|
||||
else
|
||||
force = paint_options->brush_force;
|
||||
/* Smudge uses the buffer Accum.
|
||||
* For each successive painthit Accum is built like this
|
||||
* Accum = rate*Accum + (1-rate)*I.
|
||||
* where I is the pixels under the current painthit.
|
||||
* Then the paint area (paint_area) is built as
|
||||
* (Accum,1) (if no alpha),
|
||||
*/
|
||||
|
||||
gimp_brush_core_replace_canvas (GIMP_BRUSH_CORE (paint_core), drawable,
|
||||
coords,
|
||||
MIN (opacity, GIMP_OPACITY_OPAQUE),
|
||||
gimp_context_get_opacity (context),
|
||||
gimp_paint_options_get_brush_mode (paint_options),
|
||||
force,
|
||||
GIMP_PAINT_INCREMENTAL);
|
||||
accum_buffer = g_list_nth_data (smudge->accum_buffers, i);
|
||||
gimp_gegl_smudge_blend (accum_buffer,
|
||||
GEGL_RECTANGLE (paint_buffer_x - x,
|
||||
paint_buffer_y - y,
|
||||
paint_buffer_width,
|
||||
paint_buffer_height),
|
||||
gimp_drawable_get_buffer (drawable),
|
||||
GEGL_RECTANGLE (paint_buffer_x,
|
||||
paint_buffer_y,
|
||||
paint_buffer_width,
|
||||
paint_buffer_height),
|
||||
accum_buffer,
|
||||
GEGL_RECTANGLE (paint_buffer_x - x,
|
||||
paint_buffer_y - y,
|
||||
paint_buffer_width,
|
||||
paint_buffer_height),
|
||||
rate);
|
||||
|
||||
gegl_buffer_copy (accum_buffer,
|
||||
GEGL_RECTANGLE (paint_buffer_x - x,
|
||||
paint_buffer_y - y,
|
||||
paint_buffer_width,
|
||||
paint_buffer_height),
|
||||
GEGL_ABYSS_NONE,
|
||||
paint_buffer,
|
||||
GEGL_RECTANGLE (0, 0, 0, 0));
|
||||
|
||||
if (gimp_dynamics_is_output_enabled (dynamics, GIMP_DYNAMICS_OUTPUT_FORCE))
|
||||
force = gimp_dynamics_get_linear_value (dynamics,
|
||||
GIMP_DYNAMICS_OUTPUT_FORCE,
|
||||
coords,
|
||||
paint_options,
|
||||
fade_point);
|
||||
else
|
||||
force = paint_options->brush_force;
|
||||
|
||||
gimp_brush_core_replace_canvas (GIMP_BRUSH_CORE (paint_core), drawable,
|
||||
coords,
|
||||
MIN (opacity, GIMP_OPACITY_OPAQUE),
|
||||
gimp_context_get_opacity (context),
|
||||
gimp_paint_options_get_brush_mode (paint_options),
|
||||
force,
|
||||
GIMP_PAINT_INCREMENTAL, op);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_smudge_accumulator_coords (GimpPaintCore *paint_core,
|
||||
const GimpCoords *coords,
|
||||
gint stroke,
|
||||
gint *x,
|
||||
gint *y)
|
||||
{
|
||||
GimpSmudge *smudge = GIMP_SMUDGE (paint_core);
|
||||
GeglBuffer *accum_buffer;
|
||||
|
||||
*x = (gint) coords->x - gegl_buffer_get_width (smudge->accum_buffer) / 2;
|
||||
*y = (gint) coords->y - gegl_buffer_get_height (smudge->accum_buffer) / 2;
|
||||
accum_buffer = g_list_nth_data (smudge->accum_buffers, stroke);
|
||||
*x = (gint) coords->x - gegl_buffer_get_width (accum_buffer) / 2;
|
||||
*y = (gint) coords->y - gegl_buffer_get_height (accum_buffer) / 2;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
@ -37,7 +37,7 @@ struct _GimpSmudge
|
|||
GimpBrushCore parent_instance;
|
||||
|
||||
gboolean initialized;
|
||||
GeglBuffer *accum_buffer;
|
||||
GList *accum_buffers;
|
||||
};
|
||||
|
||||
struct _GimpSmudgeClass
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "core/gimperror.h"
|
||||
#include "core/gimpimage.h"
|
||||
#include "core/gimppickable.h"
|
||||
#include "core/gimpsymmetry.h"
|
||||
|
||||
#include "gimpsourcecore.h"
|
||||
#include "gimpsourceoptions.h"
|
||||
|
@ -65,7 +66,7 @@ static gboolean gimp_source_core_start (GimpPaintCore *paint_core,
|
|||
static void gimp_source_core_paint (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time);
|
||||
|
||||
|
@ -73,7 +74,7 @@ static void gimp_source_core_paint (GimpPaintCore *paint_core,
|
|||
static void gimp_source_core_motion (GimpSourceCore *source_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords);
|
||||
GimpSymmetry *sym);
|
||||
#endif
|
||||
|
||||
static gboolean gimp_source_core_real_use_source (GimpSourceCore *source_core,
|
||||
|
@ -253,12 +254,16 @@ static void
|
|||
gimp_source_core_paint (GimpPaintCore *paint_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GimpSymmetry *sym,
|
||||
GimpPaintState paint_state,
|
||||
guint32 time)
|
||||
{
|
||||
GimpSourceCore *source_core = GIMP_SOURCE_CORE (paint_core);
|
||||
GimpSourceOptions *options = GIMP_SOURCE_OPTIONS (paint_options);
|
||||
const GimpCoords *coords;
|
||||
|
||||
/* The source is based on the original stroke */
|
||||
coords = gimp_symmetry_get_origin (sym);
|
||||
|
||||
switch (paint_state)
|
||||
{
|
||||
|
@ -322,7 +327,8 @@ gimp_source_core_paint (GimpPaintCore *paint_core,
|
|||
source_core->src_x = dest_x + source_core->offset_x;
|
||||
source_core->src_y = dest_y + source_core->offset_y;
|
||||
|
||||
gimp_source_core_motion (source_core, drawable, paint_options, coords);
|
||||
gimp_source_core_motion (source_core, drawable, paint_options,
|
||||
sym);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -347,7 +353,7 @@ void
|
|||
gimp_source_core_motion (GimpSourceCore *source_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords)
|
||||
GimpSymmetry *sym)
|
||||
|
||||
{
|
||||
GimpPaintCore *paint_core = GIMP_PAINT_CORE (source_core);
|
||||
|
@ -357,6 +363,8 @@ gimp_source_core_motion (GimpSourceCore *source_core,
|
|||
GimpPickable *src_pickable = NULL;
|
||||
GeglBuffer *src_buffer = NULL;
|
||||
GeglRectangle src_rect;
|
||||
gint base_src_offset_x;
|
||||
gint base_src_offset_y;
|
||||
gint src_offset_x;
|
||||
gint src_offset_y;
|
||||
GeglBuffer *paint_buffer;
|
||||
|
@ -368,20 +376,27 @@ gimp_source_core_motion (GimpSourceCore *source_core,
|
|||
gint paint_area_height;
|
||||
gdouble fade_point;
|
||||
gdouble opacity;
|
||||
GeglNode *op;
|
||||
GimpCoords *origin;
|
||||
GimpCoords *coords;
|
||||
gint n_strokes;
|
||||
gint i;
|
||||
|
||||
fade_point = gimp_paint_options_get_fade (paint_options, image,
|
||||
paint_core->pixel_dist);
|
||||
|
||||
origin = gimp_symmetry_get_origin (sym);
|
||||
/* Some settings are based on the original stroke. */
|
||||
opacity = gimp_dynamics_get_linear_value (dynamics,
|
||||
GIMP_DYNAMICS_OUTPUT_OPACITY,
|
||||
coords,
|
||||
origin,
|
||||
paint_options,
|
||||
fade_point);
|
||||
if (opacity == 0.0)
|
||||
return;
|
||||
|
||||
src_offset_x = source_core->offset_x;
|
||||
src_offset_y = source_core->offset_y;
|
||||
base_src_offset_x = source_core->offset_x;
|
||||
base_src_offset_y = source_core->offset_y;
|
||||
|
||||
if (gimp_source_core_use_source (source_core, options))
|
||||
{
|
||||
|
@ -397,69 +412,91 @@ gimp_source_core_motion (GimpSourceCore *source_core,
|
|||
gimp_item_get_offset (GIMP_ITEM (source_core->src_drawable),
|
||||
&off_x, &off_y);
|
||||
|
||||
src_offset_x += off_x;
|
||||
src_offset_y += off_y;
|
||||
base_src_offset_x += off_x;
|
||||
base_src_offset_y += off_y;
|
||||
}
|
||||
|
||||
gimp_pickable_flush (src_pickable);
|
||||
}
|
||||
|
||||
paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable,
|
||||
paint_options, coords,
|
||||
&paint_buffer_x,
|
||||
&paint_buffer_y);
|
||||
if (! paint_buffer)
|
||||
return;
|
||||
gimp_brush_core_eval_transform_dynamics (GIMP_BRUSH_CORE (paint_core),
|
||||
drawable,
|
||||
paint_options,
|
||||
origin);
|
||||
|
||||
paint_area_offset_x = 0;
|
||||
paint_area_offset_y = 0;
|
||||
paint_area_width = gegl_buffer_get_width (paint_buffer);
|
||||
paint_area_height = gegl_buffer_get_height (paint_buffer);
|
||||
|
||||
if (gimp_source_core_use_source (source_core, options))
|
||||
n_strokes = gimp_symmetry_get_size (sym);
|
||||
for (i = 0; i < n_strokes; i++)
|
||||
{
|
||||
src_buffer =
|
||||
GIMP_SOURCE_CORE_GET_CLASS (source_core)->get_source (source_core,
|
||||
drawable,
|
||||
paint_options,
|
||||
src_pickable,
|
||||
src_offset_x,
|
||||
src_offset_y,
|
||||
paint_buffer,
|
||||
paint_buffer_x,
|
||||
paint_buffer_y,
|
||||
&paint_area_offset_x,
|
||||
&paint_area_offset_y,
|
||||
&paint_area_width,
|
||||
&paint_area_height,
|
||||
&src_rect);
|
||||
if (! src_buffer)
|
||||
return;
|
||||
coords = gimp_symmetry_get_coords (sym, i);
|
||||
|
||||
paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable,
|
||||
paint_options, coords,
|
||||
&paint_buffer_x,
|
||||
&paint_buffer_y,
|
||||
NULL, NULL);
|
||||
if (! paint_buffer)
|
||||
continue;
|
||||
|
||||
paint_area_offset_x = 0;
|
||||
paint_area_offset_y = 0;
|
||||
paint_area_width = gegl_buffer_get_width (paint_buffer);
|
||||
paint_area_height = gegl_buffer_get_height (paint_buffer);
|
||||
|
||||
src_offset_x = base_src_offset_x;
|
||||
src_offset_y = base_src_offset_y;
|
||||
if (gimp_source_core_use_source (source_core, options))
|
||||
{
|
||||
/* When using a source, use the same for every stroke. */
|
||||
src_offset_x = src_offset_x - coords->x + origin->x;
|
||||
src_offset_y = src_offset_y - coords->y + origin->y;
|
||||
src_buffer =
|
||||
GIMP_SOURCE_CORE_GET_CLASS (source_core)->get_source (source_core,
|
||||
drawable,
|
||||
paint_options,
|
||||
src_pickable,
|
||||
src_offset_x,
|
||||
src_offset_y,
|
||||
paint_buffer,
|
||||
paint_buffer_x,
|
||||
paint_buffer_y,
|
||||
&paint_area_offset_x,
|
||||
&paint_area_offset_y,
|
||||
&paint_area_width,
|
||||
&paint_area_height,
|
||||
&src_rect);
|
||||
|
||||
if (! src_buffer)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Set the paint buffer to transparent */
|
||||
gegl_buffer_clear (paint_buffer, NULL);
|
||||
|
||||
op = gimp_symmetry_get_operation (sym, i,
|
||||
gegl_buffer_get_width (paint_buffer),
|
||||
gegl_buffer_get_height (paint_buffer));
|
||||
GIMP_SOURCE_CORE_GET_CLASS (source_core)->motion (source_core,
|
||||
drawable,
|
||||
paint_options,
|
||||
coords,
|
||||
op,
|
||||
opacity,
|
||||
src_pickable,
|
||||
src_buffer,
|
||||
&src_rect,
|
||||
src_offset_x,
|
||||
src_offset_y,
|
||||
paint_buffer,
|
||||
paint_buffer_x,
|
||||
paint_buffer_y,
|
||||
paint_area_offset_x,
|
||||
paint_area_offset_y,
|
||||
paint_area_width,
|
||||
paint_area_height);
|
||||
|
||||
if (src_buffer)
|
||||
g_object_unref (src_buffer);
|
||||
}
|
||||
|
||||
/* Set the paint buffer to transparent */
|
||||
gegl_buffer_clear (paint_buffer, NULL);
|
||||
|
||||
GIMP_SOURCE_CORE_GET_CLASS (source_core)->motion (source_core,
|
||||
drawable,
|
||||
paint_options,
|
||||
coords,
|
||||
opacity,
|
||||
src_pickable,
|
||||
src_buffer,
|
||||
&src_rect,
|
||||
src_offset_x,
|
||||
src_offset_y,
|
||||
paint_buffer,
|
||||
paint_buffer_x,
|
||||
paint_buffer_y,
|
||||
paint_area_offset_x,
|
||||
paint_area_offset_y,
|
||||
paint_area_width,
|
||||
paint_area_height);
|
||||
|
||||
if (src_buffer)
|
||||
g_object_unref (src_buffer);
|
||||
}
|
||||
|
||||
gboolean
|
||||
|
|
|
@ -77,6 +77,7 @@ struct _GimpSourceCoreClass
|
|||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords,
|
||||
GeglNode *op,
|
||||
gdouble opacity,
|
||||
GimpPickable *src_pickable,
|
||||
GeglBuffer *src_buffer,
|
||||
|
@ -103,7 +104,7 @@ gboolean gimp_source_core_use_source (GimpSourceCore *source_core,
|
|||
void gimp_source_core_motion (GimpSourceCore *source_core,
|
||||
GimpDrawable *drawable,
|
||||
GimpPaintOptions *paint_options,
|
||||
const GimpCoords *coords);
|
||||
GimpSymmetry *sym);
|
||||
|
||||
|
||||
#endif /* __GIMP_SOURCE_CORE_H__ */
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include "pdb-types.h"
|
||||
|
||||
#include "cairo.h"
|
||||
#include "core/gimpguide.h"
|
||||
#include "core/gimpimage-guides.h"
|
||||
#include "core/gimpimage-undo-push.h"
|
||||
|
|
|
@ -337,6 +337,8 @@ libappwidgets_a_sources = \
|
|||
gimpstringaction.h \
|
||||
gimpstrokeeditor.c \
|
||||
gimpstrokeeditor.h \
|
||||
gimpsymmetryeditor.c \
|
||||
gimpsymmetryeditor.h \
|
||||
gimptagentry.c \
|
||||
gimptagentry.h \
|
||||
gimptagpopup.c \
|
||||
|
|
|
@ -558,6 +558,7 @@
|
|||
#define GIMP_HELP_INFO_DIALOG "gimp-info-dialog"
|
||||
#define GIMP_HELP_MODULE_DIALOG "gimp-module-dialog"
|
||||
#define GIMP_HELP_NAVIGATION_DIALOG "gimp-navigation-dialog"
|
||||
#define GIMP_HELP_SYMMETRY_DIALOG "gimp-symmetry-dialog"
|
||||
#define GIMP_HELP_TEXT_EDITOR_DIALOG "gimp-text-editor-dialog"
|
||||
#define GIMP_HELP_TIPS_DIALOG "gimp-tips-dialog"
|
||||
#define GIMP_HELP_UNDO_DIALOG "gimp-undo-dialog"
|
||||
|
|
|
@ -0,0 +1,506 @@
|
|||
/* GIMP - The GNU Image Manipulation Program
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* gimpsymmetryeditor.c
|
||||
* Copyright (C) 2015 Jehan <jehan@girinstud.io>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gegl.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "libgimpwidgets/gimpwidgets.h"
|
||||
|
||||
#include "widgets-types.h"
|
||||
|
||||
#include "config/gimpguiconfig.h"
|
||||
|
||||
#include "core/gimp.h"
|
||||
#include "core/gimpcontext.h"
|
||||
#include "core/gimpimage.h"
|
||||
#include "core/gimpimage-symmetry.h"
|
||||
#include "core/gimpsymmetry.h"
|
||||
|
||||
#include "gimpdocked.h"
|
||||
#include "gimpmenufactory.h"
|
||||
#include "gimppropwidgets.h"
|
||||
#include "gimpspinscale.h"
|
||||
#include "gimpsymmetryeditor.h"
|
||||
|
||||
#include "gimp-intl.h"
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_GIMP,
|
||||
};
|
||||
|
||||
struct _GimpSymmetryEditorPrivate
|
||||
{
|
||||
Gimp *gimp;
|
||||
GimpImage *image;
|
||||
|
||||
GtkWidget *menu;
|
||||
GtkWidget *options_frame;
|
||||
};
|
||||
|
||||
static void gimp_symmetry_editor_docked_iface_init (GimpDockedInterface *iface);
|
||||
|
||||
/* Signal handlers on the GObject. */
|
||||
static void gimp_symmetry_editor_constructed (GObject *object);
|
||||
static void gimp_symmetry_editor_dispose (GObject *object);
|
||||
static void gimp_symmetry_editor_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec);
|
||||
static void gimp_symmetry_editor_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
|
||||
/* Signal handlers on the context. */
|
||||
static void gimp_symmetry_editor_image_changed (GimpContext *context,
|
||||
GimpImage *image,
|
||||
GimpSymmetryEditor *editor);
|
||||
|
||||
/* Signal handlers on the contextual image. */
|
||||
static void gimp_symmetry_editor_symmetry_notify (GimpImage *image,
|
||||
GParamSpec *pspec,
|
||||
GimpSymmetryEditor *editor);
|
||||
|
||||
/* Signal handlers on the symmetry. */
|
||||
static void gimp_symmetry_editor_symmetry_updated (GimpSymmetry *symmetry,
|
||||
GimpImage *image,
|
||||
GimpSymmetryEditor *editor);
|
||||
|
||||
/* Private functions. */
|
||||
static void gimp_symmetry_editor_set_options (GimpSymmetryEditor *editor,
|
||||
GimpSymmetry *symmetry);
|
||||
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GimpSymmetryEditor, gimp_symmetry_editor,
|
||||
GIMP_TYPE_EDITOR,
|
||||
G_IMPLEMENT_INTERFACE (GIMP_TYPE_DOCKED,
|
||||
gimp_symmetry_editor_docked_iface_init))
|
||||
|
||||
#define parent_class gimp_symmetry_editor_parent_class
|
||||
|
||||
static void
|
||||
gimp_symmetry_editor_class_init (GimpSymmetryEditorClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->constructed = gimp_symmetry_editor_constructed;
|
||||
object_class->dispose = gimp_symmetry_editor_dispose;
|
||||
object_class->set_property = gimp_symmetry_editor_set_property;
|
||||
object_class->get_property = gimp_symmetry_editor_get_property;
|
||||
|
||||
g_object_class_install_property (object_class, PROP_GIMP,
|
||||
g_param_spec_object ("gimp",
|
||||
NULL, NULL,
|
||||
GIMP_TYPE_GIMP,
|
||||
GIMP_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY));
|
||||
|
||||
g_type_class_add_private (klass, sizeof (GimpSymmetryEditorPrivate));
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_symmetry_editor_docked_iface_init (GimpDockedInterface *docked_iface)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_symmetry_editor_init (GimpSymmetryEditor *editor)
|
||||
{
|
||||
GtkScrolledWindow *scrolled_window;
|
||||
|
||||
editor->p = G_TYPE_INSTANCE_GET_PRIVATE (editor,
|
||||
GIMP_TYPE_SYMMETRY_EDITOR,
|
||||
GimpSymmetryEditorPrivate);
|
||||
|
||||
gtk_widget_set_size_request (GTK_WIDGET (editor), -1, 200);
|
||||
|
||||
/* Scrolled window to keep the dock size reasonable. */
|
||||
scrolled_window = GTK_SCROLLED_WINDOW (gtk_scrolled_window_new (NULL, NULL));
|
||||
|
||||
gtk_scrolled_window_set_policy (scrolled_window,
|
||||
GTK_POLICY_AUTOMATIC,
|
||||
GTK_POLICY_AUTOMATIC);
|
||||
|
||||
gtk_box_pack_start (GTK_BOX (editor),
|
||||
GTK_WIDGET (scrolled_window),
|
||||
TRUE, TRUE, 0);
|
||||
gtk_widget_show (GTK_WIDGET (scrolled_window));
|
||||
|
||||
/* A frame to hold the symmetry options. */
|
||||
editor->p->options_frame = gimp_frame_new ("");
|
||||
gtk_scrolled_window_add_with_viewport (scrolled_window,
|
||||
editor->p->options_frame);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_symmetry_editor_constructed (GObject *object)
|
||||
{
|
||||
GimpSymmetryEditor *editor = GIMP_SYMMETRY_EDITOR (object);
|
||||
GimpContext *user_context;
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->constructed (object);
|
||||
|
||||
user_context = gimp_get_user_context (editor->p->gimp);
|
||||
|
||||
g_signal_connect_object (user_context, "image-changed",
|
||||
G_CALLBACK (gimp_symmetry_editor_image_changed),
|
||||
editor,
|
||||
0);
|
||||
|
||||
gimp_symmetry_editor_image_changed (user_context,
|
||||
gimp_context_get_image (user_context),
|
||||
editor);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_symmetry_editor_dispose (GObject *object)
|
||||
{
|
||||
GimpSymmetryEditor *editor = GIMP_SYMMETRY_EDITOR (object);
|
||||
|
||||
g_clear_object (&editor->p->image);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_symmetry_editor_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GimpSymmetryEditor *editor = GIMP_SYMMETRY_EDITOR (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_GIMP:
|
||||
editor->p->gimp = g_value_get_object (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_symmetry_editor_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GimpSymmetryEditor *editor = GIMP_SYMMETRY_EDITOR (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_GIMP:
|
||||
g_value_set_object (value, editor->p->gimp);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_symmetry_editor_image_changed (GimpContext *context,
|
||||
GimpImage *image,
|
||||
GimpSymmetryEditor *editor)
|
||||
{
|
||||
GimpGuiConfig *guiconfig;
|
||||
|
||||
if (image == editor->p->image)
|
||||
return;
|
||||
|
||||
guiconfig = GIMP_GUI_CONFIG (editor->p->gimp->config);
|
||||
|
||||
/* Disconnect and unref the previous image. */
|
||||
if (editor->p->image)
|
||||
{
|
||||
g_signal_handlers_disconnect_by_func (editor->p->image,
|
||||
G_CALLBACK (gimp_symmetry_editor_symmetry_notify),
|
||||
editor);
|
||||
g_object_unref (editor->p->image);
|
||||
editor->p->image = NULL;
|
||||
}
|
||||
|
||||
/* Destroy the previous menu. */
|
||||
if (editor->p->menu)
|
||||
gtk_widget_destroy (editor->p->menu);
|
||||
editor->p->menu = NULL;
|
||||
|
||||
if (image && guiconfig->playground_symmetry)
|
||||
{
|
||||
GtkListStore *store;
|
||||
GtkTreeIter iter;
|
||||
GList *syms;
|
||||
GList *sym_iter;
|
||||
GimpSymmetry *symmetry;
|
||||
|
||||
store = gimp_int_store_new ();
|
||||
|
||||
/* The menu of available symmetries. */
|
||||
syms = gimp_image_symmetry_list ();
|
||||
for (sym_iter = syms; sym_iter; sym_iter = g_list_next (sym_iter))
|
||||
{
|
||||
GimpSymmetryClass *klass;
|
||||
GType type;
|
||||
|
||||
type = (GType) sym_iter->data;
|
||||
klass = g_type_class_ref (type);
|
||||
|
||||
gtk_list_store_prepend (store, &iter);
|
||||
gtk_list_store_set (store, &iter,
|
||||
GIMP_INT_STORE_LABEL,
|
||||
klass->label,
|
||||
GIMP_INT_STORE_VALUE,
|
||||
sym_iter->data,
|
||||
-1);
|
||||
g_type_class_unref (klass);
|
||||
}
|
||||
g_list_free (syms);
|
||||
|
||||
gtk_list_store_prepend (store, &iter);
|
||||
gtk_list_store_set (store, &iter,
|
||||
GIMP_INT_STORE_LABEL, _("None"),
|
||||
GIMP_INT_STORE_VALUE, G_TYPE_NONE,
|
||||
-1);
|
||||
editor->p->menu = gimp_prop_int_combo_box_new (G_OBJECT (image),
|
||||
"symmetry",
|
||||
GIMP_INT_STORE (store));
|
||||
g_object_unref (store);
|
||||
|
||||
gimp_int_combo_box_set_label (GIMP_INT_COMBO_BOX (editor->p->menu),
|
||||
_("Symmetry"));
|
||||
g_object_set (editor->p->menu, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
|
||||
|
||||
gtk_box_pack_start (GTK_BOX (editor), editor->p->menu,
|
||||
FALSE, FALSE, 0);
|
||||
gtk_box_reorder_child (GTK_BOX (editor), editor->p->menu, 0);
|
||||
gtk_widget_show (editor->p->menu);
|
||||
|
||||
/* Connect to symmetry change. */
|
||||
g_signal_connect (image, "notify::symmetry",
|
||||
G_CALLBACK (gimp_symmetry_editor_symmetry_notify),
|
||||
editor);
|
||||
|
||||
/* Update the symmetry options. */
|
||||
symmetry = gimp_image_symmetry_selected (image);
|
||||
gimp_symmetry_editor_set_options (editor, symmetry);
|
||||
editor->p->image = g_object_ref (image);
|
||||
}
|
||||
else if (! guiconfig->playground_symmetry)
|
||||
{
|
||||
GtkWidget *label;
|
||||
|
||||
/* Display a text when the feature is disabled. */
|
||||
label = gtk_label_new (_("Symmetry Painting is disabled.\n"
|
||||
"You can enable the feature in the "
|
||||
"\"Experimental Playground\" section of \"Preferences\"."));
|
||||
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
|
||||
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
|
||||
gimp_label_set_attributes (GTK_LABEL (label),
|
||||
PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
|
||||
-1);
|
||||
gtk_container_add (GTK_CONTAINER (editor->p->options_frame), label);
|
||||
gtk_widget_show (label);
|
||||
gtk_widget_show (editor->p->options_frame);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_symmetry_editor_symmetry_notify (GimpImage *image,
|
||||
GParamSpec *pspec,
|
||||
GimpSymmetryEditor *editor)
|
||||
{
|
||||
GimpSymmetry *symmetry = NULL;
|
||||
|
||||
if (image &&
|
||||
(symmetry = gimp_image_symmetry_selected (image)))
|
||||
{
|
||||
g_signal_connect (symmetry, "update-ui",
|
||||
G_CALLBACK (gimp_symmetry_editor_symmetry_updated),
|
||||
editor);
|
||||
}
|
||||
|
||||
gimp_symmetry_editor_set_options (editor, symmetry);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_symmetry_editor_symmetry_updated (GimpSymmetry *symmetry,
|
||||
GimpImage *image,
|
||||
GimpSymmetryEditor *editor)
|
||||
{
|
||||
GimpContext *context;
|
||||
|
||||
g_return_if_fail (GIMP_IS_SYMMETRY (symmetry));
|
||||
|
||||
context = gimp_get_user_context (editor->p->gimp);
|
||||
if (image != context->image ||
|
||||
symmetry != gimp_image_symmetry_selected (image))
|
||||
{
|
||||
g_signal_handlers_disconnect_by_func (G_OBJECT (symmetry),
|
||||
gimp_symmetry_editor_symmetry_updated,
|
||||
editor);
|
||||
return;
|
||||
}
|
||||
|
||||
gimp_symmetry_editor_set_options (editor, symmetry);
|
||||
}
|
||||
|
||||
/* private functions */
|
||||
|
||||
static void
|
||||
gimp_symmetry_editor_set_options (GimpSymmetryEditor *editor,
|
||||
GimpSymmetry *symmetry)
|
||||
{
|
||||
GimpSymmetryClass *klass;
|
||||
GtkWidget *frame;
|
||||
GtkWidget *vbox;
|
||||
GParamSpec **specs;
|
||||
gint n_properties;
|
||||
gint i;
|
||||
|
||||
frame = editor->p->options_frame;
|
||||
|
||||
/* Clean the old frame */
|
||||
gtk_widget_hide (frame);
|
||||
gtk_container_foreach (GTK_CONTAINER (frame),
|
||||
(GtkCallback) gtk_widget_destroy, NULL);
|
||||
|
||||
if (! symmetry || symmetry->type == GIMP_TYPE_SYMMETRY)
|
||||
return;
|
||||
|
||||
klass = g_type_class_ref (symmetry->type);
|
||||
gtk_frame_set_label (GTK_FRAME (frame),
|
||||
klass->label);
|
||||
g_type_class_unref (klass);
|
||||
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
|
||||
gtk_container_add (GTK_CONTAINER (frame), vbox);
|
||||
gtk_widget_show (vbox);
|
||||
|
||||
specs = gimp_symmetry_get_settings (symmetry, &n_properties);
|
||||
|
||||
for (i = 0; i < n_properties; i++)
|
||||
{
|
||||
GParamSpec *spec;
|
||||
const gchar *name;
|
||||
const gchar *blurb;
|
||||
|
||||
if (specs[i] == NULL)
|
||||
{
|
||||
GtkWidget *separator;
|
||||
|
||||
separator = gtk_hseparator_new ();
|
||||
gtk_box_pack_start (GTK_BOX (vbox), separator,
|
||||
FALSE, FALSE, 0);
|
||||
gtk_widget_show (separator);
|
||||
continue;
|
||||
}
|
||||
spec = G_PARAM_SPEC (specs[i]);
|
||||
|
||||
name = g_param_spec_get_name (spec);
|
||||
blurb = g_param_spec_get_blurb (spec);
|
||||
|
||||
switch (spec->value_type)
|
||||
{
|
||||
case G_TYPE_BOOLEAN:
|
||||
{
|
||||
GtkWidget *checkbox;
|
||||
|
||||
checkbox = gimp_prop_check_button_new (G_OBJECT (symmetry),
|
||||
name,
|
||||
blurb);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), checkbox,
|
||||
FALSE, FALSE, 0);
|
||||
gtk_widget_show (checkbox);
|
||||
}
|
||||
break;
|
||||
case G_TYPE_DOUBLE:
|
||||
case G_TYPE_INT:
|
||||
case G_TYPE_UINT:
|
||||
{
|
||||
GtkWidget *scale;
|
||||
gdouble minimum;
|
||||
gdouble maximum;
|
||||
|
||||
if (spec->value_type == G_TYPE_DOUBLE)
|
||||
{
|
||||
minimum = G_PARAM_SPEC_DOUBLE (spec)->minimum;
|
||||
maximum = G_PARAM_SPEC_DOUBLE (spec)->maximum;
|
||||
}
|
||||
else if (spec->value_type == G_TYPE_INT)
|
||||
{
|
||||
minimum = G_PARAM_SPEC_INT (spec)->minimum;
|
||||
maximum = G_PARAM_SPEC_INT (spec)->maximum;
|
||||
}
|
||||
else
|
||||
{
|
||||
minimum = G_PARAM_SPEC_UINT (spec)->minimum;
|
||||
maximum = G_PARAM_SPEC_UINT (spec)->maximum;
|
||||
}
|
||||
|
||||
scale = gimp_prop_spin_scale_new (G_OBJECT (symmetry),
|
||||
name, blurb,
|
||||
1.0, 10.0, 1);
|
||||
gimp_spin_scale_set_scale_limits (GIMP_SPIN_SCALE (scale),
|
||||
minimum,
|
||||
maximum);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), scale, TRUE, TRUE, 0);
|
||||
gtk_widget_show (scale);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Type of parameter we haven't handled yet. */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
g_free (specs);
|
||||
|
||||
/* Finally show the frame. */
|
||||
gtk_widget_show (frame);
|
||||
}
|
||||
|
||||
/* public functions */
|
||||
|
||||
GtkWidget *
|
||||
gimp_symmetry_editor_new (Gimp *gimp,
|
||||
GimpImage *image,
|
||||
GimpMenuFactory *menu_factory)
|
||||
{
|
||||
GimpSymmetryEditor *editor;
|
||||
|
||||
g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
|
||||
g_return_val_if_fail (GIMP_IS_MENU_FACTORY (menu_factory), NULL);
|
||||
g_return_val_if_fail (image == NULL || GIMP_IS_IMAGE (image), NULL);
|
||||
|
||||
editor = g_object_new (GIMP_TYPE_SYMMETRY_EDITOR,
|
||||
"gimp", gimp,
|
||||
"menu-factory", menu_factory,
|
||||
NULL);
|
||||
|
||||
return GTK_WIDGET (editor);
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/* GIMP - The GNU Image Manipulation Program
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* gimpsymmetryeditor.h
|
||||
* Copyright (C) 2015 Jehan <jehan@girinstud.io>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __GIMP_SYMMETRY_EDITOR_H__
|
||||
#define __GIMP_SYMMETRY_EDITOR_H__
|
||||
|
||||
|
||||
#include "gimpeditor.h"
|
||||
|
||||
|
||||
#define GIMP_TYPE_SYMMETRY_EDITOR (gimp_symmetry_editor_get_type ())
|
||||
#define GIMP_SYMMETRY_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_SYMMETRY_EDITOR, GimpSymmetryEditor))
|
||||
#define GIMP_SYMMETRY_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_SYMMETRY_EDITOR, GimpSymmetryEditorClass))
|
||||
#define GIMP_IS_SYMMETRY_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_SYMMETRY_EDITOR))
|
||||
#define GIMP_IS_SYMMETRY_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_SYMMETRY_EDITOR))
|
||||
#define GIMP_SYMMETRY_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_SYMMETRY_EDITOR, GimpSymmetryEditorClass))
|
||||
|
||||
|
||||
typedef struct _GimpSymmetryEditorPrivate GimpSymmetryEditorPrivate;
|
||||
typedef struct _GimpSymmetryEditorClass GimpSymmetryEditorClass;
|
||||
|
||||
struct _GimpSymmetryEditor
|
||||
{
|
||||
GimpEditor parent_instance;
|
||||
|
||||
GimpSymmetryEditorPrivate *p;
|
||||
};
|
||||
|
||||
struct _GimpSymmetryEditorClass
|
||||
{
|
||||
GimpEditorClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
GType gimp_symmetry_editor_get_type (void) G_GNUC_CONST;
|
||||
GtkWidget * gimp_symmetry_editor_new (Gimp *gimp,
|
||||
GimpImage *image,
|
||||
GimpMenuFactory *menu_factory);
|
||||
|
||||
|
||||
#endif /* __GIMP_SYMMETRY_EDITOR_H__ */
|
|
@ -76,6 +76,7 @@ typedef struct _GimpHistogramEditor GimpHistogramEditor;
|
|||
typedef struct _GimpImageEditor GimpImageEditor;
|
||||
typedef struct _GimpSamplePointEditor GimpSamplePointEditor;
|
||||
typedef struct _GimpSelectionEditor GimpSelectionEditor;
|
||||
typedef struct _GimpSymmetryEditor GimpSymmetryEditor;
|
||||
typedef struct _GimpUndoEditor GimpUndoEditor;
|
||||
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
|
@ -161,6 +161,7 @@ icons16_DATA = \
|
|||
16/gimp-shape-circle.png \
|
||||
16/gimp-shape-diamond.png \
|
||||
16/gimp-shape-square.png \
|
||||
16/gimp-symmetry.png \
|
||||
16/gimp-template.png \
|
||||
16/gimp-text-layer.png \
|
||||
16/gimp-toilet-paper.png \
|
||||
|
@ -376,6 +377,7 @@ icons24_DATA = \
|
|||
24/gimp-move-to-screen.png \
|
||||
24/gimp-print-resolution.png \
|
||||
24/gimp-sample-point.png \
|
||||
24/gimp-symmetry.png \
|
||||
24/gimp-template.png \
|
||||
24/gimp-text-dir-ltr.png \
|
||||
24/gimp-text-dir-rtl.png \
|
||||
|
|
|
@ -232,6 +232,7 @@ G_BEGIN_DECLS
|
|||
#define GIMP_STOCK_INPUT_DEVICE "gimp-input-device"
|
||||
#define GIMP_STOCK_CURSOR "gimp-cursor"
|
||||
#define GIMP_STOCK_SAMPLE_POINT "gimp-sample-point"
|
||||
#define GIMP_STOCK_SYMMETRY "gimp-symmetry"
|
||||
#define GIMP_STOCK_DYNAMICS "gimp-dynamics"
|
||||
#define GIMP_STOCK_TOOL_PRESET "gimp-tool-preset"
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
<menuitem action="dialogs-undo-history" />
|
||||
<menuitem action="dialogs-cursor" />
|
||||
<menuitem action="dialogs-sample-points" />
|
||||
<menuitem action="dialogs-symmetry"/>
|
||||
<separator />
|
||||
<menuitem action="dialogs-colors" />
|
||||
<menuitem action="dialogs-brushes" />
|
||||
|
|
Loading…
Reference in New Issue