gimpchannel.c 41.7 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/* The GIMP -- an image manipulation program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
16
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
17
 */
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 53
enum
{
54
  REMOVED,
55 56 57
  LAST_SIGNAL
};

58 59 60
static void      gimp_channel_class_init (GimpChannelClass *klass);
static void      gimp_channel_init       (GimpChannel      *channel);
static void      gimp_channel_destroy    (GtkObject        *object);
61

62 63 64 65
static TempBuf * channel_preview_private (Channel *channel,
					  gint     width,
					  gint     height);

66
static guint channel_signals[LAST_SIGNAL] = { 0 };
67 68 69

static GimpDrawableClass *parent_class = NULL;

70
GtkType
71
gimp_channel_get_type (void)
72
{
73
  static GtkType channel_type = 0;
74 75 76 77 78 79 80 81 82 83

  if (!channel_type)
    {
      GtkTypeInfo channel_info =
      {
	"GimpChannel",
	sizeof (GimpChannel),
	sizeof (GimpChannelClass),
	(GtkClassInitFunc) gimp_channel_class_init,
	(GtkObjectInitFunc) gimp_channel_init,
84 85
        /* reserved_1 */ NULL,
	/* reserved_2 */ NULL,
86
	(GtkClassInitFunc) NULL,
87 88
      };

89
      channel_type = gtk_type_unique (GIMP_TYPE_DRAWABLE, &channel_info);
90 91 92 93 94 95 96 97 98 99 100 101 102
    }

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

103 104 105 106
  channel_signals[REMOVED] =
	  gimp_signal_new ("removed",
			   0, object_class->type, 0, gimp_sigtype_void);

107 108 109 110 111 112 113 114 115
  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
116 117 118

/**************************/
/*  Function definitions  */
119
/**************************/
Elliot Lee's avatar
Elliot Lee committed
120 121

static void
122 123
channel_validate (TileManager *tm,
		  Tile        *tile)
Elliot Lee's avatar
Elliot Lee committed
124 125
{
  /*  Set the contents of the tile to empty  */
scott's avatar
scott committed
126
  memset (tile_data_pointer (tile, 0, 0), 
127
	  TRANSPARENT_OPACITY, tile_size (tile));
Elliot Lee's avatar
Elliot Lee committed
128 129 130
}

Channel *
131 132 133 134 135 136
channel_new (GimpImage    *gimage,
	     gint          width,
	     gint          height,
	     const gchar  *name,
	     gint          opacity,
	     const guchar *col)
Elliot Lee's avatar
Elliot Lee committed
137 138
{
  Channel * channel;
139
  gint i;
Elliot Lee's avatar
Elliot Lee committed
140

141
  channel = gtk_type_new (gimp_channel_get_type ());
Elliot Lee's avatar
Elliot Lee committed
142

143
  gimp_drawable_configure (GIMP_DRAWABLE (channel), 
144
			   gimage, width, height, GRAY_GIMAGE, name);
Elliot Lee's avatar
Elliot Lee committed
145 146 147 148

  /*  set the channel color and opacity  */
  for (i = 0; i < 3; i++)
    channel->col[i] = col[i];
149 150 151

  channel->opacity     = opacity;
  channel->show_masked = TRUE;
Elliot Lee's avatar
Elliot Lee committed
152 153

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

  return channel;
}

scott's avatar
scott committed
169
Channel *
170
channel_copy (const Channel *channel)
Elliot Lee's avatar
Elliot Lee committed
171
{
172 173 174 175 176 177 178
  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
179 180

  /*  formulate the new channel name  */
181
  name = drawable_get_name (GIMP_DRAWABLE (channel));
182
  ext = strrchr (name, '#');
183
  len = strlen (_("copy"));
184 185 186 187
  if ((strlen (name) >= len &&
       strcmp (&name[strlen (name) - len], _("copy")) == 0) ||
      (ext && (number = atoi (ext + 1)) > 0 && 
       ((int)(log10 (number) + 1)) == strlen (ext + 1)))
188 189
    /* don't have redundant "copy"s */
    channel_name = g_strdup (name);
190
  else
191
    channel_name = g_strdup_printf (_("%s copy"), name);
Elliot Lee's avatar
Elliot Lee committed
192 193

  /*  allocate a new channel object  */
194 195 196
  new_channel = channel_new (GIMP_DRAWABLE (channel)->gimage, 
			     GIMP_DRAWABLE (channel)->width, 
			     GIMP_DRAWABLE (channel)->height, 
197
			     channel_name, channel->opacity, channel->col);
198
  GIMP_DRAWABLE (new_channel)->visible = GIMP_DRAWABLE (channel)->visible;
Elliot Lee's avatar
Elliot Lee committed
199 200 201
  new_channel->show_masked = channel->show_masked;

  /*  copy the contents across channels  */
202 203 204 205 206 207 208
  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
209 210
  copy_region (&srcPR, &destPR);

211
  /* copy the parasites */
212 213
  GIMP_DRAWABLE (new_channel)->parasites =
    parasite_list_copy (GIMP_DRAWABLE (channel)->parasites);
214

Elliot Lee's avatar
Elliot Lee committed
215 216 217 218 219 220
  /*  free up the channel_name memory  */
  g_free (channel_name);

  return new_channel;
}

221
void
222 223
channel_set_name (Channel     *channel,
                  const gchar *name)
224
{
225
  gimp_drawable_set_name (GIMP_DRAWABLE (channel), name);
226 227
}

228 229
const gchar *
channel_get_name (const Channel *channel)
230
{
231
  return gimp_drawable_get_name (GIMP_DRAWABLE (channel));
232 233
}

234
void 
235 236
channel_set_color (Channel      *channel,
		   const guchar *color)
237
{
238 239
  gint i;

240
  if (color)
241
    {
242 243
      for (i = 0; i < 3; i++)
	channel->col[i] = color[i];
244
    }
245
}
246

247 248
const guchar *
channel_get_color (const Channel *channel)
249
{
250
  return GIMP_CHANNEL (channel)->col;
251 252
}
 
253 254
gint
channel_get_opacity (const Channel *channel)
255 256 257 258
{ 
  return channel->opacity;
}

259
void 
260 261
channel_set_opacity (Channel *channel,
		     gint     opacity)
262 263
{
  if (opacity >=0 && opacity <= 100)
264
    channel->opacity = (gint) (opacity * 255) / 100;
265 266
}

Elliot Lee's avatar
Elliot Lee committed
267
Channel *
268
channel_get_ID (gint ID)
Elliot Lee's avatar
Elliot Lee committed
269
{
270
  GimpDrawable *drawable;
271

272
  drawable = drawable_get_ID (ID);
273

274 275 276 277
  if (drawable && GIMP_IS_CHANNEL (drawable)) 
    return GIMP_CHANNEL (drawable);
  else
    return NULL;
Elliot Lee's avatar
Elliot Lee committed
278 279 280 281 282
}

void
channel_delete (Channel *channel)
{
283 284 285 286 287 288
  /*  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));
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);
Elliot Lee's avatar
Elliot Lee committed
309 310
}

311 312 313 314 315 316
/* 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
317
channel_removed (Channel *channel)
318 319 320 321 322 323 324
{
  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
325
void
326 327 328
channel_scale (Channel *channel,
	       gint     new_width,
	       gint     new_height)
Elliot Lee's avatar
Elliot Lee committed
329
{
330
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
331 332 333 334 335 336
  TileManager *new_tiles;

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

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

  /*  Configure the pixel regions  */
343 344 345 346
  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
347 348 349 350 351 352 353 354 355

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

  /*  Configure the new channel  */
359 360 361
  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
362 363 364 365 366

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

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

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

  if (!new_width || !new_height)
    return;

390 391 392 393
  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
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
  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  */
420 421 422 423
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
424 425

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

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

  /*  Configure the new channel  */
456 457 458
  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
459 460 461 462 463

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

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

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

480 481 482
/**********************/
/*  access functions  */
/**********************/
Elliot Lee's avatar
Elliot Lee committed
483

484
gboolean
Elliot Lee's avatar
Elliot Lee committed
485 486
channel_toggle_visibility (Channel *channel)
{
487
  GIMP_DRAWABLE (channel)->visible = !GIMP_DRAWABLE (channel)->visible;
Elliot Lee's avatar
Elliot Lee committed
488

489
  return GIMP_DRAWABLE (channel)->visible;
Elliot Lee's avatar
Elliot Lee committed
490 491
}

492 493 494 495
TempBuf *
channel_preview (Channel *channel,
		 gint     width,
		 gint     height)
Elliot Lee's avatar
Elliot Lee committed
496
{
497 498
  /* Ok prime the cache with a large preview if the cache is invalid */
  if (! GIMP_DRAWABLE (channel)->preview_valid &&
499 500
      width  <= PREVIEW_CACHE_PRIME_WIDTH      &&
      height <= PREVIEW_CACHE_PRIME_HEIGHT     &&
501 502 503 504 505 506 507 508 509 510 511 512 513 514
      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;
    }

515
  /* Second call - should NOT visit the tile cache... */
516 517 518 519 520 521 522 523 524 525 526 527 528
  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
529

530 531 532
  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
533
  /*  The easy way  */
534 535 536 537
  if (GIMP_DRAWABLE (channel)->preview_valid &&
      (ret_buf =
       gimp_preview_cache_get (& (GIMP_DRAWABLE (channel)->preview_cache),
			       width, height)))
538 539 540
    {
      return ret_buf;
    }
Elliot Lee's avatar
Elliot Lee committed
541 542 543 544 545
  /*  The hard way  */
  else
    {
      /*  calculate 'acceptable' subsample  */
      subsample = 1;
546 547
      if (width < 1) width = 1;
      if (height < 1) height = 1;
548 549
      while ((width  * (subsample + 1) * 2 < GIMP_DRAWABLE (channel)->width) &&
	     (height * (subsample + 1) * 2 < GIMP_DRAWABLE (channel)->height))
Elliot Lee's avatar
Elliot Lee committed
550 551
	subsample = subsample + 1;

552 553 554 555
      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
556 557

      preview_buf = mask_buf_new (width, height);
558 559 560 561 562
      destPR.bytes     = 1;
      destPR.x         = 0;
      destPR.y         = 0;
      destPR.w         = width;
      destPR.h         = height;
Elliot Lee's avatar
Elliot Lee committed
563
      destPR.rowstride = width;
564
      destPR.data      = mask_buf_data (preview_buf);
Elliot Lee's avatar
Elliot Lee committed
565 566 567

      subsample_region (&srcPR, &destPR, subsample);

568 569 570 571 572 573
      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);
574
      return preview_buf;
Elliot Lee's avatar
Elliot Lee committed
575 576 577 578
    }
}

void
579
channel_invalidate_previews (GimpImage *gimage)
Elliot Lee's avatar
Elliot Lee committed
580
{
581 582
  GSList  *tmp;
  Channel *channel;
583

Sven Neumann's avatar
Sven Neumann committed
584 585
  g_return_if_fail (gimage != NULL);

586
  tmp = gimage->channels;
Elliot Lee's avatar
Elliot Lee committed
587 588 589 590

  while (tmp)
    {
      channel = (Channel *) tmp->data;
591
      gimp_drawable_invalidate_preview (GIMP_DRAWABLE (channel), TRUE);
592
      tmp = g_slist_next (tmp);
Elliot Lee's avatar
Elliot Lee committed
593 594 595
    }
}

596
Tattoo
597
channel_get_tattoo (const Channel *channel)
598
{
599
  return gimp_drawable_get_tattoo (GIMP_DRAWABLE (channel));
600 601
}

602
void
603 604
channel_set_tattoo (Channel *channel, 
		    Tattoo   value)
605
{
606
  gimp_drawable_set_tattoo (GIMP_DRAWABLE (channel), value);
607 608
}

609 610 611
/******************************/
/*  selection mask functions  */
/******************************/
Elliot Lee's avatar
Elliot Lee committed
612 613

Channel *
614
channel_new_mask (GimpImage *gimage,
615 616
		  gint       width,
		  gint       height)
Elliot Lee's avatar
Elliot Lee committed
617
{
618
  guchar   black[3] = { 0, 0, 0 };
Elliot Lee's avatar
Elliot Lee committed
619 620 621
  Channel *new_channel;

  /*  Create the new channel  */
622 623
  new_channel = channel_new (gimage, width, height,
			     _("Selection Mask"), 127, black);
Elliot Lee's avatar
Elliot Lee committed
624 625

  /*  Set the validate procedure  */
626 627
  tile_manager_set_validate_proc (GIMP_DRAWABLE (new_channel)->tiles,
				  channel_validate);
Elliot Lee's avatar
Elliot Lee committed
628 629 630 631

  return new_channel;
}

632 633 634 635 636 637 638 639 640 641
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
642
{
643
  gint        x3, y3, x4, y4;
Elliot Lee's avatar
Elliot Lee committed
644 645 646 647 648 649 650 651 652 653 654 655
  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))
	{
656 657
	  pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles,
			     x3, y3, (x4 - x3), (y4 - y3), FALSE);
Elliot Lee's avatar
Elliot Lee committed
658 659 660 661
	  mask->segs_out = find_mask_boundary (&bPR, &mask->num_segs_out,
					       IgnoreBounds,
					       x1, y1,
					       x2, y2);
662 663 664 665
	  x1 = MAX (x1, x3);
	  y1 = MAX (y1, y3);
	  x2 = MIN (x2, x4);
	  y2 = MIN (y2, y4);
Elliot Lee's avatar
Elliot Lee committed
666 667 668

	  if (x2 > x1 && y2 > y1)
	    {
669 670 671 672
	      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
673 674 675 676 677 678 679
	      mask->segs_in =  find_mask_boundary (&bPR, &mask->num_segs_in,
						   WithinBounds,
						   x1, y1,
						   x2, y2);
	    }
	  else
	    {
680
	      mask->segs_in     = NULL;
Elliot Lee's avatar
Elliot Lee committed
681 682 683 684 685
	      mask->num_segs_in = 0;
	    }
	}
      else
	{
686 687 688
	  mask->segs_in      = NULL;
	  mask->segs_out     = NULL;
	  mask->num_segs_in  = 0;
Elliot Lee's avatar
Elliot Lee committed
689 690 691 692 693
	  mask->num_segs_out = 0;
	}
      mask->boundary_known = TRUE;
    }

694 695 696
  *segs_in      = mask->segs_in;
  *segs_out     = mask->segs_out;
  *num_segs_in  = mask->num_segs_in;
Elliot Lee's avatar
Elliot Lee committed
697 698 699 700 701
  *num_segs_out = mask->num_segs_out;

  return TRUE;
}

702 703 704 705
gint
channel_value (Channel *mask,
	       gint     x,
	       gint     y)
Elliot Lee's avatar
Elliot Lee committed
706 707
{
  Tile *tile;
708
  gint  val;
Elliot Lee's avatar
Elliot Lee committed
709 710 711 712 713 714 715 716 717 718 719

  /*  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
    {
720 721
      if (x < 0 || x >= GIMP_DRAWABLE (mask)->width ||
	  y < 0 || y >= GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
722 723 724
	return 0;
    }

725 726
  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));
727
  tile_release (tile, FALSE);
Elliot Lee's avatar
Elliot Lee committed
728 729 730 731

  return val;
}

732 733 734 735 736 737
gboolean
channel_bounds (Channel *mask,
		gint    *x1,
		gint    *y1,
		gint    *x2,
		gint    *y2)
Elliot Lee's avatar
Elliot Lee committed
738
{
739 740 741 742 743 744 745
  PixelRegion  maskPR;
  guchar      *data, *data1;
  gint         x, y;
  gint         ex, ey;
  gint         tx1, tx2, ty1, ty2;
  gint         minx, maxx;
  gpointer     pr;
746

Elliot Lee's avatar
Elliot Lee committed
747 748 749 750 751 752 753 754
  /*  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;

755
      return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
756 757 758
    }

  /*  go through and calculate the bounds  */
759 760
  tx1 = GIMP_DRAWABLE (mask)->width;
  ty1 = GIMP_DRAWABLE (mask)->height;
761 762
  tx2 = 0;
  ty2 = 0;
Elliot Lee's avatar
Elliot Lee committed
763

764 765 766 767 768 769 770
  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
771
    {
772
      data1 = data = maskPR.data;
Elliot Lee's avatar
Elliot Lee committed
773 774
      ex = maskPR.x + maskPR.w;
      ey = maskPR.y + maskPR.h;
775 776
      /* only check the pixels if this tile is not fully within the currently
	 computed bounds */
777 778
      if (maskPR.x < tx1 || ex > tx2 ||
	  maskPR.y < ty1 || ey > ty2)
779
        {
780 781 782 783 784 785 786 787 788 789 790 791 792 793
	  /* 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
794
	    for (y = maskPR.y; y < ey; y++, data1 += maskPR.rowstride)
Elliot Lee's avatar
Elliot Lee committed
795
	    {
796
	      for (x = maskPR.x, data = data1; x < ex; x++, data++)
797
		if (*data)
798 799 800 801 802 803 804 805 806 807 808 809 810 811 812
		{
		  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
813 814 815 816
	    }
	}
    }

817 818
  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
819

820
  if (tx1 == GIMP_DRAWABLE (mask)->width && ty1 == GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
821 822
    {
      mask->empty = TRUE;
823 824 825 826
      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
827 828 829 830
    }
  else
    {
      mask->empty = FALSE;
831 832 833 834
      mask->x1    = tx1;
      mask->y1    = ty1;
      mask->x2    = tx2;
      mask->y2    = ty2;
Elliot Lee's avatar
Elliot Lee committed
835 836 837
    }
  mask->bounds_known = TRUE;

838 839 840 841 842
  *x1 = tx1;
  *x2 = tx2;
  *y1 = ty1;
  *y2 = ty2;

843
  return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
844 845
}

846
gboolean
Elliot Lee's avatar
Elliot Lee committed
847 848
channel_is_empty (Channel *mask)
{
849 850 851 852
  PixelRegion  maskPR;
  guchar      *data;
  gint         x, y;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
853 854 855 856

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

857 858 859 860 861 862 863
  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
864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881
    {
      /*  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);

882 883 884 885 886 887
  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
888
  mask->boundary_known = TRUE;
889 890 891 892
  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
893 894 895 896 897

  return TRUE;
}

void
898 899 900 901 902
channel_add_segment (Channel *mask,
		     gint     x,
		     gint     y,
		     gint     width,
		     gint     value)
Elliot Lee's avatar
Elliot Lee committed
903
{
904 905 906 907 908
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
909 910 911 912

  /*  check horizontal extents...  */
  x2 = x + width;
  if (x2 < 0) x2 = 0;
913
  if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
914
  if (x < 0) x = 0;
915
  if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
916 917 918
  width = x2 - x;
  if (!width) return;

919
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
920 921
    return;

922 923 924 925 926
  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
927 928 929 930 931 932 933 934 935 936 937 938 939 940
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data + value;
	  if (val > 255)
	    val = 255;
	  *data++ = val;
	}
    }
}

void
941 942 943 944 945
channel_sub_segment (Channel *mask,
		     gint     x,
		     gint     y,
		     gint     width,
		     gint     value)
Elliot Lee's avatar
Elliot Lee committed
946
{
947 948 949 950 951
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
952 953 954 955

  /*  check horizontal extents...  */
  x2 = x + width;
  if (x2 < 0) x2 = 0;
956
  if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
957
  if (x < 0) x = 0;
958
  if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
959 960 961
  width = x2 - x;
  if (!width) return;

962
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
963 964
    return;

965 966 967 968
  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
969 970 971 972 973 974 975 976 977 978 979 980 981 982
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data - value;
	  if (val < 0)
	    val = 0;
	  *data++ = val;
	}
    }
}

void
983 984 985 986 987 988
channel_combine_rect (Channel    *mask,
		      ChannelOps  op,
		      gint        x,
		      gint        y,
		      gint        w,
		      gint        h)
Elliot Lee's avatar
Elliot Lee committed
989
{
990
  gint        x2, y2;
Elliot Lee's avatar
Elliot Lee committed
991
  PixelRegion maskPR;
992
  guchar      color;
993

994 995
  y2 = y + h;
  x2 = x + w;
Elliot Lee's avatar
Elliot Lee committed
996

997 998 999 1000
  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
1001

1002
  if (x2 - x <= 0 || y2 - y <= 0)
Elliot Lee's avatar
Elliot Lee committed
1003 1004
    return;