gimpchannel.c 41 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
#include "appenv.h"
29
#include "boundary.h"
Elliot Lee's avatar
Elliot Lee committed
30 31
#include "channel.h"
#include "drawable.h"
32
#include "gdisplay.h"
33
#include "gimpimage.h"
Elliot Lee's avatar
Elliot Lee committed
34
#include "gimage_mask.h"
35 36
#include "gimppreviewcache.h"
#include "gimplut.h"
Elliot Lee's avatar
Elliot Lee committed
37 38
#include "layer.h"
#include "paint_funcs.h"
39
#include "parasitelist.h"
40 41 42
#include "pixel_processor.h"
#include "pixel_region.h"
#include "lut_funcs.h"
Elliot Lee's avatar
Elliot Lee committed
43
#include "temp_buf.h"
scott's avatar
scott committed
44
#include "tile.h"
45 46
#include "tile_manager.h"
#include "undo.h"
47

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

#include "libgimp/gimpintl.h"


53 54 55
static void      gimp_channel_class_init (GimpChannelClass *klass);
static void      gimp_channel_init       (GimpChannel      *channel);
static void      gimp_channel_destroy    (GtkObject        *object);
56

57 58 59 60
static TempBuf * channel_preview_private (Channel *channel,
					  gint     width,
					  gint     height);

61 62 63

static GimpDrawableClass *parent_class = NULL;

64

65
GtkType
66
gimp_channel_get_type (void)
67
{
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
      channel_type = gtk_type_unique (GIMP_TYPE_DRAWABLE, &channel_info);
85 86 87 88 89 90
    }

  return channel_type;
}

static void
91
gimp_channel_class_init (GimpChannelClass *klass)
92 93 94
{
  GtkObjectClass *object_class;

95 96 97
  object_class = (GtkObjectClass *) klass;

  parent_class = gtk_type_class (GIMP_TYPE_DRAWABLE);
98 99 100 101 102 103 104 105

  object_class->destroy = gimp_channel_destroy;
}

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

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

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

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

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

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

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

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

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

  return channel;
}

scott's avatar
scott committed
159
Channel *
160
channel_copy (const Channel *channel)
Elliot Lee's avatar
Elliot Lee committed
161
{
162 163 164 165 166 167 168
  gchar       *channel_name;
  Channel     *new_channel;
  PixelRegion  srcPR, destPR;
  gchar       *ext;
  gint         number;
  const gchar *name;
  gint         len;
Elliot Lee's avatar
Elliot Lee committed
169 170

  /*  formulate the new channel name  */
171 172
  name = gimp_object_get_name (GIMP_OBJECT (channel));

173
  ext = strrchr (name, '#');
174
  len = strlen (_("copy"));
175 176 177 178
  if ((strlen (name) >= len &&
       strcmp (&name[strlen (name) - len], _("copy")) == 0) ||
      (ext && (number = atoi (ext + 1)) > 0 && 
       ((int)(log10 (number) + 1)) == strlen (ext + 1)))
179 180
    /* don't have redundant "copy"s */
    channel_name = g_strdup (name);
181
  else
182
    channel_name = g_strdup_printf (_("%s copy"), name);
Elliot Lee's avatar
Elliot Lee committed
183 184

  /*  allocate a new channel object  */
185 186 187
  new_channel = channel_new (GIMP_DRAWABLE (channel)->gimage, 
			     GIMP_DRAWABLE (channel)->width, 
			     GIMP_DRAWABLE (channel)->height, 
188
			     channel_name, channel->opacity, channel->col);
189
  GIMP_DRAWABLE (new_channel)->visible = GIMP_DRAWABLE (channel)->visible;
Elliot Lee's avatar
Elliot Lee committed
190 191 192
  new_channel->show_masked = channel->show_masked;

  /*  copy the contents across channels  */
193 194 195 196 197 198 199
  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
200 201
  copy_region (&srcPR, &destPR);

202
  /* copy the parasites */
203 204
  GIMP_DRAWABLE (new_channel)->parasites =
    parasite_list_copy (GIMP_DRAWABLE (channel)->parasites);
205

Elliot Lee's avatar
Elliot Lee committed
206 207 208 209 210 211
  /*  free up the channel_name memory  */
  g_free (channel_name);

  return new_channel;
}

212
void
213 214
channel_set_name (Channel     *channel,
                  const gchar *name)
215
{
216
  gimp_object_set_name (GIMP_OBJECT (channel), name);
217 218
}

219 220
const gchar *
channel_get_name (const Channel *channel)
221
{
222
  return gimp_object_get_name (GIMP_OBJECT (channel));
223 224
}

225
void 
226 227
channel_set_color (Channel      *channel,
		   const guchar *color)
228
{
229 230
  gint i;

231
  if (color)
232
    {
233 234
      for (i = 0; i < 3; i++)
	channel->col[i] = color[i];
235
    }
236
}
237

238 239
const guchar *
channel_get_color (const Channel *channel)
240
{
241
  return GIMP_CHANNEL (channel)->col;
242 243
}
 
244 245
gint
channel_get_opacity (const Channel *channel)
246 247 248 249
{ 
  return channel->opacity;
}

250
void 
251 252
channel_set_opacity (Channel *channel,
		     gint     opacity)
253 254
{
  if (opacity >=0 && opacity <= 100)
255
    channel->opacity = (gint) (opacity * 255) / 100;
256 257
}

Elliot Lee's avatar
Elliot Lee committed
258 259 260
void
channel_delete (Channel *channel)
{
261 262 263 264 265 266
  /*  Channels are normally deleted by removing them from the associated
      image. The only case where channel_delete() is useful is if you want
      to remove a floating channel object that has not been added to an
      image yet. We use gtk_object_sink() for this reason here.
   */
  gtk_object_sink (GTK_OBJECT (channel));
267 268 269 270 271 272 273 274 275 276 277 278
}

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
279 280 281 282 283 284
  /* free the segments?  */
  if (channel->segs_in)
    g_free (channel->segs_in);
  if (channel->segs_out)
    g_free (channel->segs_out);

285
  if (GTK_OBJECT_CLASS (parent_class)->destroy)
286
    GTK_OBJECT_CLASS (parent_class)->destroy (object);
Elliot Lee's avatar
Elliot Lee committed
287 288 289
}

void
290 291 292
channel_scale (Channel *channel,
	       gint     new_width,
	       gint     new_height)
Elliot Lee's avatar
Elliot Lee committed
293
{
294
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
295 296 297 298 299 300
  TileManager *new_tiles;

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

  /*  Update the old channel position  */
301 302 303 304
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
305 306

  /*  Configure the pixel regions  */
307 308 309 310
  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
311 312 313 314 315 316 317 318 319

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

  /*  Configure the new channel  */
323 324 325
  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
326 327 328 329 330

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

  /*  Update the new channel position  */
331 332 333 334
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
335 336 337
}

void
338 339 340 341 342
channel_resize (Channel *channel,
		gint     new_width,
		gint     new_height,
		gint     offx,
		gint     offy)
Elliot Lee's avatar
Elliot Lee committed
343
{
344
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
345
  TileManager *new_tiles;
346 347 348 349
  guchar       bg = 0;
  gint         clear;
  gint         w, h;
  gint         x1, y1, x2, y2;
Elliot Lee's avatar
Elliot Lee committed
350 351 352 353

  if (!new_width || !new_height)
    return;

354 355 356 357
  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
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
  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  */
384 385 386 387
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
388 389

  /*  Configure the pixel regions  */
390 391
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
		     x1, y1, w, h, FALSE);
Elliot Lee's avatar
Elliot Lee committed
392 393

  /*  Determine whether the new channel needs to be initially cleared  */
394 395
  if ((new_width  > GIMP_DRAWABLE (channel)->width) ||
      (new_height > GIMP_DRAWABLE (channel)->height) ||
Elliot Lee's avatar
Elliot Lee committed
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
      (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  */
417
  undo_push_channel_mod (GIMP_DRAWABLE (channel)->gimage, channel);
Elliot Lee's avatar
Elliot Lee committed
418 419

  /*  Configure the new channel  */
420 421 422
  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
423 424 425 426 427

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

  /*  update the new channel area  */
428 429 430 431
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
432 433
}

434 435 436
void            
channel_update (Channel *channel)
{
437 438 439 440 441
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
  gdisplays_flush ();
442 443
}

444 445 446
/**********************/
/*  access functions  */
/**********************/
Elliot Lee's avatar
Elliot Lee committed
447

448
gboolean
Elliot Lee's avatar
Elliot Lee committed
449 450
channel_toggle_visibility (Channel *channel)
{
451
  GIMP_DRAWABLE (channel)->visible = !GIMP_DRAWABLE (channel)->visible;
Elliot Lee's avatar
Elliot Lee committed
452

453
  return GIMP_DRAWABLE (channel)->visible;
Elliot Lee's avatar
Elliot Lee committed
454 455
}

456 457 458 459
TempBuf *
channel_preview (Channel *channel,
		 gint     width,
		 gint     height)
Elliot Lee's avatar
Elliot Lee committed
460
{
461 462
  /* Ok prime the cache with a large preview if the cache is invalid */
  if (! GIMP_DRAWABLE (channel)->preview_valid &&
463 464
      width  <= PREVIEW_CACHE_PRIME_WIDTH      &&
      height <= PREVIEW_CACHE_PRIME_HEIGHT     &&
465 466 467 468 469 470 471 472 473 474 475 476 477 478
      GIMP_DRAWABLE (channel)->gimage          &&
      GIMP_DRAWABLE (channel)->gimage->width  > PREVIEW_CACHE_PRIME_WIDTH   &&
      GIMP_DRAWABLE (channel)->gimage->height > PREVIEW_CACHE_PRIME_HEIGHT)
    {
      TempBuf * tb = channel_preview_private (channel,
					      PREVIEW_CACHE_PRIME_WIDTH,
					      PREVIEW_CACHE_PRIME_HEIGHT);
      
      /* Save the 2nd call */
      if (width  == PREVIEW_CACHE_PRIME_WIDTH &&
	  height == PREVIEW_CACHE_PRIME_HEIGHT)
	return tb;
    }

479
  /* Second call - should NOT visit the tile cache... */
480 481 482 483 484 485 486 487 488 489 490 491 492
  return channel_preview_private (channel, width, height);
}

static TempBuf *
channel_preview_private (Channel *channel,
			 gint     width,
			 gint     height)
{
  MaskBuf     *preview_buf;
  PixelRegion  srcPR;
  PixelRegion  destPR;
  gint         subsample;
  TempBuf     *ret_buf;
Elliot Lee's avatar
Elliot Lee committed
493

494 495 496
  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
497
  /*  The easy way  */
498 499 500 501
  if (GIMP_DRAWABLE (channel)->preview_valid &&
      (ret_buf =
       gimp_preview_cache_get (& (GIMP_DRAWABLE (channel)->preview_cache),
			       width, height)))
502 503 504
    {
      return ret_buf;
    }
Elliot Lee's avatar
Elliot Lee committed
505 506 507 508 509
  /*  The hard way  */
  else
    {
      /*  calculate 'acceptable' subsample  */
      subsample = 1;
510 511
      if (width < 1) width = 1;
      if (height < 1) height = 1;
512 513
      while ((width  * (subsample + 1) * 2 < GIMP_DRAWABLE (channel)->width) &&
	     (height * (subsample + 1) * 2 < GIMP_DRAWABLE (channel)->height))
Elliot Lee's avatar
Elliot Lee committed
514 515
	subsample = subsample + 1;

516 517 518 519
      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
520 521

      preview_buf = mask_buf_new (width, height);
522 523 524 525 526
      destPR.bytes     = 1;
      destPR.x         = 0;
      destPR.y         = 0;
      destPR.w         = width;
      destPR.h         = height;
Elliot Lee's avatar
Elliot Lee committed
527
      destPR.rowstride = width;
528
      destPR.data      = mask_buf_data (preview_buf);
Elliot Lee's avatar
Elliot Lee committed
529 530 531

      subsample_region (&srcPR, &destPR, subsample);

532 533 534 535 536 537
      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);
538
      return preview_buf;
Elliot Lee's avatar
Elliot Lee committed
539 540 541 542
    }
}

void
543
channel_invalidate_previews (GimpImage *gimage)
Elliot Lee's avatar
Elliot Lee committed
544
{
545 546
  GSList  *tmp;
  Channel *channel;
547

Sven Neumann's avatar
Sven Neumann committed
548 549
  g_return_if_fail (gimage != NULL);

550
  tmp = gimage->channels;
Elliot Lee's avatar
Elliot Lee committed
551 552 553 554

  while (tmp)
    {
      channel = (Channel *) tmp->data;
555
      gimp_drawable_invalidate_preview (GIMP_DRAWABLE (channel), TRUE);
556
      tmp = g_slist_next (tmp);
Elliot Lee's avatar
Elliot Lee committed
557 558 559
    }
}

560
Tattoo
561
channel_get_tattoo (const Channel *channel)
562
{
563
  return gimp_drawable_get_tattoo (GIMP_DRAWABLE (channel));
564 565
}

566
void
567 568
channel_set_tattoo (Channel *channel, 
		    Tattoo   value)
569
{
570
  gimp_drawable_set_tattoo (GIMP_DRAWABLE (channel), value);
571 572
}

573 574 575
/******************************/
/*  selection mask functions  */
/******************************/
Elliot Lee's avatar
Elliot Lee committed
576 577

Channel *
578
channel_new_mask (GimpImage *gimage,
579 580
		  gint       width,
		  gint       height)
Elliot Lee's avatar
Elliot Lee committed
581
{
582
  guchar   black[3] = { 0, 0, 0 };
Elliot Lee's avatar
Elliot Lee committed
583 584 585
  Channel *new_channel;

  /*  Create the new channel  */
586 587
  new_channel = channel_new (gimage, width, height,
			     _("Selection Mask"), 127, black);
Elliot Lee's avatar
Elliot Lee committed
588 589

  /*  Set the validate procedure  */
590 591
  tile_manager_set_validate_proc (GIMP_DRAWABLE (new_channel)->tiles,
				  channel_validate);
Elliot Lee's avatar
Elliot Lee committed
592 593 594 595

  return new_channel;
}

596 597 598 599 600 601 602 603 604 605
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
606
{
607
  gint        x3, y3, x4, y4;
Elliot Lee's avatar
Elliot Lee committed
608 609 610 611 612 613 614 615 616 617 618 619
  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))
	{
620 621
	  pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles,
			     x3, y3, (x4 - x3), (y4 - y3), FALSE);
Elliot Lee's avatar
Elliot Lee committed
622 623 624 625
	  mask->segs_out = find_mask_boundary (&bPR, &mask->num_segs_out,
					       IgnoreBounds,
					       x1, y1,
					       x2, y2);
626 627 628 629
	  x1 = MAX (x1, x3);
	  y1 = MAX (y1, y3);
	  x2 = MIN (x2, x4);
	  y2 = MIN (y2, y4);
Elliot Lee's avatar
Elliot Lee committed
630 631 632

	  if (x2 > x1 && y2 > y1)
	    {
633 634 635 636
	      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
637 638 639 640 641 642 643
	      mask->segs_in =  find_mask_boundary (&bPR, &mask->num_segs_in,
						   WithinBounds,
						   x1, y1,
						   x2, y2);
	    }
	  else
	    {
644
	      mask->segs_in     = NULL;
Elliot Lee's avatar
Elliot Lee committed
645 646 647 648 649
	      mask->num_segs_in = 0;
	    }
	}
      else
	{
650 651 652
	  mask->segs_in      = NULL;
	  mask->segs_out     = NULL;
	  mask->num_segs_in  = 0;
Elliot Lee's avatar
Elliot Lee committed
653 654 655 656 657
	  mask->num_segs_out = 0;
	}
      mask->boundary_known = TRUE;
    }

658 659 660
  *segs_in      = mask->segs_in;
  *segs_out     = mask->segs_out;
  *num_segs_in  = mask->num_segs_in;
Elliot Lee's avatar
Elliot Lee committed
661 662 663 664 665
  *num_segs_out = mask->num_segs_out;

  return TRUE;
}

666 667 668 669
gint
channel_value (Channel *mask,
	       gint     x,
	       gint     y)
Elliot Lee's avatar
Elliot Lee committed
670 671
{
  Tile *tile;
672
  gint  val;
Elliot Lee's avatar
Elliot Lee committed
673 674 675 676 677 678 679 680 681 682 683

  /*  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
    {
684 685
      if (x < 0 || x >= GIMP_DRAWABLE (mask)->width ||
	  y < 0 || y >= GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
686 687 688
	return 0;
    }

689 690
  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));
691
  tile_release (tile, FALSE);
Elliot Lee's avatar
Elliot Lee committed
692 693 694 695

  return val;
}

696 697 698 699 700 701
gboolean
channel_bounds (Channel *mask,
		gint    *x1,
		gint    *y1,
		gint    *x2,
		gint    *y2)
Elliot Lee's avatar
Elliot Lee committed
702
{
703 704 705 706 707 708 709
  PixelRegion  maskPR;
  guchar      *data, *data1;
  gint         x, y;
  gint         ex, ey;
  gint         tx1, tx2, ty1, ty2;
  gint         minx, maxx;
  gpointer     pr;
710

Elliot Lee's avatar
Elliot Lee committed
711 712 713 714 715 716 717 718
  /*  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;

719
      return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
720 721 722
    }

  /*  go through and calculate the bounds  */
723 724
  tx1 = GIMP_DRAWABLE (mask)->width;
  ty1 = GIMP_DRAWABLE (mask)->height;
725 726
  tx2 = 0;
  ty2 = 0;
Elliot Lee's avatar
Elliot Lee committed
727

728 729 730 731 732 733 734
  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
735
    {
736
      data1 = data = maskPR.data;
Elliot Lee's avatar
Elliot Lee committed
737 738
      ex = maskPR.x + maskPR.w;
      ey = maskPR.y + maskPR.h;
739 740
      /* only check the pixels if this tile is not fully within the currently
	 computed bounds */
741 742
      if (maskPR.x < tx1 || ex > tx2 ||
	  maskPR.y < ty1 || ey > ty2)
743
        {
744 745 746 747 748 749 750 751 752 753 754 755 756 757
	  /* 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
758
	    for (y = maskPR.y; y < ey; y++, data1 += maskPR.rowstride)
Elliot Lee's avatar
Elliot Lee committed
759
	    {
760
	      for (x = maskPR.x, data = data1; x < ex; x++, data++)
761
		if (*data)
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
		{
		  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
777 778 779 780
	    }
	}
    }

781 782
  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
783

784
  if (tx1 == GIMP_DRAWABLE (mask)->width && ty1 == GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
785 786
    {
      mask->empty = TRUE;
787 788 789 790
      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
791 792 793 794
    }
  else
    {
      mask->empty = FALSE;
795 796 797 798
      mask->x1    = tx1;
      mask->y1    = ty1;
      mask->x2    = tx2;
      mask->y2    = ty2;
Elliot Lee's avatar
Elliot Lee committed
799 800 801
    }
  mask->bounds_known = TRUE;

802 803 804 805 806
  *x1 = tx1;
  *x2 = tx2;
  *y1 = ty1;
  *y2 = ty2;

807
  return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
808 809
}

810
gboolean
Elliot Lee's avatar
Elliot Lee committed
811 812
channel_is_empty (Channel *mask)
{
813 814 815 816
  PixelRegion  maskPR;
  guchar      *data;
  gint         x, y;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
817 818 819 820

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

821 822 823 824 825 826 827
  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
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845
    {
      /*  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);

846 847 848 849 850 851
  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
852
  mask->boundary_known = TRUE;
853 854 855 856
  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
857 858 859 860 861

  return TRUE;
}

void
862 863 864 865 866
channel_add_segment (Channel *mask,
		     gint     x,
		     gint     y,
		     gint     width,
		     gint     value)
Elliot Lee's avatar
Elliot Lee committed
867
{
868 869 870 871 872
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
873 874 875 876

  /*  check horizontal extents...  */
  x2 = x + width;
  if (x2 < 0) x2 = 0;
877
  if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
878
  if (x < 0) x = 0;
879
  if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
880 881 882
  width = x2 - x;
  if (!width) return;

883
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
884 885
    return;

886 887 888 889 890
  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
891 892 893 894 895 896 897 898 899 900 901 902 903 904
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data + value;
	  if (val > 255)
	    val = 255;
	  *data++ = val;
	}
    }
}

void
905 906 907 908 909
channel_sub_segment (Channel *mask,
		     gint     x,
		     gint     y,
		     gint     width,
		     gint     value)
Elliot Lee's avatar
Elliot Lee committed
910
{
911 912 913 914 915
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
916 917 918 919

  /*  check horizontal extents...  */
  x2 = x + width;
  if (x2 < 0) x2 = 0;
920
  if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
921
  if (x < 0) x = 0;
922
  if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
923 924 925
  width = x2 - x;
  if (!width) return;

926
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
927 928
    return;

929 930 931 932
  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
933 934 935 936 937 938 939 940 941 942 943 944 945 946
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data - value;
	  if (val < 0)
	    val = 0;
	  *data++ = val;
	}
    }
}

void
947 948 949 950 951 952
channel_combine_rect (Channel    *mask,
		      ChannelOps  op,
		      gint        x,
		      gint        y,
		      gint        w,
		      gint        h)
Elliot Lee's avatar
Elliot Lee committed
953
{
954
  gint        x2, y2;
Elliot Lee's avatar
Elliot Lee committed
955
  PixelRegion maskPR;
956
  guchar      color;
957

958 959
  y2 = y + h;
  x2 = x + w;
Elliot Lee's avatar
Elliot Lee committed
960

961 962 963 964
  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
965

966
  if (x2 - x <= 0 || y2 - y <= 0)
Elliot Lee's avatar
Elliot Lee committed
967 968
    return;

969 970
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, x2 - x, y2 - y, TRUE);
971
  if (op == CHANNEL_OP_ADD  || op == CHANNEL_OP_REPLACE)
972 973 974
    color = 255;
  else
    color = 0;
975
  color_region (&maskPR, &color);
Elliot Lee's avatar
Elliot Lee committed
976 977

  /*  Determine new boundary  */
978
  if (mask->bounds_known && (op == CHANNEL_OP_ADD) && !mask->empty)
Elliot Lee's avatar
Elliot Lee committed
979 980 981 982 983 984 985 986 987 988
    {
      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);
    }
989
  else if (op == CHANNEL_OP_REPLACE || mask->empty)
Elliot Lee's avatar
Elliot Lee committed
990 991 992 993 994 995 996 997 998 999
    {
      mask->empty = FALSE;
      mask->x1 = x;
      mask->y1 = y;
      mask->x2 = x + w;
      mask->y2 = y + h;
    }
  else
    mask->bounds_known = FALSE;

1000 1001 1002 1003
  mask->x1 = CLAMP (mask->x1, 0, GIMP_DRAWABLE (mask)->width);
  mask->y1 = CLAMP (mask->y1, 0, GIMP_DRAWABLE (mask)->height);
  mask->x2 = CLAM