app: improve gimp_brush_core_color_area_with_pixmap()
Reimplement gimp_brush_core_color_area_with_pixmap(), which copies
the brush's dab to the paint buffer when using a pixmap brush, in
terms of gimp-gegl-loops. This simplifies the functions,
parallelizes processing, and transparently handles float brushes.
Replace the "mode" parameter of the function with an "apply_mask"
parameter, which specifies whether to apply the brush's mask to
the dab as part of copying. Avoid applying the mask in
GimpPaintbrush; previously, we would erroneously apply the mask
twice when using the paintbrush tool: once when copying the
dab to the paint buffer, and again when pasting the paint buffer
to the canvas.
We still apply the mask in GimpSmudge, which results in the same
double-application behavior, however, this might be less practical
to fix.
(cherry picked from commit 5b09af4390)
This commit is contained in:
parent
25ed35af62
commit
2ea7deff4d
4 changed files with 59 additions and 178 deletions
|
|
@ -29,6 +29,7 @@
|
|||
#include "operations/layer-modes/gimp-layer-modes.h"
|
||||
|
||||
#include "gegl/gimp-babl.h"
|
||||
#include "gegl/gimp-gegl-loops.h"
|
||||
|
||||
#include "core/gimpbrush-header.h"
|
||||
#include "core/gimpbrushgenerated.h"
|
||||
|
|
@ -110,15 +111,6 @@ static const GimpTempBuf *
|
|||
static void gimp_brush_core_invalidate_cache (GimpBrush *brush,
|
||||
GimpBrushCore *core);
|
||||
|
||||
/* brush pipe utility functions */
|
||||
static void gimp_brush_core_paint_line_pixmap_mask (const Babl *fish,
|
||||
const GimpTempBuf *pixmap_mask,
|
||||
const GimpTempBuf *brush_mask,
|
||||
gfloat *d,
|
||||
gint x,
|
||||
gint y,
|
||||
gint width);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (GimpBrushCore, gimp_brush_core, GIMP_TYPE_PAINT_CORE)
|
||||
|
||||
|
|
@ -1221,196 +1213,85 @@ gimp_brush_core_eval_transform_dynamics (GimpBrushCore *core,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**************************************************/
|
||||
/* Brush pipe utility functions */
|
||||
/**************************************************/
|
||||
|
||||
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,
|
||||
GimpBrushApplicationMode mode)
|
||||
gimp_brush_core_color_area_with_pixmap (GimpBrushCore *core,
|
||||
GimpDrawable *drawable,
|
||||
const GimpCoords *coords,
|
||||
GeglNode *op,
|
||||
GeglBuffer *area,
|
||||
gint area_x,
|
||||
gint area_y,
|
||||
gboolean apply_mask)
|
||||
{
|
||||
GeglBufferIterator *iter;
|
||||
GeglRectangle *roi;
|
||||
gint ulx;
|
||||
gint uly;
|
||||
gint offsetx;
|
||||
gint offsety;
|
||||
const GimpTempBuf *pixmap_mask;
|
||||
const GimpTempBuf *brush_mask;
|
||||
const Babl *area_format;
|
||||
const Babl *pixmap_format;
|
||||
const Babl *fish = NULL;
|
||||
const GimpTempBuf *pixmap;
|
||||
GeglBuffer *pixmap_buffer;
|
||||
const GimpTempBuf *mask;
|
||||
GeglBuffer *mask_buffer;
|
||||
gint area_width;
|
||||
gint area_height;
|
||||
gint ul_x;
|
||||
gint ul_y;
|
||||
gint offset_x;
|
||||
gint offset_y;
|
||||
|
||||
g_return_if_fail (GIMP_IS_BRUSH (core->brush));
|
||||
g_return_if_fail (gimp_brush_get_pixmap (core->brush) != NULL);
|
||||
|
||||
/* scale the brushes */
|
||||
pixmap_mask = gimp_brush_core_transform_pixmap (core, core->brush, op);
|
||||
/* scale the brush */
|
||||
pixmap = gimp_brush_core_transform_pixmap (core, core->brush, op);
|
||||
|
||||
if (! pixmap_mask)
|
||||
if (! pixmap)
|
||||
return;
|
||||
|
||||
if (mode != GIMP_BRUSH_HARD)
|
||||
brush_mask = gimp_brush_core_transform_mask (core, core->brush, op);
|
||||
if (apply_mask)
|
||||
mask = gimp_brush_core_transform_mask (core, core->brush, op);
|
||||
else
|
||||
brush_mask = NULL;
|
||||
mask = NULL;
|
||||
|
||||
/* Calculate upper left corner of brush as in
|
||||
* gimp_paint_core_get_paint_area. Ugly to have to do this here, too.
|
||||
*/
|
||||
ulx = (gint) floor (coords->x) - (gimp_temp_buf_get_width (pixmap_mask) >> 1);
|
||||
uly = (gint) floor (coords->y) - (gimp_temp_buf_get_height (pixmap_mask) >> 1);
|
||||
ul_x = (gint) floor (coords->x) - (gimp_temp_buf_get_width (pixmap) >> 1);
|
||||
ul_y = (gint) floor (coords->y) - (gimp_temp_buf_get_height (pixmap) >> 1);
|
||||
|
||||
/* Not sure why this is necessary, but empirically the code does
|
||||
* not work without it for even-sided brushes. See bug #166622.
|
||||
*/
|
||||
if (gimp_temp_buf_get_width (pixmap_mask) % 2 == 0)
|
||||
ulx += ROUND (coords->x) - floor (coords->x);
|
||||
if (gimp_temp_buf_get_height (pixmap_mask) % 2 == 0)
|
||||
uly += ROUND (coords->y) - floor (coords->y);
|
||||
if (gimp_temp_buf_get_width (pixmap) % 2 == 0)
|
||||
ul_x += ROUND (coords->x) - floor (coords->x);
|
||||
if (gimp_temp_buf_get_height (pixmap) % 2 == 0)
|
||||
ul_y += ROUND (coords->y) - floor (coords->y);
|
||||
|
||||
offsetx = area_x - ulx;
|
||||
offsety = area_y - uly;
|
||||
offset_x = area_x - ul_x;
|
||||
offset_y = area_y - ul_y;
|
||||
|
||||
area_format = gegl_buffer_get_format (area);
|
||||
pixmap_format = gimp_temp_buf_get_format (pixmap_mask);
|
||||
area_width = gegl_buffer_get_width (area);
|
||||
area_height = gegl_buffer_get_height (area);
|
||||
|
||||
iter = gegl_buffer_iterator_new (area, NULL, 0, area_format,
|
||||
GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1);
|
||||
pixmap_buffer = gimp_temp_buf_create_buffer (pixmap);
|
||||
|
||||
if (mode == GIMP_BRUSH_SOFT && brush_mask)
|
||||
gimp_gegl_buffer_copy (pixmap_buffer,
|
||||
GEGL_RECTANGLE (offset_x, offset_y,
|
||||
area_width, area_height),
|
||||
GEGL_ABYSS_NONE,
|
||||
area,
|
||||
GEGL_RECTANGLE (0, 0,
|
||||
area_width, area_height));
|
||||
|
||||
g_object_unref (pixmap_buffer);
|
||||
|
||||
if (mask)
|
||||
{
|
||||
GimpImageBaseType pixmap_base_type;
|
||||
GimpPrecision pixmap_precision;
|
||||
mask_buffer = gimp_temp_buf_create_buffer (mask);
|
||||
|
||||
pixmap_base_type = gimp_babl_format_get_base_type (pixmap_format);
|
||||
pixmap_precision = gimp_babl_format_get_precision (pixmap_format);
|
||||
gimp_gegl_apply_mask (mask_buffer,
|
||||
GEGL_RECTANGLE (offset_x, offset_y,
|
||||
area_width, area_height),
|
||||
area,
|
||||
GEGL_RECTANGLE (0, 0,
|
||||
area_width, area_height),
|
||||
1.0);
|
||||
|
||||
fish = babl_fish (gimp_babl_format (pixmap_base_type, pixmap_precision,
|
||||
TRUE),
|
||||
area_format);
|
||||
}
|
||||
else
|
||||
{
|
||||
fish = babl_fish (pixmap_format, area_format);
|
||||
|
||||
brush_mask = NULL;
|
||||
}
|
||||
|
||||
roi = &iter->items[0].roi;
|
||||
|
||||
while (gegl_buffer_iterator_next (iter))
|
||||
{
|
||||
gfloat *d = iter->items[0].data;
|
||||
gint y;
|
||||
|
||||
for (y = 0; y < roi->height; y++)
|
||||
{
|
||||
gimp_brush_core_paint_line_pixmap_mask (fish,
|
||||
pixmap_mask, brush_mask,
|
||||
d, offsetx, y + offsety,
|
||||
roi->width);
|
||||
d += roi->width * 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_brush_core_paint_line_pixmap_mask (const Babl *fish,
|
||||
const GimpTempBuf *pixmap_mask,
|
||||
const GimpTempBuf *brush_mask,
|
||||
gfloat *d,
|
||||
gint x,
|
||||
gint y,
|
||||
gint width)
|
||||
{
|
||||
const Babl *pixmap_format;
|
||||
gint pixmap_bytes;
|
||||
gint pixmap_width;
|
||||
gint pixmap_height;
|
||||
guchar *b;
|
||||
|
||||
pixmap_width = gimp_temp_buf_get_width (pixmap_mask);
|
||||
pixmap_height = gimp_temp_buf_get_height (pixmap_mask);
|
||||
|
||||
/* Make sure x, y are positive */
|
||||
x %= pixmap_width;
|
||||
if (x < 0)
|
||||
x += pixmap_width;
|
||||
|
||||
y %= pixmap_height;
|
||||
if (y < 0)
|
||||
y += pixmap_height;
|
||||
|
||||
pixmap_format = gimp_temp_buf_get_format (pixmap_mask);
|
||||
pixmap_bytes = babl_format_get_bytes_per_pixel (pixmap_format);
|
||||
|
||||
/* Point to the appropriate scanline */
|
||||
b = (gimp_temp_buf_get_data (pixmap_mask) +
|
||||
y * pixmap_width * pixmap_bytes);
|
||||
|
||||
if (brush_mask)
|
||||
{
|
||||
const guchar *mask = (gimp_temp_buf_get_data (brush_mask) +
|
||||
y * pixmap_width);
|
||||
guchar *line_buf = g_alloca (width * (pixmap_bytes + 1));
|
||||
guchar *l = line_buf;
|
||||
gint i;
|
||||
|
||||
g_return_if_fail (gimp_temp_buf_get_width (brush_mask) == pixmap_width);
|
||||
g_return_if_fail (gimp_temp_buf_get_height (brush_mask) == pixmap_height);
|
||||
|
||||
/* put the source pixmap's pixels, plus the mask's alpha, into
|
||||
* one line, so we can use one single call to babl_process() to
|
||||
* convert the entire line
|
||||
*/
|
||||
|
||||
for (i = 0; i < width; i++)
|
||||
{
|
||||
gint p_bytes = pixmap_bytes;
|
||||
guchar *p = b + x * p_bytes;
|
||||
|
||||
while (p_bytes--)
|
||||
*l++ = *p++;
|
||||
|
||||
*l++ = mask[x++];
|
||||
|
||||
if (x == pixmap_width)
|
||||
x = 0;
|
||||
}
|
||||
|
||||
babl_process (fish, line_buf, d, width);
|
||||
}
|
||||
else
|
||||
{
|
||||
guchar *line_buf = g_alloca (width * (pixmap_bytes));
|
||||
guchar *l = line_buf;
|
||||
gint i;
|
||||
|
||||
/* put the source pixmap's pixels into one line, so we can use
|
||||
* one single call to babl_process() to convert the entire line
|
||||
*/
|
||||
for (i = 0; i < width; i++)
|
||||
{
|
||||
gint p_bytes = pixmap_bytes;
|
||||
guchar *p = b + x * p_bytes;
|
||||
|
||||
while (p_bytes--)
|
||||
*l++ = *p++;
|
||||
|
||||
x++;
|
||||
|
||||
if (x == pixmap_width)
|
||||
x = 0;
|
||||
}
|
||||
|
||||
babl_process (fish, line_buf, d, width);
|
||||
g_object_unref (mask_buffer);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ void gimp_brush_core_color_area_with_pixmap
|
|||
GeglBuffer *area,
|
||||
gint area_x,
|
||||
gint area_y,
|
||||
GimpBrushApplicationMode mode);
|
||||
gboolean apply_mask);
|
||||
|
||||
const GimpTempBuf * gimp_brush_core_get_brush_mask
|
||||
(GimpBrushCore *core,
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ _gimp_paintbrush_motion (GimpPaintCore *paint_core,
|
|||
paint_buffer,
|
||||
paint_buffer_x,
|
||||
paint_buffer_y,
|
||||
gimp_paint_options_get_brush_mode (paint_options));
|
||||
FALSE);
|
||||
|
||||
paint_appl_mode = GIMP_PAINT_INCREMENTAL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -501,7 +501,7 @@ gimp_smudge_motion (GimpPaintCore *paint_core,
|
|||
paint_buffer,
|
||||
paint_buffer_x,
|
||||
paint_buffer_y,
|
||||
gimp_paint_options_get_brush_mode (paint_options));
|
||||
TRUE);
|
||||
}
|
||||
|
||||
gimp_gegl_smudge_with_paint (accum_buffer,
|
||||
|
|
|
|||
Loading…
Reference in a new issue