gimp/app/image_map.c

299 lines
8.6 KiB
C

/* The GIMP -- an image manipulation program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "appenv.h"
#include "drawable.h"
#include "errors.h"
#include "gdisplay.h"
#include "gimage.h"
#include "gimage_mask.h"
#include "image_map.h"
#include "tile_manager.h"
#define WAITING 0
#define WORKING 1
#define WORK_DELAY 1
/* Local structures */
typedef struct _ImageMap
{
GDisplay * gdisp;
int drawable_id;
TileManager * undo_tiles;
ImageMapApplyFunc apply_func;
void * user_data;
PixelRegion srcPR, destPR;
void * pr;
int state;
gint idle;
} _ImageMap;
/**************************/
/* Function definitions */
static gint
image_map_do (gpointer data)
{
_ImageMap *_image_map;
GImage *gimage;
PixelRegion shadowPR;
int x, y, w, h;
_image_map = (_ImageMap *) data;
if (! (gimage = drawable_gimage (_image_map->drawable_id)))
{
_image_map->state = WAITING;
return FALSE;
}
/* Process the pixel regions and apply the image mapping */
(* _image_map->apply_func) (&_image_map->srcPR, &_image_map->destPR, _image_map->user_data);
x = _image_map->destPR.x;
y = _image_map->destPR.y;
w = _image_map->destPR.w;
h = _image_map->destPR.h;
/* apply the results */
pixel_region_init (&shadowPR, gimage->shadow, x, y, w, h, FALSE);
gimage_apply_image (gimage, _image_map->drawable_id, &shadowPR,
FALSE, OPAQUE, REPLACE_MODE, NULL, x, y);
/* display the results */
if (_image_map->gdisp)
{
drawable_update (_image_map->drawable_id, x, y, w, h);
gdisplay_flush (_image_map->gdisp);
}
_image_map->pr = pixel_regions_process (_image_map->pr);
if (_image_map->pr == NULL)
{
_image_map->state = WAITING;
gdisplays_flush ();
return FALSE;
}
else
return TRUE;
}
ImageMap
image_map_create (void *gdisp_ptr,
int drawable_id)
{
_ImageMap *_image_map;
_image_map = (_ImageMap *) g_malloc (sizeof (_ImageMap));
_image_map->gdisp = (GDisplay *) gdisp_ptr;
_image_map->drawable_id = drawable_id;
_image_map->undo_tiles = NULL;
_image_map->state = WAITING;
return (ImageMap) _image_map;
}
void
image_map_apply (ImageMap image_map,
ImageMapApplyFunc apply_func,
void *user_data)
{
_ImageMap *_image_map;
int x1, y1, x2, y2;
_image_map = (_ImageMap *) image_map;
_image_map->apply_func = apply_func;
_image_map->user_data = user_data;
/* If we're still working, remove the timer */
if (_image_map->state == WORKING)
{
gtk_idle_remove (_image_map->idle);
pixel_regions_process_stop (_image_map->pr);
_image_map->pr = NULL;
}
/* Make sure the drawable is still valid */
if (! drawable_gimage (_image_map->drawable_id))
return;
/* The application should occur only within selection bounds */
drawable_mask_bounds (_image_map->drawable_id, &x1, &y1, &x2, &y2);
/* If undo tiles don't exist, or change size, (re)allocate */
if (!_image_map->undo_tiles ||
_image_map->undo_tiles->x != x1 ||
_image_map->undo_tiles->y != y1 ||
_image_map->undo_tiles->levels[0].width != (x2 - x1) ||
_image_map->undo_tiles->levels[0].height != (y2 - y1))
{
/* If undo tiles exist, copy them to the drawable*/
if (_image_map->undo_tiles)
{
/* Copy from the drawable to the tiles */
pixel_region_init (&_image_map->srcPR, _image_map->undo_tiles, 0, 0,
_image_map->undo_tiles->levels[0].width,
_image_map->undo_tiles->levels[0].height,
FALSE);
pixel_region_init (&_image_map->destPR, drawable_data (_image_map->drawable_id),
_image_map->undo_tiles->x, _image_map->undo_tiles->y,
_image_map->undo_tiles->levels[0].width,
_image_map->undo_tiles->levels[0].height,
TRUE);
copy_region (&_image_map->srcPR, &_image_map->destPR);
}
/* If either the extents changed or the tiles don't exist, allocate new */
if (!_image_map->undo_tiles ||
_image_map->undo_tiles->levels[0].width != (x2 - x1) ||
_image_map->undo_tiles->levels[0].height != (y2 - y1))
{
/* Destroy old tiles--If they exist */
if (_image_map->undo_tiles != NULL)
tile_manager_destroy (_image_map->undo_tiles);
/* Allocate new tiles */
_image_map->undo_tiles = tile_manager_new ((x2 - x1), (y2 - y1),
drawable_bytes (_image_map->drawable_id));
}
/* Copy from the image to the new tiles */
pixel_region_init (&_image_map->srcPR, drawable_data (_image_map->drawable_id),
x1, y1, (x2 - x1), (y2 - y1), FALSE);
pixel_region_init (&_image_map->destPR, _image_map->undo_tiles,
0, 0, (x2 - x1), (y2 - y1), TRUE);
copy_region (&_image_map->srcPR, &_image_map->destPR);
/* Set the offsets */
_image_map->undo_tiles->x = x1;
_image_map->undo_tiles->y = y1;
}
/* Configure the src from the drawable data */
pixel_region_init (&_image_map->srcPR, _image_map->undo_tiles,
0, 0, (x2 - x1), (y2 - y1), FALSE);
/* Configure the dest as the shadow buffer */
pixel_region_init (&_image_map->destPR, drawable_shadow (_image_map->drawable_id),
x1, y1, (x2 - x1), (y2 - y1), TRUE);
/* Apply the image transformation to the pixels */
_image_map->pr = pixel_regions_register (2, &_image_map->srcPR, &_image_map->destPR);
/* Start the intermittant work procedure */
_image_map->state = WORKING;
_image_map->idle = gtk_idle_add (image_map_do, image_map);
}
void
image_map_commit (ImageMap image_map)
{
_ImageMap *_image_map;
int x1, y1, x2, y2;
_image_map = (_ImageMap *) image_map;
if (_image_map->state == WORKING)
{
gtk_idle_remove (_image_map->idle);
/* Finish the changes */
while (image_map_do (image_map)) ;
}
/* Make sure the drawable is still valid */
if (! drawable_gimage (_image_map->drawable_id))
return;
/* Register an undo step */
if (_image_map->undo_tiles)
{
x1 = _image_map->undo_tiles->x;
y1 = _image_map->undo_tiles->y;
x2 = _image_map->undo_tiles->x + _image_map->undo_tiles->levels[0].width;
y2 = _image_map->undo_tiles->y + _image_map->undo_tiles->levels[0].height;
drawable_apply_image (_image_map->drawable_id, x1, y1, x2, y2, _image_map->undo_tiles, FALSE);
}
g_free (_image_map);
}
void
image_map_abort (ImageMap image_map)
{
_ImageMap *_image_map;
PixelRegion srcPR, destPR;
_image_map = (_ImageMap *) image_map;
if (_image_map->state == WORKING)
{
gtk_idle_remove (_image_map->idle);
pixel_regions_process_stop (_image_map->pr);
_image_map->pr = NULL;
}
/* Make sure the drawable is still valid */
if (! drawable_gimage (_image_map->drawable_id))
return;
/* restore the original image */
if (_image_map->undo_tiles)
{
/* Copy from the drawable to the tiles */
pixel_region_init (&srcPR, _image_map->undo_tiles, 0, 0,
_image_map->undo_tiles->levels[0].width,
_image_map->undo_tiles->levels[0].height,
FALSE);
pixel_region_init (&destPR, drawable_data (_image_map->drawable_id),
_image_map->undo_tiles->x, _image_map->undo_tiles->y,
_image_map->undo_tiles->levels[0].width,
_image_map->undo_tiles->levels[0].height,
TRUE);
/* if the user has changed the image depth get out quickly */
if (destPR.bytes != srcPR.bytes)
{
g_warning ("image depth change, unable to restore original image");
tile_manager_destroy (_image_map->undo_tiles);
g_free (_image_map);
return;
}
copy_region (&srcPR, &destPR);
/* Update the area */
drawable_update (_image_map->drawable_id,
_image_map->undo_tiles->x, _image_map->undo_tiles->y,
_image_map->undo_tiles->levels[0].width,
_image_map->undo_tiles->levels[0].height);
/* Free the undo_tiles tile manager */
tile_manager_destroy (_image_map->undo_tiles);
}
g_free (_image_map);
}