image_map.c 11.7 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/* 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
16
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
17
 */
Sven Neumann's avatar
Sven Neumann committed
18 19 20

#include "config.h"

21
#include <gtk/gtk.h>
Sven Neumann's avatar
Sven Neumann committed
22

23
#include "display/display-types.h"
Sven Neumann's avatar
Sven Neumann committed
24

25 26 27 28
#include "base/pixel-region.h"
#include "base/tile-manager.h"
#include "base/tile.h"

29 30
#include "paint-funcs/paint-funcs.h"

31 32 33
#include "core/gimpdrawable.h"
#include "core/gimpimage.h"

34
#include "display/gimpdisplay.h"
35
#include "display/gimpdisplay-foreach.h"
36
#include "display/gimpdisplayshell.h"
37

Elliot Lee's avatar
Elliot Lee committed
38
#include "image_map.h"
39

40

41 42 43 44 45
typedef enum
{
  IMAGE_MAP_WAITING,
  IMAGE_MAP_WORKING
} ImageMapState;
Elliot Lee's avatar
Elliot Lee committed
46 47


48
struct _ImageMap
Elliot Lee's avatar
Elliot Lee committed
49
{
Michael Natterer's avatar
Michael Natterer committed
50
  GimpDisplay         *gdisp;
51 52
  GimpDrawable        *drawable;
  TileManager         *undo_tiles;
53 54
  gint		       undo_offset_x;
  gint		       undo_offset_y;
55 56 57 58 59 60 61
  ImageMapApplyFunc    apply_func;
  gpointer             user_data;
  PixelRegion          srcPR;
  PixelRegion          destPR;
  PixelRegionIterator *PRI;
  ImageMapState        state;
  guint                idle_id;
62
};
Elliot Lee's avatar
Elliot Lee committed
63 64


65
/*  local function prototypes  */
Elliot Lee's avatar
Elliot Lee committed
66

67
static gboolean   image_map_do (gpointer data);
68 69


70
/*  public functions  */
Elliot Lee's avatar
Elliot Lee committed
71

72
ImageMap *
Michael Natterer's avatar
Michael Natterer committed
73
image_map_create (GimpDisplay  *gdisp,
74
		  GimpDrawable *drawable)
Elliot Lee's avatar
Elliot Lee committed
75
{
76 77 78 79
  ImageMap *image_map;

  g_return_val_if_fail (gdisp != NULL, NULL);
  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
Elliot Lee's avatar
Elliot Lee committed
80

81
  image_map = g_new0 (ImageMap, 1);
Elliot Lee's avatar
Elliot Lee committed
82

83 84 85
  image_map->gdisp      = gdisp;
  image_map->drawable   = drawable;
  image_map->undo_tiles = NULL;
86 87
  image_map->undo_offset_x = 0;
  image_map->undo_offset_y = 0;
88 89 90 91
  image_map->apply_func = NULL;
  image_map->user_data  = NULL;
  image_map->state      = IMAGE_MAP_WAITING;
  image_map->idle_id    = 0;
92

93 94 95 96
  /* Interactive tools based on image_map disable the undo stack
   * to avert any unintented undo interaction through the UI
   */
  gimp_image_undo_freeze (image_map->gdisp->gimage);
97
  gimp_display_shell_set_menu_sensitivity (GIMP_DISPLAY_SHELL (image_map->gdisp->shell));
98 99

  return image_map;
Elliot Lee's avatar
Elliot Lee committed
100 101 102
}

void
103
image_map_apply (ImageMap          *image_map,
Elliot Lee's avatar
Elliot Lee committed
104
		 ImageMapApplyFunc  apply_func,
105
		 gpointer           apply_data)
Elliot Lee's avatar
Elliot Lee committed
106
{
107 108
  gint x1, y1;
  gint x2, y2;
109 110
  gint offset_x, offset_y;
  gint width, height;
Elliot Lee's avatar
Elliot Lee committed
111

112 113 114 115 116
  g_return_if_fail (image_map != NULL);
  g_return_if_fail (apply_func != NULL);

  image_map->apply_func = apply_func;
  image_map->user_data  = apply_data;
Elliot Lee's avatar
Elliot Lee committed
117 118

  /*  If we're still working, remove the timer  */
119
  if (image_map->state == IMAGE_MAP_WORKING)
Elliot Lee's avatar
Elliot Lee committed
120
    {
121 122
      g_source_remove (image_map->idle_id);
      image_map->idle_id = 0;
123

124 125
      pixel_regions_process_stop (image_map->PRI);
      image_map->PRI = NULL;
Elliot Lee's avatar
Elliot Lee committed
126 127 128
    }

  /*  Make sure the drawable is still valid  */
129
  if (! gimp_drawable_gimage (image_map->drawable))
Elliot Lee's avatar
Elliot Lee committed
130 131 132
    return;

  /*  The application should occur only within selection bounds  */
133
  gimp_drawable_mask_bounds (image_map->drawable, &x1, &y1, &x2, &y2);
Elliot Lee's avatar
Elliot Lee committed
134 135

  /*  If undo tiles don't exist, or change size, (re)allocate  */
136
  if (image_map->undo_tiles)
137
    {
138 139
      offset_x = image_map->undo_offset_x;
      offset_y = image_map->undo_offset_y;
140 141
      width  = tile_manager_width (image_map->undo_tiles);
      height = tile_manager_height (image_map->undo_tiles);
142 143 144 145 146 147
    }
  else
    {
      offset_x = offset_y = width = height = 0;
    }

148
  if (! image_map->undo_tiles ||
149 150
      offset_x != x1 || offset_y != y1 ||
      width != (x2 - x1) || height != (y2 - y1))
Elliot Lee's avatar
Elliot Lee committed
151
    {
152 153 154 155
      /* If either the extents changed or the tiles don't exist,
       * allocate new
       */
      if (! image_map->undo_tiles ||
156
	  width != (x2 - x1) || height != (y2 - y1))
Elliot Lee's avatar
Elliot Lee committed
157 158
	{
	  /*  Destroy old tiles--If they exist  */
159 160 161
	  if (image_map->undo_tiles != NULL)
	    tile_manager_destroy (image_map->undo_tiles);

Elliot Lee's avatar
Elliot Lee committed
162
	  /*  Allocate new tiles  */
163
	  image_map->undo_tiles =
164
	    tile_manager_new ((x2 - x1), (y2 - y1),
165
			      gimp_drawable_bytes (image_map->drawable));
Elliot Lee's avatar
Elliot Lee committed
166 167 168
	}

      /*  Copy from the image to the new tiles  */
169 170
      pixel_region_init (&image_map->srcPR,
			 gimp_drawable_data (image_map->drawable),
Elliot Lee's avatar
Elliot Lee committed
171
			 x1, y1, (x2 - x1), (y2 - y1), FALSE);
172
      pixel_region_init (&image_map->destPR, image_map->undo_tiles,
Elliot Lee's avatar
Elliot Lee committed
173 174
			 0, 0, (x2 - x1), (y2 - y1), TRUE);

175
      copy_region (&image_map->srcPR, &image_map->destPR);
Elliot Lee's avatar
Elliot Lee committed
176 177

      /*  Set the offsets  */
178 179
      image_map->undo_offset_x = x1;
      image_map->undo_offset_y = y1;
Elliot Lee's avatar
Elliot Lee committed
180
    }
181
  else /* image_map->undo_tiles exist AND drawable dimensions have not changed... */
182 183 184
    {
      /* Reset to initial drawable conditions.            */
      /* Copy from the backup undo tiles to the drawable  */
185
      pixel_region_init (&image_map->srcPR, image_map->undo_tiles, 
186
			 0, 0, width, height, FALSE);
187 188
      pixel_region_init (&image_map->destPR,
			 gimp_drawable_data (image_map->drawable), 
189
			 offset_x, offset_y, width, height, TRUE);
190

191
      copy_region (&image_map->srcPR, &image_map->destPR);
192
    }
Elliot Lee's avatar
Elliot Lee committed
193 194

  /*  Configure the src from the drawable data  */
195
  pixel_region_init (&image_map->srcPR, image_map->undo_tiles,
Elliot Lee's avatar
Elliot Lee committed
196 197 198
		     0, 0, (x2 - x1), (y2 - y1), FALSE);

  /*  Configure the dest as the shadow buffer  */
199 200
  pixel_region_init (&image_map->destPR,
		     gimp_drawable_shadow (image_map->drawable),
Elliot Lee's avatar
Elliot Lee committed
201 202 203
		     x1, y1, (x2 - x1), (y2 - y1), TRUE);

  /*  Apply the image transformation to the pixels  */
204 205 206
  image_map->PRI = pixel_regions_register (2,
					   &image_map->srcPR,
					   &image_map->destPR);
Elliot Lee's avatar
Elliot Lee committed
207 208

  /*  Start the intermittant work procedure  */
209 210
  image_map->state   = IMAGE_MAP_WORKING;
  image_map->idle_id = g_idle_add (image_map_do, image_map);
Elliot Lee's avatar
Elliot Lee committed
211 212 213
}

void
214
image_map_commit (ImageMap *image_map)
Elliot Lee's avatar
Elliot Lee committed
215
{
216
  gint x1, y1, x2, y2;
Elliot Lee's avatar
Elliot Lee committed
217

218
  g_return_if_fail (image_map != NULL);
Elliot Lee's avatar
Elliot Lee committed
219

220
  if (image_map->state == IMAGE_MAP_WORKING)
Elliot Lee's avatar
Elliot Lee committed
221
    {
222 223
      g_source_remove (image_map->idle_id);
      image_map->idle_id = 0;
Elliot Lee's avatar
Elliot Lee committed
224 225

      /*  Finish the changes  */
226
      while (image_map_do (image_map));
Elliot Lee's avatar
Elliot Lee committed
227 228 229
    }

  /*  Make sure the drawable is still valid  */
230
  if (! gimp_drawable_gimage (image_map->drawable))
Elliot Lee's avatar
Elliot Lee committed
231 232
    return;

233
  /* Interactive phase ends: we can commit an undo frame now */
234
  gimp_image_undo_thaw(image_map->gdisp->gimage);
235

Elliot Lee's avatar
Elliot Lee committed
236
  /*  Register an undo step  */
237
  if (image_map->undo_tiles)
Elliot Lee's avatar
Elliot Lee committed
238
    {
239 240
      x1 = image_map->undo_offset_x;
      y1 = image_map->undo_offset_y;
241 242
      x2 = x1 + tile_manager_width (image_map->undo_tiles);
      y2 = y1 + tile_manager_height (image_map->undo_tiles);
243 244 245

      gimp_drawable_apply_image (image_map->drawable,
				 x1, y1, x2, y2, image_map->undo_tiles, FALSE);
Elliot Lee's avatar
Elliot Lee committed
246 247
    }

248
  gimp_display_shell_set_menu_sensitivity (GIMP_DISPLAY_SHELL (image_map->gdisp->shell));
249
  g_free (image_map);
Elliot Lee's avatar
Elliot Lee committed
250 251 252
}

void
253
image_map_clear (ImageMap *image_map)
Elliot Lee's avatar
Elliot Lee committed
254 255 256
{
  PixelRegion srcPR, destPR;

257
  g_return_if_fail (image_map != NULL);
Elliot Lee's avatar
Elliot Lee committed
258

259
  if (image_map->state == IMAGE_MAP_WORKING)
Elliot Lee's avatar
Elliot Lee committed
260
    {
261 262 263 264 265
      g_source_remove (image_map->idle_id);
      image_map->idle_id = 0;

      pixel_regions_process_stop (image_map->PRI);
      image_map->PRI = NULL;
Elliot Lee's avatar
Elliot Lee committed
266 267
    }

268
  image_map->state = IMAGE_MAP_WAITING;
269

Elliot Lee's avatar
Elliot Lee committed
270
  /*  Make sure the drawable is still valid  */
271
  if (! gimp_drawable_gimage (image_map->drawable))
Elliot Lee's avatar
Elliot Lee committed
272 273 274
    return;

  /*  restore the original image  */
275
  if (image_map->undo_tiles)
Elliot Lee's avatar
Elliot Lee committed
276
    {
277 278 279 280 281
      gint offset_x;
      gint offset_y;
      gint width;
      gint height;

282 283
      offset_x = image_map->undo_offset_x;
      offset_y = image_map->undo_offset_y;
284 285
      width  = tile_manager_width (image_map->undo_tiles);
      height = tile_manager_height (image_map->undo_tiles),
286

Elliot Lee's avatar
Elliot Lee committed
287
      /*  Copy from the drawable to the tiles  */
288
      pixel_region_init (&srcPR, image_map->undo_tiles, 
289
			 0, 0, width, height, FALSE);
290
      pixel_region_init (&destPR,
291
			 gimp_drawable_data (image_map->drawable),
292
			 offset_x, offset_y, width, height, TRUE);
Elliot Lee's avatar
Elliot Lee committed
293 294 295 296

      /* if the user has changed the image depth get out quickly */
      if (destPR.bytes != srcPR.bytes) 
	{
Sven Neumann's avatar
Sven Neumann committed
297
	  g_message ("image depth change, unable to restore original image");
298 299
	  tile_manager_destroy (image_map->undo_tiles);
          gimp_image_undo_thaw (image_map->gdisp->gimage);
300
          gimp_display_shell_set_menu_sensitivity (GIMP_DISPLAY_SHELL (image_map->gdisp->shell));
301
	  g_free (image_map);
Elliot Lee's avatar
Elliot Lee committed
302 303
	  return;
	}
304

Elliot Lee's avatar
Elliot Lee committed
305 306 307
      copy_region (&srcPR, &destPR);

      /*  Update the area  */
308 309 310
      gimp_drawable_update (image_map->drawable,
			    offset_x, offset_y,
			    width, height);
Elliot Lee's avatar
Elliot Lee committed
311 312

      /*  Free the undo_tiles tile manager  */
313 314
      tile_manager_destroy (image_map->undo_tiles);
      image_map->undo_tiles = NULL;
Elliot Lee's avatar
Elliot Lee committed
315
    }
316
}
Elliot Lee's avatar
Elliot Lee committed
317

318
void
319
image_map_abort (ImageMap *image_map)
320
{
321
  g_return_if_fail (image_map != NULL);
322

323 324
  image_map_clear (image_map);
  gimp_image_undo_thaw (image_map->gdisp->gimage);
325
  gimp_display_shell_set_menu_sensitivity (GIMP_DISPLAY_SHELL (image_map->gdisp->shell));
326
  g_free (image_map);
Elliot Lee's avatar
Elliot Lee committed
327
}
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
328

329
guchar *
330 331 332
image_map_get_color_at (ImageMap *image_map, 
			gint      x, 
			gint      y)
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
333
{
334
  guchar    src[5];
335
  guchar    *dest;
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
336

337
  g_return_val_if_fail (image_map != NULL, NULL);
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
338

339 340
  if (x >= 0 && x < gimp_drawable_width (image_map->drawable) && 
      y >= 0 && y < gimp_drawable_height (image_map->drawable))
341 342
    {
      /* Check if done damage to original image */
343 344 345 346 347 348 349 350 351
      if (! image_map->undo_tiles)
	return (gimp_drawable_get_color_at (image_map->drawable, x, y));

      if (! image_map ||
	  (! gimp_drawable_gimage (image_map->drawable) && 
	   gimp_drawable_is_indexed (image_map->drawable)) ||
	  x < 0 || y < 0                                   ||
	  x >= tile_manager_width (image_map->undo_tiles)  ||
	  y >= tile_manager_height (image_map->undo_tiles))
352 353 354
	{
	  return NULL;
	}
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
355

356
      dest = g_new (guchar, 5);
357 358
      
      read_pixel_data_1 (image_map->undo_tiles, x, y, src);
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
359

360 361
      gimp_image_get_color (gimp_drawable_gimage (image_map->drawable),
			    gimp_drawable_type (image_map->drawable),
362
			    dest, src);
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
363

364 365
      if (GIMP_IMAGE_TYPE_HAS_ALPHA (gimp_drawable_type (image_map->drawable)))
	dest[3] = src[gimp_drawable_bytes (image_map->drawable) - 1];
366 367
      else
	dest[3] = 255;
368 369

      if (gimp_drawable_is_indexed (image_map->drawable))
370 371 372
	dest[4] = src[0];
      else
	dest[4] = 0;
373

374 375 376 377 378 379
      return dest;
    }
  else /* out of bounds error */
    {
      return NULL;
    }
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
380
}
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414


/*  private functions  */

static gboolean
image_map_do (gpointer data)
{
  ImageMap    *image_map;
  GimpImage   *gimage;
  PixelRegion  shadowPR;
  gint         x, y, w, h;

  image_map = (ImageMap *) data;

  if (! (gimage = gimp_drawable_gimage (image_map->drawable)))
    {
      image_map->state = IMAGE_MAP_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);
  gimp_image_apply_image (gimage, image_map->drawable, &shadowPR,
415 416
			  FALSE, OPAQUE_OPACITY, GIMP_REPLACE_MODE, NULL, 
                          x, y);
417 418 419 420 421 422 423

  /*  display the results  */
  if (image_map->gdisp)
    {
      gimp_drawable_update (image_map->drawable,
			    x, y,
			    w, h);
424
      gimp_display_flush_now (image_map->gdisp);
425 426 427 428 429 430 431 432 433 434 435 436 437 438
    }

  image_map->PRI = pixel_regions_process (image_map->PRI);

  if (image_map->PRI == NULL)
    {
      image_map->state = IMAGE_MAP_WAITING;
      gdisplays_flush ();

      return FALSE;
    }

  return TRUE;
}