Commit cd91144f authored by Daniel Sabo's avatar Daniel Sabo

Faster paintcore

Directly access the brush and paint buffers rather than using
GEGL iterators.

Replicate the relevant parts of GimpApplicator using direct
access.
parent 413a516f
......@@ -55,6 +55,8 @@ libapppaint_a_sources = \
gimpinkundo.h \
gimppaintcore.c \
gimppaintcore.h \
gimppaintcore-loops.c \
gimppaintcore-loops.h \
gimppaintcore-stroke.c \
gimppaintcore-stroke.h \
gimppaintcoreundo.c \
......
......@@ -822,10 +822,17 @@ gimp_brush_core_get_paint_buffer (GimpPaintCore *paint_core,
if ((x2 - x1) && (y2 - y1))
{
GimpTempBuf *temp_buf;
const Babl *format;
if (gimp_drawable_get_linear (drawable))
format = babl_format ("RGBA float");
else
format = babl_format ("R'G'B'A float");
if (paint_core->paint_buffer &&
gegl_buffer_get_width (paint_core->paint_buffer) == (x2 - x1) &&
gegl_buffer_get_height (paint_core->paint_buffer) == (y2 - y1))
gegl_buffer_get_height (paint_core->paint_buffer) == (y2 - y1) &&
gegl_buffer_get_format (paint_core->paint_buffer) == format )
{
*paint_buffer_x = x1;
*paint_buffer_y = y1;
......@@ -834,7 +841,7 @@ gimp_brush_core_get_paint_buffer (GimpPaintCore *paint_core,
}
temp_buf = gimp_temp_buf_new ((x2 - x1), (y2 - y1),
babl_format ("RGBA float"));
format);
*paint_buffer_x = x1;
*paint_buffer_y = y1;
......@@ -939,7 +946,6 @@ gimp_brush_core_paste_canvas (GimpBrushCore *core,
if (brush_mask)
{
GimpPaintCore *paint_core = GIMP_PAINT_CORE (core);
GeglBuffer *paint_mask;
gint x;
gint y;
gint off_x;
......@@ -951,18 +957,12 @@ gimp_brush_core_paste_canvas (GimpBrushCore *core,
off_x = (x < 0) ? -x : 0;
off_y = (y < 0) ? -y : 0;
paint_mask = gimp_temp_buf_create_buffer ((GimpTempBuf *) brush_mask);
gimp_paint_core_paste (paint_core, paint_mask,
GEGL_RECTANGLE (off_x, off_y,
gegl_buffer_get_width (paint_core->paint_buffer),
gegl_buffer_get_height (paint_core->paint_buffer)),
gimp_paint_core_paste (paint_core, brush_mask,
off_x, off_y,
drawable,
brush_opacity,
image_opacity, paint_mode,
mode);
g_object_unref (paint_mask);
}
}
......@@ -989,7 +989,6 @@ gimp_brush_core_replace_canvas (GimpBrushCore *core,
if (brush_mask)
{
GimpPaintCore *paint_core = GIMP_PAINT_CORE (core);
GeglBuffer *paint_mask;
gint x;
gint y;
gint off_x;
......@@ -1001,18 +1000,12 @@ gimp_brush_core_replace_canvas (GimpBrushCore *core,
off_x = (x < 0) ? -x : 0;
off_y = (y < 0) ? -y : 0;
paint_mask = gimp_temp_buf_create_buffer ((GimpTempBuf *) brush_mask);
gimp_paint_core_replace (paint_core, paint_mask,
GEGL_RECTANGLE (off_x, off_y,
gegl_buffer_get_width (paint_core->paint_buffer),
gegl_buffer_get_height (paint_core->paint_buffer)),
gimp_paint_core_replace (paint_core, brush_mask,
off_x, off_y,
drawable,
brush_opacity,
image_opacity,
mode);
g_object_unref (paint_mask);
}
}
......
......@@ -220,9 +220,15 @@ gimp_ink_get_paint_buffer (GimpPaintCore *paint_core,
if ((x2 - x1) && (y2 - y1))
{
GimpTempBuf *temp_buf;
const Babl *format;
if (gimp_drawable_get_linear (drawable))
format = babl_format ("RGBA float");
else
format = babl_format ("R'G'B'A float");
temp_buf = gimp_temp_buf_new ((x2 - x1), (y2 - y1),
babl_format ("RGBA float"));
format);
*paint_buffer_x = x1;
*paint_buffer_y = y1;
......@@ -332,11 +338,9 @@ gimp_ink_motion (GimpPaintCore *paint_core,
/* draw the paint_area using the just rendered canvas_buffer as mask */
gimp_paint_core_paste (paint_core,
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)),
NULL,
paint_core->paint_buffer_x,
paint_core->paint_buffer_y,
drawable,
GIMP_OPACITY_OPAQUE,
gimp_context_get_opacity (context),
......
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 2013 Daniel Sabo
*
* 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 "paint-types.h"
#include "core/gimptempbuf.h"
#include "gimppaintcore-loops.h"
#include "operations/gimplayermodefunctions.h"
void
combine_paint_mask_to_canvas_mask (const GimpTempBuf *paint_mask,
gint mask_x_offset,
gint mask_y_offset,
GeglBuffer *canvas_buffer,
gint x_offset,
gint y_offset,
gfloat opacity,
gboolean stipple)
{
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);
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;
iter = gegl_buffer_iterator_new (canvas_buffer, &roi, 0,
babl_format ("Y float"),
GEGL_BUFFER_READWRITE, GEGL_ABYSS_NONE);
if (stipple)
{
if (mask_format == babl_format ("Y u8"))
{
const guint8 *mask_data = (const guint8 *) gimp_temp_buf_get_data (paint_mask);
mask_data += mask_start_offset;
while (gegl_buffer_iterator_next (iter))
{
gfloat *out_pixel = (gfloat *)iter->data[0];
int iy, ix;
for (iy = 0; iy < iter->roi[0].height; iy++)
{
int mask_offset = (iy + iter->roi[0].y - roi.y) * mask_stride + iter->roi[0].x - roi.x;
const guint8 *mask_pixel = &mask_data[mask_offset];
for (ix = 0; ix < iter->roi[0].width; ix++)
{
out_pixel[0] += (1.0 - out_pixel[0]) * (*mask_pixel / 255.0f) * opacity;
mask_pixel += 1;
out_pixel += 1;
}
}
}
}
else if (mask_format == babl_format ("Y float"))
{
const gfloat *mask_data = (const gfloat *) gimp_temp_buf_get_data (paint_mask);
mask_data += mask_start_offset;
while (gegl_buffer_iterator_next (iter))
{
gfloat *out_pixel = (gfloat *)iter->data[0];
int iy, ix;
for (iy = 0; iy < iter->roi[0].height; iy++)
{
int mask_offset = (iy + iter->roi[0].y - roi.y) * mask_stride + iter->roi[0].x - roi.x;
const gfloat *mask_pixel = &mask_data[mask_offset];
for (ix = 0; ix < iter->roi[0].width; ix++)
{
out_pixel[0] += (1.0 - out_pixel[0]) * (*mask_pixel) * opacity;
mask_pixel += 1;
out_pixel += 1;
}
}
}
}
else
{
g_warning("Mask format not supported: %s", babl_get_name (mask_format));
}
}
else
{
if (mask_format == babl_format ("Y u8"))
{
const guint8 *mask_data = (const guint8 *) gimp_temp_buf_get_data (paint_mask);
mask_data += mask_start_offset;
while (gegl_buffer_iterator_next (iter))
{
gfloat *out_pixel = (gfloat *)iter->data[0];
int iy, ix;
for (iy = 0; iy < iter->roi[0].height; iy++)
{
int mask_offset = (iy + iter->roi[0].y - roi.y) * mask_stride + iter->roi[0].x - roi.x;
const guint8 *mask_pixel = &mask_data[mask_offset];
for (ix = 0; ix < iter->roi[0].width; ix++)
{
if (opacity > out_pixel[0])
out_pixel[0] += (opacity - out_pixel[0]) * (*mask_pixel / 255.0f) * opacity;
mask_pixel += 1;
out_pixel += 1;
}
}
}
}
else if (mask_format == babl_format ("Y float"))
{
const gfloat *mask_data = (const gfloat *) gimp_temp_buf_get_data (paint_mask);
mask_data += mask_start_offset;
while (gegl_buffer_iterator_next (iter))
{
gfloat *out_pixel = (gfloat *)iter->data[0];
int iy, ix;
for (iy = 0; iy < iter->roi[0].height; iy++)
{
int mask_offset = (iy + iter->roi[0].y - roi.y) * mask_stride + iter->roi[0].x - roi.x;
const gfloat *mask_pixel = &mask_data[mask_offset];
for (ix = 0; ix < iter->roi[0].width; ix++)
{
if (opacity > out_pixel[0])
out_pixel[0] += (opacity - out_pixel[0]) * (*mask_pixel) * opacity;
mask_pixel += 1;
out_pixel += 1;
}
}
}
}
else
{
g_warning("Mask format not supported: %s", babl_get_name (mask_format));
}
}
}
void
canvas_buffer_to_paint_buf_alpha (GimpTempBuf *paint_buf,
GeglBuffer *canvas_buffer,
gint x_offset,
gint y_offset)
{
/* Copy the canvas buffer in rect to the paint buffer's alpha channel */
GeglRectangle roi;
GeglBufferIterator *iter;
const guint paint_stride = gimp_temp_buf_get_width (paint_buf);
gfloat *paint_data = (gfloat *) gimp_temp_buf_get_data (paint_buf);
roi.x = x_offset;
roi.y = y_offset;
roi.width = gimp_temp_buf_get_width (paint_buf);
roi.height = gimp_temp_buf_get_height (paint_buf);
iter = gegl_buffer_iterator_new (canvas_buffer, &roi, 0,
babl_format ("Y float"),
GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
while (gegl_buffer_iterator_next (iter))
{
gfloat *canvas_pixel = (gfloat *)iter->data[0];
int iy, ix;
for (iy = 0; iy < iter->roi[0].height; iy++)
{
int paint_offset = (iy + iter->roi[0].y - roi.y) * paint_stride + iter->roi[0].x - roi.x;
float *paint_pixel = &paint_data[paint_offset * 4];
for (ix = 0; ix < iter->roi[0].width; ix++)
{
paint_pixel[3] *= *canvas_pixel;
canvas_pixel += 1;
paint_pixel += 4;
}
}
}
}
void
paint_mask_to_paint_buffer (const GimpTempBuf *paint_mask,
gint mask_x_offset,
gint mask_y_offset,
GimpTempBuf *paint_buf,
gfloat paint_opacity)
{
gint width = gimp_temp_buf_get_width (paint_buf);
gint height = gimp_temp_buf_get_height (paint_buf);
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);
int iy, ix;
gfloat *paint_pixel = (gfloat *)gimp_temp_buf_get_data (paint_buf);
/* Validate that the paint buffer is withing the bounds of the paint mask */
g_return_if_fail (width <= gimp_temp_buf_get_width (paint_mask) - mask_x_offset);
g_return_if_fail (height <= gimp_temp_buf_get_height (paint_mask) - mask_y_offset);
if (mask_format == babl_format ("Y u8"))
{
const guint8 *mask_data = (const guint8 *) gimp_temp_buf_get_data (paint_mask);
mask_data += mask_start_offset;
for (iy = 0; iy < height; iy++)
{
int mask_offset = iy * mask_stride;
const guint8 *mask_pixel = &mask_data[mask_offset];
for (ix = 0; ix < width; ix++)
{
paint_pixel[3] *= (((gfloat)*mask_pixel) / 255.0f) * paint_opacity;
mask_pixel += 1;
paint_pixel += 4;
}
}
}
else if (mask_format == babl_format ("Y float"))
{
const gfloat *mask_data = (const gfloat *) gimp_temp_buf_get_data (paint_mask);
mask_data += mask_start_offset;
for (iy = 0; iy < height; iy++)
{
int mask_offset = iy * mask_stride;
const gfloat *mask_pixel = &mask_data[mask_offset];
for (ix = 0; ix < width; ix++)
{
paint_pixel[3] *= (*mask_pixel) * paint_opacity;
mask_pixel += 1;
paint_pixel += 4;
}
}
}
}
void
do_layer_blend (GeglBuffer *src_buffer,
GeglBuffer *dst_buffer,
GimpTempBuf *paint_buf,
GeglBuffer *mask_buffer,
gfloat opacity,
gint x_offset,
gint y_offset,
gint mask_x_offset,
gint mask_y_offset,
gboolean linear_mode,
GimpLayerModeEffects paint_mode)
{
GeglRectangle roi;
GeglRectangle mask_roi;
GeglRectangle process_roi;
const Babl *iterator_format;
GeglBufferIterator *iter;
const guint paint_stride = gimp_temp_buf_get_width (paint_buf);
gfloat *paint_data = (gfloat *) gimp_temp_buf_get_data (paint_buf);
GimpLayerModeFunction apply_func = get_layer_mode_function (paint_mode);
if (linear_mode)
iterator_format = babl_format ("RGBA float");
else
iterator_format = babl_format ("R'G'B'A float");
roi.x = x_offset;
roi.y = y_offset;
roi.width = gimp_temp_buf_get_width (paint_buf);
roi.height = gimp_temp_buf_get_height (paint_buf);
mask_roi.x = roi.x + mask_x_offset;
mask_roi.y = roi.y + mask_y_offset;
mask_roi.width = roi.width;
mask_roi.height = roi.height;
g_return_if_fail (gimp_temp_buf_get_format (paint_buf) == iterator_format);
iter = gegl_buffer_iterator_new (dst_buffer, &roi, 0,
iterator_format,
GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE);
gegl_buffer_iterator_add (iter, src_buffer, &roi, 0,
iterator_format,
GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
if (mask_buffer)
{
gegl_buffer_iterator_add (iter, mask_buffer, &mask_roi, 0,
babl_format ("Y float"),
GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
}
while (gegl_buffer_iterator_next(iter))
{
gfloat *out_pixel = (gfloat *)iter->data[0];
gfloat *in_pixel = (gfloat *)iter->data[1];
gfloat *mask_pixel = NULL;
gfloat *paint_pixel = paint_data + ((iter->roi[0].y - roi.y) * paint_stride + iter->roi[0].x - roi.x) * 4;
int iy;
if (mask_buffer)
mask_pixel = (gfloat *)iter->data[2];
process_roi.x = iter->roi[0].x;
process_roi.width = iter->roi[0].width;
process_roi.height = 1;
for (iy = 0; iy < iter->roi[0].height; iy++)
{
process_roi.x = iter->roi[0].y + iy;
(*apply_func) (in_pixel,
paint_pixel,
mask_pixel,
out_pixel,
opacity,
iter->roi[0].width,
&process_roi,
0);
in_pixel += iter->roi[0].width * 4;
out_pixel += iter->roi[0].width * 4;
if (mask_buffer)
mask_pixel += iter->roi[0].width;
paint_pixel += paint_stride * 4;
}
}
}
void
mask_components_onto (GeglBuffer *src_buffer,
GeglBuffer *aux_buffer,
GeglBuffer *dst_buffer,
GeglRectangle *roi,
GimpComponentMask mask,
gboolean linear_mode)
{
GeglBufferIterator *iter;
const Babl *iterator_format;
if (linear_mode)
iterator_format = babl_format ("RGBA float");
else
iterator_format = babl_format ("R'G'B'A float");
iter = gegl_buffer_iterator_new (dst_buffer, roi, 0,
iterator_format,
GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE);
gegl_buffer_iterator_add (iter, src_buffer, roi, 0,
iterator_format,
GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
gegl_buffer_iterator_add (iter, aux_buffer, roi, 0,
iterator_format,
GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
while (gegl_buffer_iterator_next(iter))
{
gfloat *dest = (gfloat *)iter->data[0];
gfloat *src = (gfloat *)iter->data[1];
gfloat *aux = (gfloat *)iter->data[2];
glong samples = iter->length;
while (samples--)
{
dest[RED] = (mask & GIMP_COMPONENT_RED) ? aux[RED] : src[RED];
dest[GREEN] = (mask & GIMP_COMPONENT_GREEN) ? aux[GREEN] : src[GREEN];
dest[BLUE] = (mask & GIMP_COMPONENT_BLUE) ? aux[BLUE] : src[BLUE];
dest[ALPHA] = (mask & GIMP_COMPONENT_ALPHA) ? aux[ALPHA] : src[ALPHA];
src += 4;
aux += 4;
dest += 4;
}
}
}
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 2013 Daniel Sabo
*
* 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/>.
*/
void combine_paint_mask_to_canvas_mask (const GimpTempBuf *paint_mask,
gint mask_x_offset,
gint mask_y_offset,
GeglBuffer *canvas_buffer,
gint x_offset,
gint y_offset,
gfloat opacity,
gboolean stipple);
void canvas_buffer_to_paint_buf_alpha (GimpTempBuf *paint_buf,
GeglBuffer *canvas_buffer,
gint x_offset,
gint y_offset);
void paint_mask_to_paint_buffer (const GimpTempBuf *paint_mask,
gint mask_x_offset,
gint mask_y_offset,
GimpTempBuf *paint_buf,
gfloat paint_opacity);
void do_layer_blend (GeglBuffer *src_buffer,
GeglBuffer *dst_buffer,
GimpTempBuf *paint_buf,
GeglBuffer *mask_buffer,
gfloat opacity,
gint x_offset,
gint y_offset,
gint mask_x_offset,
gint mask_y_offset,
gboolean linear_mode,
GimpLayerModeEffects paint_mode);
void mask_components_onto (GeglBuffer *src_buffer,
GeglBuffer *aux_buffer,
GeglBuffer *dst_buffer,
GeglRectangle *roi,
GimpComponentMask mask,
gboolean linear_mode);
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
* Copyright (C) 2013 Daniel Sabo
*
* 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
......@@ -26,10 +27,8 @@
#include "paint-types.h"
#include "gegl/gimp-gegl-loops.h"
#include "gegl/gimp-gegl-nodes.h"
#include "gegl/gimp-gegl-utils.h"
#include "gegl/gimpapplicator.h"
#include "core/gimp.h"
#include "core/gimp-utils.h"
......@@ -42,6 +41,7 @@
#include "gimppaintcore.h"
#include "gimppaintcoreundo.h"
#include "gimppaintcore-loops.h"
#include "gimppaintoptions.h"
#include "gimpairbrush.h"
......@@ -405,9 +405,6 @@ gimp_paint_core_start (GimpPaintCore *core,
if (GIMP_DRAWABLE (mask) == drawable || gimp_channel_is_empty (mask))
mask = NULL;
core->applicator = gimp_applicator_new (NULL,
gimp_drawable_get_linear (drawable));
if (mask)
{
GeglBuffer *mask_buffer;
......@@ -417,15 +414,42 @@ gimp_paint_core_start (GimpPaintCore *core,
mask_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask));
gimp_item_get_offset (item, &offset_x, &offset_y);
gimp_applicator_set_mask_buffer (core->applicator, mask_buffer);
gimp_applicator_set_mask_offset (core->applicator,
-offset_x, -offset_y);
g_object_ref (mask_buffer);
core->mask_buffer = mask_buffer;
core->mask_x_offset = -offset_x;
core->mask_y_offset = -offset_y;
}
else
{
core->mask_buffer = NULL;
}
gimp_applicator_set_affect (core->applicator,
gimp_drawable_get_active_mask (drawable));
gimp_applicator_set_dest_buffer (core->applicator,
gimp_drawable_get_buffer (drawable));
core->linear_mode = gimp_drawable_get_linear (drawable);
/* Allocate the scratch buffer if there's a component mask */
if (gimp_drawable_get_active_mask (drawable) != GIMP_COMPONENT_ALL)
{
if (core->linear_mode)
{
core->comp_buffer =
gegl_buffer_new (GEGL_RECTANGLE (0, 0,
gimp_item_get_width (item),
gimp_item_get_height (item)),
babl_format ("RGBA float"));
}
else
{
core->comp_buffer =
gegl_buffer_new (GEGL_RECTANGLE (0, 0,
gimp_item_get_width (item),
gimp_item_get_height (item)),
babl_format ("R'G'B'A float"));
}
}
else
{
core->comp_buffer = NULL;
}
/* Freeze the drawable preview so that it isn't constantly updated. */
gimp_viewable_preview_freeze (GIMP_VIEWABLE (drawable));
......@@ -444,18 +468,24 @@ gimp_paint_core_finish (GimpPaintCore *core,
g_return_if_fail (GIMP_IS_DRAWABLE (drawable));
g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)));
if (core->applicator)
{
g_object_unref (core->applicator);
core->applicator = NULL;
}
if (core->stroke_buffer)
{
g_array_free (core->stroke_buffer, TRUE);
core->stroke_buffer = NULL;
}
if (core->mask_buffer)
{
g_object_unref (core->mask_buffer);
core->mask_buffer = NULL;
}
if (core->comp_buffer)
{
g_object_unref (core->comp_buffer);
core->comp_buffer = NULL;
}
image = gimp_item_get_image (GIMP_ITEM (drawable));
/* Determine if any part of the image has been altered--
......@@ -737,75 +767,94 @@ gimp_paint_core_get_orig_proj (GimpPaintCore *core)
void
gimp_paint_core_paste (GimpPaintCore *core,
GeglBuffer *paint_mask,
const GeglRectangle *paint_mask_rect,
const GimpTempBuf *paint_mask,
gint paint_mask_offset_x,
gint paint_mask_offset_y,
GimpDrawable *drawable,
gdouble paint_opacity,
gdouble image_opacity,
GimpLayerModeEffects paint_mode,
GimpPaintApplicationMode mode)
{
gint width = gegl_buffer_get_width (core->paint_buffer);
gint height = gegl_buffer_get_height (core->paint_buffer);
GimpTempBuf *paint_buf = gimp_gegl_buffer_get_temp_buf (core->paint_buffer);