gimp/app/core/gimplayer.c

1572 lines
40 KiB
C
Raw Normal View History

/* TODO: make sure has_alpha gets set */
1997-11-25 06:05:25 +08:00
/* 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.
1997-11-25 06:05:25 +08:00
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
1997-11-25 06:05:25 +08:00
#include "drawable.h"
#include "errors.h"
#include "floating_sel.h"
#include "gdisplay.h"
#include "gimage.h"
#include "gimage_mask.h"
#include "layer.h"
#include "paint_funcs.h"
#include "temp_buf.h"
#include "parasitelist.h"
1997-11-25 06:05:25 +08:00
#include "undo.h"
#include "gimpsignal.h"
gimp/app/gimppreviewcache.c gimp/app/gimppreviewcache.h Wed Jun 23 23:52:54 BST 1999 Andy Thomas <alt@gimp.org> * gimp/app/gimppreviewcache.c * gimp/app/gimppreviewcache.h * gimp/app/drawable_cmds.c * gimp/app/gdisplay.c * gimp/app/gimpdrawableP.h * gimp/app/gimage_cmds.c * gimp/app/Makefile.am * gimp/app/layers_dialog.c * gimp/app/channel.c * gimp/app/lc_dialog.c * gimp/app/lc_dialog.h * gimp/app/lc_dialogP.h * gimp/app/layer.c * gimp/app/gimpdrawable.c * gimp/app/internal_procs.c * gimp/libgimp/gimp.h * gimp/libgimp/gimpimage.c * gimp/libgimp/gimpdrawable.c * gimp/libgimp/gimpmenu.c * gimp/tools/pdbgen/pdb/drawable.pdb * gimp/tools/pdbgen/pdb/gimage.pdb Added thumbnail image preview functions. Previews are visible on the L&C&P dialogs as well as in the drawables/channels/ menus generated for plugins (see the bumpmap & Mapobject plugins). PDB interface exists to simply extract a thumbnail preview of a given size. This is much quicker & more efficient than getting the image data tile-by-tile if you only need a small image since a "preview cache" has been implemented. This cache also reduces the number of times the tiles cached is scanned since smaller previews are always generated from large ones if they exists and are valid. Some possible usages (I don't intend to implement these ideas. Just suggestions). More plugins using the thumbnail preview (ie any that use multiple images). Indication of "active image" somewhere..... Actually almost anywhere a drawable/image name appears.
1999-06-24 07:01:14 +08:00
#include "gimppreviewcache.h"
1997-11-25 06:05:25 +08:00
#include "libgimp/gimpintl.h"
1999-03-07 20:56:03 +08:00
#include "libgimp/parasite.h"
#include "layer_pvt.h"
#include "tile_manager_pvt.h"
1998-08-12 01:35:34 +08:00
#include "tile.h" /* ick. */
enum {
REMOVED,
LAST_SIGNAL
};
static void gimp_layer_class_init (GimpLayerClass *klass);
static void gimp_layer_init (GimpLayer *layer);
static void gimp_layer_destroy (GtkObject *object);
static void layer_invalidate_preview (GtkObject *);
static void gimp_layer_mask_class_init (GimpLayerMaskClass *klass);
static void gimp_layer_mask_init (GimpLayerMask *layermask);
static void gimp_layer_mask_destroy (GtkObject *object);
static guint layer_signals[LAST_SIGNAL] = { 0 };
/*
static gint layer_mask_signals[LAST_SIGNAL] = { 0 };
*/
static GimpDrawableClass *layer_parent_class = NULL;
static GimpChannelClass *layer_mask_parent_class = NULL;
GtkType
gimp_layer_get_type ()
{
static GtkType layer_type = 0;
if (!layer_type)
{
GtkTypeInfo layer_info =
{
"GimpLayer",
sizeof (GimpLayer),
sizeof (GimpLayerClass),
(GtkClassInitFunc) gimp_layer_class_init,
(GtkObjectInitFunc) gimp_layer_init,
/* reserved_1 */ NULL,
/* reserved_2 */ NULL,
(GtkClassInitFunc) NULL,
};
layer_type = gtk_type_unique (gimp_drawable_get_type (), &layer_info);
}
return layer_type;
}
static void
gimp_layer_class_init (GimpLayerClass *class)
{
GtkObjectClass *object_class;
GimpDrawableClass *drawable_class;
object_class = (GtkObjectClass*) class;
drawable_class = (GimpDrawableClass*) class;
layer_parent_class = gtk_type_class (gimp_drawable_get_type ());
layer_signals[REMOVED] =
gimp_signal_new ("removed",
0, object_class->type, 0, gimp_sigtype_void);
gtk_object_class_add_signals (object_class, layer_signals, LAST_SIGNAL);
object_class->destroy = gimp_layer_destroy;
drawable_class->invalidate_preview = layer_invalidate_preview;
}
static void
gimp_layer_init (GimpLayer *layer)
{
}
GtkType
gimp_layer_mask_get_type ()
{
static GtkType layer_mask_type = 0;
if (!layer_mask_type)
{
GtkTypeInfo layer_mask_info =
{
"GimpLayerMask",
sizeof (GimpLayerMask),
sizeof (GimpLayerMaskClass),
(GtkClassInitFunc) gimp_layer_mask_class_init,
(GtkObjectInitFunc) gimp_layer_mask_init,
/* reserved_1 */ NULL,
/* reserved_2 */ NULL,
(GtkClassInitFunc) NULL,
};
layer_mask_type = gtk_type_unique (gimp_channel_get_type (), &layer_mask_info);
}
return layer_mask_type;
}
static void
gimp_layer_mask_class_init (GimpLayerMaskClass *class)
{
GtkObjectClass *object_class;
object_class = (GtkObjectClass*) class;
layer_mask_parent_class = gtk_type_class (gimp_channel_get_type ());
/*
gtk_object_class_add_signals (object_class, layer_mask_signals, LAST_SIGNAL);
*/
object_class->destroy = gimp_layer_mask_destroy;
}
static void
gimp_layer_mask_init (GimpLayerMask *layermask)
{
}
1997-11-25 06:05:25 +08:00
/* static functions */
static void transform_color (GImage *, PixelRegion *,
PixelRegion *, GimpDrawable *, int);
1997-11-25 06:05:25 +08:00
static void layer_preview_scale (int, unsigned char *, PixelRegion *,
PixelRegion *, int);
/*
* Static variables
*/
extern int global_drawable_ID;
int layer_get_count = 0;
1997-11-25 06:05:25 +08:00
/********************************/
/* Local function definitions */
static void
layer_invalidate_preview (GtkObject *object)
{
GimpLayer *layer;
g_return_if_fail (object != NULL);
g_return_if_fail (GIMP_IS_LAYER (object));
layer = GIMP_LAYER (object);
if (layer_is_floating_sel (layer))
floating_sel_invalidate (layer);
}
static void
transform_color (gimage, layerPR, bufPR, drawable, type)
1997-11-25 06:05:25 +08:00
GImage * gimage;
PixelRegion * layerPR;
PixelRegion * bufPR;
GimpDrawable *drawable;
1997-11-25 06:05:25 +08:00
int type;
{
int i, h;
unsigned char * s, * d;
void * 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++)
{
gimage_transform_color (gimage, drawable,
1997-11-25 06:05:25 +08:00
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 */
Layer *
layer_new (gimage, width, height, type, name, opacity, mode)
GimpImage* gimage;
1997-11-25 06:05:25 +08:00
int width, height;
int type;
char * name;
int opacity;
int mode;
{
Layer * layer;
if (width == 0 || height == 0) {
g_message (_("Zero width or height layers not allowed."));
1997-11-25 06:05:25 +08:00
return NULL;
}
layer = gtk_type_new (gimp_layer_get_type ());
1997-11-25 06:05:25 +08:00
gimp_drawable_configure (GIMP_DRAWABLE(layer),
gimage, width, height, type, name);
1997-11-25 06:05:25 +08:00
/* allocate the memory for this layer */
layer->linked = 0;
layer->preserve_trans = 0;
/* no layer mask is present at start */
layer->mask = NULL;
layer->apply_mask = 0;
layer->edit_mask = 0;
layer->show_mask = 0;
/* mode and opacity */
layer->mode = mode;
layer->opacity = opacity;
/* floating selection variables */
layer->fs.backing_store = NULL;
layer->fs.drawable = NULL;
1997-11-25 06:05:25 +08:00
layer->fs.initial = TRUE;
layer->fs.boundary_known = FALSE;
layer->fs.segs = NULL;
layer->fs.num_segs = 0;
return layer;
}
Layer *
layer_ref (Layer *layer)
{
gtk_object_ref (GTK_OBJECT (layer));
gtk_object_sink (GTK_OBJECT (layer));
return layer;
}
void
layer_unref (Layer *layer)
{
gtk_object_unref (GTK_OBJECT (layer));
}
1997-11-25 06:05:25 +08:00
Layer *
layer_copy (layer, add_alpha)
Layer * layer;
int add_alpha;
{
char * layer_name;
Layer * new_layer;
int new_type;
char *ext;
int number;
char *name;
int len;
1997-11-25 06:05:25 +08:00
PixelRegion srcPR, destPR;
/* formulate the new layer name */
name = layer_get_name(layer);
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 */
layer_name = g_strdup (name);
else
layer_name = g_strdup_printf (_("%s copy"), name);
1997-11-25 06:05:25 +08:00
/* when copying a layer, the copy ALWAYS has an alpha channel */
if (add_alpha)
{
switch (GIMP_DRAWABLE(layer)->type)
1997-11-25 06:05:25 +08:00
{
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;
1997-11-25 06:05:25 +08:00
break;
}
}
else
new_type = GIMP_DRAWABLE(layer)->type;
1997-11-25 06:05:25 +08:00
/* allocate a new layer object */
new_layer = layer_new (GIMP_DRAWABLE(layer)->gimage,
GIMP_DRAWABLE(layer)->width,
GIMP_DRAWABLE(layer)->height,
1997-11-25 06:05:25 +08:00
new_type, layer_name, layer->opacity, layer->mode);
if (!new_layer) {
g_message (_("layer_copy: could not allocate new layer"));
1997-11-25 06:05:25 +08:00
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;
1997-11-25 06:05:25 +08:00
new_layer->linked = layer->linked;
new_layer->preserve_trans = layer->preserve_trans;
/* copy the contents across layers */
if (new_type == GIMP_DRAWABLE(layer)->type)
1997-11-25 06:05:25 +08:00
{
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);
1997-11-25 06:05:25 +08:00
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);
1997-11-25 06:05:25 +08:00
add_alpha_region (&srcPR, &destPR);
}
/* duplicate the layer mask if necessary */
if (layer->mask)
{
new_layer->mask = layer_mask_ref (layer_mask_copy (layer->mask));
1997-11-25 06:05:25 +08:00
new_layer->apply_mask = layer->apply_mask;
new_layer->edit_mask = layer->edit_mask;
new_layer->show_mask = layer->show_mask;
}
/* copy the parasites */
GIMP_DRAWABLE(new_layer)->parasites
= parasite_list_copy(GIMP_DRAWABLE(layer)->parasites);
1997-11-25 06:05:25 +08:00
cleanup:
/* free up the layer_name memory */
g_free (layer_name);
return new_layer;
}
Layer *
layer_from_tiles (gimage_ptr, drawable, tiles, name, opacity, mode)
1997-11-25 06:05:25 +08:00
void *gimage_ptr;
GimpDrawable *drawable;
1997-11-25 06:05:25 +08:00
TileManager *tiles;
char *name;
int opacity;
int mode;
{
GImage * gimage;
Layer * new_layer;
int layer_type;
PixelRegion layerPR, 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 tile manager, return NULL */
if (!tiles)
return NULL;
gimage = (GImage *) gimage_ptr;
layer_type = drawable_type_with_alpha ( (drawable));
1997-11-25 06:05:25 +08:00
/* Create the new layer */
new_layer = layer_new (0, tiles->width, tiles->height,
1997-11-25 06:05:25 +08:00
layer_type, name, opacity, mode);
if (!new_layer) {
g_message (_("layer_from_tiles: could not allocate new layer"));
1997-11-25 06:05:25 +08:00
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);
1997-11-25 06:05:25 +08:00
if ((tiles->bpp == 4 && GIMP_DRAWABLE(new_layer)->type == RGBA_GIMAGE) ||
(tiles->bpp == 2 && GIMP_DRAWABLE(new_layer)->type == GRAYA_GIMAGE))
1997-11-25 06:05:25 +08:00
/* 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 */
transform_color (gimage, &layerPR, &bufPR, GIMP_DRAWABLE(new_layer),
(tiles->bpp == 4) ? RGB : GRAY);
1997-11-25 06:05:25 +08:00
return new_layer;
}
LayerMask *
layer_add_mask (layer, mask)
1997-11-25 06:05:25 +08:00
Layer * layer;
LayerMask * mask;
1997-11-25 06:05:25 +08:00
{
if (layer->mask)
return NULL;
layer->mask = layer_mask_ref (mask);
mask->layer = layer;
1997-11-25 06:05:25 +08:00
/* Set the application mode in the layer to "apply" */
layer->apply_mask = 1;
layer->edit_mask = 1;
layer->show_mask = 0;
drawable_update (GIMP_DRAWABLE(layer), 0, 0, GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height);
1997-11-25 06:05:25 +08:00
return layer->mask;
}
LayerMask *
1997-11-25 06:05:25 +08:00
layer_create_mask (layer, add_mask_type)
Layer * layer;
AddMaskType add_mask_type;
{
PixelRegion maskPR, layerPR;
LayerMask *mask;
1997-11-25 06:05:25 +08:00
char * mask_name;
unsigned char black[3] = {0, 0, 0};
unsigned char white_mask = OPAQUE_OPACITY;
unsigned char black_mask = TRANSPARENT_OPACITY;
1997-11-25 06:05:25 +08:00
mask_name = g_strdup_printf (_("%s mask"), GIMP_DRAWABLE(layer)->name);
1997-11-25 06:05:25 +08:00
/* Create the layer mask */
mask = layer_mask_new (GIMP_DRAWABLE(layer)->gimage, GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height,
mask_name, OPAQUE_OPACITY, black);
GIMP_DRAWABLE(mask)->offset_x = GIMP_DRAWABLE(layer)->offset_x;
GIMP_DRAWABLE(mask)->offset_y = GIMP_DRAWABLE(layer)->offset_y;
1997-11-25 06:05:25 +08:00
pixel_region_init (&maskPR, GIMP_DRAWABLE(mask)->tiles, 0, 0, GIMP_DRAWABLE(mask)->width, GIMP_DRAWABLE(mask)->height, TRUE);
1997-11-25 06:05:25 +08:00
switch (add_mask_type)
{
case ADD_WHITE_MASK:
1997-11-25 06:05:25 +08:00
color_region (&maskPR, &white_mask);
break;
case ADD_BLACK_MASK:
1997-11-25 06:05:25 +08:00
color_region (&maskPR, &black_mask);
break;
case ADD_ALPHA_MASK:
1997-11-25 06:05:25 +08:00
/* Extract the layer's alpha channel */
if (layer_has_alpha (layer))
{
pixel_region_init (&layerPR, GIMP_DRAWABLE(layer)->tiles, 0, 0, GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height, FALSE);
1997-11-25 06:05:25 +08:00
extract_alpha_region (&layerPR, NULL, &maskPR);
}
break;
}
g_free (mask_name);
return mask;
}
Layer *
layer_get_ID (ID)
int ID;
{
GimpDrawable *drawable;
drawable = drawable_get_ID (ID);
if (drawable && GIMP_IS_LAYER (drawable))
return GIMP_LAYER (drawable);
else
return NULL;
1997-11-25 06:05:25 +08:00
}
void
layer_delete (Layer * layer)
{
1998-02-14 08:44:47 +08:00
gtk_object_unref (GTK_OBJECT (layer));
}
static void
gimp_layer_destroy (GtkObject *object)
1997-11-25 06:05:25 +08:00
{
GimpLayer *layer;
g_return_if_fail (object != NULL);
g_return_if_fail (GIMP_IS_LAYER (object));
1997-11-25 06:05:25 +08:00
layer = GIMP_LAYER (object);
1997-11-25 06:05:25 +08:00
/* if a layer mask exists, free it */
if (layer->mask)
layer_mask_delete (layer->mask);
1997-11-25 06:05:25 +08:00
/* free the layer boundary if it exists */
if (layer->fs.segs)
g_free (layer->fs.segs);
/* free the floating selection if it exists */
if (layer_is_floating_sel (layer))
{
tile_manager_destroy (layer->fs.backing_store);
}
if (GTK_OBJECT_CLASS (layer_parent_class)->destroy)
(*GTK_OBJECT_CLASS (layer_parent_class)->destroy) (object);
1997-11-25 06:05:25 +08:00
}
/* The removed signal is sent out when the layer is no longer
* associcated with an image. It's needed because layers 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
layer_removed (Layer *layer, gpointer image)
{
g_return_if_fail (layer != NULL);
g_return_if_fail (GIMP_IS_LAYER (layer));
gtk_signal_emit (GTK_OBJECT (layer), layer_signals[REMOVED]);
}
1997-11-25 06:05:25 +08:00
void
layer_apply_mask (layer, mode)
Layer * layer;
int mode;
{
PixelRegion srcPR, maskPR;
if (!layer->mask)
return;
/* this operation can only be done to layers with an alpha channel */
if (! layer_has_alpha (layer))
return;
/* Need to save the mask here for undo */
if (mode == APPLY)
{
/* Put this apply mask operation on the undo stack
*/
drawable_apply_image (GIMP_DRAWABLE(layer), 0, 0, GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height, NULL, FALSE);
1997-11-25 06:05:25 +08:00
/* 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);
1997-11-25 06:05:25 +08:00
apply_mask_to_region (&srcPR, &maskPR, OPAQUE_OPACITY);
GIMP_DRAWABLE(layer)->preview_valid = FALSE;
1997-11-25 06:05:25 +08:00
layer->mask = NULL;
layer->apply_mask = 0;
layer->edit_mask = 0;
layer->show_mask = 0;
}
else if (mode == DISCARD)
{
layer->mask = NULL;
layer->apply_mask = 0;
layer->edit_mask = 0;
layer->show_mask = 0;
}
}
static void
layer_translate_lowlevel (layer, off_x, off_y, temporary)
1997-11-25 06:05:25 +08:00
Layer * layer;
int off_x, off_y;
gboolean temporary;
1997-11-25 06:05:25 +08:00
{
if (!temporary)
{
/* the undo call goes here */
/*g_warning ("setting undo for layer translation");*/
undo_push_layer_displace (GIMP_DRAWABLE(layer)->gimage, layer);
}
1997-11-25 06:05:25 +08:00
/* update the affected region */
drawable_update (GIMP_DRAWABLE(layer), 0, 0, GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height);
1997-11-25 06:05:25 +08:00
if (!temporary)
{
/* invalidate the selection boundary because of a layer modification */
layer_invalidate_boundary (layer);
}
1997-11-25 06:05:25 +08:00
/* update the layer offsets */
GIMP_DRAWABLE(layer)->offset_x += off_x;
GIMP_DRAWABLE(layer)->offset_y += off_y;
1997-11-25 06:05:25 +08:00
/* update the affected region */
drawable_update (GIMP_DRAWABLE(layer), 0, 0, GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height);
1997-11-25 06:05:25 +08:00
if (layer->mask)
{
GIMP_DRAWABLE(layer->mask)->offset_x += off_x;
GIMP_DRAWABLE(layer->mask)->offset_y += off_y;
if (!temporary)
{
/* invalidate the mask preview */
drawable_invalidate_preview (GIMP_DRAWABLE(layer->mask));
}
}
1997-11-25 06:05:25 +08:00
}
void
layer_temporarily_translate (layer, off_x, off_y)
Layer * layer;
int off_x, off_y;
{
layer_translate_lowlevel (layer, off_x, off_y, TRUE);
}
void
layer_translate (layer, off_x, off_y)
Layer * layer;
int off_x, off_y;
{
layer_translate_lowlevel (layer, off_x, off_y, FALSE);
}
1997-11-25 06:05:25 +08:00
void
layer_add_alpha (layer)
Layer *layer;
{
PixelRegion srcPR, destPR;
TileManager *new_tiles;
int type;
/* Don't bother if the layer already has alpha */
switch (GIMP_DRAWABLE(layer)->type)
1997-11-25 06:05:25 +08:00
{
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);
1997-11-25 06:05:25 +08:00
/* 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);
1997-11-25 06:05:25 +08:00
/* 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);
1997-11-25 06:05:25 +08:00
/* 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 = TYPE_HAS_ALPHA (type);
1997-11-25 06:05:25 +08:00
gtk_signal_emit_by_name(GTK_OBJECT(gimp_drawable_gimage(GIMP_DRAWABLE(layer))),
"restructure");
1997-11-25 06:05:25 +08:00
}
void
layer_scale (layer, new_width, new_height, local_origin)
Layer *layer;
int new_width, new_height;
int local_origin;
{
PixelRegion srcPR, destPR;
TileManager *new_tiles;
if (new_width == 0 || new_height == 0)
return;
/* Update the old layer position */
drawable_update (GIMP_DRAWABLE(layer), 0, 0, GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height);
1997-11-25 06:05:25 +08:00
/* Configure the pixel regions */
pixel_region_init (&srcPR, GIMP_DRAWABLE(layer)->tiles, 0, 0, GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height, FALSE);
1997-11-25 06:05:25 +08:00
/* Allocate the new layer, configure dest region */
new_tiles = tile_manager_new (new_width, new_height, GIMP_DRAWABLE(layer)->bytes);
1997-11-25 06:05:25 +08:00
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))
1997-11-25 06:05:25 +08:00
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);
1997-11-25 06:05:25 +08:00
/* Configure the new layer */
if (local_origin)
{
int cx, cy;
cx = GIMP_DRAWABLE(layer)->offset_x + GIMP_DRAWABLE(layer)->width / 2;
cy = GIMP_DRAWABLE(layer)->offset_y + GIMP_DRAWABLE(layer)->height / 2;
1997-11-25 06:05:25 +08:00
GIMP_DRAWABLE(layer)->offset_x = cx - (new_width / 2);
GIMP_DRAWABLE(layer)->offset_y = cy - (new_height / 2);
1997-11-25 06:05:25 +08:00
}
else
{
double xrat, yrat;
xrat = (double) new_width / (double) GIMP_DRAWABLE(layer)->width;
yrat = (double) new_height / (double) GIMP_DRAWABLE(layer)->height;
1997-11-25 06:05:25 +08:00
GIMP_DRAWABLE(layer)->offset_x = (int) (xrat * GIMP_DRAWABLE(layer)->offset_x);
GIMP_DRAWABLE(layer)->offset_y = (int) (yrat * GIMP_DRAWABLE(layer)->offset_y);
1997-11-25 06:05:25 +08:00
}
GIMP_DRAWABLE(layer)->tiles = new_tiles;
GIMP_DRAWABLE(layer)->width = new_width;
GIMP_DRAWABLE(layer)->height = new_height;
1997-11-25 06:05:25 +08:00
/* 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;
channel_scale (GIMP_CHANNEL(layer->mask), new_width, new_height);
}
1997-11-25 06:05:25 +08:00
/* Update the new layer position */
drawable_update (GIMP_DRAWABLE(layer), 0, 0, GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height);
1997-11-25 06:05:25 +08:00
}
void
layer_resize (layer, new_width, new_height, offx, offy)
Layer *layer;
int new_width, new_height;
int offx, offy;
{
PixelRegion srcPR, destPR;
TileManager *new_tiles;
int w, h;
int 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(layer)->width), 0, new_width);
y2 = CLAMP ((offy + GIMP_DRAWABLE(layer)->height), 0, new_height);
1997-11-25 06:05:25 +08:00
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 */
drawable_update (GIMP_DRAWABLE(layer), 0, 0, GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height);
1997-11-25 06:05:25 +08:00
/* Configure the pixel regions */
pixel_region_init (&srcPR, GIMP_DRAWABLE(layer)->tiles, x1, y1, w, h, FALSE);
1997-11-25 06:05:25 +08:00
/* Allocate the new layer, configure dest region */
new_tiles = tile_manager_new (new_width, new_height, GIMP_DRAWABLE(layer)->bytes);
1997-11-25 06:05:25 +08:00
pixel_region_init (&destPR, new_tiles, 0, 0, new_width, new_height, TRUE);
/* fill with the fill color */
if (layer_has_alpha (layer))
{
/* Set to transparent and black */
unsigned char bg[4] = {0, 0, 0, 0};
color_region (&destPR, bg);
}
else
{
unsigned char bg[3];
gimage_get_background (GIMP_DRAWABLE(layer)->gimage, GIMP_DRAWABLE(layer), bg);
1997-11-25 06:05:25 +08:00
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);
1997-11-25 06:05:25 +08:00
/* 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;
channel_resize (GIMP_CHANNEL(layer->mask), new_width, new_height, offx, offy);
}
1997-11-25 06:05:25 +08:00
/* update the new layer area */
drawable_update (GIMP_DRAWABLE(layer), 0, 0, GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height);
1997-11-25 06:05:25 +08:00
}
BoundSeg *
layer_boundary (layer, num_segs)
Layer *layer;
int *num_segs;
{
BoundSeg *new_segs;
/* Create the four boundary segments that encompass this
* layer's boundary.
*/
new_segs = (BoundSeg *) g_malloc (sizeof (BoundSeg) * 4);
*num_segs = 4;
/* if the layer is a floating selection */
if (layer_is_floating_sel (layer))
{
/* if the owner drawable is a channel, just return nothing */
if (drawable_channel (layer->fs.drawable))
{
*num_segs = 0;
return NULL;
}
/* otherwise, set the layer to the owner drawable */
else
layer = GIMP_LAYER(layer->fs.drawable);
1997-11-25 06:05:25 +08:00
}
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;
1997-11-25 06:05:25 +08:00
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;
1997-11-25 06:05:25 +08:00
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;
1997-11-25 06:05:25 +08:00
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;
1997-11-25 06:05:25 +08:00
new_segs[3].open = 0;
return new_segs;
}
void
layer_invalidate_boundary (layer)
Layer *layer;
{
GImage *gimage;
Channel *mask;
/* first get the selection mask channel */
if (! (gimage = drawable_gimage(GIMP_DRAWABLE(layer))))
1997-11-25 06:05:25 +08:00
return;
/* Turn the current selection off */
gdisplays_selection_visibility (gimage, SelectionOff);
1997-11-25 06:05:25 +08:00
/* clear the affected region surrounding the layer */
gdisplays_selection_visibility (gimage, SelectionLayerOff);
1997-11-25 06:05:25 +08:00
mask = gimage_get_mask (gimage);
/* Only bother with the bounds if there is a selection */
if (! channel_is_empty (mask))
{
mask->bounds_known = FALSE;
mask->boundary_known = FALSE;
}
if (layer_is_floating_sel(layer))
floating_sel_invalidate(layer);
1997-11-25 06:05:25 +08:00
}
int
layer_pick_correlate (layer, x, y)
Layer *layer;
int x, y;
{
Tile *tile;
Tile *mask_tile;
int 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(layer)->visible)
1997-11-25 06:05:25 +08:00
{
/* If the point is inside, and the layer has no
* alpha channel, success!
*/
if (! 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);
1997-11-25 06:05:25 +08:00
val = * ((unsigned char*) tile_data_pointer (tile,
1998-08-12 01:35:34 +08:00
x % TILE_WIDTH,
y % TILE_HEIGHT) +
tile_bpp (tile) - 1);
1997-11-25 06:05:25 +08:00
if (layer->mask)
{
1998-08-12 01:35:34 +08:00
unsigned char *ptr;
mask_tile = tile_manager_get_tile (GIMP_DRAWABLE(layer->mask)->tiles, x, y, TRUE, FALSE);
1998-08-12 01:35:34 +08:00
ptr = tile_data_pointer (mask_tile, x % TILE_WIDTH, y % TILE_HEIGHT);
val = val * (*ptr) / 255;
tile_release (mask_tile, FALSE);
1997-11-25 06:05:25 +08:00
}
tile_release (tile, FALSE);
1997-11-25 06:05:25 +08:00
if (val > 63)
return TRUE;
}
return FALSE;
}
/********************/
/* access functions */
void
layer_set_name (Layer *layer, char *name)
{
gimp_drawable_set_name(GIMP_DRAWABLE(layer), name);
}
char *
layer_get_name (Layer *layer)
{
return gimp_drawable_get_name(GIMP_DRAWABLE(layer));
}
1997-11-25 06:05:25 +08:00
unsigned char *
layer_data (layer)
Layer * layer;
{
return NULL;
}
LayerMask *
1997-11-25 06:05:25 +08:00
layer_mask (layer)
Layer * layer;
{
return layer->mask;
}
int
layer_has_alpha (layer)
Layer * layer;
{
if (GIMP_DRAWABLE(layer)->type == RGBA_GIMAGE ||
GIMP_DRAWABLE(layer)->type == GRAYA_GIMAGE ||
GIMP_DRAWABLE(layer)->type == INDEXEDA_GIMAGE)
1997-11-25 06:05:25 +08:00
return 1;
else
return 0;
}
int
layer_is_floating_sel (layer)
Layer *layer;
{
if (layer->fs.drawable != NULL)
1997-11-25 06:05:25 +08:00
return 1;
else
return 0;
}
int
layer_linked (Layer *layer)
{
return layer->linked;
}
1997-11-25 06:05:25 +08:00
TempBuf *
layer_preview (layer, w, h)
Layer *layer;
int w, h;
{
GImage *gimage;
TempBuf *preview_buf;
PixelRegion srcPR, destPR;
int type;
int bytes;
int subsample;
gimp/app/gimppreviewcache.c gimp/app/gimppreviewcache.h Wed Jun 23 23:52:54 BST 1999 Andy Thomas <alt@gimp.org> * gimp/app/gimppreviewcache.c * gimp/app/gimppreviewcache.h * gimp/app/drawable_cmds.c * gimp/app/gdisplay.c * gimp/app/gimpdrawableP.h * gimp/app/gimage_cmds.c * gimp/app/Makefile.am * gimp/app/layers_dialog.c * gimp/app/channel.c * gimp/app/lc_dialog.c * gimp/app/lc_dialog.h * gimp/app/lc_dialogP.h * gimp/app/layer.c * gimp/app/gimpdrawable.c * gimp/app/internal_procs.c * gimp/libgimp/gimp.h * gimp/libgimp/gimpimage.c * gimp/libgimp/gimpdrawable.c * gimp/libgimp/gimpmenu.c * gimp/tools/pdbgen/pdb/drawable.pdb * gimp/tools/pdbgen/pdb/gimage.pdb Added thumbnail image preview functions. Previews are visible on the L&C&P dialogs as well as in the drawables/channels/ menus generated for plugins (see the bumpmap & Mapobject plugins). PDB interface exists to simply extract a thumbnail preview of a given size. This is much quicker & more efficient than getting the image data tile-by-tile if you only need a small image since a "preview cache" has been implemented. This cache also reduces the number of times the tiles cached is scanned since smaller previews are always generated from large ones if they exists and are valid. Some possible usages (I don't intend to implement these ideas. Just suggestions). More plugins using the thumbnail preview (ie any that use multiple images). Indication of "active image" somewhere..... Actually almost anywhere a drawable/image name appears.
1999-06-24 07:01:14 +08:00
TempBuf *ret_buf;
1997-11-25 06:05:25 +08:00
type = 0;
bytes = 0;
/* The easy way */
if (GIMP_DRAWABLE(layer)->preview_valid &&
gimp/app/gimppreviewcache.c gimp/app/gimppreviewcache.h Wed Jun 23 23:52:54 BST 1999 Andy Thomas <alt@gimp.org> * gimp/app/gimppreviewcache.c * gimp/app/gimppreviewcache.h * gimp/app/drawable_cmds.c * gimp/app/gdisplay.c * gimp/app/gimpdrawableP.h * gimp/app/gimage_cmds.c * gimp/app/Makefile.am * gimp/app/layers_dialog.c * gimp/app/channel.c * gimp/app/lc_dialog.c * gimp/app/lc_dialog.h * gimp/app/lc_dialogP.h * gimp/app/layer.c * gimp/app/gimpdrawable.c * gimp/app/internal_procs.c * gimp/libgimp/gimp.h * gimp/libgimp/gimpimage.c * gimp/libgimp/gimpdrawable.c * gimp/libgimp/gimpmenu.c * gimp/tools/pdbgen/pdb/drawable.pdb * gimp/tools/pdbgen/pdb/gimage.pdb Added thumbnail image preview functions. Previews are visible on the L&C&P dialogs as well as in the drawables/channels/ menus generated for plugins (see the bumpmap & Mapobject plugins). PDB interface exists to simply extract a thumbnail preview of a given size. This is much quicker & more efficient than getting the image data tile-by-tile if you only need a small image since a "preview cache" has been implemented. This cache also reduces the number of times the tiles cached is scanned since smaller previews are always generated from large ones if they exists and are valid. Some possible usages (I don't intend to implement these ideas. Just suggestions). More plugins using the thumbnail preview (ie any that use multiple images). Indication of "active image" somewhere..... Actually almost anywhere a drawable/image name appears.
1999-06-24 07:01:14 +08:00
(ret_buf = gimp_preview_cache_get(&(GIMP_DRAWABLE(layer)->preview_cache),
w,h)))
return ret_buf;
1997-11-25 06:05:25 +08:00
/* The hard way */
else
{
gimage = GIMP_DRAWABLE(layer)->gimage;
switch (GIMP_DRAWABLE(layer)->type)
1997-11-25 06:05:25 +08:00
{
case RGB_GIMAGE: case RGBA_GIMAGE:
type = 0;
bytes = GIMP_DRAWABLE(layer)->bytes;
1997-11-25 06:05:25 +08:00
break;
case GRAY_GIMAGE: case GRAYA_GIMAGE:
type = 1;
bytes = GIMP_DRAWABLE(layer)->bytes;
1997-11-25 06:05:25 +08:00
break;
case INDEXED_GIMAGE: case INDEXEDA_GIMAGE:
type = 2;
bytes = (GIMP_DRAWABLE(layer)->type == INDEXED_GIMAGE) ? 3 : 4;
1997-11-25 06:05:25 +08:00
break;
}
/* calculate 'acceptable' subsample */
subsample = 1;
/* handle some truncation errors */
if (w < 1) w = 1;
if (h < 1) h = 1;
while ((w * (subsample + 1) * 2 < GIMP_DRAWABLE(layer)->width) &&
(h * (subsample + 1) * 2 < GIMP_DRAWABLE(layer)->height))
1997-11-25 06:05:25 +08:00
subsample = subsample + 1;
pixel_region_init (&srcPR, GIMP_DRAWABLE(layer)->tiles, 0, 0, GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height, FALSE);
1997-11-25 06:05:25 +08:00
preview_buf = temp_buf_new (w, h, bytes, 0, 0, NULL);
destPR.bytes = preview_buf->bytes;
destPR.w = w;
destPR.h = h;
destPR.rowstride = w * destPR.bytes;
destPR.data = temp_buf_data (preview_buf);
layer_preview_scale (type, gimage->cmap, &srcPR, &destPR, subsample);
gimp/app/gimppreviewcache.c gimp/app/gimppreviewcache.h Wed Jun 23 23:52:54 BST 1999 Andy Thomas <alt@gimp.org> * gimp/app/gimppreviewcache.c * gimp/app/gimppreviewcache.h * gimp/app/drawable_cmds.c * gimp/app/gdisplay.c * gimp/app/gimpdrawableP.h * gimp/app/gimage_cmds.c * gimp/app/Makefile.am * gimp/app/layers_dialog.c * gimp/app/channel.c * gimp/app/lc_dialog.c * gimp/app/lc_dialog.h * gimp/app/lc_dialogP.h * gimp/app/layer.c * gimp/app/gimpdrawable.c * gimp/app/internal_procs.c * gimp/libgimp/gimp.h * gimp/libgimp/gimpimage.c * gimp/libgimp/gimpdrawable.c * gimp/libgimp/gimpmenu.c * gimp/tools/pdbgen/pdb/drawable.pdb * gimp/tools/pdbgen/pdb/gimage.pdb Added thumbnail image preview functions. Previews are visible on the L&C&P dialogs as well as in the drawables/channels/ menus generated for plugins (see the bumpmap & Mapobject plugins). PDB interface exists to simply extract a thumbnail preview of a given size. This is much quicker & more efficient than getting the image data tile-by-tile if you only need a small image since a "preview cache" has been implemented. This cache also reduces the number of times the tiles cached is scanned since smaller previews are always generated from large ones if they exists and are valid. Some possible usages (I don't intend to implement these ideas. Just suggestions). More plugins using the thumbnail preview (ie any that use multiple images). Indication of "active image" somewhere..... Actually almost anywhere a drawable/image name appears.
1999-06-24 07:01:14 +08:00
if (!GIMP_DRAWABLE(layer)->preview_valid)
gimp_preview_cache_invalidate(&(GIMP_DRAWABLE(layer)->preview_cache));
1997-11-25 06:05:25 +08:00
GIMP_DRAWABLE(layer)->preview_valid = TRUE;
1997-11-25 06:05:25 +08:00
gimp/app/gimppreviewcache.c gimp/app/gimppreviewcache.h Wed Jun 23 23:52:54 BST 1999 Andy Thomas <alt@gimp.org> * gimp/app/gimppreviewcache.c * gimp/app/gimppreviewcache.h * gimp/app/drawable_cmds.c * gimp/app/gdisplay.c * gimp/app/gimpdrawableP.h * gimp/app/gimage_cmds.c * gimp/app/Makefile.am * gimp/app/layers_dialog.c * gimp/app/channel.c * gimp/app/lc_dialog.c * gimp/app/lc_dialog.h * gimp/app/lc_dialogP.h * gimp/app/layer.c * gimp/app/gimpdrawable.c * gimp/app/internal_procs.c * gimp/libgimp/gimp.h * gimp/libgimp/gimpimage.c * gimp/libgimp/gimpdrawable.c * gimp/libgimp/gimpmenu.c * gimp/tools/pdbgen/pdb/drawable.pdb * gimp/tools/pdbgen/pdb/gimage.pdb Added thumbnail image preview functions. Previews are visible on the L&C&P dialogs as well as in the drawables/channels/ menus generated for plugins (see the bumpmap & Mapobject plugins). PDB interface exists to simply extract a thumbnail preview of a given size. This is much quicker & more efficient than getting the image data tile-by-tile if you only need a small image since a "preview cache" has been implemented. This cache also reduces the number of times the tiles cached is scanned since smaller previews are always generated from large ones if they exists and are valid. Some possible usages (I don't intend to implement these ideas. Just suggestions). More plugins using the thumbnail preview (ie any that use multiple images). Indication of "active image" somewhere..... Actually almost anywhere a drawable/image name appears.
1999-06-24 07:01:14 +08:00
gimp_preview_cache_add(&(GIMP_DRAWABLE(layer)->preview_cache),preview_buf);
return preview_buf;
1997-11-25 06:05:25 +08:00
}
}
TempBuf *
layer_mask_preview (layer, w, h)
Layer *layer;
int w, h;
{
TempBuf *preview_buf;
LayerMask *mask;
1997-11-25 06:05:25 +08:00
PixelRegion srcPR, destPR;
int subsample;
gimp/app/gimppreviewcache.c gimp/app/gimppreviewcache.h Wed Jun 23 23:52:54 BST 1999 Andy Thomas <alt@gimp.org> * gimp/app/gimppreviewcache.c * gimp/app/gimppreviewcache.h * gimp/app/drawable_cmds.c * gimp/app/gdisplay.c * gimp/app/gimpdrawableP.h * gimp/app/gimage_cmds.c * gimp/app/Makefile.am * gimp/app/layers_dialog.c * gimp/app/channel.c * gimp/app/lc_dialog.c * gimp/app/lc_dialog.h * gimp/app/lc_dialogP.h * gimp/app/layer.c * gimp/app/gimpdrawable.c * gimp/app/internal_procs.c * gimp/libgimp/gimp.h * gimp/libgimp/gimpimage.c * gimp/libgimp/gimpdrawable.c * gimp/libgimp/gimpmenu.c * gimp/tools/pdbgen/pdb/drawable.pdb * gimp/tools/pdbgen/pdb/gimage.pdb Added thumbnail image preview functions. Previews are visible on the L&C&P dialogs as well as in the drawables/channels/ menus generated for plugins (see the bumpmap & Mapobject plugins). PDB interface exists to simply extract a thumbnail preview of a given size. This is much quicker & more efficient than getting the image data tile-by-tile if you only need a small image since a "preview cache" has been implemented. This cache also reduces the number of times the tiles cached is scanned since smaller previews are always generated from large ones if they exists and are valid. Some possible usages (I don't intend to implement these ideas. Just suggestions). More plugins using the thumbnail preview (ie any that use multiple images). Indication of "active image" somewhere..... Actually almost anywhere a drawable/image name appears.
1999-06-24 07:01:14 +08:00
TempBuf *ret_buf;
1997-11-25 06:05:25 +08:00
mask = layer->mask;
if (!mask)
return NULL;
/* The easy way */
if (GIMP_DRAWABLE(mask)->preview_valid &&
gimp/app/gimppreviewcache.c gimp/app/gimppreviewcache.h Wed Jun 23 23:52:54 BST 1999 Andy Thomas <alt@gimp.org> * gimp/app/gimppreviewcache.c * gimp/app/gimppreviewcache.h * gimp/app/drawable_cmds.c * gimp/app/gdisplay.c * gimp/app/gimpdrawableP.h * gimp/app/gimage_cmds.c * gimp/app/Makefile.am * gimp/app/layers_dialog.c * gimp/app/channel.c * gimp/app/lc_dialog.c * gimp/app/lc_dialog.h * gimp/app/lc_dialogP.h * gimp/app/layer.c * gimp/app/gimpdrawable.c * gimp/app/internal_procs.c * gimp/libgimp/gimp.h * gimp/libgimp/gimpimage.c * gimp/libgimp/gimpdrawable.c * gimp/libgimp/gimpmenu.c * gimp/tools/pdbgen/pdb/drawable.pdb * gimp/tools/pdbgen/pdb/gimage.pdb Added thumbnail image preview functions. Previews are visible on the L&C&P dialogs as well as in the drawables/channels/ menus generated for plugins (see the bumpmap & Mapobject plugins). PDB interface exists to simply extract a thumbnail preview of a given size. This is much quicker & more efficient than getting the image data tile-by-tile if you only need a small image since a "preview cache" has been implemented. This cache also reduces the number of times the tiles cached is scanned since smaller previews are always generated from large ones if they exists and are valid. Some possible usages (I don't intend to implement these ideas. Just suggestions). More plugins using the thumbnail preview (ie any that use multiple images). Indication of "active image" somewhere..... Actually almost anywhere a drawable/image name appears.
1999-06-24 07:01:14 +08:00
(ret_buf = gimp_preview_cache_get(&(GIMP_DRAWABLE(mask)->preview_cache),
w,h)))
return ret_buf;
1997-11-25 06:05:25 +08:00
/* The hard way */
else
{
/* calculate 'acceptable' subsample */
subsample = 1;
if (w < 1) w = 1;
if (h < 1) h = 1;
while ((w * (subsample + 1) * 2 < GIMP_DRAWABLE(layer)->width) &&
(h * (subsample + 1) * 2 < GIMP_DRAWABLE(layer)->height))
1997-11-25 06:05:25 +08:00
subsample = subsample + 1;
pixel_region_init (&srcPR, GIMP_DRAWABLE(mask)->tiles, 0, 0, GIMP_DRAWABLE(mask)->width, GIMP_DRAWABLE(mask)->height, FALSE);
1997-11-25 06:05:25 +08:00
preview_buf = temp_buf_new (w, h, 1, 0, 0, NULL);
destPR.bytes = preview_buf->bytes;
destPR.w = w;
destPR.h = h;
destPR.rowstride = w * destPR.bytes;
destPR.data = temp_buf_data (preview_buf);
layer_preview_scale (1 /* GRAY */, NULL, &srcPR, &destPR, subsample);
gimp/app/gimppreviewcache.c gimp/app/gimppreviewcache.h Wed Jun 23 23:52:54 BST 1999 Andy Thomas <alt@gimp.org> * gimp/app/gimppreviewcache.c * gimp/app/gimppreviewcache.h * gimp/app/drawable_cmds.c * gimp/app/gdisplay.c * gimp/app/gimpdrawableP.h * gimp/app/gimage_cmds.c * gimp/app/Makefile.am * gimp/app/layers_dialog.c * gimp/app/channel.c * gimp/app/lc_dialog.c * gimp/app/lc_dialog.h * gimp/app/lc_dialogP.h * gimp/app/layer.c * gimp/app/gimpdrawable.c * gimp/app/internal_procs.c * gimp/libgimp/gimp.h * gimp/libgimp/gimpimage.c * gimp/libgimp/gimpdrawable.c * gimp/libgimp/gimpmenu.c * gimp/tools/pdbgen/pdb/drawable.pdb * gimp/tools/pdbgen/pdb/gimage.pdb Added thumbnail image preview functions. Previews are visible on the L&C&P dialogs as well as in the drawables/channels/ menus generated for plugins (see the bumpmap & Mapobject plugins). PDB interface exists to simply extract a thumbnail preview of a given size. This is much quicker & more efficient than getting the image data tile-by-tile if you only need a small image since a "preview cache" has been implemented. This cache also reduces the number of times the tiles cached is scanned since smaller previews are always generated from large ones if they exists and are valid. Some possible usages (I don't intend to implement these ideas. Just suggestions). More plugins using the thumbnail preview (ie any that use multiple images). Indication of "active image" somewhere..... Actually almost anywhere a drawable/image name appears.
1999-06-24 07:01:14 +08:00
if(!GIMP_DRAWABLE(mask)->preview_valid)
gimp_preview_cache_invalidate(&(GIMP_DRAWABLE(mask)->preview_cache));
1997-11-25 06:05:25 +08:00
GIMP_DRAWABLE(mask)->preview_valid = TRUE;
gimp/app/gimppreviewcache.c gimp/app/gimppreviewcache.h Wed Jun 23 23:52:54 BST 1999 Andy Thomas <alt@gimp.org> * gimp/app/gimppreviewcache.c * gimp/app/gimppreviewcache.h * gimp/app/drawable_cmds.c * gimp/app/gdisplay.c * gimp/app/gimpdrawableP.h * gimp/app/gimage_cmds.c * gimp/app/Makefile.am * gimp/app/layers_dialog.c * gimp/app/channel.c * gimp/app/lc_dialog.c * gimp/app/lc_dialog.h * gimp/app/lc_dialogP.h * gimp/app/layer.c * gimp/app/gimpdrawable.c * gimp/app/internal_procs.c * gimp/libgimp/gimp.h * gimp/libgimp/gimpimage.c * gimp/libgimp/gimpdrawable.c * gimp/libgimp/gimpmenu.c * gimp/tools/pdbgen/pdb/drawable.pdb * gimp/tools/pdbgen/pdb/gimage.pdb Added thumbnail image preview functions. Previews are visible on the L&C&P dialogs as well as in the drawables/channels/ menus generated for plugins (see the bumpmap & Mapobject plugins). PDB interface exists to simply extract a thumbnail preview of a given size. This is much quicker & more efficient than getting the image data tile-by-tile if you only need a small image since a "preview cache" has been implemented. This cache also reduces the number of times the tiles cached is scanned since smaller previews are always generated from large ones if they exists and are valid. Some possible usages (I don't intend to implement these ideas. Just suggestions). More plugins using the thumbnail preview (ie any that use multiple images). Indication of "active image" somewhere..... Actually almost anywhere a drawable/image name appears.
1999-06-24 07:01:14 +08:00
gimp_preview_cache_add(&(GIMP_DRAWABLE(mask)->preview_cache),preview_buf);
1997-11-25 06:05:25 +08:00
gimp/app/gimppreviewcache.c gimp/app/gimppreviewcache.h Wed Jun 23 23:52:54 BST 1999 Andy Thomas <alt@gimp.org> * gimp/app/gimppreviewcache.c * gimp/app/gimppreviewcache.h * gimp/app/drawable_cmds.c * gimp/app/gdisplay.c * gimp/app/gimpdrawableP.h * gimp/app/gimage_cmds.c * gimp/app/Makefile.am * gimp/app/layers_dialog.c * gimp/app/channel.c * gimp/app/lc_dialog.c * gimp/app/lc_dialog.h * gimp/app/lc_dialogP.h * gimp/app/layer.c * gimp/app/gimpdrawable.c * gimp/app/internal_procs.c * gimp/libgimp/gimp.h * gimp/libgimp/gimpimage.c * gimp/libgimp/gimpdrawable.c * gimp/libgimp/gimpmenu.c * gimp/tools/pdbgen/pdb/drawable.pdb * gimp/tools/pdbgen/pdb/gimage.pdb Added thumbnail image preview functions. Previews are visible on the L&C&P dialogs as well as in the drawables/channels/ menus generated for plugins (see the bumpmap & Mapobject plugins). PDB interface exists to simply extract a thumbnail preview of a given size. This is much quicker & more efficient than getting the image data tile-by-tile if you only need a small image since a "preview cache" has been implemented. This cache also reduces the number of times the tiles cached is scanned since smaller previews are always generated from large ones if they exists and are valid. Some possible usages (I don't intend to implement these ideas. Just suggestions). More plugins using the thumbnail preview (ie any that use multiple images). Indication of "active image" somewhere..... Actually almost anywhere a drawable/image name appears.
1999-06-24 07:01:14 +08:00
return preview_buf;
1997-11-25 06:05:25 +08:00
}
}
Tattoo
Modified Files: ChangeLog app/Makefile.am app/channel.c app/channel.h Modified Files: ChangeLog app/Makefile.am app/channel.c app/channel.h app/channel_cmds.c app/channel_cmds.h app/drawable_cmds.c app/gimage_cmds.c app/gimpdrawable.c app/gimpdrawable.h app/gimpdrawableP.h app/gimpimage.c app/gimpimage.h app/gimpimageP.h app/internal_procs.c app/layer.c app/layer.h app/layer_cmds.c app/layer_cmds.h app/parasite_cmds.c app/perspective_tool.c app/plug_in.c app/procedural_db.c app/rotate_tool.c app/scale_tool.c app/shear_tool.c app/transform_core.c app/transform_core.h docs/parasites.txt libgimp/Makefile.am libgimp/gimp.c libgimp/gimp.h libgimp/gimpdrawable.c libgimp/gimpimage.c libgimp/gimpprotocol.c libgimp/gimpprotocol.h plug-ins/gif/gif.c plug-ins/script-fu/script-fu.c plug-ins/tiff/tiff.c Added Files: libgimp/gimpmatrix.c libgimp/gimpmatrix.h libgimp/parasite.c libgimp/parasite.h libgimp/parasiteF.h libgimp/parasiteP.h Removed Files: app/parasite.c app/parasite.h app/parasiteF.h app/parasiteP.h libgimp/gimpparasite.c libgimp/gimpparasite.h Tue Oct 13 19:24:03 1998 Jay Cox (jaycox@earthlink.net) * app/parasite.c * app/parasite.h * app/parasiteF.h * app/parasiteP.h : use a single name field instead of seperate creator/type fields. moved to libgimp/parasite* * libgimp/Makefile.am * libgimp/gimp.c * libgimp/gimp.h * libgimp/gimpdrawable.c * libgimp/gimpimage.c * libgimp/gimpprotocol.c * libgimp/gimpprotocol.h * app/Makefile.am * app/channel.c * app/channel.h * app/channel_cmds.c * app/channel_cmds.h * app/drawable_cmds.c * app/gimage_cmds.c * app/gimpdrawable.c * app/gimpdrawable.h * app/gimpdrawableP.h * app/gimpimage.c * app/gimpimage.h * app/gimpimageP.h * app/internal_procs.c * app/layer.c * app/layer.h * app/layer_cmds.c * app/layer_cmds.h * app/parasite_cmds.c * app/plug_in.c * app/procedural_db.c: Add tattoos to layers and drawables. Use new style parasites. * libgimp/gimpmatrix.c * libgimp/gimpmatrix.h: new files for matrix math. * app/perspective_tool.c * app/rotate_tool.c * app/scale_tool.c * app/shear_tool.c * app/transform_core.c * app/transform_core.h: use GimpMatrix instead of the old matrix code from transform_core. * ligimp/gimpparasite*: removed. now useing the same source for plug-ins and the core. * plug-ins/script-fu/script-fu.c * plug-ins/tiff/tiff.c * plug-ins/gif/gif.c: updated to use new style parasites.
1998-10-14 10:54:02 +08:00
layer_get_tattoo(const Layer *layer)
{
return (gimp_drawable_get_tattoo(GIMP_DRAWABLE(layer)));
}
1997-11-25 06:05:25 +08:00
void
layer_invalidate_previews (GimpImage* gimage)
1997-11-25 06:05:25 +08:00
{
GSList * tmp;
1997-11-25 06:05:25 +08:00
Layer * layer;
g_return_if_fail (gimage != NULL);
tmp = gimage->layers;
1997-11-25 06:05:25 +08:00
while (tmp)
{
layer = (Layer *) tmp->data;
drawable_invalidate_preview (GIMP_DRAWABLE(layer));
tmp = g_slist_next (tmp);
1997-11-25 06:05:25 +08:00
}
}
static void
layer_preview_scale (type, cmap, srcPR, destPR, subsample)
int type;
unsigned char *cmap;
PixelRegion *srcPR;
PixelRegion *destPR;
int subsample;
{
#define EPSILON 0.000001
unsigned char * src, * s;
unsigned char * dest, * d;
double * row, * r;
int destwidth;
int src_row, src_col;
int bytes, b;
int width, height;
int orig_width, orig_height;
double x_rat, y_rat;
double x_cum, y_cum;
double x_last, y_last;
double * x_frac, y_frac, tot_frac;
int i, j;
int frac;
int advance_dest;
unsigned char rgb[MAX_CHANNELS];
orig_width = srcPR->w / subsample;
orig_height = srcPR->h / subsample;
width = destPR->w;
height = destPR->h;
/* Some calculations... */
bytes = destPR->bytes;
destwidth = destPR->rowstride;
/* the data pointers... */
src = (unsigned char *) g_malloc (orig_width * bytes);
dest = destPR->data;
/* find the ratios of old x to new x and old y to new y */
x_rat = (double) orig_width / (double) width;
y_rat = (double) orig_height / (double) height;
/* allocate an array to help with the calculations */
row = (double *) g_malloc (sizeof (double) * width * bytes);
x_frac = (double *) g_malloc (sizeof (double) * (width + orig_width));
/* initialize the pre-calculated pixel fraction array */
src_col = 0;
x_cum = (double) src_col;
x_last = x_cum;
for (i = 0; i < width + orig_width; i++)
{
if (x_cum + x_rat <= (src_col + 1 + EPSILON))
{
x_cum += x_rat;
x_frac[i] = x_cum - x_last;
}
else
{
src_col ++;
x_frac[i] = src_col - x_last;
}
x_last += x_frac[i];
}
/* clear the "row" array */
memset (row, 0, sizeof (double) * width * bytes);
/* counters... */
src_row = 0;
y_cum = (double) src_row;
y_last = y_cum;
pixel_region_get_row (srcPR, 0, src_row * subsample, orig_width * subsample, src, subsample);
/* Scale the selected region */
for (i = 0; i < height; )
{
src_col = 0;
x_cum = (double) src_col;
/* determine the fraction of the src pixel we are using for y */
if (y_cum + y_rat <= (src_row + 1 + EPSILON))
{
y_cum += y_rat;
y_frac = y_cum - y_last;
advance_dest = TRUE;
}
else
{
src_row ++;
y_frac = src_row - y_last;
advance_dest = FALSE;
}
y_last += y_frac;
s = src;
r = row;
frac = 0;
j = width;
while (j)
{
tot_frac = x_frac[frac++] * y_frac;
/* If indexed, transform the color to RGB */
if (type == 2)
{
map_to_color (2, cmap, s, rgb);
r[RED_PIX] += rgb[RED_PIX] * tot_frac;
r[GREEN_PIX] += rgb[GREEN_PIX] * tot_frac;
r[BLUE_PIX] += rgb[BLUE_PIX] * tot_frac;
if (bytes == 4)
r[ALPHA_PIX] += s[ALPHA_I_PIX] * tot_frac;
}
else
for (b = 0; b < bytes; b++)
r[b] += s[b] * tot_frac;
/* increment the destination */
if (x_cum + x_rat <= (src_col + 1 + EPSILON))
{
r += bytes;
x_cum += x_rat;
j--;
}
/* increment the source */
else
{
s += srcPR->bytes;
src_col++;
}
}
if (advance_dest)
{
tot_frac = 1.0 / (x_rat * y_rat);
/* copy "row" to "dest" */
d = dest;
r = row;
j = width;
while (j--)
{
b = bytes;
while (b--)
*d++ = (unsigned char) (*r++ * tot_frac);
}
dest += destwidth;
/* clear the "row" array */
memset (row, 0, sizeof (double) * destwidth);
i++;
}
else
pixel_region_get_row (srcPR, 0, src_row * subsample, orig_width * subsample, src, subsample);
}
/* free up temporary arrays */
g_free (row);
g_free (x_frac);
g_free (src);
}
static void
gimp_layer_mask_destroy (GtkObject *object)
{
GimpLayerMask *layermask;
g_return_if_fail (object != NULL);
g_return_if_fail (GIMP_IS_LAYER_MASK (object));
layermask = GIMP_LAYER_MASK (object);
if (GTK_OBJECT_CLASS (layer_mask_parent_class)->destroy)
(*GTK_OBJECT_CLASS (layer_mask_parent_class)->destroy) (object);
}
LayerMask *
layer_mask_new (GimpImage* gimage, int width, int height, char *name, int opacity,
unsigned char *col)
{
LayerMask * layer_mask;
int i;
layer_mask = gtk_type_new (gimp_layer_mask_get_type ());
gimp_drawable_configure (GIMP_DRAWABLE(layer_mask),
gimage, width, height, GRAY_GIMAGE, name);
/* set the layer_mask color and opacity */
for (i = 0; i < 3; i++)
GIMP_CHANNEL(layer_mask)->col[i] = col[i];
GIMP_CHANNEL(layer_mask)->opacity = opacity;
GIMP_CHANNEL(layer_mask)->show_masked = 1;
/* selection mask variables */
GIMP_CHANNEL(layer_mask)->empty = TRUE;
GIMP_CHANNEL(layer_mask)->segs_in = NULL;
GIMP_CHANNEL(layer_mask)->segs_out = NULL;
GIMP_CHANNEL(layer_mask)->num_segs_in = 0;
GIMP_CHANNEL(layer_mask)->num_segs_out = 0;
GIMP_CHANNEL(layer_mask)->bounds_known = TRUE;
GIMP_CHANNEL(layer_mask)->boundary_known = TRUE;
GIMP_CHANNEL(layer_mask)->x1 = GIMP_CHANNEL(layer_mask)->y1 = 0;
GIMP_CHANNEL(layer_mask)->x2 = width;
GIMP_CHANNEL(layer_mask)->y2 = height;
return layer_mask;
}
LayerMask *
layer_mask_copy (LayerMask *layer_mask)
{
char * layer_mask_name;
LayerMask * new_layer_mask;
PixelRegion srcPR, destPR;
/* formulate the new layer_mask name */
layer_mask_name = g_strdup_printf (_("%s copy"), GIMP_DRAWABLE(layer_mask)->name);
/* allocate a new layer_mask object */
new_layer_mask = layer_mask_new (GIMP_DRAWABLE(layer_mask)->gimage,
GIMP_DRAWABLE(layer_mask)->width,
GIMP_DRAWABLE(layer_mask)->height,
layer_mask_name,
GIMP_CHANNEL(layer_mask)->opacity,
GIMP_CHANNEL(layer_mask)->col);
GIMP_DRAWABLE(new_layer_mask)->visible = GIMP_DRAWABLE(layer_mask)->visible;
GIMP_DRAWABLE(new_layer_mask)->offset_x = GIMP_DRAWABLE(layer_mask)->offset_x;
GIMP_DRAWABLE(new_layer_mask)->offset_y = GIMP_DRAWABLE(layer_mask)->offset_y;
GIMP_CHANNEL(new_layer_mask)->show_masked = GIMP_CHANNEL(layer_mask)->show_masked;
/* copy the contents across layer masks */
pixel_region_init (&srcPR, GIMP_DRAWABLE(layer_mask)->tiles, 0, 0,
GIMP_DRAWABLE(layer_mask)->width,
GIMP_DRAWABLE(layer_mask)->height, FALSE);
pixel_region_init (&destPR, GIMP_DRAWABLE(new_layer_mask)->tiles, 0, 0, GIMP_DRAWABLE(layer_mask)->width, GIMP_DRAWABLE(layer_mask)->height, TRUE);
copy_region (&srcPR, &destPR);
/* free up the layer_mask_name memory */
g_free (layer_mask_name);
return new_layer_mask;
}
LayerMask *
layer_mask_get_ID (int ID)
{
GimpDrawable *drawable;
drawable = drawable_get_ID (ID);
if (drawable && GIMP_IS_LAYER_MASK (drawable))
return GIMP_LAYER_MASK (drawable);
else
return NULL;
}
void
layer_mask_delete (LayerMask * layermask)
{
gtk_object_unref (GTK_OBJECT (layermask));
}
LayerMask *
layer_mask_ref (LayerMask *mask)
{
gtk_object_ref (GTK_OBJECT (mask));
gtk_object_sink (GTK_OBJECT (mask));
return mask;
}
void
layer_mask_unref (LayerMask *mask)
{
gtk_object_unref (GTK_OBJECT (mask));
}
void
channel_layer_mask (Channel *mask, Layer * layer)
{
PixelRegion srcPR, destPR;
unsigned char empty = 0;
int 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->mask)->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);
copy_region (&srcPR, &destPR);
mask->bounds_known = FALSE;
}