gimp/app/paint/gimpsmudge.c
Michael Natterer 8224476afb added utility function gimp_paint_options_get_fade() which calculates an
2003-07-16  Michael Natterer  <mitch@gimp.org>

	* app/paint/gimppaintoptions.[ch]: added utility function
	gimp_paint_options_get_fade() which calculates an opacity
	value from paint_core->pixel_dist.

	* app/paint/gimppaintbrush.c: removed the same code here and use
	gimp_paint_options_get_fade().

	* app/paint/gimpclone.c
	* app/paint/gimpconvolve.c
	* app/paint/gimpdodgeburn.c
	* app/paint/gimperaser.c
	* app/paint/gimpsmudge.c: enabled fade for all paint tools, along
	with a general opacity cleanup:

	Use the opacity from gimp_context_get_opacity() *only* for the
	image_opacity. In particular, *never* use it as initial value for
	calculating the brush_opacity. Instead, start calculating the
	brush_opacity from gimp_paint_options_get_fade() and return early
	if it returns 0.0, if not, multiply tool specific opacity sources
	like the current pressure.

	(This changes the effect of the paint tools for particular opacity
	values, but makes the impact of opacity on the final rendering
	linear and more intuitive)

	* app/tools/gimppaintoptions-gui.c: enabled the "Fade" frame for
	the tools above.

	* app/paint/gimppaintcore.c: purely cosmetic cleanup.
2003-07-16 11:25:37 +00:00

390 lines
12 KiB
C

/* The GIMP -- an image manipulation program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <glib-object.h>
#include "libgimpmath/gimpmath.h"
#include "paint-types.h"
#include "base/pixel-region.h"
#include "base/temp-buf.h"
#include "paint-funcs/paint-funcs.h"
#include "core/gimp.h"
#include "core/gimpbrush.h"
#include "core/gimpdrawable.h"
#include "core/gimpimage.h"
#include "gimpsmudge.h"
#include "gimpsmudgeoptions.h"
#include "gimp-intl.h"
static void gimp_smudge_class_init (GimpSmudgeClass *klass);
static void gimp_smudge_init (GimpSmudge *smudge);
static void gimp_smudge_finalize (GObject *object);
static void gimp_smudge_paint (GimpPaintCore *paint_core,
GimpDrawable *drawable,
GimpPaintOptions *paint_options,
GimpPaintCoreState paint_state);
static gboolean gimp_smudge_start (GimpPaintCore *paint_core,
GimpDrawable *drawable);
static void gimp_smudge_motion (GimpPaintCore *paint_core,
GimpDrawable *drawable,
GimpPaintOptions *paint_options);
static void gimp_smudge_nonclipped_painthit_coords (GimpPaintCore *paint_core,
gint *x,
gint *y,
gint *w,
gint *h);
static GimpPaintCoreClass *parent_class = NULL;
void
gimp_smudge_register (Gimp *gimp,
GimpPaintRegisterCallback callback)
{
(* callback) (gimp,
GIMP_TYPE_SMUDGE,
GIMP_TYPE_SMUDGE_OPTIONS,
_("Smudge"));
}
GType
gimp_smudge_get_type (void)
{
static GType type = 0;
if (! type)
{
static const GTypeInfo info =
{
sizeof (GimpSmudgeClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) gimp_smudge_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GimpSmudge),
0, /* n_preallocs */
(GInstanceInitFunc) gimp_smudge_init,
};
type = g_type_register_static (GIMP_TYPE_PAINT_CORE,
"GimpSmudge",
&info, 0);
}
return type;
}
static void
gimp_smudge_class_init (GimpSmudgeClass *klass)
{
GObjectClass *object_class;
GimpPaintCoreClass *paint_core_class;
object_class = G_OBJECT_CLASS (klass);
paint_core_class = GIMP_PAINT_CORE_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
object_class->finalize = gimp_smudge_finalize;
paint_core_class->paint = gimp_smudge_paint;
}
static void
gimp_smudge_init (GimpSmudge *smudge)
{
GimpPaintCore *paint_core;
paint_core = GIMP_PAINT_CORE (smudge);
smudge->initialized = FALSE;
smudge->accum_data = NULL;
}
static void
gimp_smudge_finalize (GObject *object)
{
GimpSmudge *smudge;
smudge = GIMP_SMUDGE (object);
if (smudge->accum_data)
{
g_free (smudge->accum_data);
smudge->accum_data = NULL;
}
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_smudge_paint (GimpPaintCore *paint_core,
GimpDrawable *drawable,
GimpPaintOptions *paint_options,
GimpPaintCoreState paint_state)
{
GimpSmudge *smudge;
smudge = GIMP_SMUDGE (paint_core);
switch (paint_state)
{
case MOTION_PAINT:
/* initialization fails if the user starts outside the drawable */
if (! smudge->initialized)
smudge->initialized = gimp_smudge_start (paint_core, drawable);
if (smudge->initialized)
gimp_smudge_motion (paint_core, drawable, paint_options);
break;
case FINISH_PAINT:
if (smudge->accum_data)
{
g_free (smudge->accum_data);
smudge->accum_data = NULL;
}
smudge->initialized = FALSE;
break;
default:
break;
}
return;
}
static gboolean
gimp_smudge_start (GimpPaintCore *paint_core,
GimpDrawable *drawable)
{
GimpSmudge *smudge;
GimpImage *gimage;
TempBuf *area;
PixelRegion srcPR;
gint x, y, w, h;
smudge = GIMP_SMUDGE (paint_core);
if (! (gimage = gimp_item_get_image (GIMP_ITEM (drawable))))
return FALSE;
if (gimp_drawable_is_indexed (drawable))
return FALSE;
if (! (area = gimp_paint_core_get_paint_area (paint_core, drawable, 1.0)))
return FALSE;
/* adjust the x and y coordinates to the upper left corner of the brush */
gimp_smudge_nonclipped_painthit_coords (paint_core, &x, &y, &w, &h);
/* Allocate the accumulation buffer */
smudge->accumPR.bytes = gimp_drawable_bytes (drawable);
smudge->accum_data = g_malloc (w * h * smudge->accumPR.bytes);
/* If clipped, prefill the smudge buffer
with the color at the brush position. */
if (x != area->x || y != area->y || w != area->width || h != area->height)
{
guchar *fill;
fill = gimp_drawable_get_color_at (drawable,
CLAMP ((gint) paint_core->cur_coords.x,
0, gimp_item_width (GIMP_ITEM (drawable)) - 1),
CLAMP ((gint) paint_core->cur_coords.y,
0, gimp_item_height (GIMP_ITEM (drawable)) - 1));
g_return_val_if_fail (fill != NULL, FALSE);
srcPR.w = w;
srcPR.h = h;
srcPR.bytes = smudge->accumPR.bytes;
srcPR.rowstride = srcPR.bytes * w;
srcPR.data = smudge->accum_data;
color_region (&srcPR, fill);
g_free (fill);
}
smudge->accumPR.x = area->x - x;
smudge->accumPR.y = area->y - y;
smudge->accumPR.w = area->width;
smudge->accumPR.h = area->height;
smudge->accumPR.rowstride = smudge->accumPR.bytes * w;
smudge->accumPR.data = (smudge->accum_data +
smudge->accumPR.rowstride * smudge->accumPR.y +
smudge->accumPR.x * smudge->accumPR.bytes);
pixel_region_init (&srcPR, gimp_drawable_data (drawable),
area->x, area->y, area->width, area->height, FALSE);
/* copy the region under the original painthit. */
copy_region (&srcPR, &smudge->accumPR);
smudge->accumPR.x = area->x - x;
smudge->accumPR.y = area->y - y;
smudge->accumPR.w = area->width;
smudge->accumPR.h = area->height;
smudge->accumPR.rowstride = smudge->accumPR.bytes * w;
smudge->accumPR.data = (smudge->accum_data +
smudge->accumPR.rowstride * smudge->accumPR.y +
smudge->accumPR.x * smudge->accumPR.bytes);
return TRUE;
}
static void
gimp_smudge_motion (GimpPaintCore *paint_core,
GimpDrawable *drawable,
GimpPaintOptions *paint_options)
{
GimpSmudge *smudge;
GimpSmudgeOptions *options;
GimpPressureOptions *pressure_options;
GimpContext *context;
GimpImage *gimage;
TempBuf *area;
PixelRegion srcPR, destPR, tempPR;
gdouble rate;
gdouble opacity;
gint x, y, w, h;
smudge = GIMP_SMUDGE (paint_core);
options = GIMP_SMUDGE_OPTIONS (paint_options);
context = GIMP_CONTEXT (paint_options);
pressure_options = paint_options->pressure_options;
if (! (gimage = gimp_item_get_image (GIMP_ITEM (drawable))))
return;
if (gimp_drawable_is_indexed (drawable))
return;
opacity = gimp_paint_options_get_fade (paint_options, gimage,
paint_core->pixel_dist);
if (opacity == 0.0)
return;
gimp_smudge_nonclipped_painthit_coords (paint_core, &x, &y, &w, &h);
/* Get the paint area (Smudge won't scale!) */
if (! (area = gimp_paint_core_get_paint_area (paint_core, drawable, 1.0)))
return;
/* srcPR will be the pixels under the current painthit from the drawable */
pixel_region_init (&srcPR, gimp_drawable_data (drawable),
area->x, area->y, area->width, area->height, FALSE);
/* Enable pressure sensitive rate */
if (pressure_options->rate)
rate = MIN (options->rate / 100.0 * 2.0 * paint_core->cur_coords.pressure,
1.0);
else
rate = options->rate / 100.0;
/* The tempPR will be the built up buffer (for smudge) */
tempPR.x = area->x - x;
tempPR.y = area->y - y;
tempPR.w = area->width;
tempPR.h = area->height;
tempPR.bytes = smudge->accumPR.bytes;
tempPR.rowstride = smudge->accumPR.rowstride;
tempPR.data = (smudge->accum_data +
tempPR.y * tempPR.rowstride +
tempPR.x * tempPR.bytes);
/* The dest will be the paint area we got above (= canvas_buf) */
destPR.x = 0;
destPR.y = 0;
destPR.w = area->width;
destPR.h = area->height;
destPR.bytes = area->bytes;
destPR.rowstride = area->width * area->bytes;
destPR.data = temp_buf_data (area);
/* 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 (canvas_buf) is built as
* (Accum,1) (if no alpha),
*/
blend_region (&srcPR, &tempPR, &tempPR, ROUND (rate * 255.0));
/* re-init the tempPR */
tempPR.x = area->x - x;
tempPR.y = area->y - y;
tempPR.w = area->width;
tempPR.h = area->height;
tempPR.bytes = smudge->accumPR.bytes;
tempPR.rowstride = smudge->accumPR.rowstride;
tempPR.data = (smudge->accum_data +
tempPR.y * tempPR.rowstride +
tempPR.x * tempPR.bytes);
if (! gimp_drawable_has_alpha (drawable))
add_alpha_region (&tempPR, &destPR);
else
copy_region (&tempPR, &destPR);
if (pressure_options->opacity)
opacity *= 2.0 * paint_core->cur_coords.pressure;
/* Replace the newly made paint area to the gimage */
gimp_paint_core_replace_canvas (paint_core, drawable,
MIN (opacity, GIMP_OPACITY_OPAQUE),
gimp_context_get_opacity (context),
gimp_paint_options_get_brush_mode (paint_options),
1.0,
GIMP_PAINT_INCREMENTAL);
}
static void
gimp_smudge_nonclipped_painthit_coords (GimpPaintCore *paint_core,
gint *x,
gint *y,
gint *w,
gint *h)
{
/* Note: these are the brush mask size plus a border of 1 pixel */
*x =
(gint) paint_core->cur_coords.x - paint_core->brush->mask->width / 2 - 1;
*y =
(gint) paint_core->cur_coords.y - paint_core->brush->mask->height / 2 - 1;
*w = paint_core->brush->mask->width + 2;
*h = paint_core->brush->mask->height + 2;
}