gimp/app/core/gimpchannel-combine.c

472 lines
14 KiB
C
Raw Normal View History

/* GIMP - The GNU Image Manipulation Program
1997-11-25 06:05:25 +08:00
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
1997-11-25 06:05:25 +08:00
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
1997-11-25 06:05:25 +08:00
* (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 <https://www.gnu.org/licenses/>.
1997-11-25 06:05:25 +08:00
*/
app/appenv.h New file. Includes <math.h>. Move G_PI, RINT(), ROUND() etc 1999-09-01 Tor Lillqvist <tml@iki.fi> * app/appenv.h * libgimp/gimpmath.h: New file. Includes <math.h>. Move G_PI, RINT(), ROUND() etc from app/appenv.h here, so plug-ins can use them, too. Remove some commented-out old stuff in appenv.h. * libgimp/gimp.h: Include gimpmath.h. * libgimp/gimp.c (gimp_main): Win32: Don't install signal handlers, we can't do anything useful in the handler ourselves anyway (it would be nice to print out a backtrace, but that seems pretty hard to do, even if not impossible). Let Windows inform the user about the crash. If the plug-in was compiled with MSVC, and the user also has it, she is offered a chance to start the debugger automatically anyway. * app/*several*.c: Include gimpmath.h for G_PI etc. Don't include <math.h>, as gimpmath.h includes it. * plug-ins/*/*many*.c: Include config.h. Don't include <math.h>. Remove all the duplicated definitions of G_PI and rint(). Use RINT() instead of rint(). * app/app_procs.[ch]: app_exit() takes a gboolean. * app/batch.c * app/commands.c * app/interface.c: Call app_exit() with FALSE or TRUE. * app/main.c (on_error): Call gimp_fatal_error. (main): Don't install any signal handler on Win32 here, either. * app/errors.c (gimp_fatal_error, gimp_terminate): Win32: Format the message and call MessageBox with it. g_on_error_query doesn't do anything useful on Win32, and printf'ing a message to stdout or stderr doesn't do anything, either, in a windowing application.
1999-09-02 04:30:56 +08:00
#include "config.h"
#include <string.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
1997-11-25 06:05:25 +08:00
#include "libgimpbase/gimpbase.h"
#include "libgimpmath/gimpmath.h"
#include "core-types.h"
Make sure the selection (gimpimage-mask.c) functionality is really built 2002-08-20 Michael Natterer <mitch@gimp.org> Make sure the selection (gimpimage-mask.c) functionality is really built *on top* of the GimpChannel functionality: * app/undo.[ch]: renamed undo_push_image_mask() to undo_push_mask() and generalized it's API to take a GimpChannel param so undos can be pushed for channels which are not the image's selection. Simplified the API and added code which copies the region of interest instead of leaving this to callers. * app/undo_types.h: s/IMAGE_MASK_UNDO/MASK_UNDO/ * app/undo_history.c: changed accordingly. * app/core/gimpchannel.[ch]: don't #include "gimpimage-mask.h". Changed gimp_channel_push_undo() to really push a channel undo, not a selection undo. Added "gboolean push_undo" params to all functions which are called from gimpimage-mask.c. Various cleanups and optimizations. Added /*< proxy-foo >*/ stuff to the header so we export just the struct itself to libgimpproxy. Added accessors gimp_channel_[get|set]_show_masked(). * app/core/gimpimage-mask.[ch]: renamed gimp_image_mask_undo() to gimp_image_mask_push_undo(). Call it before calling GimpChannel functions which modify the mask, also call all GimpChannel functions with push_undo = FALSE. Emit gimp_image_mask_changed() after each operation instead of calling it in gimp_image_mask_invalidate(). Removed gimp_image_mask_none() because it is the same as gimp_image_mask_clear(). General cleanup. * app/core/gimpimage-mask-select.c * app/core/gimpimage-qmask.c: changed accordingly. * app/core/gimpedit.c: call gimp_image_mask_clear(), not gimp_channel_clear (gimp_image_get_mask()). * app/core/gimpimage-crop.c * app/core/gimpimage-resize.c * app/core/gimpimage-scale.c: call gimp_image_mask_changed() * app/gui/channels-commands.c * app/gui/select-commands.c * app/tools/gimptexttool.c * tools/pdbgen/pdb/channel.pdb * tools/pdbgen/pdb/selection.pdb: follow GimpChannel and gimp_image_mask* API changes. * app/pdb/channel_cmds.c * app/pdb/selection_cmds.c * libgimpproxy/gimpchannel.h: regenerated. Unrelated: * app/core/gimpimage.c: call gimp_drawable_push_undo() instead of undo_push_image() directly.
2002-08-20 18:22:23 +08:00
#include "gegl/gimp-gegl-mask-combine.h"
#include "gimpchannel.h"
#include "gimpchannel-combine.h"
1997-11-25 06:05:25 +08:00
typedef struct
1997-11-25 06:05:25 +08:00
{
GeglRectangle rect;
gboolean bounds_known;
gboolean empty;
GeglRectangle bounds;
} GimpChannelCombineData;
1997-11-25 06:05:25 +08:00
/* local function prototypes */
static void gimp_channel_combine_clear (GimpChannel *mask,
const GeglRectangle *rect);
static void gimp_channel_combine_clear_complement (GimpChannel *mask,
const GeglRectangle *rect);
static gboolean gimp_channel_combine_start (GimpChannel *mask,
GimpChannelOps op,
const GeglRectangle *rect,
gboolean full_extent,
gboolean full_value,
GimpChannelCombineData *data);
static void gimp_channel_combine_end (GimpChannel *mask,
GimpChannelCombineData *data);
/* private functions */
static void
gimp_channel_combine_clear (GimpChannel *mask,
const GeglRectangle *rect)
{
GeglBuffer *buffer;
GeglRectangle area;
GeglRectangle update_area;
if (mask->bounds_known && mask->empty)
return;
buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask));
1997-11-25 06:05:25 +08:00
if (rect)
1997-11-25 06:05:25 +08:00
{
if (rect->width <= 0 || rect->height <= 0)
return;
if (mask->bounds_known)
{
if (! gegl_rectangle_intersect (&area,
GEGL_RECTANGLE (mask->x1,
mask->y1,
mask->x2 - mask->x1,
mask->y2 - mask->y1),
rect))
{
return;
}
}
else
{
area = *rect;
}
update_area = area;
1997-11-25 06:05:25 +08:00
}
else
1997-11-25 06:05:25 +08:00
{
if (mask->bounds_known)
{
area.x = mask->x1;
area.y = mask->y1;
area.width = mask->x2 - mask->x1;
area.height = mask->y2 - mask->y1;
}
else
{
area.x = 0;
area.y = 0;
area.width = gimp_item_get_width (GIMP_ITEM (mask));
area.height = gimp_item_get_height (GIMP_ITEM (mask));
}
update_area = area;
gegl_rectangle_align_to_buffer (&area, &area, buffer,
GEGL_RECTANGLE_ALIGNMENT_SUPERSET);
1997-11-25 06:05:25 +08:00
}
gegl_buffer_clear (buffer, &area);
gimp_drawable_update (GIMP_DRAWABLE (mask),
update_area.x, update_area.y,
update_area.width, update_area.height);
}
static void
gimp_channel_combine_clear_complement (GimpChannel *mask,
const GeglRectangle *rect)
{
gint width = gimp_item_get_width (GIMP_ITEM (mask));
gint height = gimp_item_get_height (GIMP_ITEM (mask));
gimp_channel_combine_clear (
mask,
GEGL_RECTANGLE (0,
0,
width,
rect->y));
gimp_channel_combine_clear (
mask,
GEGL_RECTANGLE (0,
rect->y + rect->height,
width,
height - (rect->y + rect->height)));
gimp_channel_combine_clear (
mask,
GEGL_RECTANGLE (0,
rect->y,
rect->x,
rect->height));
gimp_channel_combine_clear (
mask,
GEGL_RECTANGLE (rect->x + rect->width,
rect->y,
width - (rect->x + rect->width),
rect->height));
}
static gboolean
gimp_channel_combine_start (GimpChannel *mask,
GimpChannelOps op,
const GeglRectangle *rect,
gboolean full_extent,
gboolean full_value,
GimpChannelCombineData *data)
{
GeglBuffer *buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask));
GeglRectangle extent;
gboolean intersects;
extent.x = 0;
extent.y = 0;
extent.width = gimp_item_get_width (GIMP_ITEM (mask));
extent.height = gimp_item_get_height (GIMP_ITEM (mask));
intersects = gegl_rectangle_intersect (&data->rect, rect, &extent);
data->bounds_known = mask->bounds_known;
data->empty = mask->empty;
data->bounds.x = mask->x1;
data->bounds.y = mask->y1;
data->bounds.width = mask->x2 - mask->x1;
data->bounds.height = mask->y2 - mask->y1;
gegl_buffer_freeze_changed (buffer);
/* Determine new boundary */
switch (op)
{
case GIMP_CHANNEL_OP_REPLACE:
gimp_channel_combine_clear (mask, NULL);
if (! intersects)
{
data->bounds_known = TRUE;
data->empty = TRUE;
return FALSE;
}
data->bounds_known = FALSE;
if (full_extent)
{
data->bounds_known = TRUE;
data->empty = FALSE;
data->bounds = data->rect;
}
break;
case GIMP_CHANNEL_OP_ADD:
if (! intersects)
return FALSE;
data->bounds_known = FALSE;
if (full_extent && (mask->bounds_known ||
gegl_rectangle_equal (&data->rect, &extent)))
{
data->bounds_known = TRUE;
data->empty = FALSE;
if (mask->bounds_known && ! mask->empty)
{
gegl_rectangle_bounding_box (&data->bounds,
&data->bounds, &data->rect);
}
else
{
data->bounds = data->rect;
}
}
break;
case GIMP_CHANNEL_OP_SUBTRACT:
if (intersects && mask->bounds_known)
{
if (mask->empty)
{
intersects = FALSE;
}
else
{
intersects = gegl_rectangle_intersect (&data->rect,
&data->rect,
&data->bounds);
}
}
if (! intersects)
return FALSE;
if (full_value &&
gegl_rectangle_contains (&data->rect,
mask->bounds_known ? &data->bounds :
&extent))
{
gimp_channel_combine_clear (mask, NULL);
data->bounds_known = TRUE;
data->empty = TRUE;
return FALSE;
}
data->bounds_known = FALSE;
gegl_buffer_set_abyss (buffer, &data->rect);
break;
case GIMP_CHANNEL_OP_INTERSECT:
if (intersects && mask->bounds_known)
{
if (mask->empty)
{
intersects = FALSE;
}
else
{
intersects = gegl_rectangle_intersect (&data->rect,
&data->rect,
&data->bounds);
}
}
if (! intersects)
{
gimp_channel_combine_clear (mask, NULL);
data->bounds_known = TRUE;
data->empty = TRUE;
return FALSE;
}
if (full_value && mask->bounds_known &&
gegl_rectangle_contains (&data->rect, &data->bounds))
{
return FALSE;
}
data->bounds_known = FALSE;
gimp_channel_combine_clear_complement (mask, &data->rect);
gegl_buffer_set_abyss (buffer, &data->rect);
break;
}
1997-11-25 06:05:25 +08:00
return TRUE;
}
static void
gimp_channel_combine_end (GimpChannel *mask,
GimpChannelCombineData *data)
{
GeglBuffer *buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask));
gegl_buffer_set_abyss (buffer, gegl_buffer_get_extent (buffer));
gegl_buffer_thaw_changed (buffer);
Treat changes to the selection like changes to any other drawable: 2003-10-06 Michael Natterer <mitch@gimp.org> Treat changes to the selection like changes to any other drawable: * app/core/gimpchannel.c * app/core/gimpchannel-combine.c: call gimp_drawable_update() after changing the channel. * app/core/gimpimage.[ch]: added struct GimpImageFlushAccumulator with one member "gboolean mask_changed". Connect to "update" of the selection and set accum.mask_changed to TRUE in the callback. Added default implementation for GimpImage::flush() and emit "mask_changed" there. Unrelated: * app/core/gimpimage.h: removed GimpGuide struct... * app/core/gimpimage-guides.h: ...and added it here. * app/core/gimpimage-undo-push.c (undo_pop_mask) (undo_pop_channel_mod): don't distinguish between selection and non-selection channels and just call gimp_drawable_update(). * app/core/gimpundo.h * app/core/gimpimage-undo.c: removed "gboolean mask_changed" from the GimpUndoAccumulator struct since we don't have to care about that signal explicitly any more. * app/display/gimpdisplay-foreach.[ch]: removed gimp_displays_flush(). * tools/pdbgen/pdb/display.pdb (displays_flush_invoker): call gimp_image_flush() on all images so the flush accumulator is honored. This generalization enables the removal of more special purpose code which was needed to treat the selection different: * app/core/gimpimage-mask-select.[ch]: removed... * app/core/gimpchannel-select.[ch]: ...and added under a new name because it's not selection specific any more. * app/core/gimpimage-mask.[ch]: removed... * app/core/gimpselection.[ch]: ...added the two remaining functions here. Removed all calls to gimp_image_mask_changed(). * app/core/Makefile.am * app/core/gimp-edit.c * app/core/gimpdrawable-transform.c * app/core/gimpimage-scale.c * app/core/gimpimage-snap.c * app/display/gimpdisplayshell.c * app/gui/channels-commands.c * app/gui/layers-commands.c * app/gui/select-commands.c * app/gui/vectors-commands.c * app/tools/gimpbycolorselecttool.c * app/tools/gimpeditselectiontool.c * app/tools/gimpellipseselecttool.c * app/tools/gimpfreeselecttool.c * app/tools/gimpfuzzyselecttool.c * app/tools/gimpiscissorstool.c * app/tools/gimprectselecttool.c * app/tools/gimptransformtool.c * app/widgets/gimpchanneltreeview.c * app/widgets/gimpselectioneditor.c * app/widgets/gimpvectorstreeview.c * app/xcf/xcf-save.c * tools/pdbgen/pdb/paths.pdb * tools/pdbgen/pdb/selection.pdb * tools/pdbgen/pdb/selection_tools.pdb: changed accordingly. * app/core/gimpdrawable-bucket-fill.c * app/core/gimpimage-colormap.c * app/core/gimplayer-floating-sel.c * app/core/gimplayer.c * app/gui/image-menu.c * app/paint/gimppaintcore.c * app/tools/gimpcroptool.c * app/tools/gimpinkoptions.c * app/tools/gimpvectortool.c: removed useless and/or obsolete #includes. * app/pdb/display_cmds.c * app/pdb/paths_cmds.c * app/pdb/selection_cmds.c * app/pdb/selection_tools_cmds.c: regenerated.
2003-10-06 20:17:11 +08:00
mask->bounds_known = data->bounds_known;
if (data->bounds_known)
{
mask->empty = data->empty;
if (data->empty)
{
mask->x1 = 0;
mask->y1 = 0;
mask->x2 = gimp_item_get_width (GIMP_ITEM (mask));
mask->y2 = gimp_item_get_height (GIMP_ITEM (mask));
}
else
{
mask->x1 = data->bounds.x;
mask->y1 = data->bounds.y;
mask->x2 = data->bounds.x + data->bounds.width;
mask->y2 = data->bounds.y + data->bounds.height;
}
}
gimp_drawable_update (GIMP_DRAWABLE (mask),
data->rect.x, data->rect.y,
data->rect.width, data->rect.height);
}
/* public functions */
void
gimp_channel_combine_rect (GimpChannel *mask,
GimpChannelOps op,
gint x,
gint y,
gint w,
gint h)
{
GimpChannelCombineData data;
g_return_if_fail (GIMP_IS_CHANNEL (mask));
if (gimp_channel_combine_start (mask, op, GEGL_RECTANGLE (x, y, w, h),
TRUE, TRUE, &data))
{
GeglBuffer *buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask));
gimp_gegl_mask_combine_rect (buffer, op, x, y, w, h);
}
gimp_channel_combine_end (mask, &data);
1997-11-25 06:05:25 +08:00
}
void
gimp_channel_combine_ellipse (GimpChannel *mask,
GimpChannelOps op,
gint x,
gint y,
gint w,
gint h,
gboolean antialias)
1997-11-25 06:05:25 +08:00
{
gimp_channel_combine_ellipse_rect (mask, op, x, y, w, h,
w / 2.0, h / 2.0, antialias);
}
void
gimp_channel_combine_ellipse_rect (GimpChannel *mask,
GimpChannelOps op,
gint x,
gint y,
gint w,
gint h,
gdouble rx,
gdouble ry,
gboolean antialias)
{
GimpChannelCombineData data;
1997-11-25 06:05:25 +08:00
g_return_if_fail (GIMP_IS_CHANNEL (mask));
if (gimp_channel_combine_start (mask, op, GEGL_RECTANGLE (x, y, w, h),
TRUE, FALSE, &data))
1997-11-25 06:05:25 +08:00
{
GeglBuffer *buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask));
gimp_gegl_mask_combine_ellipse_rect (buffer, op, x, y, w, h,
rx, ry, antialias);
}
1997-11-25 06:05:25 +08:00
gimp_channel_combine_end (mask, &data);
1997-11-25 06:05:25 +08:00
}
void
gimp_channel_combine_mask (GimpChannel *mask,
GimpChannel *add_on,
GimpChannelOps op,
gint off_x,
gint off_y)
1997-11-25 06:05:25 +08:00
{
GeglBuffer *add_on_buffer;
1997-11-25 06:05:25 +08:00
g_return_if_fail (GIMP_IS_CHANNEL (mask));
g_return_if_fail (GIMP_IS_CHANNEL (add_on));
add_on_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (add_on));
gimp_channel_combine_buffer (mask, add_on_buffer,
op, off_x, off_y);
}
void
gimp_channel_combine_buffer (GimpChannel *mask,
GeglBuffer *add_on_buffer,
GimpChannelOps op,
gint off_x,
gint off_y)
{
GimpChannelCombineData data;
g_return_if_fail (GIMP_IS_CHANNEL (mask));
g_return_if_fail (GEGL_IS_BUFFER (add_on_buffer));
if (gimp_channel_combine_start (mask, op,
GEGL_RECTANGLE (
off_x + gegl_buffer_get_x (add_on_buffer),
off_y + gegl_buffer_get_y (add_on_buffer),
gegl_buffer_get_width (add_on_buffer),
gegl_buffer_get_height (add_on_buffer)),
FALSE, FALSE, &data))
{
GeglBuffer *buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask));
gimp_gegl_mask_combine_buffer (buffer, add_on_buffer, op,
off_x, off_y);
}
Treat changes to the selection like changes to any other drawable: 2003-10-06 Michael Natterer <mitch@gimp.org> Treat changes to the selection like changes to any other drawable: * app/core/gimpchannel.c * app/core/gimpchannel-combine.c: call gimp_drawable_update() after changing the channel. * app/core/gimpimage.[ch]: added struct GimpImageFlushAccumulator with one member "gboolean mask_changed". Connect to "update" of the selection and set accum.mask_changed to TRUE in the callback. Added default implementation for GimpImage::flush() and emit "mask_changed" there. Unrelated: * app/core/gimpimage.h: removed GimpGuide struct... * app/core/gimpimage-guides.h: ...and added it here. * app/core/gimpimage-undo-push.c (undo_pop_mask) (undo_pop_channel_mod): don't distinguish between selection and non-selection channels and just call gimp_drawable_update(). * app/core/gimpundo.h * app/core/gimpimage-undo.c: removed "gboolean mask_changed" from the GimpUndoAccumulator struct since we don't have to care about that signal explicitly any more. * app/display/gimpdisplay-foreach.[ch]: removed gimp_displays_flush(). * tools/pdbgen/pdb/display.pdb (displays_flush_invoker): call gimp_image_flush() on all images so the flush accumulator is honored. This generalization enables the removal of more special purpose code which was needed to treat the selection different: * app/core/gimpimage-mask-select.[ch]: removed... * app/core/gimpchannel-select.[ch]: ...and added under a new name because it's not selection specific any more. * app/core/gimpimage-mask.[ch]: removed... * app/core/gimpselection.[ch]: ...added the two remaining functions here. Removed all calls to gimp_image_mask_changed(). * app/core/Makefile.am * app/core/gimp-edit.c * app/core/gimpdrawable-transform.c * app/core/gimpimage-scale.c * app/core/gimpimage-snap.c * app/display/gimpdisplayshell.c * app/gui/channels-commands.c * app/gui/layers-commands.c * app/gui/select-commands.c * app/gui/vectors-commands.c * app/tools/gimpbycolorselecttool.c * app/tools/gimpeditselectiontool.c * app/tools/gimpellipseselecttool.c * app/tools/gimpfreeselecttool.c * app/tools/gimpfuzzyselecttool.c * app/tools/gimpiscissorstool.c * app/tools/gimprectselecttool.c * app/tools/gimptransformtool.c * app/widgets/gimpchanneltreeview.c * app/widgets/gimpselectioneditor.c * app/widgets/gimpvectorstreeview.c * app/xcf/xcf-save.c * tools/pdbgen/pdb/paths.pdb * tools/pdbgen/pdb/selection.pdb * tools/pdbgen/pdb/selection_tools.pdb: changed accordingly. * app/core/gimpdrawable-bucket-fill.c * app/core/gimpimage-colormap.c * app/core/gimplayer-floating-sel.c * app/core/gimplayer.c * app/gui/image-menu.c * app/paint/gimppaintcore.c * app/tools/gimpcroptool.c * app/tools/gimpinkoptions.c * app/tools/gimpvectortool.c: removed useless and/or obsolete #includes. * app/pdb/display_cmds.c * app/pdb/paths_cmds.c * app/pdb/selection_cmds.c * app/pdb/selection_tools_cmds.c: regenerated.
2003-10-06 20:17:11 +08:00
gimp_channel_combine_end (mask, &data);
1997-11-25 06:05:25 +08:00
}