mirror of
https://gitlab.gnome.org/GNOME/gimp
synced 2024-10-22 12:32:37 +00:00
e29df7e285
2000-12-15 Sven Neumann <sven@gimp.org> * app/gimppreviewcache.[ch]: added new function gimp_preview_scale(). * app/channel.c * app/channels_dialog.c * app/gimpimage.c * app/layer.c * app/layers_dialog.c * app/lc_dialog.c * app/nav_window.c: unified preview drawing code. Previews are never generated larger than the canvas size. Image and channel previews are scaled up to the desired size, so does the navigation window. The layer previews are not yet scaled up again since we can not use gimp_preview_scale here. I have removed the preview_cache priming code since we don't need it any longer and it caused bad results.
1740 lines
40 KiB
C
1740 lines
40 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 <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "appenv.h"
|
|
#include "channel.h"
|
|
#include "drawable.h"
|
|
#include "gdisplay.h"
|
|
#include "gimage_mask.h"
|
|
#include "layer.h"
|
|
#include "paint_funcs.h"
|
|
#include "parasitelist.h"
|
|
#include "temp_buf.h"
|
|
#include "undo.h"
|
|
#include "gimpsignal.h"
|
|
#include "gimppreviewcache.h"
|
|
|
|
#include "channel_pvt.h"
|
|
#include "tile.h"
|
|
|
|
#include "gimplut.h"
|
|
#include "lut_funcs.h"
|
|
|
|
#include "libgimp/gimpmath.h"
|
|
|
|
#include "libgimp/gimpintl.h"
|
|
|
|
|
|
enum {
|
|
REMOVED,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static void gimp_channel_class_init (GimpChannelClass *klass);
|
|
static void gimp_channel_init (GimpChannel *channel);
|
|
static void gimp_channel_destroy (GtkObject *object);
|
|
|
|
static guint channel_signals[LAST_SIGNAL] = { 0 };
|
|
|
|
static GimpDrawableClass *parent_class = NULL;
|
|
|
|
GtkType
|
|
gimp_channel_get_type ()
|
|
{
|
|
static GtkType channel_type = 0;
|
|
|
|
if (!channel_type)
|
|
{
|
|
GtkTypeInfo channel_info =
|
|
{
|
|
"GimpChannel",
|
|
sizeof (GimpChannel),
|
|
sizeof (GimpChannelClass),
|
|
(GtkClassInitFunc) gimp_channel_class_init,
|
|
(GtkObjectInitFunc) gimp_channel_init,
|
|
/* reserved_1 */ NULL,
|
|
/* reserved_2 */ NULL,
|
|
(GtkClassInitFunc) NULL,
|
|
};
|
|
|
|
channel_type = gtk_type_unique (gimp_drawable_get_type (), &channel_info);
|
|
}
|
|
|
|
return channel_type;
|
|
}
|
|
|
|
static void
|
|
gimp_channel_class_init (GimpChannelClass *class)
|
|
{
|
|
GtkObjectClass *object_class;
|
|
|
|
object_class = (GtkObjectClass*) class;
|
|
parent_class = gtk_type_class (gimp_drawable_get_type ());
|
|
|
|
channel_signals[REMOVED] =
|
|
gimp_signal_new ("removed",
|
|
0, object_class->type, 0, gimp_sigtype_void);
|
|
|
|
gtk_object_class_add_signals (object_class, channel_signals, LAST_SIGNAL);
|
|
|
|
object_class->destroy = gimp_channel_destroy;
|
|
}
|
|
|
|
static void
|
|
gimp_channel_init (GimpChannel *channel)
|
|
{
|
|
}
|
|
|
|
/**************************/
|
|
/* Function definitions */
|
|
/**************************/
|
|
|
|
static void
|
|
channel_validate (TileManager *tm,
|
|
Tile *tile)
|
|
{
|
|
/* Set the contents of the tile to empty */
|
|
memset (tile_data_pointer (tile, 0, 0),
|
|
TRANSPARENT_OPACITY, tile_size (tile));
|
|
}
|
|
|
|
Channel *
|
|
channel_new (GimpImage *gimage,
|
|
gint width,
|
|
gint height,
|
|
gchar *name,
|
|
gint opacity,
|
|
guchar *col)
|
|
{
|
|
Channel * channel;
|
|
gint i;
|
|
|
|
channel = gtk_type_new (gimp_channel_get_type ());
|
|
|
|
gimp_drawable_configure (GIMP_DRAWABLE (channel),
|
|
gimage, width, height, GRAY_GIMAGE, name);
|
|
|
|
/* set the channel color and opacity */
|
|
for (i = 0; i < 3; i++)
|
|
channel->col[i] = col[i];
|
|
|
|
channel->opacity = opacity;
|
|
channel->show_masked = TRUE;
|
|
|
|
/* selection mask variables */
|
|
channel->empty = TRUE;
|
|
channel->segs_in = NULL;
|
|
channel->segs_out = NULL;
|
|
channel->num_segs_in = 0;
|
|
channel->num_segs_out = 0;
|
|
channel->bounds_known = TRUE;
|
|
channel->boundary_known = TRUE;
|
|
channel->x1 = 0;
|
|
channel->y1 = 0;
|
|
channel->x2 = width;
|
|
channel->y2 = height;
|
|
|
|
return channel;
|
|
}
|
|
|
|
Channel *
|
|
channel_ref (Channel *channel)
|
|
{
|
|
gtk_object_ref (GTK_OBJECT (channel));
|
|
gtk_object_sink (GTK_OBJECT (channel));
|
|
|
|
return channel;
|
|
}
|
|
|
|
void
|
|
channel_unref (Channel *channel)
|
|
{
|
|
gtk_object_unref (GTK_OBJECT (channel));
|
|
}
|
|
|
|
Channel *
|
|
channel_copy (Channel *channel)
|
|
{
|
|
gchar *channel_name;
|
|
Channel *new_channel;
|
|
PixelRegion srcPR, destPR;
|
|
gchar *ext;
|
|
gint number;
|
|
gchar *name;
|
|
gint len;
|
|
|
|
/* formulate the new channel name */
|
|
name = channel_get_name (channel);
|
|
ext = strrchr (name, '#');
|
|
len = strlen (_("copy"));
|
|
if ((strlen (name) >= len &&
|
|
strcmp (&name[strlen (name) - len], _("copy")) == 0) ||
|
|
(ext && (number = atoi (ext + 1)) > 0 &&
|
|
((int)(log10 (number) + 1)) == strlen (ext + 1)))
|
|
/* don't have redundant "copy"s */
|
|
channel_name = g_strdup (name);
|
|
else
|
|
channel_name = g_strdup_printf (_("%s copy"), name);
|
|
|
|
/* allocate a new channel object */
|
|
new_channel = channel_new (GIMP_DRAWABLE (channel)->gimage,
|
|
GIMP_DRAWABLE (channel)->width,
|
|
GIMP_DRAWABLE (channel)->height,
|
|
channel_name, channel->opacity, channel->col);
|
|
GIMP_DRAWABLE (new_channel)->visible = GIMP_DRAWABLE (channel)->visible;
|
|
new_channel->show_masked = channel->show_masked;
|
|
|
|
/* copy the contents across channels */
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles, 0, 0,
|
|
GIMP_DRAWABLE (channel)->width,
|
|
GIMP_DRAWABLE (channel)->height, FALSE);
|
|
pixel_region_init (&destPR, GIMP_DRAWABLE (new_channel)->tiles,
|
|
0, 0,
|
|
GIMP_DRAWABLE (channel)->width,
|
|
GIMP_DRAWABLE (channel)->height, TRUE);
|
|
copy_region (&srcPR, &destPR);
|
|
|
|
/* copy the parasites */
|
|
GIMP_DRAWABLE (new_channel)->parasites
|
|
= parasite_list_copy (GIMP_DRAWABLE (channel)->parasites);
|
|
|
|
/* free up the channel_name memory */
|
|
g_free (channel_name);
|
|
|
|
return new_channel;
|
|
}
|
|
|
|
void
|
|
channel_set_name (Channel *channel,
|
|
gchar *name)
|
|
{
|
|
gimp_drawable_set_name (GIMP_DRAWABLE (channel), name);
|
|
}
|
|
|
|
gchar *
|
|
channel_get_name (Channel *channel)
|
|
{
|
|
return gimp_drawable_get_name (GIMP_DRAWABLE (channel));
|
|
}
|
|
|
|
void
|
|
channel_set_color (Channel *channel,
|
|
guchar *color)
|
|
{
|
|
gint i;
|
|
|
|
if (color)
|
|
{
|
|
for (i = 0; i < 3; i++)
|
|
channel->col[i] = color[i];
|
|
}
|
|
}
|
|
|
|
guchar *
|
|
channel_get_color (Channel *channel)
|
|
{
|
|
return (GIMP_CHANNEL (channel)->col);
|
|
}
|
|
|
|
int
|
|
channel_get_opacity (Channel *channel)
|
|
{
|
|
return channel->opacity;
|
|
}
|
|
|
|
void
|
|
channel_set_opacity (Channel *channel,
|
|
gint opacity)
|
|
{
|
|
if (opacity >=0 && opacity <= 100)
|
|
channel->opacity = (gint) (opacity * 255) / 100;
|
|
}
|
|
|
|
Channel *
|
|
channel_get_ID (gint ID)
|
|
{
|
|
GimpDrawable *drawable;
|
|
|
|
drawable = drawable_get_ID (ID);
|
|
if (drawable && GIMP_IS_CHANNEL (drawable))
|
|
return GIMP_CHANNEL (drawable);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
channel_delete (Channel *channel)
|
|
{
|
|
gtk_object_unref (GTK_OBJECT (channel));
|
|
}
|
|
|
|
static void
|
|
gimp_channel_destroy (GtkObject *object)
|
|
{
|
|
GimpChannel *channel;
|
|
|
|
g_return_if_fail (object != NULL);
|
|
g_return_if_fail (GIMP_IS_CHANNEL (object));
|
|
|
|
channel = GIMP_CHANNEL (object);
|
|
|
|
/* free the segments? */
|
|
if (channel->segs_in)
|
|
g_free (channel->segs_in);
|
|
if (channel->segs_out)
|
|
g_free (channel->segs_out);
|
|
|
|
if (GTK_OBJECT_CLASS (parent_class)->destroy)
|
|
(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
|
|
|
|
}
|
|
|
|
/* The removed signal is sent out when the channel is no longer
|
|
* associcated with an image. It's needed because channels aren't
|
|
* destroyed immediately, but kept around for undo purposes. Connect
|
|
* to the removed signal to update bits of UI that are tied to a
|
|
* particular layer. */
|
|
void
|
|
channel_removed (Channel *channel,
|
|
gpointer data)
|
|
{
|
|
g_return_if_fail (channel != NULL);
|
|
g_return_if_fail (GIMP_IS_CHANNEL (channel));
|
|
|
|
gtk_signal_emit (GTK_OBJECT (channel), channel_signals[REMOVED]);
|
|
}
|
|
|
|
|
|
void
|
|
channel_scale (Channel *channel,
|
|
gint new_width,
|
|
gint new_height)
|
|
{
|
|
PixelRegion srcPR, destPR;
|
|
TileManager *new_tiles;
|
|
|
|
if (new_width == 0 || new_height == 0)
|
|
return;
|
|
|
|
/* Update the old channel position */
|
|
drawable_update (GIMP_DRAWABLE (channel),
|
|
0, 0,
|
|
GIMP_DRAWABLE (channel)->width,
|
|
GIMP_DRAWABLE (channel)->height);
|
|
|
|
/* Configure the pixel regions */
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
|
|
0, 0,
|
|
GIMP_DRAWABLE (channel)->width,
|
|
GIMP_DRAWABLE (channel)->height, FALSE);
|
|
|
|
/* Allocate the new channel, configure dest region */
|
|
new_tiles = tile_manager_new (new_width, new_height, 1);
|
|
pixel_region_init (&destPR, new_tiles, 0, 0, new_width, new_height, TRUE);
|
|
|
|
/* Add an alpha channel */
|
|
scale_region (&srcPR, &destPR);
|
|
|
|
/* Push the channel on the undo stack */
|
|
undo_push_channel_mod (GIMP_DRAWABLE (channel)->gimage, channel);
|
|
|
|
/* Configure the new channel */
|
|
GIMP_DRAWABLE (channel)->tiles = new_tiles;
|
|
GIMP_DRAWABLE (channel)->width = new_width;
|
|
GIMP_DRAWABLE (channel)->height = new_height;
|
|
|
|
/* bounds are now unknown */
|
|
channel->bounds_known = FALSE;
|
|
|
|
/* Update the new channel position */
|
|
drawable_update (GIMP_DRAWABLE (channel),
|
|
0, 0,
|
|
GIMP_DRAWABLE (channel)->width,
|
|
GIMP_DRAWABLE (channel)->height);
|
|
}
|
|
|
|
void
|
|
channel_resize (Channel *channel,
|
|
gint new_width,
|
|
gint new_height,
|
|
gint offx,
|
|
gint offy)
|
|
{
|
|
PixelRegion srcPR, destPR;
|
|
TileManager *new_tiles;
|
|
guchar bg = 0;
|
|
gint clear;
|
|
gint w, h;
|
|
gint x1, y1, x2, y2;
|
|
|
|
if (!new_width || !new_height)
|
|
return;
|
|
|
|
x1 = CLAMP (offx, 0, new_width);
|
|
y1 = CLAMP (offy, 0, new_height);
|
|
x2 = CLAMP ((offx + GIMP_DRAWABLE (channel)->width), 0, new_width);
|
|
y2 = CLAMP ((offy + GIMP_DRAWABLE (channel)->height), 0, new_height);
|
|
w = x2 - x1;
|
|
h = y2 - y1;
|
|
|
|
if (offx > 0)
|
|
{
|
|
x1 = 0;
|
|
x2 = offx;
|
|
}
|
|
else
|
|
{
|
|
x1 = -offx;
|
|
x2 = 0;
|
|
}
|
|
|
|
if (offy > 0)
|
|
{
|
|
y1 = 0;
|
|
y2 = offy;
|
|
}
|
|
else
|
|
{
|
|
y1 = -offy;
|
|
y2 = 0;
|
|
}
|
|
|
|
/* Update the old channel position */
|
|
drawable_update (GIMP_DRAWABLE (channel),
|
|
0, 0,
|
|
GIMP_DRAWABLE (channel)->width,
|
|
GIMP_DRAWABLE (channel)->height);
|
|
|
|
/* Configure the pixel regions */
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
|
|
x1, y1, w, h, FALSE);
|
|
|
|
/* Determine whether the new channel needs to be initially cleared */
|
|
if ((new_width > GIMP_DRAWABLE (channel)->width) ||
|
|
(new_height > GIMP_DRAWABLE (channel)->height) ||
|
|
(x2 || y2))
|
|
clear = TRUE;
|
|
else
|
|
clear = FALSE;
|
|
|
|
/* Allocate the new channel, configure dest region */
|
|
new_tiles = tile_manager_new (new_width, new_height, 1);
|
|
|
|
/* Set to black (empty--for selections) */
|
|
if (clear)
|
|
{
|
|
pixel_region_init (&destPR, new_tiles, 0, 0, new_width, new_height, TRUE);
|
|
color_region (&destPR, &bg);
|
|
}
|
|
|
|
/* copy from the old to the new */
|
|
pixel_region_init (&destPR, new_tiles, x2, y2, w, h, TRUE);
|
|
if (w && h)
|
|
copy_region (&srcPR, &destPR);
|
|
|
|
/* Push the channel on the undo stack */
|
|
undo_push_channel_mod (GIMP_DRAWABLE (channel)->gimage, channel);
|
|
|
|
/* Configure the new channel */
|
|
GIMP_DRAWABLE (channel)->tiles = new_tiles;
|
|
GIMP_DRAWABLE (channel)->width = new_width;
|
|
GIMP_DRAWABLE (channel)->height = new_height;
|
|
|
|
/* bounds are now unknown */
|
|
channel->bounds_known = FALSE;
|
|
|
|
/* update the new channel area */
|
|
drawable_update (GIMP_DRAWABLE (channel),
|
|
0, 0,
|
|
GIMP_DRAWABLE (channel)->width,
|
|
GIMP_DRAWABLE (channel)->height);
|
|
}
|
|
|
|
void
|
|
channel_update (Channel *channel)
|
|
{
|
|
drawable_update (GIMP_DRAWABLE (channel),
|
|
0, 0,
|
|
GIMP_DRAWABLE (channel)->width,
|
|
GIMP_DRAWABLE (channel)->height);
|
|
gdisplays_flush ();
|
|
}
|
|
|
|
/**********************/
|
|
/* access functions */
|
|
/**********************/
|
|
|
|
gboolean
|
|
channel_toggle_visibility (Channel *channel)
|
|
{
|
|
GIMP_DRAWABLE (channel)->visible = !GIMP_DRAWABLE (channel)->visible;
|
|
|
|
return GIMP_DRAWABLE (channel)->visible;
|
|
}
|
|
|
|
TempBuf *
|
|
channel_preview (Channel *channel,
|
|
gint width,
|
|
gint height)
|
|
{
|
|
MaskBuf * preview_buf;
|
|
PixelRegion srcPR, destPR;
|
|
gint subsample;
|
|
TempBuf *ret_buf;
|
|
|
|
g_return_val_if_fail (channel != NULL, NULL);
|
|
g_return_val_if_fail (GIMP_IS_CHANNEL (channel), NULL);
|
|
|
|
/* The easy way */
|
|
if (GIMP_DRAWABLE (channel)->preview_valid &&
|
|
(ret_buf =
|
|
gimp_preview_cache_get (& (GIMP_DRAWABLE (channel)->preview_cache),
|
|
width, height)))
|
|
return ret_buf;
|
|
/* The hard way */
|
|
else
|
|
{
|
|
/* calculate 'acceptable' subsample */
|
|
subsample = 1;
|
|
if (width < 1) width = 1;
|
|
if (height < 1) height = 1;
|
|
while ((width * (subsample + 1) * 2 < GIMP_DRAWABLE (channel)->width) &&
|
|
(height * (subsample + 1) * 2 < GIMP_DRAWABLE (channel)->height))
|
|
subsample = subsample + 1;
|
|
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
|
|
0, 0,
|
|
GIMP_DRAWABLE (channel)->width,
|
|
GIMP_DRAWABLE (channel)->height, FALSE);
|
|
|
|
preview_buf = mask_buf_new (width, height);
|
|
destPR.bytes = 1;
|
|
destPR.x = 0;
|
|
destPR.y = 0;
|
|
destPR.w = width;
|
|
destPR.h = height;
|
|
destPR.rowstride = width;
|
|
destPR.data = mask_buf_data (preview_buf);
|
|
|
|
subsample_region (&srcPR, &destPR, subsample);
|
|
|
|
if (!GIMP_DRAWABLE (channel)->preview_valid)
|
|
gimp_preview_cache_invalidate (&(GIMP_DRAWABLE(channel)->preview_cache));
|
|
|
|
GIMP_DRAWABLE (channel)->preview_valid = TRUE;
|
|
gimp_preview_cache_add (&(GIMP_DRAWABLE (channel)->preview_cache),
|
|
preview_buf);
|
|
return preview_buf;
|
|
}
|
|
}
|
|
|
|
void
|
|
channel_invalidate_previews (GimpImage* gimage)
|
|
{
|
|
GSList * tmp;
|
|
Channel * channel;
|
|
|
|
g_return_if_fail (gimage != NULL);
|
|
|
|
tmp = gimage->channels;
|
|
|
|
while (tmp)
|
|
{
|
|
channel = (Channel *) tmp->data;
|
|
gimp_drawable_invalidate_preview (GIMP_DRAWABLE (channel), TRUE);
|
|
tmp = g_slist_next (tmp);
|
|
}
|
|
}
|
|
|
|
Tattoo
|
|
channel_get_tattoo (const Channel *channel)
|
|
{
|
|
return (gimp_drawable_get_tattoo (GIMP_DRAWABLE (channel)));
|
|
}
|
|
|
|
void
|
|
channel_set_tattoo (const Channel *channel,
|
|
Tattoo value)
|
|
{
|
|
gimp_drawable_set_tattoo (GIMP_DRAWABLE (channel), value);
|
|
}
|
|
|
|
/******************************/
|
|
/* selection mask functions */
|
|
/******************************/
|
|
|
|
Channel *
|
|
channel_new_mask (GimpImage *gimage,
|
|
gint width,
|
|
gint height)
|
|
{
|
|
guchar black[3] = {0, 0, 0};
|
|
Channel *new_channel;
|
|
|
|
/* Create the new channel */
|
|
new_channel = channel_new (gimage, width, height,
|
|
_("Selection Mask"), 127, black);
|
|
|
|
/* Set the validate procedure */
|
|
tile_manager_set_validate_proc (GIMP_DRAWABLE (new_channel)->tiles,
|
|
channel_validate);
|
|
|
|
return new_channel;
|
|
}
|
|
|
|
gboolean
|
|
channel_boundary (Channel *mask,
|
|
BoundSeg **segs_in,
|
|
BoundSeg **segs_out,
|
|
gint *num_segs_in,
|
|
gint *num_segs_out,
|
|
gint x1,
|
|
gint y1,
|
|
gint x2,
|
|
gint y2)
|
|
{
|
|
gint x3, y3, x4, y4;
|
|
PixelRegion bPR;
|
|
|
|
if (! mask->boundary_known)
|
|
{
|
|
/* free the out of date boundary segments */
|
|
if (mask->segs_in)
|
|
g_free (mask->segs_in);
|
|
if (mask->segs_out)
|
|
g_free (mask->segs_out);
|
|
|
|
if (channel_bounds (mask, &x3, &y3, &x4, &y4))
|
|
{
|
|
pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles,
|
|
x3, y3, (x4 - x3), (y4 - y3), FALSE);
|
|
mask->segs_out = find_mask_boundary (&bPR, &mask->num_segs_out,
|
|
IgnoreBounds,
|
|
x1, y1,
|
|
x2, y2);
|
|
x1 = MAX (x1, x3);
|
|
y1 = MAX (y1, y3);
|
|
x2 = MIN (x2, x4);
|
|
y2 = MIN (y2, y4);
|
|
|
|
if (x2 > x1 && y2 > y1)
|
|
{
|
|
pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles,
|
|
0, 0,
|
|
GIMP_DRAWABLE (mask)->width,
|
|
GIMP_DRAWABLE (mask)->height, FALSE);
|
|
mask->segs_in = find_mask_boundary (&bPR, &mask->num_segs_in,
|
|
WithinBounds,
|
|
x1, y1,
|
|
x2, y2);
|
|
}
|
|
else
|
|
{
|
|
mask->segs_in = NULL;
|
|
mask->num_segs_in = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mask->segs_in = NULL;
|
|
mask->segs_out = NULL;
|
|
mask->num_segs_in = 0;
|
|
mask->num_segs_out = 0;
|
|
}
|
|
mask->boundary_known = TRUE;
|
|
}
|
|
|
|
*segs_in = mask->segs_in;
|
|
*segs_out = mask->segs_out;
|
|
*num_segs_in = mask->num_segs_in;
|
|
*num_segs_out = mask->num_segs_out;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gint
|
|
channel_value (Channel *mask,
|
|
gint x,
|
|
gint y)
|
|
{
|
|
Tile *tile;
|
|
gint val;
|
|
|
|
/* Some checks to cut back on unnecessary work */
|
|
if (mask->bounds_known)
|
|
{
|
|
if (mask->empty)
|
|
return 0;
|
|
else if (x < mask->x1 || x >= mask->x2 || y < mask->y1 || y >= mask->y2)
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
if (x < 0 || x >= GIMP_DRAWABLE (mask)->width ||
|
|
y < 0 || y >= GIMP_DRAWABLE (mask)->height)
|
|
return 0;
|
|
}
|
|
|
|
tile = tile_manager_get_tile (GIMP_DRAWABLE (mask)->tiles, x, y, TRUE, FALSE);
|
|
val = *(guchar *) (tile_data_pointer (tile, x % TILE_WIDTH, y % TILE_HEIGHT));
|
|
tile_release (tile, FALSE);
|
|
|
|
return val;
|
|
}
|
|
|
|
gboolean
|
|
channel_bounds (Channel *mask,
|
|
gint *x1,
|
|
gint *y1,
|
|
gint *x2,
|
|
gint *y2)
|
|
{
|
|
PixelRegion maskPR;
|
|
guchar *data, *data1;
|
|
gint x, y;
|
|
gint ex, ey;
|
|
gint tx1, tx2, ty1, ty2;
|
|
gint minx, maxx;
|
|
gpointer pr;
|
|
|
|
/* if the mask's bounds have already been reliably calculated... */
|
|
if (mask->bounds_known)
|
|
{
|
|
*x1 = mask->x1;
|
|
*y1 = mask->y1;
|
|
*x2 = mask->x2;
|
|
*y2 = mask->y2;
|
|
|
|
return !mask->empty;
|
|
}
|
|
|
|
/* go through and calculate the bounds */
|
|
tx1 = GIMP_DRAWABLE (mask)->width;
|
|
ty1 = GIMP_DRAWABLE (mask)->height;
|
|
tx2 = 0;
|
|
ty2 = 0;
|
|
|
|
pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
|
|
0, 0,
|
|
GIMP_DRAWABLE (mask)->width,
|
|
GIMP_DRAWABLE (mask)->height, FALSE);
|
|
for (pr = pixel_regions_register (1, &maskPR);
|
|
pr != NULL;
|
|
pr = pixel_regions_process (pr))
|
|
{
|
|
data1 = data = maskPR.data;
|
|
ex = maskPR.x + maskPR.w;
|
|
ey = maskPR.y + maskPR.h;
|
|
/* only check the pixels if this tile is not fully within the currently
|
|
computed bounds */
|
|
if (maskPR.x < tx1 || ex > tx2 ||
|
|
maskPR.y < ty1 || ey > ty2)
|
|
{
|
|
/* Check upper left and lower right corners to see if we can
|
|
avoid checking the rest of the pixels in this tile */
|
|
if (data[0] && data[maskPR.rowstride*(maskPR.h - 1) + maskPR.w - 1])
|
|
{
|
|
if (maskPR.x < tx1)
|
|
tx1 = maskPR.x;
|
|
if (ex > tx2)
|
|
tx2 = ex;
|
|
if (maskPR.y < ty1)
|
|
ty1 = maskPR.y;
|
|
if (ey > ty2)
|
|
ty2 = ey;
|
|
}
|
|
else
|
|
for (y = maskPR.y; y < ey; y++, data1 += maskPR.rowstride)
|
|
{
|
|
for (x = maskPR.x, data = data1; x < ex; x++, data++)
|
|
if (*data)
|
|
{
|
|
minx = x;
|
|
maxx = x;
|
|
for (; x < ex; x++, data++)
|
|
if (*data)
|
|
maxx = x;
|
|
if (minx < tx1)
|
|
tx1 = minx;
|
|
if (maxx > tx2)
|
|
tx2 = maxx;
|
|
if (y < ty1)
|
|
ty1 = y;
|
|
if (y > ty2)
|
|
ty2 = y;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
tx2 = CLAMP (tx2 + 1, 0, GIMP_DRAWABLE (mask)->width);
|
|
ty2 = CLAMP (ty2 + 1, 0, GIMP_DRAWABLE (mask)->height);
|
|
|
|
if (tx1 == GIMP_DRAWABLE (mask)->width && ty1 == GIMP_DRAWABLE (mask)->height)
|
|
{
|
|
mask->empty = TRUE;
|
|
mask->x1 = 0;
|
|
mask->y1 = 0;
|
|
mask->x2 = GIMP_DRAWABLE (mask)->width;
|
|
mask->y2 = GIMP_DRAWABLE (mask)->height;
|
|
}
|
|
else
|
|
{
|
|
mask->empty = FALSE;
|
|
mask->x1 = tx1;
|
|
mask->y1 = ty1;
|
|
mask->x2 = tx2;
|
|
mask->y2 = ty2;
|
|
}
|
|
mask->bounds_known = TRUE;
|
|
|
|
*x1 = tx1;
|
|
*x2 = tx2;
|
|
*y1 = ty1;
|
|
*y2 = ty2;
|
|
|
|
return !mask->empty;
|
|
}
|
|
|
|
gboolean
|
|
channel_is_empty (Channel *mask)
|
|
{
|
|
PixelRegion maskPR;
|
|
guchar * data;
|
|
gint x, y;
|
|
gpointer pr;
|
|
|
|
if (mask->bounds_known)
|
|
return mask->empty;
|
|
|
|
pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
|
|
0, 0,
|
|
GIMP_DRAWABLE (mask)->width,
|
|
GIMP_DRAWABLE (mask)->height, FALSE);
|
|
for (pr = pixel_regions_register (1, &maskPR);
|
|
pr != NULL;
|
|
pr = pixel_regions_process (pr))
|
|
{
|
|
/* check if any pixel in the mask is non-zero */
|
|
data = maskPR.data;
|
|
for (y = 0; y < maskPR.h; y++)
|
|
for (x = 0; x < maskPR.w; x++)
|
|
if (*data++)
|
|
{
|
|
pixel_regions_process_stop (pr);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* The mask is empty, meaning we can set the bounds as known */
|
|
if (mask->segs_in)
|
|
g_free (mask->segs_in);
|
|
if (mask->segs_out)
|
|
g_free (mask->segs_out);
|
|
|
|
mask->empty = TRUE;
|
|
mask->segs_in = NULL;
|
|
mask->segs_out = NULL;
|
|
mask->num_segs_in = 0;
|
|
mask->num_segs_out = 0;
|
|
mask->bounds_known = TRUE;
|
|
mask->boundary_known = TRUE;
|
|
mask->x1 = 0;
|
|
mask->y1 = 0;
|
|
mask->x2 = GIMP_DRAWABLE (mask)->width;
|
|
mask->y2 = GIMP_DRAWABLE (mask)->height;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
channel_add_segment (Channel *mask,
|
|
gint x,
|
|
gint y,
|
|
gint width,
|
|
gint value)
|
|
{
|
|
PixelRegion maskPR;
|
|
guchar *data;
|
|
gint val;
|
|
gint x2;
|
|
gpointer pr;
|
|
|
|
/* check horizontal extents... */
|
|
x2 = x + width;
|
|
if (x2 < 0) x2 = 0;
|
|
if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
|
|
if (x < 0) x = 0;
|
|
if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
|
|
width = x2 - x;
|
|
if (!width) return;
|
|
|
|
if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
|
|
return;
|
|
|
|
pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
|
|
x, y, width, 1, TRUE);
|
|
for (pr = pixel_regions_register (1, &maskPR);
|
|
pr != NULL;
|
|
pr = pixel_regions_process (pr))
|
|
{
|
|
data = maskPR.data;
|
|
width = maskPR.w;
|
|
while (width--)
|
|
{
|
|
val = *data + value;
|
|
if (val > 255)
|
|
val = 255;
|
|
*data++ = val;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
channel_sub_segment (Channel *mask,
|
|
gint x,
|
|
gint y,
|
|
gint width,
|
|
gint value)
|
|
{
|
|
PixelRegion maskPR;
|
|
guchar *data;
|
|
gint val;
|
|
gint x2;
|
|
gpointer pr;
|
|
|
|
/* check horizontal extents... */
|
|
x2 = x + width;
|
|
if (x2 < 0) x2 = 0;
|
|
if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
|
|
if (x < 0) x = 0;
|
|
if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
|
|
width = x2 - x;
|
|
if (!width) return;
|
|
|
|
if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
|
|
return;
|
|
|
|
pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles, x, y, width, 1, TRUE);
|
|
for (pr = pixel_regions_register (1, &maskPR);
|
|
pr != NULL;
|
|
pr = pixel_regions_process (pr))
|
|
{
|
|
data = maskPR.data;
|
|
width = maskPR.w;
|
|
while (width--)
|
|
{
|
|
val = *data - value;
|
|
if (val < 0)
|
|
val = 0;
|
|
*data++ = val;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
channel_combine_rect (Channel *mask,
|
|
ChannelOps op,
|
|
gint x,
|
|
gint y,
|
|
gint w,
|
|
gint h)
|
|
{
|
|
gint x2, y2;
|
|
PixelRegion maskPR;
|
|
guchar color;
|
|
|
|
y2 = y + h;
|
|
x2 = x + w;
|
|
|
|
x = CLAMP (x, 0, GIMP_DRAWABLE (mask)->width);
|
|
y = CLAMP (y, 0, GIMP_DRAWABLE (mask)->height);
|
|
x2 = CLAMP (x2, 0, GIMP_DRAWABLE (mask)->width);
|
|
y2 = CLAMP (y2, 0, GIMP_DRAWABLE (mask)->height);
|
|
|
|
if (x2 - x <= 0 || y2 - y <= 0)
|
|
return;
|
|
|
|
pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
|
|
x, y, x2 - x, y2 - y, TRUE);
|
|
if (op == ADD || op == REPLACE)
|
|
color = 255;
|
|
else
|
|
color = 0;
|
|
color_region (&maskPR, &color);
|
|
|
|
/* Determine new boundary */
|
|
if (mask->bounds_known && (op == ADD) && !mask->empty)
|
|
{
|
|
if (x < mask->x1)
|
|
mask->x1 = x;
|
|
if (y < mask->y1)
|
|
mask->y1 = y;
|
|
if ((x + w) > mask->x2)
|
|
mask->x2 = (x + w);
|
|
if ((y + h) > mask->y2)
|
|
mask->y2 = (y + h);
|
|
}
|
|
else if (op == REPLACE || mask->empty)
|
|
{
|
|
mask->empty = FALSE;
|
|
mask->x1 = x;
|
|
mask->y1 = y;
|
|
mask->x2 = x + w;
|
|
mask->y2 = y + h;
|
|
}
|
|
else
|
|
mask->bounds_known = FALSE;
|
|
|
|
mask->x1 = CLAMP (mask->x1, 0, GIMP_DRAWABLE (mask)->width);
|
|
mask->y1 = CLAMP (mask->y1, 0, GIMP_DRAWABLE (mask)->height);
|
|
mask->x2 = CLAMP (mask->x2, 0, GIMP_DRAWABLE (mask)->width);
|
|
mask->y2 = CLAMP (mask->y2, 0, GIMP_DRAWABLE (mask)->height);
|
|
}
|
|
|
|
void
|
|
channel_combine_ellipse (Channel *mask,
|
|
ChannelOps op,
|
|
gint x,
|
|
gint y,
|
|
gint w,
|
|
gint h,
|
|
gboolean antialias)
|
|
{
|
|
gint i, j;
|
|
gint x0, x1, x2;
|
|
gint val, last;
|
|
gfloat a_sqr, b_sqr, aob_sqr;
|
|
gfloat w_sqr, h_sqr;
|
|
gfloat y_sqr;
|
|
gfloat t0, t1;
|
|
gfloat r;
|
|
gfloat cx, cy;
|
|
gfloat rad;
|
|
gfloat dist;
|
|
|
|
if (!w || !h)
|
|
return;
|
|
|
|
a_sqr = (w * w / 4.0);
|
|
b_sqr = (h * h / 4.0);
|
|
aob_sqr = a_sqr / b_sqr;
|
|
|
|
cx = x + w / 2.0;
|
|
cy = y + h / 2.0;
|
|
|
|
for (i = y; i < (y + h); i++)
|
|
{
|
|
if (i >= 0 && i < GIMP_DRAWABLE (mask)->height)
|
|
{
|
|
/* Non-antialiased code */
|
|
if (!antialias)
|
|
{
|
|
y_sqr = (i + 0.5 - cy) * (i + 0.5 - cy);
|
|
rad = sqrt (a_sqr - a_sqr * y_sqr / (double) b_sqr);
|
|
x1 = ROUND (cx - rad);
|
|
x2 = ROUND (cx + rad);
|
|
|
|
switch (op)
|
|
{
|
|
case ADD: case REPLACE:
|
|
channel_add_segment (mask, x1, i, (x2 - x1), 255);
|
|
break;
|
|
case SUB :
|
|
channel_sub_segment (mask, x1, i, (x2 - x1), 255);
|
|
break;
|
|
default:
|
|
g_warning ("Only ADD, REPLACE and SUB are valid for channel_combine!");
|
|
break;
|
|
}
|
|
}
|
|
/* antialiasing */
|
|
else
|
|
{
|
|
x0 = x;
|
|
last = 0;
|
|
h_sqr = (i + 0.5 - cy) * (i + 0.5 - cy);
|
|
for (j = x; j < (x + w); j++)
|
|
{
|
|
w_sqr = (j + 0.5 - cx) * (j + 0.5 - cx);
|
|
|
|
if (h_sqr != 0)
|
|
{
|
|
t0 = w_sqr / h_sqr;
|
|
t1 = a_sqr / (t0 + aob_sqr);
|
|
r = sqrt (t1 + t0 * t1);
|
|
rad = sqrt (w_sqr + h_sqr);
|
|
dist = rad - r;
|
|
}
|
|
else
|
|
dist = -1.0;
|
|
|
|
if (dist < -0.5)
|
|
val = 255;
|
|
else if (dist < 0.5)
|
|
val = (int) (255 * (1 - (dist + 0.5)));
|
|
else
|
|
val = 0;
|
|
|
|
if (last != val && last)
|
|
{
|
|
switch (op)
|
|
{
|
|
case ADD: case REPLACE:
|
|
channel_add_segment (mask, x0, i, j - x0, last);
|
|
break;
|
|
case SUB:
|
|
channel_sub_segment (mask, x0, i, j - x0, last);
|
|
break;
|
|
default:
|
|
g_warning ("Only ADD, REPLACE and SUB are valid for channel_combine!");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (last != val)
|
|
{
|
|
x0 = j;
|
|
last = val;
|
|
/* because we are symetric accross the y axis we can
|
|
skip ahead a bit if we are inside the ellipse*/
|
|
if (val == 255 && j < cx)
|
|
j = cx + (cx - j) - 1;
|
|
}
|
|
}
|
|
|
|
if (last)
|
|
{
|
|
if (op == ADD || op == REPLACE)
|
|
channel_add_segment (mask, x0, i, j - x0, last);
|
|
else if (op == SUB)
|
|
channel_sub_segment (mask, x0, i, j - x0, last);
|
|
else
|
|
g_warning ("Only ADD, REPLACE and SUB are valid for channel_combine!");
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/* Determine new boundary */
|
|
if (mask->bounds_known && (op == ADD) && !mask->empty)
|
|
{
|
|
if (x < mask->x1)
|
|
mask->x1 = x;
|
|
if (y < mask->y1)
|
|
mask->y1 = y;
|
|
if ((x + w) > mask->x2)
|
|
mask->x2 = (x + w);
|
|
if ((y + h) > mask->y2)
|
|
mask->y2 = (y + h);
|
|
}
|
|
else if (op == REPLACE || mask->empty)
|
|
{
|
|
mask->empty = FALSE;
|
|
mask->x1 = x;
|
|
mask->y1 = y;
|
|
mask->x2 = x + w;
|
|
mask->y2 = y + h;
|
|
}
|
|
else
|
|
mask->bounds_known = FALSE;
|
|
|
|
mask->x1 = CLAMP (mask->x1, 0, GIMP_DRAWABLE (mask)->width);
|
|
mask->y1 = CLAMP (mask->y1, 0, GIMP_DRAWABLE (mask)->height);
|
|
mask->x2 = CLAMP (mask->x2, 0, GIMP_DRAWABLE (mask)->width);
|
|
mask->y2 = CLAMP (mask->y2, 0, GIMP_DRAWABLE (mask)->height);
|
|
}
|
|
|
|
static void
|
|
channel_combine_sub_region_add (void *unused,
|
|
PixelRegion *srcPR,
|
|
PixelRegion *destPR)
|
|
{
|
|
guchar *src, *dest;
|
|
gint x, y, val;
|
|
|
|
src = srcPR->data;
|
|
dest = destPR->data;
|
|
|
|
for (y = 0; y < srcPR->h; y++)
|
|
{
|
|
for (x = 0; x < srcPR->w; x++)
|
|
{
|
|
val = dest[x] + src[x];
|
|
if (val > 255)
|
|
dest[x] = 255;
|
|
else
|
|
dest[x] = val;
|
|
}
|
|
src += srcPR->rowstride;
|
|
dest += destPR->rowstride;
|
|
}
|
|
}
|
|
|
|
static void
|
|
channel_combine_sub_region_sub (void *unused,
|
|
PixelRegion *srcPR,
|
|
PixelRegion *destPR)
|
|
{
|
|
guchar *src, *dest;
|
|
gint x, y;
|
|
|
|
src = srcPR->data;
|
|
dest = destPR->data;
|
|
|
|
for (y = 0; y < srcPR->h; y++)
|
|
{
|
|
for (x = 0; x < srcPR->w; x++)
|
|
{
|
|
if (src[x] > dest[x])
|
|
dest[x] = 0;
|
|
else
|
|
dest[x]-= src[x];
|
|
}
|
|
src += srcPR->rowstride;
|
|
dest += destPR->rowstride;
|
|
}
|
|
}
|
|
|
|
static void
|
|
channel_combine_sub_region_intersect (void *unused,
|
|
PixelRegion *srcPR,
|
|
PixelRegion *destPR)
|
|
{
|
|
guchar *src, *dest;
|
|
gint x, y;
|
|
|
|
src = srcPR->data;
|
|
dest = destPR->data;
|
|
|
|
for (y = 0; y < srcPR->h; y++)
|
|
{
|
|
for (x = 0; x < srcPR->w; x++)
|
|
{
|
|
dest[x] = MIN (dest[x], src[x]);
|
|
}
|
|
src += srcPR->rowstride;
|
|
dest += destPR->rowstride;
|
|
}
|
|
}
|
|
|
|
void
|
|
channel_combine_mask (Channel *mask,
|
|
Channel *add_on,
|
|
ChannelOps op,
|
|
gint off_x,
|
|
gint off_y)
|
|
{
|
|
PixelRegion srcPR, destPR;
|
|
gint x1, y1, x2, y2;
|
|
gint w, h;
|
|
|
|
x1 = CLAMP (off_x, 0, GIMP_DRAWABLE (mask)->width);
|
|
y1 = CLAMP (off_y, 0, GIMP_DRAWABLE (mask)->height);
|
|
x2 = CLAMP (off_x + GIMP_DRAWABLE (add_on)->width, 0,
|
|
GIMP_DRAWABLE (mask)->width);
|
|
y2 = CLAMP (off_y + GIMP_DRAWABLE (add_on)->height, 0,
|
|
GIMP_DRAWABLE (mask)->height);
|
|
|
|
w = (x2 - x1);
|
|
h = (y2 - y1);
|
|
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE (add_on)->tiles,
|
|
(x1 - off_x), (y1 - off_y), w, h, FALSE);
|
|
pixel_region_init (&destPR, GIMP_DRAWABLE (mask)->tiles, x1, y1, w, h, TRUE);
|
|
|
|
switch (op)
|
|
{
|
|
case ADD: case REPLACE:
|
|
pixel_regions_process_parallel ((p_func) channel_combine_sub_region_add,
|
|
NULL, 2, &srcPR, &destPR);
|
|
break;
|
|
case SUB:
|
|
pixel_regions_process_parallel ((p_func) channel_combine_sub_region_sub,
|
|
NULL, 2, &srcPR, &destPR);
|
|
break;
|
|
case INTERSECT:
|
|
pixel_regions_process_parallel ((p_func)
|
|
channel_combine_sub_region_intersect,
|
|
NULL, 2, &srcPR, &destPR);
|
|
break;
|
|
default:
|
|
g_message ("Error: unknown opperation type in channel_combine_mask\n");
|
|
break;
|
|
}
|
|
mask->bounds_known = FALSE;
|
|
}
|
|
|
|
void
|
|
channel_feather (Channel *input,
|
|
Channel *output,
|
|
gdouble radius_x,
|
|
gdouble radius_y,
|
|
ChannelOps op,
|
|
gint off_x,
|
|
gint off_y)
|
|
{
|
|
gint x1, y1, x2, y2;
|
|
PixelRegion srcPR;
|
|
|
|
x1 = CLAMP (off_x, 0, GIMP_DRAWABLE (output)->width);
|
|
y1 = CLAMP (off_y, 0, GIMP_DRAWABLE (output)->height);
|
|
x2 = CLAMP (off_x + GIMP_DRAWABLE (input)->width, 0,
|
|
GIMP_DRAWABLE (output)->width);
|
|
y2 = CLAMP (off_y + GIMP_DRAWABLE (input)->height, 0,
|
|
GIMP_DRAWABLE (output)->height);
|
|
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE (input)->tiles,
|
|
(x1 - off_x), (y1 - off_y), (x2 - x1), (y2 - y1), FALSE);
|
|
gaussian_blur_region (&srcPR, radius_x, radius_y);
|
|
|
|
if (input != output)
|
|
channel_combine_mask (output, input, op, 0, 0);
|
|
|
|
output->bounds_known = FALSE;
|
|
}
|
|
|
|
void
|
|
channel_push_undo (Channel *mask)
|
|
{
|
|
gint x1, y1, x2, y2;
|
|
MaskUndo *mask_undo;
|
|
TileManager *undo_tiles;
|
|
PixelRegion srcPR, destPR;
|
|
GImage *gimage;
|
|
|
|
mask_undo = g_new (MaskUndo, 1);
|
|
if (channel_bounds (mask, &x1, &y1, &x2, &y2))
|
|
{
|
|
undo_tiles = tile_manager_new ((x2 - x1), (y2 - y1), 1);
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE (mask)->tiles,
|
|
x1, y1, (x2 - x1), (y2 - y1), FALSE);
|
|
pixel_region_init (&destPR, undo_tiles, 0, 0, (x2 - x1), (y2 - y1), TRUE);
|
|
copy_region (&srcPR, &destPR);
|
|
}
|
|
else
|
|
undo_tiles = NULL;
|
|
|
|
mask_undo->tiles = undo_tiles;
|
|
mask_undo->x = x1;
|
|
mask_undo->y = y1;
|
|
|
|
/* push the undo buffer onto the undo stack */
|
|
gimage = GIMP_DRAWABLE (mask)->gimage;
|
|
undo_push_mask (gimage, mask_undo);
|
|
gimage_mask_invalidate (gimage);
|
|
|
|
/* invalidate the preview */
|
|
GIMP_DRAWABLE (mask)->preview_valid = FALSE;
|
|
}
|
|
|
|
void
|
|
channel_clear (Channel *mask)
|
|
{
|
|
PixelRegion maskPR;
|
|
guchar bg = 0;
|
|
|
|
/* push the current channel onto the undo stack */
|
|
channel_push_undo (mask);
|
|
|
|
if (mask->bounds_known && !mask->empty)
|
|
{
|
|
pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
|
|
mask->x1, mask->y1,
|
|
(mask->x2 - mask->x1), (mask->y2 - mask->y1), TRUE);
|
|
color_region (&maskPR, &bg);
|
|
}
|
|
else
|
|
{
|
|
/* clear the mask */
|
|
pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
|
|
0, 0,
|
|
GIMP_DRAWABLE (mask)->width,
|
|
GIMP_DRAWABLE (mask)->height, TRUE);
|
|
color_region (&maskPR, &bg);
|
|
}
|
|
|
|
/* we know the bounds */
|
|
mask->bounds_known = TRUE;
|
|
mask->empty = TRUE;
|
|
mask->x1 = 0;
|
|
mask->y1 = 0;
|
|
mask->x2 = GIMP_DRAWABLE (mask)->width;
|
|
mask->y2 = GIMP_DRAWABLE (mask)->height;
|
|
}
|
|
|
|
void
|
|
channel_invert (Channel *mask)
|
|
{
|
|
PixelRegion maskPR;
|
|
GimpLut *lut;
|
|
|
|
/* push the current channel onto the undo stack */
|
|
channel_push_undo (mask);
|
|
|
|
pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
|
|
0, 0,
|
|
GIMP_DRAWABLE (mask)->width,
|
|
GIMP_DRAWABLE (mask)->height, TRUE);
|
|
|
|
lut = invert_lut_new (1);
|
|
|
|
pixel_regions_process_parallel ((p_func) gimp_lut_process_inline,
|
|
lut, 1, &maskPR);
|
|
gimp_lut_free (lut);
|
|
mask->bounds_known = FALSE;
|
|
}
|
|
|
|
void
|
|
channel_sharpen (Channel *mask)
|
|
{
|
|
PixelRegion maskPR;
|
|
GimpLut *lut;
|
|
|
|
/* push the current channel onto the undo stack */
|
|
channel_push_undo (mask);
|
|
|
|
pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
|
|
0, 0,
|
|
GIMP_DRAWABLE (mask)->width,
|
|
GIMP_DRAWABLE (mask)->height, TRUE);
|
|
lut = threshold_lut_new (0.5, 1);
|
|
|
|
pixel_regions_process_parallel ((p_func) gimp_lut_process_inline,
|
|
lut, 1, &maskPR);
|
|
gimp_lut_free (lut);
|
|
}
|
|
|
|
void
|
|
channel_all (Channel *mask)
|
|
{
|
|
PixelRegion maskPR;
|
|
guchar bg = 255;
|
|
|
|
/* push the current channel onto the undo stack */
|
|
channel_push_undo (mask);
|
|
|
|
/* clear the mask */
|
|
pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
|
|
0, 0,
|
|
GIMP_DRAWABLE (mask)->width,
|
|
GIMP_DRAWABLE (mask)->height, TRUE);
|
|
color_region (&maskPR, &bg);
|
|
|
|
/* we know the bounds */
|
|
mask->bounds_known = TRUE;
|
|
mask->empty = FALSE;
|
|
mask->x1 = 0;
|
|
mask->y1 = 0;
|
|
mask->x2 = GIMP_DRAWABLE (mask)->width;
|
|
mask->y2 = GIMP_DRAWABLE (mask)->height;
|
|
}
|
|
|
|
void
|
|
channel_border (Channel *mask,
|
|
gint radius_x,
|
|
gint radius_y)
|
|
{
|
|
PixelRegion bPR;
|
|
gint x1, y1, x2, y2;
|
|
|
|
if (radius_x < 0 || radius_y < 0)
|
|
return;
|
|
|
|
if (! channel_bounds (mask, &x1, &y1, &x2, &y2))
|
|
return;
|
|
if (channel_is_empty (mask))
|
|
return;
|
|
|
|
if (x1 - radius_x < 0)
|
|
x1 = 0;
|
|
else
|
|
x1 -= radius_x;
|
|
if (x2 + radius_x > GIMP_DRAWABLE (mask)->width)
|
|
x2 = GIMP_DRAWABLE (mask)->width;
|
|
else
|
|
x2 += radius_x;
|
|
|
|
if (y1 - radius_y < 0)
|
|
y1 = 0;
|
|
else
|
|
y1 -= radius_y;
|
|
if (y2 + radius_y > GIMP_DRAWABLE (mask)->height)
|
|
y2 = GIMP_DRAWABLE (mask)->height;
|
|
else
|
|
y2 += radius_y;
|
|
/* push the current channel onto the undo stack */
|
|
channel_push_undo (mask);
|
|
|
|
pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles, x1, y1,
|
|
(x2-x1), (y2-y1), TRUE);
|
|
|
|
border_region (&bPR, radius_x, radius_y);
|
|
|
|
mask->bounds_known = FALSE;
|
|
}
|
|
|
|
void
|
|
channel_grow (Channel *mask,
|
|
gint radius_x,
|
|
gint radius_y)
|
|
{
|
|
PixelRegion bPR;
|
|
gint x1, y1, x2, y2;
|
|
|
|
if (radius_x == 0 && radius_y == 0)
|
|
return;
|
|
|
|
if (radius_x <= 0 && radius_y <= 0)
|
|
{
|
|
channel_shrink (mask, -radius_x, -radius_y, FALSE);
|
|
return;
|
|
}
|
|
|
|
if (radius_x < 0 || radius_y < 0)
|
|
return;
|
|
|
|
if (! channel_bounds (mask, &x1, &y1, &x2, &y2))
|
|
return;
|
|
if (channel_is_empty (mask))
|
|
return;
|
|
|
|
if (x1 - radius_x > 0)
|
|
x1 = x1 - radius_x;
|
|
else
|
|
x1 = 0;
|
|
if (y1 - radius_y > 0)
|
|
y1 = y1 - radius_y;
|
|
else
|
|
y1 = 0;
|
|
if (x2 + radius_x < GIMP_DRAWABLE (mask)->width)
|
|
x2 = x2 + radius_x;
|
|
else
|
|
x2 = GIMP_DRAWABLE (mask)->width;
|
|
if (y2 + radius_y < GIMP_DRAWABLE (mask)->height)
|
|
y2 = y2 + radius_y;
|
|
else
|
|
y2 = GIMP_DRAWABLE (mask)->height;
|
|
|
|
/* push the current channel onto the undo stack */
|
|
channel_push_undo (mask);
|
|
|
|
/* need full extents for grow, not! */
|
|
pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles, x1, y1, (x2 - x1),
|
|
(y2 - y1), TRUE);
|
|
|
|
fatten_region (&bPR, radius_x, radius_y);
|
|
|
|
mask->bounds_known = FALSE;
|
|
}
|
|
|
|
void
|
|
channel_shrink (Channel *mask,
|
|
gint radius_x,
|
|
gint radius_y,
|
|
gboolean edge_lock)
|
|
{
|
|
PixelRegion bPR;
|
|
gint x1, y1, x2, y2;
|
|
|
|
if (radius_x == 0 && radius_y == 0)
|
|
return;
|
|
|
|
if (radius_x <= 0 && radius_y <= 0)
|
|
{
|
|
channel_grow (mask, -radius_x, -radius_y);
|
|
return;
|
|
}
|
|
|
|
if (radius_x < 0 || radius_y < 0)
|
|
return;
|
|
|
|
if (! channel_bounds (mask, &x1, &y1, &x2, &y2))
|
|
return;
|
|
if (channel_is_empty (mask))
|
|
return;
|
|
|
|
if (x1 > 0)
|
|
x1--;
|
|
if (y1 > 0)
|
|
y1--;
|
|
if (x2 < GIMP_DRAWABLE (mask)->width)
|
|
x2++;
|
|
if (y2 < GIMP_DRAWABLE (mask)->height)
|
|
y2++;
|
|
|
|
/* push the current channel onto the undo stack */
|
|
channel_push_undo (mask);
|
|
|
|
pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles, x1, y1, (x2 - x1),
|
|
(y2 - y1), TRUE);
|
|
|
|
thin_region (&bPR, radius_x, radius_y, edge_lock);
|
|
|
|
mask->bounds_known = FALSE;
|
|
}
|
|
|
|
void
|
|
channel_translate (Channel *mask,
|
|
gint off_x,
|
|
gint off_y)
|
|
{
|
|
gint width, height;
|
|
Channel *tmp_mask;
|
|
PixelRegion srcPR, destPR;
|
|
guchar empty = 0;
|
|
gint x1, y1, x2, y2;
|
|
|
|
tmp_mask = NULL;
|
|
|
|
/* push the current channel onto the undo stack */
|
|
channel_push_undo (mask);
|
|
|
|
channel_bounds (mask, &x1, &y1, &x2, &y2);
|
|
x1 = CLAMP ((x1 + off_x), 0, GIMP_DRAWABLE (mask)->width);
|
|
y1 = CLAMP ((y1 + off_y), 0, GIMP_DRAWABLE (mask)->height);
|
|
x2 = CLAMP ((x2 + off_x), 0, GIMP_DRAWABLE (mask)->width);
|
|
y2 = CLAMP ((y2 + off_y), 0, GIMP_DRAWABLE (mask)->height);
|
|
|
|
width = x2 - x1;
|
|
height = y2 - y1;
|
|
|
|
/* make sure width and height are non-zero */
|
|
if (width != 0 && height != 0)
|
|
{
|
|
/* copy the portion of the mask we will keep to a
|
|
* temporary buffer
|
|
*/
|
|
tmp_mask = channel_new_mask (GIMP_DRAWABLE (mask)->gimage, width, height);
|
|
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE (mask)->tiles,
|
|
x1 - off_x, y1 - off_y, width, height, FALSE);
|
|
pixel_region_init (&destPR, GIMP_DRAWABLE (tmp_mask)->tiles,
|
|
0, 0, width, height, TRUE);
|
|
copy_region (&srcPR, &destPR);
|
|
}
|
|
|
|
/* clear the mask */
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE (mask)->tiles,
|
|
0, 0,
|
|
GIMP_DRAWABLE (mask)->width,
|
|
GIMP_DRAWABLE (mask)->height, TRUE);
|
|
color_region (&srcPR, &empty);
|
|
|
|
if (width != 0 && height != 0)
|
|
{
|
|
/* copy the temp mask back to the mask */
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE (tmp_mask)->tiles,
|
|
0, 0, width, height, FALSE);
|
|
pixel_region_init (&destPR, GIMP_DRAWABLE (mask)->tiles,
|
|
x1, y1, width, height, TRUE);
|
|
copy_region (&srcPR, &destPR);
|
|
|
|
/* free the temporary mask */
|
|
channel_delete (tmp_mask);
|
|
}
|
|
|
|
/* calculate new bounds */
|
|
if (width == 0 || height == 0)
|
|
{
|
|
mask->empty = TRUE;
|
|
mask->x1 = 0; mask->y1 = 0;
|
|
mask->x2 = GIMP_DRAWABLE (mask)->width;
|
|
mask->y2 = GIMP_DRAWABLE (mask)->height;
|
|
}
|
|
else
|
|
{
|
|
mask->x1 = x1;
|
|
mask->y1 = y1;
|
|
mask->x2 = x2;
|
|
mask->y2 = y2;
|
|
}
|
|
}
|
|
|
|
void
|
|
channel_layer_alpha (Channel *mask,
|
|
Layer *layer)
|
|
{
|
|
PixelRegion srcPR, destPR;
|
|
guchar empty = 0;
|
|
gint x1, y1, x2, y2;
|
|
|
|
/* push the current mask onto the undo stack */
|
|
channel_push_undo (mask);
|
|
|
|
/* clear the mask */
|
|
pixel_region_init (&destPR, GIMP_DRAWABLE (mask)->tiles,
|
|
0, 0,
|
|
GIMP_DRAWABLE (mask)->width,
|
|
GIMP_DRAWABLE (mask)->height, TRUE);
|
|
color_region (&destPR, &empty);
|
|
|
|
x1 = CLAMP (GIMP_DRAWABLE (layer)->offset_x, 0, GIMP_DRAWABLE (mask)->width);
|
|
y1 = CLAMP (GIMP_DRAWABLE (layer)->offset_y, 0, GIMP_DRAWABLE (mask)->height);
|
|
x2 = CLAMP (GIMP_DRAWABLE (layer)->offset_x + GIMP_DRAWABLE (layer)->width,
|
|
0, GIMP_DRAWABLE (mask)->width);
|
|
y2 = CLAMP (GIMP_DRAWABLE( layer)->offset_y + GIMP_DRAWABLE (layer)->height,
|
|
0, GIMP_DRAWABLE (mask)->height);
|
|
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE (layer)->tiles,
|
|
(x1 - GIMP_DRAWABLE (layer)->offset_x),
|
|
(y1 - GIMP_DRAWABLE (layer)->offset_y),
|
|
(x2 - x1), (y2 - y1), FALSE);
|
|
pixel_region_init (&destPR, GIMP_DRAWABLE (mask)->tiles,
|
|
x1, y1, (x2 - x1), (y2 - y1), TRUE);
|
|
extract_alpha_region (&srcPR, NULL, &destPR);
|
|
|
|
mask->bounds_known = FALSE;
|
|
}
|
|
|
|
void
|
|
channel_load (Channel *mask,
|
|
Channel *channel)
|
|
{
|
|
PixelRegion srcPR, destPR;
|
|
|
|
/* push the current mask onto the undo stack */
|
|
channel_push_undo (mask);
|
|
|
|
/* copy the channel to the mask */
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
|
|
0, 0,
|
|
GIMP_DRAWABLE (channel)->width,
|
|
GIMP_DRAWABLE (channel)->height, FALSE);
|
|
pixel_region_init (&destPR, GIMP_DRAWABLE (mask)->tiles,
|
|
0, 0,
|
|
GIMP_DRAWABLE (channel)->width,
|
|
GIMP_DRAWABLE (channel)->height, TRUE);
|
|
copy_region (&srcPR, &destPR);
|
|
|
|
mask->bounds_known = FALSE;
|
|
}
|
|
|
|
void
|
|
channel_invalidate_bounds (Channel *channel)
|
|
{
|
|
channel->bounds_known = FALSE;
|
|
}
|
|
|