Files
gimp/app/core/gimplayer.c
Michael Natterer 5f63e079b8 Chopped up the display stuff (beware: unfinished)...
2001-10-31  Michael Natterer  <mitch@gimp.org>

	Chopped up the display stuff (beware: unfinished)...

	The plan is that GimpDisplay is the object which collects
	updates from the image, compresses them and waits for the GIMP
	to be idle to actually paint them. It should be a non-GUI object
	which is the model for the actual widget to connect to.

	GimpDisplayShell has all the widgets and handles painting and
	exposing of the result. Nobody should actually be required to
	update ot look at it as it should be a view on the GimpDisplay
	object.

	Much stuff is still in the wrong place and the functions don't
	follow their files' filename namespace any more. More to come...

	* app/display/Makefile.am
	* app/display/gimpdisplay-ops.[ch]: removed. It's functions didn't
	belong together anyway.

	* app/display/gimpdisplay-area.[ch]: new files: the GimpArea
	functions.

	* app/display/gimpdisplay-handlers.[ch]: new files: signal
	handlers for GimpImage signals. Mostly from app/gui.c.

	* app/display/gimpdisplay.[ch]: removed all widgets and other
	GUI stuff. There is still much undecided here...

	* app/display/gimpdisplayshell.[ch]: actually use the object and
	filled it with all the stuff from GimpDisplay.

	* app/display/gimpdisplay-callbacks.[ch]
	* app/display/gimpdisplay-foreach.[ch]
	* app/display/gimpdisplay-render.c
	* app/display/gimpdisplay-scale.[ch]
	* app/display/gimpdisplay-scroll.[ch]
	* app/display/gimpdisplay-selection.c: changed accordingly.

	* app/core/gimp.[ch]: return a GimpObject from
	gimp_create_display() so it can be used as single GUI independent
	point to create displays, require the initial scale as parameter.

	* app/core/gimpcontext.c: changed the ugly EEKWrapper according to
	the GimpDisplay structure changes. Bugfix: set the image to NULL
	in gimp_context_display_destroyed().

	* app/core/gimpedit.c
	* app/core/gimpimage-new.c: changed gimp_create_display() calls
	accordingly.

	* app/core/gimpimage-convert.c: invalidate the layer & image
	previews here, not in the caller.

	* app/core/gimpimage-crop.c: update the whole image after cropping.

	* app/core/gimpimage.[ch]: added gimp_image_find_guide(),
	gimp_image_snap_point() and gimp_image_snap_rectangle(). Added
	"resolution_changed" and "unit_changed" signals and corresp.
	public convenience functions to emit them.

	* app/core/gimplayer.c: emit the image's "alpha_changed" signal
	when adding alpha to the bottom (and only) layer of the image.

	* app/gimpprogress.c
	* app/image_map.c
	* app/nav_window.c
	* app/qmask.c
	* app/undo.c
	* app/user_install.c: changed accordingly.

	* app/gui/edit-commands.c
	* app/gui/file-commands.c
	* app/gui/file-open-dialog.c
	* app/gui/image-commands.c
	* app/gui/info-window.c
	* app/gui/preferences-dialog.c
	* app/gui/toolbox.c
	* app/gui/view-commands.c: ditto.

	* app/gui/gui.[ch]: removed most gimp->images handlers as the
	displays connect to them themselves now. chaged gui_display_new()
	according to the gimp_create_display() changes.
	Added gui_get_screen_resolution().

	* app/tools/gimpbezierselecttool.c
	* app/tools/gimpblendtool.c
	* app/tools/gimpbucketfilltool.c
	* app/tools/gimpbycolorselecttool.c
	* app/tools/gimpclonetool.c
	* app/tools/gimpcolorpickertool.c
	* app/tools/gimpcroptool.c
	* app/tools/gimpdrawtool.c
	* app/tools/gimpeditselectiontool.c
	* app/tools/gimpfliptool.c
	* app/tools/gimpfreeselecttool.c
	* app/tools/gimpfuzzyselecttool.c
	* app/tools/gimpinktool.c
	* app/tools/gimpiscissorstool.c
	* app/tools/gimpmagnifytool.c
	* app/tools/gimpmeasuretool.c
	* app/tools/gimpmovetool.c
	* app/tools/gimppainttool.c
	* app/tools/gimppathtool.c
	* app/tools/gimprectselecttool.c
	* app/tools/gimpselectiontool.c
	* app/tools/gimptexttool.c
	* app/tools/gimptool.c
	* app/tools/gimptransformtool.c
	* app/tools/xinput_airbrush.c: lots of changes because GimpDisplay
	has become two objects. Lots of gdisp->shell casting uglyness
	added. This is fine because exactly these parts will have to go
	away.

	(GimpDisplay will provide methods for XOR drawing upon the display
	in image coordinates without the need to transform coordinates all
	the time. Also the tools shouldn't see GdkEvents but get more
	useful virtual functions which speak in image coordinates too).

	* app/widgets/gimpcomponentlistitem.c: removed a now useless image
	update.

	* tools/pdbgen/pdb/display.pdb: use gimp_create_display().

	* app/pdb/display_cmds.c: regenerated.
2001-10-31 21:18:57 +00:00

1502 lines
39 KiB
C

/* TODO: make sure has_alpha gets set */
/* 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 <glib-object.h>
#include "libgimpmath/gimpmath.h"
#include "core-types.h"
#include "base/boundary.h"
#include "base/pixel-region.h"
#include "base/tile-manager.h"
#include "base/tile.h"
#include "paint-funcs/paint-funcs.h"
#include "gimpdrawable-invert.h"
#include "gimpcontainer.h"
#include "gimpimage.h"
#include "gimplayer.h"
#include "gimplayermask.h"
#include "gimpparasitelist.h"
#include "floating_sel.h"
#include "undo.h"
#include "libgimp/gimpintl.h"
enum
{
OPACITY_CHANGED,
MODE_CHANGED,
PRESERVE_TRANS_CHANGED,
LINKED_CHANGED,
MASK_CHANGED,
LAST_SIGNAL
};
static void gimp_layer_class_init (GimpLayerClass *klass);
static void gimp_layer_init (GimpLayer *layer);
static void gimp_layer_finalize (GObject *object);
static void gimp_layer_invalidate_preview (GimpViewable *viewable);
static void gimp_layer_transform_color (GimpImage *gimage,
PixelRegion *layerPR,
PixelRegion *bufPR,
GimpDrawable *drawable,
GimpImageBaseType type);
static guint layer_signals[LAST_SIGNAL] = { 0 };
static GimpDrawableClass *parent_class = NULL;
GType
gimp_layer_get_type (void)
{
static GType layer_type = 0;
if (! layer_type)
{
static const GTypeInfo layer_info =
{
sizeof (GimpLayerClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) gimp_layer_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GimpLayer),
0, /* n_preallocs */
(GInstanceInitFunc) gimp_layer_init,
};
layer_type = g_type_register_static (GIMP_TYPE_DRAWABLE,
"GimpLayer",
&layer_info, 0);
}
return layer_type;
}
static void
gimp_layer_class_init (GimpLayerClass *klass)
{
GObjectClass *object_class;
GimpViewableClass *viewable_class;
object_class = G_OBJECT_CLASS (klass);
viewable_class = GIMP_VIEWABLE_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
layer_signals[OPACITY_CHANGED] =
g_signal_new ("opacity_changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpLayerClass, opacity_changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
layer_signals[MODE_CHANGED] =
g_signal_new ("mode_changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpLayerClass, mode_changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
layer_signals[PRESERVE_TRANS_CHANGED] =
g_signal_new ("preserve_trans_changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpLayerClass, preserve_trans_changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
layer_signals[LINKED_CHANGED] =
g_signal_new ("linked_changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpLayerClass, linked_changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
layer_signals[MASK_CHANGED] =
g_signal_new ("mask_changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpLayerClass, mask_changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
object_class->finalize = gimp_layer_finalize;
viewable_class->invalidate_preview = gimp_layer_invalidate_preview;
klass->mask_changed = NULL;
}
static void
gimp_layer_init (GimpLayer *layer)
{
layer->linked = FALSE;
layer->preserve_trans = FALSE;
layer->mask = NULL;
layer->opacity = 100;
layer->mode = NORMAL_MODE;
/* floating selection */
layer->fs.backing_store = NULL;
layer->fs.drawable = NULL;
layer->fs.initial = TRUE;
layer->fs.boundary_known = FALSE;
layer->fs.segs = NULL;
layer->fs.num_segs = 0;
}
static void
gimp_layer_finalize (GObject *object)
{
GimpLayer *layer;
g_return_if_fail (GIMP_IS_LAYER (object));
layer = GIMP_LAYER (object);
if (layer->mask)
{
g_object_unref (G_OBJECT (layer->mask));
layer->mask = NULL;
}
if (layer->fs.segs)
{
g_free (layer->fs.segs);
layer->fs.segs = NULL;
}
/* free the floating selection if it exists */
if (layer->fs.backing_store)
{
tile_manager_destroy (layer->fs.backing_store);
layer->fs.backing_store = NULL;
}
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_layer_invalidate_preview (GimpViewable *viewable)
{
GimpLayer *layer;
if (GIMP_VIEWABLE_CLASS (parent_class)->invalidate_preview)
GIMP_VIEWABLE_CLASS (parent_class)->invalidate_preview (viewable);
layer = GIMP_LAYER (viewable);
if (gimp_layer_is_floating_sel (layer))
floating_sel_invalidate (layer);
}
static void
gimp_layer_transform_color (GimpImage *gimage,
PixelRegion *layerPR,
PixelRegion *bufPR,
GimpDrawable *drawable,
GimpImageBaseType type)
{
gint i;
gint h;
guchar *s;
guchar *d;
gpointer pr;
for (pr = pixel_regions_register (2, layerPR, bufPR);
pr != NULL;
pr = pixel_regions_process (pr))
{
h = layerPR->h;
s = bufPR->data;
d = layerPR->data;
while (h--)
{
for (i = 0; i < layerPR->w; i++)
{
gimp_image_transform_color (gimage, drawable,
s + (i * bufPR->bytes),
d + (i * layerPR->bytes), type);
/* copy alpha channel */
d[(i + 1) * layerPR->bytes - 1] = s[(i + 1) * bufPR->bytes - 1];
}
s += bufPR->rowstride;
d += layerPR->rowstride;
}
}
}
/**************************/
/* Function definitions */
/**************************/
GimpLayer *
gimp_layer_new (GimpImage *gimage,
gint width,
gint height,
GimpImageType type,
const gchar *name,
gint opacity,
LayerModeEffects mode)
{
GimpLayer *layer;
if (width < 1 || height < 1)
{
g_message (_("Zero width or height layers not allowed."));
return NULL;
}
layer = g_object_new (GIMP_TYPE_LAYER, NULL);
gimp_drawable_configure (GIMP_DRAWABLE (layer),
gimage, width, height, type, name);
/* mode and opacity */
layer->mode = mode;
layer->opacity = opacity;
return layer;
}
GimpLayer *
gimp_layer_copy (GimpLayer *layer,
gboolean add_alpha)
{
gchar *layer_name;
GimpLayer *new_layer;
GimpImageType new_type;
gchar *ext;
gint number;
const gchar *name;
gint len;
PixelRegion srcPR;
PixelRegion destPR;
/* formulate the new layer name */
name = gimp_object_get_name (GIMP_OBJECT (layer));
ext = strrchr (name, '#');
len = strlen (_("copy"));
if ((strlen(name) >= len &&
strcmp (&name[strlen (name) - len], _("copy")) == 0) ||
(ext && (number = atoi (ext + 1)) > 0 &&
((gint) (log10 (number) + 1)) == strlen (ext + 1)))
/* don't have redundant "copy"s */
layer_name = g_strdup (name);
else
layer_name = g_strdup_printf (_("%s copy"), name);
/* when copying a layer, the copy ALWAYS has an alpha channel */
if (add_alpha)
{
switch (GIMP_DRAWABLE (layer)->type)
{
case RGB_GIMAGE:
new_type = RGBA_GIMAGE;
break;
case GRAY_GIMAGE:
new_type = GRAYA_GIMAGE;
break;
case INDEXED_GIMAGE:
new_type = INDEXEDA_GIMAGE;
break;
default:
new_type = GIMP_DRAWABLE (layer)->type;
break;
}
}
else
{
new_type = GIMP_DRAWABLE (layer)->type;
}
/* allocate a new layer object */
new_layer = gimp_layer_new (GIMP_DRAWABLE (layer)->gimage,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height,
new_type,
layer_name,
layer->opacity,
layer->mode);
if (! new_layer)
{
g_message ("gimp_layer_copy: could not allocate new layer");
goto cleanup;
}
GIMP_DRAWABLE (new_layer)->offset_x = GIMP_DRAWABLE (layer)->offset_x;
GIMP_DRAWABLE (new_layer)->offset_y = GIMP_DRAWABLE (layer)->offset_y;
GIMP_DRAWABLE (new_layer)->visible = GIMP_DRAWABLE (layer)->visible;
new_layer->linked = layer->linked;
new_layer->preserve_trans = layer->preserve_trans;
/* copy the contents across layers */
if (new_type == GIMP_DRAWABLE (layer)->type)
{
pixel_region_init (&srcPR, GIMP_DRAWABLE (layer)->tiles,
0, 0,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height,
FALSE);
pixel_region_init (&destPR, GIMP_DRAWABLE(new_layer)->tiles,
0, 0,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height,
TRUE);
copy_region (&srcPR, &destPR);
}
else
{
pixel_region_init (&srcPR, GIMP_DRAWABLE (layer)->tiles,
0, 0,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height,
FALSE);
pixel_region_init (&destPR, GIMP_DRAWABLE (new_layer)->tiles,
0, 0,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE(layer)->height,
TRUE);
add_alpha_region (&srcPR, &destPR);
}
/* duplicate the layer mask if necessary */
if (layer->mask)
{
new_layer->mask = gimp_layer_mask_copy (layer->mask);
gimp_layer_mask_set_layer (new_layer->mask, new_layer);
}
/* copy the parasites */
g_object_unref (G_OBJECT (GIMP_DRAWABLE (new_layer)->parasites));
GIMP_DRAWABLE (new_layer)->parasites =
gimp_parasite_list_copy (GIMP_DRAWABLE (layer)->parasites);
cleanup:
/* free up the layer_name memory */
g_free (layer_name);
return new_layer;
}
GimpLayer *
gimp_layer_new_from_tiles (GimpImage *gimage,
GimpImageType layer_type,
TileManager *tiles,
gchar *name,
gint opacity,
LayerModeEffects mode)
{
GimpLayer *new_layer;
PixelRegion layerPR;
PixelRegion bufPR;
/* Function copies buffer to a layer
* taking into consideration the possibility of transforming
* the contents to meet the requirements of the target image type
*/
/* If no image or no tile manager, return NULL */
if (!gimage || !tiles)
return NULL;
/* the layer_type needs to have alpha */
g_return_val_if_fail (GIMP_IMAGE_TYPE_HAS_ALPHA (layer_type), NULL);
/* Create the new layer */
new_layer = gimp_layer_new (gimage,
tile_manager_width (tiles),
tile_manager_height (tiles),
layer_type,
name,
opacity,
mode);
if (!new_layer)
{
g_message ("gimp_layer_new_from_tiles: could not allocate new layer");
return NULL;
}
/* Configure the pixel regions */
pixel_region_init (&layerPR, GIMP_DRAWABLE (new_layer)->tiles,
0, 0,
GIMP_DRAWABLE (new_layer)->width,
GIMP_DRAWABLE (new_layer)->height,
TRUE);
pixel_region_init (&bufPR, tiles,
0, 0,
GIMP_DRAWABLE (new_layer)->width,
GIMP_DRAWABLE (new_layer)->height,
FALSE);
if ((tile_manager_bpp (tiles) == 4 &&
GIMP_DRAWABLE (new_layer)->type == RGBA_GIMAGE) ||
(tile_manager_bpp (tiles) == 2 &&
GIMP_DRAWABLE (new_layer)->type == GRAYA_GIMAGE))
/* If we want a layer the same type as the buffer */
copy_region (&bufPR, &layerPR);
else
/* Transform the contents of the buf to the new_layer */
gimp_layer_transform_color (gimage, &layerPR, &bufPR,
GIMP_DRAWABLE (new_layer),
(tile_manager_bpp (tiles) == 4) ? RGB : GRAY);
return new_layer;
}
GimpLayerMask *
gimp_layer_add_mask (GimpLayer *layer,
GimpLayerMask *mask,
gboolean push_undo)
{
GimpImage *gimage;
LayerMaskUndo *lmu;
g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL);
g_return_val_if_fail (GIMP_IS_LAYER_MASK (mask), NULL);
gimage = gimp_drawable_gimage (GIMP_DRAWABLE (layer));
if (! gimage)
{
g_message (_("Cannot add layer mask to layer\n"
"which is not part of an image."));
return NULL;
}
if (layer->mask)
{
g_message(_("Unable to add a layer mask since\n"
"the layer already has one."));
return NULL;
}
if (gimp_drawable_is_indexed (GIMP_DRAWABLE (layer)))
{
g_message(_("Unable to add a layer mask to a\n"
"layer in an indexed image."));
return NULL;
}
if (! gimp_layer_has_alpha (layer))
{
g_message (_("Cannot add layer mask to a layer\n"
"with no alpha channel."));
return NULL;
}
if ((gimp_drawable_width (GIMP_DRAWABLE (layer)) !=
gimp_drawable_width (GIMP_DRAWABLE (mask))) ||
(gimp_drawable_height (GIMP_DRAWABLE (layer)) !=
gimp_drawable_height (GIMP_DRAWABLE (mask))))
{
g_message(_("Cannot add layer mask of different\n"
"dimensions than specified layer."));
return NULL;
}
layer->mask = mask;
gimp_layer_mask_set_layer (mask, layer);
gimp_drawable_update (GIMP_DRAWABLE (layer),
0, 0,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height);
if (push_undo)
{
/* Prepare a layer undo and push it */
lmu = g_new0 (LayerMaskUndo, 1);
lmu->layer = layer;
lmu->mask = mask;
undo_push_layer_mask (gimage, LAYER_MASK_ADD_UNDO, lmu);
}
g_signal_emit (G_OBJECT (layer), layer_signals[MASK_CHANGED], 0);
return layer->mask;
}
GimpLayerMask *
gimp_layer_create_mask (GimpLayer *layer,
AddMaskType add_mask_type)
{
PixelRegion maskPR;
PixelRegion layerPR;
GimpLayerMask *mask;
GimpImage *gimage;
GimpDrawable *selection;
gchar *mask_name;
GimpRGB black = { 0.0, 0.0, 0.0, 1.0 };
guchar white_mask = OPAQUE_OPACITY;
guchar black_mask = TRANSPARENT_OPACITY;
gimage = GIMP_DRAWABLE (layer)->gimage;
selection = GIMP_DRAWABLE(gimage->selection_mask);
mask_name = g_strdup_printf (_("%s mask"),
gimp_object_get_name (GIMP_OBJECT (layer)));
/* Create the layer mask */
mask = gimp_layer_mask_new (GIMP_DRAWABLE (layer)->gimage,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height,
mask_name, &black);
GIMP_DRAWABLE (mask)->offset_x = GIMP_DRAWABLE (layer)->offset_x;
GIMP_DRAWABLE (mask)->offset_y = GIMP_DRAWABLE (layer)->offset_y;
pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
0, 0,
GIMP_DRAWABLE (mask)->width, GIMP_DRAWABLE (mask)->height,
TRUE);
switch (add_mask_type)
{
case ADD_WHITE_MASK:
color_region (&maskPR, &white_mask);
break;
case ADD_BLACK_MASK:
color_region (&maskPR, &black_mask);
break;
case ADD_ALPHA_MASK:
/* Extract the layer's alpha channel */
if (gimp_layer_has_alpha (layer))
{
pixel_region_init (&layerPR, GIMP_DRAWABLE (layer)->tiles,
0, 0,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height,
FALSE);
extract_alpha_region (&layerPR, NULL, &maskPR);
}
break;
case ADD_SELECTION_MASK:
pixel_region_init (&layerPR, GIMP_DRAWABLE (selection)->tiles,
GIMP_DRAWABLE (layer)->offset_x,
GIMP_DRAWABLE (layer)->offset_y,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height,
FALSE);
copy_region (&layerPR, &maskPR);
break;
case ADD_INV_SELECTION_MASK:
pixel_region_init (&layerPR, GIMP_DRAWABLE (selection)->tiles,
GIMP_DRAWABLE (layer)->offset_x,
GIMP_DRAWABLE (layer)->offset_y,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height,
FALSE);
copy_region (&layerPR, &maskPR);
gimp_drawable_invert (GIMP_DRAWABLE (mask));
break;
}
g_free (mask_name);
return mask;
}
void
gimp_layer_apply_mask (GimpLayer *layer,
MaskApplyMode mode,
gboolean push_undo)
{
GimpImage *gimage;
LayerMaskUndo *lmu = NULL;
gint off_x;
gint off_y;
PixelRegion srcPR, maskPR;
gboolean view_changed = FALSE;
g_return_if_fail (layer != NULL);
g_return_if_fail (GIMP_IS_LAYER (layer));
if (! layer->mask)
return;
/* this operation can only be done to layers with an alpha channel */
if (! gimp_layer_has_alpha (layer))
return;
gimage = gimp_drawable_gimage (GIMP_DRAWABLE (layer));
if (! gimage)
return;
if (push_undo)
{
/* Start an undo group */
undo_push_group_start (gimage, LAYER_APPLY_MASK_UNDO);
/* Prepare a layer mask undo--push it below */
lmu = g_new (LayerMaskUndo, 1);
lmu->layer = layer;
lmu->mask = layer->mask;
lmu->mode = mode;
}
/* check if applying the mask changes the projection */
if ((mode == APPLY && (!layer->mask->apply_mask || layer->mask->show_mask)) ||
(mode == DISCARD && ( layer->mask->apply_mask || layer->mask->show_mask)))
{
view_changed = TRUE;
}
if (mode == APPLY)
{
if (push_undo)
{
/* Put this apply mask operation on the undo stack */
gimp_drawable_apply_image (GIMP_DRAWABLE (layer),
0, 0,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height,
NULL, FALSE);
}
/* Combine the current layer's alpha channel and the mask */
pixel_region_init (&srcPR, GIMP_DRAWABLE (layer)->tiles,
0, 0,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height,
TRUE);
pixel_region_init (&maskPR, GIMP_DRAWABLE (layer->mask)->tiles,
0, 0,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height,
FALSE);
apply_mask_to_region (&srcPR, &maskPR, OPAQUE_OPACITY);
GIMP_DRAWABLE (layer)->preview_valid = FALSE;
}
layer->mask = NULL;
if (push_undo)
{
/* Push the undo--Important to do it here, AFTER applying
* the mask, in case the undo push fails and the
* mask is deleted
*/
undo_push_layer_mask (gimage, LAYER_MASK_REMOVE_UNDO, lmu);
/* end the undo group */
undo_push_group_end (gimage);
}
/* If applying actually changed the view */
if (view_changed)
{
gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage));
gimp_drawable_offsets (GIMP_DRAWABLE (layer), &off_x, &off_y);
gimp_drawable_update (GIMP_DRAWABLE (layer),
0, 0,
gimp_drawable_width (GIMP_DRAWABLE (layer)),
gimp_drawable_height (GIMP_DRAWABLE (layer)));
}
g_signal_emit (G_OBJECT (layer), layer_signals[MASK_CHANGED], 0);
}
void
gimp_layer_translate (GimpLayer *layer,
gint off_x,
gint off_y)
{
/* the undo call goes here */
undo_push_layer_displace (GIMP_DRAWABLE (layer)->gimage, layer);
/* update the affected region */
gimp_drawable_update (GIMP_DRAWABLE (layer),
0, 0,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height);
/* invalidate the selection boundary because of a layer modification */
gimp_layer_invalidate_boundary (layer);
/* update the layer offsets */
GIMP_DRAWABLE (layer)->offset_x += off_x;
GIMP_DRAWABLE (layer)->offset_y += off_y;
/* update the affected region */
gimp_drawable_update (GIMP_DRAWABLE (layer),
0, 0,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height);
if (layer->mask)
{
GIMP_DRAWABLE (layer->mask)->offset_x += off_x;
GIMP_DRAWABLE (layer->mask)->offset_y += off_y;
/* invalidate the mask preview */
gimp_viewable_invalidate_preview (GIMP_VIEWABLE (layer->mask));
}
}
void
gimp_layer_add_alpha (GimpLayer *layer)
{
PixelRegion srcPR, destPR;
TileManager *new_tiles;
GimpImageType type;
GimpImage *gimage;
/* Don't bother if the layer already has alpha */
switch (GIMP_DRAWABLE (layer)->type)
{
case RGB_GIMAGE:
type = RGBA_GIMAGE;
break;
case GRAY_GIMAGE:
type = GRAYA_GIMAGE;
break;
case INDEXED_GIMAGE:
type = INDEXEDA_GIMAGE;
break;
case RGBA_GIMAGE:
case GRAYA_GIMAGE:
case INDEXEDA_GIMAGE:
default:
return;
break;
}
/* Configure the pixel regions */
pixel_region_init (&srcPR, GIMP_DRAWABLE (layer)->tiles,
0, 0,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height,
FALSE);
/* Allocate the new layer, configure dest region */
new_tiles = tile_manager_new (GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height,
GIMP_DRAWABLE (layer)->bytes + 1);
pixel_region_init (&destPR, new_tiles,
0, 0,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height,
TRUE);
/* Add an alpha channel */
add_alpha_region (&srcPR, &destPR);
/* Push the layer on the undo stack */
undo_push_layer_mod (GIMP_DRAWABLE (layer)->gimage, layer);
/* Configure the new layer */
GIMP_DRAWABLE (layer)->tiles = new_tiles;
GIMP_DRAWABLE (layer)->type = type;
GIMP_DRAWABLE (layer)->bytes = GIMP_DRAWABLE (layer)->bytes + 1;
GIMP_DRAWABLE (layer)->has_alpha = GIMP_IMAGE_TYPE_HAS_ALPHA (type);
GIMP_DRAWABLE (layer)->preview_valid = FALSE;
gimage = gimp_drawable_gimage (GIMP_DRAWABLE (layer));
if (gimage->layers->num_children == 1)
{
gimp_image_alpha_changed (gimage);
}
}
static void
gimp_layer_scale_lowlevel (GimpLayer *layer,
gint new_width,
gint new_height,
gint new_offset_x,
gint new_offset_y)
{
PixelRegion srcPR, destPR;
TileManager *new_tiles;
/* Update the old layer position */
gimp_drawable_update (GIMP_DRAWABLE (layer),
0, 0,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height);
/* Configure the pixel regions */
pixel_region_init (&srcPR, GIMP_DRAWABLE(layer)->tiles,
0, 0,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height,
FALSE);
/* Allocate the new layer, configure dest region */
new_tiles = tile_manager_new (new_width, new_height,
GIMP_DRAWABLE (layer)->bytes);
pixel_region_init (&destPR, new_tiles,
0, 0,
new_width, new_height,
TRUE);
/* Scale the layer -
* If the layer is of type INDEXED, then we don't use pixel-value
* resampling because that doesn't necessarily make sense for INDEXED
* images.
*/
if ((GIMP_DRAWABLE (layer)->type == INDEXED_GIMAGE) ||
(GIMP_DRAWABLE (layer)->type == INDEXEDA_GIMAGE))
scale_region_no_resample (&srcPR, &destPR);
else
scale_region (&srcPR, &destPR);
/* Push the layer on the undo stack */
undo_push_layer_mod (GIMP_DRAWABLE (layer)->gimage, layer);
/* Configure the new layer */
GIMP_DRAWABLE (layer)->offset_x = new_offset_x;
GIMP_DRAWABLE (layer)->offset_y = new_offset_y;
GIMP_DRAWABLE (layer)->tiles = new_tiles;
GIMP_DRAWABLE (layer)->width = new_width;
GIMP_DRAWABLE (layer)->height = new_height;
/* If there is a layer mask, make sure it gets scaled also */
if (layer->mask)
{
GIMP_DRAWABLE (layer->mask)->offset_x = GIMP_DRAWABLE (layer)->offset_x;
GIMP_DRAWABLE (layer->mask)->offset_y = GIMP_DRAWABLE (layer)->offset_y;
gimp_channel_scale (GIMP_CHANNEL (layer->mask), new_width, new_height);
}
/* Make sure we're not caching any old selection info */
gimp_layer_invalidate_boundary (layer);
/* Update the new layer position */
gimp_drawable_update (GIMP_DRAWABLE (layer),
0, 0,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height);
}
/**
* gimp_layer_check_scaling:
* @layer: Layer to check
* @new_width: proposed width of layer's image, in pixels
* @new_height: proposed height of layer's image, in pixels
*
* Scales layer dimensions, then snaps them to pixel centers
*
* Returns: #FALSE if any dimension reduces to zero as a result
* of this; otherwise, returns #TRUE.
**/
gboolean
gimp_layer_check_scaling (GimpLayer *layer,
gint new_width,
gint new_height)
{
GimpImage *gimage;
gdouble img_scale_w;
gdouble img_scale_h;
gint new_layer_width;
gint new_layer_height;
gimage = GIMP_DRAWABLE (layer)->gimage;
img_scale_w = (gdouble) new_width / (gdouble) gimage->width;
img_scale_h = (gdouble) new_height / (gdouble) gimage->height;
new_layer_width = ROUND (img_scale_w *
(gdouble) GIMP_DRAWABLE (layer)->width);
new_layer_height = ROUND (img_scale_h *
(gdouble) GIMP_DRAWABLE (layer)->height);
return (new_layer_width != 0 && new_layer_height != 0);
}
/**
* gimp_layer_scale_by_factors:
* @layer: Layer to be transformed by explicit width and height factors.
* @w_factor: scale factor to apply to width and horizontal offset
* @h_factor: scale factor to apply to height and vertical offset
*
* Scales layer dimensions and offsets by uniform width and
* height factors.
*
* Use gimp_layer_scale_by_factors() in circumstances when the
* same width and height scaling factors are to be uniformly
* applied to a set of layers. In this context, the layer's
* dimensions and offsets from the sides of the containing
* image all change by these predetermined factors. By fiat,
* the fixed point of the transform is the upper left hand
* corner of the image. Returns gboolean FALSE if a requested
* scale factor is zero or if a scaling zero's out a layer
* dimension; returns #TRUE otherwise.
*
* Use gimp_layer_scale() in circumstances where new layer width
* and height dimensions are predetermined instead.
*
* Side effects: Undo set created for layer. Old layer imagery
* scaled & painted to new layer tiles.
*
* Returns: #TRUE, if the scaled layer has positive dimensions
* #FALSE if the scaled layer has at least one zero dimension
**/
gboolean
gimp_layer_scale_by_factors (GimpLayer *layer,
gdouble w_factor,
gdouble h_factor)
{
gint new_width, new_height;
gint new_offset_x, new_offset_y;
if (w_factor == 0.0 || h_factor == 0.0)
{
g_message ("gimp_layer_scale_by_factors: Error. Requested width or height scale equals zero.");
return FALSE;
}
new_offset_x = ROUND (w_factor * (gdouble) GIMP_DRAWABLE (layer)->offset_x);
new_offset_y = ROUND (h_factor * (gdouble) GIMP_DRAWABLE (layer)->offset_y);
new_width = ROUND (w_factor * (gdouble) GIMP_DRAWABLE (layer)->width);
new_height = ROUND (h_factor * (gdouble) GIMP_DRAWABLE (layer)->height);
if (new_width != 0 && new_height != 0)
{
gimp_layer_scale_lowlevel (layer,
new_width, new_height,
new_offset_x, new_offset_y);
return TRUE;
}
return FALSE;
}
/**
* gimp_layer_scale:
* @layer: The layer to be transformed by width & height scale factors
* @new_width: The width that layer will acquire
* @new_height: The height that the layer will acquire
* @local_origin: sets fixed point of the scaling transform. See below.
*
* Sets layer dimensions to new_width and
* new_height. Derives vertical and horizontal scaling
* transforms from new width and height. If local_origin is
* TRUE, the fixed point of the scaling transform coincides
* with the layer's center point. Otherwise, the fixed
* point is taken to be [-GIMP_DRAWABLE(layer)->offset_x,
* -GIMP_DRAWABLE(layer)->offset_y].
*
* Since this function derives scale factors from new and
* current layer dimensions, these factors will vary from
* layer to layer because of aliasing artifacts; factor
* variations among layers can be quite large where layer
* dimensions approach pixel dimensions. Use
* gimp_layer_scale_by_factors() where constant scales are to
* be uniformly applied to a number of layers.
*
* Side effects: undo set created for layer.
* Old layer imagery scaled
* & painted to new layer tiles
**/
void
gimp_layer_scale (GimpLayer *layer,
gint new_width,
gint new_height,
gboolean local_origin)
{
gint new_offset_x, new_offset_y;
if (new_width == 0 || new_height == 0)
{
g_message ("gimp_layer_scale: Error. Requested width or height equals zero.");
return;
}
if (local_origin)
{
new_offset_x = GIMP_DRAWABLE (layer)->offset_x +
((GIMP_DRAWABLE (layer)->width - new_width) / 2.0);
new_offset_y = GIMP_DRAWABLE (layer)->offset_y +
((GIMP_DRAWABLE (layer)->height - new_height) / 2.0);
}
else
{
new_offset_x = (gint) (((gdouble) new_width *
GIMP_DRAWABLE (layer)->offset_x /
(gdouble) GIMP_DRAWABLE (layer)->width));
new_offset_y = (gint) (((gdouble) new_height *
GIMP_DRAWABLE (layer)->offset_y /
(gdouble) GIMP_DRAWABLE (layer)->height));
}
gimp_layer_scale_lowlevel (layer,
new_width, new_height,
new_offset_x, new_offset_y);
}
void
gimp_layer_resize (GimpLayer *layer,
gint new_width,
gint new_height,
gint offx,
gint offy)
{
PixelRegion srcPR, destPR;
TileManager *new_tiles;
gint w, h;
gint x1, y1, x2, y2;
if (new_width < 1 || new_height < 1)
return;
x1 = CLAMP (offx, 0, new_width);
y1 = CLAMP (offy, 0, new_height);
x2 = CLAMP ((offx + GIMP_DRAWABLE(layer)->width), 0, new_width);
y2 = CLAMP ((offy + GIMP_DRAWABLE(layer)->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 layer position */
gimp_drawable_update (GIMP_DRAWABLE( layer),
0, 0,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height);
/* Configure the pixel regions */
pixel_region_init (&srcPR, GIMP_DRAWABLE (layer)->tiles,
x1, y1,
w, h,
FALSE);
/* Allocate the new layer, configure dest region */
new_tiles = tile_manager_new (new_width, new_height,
GIMP_DRAWABLE (layer)->bytes);
pixel_region_init (&destPR, new_tiles,
0, 0,
new_width, new_height,
TRUE);
/* fill with the fill color */
if (gimp_layer_has_alpha (layer))
{
/* Set to transparent and black */
guchar bg[4] = {0, 0, 0, 0};
color_region (&destPR, bg);
}
else
{
guchar bg[3];
gimp_image_get_background (GIMP_DRAWABLE (layer)->gimage,
GIMP_DRAWABLE (layer), bg);
color_region (&destPR, bg);
}
pixel_region_init (&destPR, new_tiles,
x2, y2,
w, h,
TRUE);
/* copy from the old to the new */
if (w && h)
copy_region (&srcPR, &destPR);
/* Push the layer on the undo stack */
undo_push_layer_mod (GIMP_DRAWABLE(layer)->gimage, layer);
/* Configure the new layer */
GIMP_DRAWABLE (layer)->tiles = new_tiles;
GIMP_DRAWABLE (layer)->offset_x = x1 + GIMP_DRAWABLE (layer)->offset_x - x2;
GIMP_DRAWABLE (layer)->offset_y = y1 + GIMP_DRAWABLE (layer)->offset_y - y2;
GIMP_DRAWABLE (layer)->width = new_width;
GIMP_DRAWABLE (layer)->height = new_height;
/* If there is a layer mask, make sure it gets resized also */
if (layer->mask)
{
GIMP_DRAWABLE (layer->mask)->offset_x = GIMP_DRAWABLE (layer)->offset_x;
GIMP_DRAWABLE (layer->mask)->offset_y = GIMP_DRAWABLE (layer)->offset_y;
gimp_channel_resize (GIMP_CHANNEL (layer->mask),
new_width, new_height, offx, offy);
}
/* Make sure we're not caching any old selection info */
gimp_layer_invalidate_boundary (layer);
/* update the new layer area */
gimp_drawable_update (GIMP_DRAWABLE (layer),
0, 0,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height);
}
void
gimp_layer_resize_to_image (GimpLayer *layer)
{
GimpImage *gimage;
gint offset_x;
gint offset_y;
if (!(gimage = GIMP_DRAWABLE (layer)->gimage))
return;
undo_push_group_start (gimage, LAYER_RESIZE_UNDO);
if (gimp_layer_is_floating_sel (layer))
floating_sel_relax (layer, TRUE);
gimp_drawable_offsets (GIMP_DRAWABLE (layer), &offset_x, &offset_y);
gimp_layer_resize (layer, gimage->width, gimage->height, offset_x, offset_y);
if (gimp_layer_is_floating_sel (layer))
floating_sel_rigor (layer, TRUE);
undo_push_group_end (gimage);
}
BoundSeg *
gimp_layer_boundary (GimpLayer *layer,
gint *num_segs)
{
BoundSeg *new_segs;
/* Create the four boundary segments that encompass this
* layer's boundary.
*/
new_segs = g_new (BoundSeg, 4);
*num_segs = 4;
/* if the layer is a floating selection */
if (gimp_layer_is_floating_sel (layer))
{
if (GIMP_IS_CHANNEL (layer->fs.drawable))
{
/* if the owner drawable is a channel, just return nothing */
g_free (new_segs);
*num_segs = 0;
return NULL;
}
else
{
/* otherwise, set the layer to the owner drawable */
layer = GIMP_LAYER (layer->fs.drawable);
}
}
new_segs[0].x1 = GIMP_DRAWABLE (layer)->offset_x;
new_segs[0].y1 = GIMP_DRAWABLE (layer)->offset_y;
new_segs[0].x2 = GIMP_DRAWABLE (layer)->offset_x;
new_segs[0].y2 = GIMP_DRAWABLE (layer)->offset_y + GIMP_DRAWABLE (layer)->height;
new_segs[0].open = 1;
new_segs[1].x1 = GIMP_DRAWABLE (layer)->offset_x;
new_segs[1].y1 = GIMP_DRAWABLE (layer)->offset_y;
new_segs[1].x2 = GIMP_DRAWABLE (layer)->offset_x + GIMP_DRAWABLE (layer)->width;
new_segs[1].y2 = GIMP_DRAWABLE (layer)->offset_y;
new_segs[1].open = 1;
new_segs[2].x1 = GIMP_DRAWABLE (layer)->offset_x + GIMP_DRAWABLE (layer)->width;
new_segs[2].y1 = GIMP_DRAWABLE (layer)->offset_y;
new_segs[2].x2 = GIMP_DRAWABLE (layer)->offset_x + GIMP_DRAWABLE (layer)->width;
new_segs[2].y2 = GIMP_DRAWABLE (layer)->offset_y + GIMP_DRAWABLE (layer)->height;
new_segs[2].open = 0;
new_segs[3].x1 = GIMP_DRAWABLE (layer)->offset_x;
new_segs[3].y1 = GIMP_DRAWABLE (layer)->offset_y + GIMP_DRAWABLE (layer)->height;
new_segs[3].x2 = GIMP_DRAWABLE (layer)->offset_x + GIMP_DRAWABLE (layer)->width;
new_segs[3].y2 = GIMP_DRAWABLE (layer)->offset_y + GIMP_DRAWABLE (layer)->height;
new_segs[3].open = 0;
return new_segs;
}
void
gimp_layer_invalidate_boundary (GimpLayer *layer)
{
GimpImage *gimage;
GimpChannel *mask;
if (! (gimage = gimp_drawable_gimage (GIMP_DRAWABLE (layer))))
return;
/* Turn the current selection off */
gimp_image_selection_control (gimage, GIMP_SELECTION_OFF);
/* clear the affected region surrounding the layer */
gimp_image_selection_control (gimage, GIMP_SELECTION_LAYER_OFF);
/* get the selection mask channel */
mask = gimp_image_get_mask (gimage);
/* Only bother with the bounds if there is a selection */
if (! gimp_channel_is_empty (mask))
{
mask->bounds_known = FALSE;
mask->boundary_known = FALSE;
}
if (gimp_layer_is_floating_sel (layer))
floating_sel_invalidate (layer);
}
gint
gimp_layer_pick_correlate (GimpLayer *layer,
gint x,
gint y)
{
Tile *tile;
Tile *mask_tile;
gint val;
/* Is the point inside the layer?
* First transform the point to layer coordinates...
*/
x -= GIMP_DRAWABLE (layer)->offset_x;
y -= GIMP_DRAWABLE (layer)->offset_y;
if (x >= 0 && x < GIMP_DRAWABLE (layer)->width &&
y >= 0 && y < GIMP_DRAWABLE (layer)->height &&
gimp_drawable_get_visible (GIMP_DRAWABLE (layer)))
{
/* If the point is inside, and the layer has no
* alpha channel, success!
*/
if (! gimp_layer_has_alpha (layer))
return TRUE;
/* Otherwise, determine if the alpha value at
* the given point is non-zero
*/
tile = tile_manager_get_tile (GIMP_DRAWABLE(layer)->tiles,
x, y, TRUE, FALSE);
val = * ((guchar *) tile_data_pointer (tile,
x % TILE_WIDTH,
y % TILE_HEIGHT) +
tile_bpp (tile) - 1);
if (layer->mask)
{
guchar *ptr;
mask_tile = tile_manager_get_tile (GIMP_DRAWABLE(layer->mask)->tiles,
x, y, TRUE, FALSE);
ptr = tile_data_pointer (mask_tile, x % TILE_WIDTH, y % TILE_HEIGHT);
val = val * (*ptr) / 255;
tile_release (mask_tile, FALSE);
}
tile_release (tile, FALSE);
if (val > 63)
return TRUE;
}
return FALSE;
}
/**********************/
/* access functions */
/**********************/
GimpLayerMask *
gimp_layer_get_mask (GimpLayer *layer)
{
return layer->mask;
}
gboolean
gimp_layer_has_alpha (GimpLayer *layer)
{
g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE);
return GIMP_IMAGE_TYPE_HAS_ALPHA (GIMP_DRAWABLE (layer)->type);
}
gboolean
gimp_layer_is_floating_sel (GimpLayer *layer)
{
g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE);
return (layer->fs.drawable != NULL);
}
void
gimp_layer_set_opacity (GimpLayer *layer,
gdouble opacity)
{
gint layer_opacity;
g_return_if_fail (GIMP_IS_LAYER (layer));
layer_opacity = (gint) (opacity * 255.999);
if (layer->opacity != layer_opacity)
{
layer->opacity = layer_opacity;
g_signal_emit (G_OBJECT (layer), layer_signals[OPACITY_CHANGED], 0);
gimp_drawable_update (GIMP_DRAWABLE (layer),
0, 0,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height);
}
}
gdouble
gimp_layer_get_opacity (GimpLayer *layer)
{
g_return_val_if_fail (GIMP_IS_LAYER (layer), 1.0);
return (gdouble) layer->opacity / 255.0;
}
void
gimp_layer_set_mode (GimpLayer *layer,
LayerModeEffects mode)
{
g_return_if_fail (GIMP_IS_LAYER (layer));
if (layer->mode != mode)
{
layer->mode = mode;
g_signal_emit (G_OBJECT (layer), layer_signals[MODE_CHANGED], 0);
gimp_drawable_update (GIMP_DRAWABLE (layer),
0, 0,
GIMP_DRAWABLE (layer)->width,
GIMP_DRAWABLE (layer)->height);
}
}
LayerModeEffects
gimp_layer_get_mode (GimpLayer *layer)
{
g_return_val_if_fail (GIMP_IS_LAYER (layer), NORMAL_MODE);
return layer->mode;
}
void
gimp_layer_set_preserve_trans (GimpLayer *layer,
gboolean preserve)
{
g_return_if_fail (GIMP_IS_LAYER (layer));
if (layer->preserve_trans != preserve)
{
layer->preserve_trans = preserve ? TRUE : FALSE;
g_signal_emit (G_OBJECT (layer),
layer_signals[PRESERVE_TRANS_CHANGED], 0);
}
}
gboolean
gimp_layer_get_preserve_trans (GimpLayer *layer)
{
g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE);
return layer->preserve_trans;
}
void
gimp_layer_set_linked (GimpLayer *layer,
gboolean linked)
{
g_return_if_fail (GIMP_IS_LAYER (layer));
if (layer->linked != linked)
{
layer->linked = linked ? TRUE : FALSE;
g_signal_emit (G_OBJECT (layer), layer_signals[LINKED_CHANGED], 0);
}
}
gboolean
gimp_layer_get_linked (GimpLayer *layer)
{
g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE);
return layer->linked;
}