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

Sven Neumann's avatar
Sven Neumann committed
24 25 26 27
#include <gtk/gtk.h>

#include "apptypes.h"

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

41
#include "channel_pvt.h"
scott's avatar
scott committed
42
#include "tile.h"
43

44 45 46
#include "gimplut.h"
#include "lut_funcs.h"

47 48 49 50 51
#include "libgimp/gimpmath.h"

#include "libgimp/gimpintl.h"


52
enum {
53
  REMOVED,
54 55 56 57 58 59 60
  LAST_SIGNAL
};

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

61
static guint channel_signals[LAST_SIGNAL] = { 0 };
62 63 64

static GimpDrawableClass *parent_class = NULL;

65
GtkType
66 67
gimp_channel_get_type ()
{
68
  static GtkType channel_type = 0;
69 70 71 72 73 74 75 76 77 78

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

      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 ());

98 99 100 101
  channel_signals[REMOVED] =
	  gimp_signal_new ("removed",
			   0, object_class->type, 0, gimp_sigtype_void);

102 103 104 105 106 107 108 109 110
  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
111 112 113

/**************************/
/*  Function definitions  */
114
/**************************/
Elliot Lee's avatar
Elliot Lee committed
115 116

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

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

136
  channel = gtk_type_new (gimp_channel_get_type ());
Elliot Lee's avatar
Elliot Lee committed
137

138
  gimp_drawable_configure (GIMP_DRAWABLE (channel), 
139
			   gimage, width, height, GRAY_GIMAGE, name);
Elliot Lee's avatar
Elliot Lee committed
140 141 142 143

  /*  set the channel color and opacity  */
  for (i = 0; i < 3; i++)
    channel->col[i] = col[i];
144 145 146

  channel->opacity     = opacity;
  channel->show_masked = TRUE;
Elliot Lee's avatar
Elliot Lee committed
147 148

  /*  selection mask variables  */
149 150 151 152 153 154
  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
155
  channel->boundary_known = TRUE;
156 157 158 159
  channel->x1             = 0;
  channel->y1             = 0;
  channel->x2             = width;
  channel->y2             = height;
Elliot Lee's avatar
Elliot Lee committed
160 161 162 163

  return channel;
}

scott's avatar
scott committed
164 165 166 167 168
Channel *
channel_ref (Channel *channel)
{
  gtk_object_ref  (GTK_OBJECT (channel));
  gtk_object_sink (GTK_OBJECT (channel));
169

scott's avatar
scott committed
170 171 172 173 174 175 176 177 178
  return channel;
}

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

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

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

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

  /*  copy the contents across channels  */
212 213 214 215 216 217 218
  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
219 220
  copy_region (&srcPR, &destPR);

221
  /* copy the parasites */
222 223
  GIMP_DRAWABLE (new_channel)->parasites 
    = parasite_list_copy (GIMP_DRAWABLE (channel)->parasites);
224

Elliot Lee's avatar
Elliot Lee committed
225 226 227 228 229 230
  /*  free up the channel_name memory  */
  g_free (channel_name);

  return new_channel;
}

231
void
232 233
channel_set_name (Channel *channel,
		  gchar   *name)
234
{
235
  gimp_drawable_set_name (GIMP_DRAWABLE (channel), name);
236 237
}

238
gchar *
239 240
channel_get_name (Channel *channel)
{
241
  return gimp_drawable_get_name (GIMP_DRAWABLE (channel));
242 243
}

244
void 
245
channel_set_color (Channel *channel,
246
		   guchar  *color)
247
{
248 249
  gint i;

250 251
  if (color)
    {  
252 253
      for (i = 0; i < 3; i++)
	channel->col[i] = color[i];
254
    }
255
}
256

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

269
void 
270 271
channel_set_opacity (Channel *channel,
		     gint     opacity)
272 273
{
  if (opacity >=0 && opacity <= 100)
274
    channel->opacity = (gint) (opacity * 255) / 100;
275 276
}

Elliot Lee's avatar
Elliot Lee committed
277
Channel *
278
channel_get_ID (gint ID)
Elliot Lee's avatar
Elliot Lee committed
279
{
280
  GimpDrawable *drawable;
281

282 283 284 285 286
  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
287 288 289 290 291
}

void
channel_delete (Channel *channel)
{
scott's avatar
scott committed
292
  gtk_object_unref (GTK_OBJECT (channel));
293 294 295 296 297 298 299 300 301 302 303 304
}

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
305 306 307 308 309 310
  /* free the segments?  */
  if (channel->segs_in)
    g_free (channel->segs_in);
  if (channel->segs_out)
    g_free (channel->segs_out);

311
  if (GTK_OBJECT_CLASS (parent_class)->destroy)
312
    (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
313
  
Elliot Lee's avatar
Elliot Lee committed
314 315
}

316 317 318 319 320 321 322
/* 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,
323
		 gpointer  data)
324 325 326 327 328 329 330 331
{
  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
332
void
333 334 335
channel_scale (Channel *channel,
	       gint     new_width,
	       gint     new_height)
Elliot Lee's avatar
Elliot Lee committed
336 337 338 339 340 341 342 343
{
  PixelRegion srcPR, destPR;
  TileManager *new_tiles;

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

  /*  Update the old channel position  */
344 345 346 347
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
348 349

  /*  Configure the pixel regions  */
350 351 352 353
  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
354 355 356 357 358 359 360 361 362

  /*  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  */
363
  undo_push_channel_mod (GIMP_DRAWABLE (channel)->gimage, channel);
Elliot Lee's avatar
Elliot Lee committed
364 365

  /*  Configure the new channel  */
366 367 368
  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
369 370 371 372 373

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

  /*  Update the new channel position  */
374 375 376 377
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
378 379 380
}

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

  if (!new_width || !new_height)
    return;

397 398 399 400
  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
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
  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  */
427 428 429 430
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
431 432

  /*  Configure the pixel regions  */
433 434
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
		     x1, y1, w, h, FALSE);
Elliot Lee's avatar
Elliot Lee committed
435 436

  /*  Determine whether the new channel needs to be initially cleared  */
437 438
  if ((new_width  > GIMP_DRAWABLE (channel)->width) ||
      (new_height > GIMP_DRAWABLE (channel)->height) ||
Elliot Lee's avatar
Elliot Lee committed
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
      (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  */
460
  undo_push_channel_mod (GIMP_DRAWABLE (channel)->gimage, channel);
Elliot Lee's avatar
Elliot Lee committed
461 462

  /*  Configure the new channel  */
463 464 465
  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
466 467 468 469 470

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

  /*  update the new channel area  */
471 472 473 474
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
475 476
}

477 478 479
void            
channel_update (Channel *channel)
{
480 481 482 483 484
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
  gdisplays_flush ();
485 486
}

487 488 489
/**********************/
/*  access functions  */
/**********************/
Elliot Lee's avatar
Elliot Lee committed
490

491
gboolean
Elliot Lee's avatar
Elliot Lee committed
492 493
channel_toggle_visibility (Channel *channel)
{
494
  GIMP_DRAWABLE (channel)->visible = !GIMP_DRAWABLE (channel)->visible;
Elliot Lee's avatar
Elliot Lee committed
495

496
  return GIMP_DRAWABLE (channel)->visible;
Elliot Lee's avatar
Elliot Lee committed
497 498
}

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

509 510 511
  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
512
  /*  The easy way  */
513 514 515 516
  if (GIMP_DRAWABLE (channel)->preview_valid &&
      (ret_buf =
       gimp_preview_cache_get (& (GIMP_DRAWABLE (channel)->preview_cache),
			       width, height)))
517
    return ret_buf;
Elliot Lee's avatar
Elliot Lee committed
518 519 520 521 522
  /*  The hard way  */
  else
    {
      /*  calculate 'acceptable' subsample  */
      subsample = 1;
523 524
      if (width < 1) width = 1;
      if (height < 1) height = 1;
525 526
      while ((width  * (subsample + 1) * 2 < GIMP_DRAWABLE (channel)->width) &&
	     (height * (subsample + 1) * 2 < GIMP_DRAWABLE (channel)->height))
Elliot Lee's avatar
Elliot Lee committed
527 528
	subsample = subsample + 1;

529 530 531 532
      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
533 534

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

      subsample_region (&srcPR, &destPR, subsample);

545 546 547 548 549 550
      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);
551
      return preview_buf;
Elliot Lee's avatar
Elliot Lee committed
552 553 554 555
    }
}

void
556
channel_invalidate_previews (GimpImage* gimage)
Elliot Lee's avatar
Elliot Lee committed
557
{
558
  GSList * tmp;
Elliot Lee's avatar
Elliot Lee committed
559
  Channel * channel;
560

Sven Neumann's avatar
Sven Neumann committed
561 562
  g_return_if_fail (gimage != NULL);

563
  tmp = gimage->channels;
Elliot Lee's avatar
Elliot Lee committed
564 565 566 567

  while (tmp)
    {
      channel = (Channel *) tmp->data;
568
      gimp_drawable_invalidate_preview (GIMP_DRAWABLE (channel), TRUE);
569
      tmp = g_slist_next (tmp);
Elliot Lee's avatar
Elliot Lee committed
570 571 572
    }
}

573
Tattoo
574
channel_get_tattoo (const Channel *channel)
575
{
576
  return (gimp_drawable_get_tattoo (GIMP_DRAWABLE (channel)));
577 578
}

579
void
580 581
channel_set_tattoo (const Channel *channel, 
		    Tattoo         value)
582
{
583
  gimp_drawable_set_tattoo (GIMP_DRAWABLE (channel), value);
584 585
}

586 587 588
/******************************/
/*  selection mask functions  */
/******************************/
Elliot Lee's avatar
Elliot Lee committed
589 590

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

  /*  Create the new channel  */
599 600
  new_channel = channel_new (gimage, width, height,
			     _("Selection Mask"), 127, black);
Elliot Lee's avatar
Elliot Lee committed
601 602

  /*  Set the validate procedure  */
603 604
  tile_manager_set_validate_proc (GIMP_DRAWABLE (new_channel)->tiles,
				  channel_validate);
Elliot Lee's avatar
Elliot Lee committed
605 606 607 608

  return new_channel;
}

609 610 611 612 613 614 615 616 617 618
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
619
{
620
  gint x3, y3, x4, y4;
Elliot Lee's avatar
Elliot Lee committed
621 622 623 624 625 626 627 628 629 630 631 632
  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))
	{
633 634
	  pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles,
			     x3, y3, (x4 - x3), (y4 - y3), FALSE);
Elliot Lee's avatar
Elliot Lee committed
635 636 637 638
	  mask->segs_out = find_mask_boundary (&bPR, &mask->num_segs_out,
					       IgnoreBounds,
					       x1, y1,
					       x2, y2);
639 640 641 642
	  x1 = MAX (x1, x3);
	  y1 = MAX (y1, y3);
	  x2 = MIN (x2, x4);
	  y2 = MIN (y2, y4);
Elliot Lee's avatar
Elliot Lee committed
643 644 645

	  if (x2 > x1 && y2 > y1)
	    {
646 647 648 649
	      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
650 651 652 653 654 655 656
	      mask->segs_in =  find_mask_boundary (&bPR, &mask->num_segs_in,
						   WithinBounds,
						   x1, y1,
						   x2, y2);
	    }
	  else
	    {
657
	      mask->segs_in     = NULL;
Elliot Lee's avatar
Elliot Lee committed
658 659 660 661 662
	      mask->num_segs_in = 0;
	    }
	}
      else
	{
663 664 665
	  mask->segs_in      = NULL;
	  mask->segs_out     = NULL;
	  mask->num_segs_in  = 0;
Elliot Lee's avatar
Elliot Lee committed
666 667 668 669 670
	  mask->num_segs_out = 0;
	}
      mask->boundary_known = TRUE;
    }

671 672 673
  *segs_in      = mask->segs_in;
  *segs_out     = mask->segs_out;
  *num_segs_in  = mask->num_segs_in;
Elliot Lee's avatar
Elliot Lee committed
674 675 676 677 678
  *num_segs_out = mask->num_segs_out;

  return TRUE;
}

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

  /*  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
    {
697 698
      if (x < 0 || x >= GIMP_DRAWABLE (mask)->width ||
	  y < 0 || y >= GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
699 700 701
	return 0;
    }

702 703
  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));
704
  tile_release (tile, FALSE);
Elliot Lee's avatar
Elliot Lee committed
705 706 707 708

  return val;
}

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

Elliot Lee's avatar
Elliot Lee committed
724 725 726 727 728 729 730 731
  /*  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;

732
      return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
733 734 735
    }

  /*  go through and calculate the bounds  */
736 737
  tx1 = GIMP_DRAWABLE (mask)->width;
  ty1 = GIMP_DRAWABLE (mask)->height;
738 739
  tx2 = 0;
  ty2 = 0;
Elliot Lee's avatar
Elliot Lee committed
740

741 742 743 744 745 746 747
  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
748
    {
749
      data1 = data = maskPR.data;
Elliot Lee's avatar
Elliot Lee committed
750 751
      ex = maskPR.x + maskPR.w;
      ey = maskPR.y + maskPR.h;
752 753
      /* only check the pixels if this tile is not fully within the currently
	 computed bounds */
754 755
      if (maskPR.x < tx1 || ex > tx2 ||
	  maskPR.y < ty1 || ey > ty2)
756
        {
757 758 759 760 761 762 763 764 765 766 767 768 769 770
	  /* 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
771
	    for (y = maskPR.y; y < ey; y++, data1 += maskPR.rowstride)
Elliot Lee's avatar
Elliot Lee committed
772
	    {
773
	      for (x = maskPR.x, data = data1; x < ex; x++, data++)
774
		if (*data)
775 776 777 778 779 780 781 782 783 784 785 786 787 788 789
		{
		  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
790 791 792 793
	    }
	}
    }

794 795
  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
796

797
  if (tx1 == GIMP_DRAWABLE (mask)->width && ty1 == GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
798 799
    {
      mask->empty = TRUE;
800 801 802 803
      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
804 805 806 807
    }
  else
    {
      mask->empty = FALSE;
808 809 810 811
      mask->x1    = tx1;
      mask->y1    = ty1;
      mask->x2    = tx2;
      mask->y2    = ty2;
Elliot Lee's avatar
Elliot Lee committed
812 813 814
    }
  mask->bounds_known = TRUE;

815 816 817 818 819
  *x1 = tx1;
  *x2 = tx2;
  *y1 = ty1;
  *y2 = ty2;

820
  return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
821 822
}

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

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

834 835 836 837 838 839 840
  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
841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858
    {
      /*  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);

859 860 861 862 863 864
  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
865
  mask->boundary_known = TRUE;
866 867 868 869
  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
870 871 872 873 874

  return TRUE;
}

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

  /*  check horizontal extents...  */
  x2 = x + width;
  if (x2 < 0) x2 = 0;
890
  if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
891
  if (x < 0) x = 0;
892
  if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
893 894 895
  width = x2 - x;
  if (!width) return;

896
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
897 898
    return;

899 900 901 902 903
  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
904 905 906 907 908 909 910 911 912 913 914 915 916 917
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data + value;
	  if (val > 255)
	    val = 255;
	  *data++ = val;
	}
    }
}

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

  /*  check horizontal extents...  */
  x2 = x + width;
  if (x2 < 0) x2 = 0;
933
  if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
934
  if (x < 0) x = 0;
935
  if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
936 937 938
  width = x2 - x;
  if (!width) return;

939
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
940 941
    return;

942 943 944 945
  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
946 947 948 949 950 951 952 953 954 955 956 957 958 959
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data - value;
	  if (val < 0)
	    val = 0;
	  *data++ = val;
	}
    }
}

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

971 972
  y2 = y + h;
  x2 = x + w;
Elliot Lee's avatar
Elliot Lee committed
973

974 975 976 977
  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
978

979
  if (x2 - x <= 0 || y2 - y <= 0)
Elliot Lee's avatar
Elliot Lee committed
980 981
    return;

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

  /*  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;

1013 1014 1015 1016
  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
1017