gimpchannel.c 40.2 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
 */
18

19 20
#include "config.h"

Elliot Lee's avatar
Elliot Lee committed
21 22
#include <stdlib.h>
#include <string.h>
23

Elliot Lee's avatar
Elliot Lee committed
24 25 26
#include "appenv.h"
#include "channel.h"
#include "drawable.h"
27
#include "gdisplay.h"
Elliot Lee's avatar
Elliot Lee committed
28 29 30
#include "gimage_mask.h"
#include "layer.h"
#include "paint_funcs.h"
31
#include "parasitelist.h"
Elliot Lee's avatar
Elliot Lee committed
32 33
#include "temp_buf.h"
#include "undo.h"
34
#include "gimpsignal.h"
35
#include "gimppreviewcache.h"
Elliot Lee's avatar
Elliot Lee committed
36

37
#include "channel_pvt.h"
scott's avatar
scott committed
38
#include "tile.h"
39

40 41 42
#include "gimplut.h"
#include "lut_funcs.h"

43 44 45 46 47
#include "libgimp/gimpmath.h"

#include "libgimp/gimpintl.h"


48
enum {
49
  REMOVED,
50 51 52 53 54 55 56
  LAST_SIGNAL
};

static void gimp_channel_class_init (GimpChannelClass *klass);
static void gimp_channel_init       (GimpChannel      *channel);
static void gimp_channel_destroy    (GtkObject        *object);

57
static guint channel_signals[LAST_SIGNAL] = { 0 };
58 59 60

static GimpDrawableClass *parent_class = NULL;

61
GtkType
62 63
gimp_channel_get_type ()
{
64
  static GtkType channel_type = 0;
65 66 67 68 69 70 71 72 73 74

  if (!channel_type)
    {
      GtkTypeInfo channel_info =
      {
	"GimpChannel",
	sizeof (GimpChannel),
	sizeof (GimpChannelClass),
	(GtkClassInitFunc) gimp_channel_class_init,
	(GtkObjectInitFunc) gimp_channel_init,
75 76
        /* reserved_1 */ NULL,
	/* reserved_2 */ NULL,
77
	(GtkClassInitFunc) NULL,
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
      };

      channel_type = gtk_type_unique (gimp_drawable_get_type (), &channel_info);
    }

  return channel_type;
}

static void
gimp_channel_class_init (GimpChannelClass *class)
{
  GtkObjectClass *object_class;

  object_class = (GtkObjectClass*) class;
  parent_class = gtk_type_class (gimp_drawable_get_type ());

94 95 96 97
  channel_signals[REMOVED] =
	  gimp_signal_new ("removed",
			   0, object_class->type, 0, gimp_sigtype_void);

98 99 100 101 102 103 104 105 106
  gtk_object_class_add_signals (object_class, channel_signals, LAST_SIGNAL);

  object_class->destroy = gimp_channel_destroy;
}

static void
gimp_channel_init (GimpChannel *channel)
{
}
Elliot Lee's avatar
Elliot Lee committed
107 108 109

/**************************/
/*  Function definitions  */
110
/**************************/
Elliot Lee's avatar
Elliot Lee committed
111 112

static void
113 114
channel_validate (TileManager *tm,
		  Tile        *tile)
Elliot Lee's avatar
Elliot Lee committed
115 116
{
  /*  Set the contents of the tile to empty  */
scott's avatar
scott committed
117
  memset (tile_data_pointer (tile, 0, 0), 
118
	  TRANSPARENT_OPACITY, tile_size (tile));
Elliot Lee's avatar
Elliot Lee committed
119 120 121
}

Channel *
122 123 124 125 126 127
channel_new (GimpImage *gimage,
	     gint       width,
	     gint       height,
	     gchar     *name,
	     gint       opacity,
	     guchar    *col)
Elliot Lee's avatar
Elliot Lee committed
128 129
{
  Channel * channel;
130
  gint i;
Elliot Lee's avatar
Elliot Lee committed
131

132
  channel = gtk_type_new (gimp_channel_get_type ());
Elliot Lee's avatar
Elliot Lee committed
133

134
  gimp_drawable_configure (GIMP_DRAWABLE (channel), 
135
			   gimage, width, height, GRAY_GIMAGE, name);
Elliot Lee's avatar
Elliot Lee committed
136 137 138 139

  /*  set the channel color and opacity  */
  for (i = 0; i < 3; i++)
    channel->col[i] = col[i];
140 141 142

  channel->opacity     = opacity;
  channel->show_masked = TRUE;
Elliot Lee's avatar
Elliot Lee committed
143 144

  /*  selection mask variables  */
145 146 147 148 149 150
  channel->empty          = TRUE;
  channel->segs_in        = NULL;
  channel->segs_out       = NULL;
  channel->num_segs_in    = 0;
  channel->num_segs_out   = 0;
  channel->bounds_known   = TRUE;
Elliot Lee's avatar
Elliot Lee committed
151
  channel->boundary_known = TRUE;
152 153 154 155
  channel->x1             = 0;
  channel->y1             = 0;
  channel->x2             = width;
  channel->y2             = height;
Elliot Lee's avatar
Elliot Lee committed
156 157 158 159

  return channel;
}

scott's avatar
scott committed
160 161 162 163 164
Channel *
channel_ref (Channel *channel)
{
  gtk_object_ref  (GTK_OBJECT (channel));
  gtk_object_sink (GTK_OBJECT (channel));
165

scott's avatar
scott committed
166 167 168 169 170 171 172 173 174
  return channel;
}

void
channel_unref (Channel *channel)
{
  gtk_object_unref (GTK_OBJECT (channel));
}

Elliot Lee's avatar
Elliot Lee committed
175 176 177
Channel *
channel_copy (Channel *channel)
{
178 179
  gchar *channel_name;
  Channel *new_channel;
Elliot Lee's avatar
Elliot Lee committed
180
  PixelRegion srcPR, destPR;
181 182 183 184
  gchar *ext;
  gint number;
  gchar *name;
  gint len;
Elliot Lee's avatar
Elliot Lee committed
185 186

  /*  formulate the new channel name  */
187 188
  name = channel_get_name (channel);
  ext = strrchr (name, '#');
189
  len = strlen (_("copy"));
190 191 192 193
  if ((strlen (name) >= len &&
       strcmp (&name[strlen (name) - len], _("copy")) == 0) ||
      (ext && (number = atoi (ext + 1)) > 0 && 
       ((int)(log10 (number) + 1)) == strlen (ext + 1)))
194 195
    /* don't have redundant "copy"s */
    channel_name = g_strdup (name);
196
  else
197
    channel_name = g_strdup_printf (_("%s copy"), name);
Elliot Lee's avatar
Elliot Lee committed
198 199

  /*  allocate a new channel object  */
200 201 202
  new_channel = channel_new (GIMP_DRAWABLE (channel)->gimage, 
			     GIMP_DRAWABLE (channel)->width, 
			     GIMP_DRAWABLE (channel)->height, 
203
			     channel_name, channel->opacity, channel->col);
204
  GIMP_DRAWABLE (new_channel)->visible = GIMP_DRAWABLE (channel)->visible;
Elliot Lee's avatar
Elliot Lee committed
205 206 207
  new_channel->show_masked = channel->show_masked;

  /*  copy the contents across channels  */
208 209 210 211 212 213 214
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles, 0, 0, 
		     GIMP_DRAWABLE (channel)->width, 
		     GIMP_DRAWABLE (channel)->height, FALSE);
  pixel_region_init (&destPR, GIMP_DRAWABLE (new_channel)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (channel)->width,
		     GIMP_DRAWABLE (channel)->height, TRUE);
Elliot Lee's avatar
Elliot Lee committed
215 216
  copy_region (&srcPR, &destPR);

217
  /* copy the parasites */
218 219
  GIMP_DRAWABLE (new_channel)->parasites 
    = parasite_list_copy (GIMP_DRAWABLE (channel)->parasites);
220

Elliot Lee's avatar
Elliot Lee committed
221 222 223 224 225 226
  /*  free up the channel_name memory  */
  g_free (channel_name);

  return new_channel;
}

227
void
228 229
channel_set_name (Channel *channel,
		  gchar   *name)
230
{
231
  gimp_drawable_set_name (GIMP_DRAWABLE (channel), name);
232 233
}

234
gchar *
235 236
channel_get_name (Channel *channel)
{
237
  return gimp_drawable_get_name (GIMP_DRAWABLE (channel));
238 239
}

240
void 
241
channel_set_color (Channel *channel,
242
		   guchar  *color)
243
{
244 245
  gint i;

246 247
  if (color)
    {  
248 249
      for (i = 0; i < 3; i++)
	channel->col[i] = color[i];
250
    }
251
}
252

Sven Neumann's avatar
Sven Neumann committed
253
guchar *
254 255
channel_get_color (Channel *channel)
{
256
  return (GIMP_CHANNEL (channel)->col); 
257 258 259 260 261 262 263 264
}
 
int
channel_get_opacity (Channel *channel)
{ 
  return channel->opacity;
}

265
void 
266 267
channel_set_opacity (Channel *channel,
		     gint     opacity)
268 269
{
  if (opacity >=0 && opacity <= 100)
270
    channel->opacity = (gint) (opacity * 255) / 100;
271 272
}

Elliot Lee's avatar
Elliot Lee committed
273
Channel *
274
channel_get_ID (gint ID)
Elliot Lee's avatar
Elliot Lee committed
275
{
276
  GimpDrawable *drawable;
277

278 279 280 281 282
  drawable = drawable_get_ID (ID);
  if (drawable && GIMP_IS_CHANNEL (drawable)) 
    return GIMP_CHANNEL (drawable);
  else
    return NULL;
Elliot Lee's avatar
Elliot Lee committed
283 284 285 286 287
}

void
channel_delete (Channel *channel)
{
scott's avatar
scott committed
288
  gtk_object_unref (GTK_OBJECT (channel));
289 290 291 292 293 294 295 296 297 298 299 300
}

static void
gimp_channel_destroy (GtkObject *object)
{
  GimpChannel *channel;

  g_return_if_fail (object != NULL);
  g_return_if_fail (GIMP_IS_CHANNEL (object));

  channel = GIMP_CHANNEL (object);

Elliot Lee's avatar
Elliot Lee committed
301 302 303 304 305 306
  /* free the segments?  */
  if (channel->segs_in)
    g_free (channel->segs_in);
  if (channel->segs_out)
    g_free (channel->segs_out);

307
  if (GTK_OBJECT_CLASS (parent_class)->destroy)
308
    (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
309
  
Elliot Lee's avatar
Elliot Lee committed
310 311
}

312 313 314 315 316 317 318
/* The removed signal is sent out when the channel is no longer
 * associcated with an image.  It's needed because channels aren't
 * destroyed immediately, but kept around for undo purposes.  Connect
 * to the removed signal to update bits of UI that are tied to a
 * particular layer. */
void
channel_removed (Channel  *channel,
319
		 gpointer  data)
320 321 322 323 324 325 326 327
{
  g_return_if_fail (channel != NULL);
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

  gtk_signal_emit (GTK_OBJECT (channel), channel_signals[REMOVED]);
}


Elliot Lee's avatar
Elliot Lee committed
328
void
329 330 331
channel_scale (Channel *channel,
	       gint     new_width,
	       gint     new_height)
Elliot Lee's avatar
Elliot Lee committed
332 333 334 335 336 337 338 339
{
  PixelRegion srcPR, destPR;
  TileManager *new_tiles;

  if (new_width == 0 || new_height == 0)
    return;

  /*  Update the old channel position  */
340 341 342 343
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
344 345

  /*  Configure the pixel regions  */
346 347 348 349
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (channel)->width,
		     GIMP_DRAWABLE (channel)->height, FALSE);
Elliot Lee's avatar
Elliot Lee committed
350 351 352 353 354 355 356 357 358

  /*  Allocate the new channel, configure dest region  */
  new_tiles = tile_manager_new (new_width, new_height, 1);
  pixel_region_init (&destPR, new_tiles, 0, 0, new_width, new_height, TRUE);

  /*  Add an alpha channel  */
  scale_region (&srcPR, &destPR);

  /*  Push the channel on the undo stack  */
359
  undo_push_channel_mod (GIMP_DRAWABLE (channel)->gimage, channel);
Elliot Lee's avatar
Elliot Lee committed
360 361

  /*  Configure the new channel  */
362 363 364
  GIMP_DRAWABLE (channel)->tiles = new_tiles;
  GIMP_DRAWABLE (channel)->width = new_width;
  GIMP_DRAWABLE (channel)->height = new_height;
Elliot Lee's avatar
Elliot Lee committed
365 366 367 368 369

  /*  bounds are now unknown  */
  channel->bounds_known = FALSE;

  /*  Update the new channel position  */
370 371 372 373
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
374 375 376
}

void
377 378 379 380 381
channel_resize (Channel *channel,
		gint     new_width,
		gint     new_height,
		gint     offx,
		gint     offy)
Elliot Lee's avatar
Elliot Lee committed
382 383 384
{
  PixelRegion srcPR, destPR;
  TileManager *new_tiles;
385 386 387 388
  guchar bg = 0;
  gint clear;
  gint w, h;
  gint x1, y1, x2, y2;
Elliot Lee's avatar
Elliot Lee committed
389 390 391 392

  if (!new_width || !new_height)
    return;

393 394 395 396
  x1 = CLAMP (offx, 0, new_width);
  y1 = CLAMP (offy, 0, new_height);
  x2 = CLAMP ((offx + GIMP_DRAWABLE (channel)->width), 0, new_width);
  y2 = CLAMP ((offy + GIMP_DRAWABLE (channel)->height), 0, new_height);
Elliot Lee's avatar
Elliot Lee committed
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
  w = x2 - x1;
  h = y2 - y1;

  if (offx > 0)
    {
      x1 = 0;
      x2 = offx;
    }
  else
    {
      x1 = -offx;
      x2 = 0;
    }

  if (offy > 0)
    {
      y1 = 0;
      y2 = offy;
    }
  else
    {
      y1 = -offy;
      y2 = 0;
    }

  /*  Update the old channel position  */
423 424 425 426
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
427 428

  /*  Configure the pixel regions  */
429 430
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
		     x1, y1, w, h, FALSE);
Elliot Lee's avatar
Elliot Lee committed
431 432

  /*  Determine whether the new channel needs to be initially cleared  */
433 434
  if ((new_width  > GIMP_DRAWABLE (channel)->width) ||
      (new_height > GIMP_DRAWABLE (channel)->height) ||
Elliot Lee's avatar
Elliot Lee committed
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
      (x2 || y2))
    clear = TRUE;
  else
    clear = FALSE;

  /*  Allocate the new channel, configure dest region  */
  new_tiles = tile_manager_new (new_width, new_height, 1);

  /*  Set to black (empty--for selections)  */
  if (clear)
    {
      pixel_region_init (&destPR, new_tiles, 0, 0, new_width, new_height, TRUE);
      color_region (&destPR, &bg);
    }

  /*  copy from the old to the new  */
  pixel_region_init (&destPR, new_tiles, x2, y2, w, h, TRUE);
  if (w && h)
    copy_region (&srcPR, &destPR);

  /*  Push the channel on the undo stack  */
456
  undo_push_channel_mod (GIMP_DRAWABLE (channel)->gimage, channel);
Elliot Lee's avatar
Elliot Lee committed
457 458

  /*  Configure the new channel  */
459 460 461
  GIMP_DRAWABLE (channel)->tiles  = new_tiles;
  GIMP_DRAWABLE (channel)->width  = new_width;
  GIMP_DRAWABLE (channel)->height = new_height;
Elliot Lee's avatar
Elliot Lee committed
462 463 464 465 466

  /*  bounds are now unknown  */
  channel->bounds_known = FALSE;

  /*  update the new channel area  */
467 468 469 470
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
471 472
}

473 474 475
void            
channel_update (Channel *channel)
{
476 477 478 479 480
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
  gdisplays_flush ();
481 482
}

483 484 485
/**********************/
/*  access functions  */
/**********************/
Elliot Lee's avatar
Elliot Lee committed
486

487
gboolean
Elliot Lee's avatar
Elliot Lee committed
488 489
channel_toggle_visibility (Channel *channel)
{
490
  GIMP_DRAWABLE (channel)->visible = !GIMP_DRAWABLE (channel)->visible;
Elliot Lee's avatar
Elliot Lee committed
491

492
  return GIMP_DRAWABLE (channel)->visible;
Elliot Lee's avatar
Elliot Lee committed
493 494
}

495 496 497 498
TempBuf *
channel_preview (Channel *channel,
		 gint     width,
		 gint     height)
Elliot Lee's avatar
Elliot Lee committed
499 500 501
{
  MaskBuf * preview_buf;
  PixelRegion srcPR, destPR;
502
  gint subsample;
503
  TempBuf *ret_buf;
Elliot Lee's avatar
Elliot Lee committed
504

505 506 507
  g_return_val_if_fail (channel != NULL, NULL);
  g_return_val_if_fail (GIMP_IS_CHANNEL (channel), NULL);
  
Elliot Lee's avatar
Elliot Lee committed
508
  /*  The easy way  */
509 510 511 512
  if (GIMP_DRAWABLE (channel)->preview_valid &&
      (ret_buf =
       gimp_preview_cache_get (& (GIMP_DRAWABLE (channel)->preview_cache),
			       width, height)))
513
    return ret_buf;
Elliot Lee's avatar
Elliot Lee committed
514 515 516 517 518
  /*  The hard way  */
  else
    {
      /*  calculate 'acceptable' subsample  */
      subsample = 1;
519 520
      if (width < 1) width = 1;
      if (height < 1) height = 1;
521 522
      while ((width  * (subsample + 1) * 2 < GIMP_DRAWABLE (channel)->width) &&
	     (height * (subsample + 1) * 2 < GIMP_DRAWABLE (channel)->height))
Elliot Lee's avatar
Elliot Lee committed
523 524
	subsample = subsample + 1;

525 526 527 528
      pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
			 0, 0,
			 GIMP_DRAWABLE (channel)->width,
			 GIMP_DRAWABLE (channel)->height, FALSE);
Elliot Lee's avatar
Elliot Lee committed
529 530

      preview_buf = mask_buf_new (width, height);
531 532 533 534 535
      destPR.bytes     = 1;
      destPR.x         = 0;
      destPR.y         = 0;
      destPR.w         = width;
      destPR.h         = height;
Elliot Lee's avatar
Elliot Lee committed
536
      destPR.rowstride = width;
537
      destPR.data      = mask_buf_data (preview_buf);
Elliot Lee's avatar
Elliot Lee committed
538 539 540

      subsample_region (&srcPR, &destPR, subsample);

541 542 543 544 545 546
      if (!GIMP_DRAWABLE (channel)->preview_valid)
	gimp_preview_cache_invalidate (&(GIMP_DRAWABLE(channel)->preview_cache));

      GIMP_DRAWABLE (channel)->preview_valid = TRUE;
      gimp_preview_cache_add (&(GIMP_DRAWABLE (channel)->preview_cache),
			      preview_buf);
547
      return preview_buf;
Elliot Lee's avatar
Elliot Lee committed
548 549 550 551
    }
}

void
552
channel_invalidate_previews (GimpImage* gimage)
Elliot Lee's avatar
Elliot Lee committed
553
{
554
  GSList * tmp;
Elliot Lee's avatar
Elliot Lee committed
555
  Channel * channel;
556

Sven Neumann's avatar
Sven Neumann committed
557 558
  g_return_if_fail (gimage != NULL);

559
  tmp = gimage->channels;
Elliot Lee's avatar
Elliot Lee committed
560 561 562 563

  while (tmp)
    {
      channel = (Channel *) tmp->data;
564
      gimp_drawable_invalidate_preview (GIMP_DRAWABLE (channel), TRUE);
565
      tmp = g_slist_next (tmp);
Elliot Lee's avatar
Elliot Lee committed
566 567 568
    }
}

569
Tattoo
570
channel_get_tattoo (const Channel *channel)
571
{
572
  return (gimp_drawable_get_tattoo (GIMP_DRAWABLE (channel)));
573 574
}

575
void
576 577
channel_set_tattoo (const Channel *channel, 
		    Tattoo         value)
578
{
579
  gimp_drawable_set_tattoo (GIMP_DRAWABLE (channel), value);
580 581
}

582 583 584
/******************************/
/*  selection mask functions  */
/******************************/
Elliot Lee's avatar
Elliot Lee committed
585 586

Channel *
587
channel_new_mask (GimpImage *gimage,
588 589
		  gint       width,
		  gint       height)
Elliot Lee's avatar
Elliot Lee committed
590
{
591
  guchar black[3] = {0, 0, 0};
Elliot Lee's avatar
Elliot Lee committed
592 593 594
  Channel *new_channel;

  /*  Create the new channel  */
595 596
  new_channel = channel_new (gimage, width, height,
			     _("Selection Mask"), 127, black);
Elliot Lee's avatar
Elliot Lee committed
597 598

  /*  Set the validate procedure  */
599 600
  tile_manager_set_validate_proc (GIMP_DRAWABLE (new_channel)->tiles,
				  channel_validate);
Elliot Lee's avatar
Elliot Lee committed
601 602 603 604

  return new_channel;
}

605 606 607 608 609 610 611 612 613 614
gboolean
channel_boundary (Channel   *mask,
		  BoundSeg **segs_in,
		  BoundSeg **segs_out,
		  gint      *num_segs_in,
		  gint      *num_segs_out,
		  gint       x1,
		  gint       y1,
		  gint       x2,
		  gint       y2)
Elliot Lee's avatar
Elliot Lee committed
615
{
616
  gint x3, y3, x4, y4;
Elliot Lee's avatar
Elliot Lee committed
617 618 619 620 621 622 623 624 625 626 627 628
  PixelRegion bPR;

  if (! mask->boundary_known)
    {
      /* free the out of date boundary segments */
      if (mask->segs_in)
	g_free (mask->segs_in);
      if (mask->segs_out)
	g_free (mask->segs_out);

      if (channel_bounds (mask, &x3, &y3, &x4, &y4))
	{
629 630
	  pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles,
			     x3, y3, (x4 - x3), (y4 - y3), FALSE);
Elliot Lee's avatar
Elliot Lee committed
631 632 633 634
	  mask->segs_out = find_mask_boundary (&bPR, &mask->num_segs_out,
					       IgnoreBounds,
					       x1, y1,
					       x2, y2);
635 636 637 638
	  x1 = MAX (x1, x3);
	  y1 = MAX (y1, y3);
	  x2 = MIN (x2, x4);
	  y2 = MIN (y2, y4);
Elliot Lee's avatar
Elliot Lee committed
639 640 641

	  if (x2 > x1 && y2 > y1)
	    {
642 643 644 645
	      pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles,
				 0, 0,
				 GIMP_DRAWABLE (mask)->width,
				 GIMP_DRAWABLE (mask)->height, FALSE);
Elliot Lee's avatar
Elliot Lee committed
646 647 648 649 650 651 652
	      mask->segs_in =  find_mask_boundary (&bPR, &mask->num_segs_in,
						   WithinBounds,
						   x1, y1,
						   x2, y2);
	    }
	  else
	    {
653
	      mask->segs_in     = NULL;
Elliot Lee's avatar
Elliot Lee committed
654 655 656 657 658
	      mask->num_segs_in = 0;
	    }
	}
      else
	{
659 660 661
	  mask->segs_in      = NULL;
	  mask->segs_out     = NULL;
	  mask->num_segs_in  = 0;
Elliot Lee's avatar
Elliot Lee committed
662 663 664 665 666
	  mask->num_segs_out = 0;
	}
      mask->boundary_known = TRUE;
    }

667 668 669
  *segs_in      = mask->segs_in;
  *segs_out     = mask->segs_out;
  *num_segs_in  = mask->num_segs_in;
Elliot Lee's avatar
Elliot Lee committed
670 671 672 673 674
  *num_segs_out = mask->num_segs_out;

  return TRUE;
}

675 676 677 678
gint
channel_value (Channel *mask,
	       gint     x,
	       gint     y)
Elliot Lee's avatar
Elliot Lee committed
679 680
{
  Tile *tile;
681
  gint val;
Elliot Lee's avatar
Elliot Lee committed
682 683 684 685 686 687 688 689 690 691 692

  /*  Some checks to cut back on unnecessary work  */
  if (mask->bounds_known)
    {
      if (mask->empty)
	return 0;
      else if (x < mask->x1 || x >= mask->x2 || y < mask->y1 || y >= mask->y2)
	return 0;
    }
  else
    {
693 694
      if (x < 0 || x >= GIMP_DRAWABLE (mask)->width ||
	  y < 0 || y >= GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
695 696 697
	return 0;
    }

698 699
  tile = tile_manager_get_tile (GIMP_DRAWABLE (mask)->tiles, x, y, TRUE, FALSE);
  val = *(guchar *) (tile_data_pointer (tile, x % TILE_WIDTH, y % TILE_HEIGHT));
700
  tile_release (tile, FALSE);
Elliot Lee's avatar
Elliot Lee committed
701 702 703 704

  return val;
}

705 706 707 708 709 710
gboolean
channel_bounds (Channel *mask,
		gint    *x1,
		gint    *y1,
		gint    *x2,
		gint    *y2)
Elliot Lee's avatar
Elliot Lee committed
711 712
{
  PixelRegion maskPR;
713 714 715 716 717
  guchar *data, *data1;
  gint x, y;
  gint ex, ey;
  gint tx1, tx2, ty1, ty2;
  gint minx, maxx;
718
  gpointer pr;
719

Elliot Lee's avatar
Elliot Lee committed
720 721 722 723 724 725 726 727
  /*  if the mask's bounds have already been reliably calculated...  */
  if (mask->bounds_known)
    {
      *x1 = mask->x1;
      *y1 = mask->y1;
      *x2 = mask->x2;
      *y2 = mask->y2;

728
      return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
729 730 731
    }

  /*  go through and calculate the bounds  */
732 733
  tx1 = GIMP_DRAWABLE (mask)->width;
  ty1 = GIMP_DRAWABLE (mask)->height;
734 735
  tx2 = 0;
  ty2 = 0;
Elliot Lee's avatar
Elliot Lee committed
736

737 738 739 740 741 742 743
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (mask)->width,
		     GIMP_DRAWABLE (mask)->height, FALSE);
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
744
    {
745
      data1 = data = maskPR.data;
Elliot Lee's avatar
Elliot Lee committed
746 747
      ex = maskPR.x + maskPR.w;
      ey = maskPR.y + maskPR.h;
748 749
      /* only check the pixels if this tile is not fully within the currently
	 computed bounds */
750 751
      if (maskPR.x < tx1 || ex > tx2 ||
	  maskPR.y < ty1 || ey > ty2)
752
        {
753 754 755 756 757 758 759 760 761 762 763 764 765 766
	  /* Check upper left and lower right corners to see if we can
	     avoid checking the rest of the pixels in this tile */
	  if (data[0] && data[maskPR.rowstride*(maskPR.h - 1) + maskPR.w - 1])
	  {
	    if (maskPR.x < tx1)
	      tx1 = maskPR.x;
	    if (ex > tx2)
	      tx2 = ex;
	    if (maskPR.y < ty1)
	      ty1 = maskPR.y;
	    if (ey > ty2)
	      ty2 = ey;
	  }
	  else
767
	    for (y = maskPR.y; y < ey; y++, data1 += maskPR.rowstride)
Elliot Lee's avatar
Elliot Lee committed
768
	    {
769
	      for (x = maskPR.x, data = data1; x < ex; x++, data++)
770
		if (*data)
771 772 773 774 775 776 777 778 779 780 781 782 783 784 785
		{
		  minx = x;
		  maxx = x;
		  for (; x < ex; x++, data++)
		    if (*data)
		      maxx = x;
		  if (minx < tx1)
		    tx1 = minx;
		  if (maxx > tx2)
		    tx2 = maxx;
		  if (y < ty1)
		    ty1 = y;
		  if (y > ty2)
		    ty2 = y;
	      }
Elliot Lee's avatar
Elliot Lee committed
786 787 788 789
	    }
	}
    }

790 791
  tx2 = CLAMP (tx2 + 1, 0, GIMP_DRAWABLE (mask)->width);
  ty2 = CLAMP (ty2 + 1, 0, GIMP_DRAWABLE (mask)->height);
Elliot Lee's avatar
Elliot Lee committed
792

793
  if (tx1 == GIMP_DRAWABLE (mask)->width && ty1 == GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
794 795
    {
      mask->empty = TRUE;
796 797 798 799
      mask->x1    = 0;
      mask->y1    = 0;
      mask->x2    = GIMP_DRAWABLE (mask)->width;
      mask->y2    = GIMP_DRAWABLE (mask)->height;
Elliot Lee's avatar
Elliot Lee committed
800 801 802 803
    }
  else
    {
      mask->empty = FALSE;
804 805 806 807
      mask->x1    = tx1;
      mask->y1    = ty1;
      mask->x2    = tx2;
      mask->y2    = ty2;
Elliot Lee's avatar
Elliot Lee committed
808 809 810
    }
  mask->bounds_known = TRUE;

811 812 813 814 815
  *x1 = tx1;
  *x2 = tx2;
  *y1 = ty1;
  *y2 = ty2;

816
  return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
817 818
}

819
gboolean
Elliot Lee's avatar
Elliot Lee committed
820 821 822
channel_is_empty (Channel *mask)
{
  PixelRegion maskPR;
823 824
  guchar * data;
  gint x, y;
825
  gpointer pr;
Elliot Lee's avatar
Elliot Lee committed
826 827 828 829

  if (mask->bounds_known)
    return mask->empty;

830 831 832 833 834 835 836
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (mask)->width,
		     GIMP_DRAWABLE (mask)->height, FALSE);
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854
    {
      /*  check if any pixel in the mask is non-zero  */
      data = maskPR.data;
      for (y = 0; y < maskPR.h; y++)
	for (x = 0; x < maskPR.w; x++)
	  if (*data++)
	    {
	      pixel_regions_process_stop (pr);
	      return FALSE;
	    }
    }

  /*  The mask is empty, meaning we can set the bounds as known  */
  if (mask->segs_in)
    g_free (mask->segs_in);
  if (mask->segs_out)
    g_free (mask->segs_out);

855 856 857 858 859 860
  mask->empty          = TRUE;
  mask->segs_in        = NULL;
  mask->segs_out       = NULL;
  mask->num_segs_in    = 0;
  mask->num_segs_out   = 0;
  mask->bounds_known   = TRUE;
Elliot Lee's avatar
Elliot Lee committed
861
  mask->boundary_known = TRUE;
862 863 864 865
  mask->x1             = 0;
  mask->y1             = 0;
  mask->x2             = GIMP_DRAWABLE (mask)->width;
  mask->y2             = GIMP_DRAWABLE (mask)->height;
Elliot Lee's avatar
Elliot Lee committed
866 867 868 869 870

  return TRUE;
}

void
871 872 873 874 875
channel_add_segment (Channel *mask,
		     gint     x,
		     gint     y,
		     gint     width,
		     gint     value)
Elliot Lee's avatar
Elliot Lee committed
876 877
{
  PixelRegion maskPR;
878 879 880
  guchar *data;
  gint val;
  gint x2;
881
  gpointer pr;
Elliot Lee's avatar
Elliot Lee committed
882 883 884 885

  /*  check horizontal extents...  */
  x2 = x + width;
  if (x2 < 0) x2 = 0;
886
  if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
887
  if (x < 0) x = 0;
888
  if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
889 890 891
  width = x2 - x;
  if (!width) return;

892
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
893 894
    return;

895 896 897 898 899
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, width, 1, TRUE);
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
900 901 902 903 904 905 906 907 908 909 910 911 912 913
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data + value;
	  if (val > 255)
	    val = 255;
	  *data++ = val;
	}
    }
}

void
914 915 916 917 918
channel_sub_segment (Channel *mask,
		     gint     x,
		     gint     y,
		     gint     width,
		     gint     value)
Elliot Lee's avatar
Elliot Lee committed
919 920
{
  PixelRegion maskPR;
921 922 923
  guchar *data;
  gint val;
  gint x2;
924
  gpointer pr;
Elliot Lee's avatar
Elliot Lee committed
925 926 927 928

  /*  check horizontal extents...  */
  x2 = x + width;
  if (x2 < 0) x2 = 0;
929
  if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
930
  if (x < 0) x = 0;
931
  if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
932 933 934
  width = x2 - x;
  if (!width) return;

935
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
936 937
    return;

938 939 940 941
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles, x, y, width, 1, TRUE);
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
942 943 944 945 946 947 948 949 950 951 952 953 954 955
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data - value;
	  if (val < 0)
	    val = 0;
	  *data++ = val;
	}
    }
}

void
956 957 958 959 960 961
channel_combine_rect (Channel    *mask,
		      ChannelOps  op,
		      gint        x,
		      gint        y,
		      gint        w,
		      gint        h)
Elliot Lee's avatar
Elliot Lee committed
962
{
963
  gint x2, y2;
Elliot Lee's avatar
Elliot Lee committed
964
  PixelRegion maskPR;
965 966
  guchar color;

967 968
  y2 = y + h;
  x2 = x + w;
Elliot Lee's avatar
Elliot Lee committed
969

970 971 972 973
  x  = CLAMP (x,  0, GIMP_DRAWABLE (mask)->width);
  y  = CLAMP (y,  0, GIMP_DRAWABLE (mask)->height);
  x2 = CLAMP (x2, 0, GIMP_DRAWABLE (mask)->width);
  y2 = CLAMP (y2, 0, GIMP_DRAWABLE (mask)->height);
Elliot Lee's avatar
Elliot Lee committed
974

975
  if (x2 - x <= 0 || y2 - y <= 0)
Elliot Lee's avatar
Elliot Lee committed
976 977
    return;

978 979
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, x2 - x, y2 - y, TRUE);
980 981 982 983
  if (op == ADD  || op == REPLACE)
    color = 255;
  else
    color = 0;
984
  color_region (&maskPR, &color);
Elliot Lee's avatar
Elliot Lee committed
985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008

  /*  Determine new boundary  */
  if (mask->bounds_known && (op == ADD) && !mask->empty)
    {
      if (x < mask->x1)
	mask->x1 = x;
      if (y < mask->y1)
	mask->y1 = y;
      if ((x + w) > mask->x2)
	mask->x2 = (x + w);
      if ((y + h) > mask->y2)
	mask->y2 = (y + h);
    }
  else if (op == REPLACE || mask->empty)
    {
      mask->empty = FALSE;
      mask->x1 = x;
      mask->y1 = y;
      mask->x2 = x + w;
      mask->y2 = y + h;
    }
  else
    mask->bounds_known = FALSE;

1009 1010 1011 1012
  mask->x1 = CLAMP (mask->x1, 0, GIMP_DRAWABLE (mask)->width);
  mask->y1 = CLAMP (mask->y1, 0, GIMP_DRAWABLE (mask)->height);
  mask->x2 = CLAMP (mask->x2, 0, GIMP_DRAWABLE (mask)->width);
  mask->y2 = CLAMP (mask->y2, 0, GIMP_DRAWABLE (mask)->height);
Elliot Lee's avatar
Elliot Lee committed
1013 1014 1015
}

void
1016