gimpchannel.c 42.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
#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
enum
{
55
  REMOVED,
56 57 58
  LAST_SIGNAL
};

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

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

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

static GimpDrawableClass *parent_class = NULL;

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

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

90
      channel_type = gtk_type_unique (GIMP_TYPE_DRAWABLE, &channel_info);
91 92 93 94 95 96
    }

  return channel_type;
}

static void
97
gimp_channel_class_init (GimpChannelClass *klass)
98 99 100
{
  GtkObjectClass *object_class;

101 102 103
  object_class = (GtkObjectClass *) klass;

  parent_class = gtk_type_class (GIMP_TYPE_DRAWABLE);
104

105
  channel_signals[REMOVED] =
106 107 108 109 110 111 112
    gtk_signal_new ("removed",
                    GTK_RUN_FIRST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GimpChannelClass,
				       removed),
                    gtk_signal_default_marshaller,
                    GTK_TYPE_NONE, 0);
113

114 115 116
  gtk_object_class_add_signals (object_class, channel_signals, LAST_SIGNAL);

  object_class->destroy = gimp_channel_destroy;
117 118

  klass->removed = NULL;
119 120 121 122 123 124
}

static void
gimp_channel_init (GimpChannel *channel)
{
}
Elliot Lee's avatar
Elliot Lee committed
125 126 127

/**************************/
/*  Function definitions  */
128
/**************************/
Elliot Lee's avatar
Elliot Lee committed
129 130

static void
131 132
channel_validate (TileManager *tm,
		  Tile        *tile)
Elliot Lee's avatar
Elliot Lee committed
133 134
{
  /*  Set the contents of the tile to empty  */
scott's avatar
scott committed
135
  memset (tile_data_pointer (tile, 0, 0), 
136
	  TRANSPARENT_OPACITY, tile_size (tile));
Elliot Lee's avatar
Elliot Lee committed
137 138 139
}

Channel *
140 141 142 143 144 145
channel_new (GimpImage    *gimage,
	     gint          width,
	     gint          height,
	     const gchar  *name,
	     gint          opacity,
	     const guchar *col)
Elliot Lee's avatar
Elliot Lee committed
146 147
{
  Channel * channel;
148
  gint i;
Elliot Lee's avatar
Elliot Lee committed
149

150
  channel = gtk_type_new (gimp_channel_get_type ());
Elliot Lee's avatar
Elliot Lee committed
151

152
  gimp_drawable_configure (GIMP_DRAWABLE (channel), 
153
			   gimage, width, height, GRAY_GIMAGE, name);
Elliot Lee's avatar
Elliot Lee committed
154 155 156 157

  /*  set the channel color and opacity  */
  for (i = 0; i < 3; i++)
    channel->col[i] = col[i];
158 159 160

  channel->opacity     = opacity;
  channel->show_masked = TRUE;
Elliot Lee's avatar
Elliot Lee committed
161 162

  /*  selection mask variables  */
163 164 165 166 167 168
  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
169
  channel->boundary_known = TRUE;
170 171 172 173
  channel->x1             = 0;
  channel->y1             = 0;
  channel->x2             = width;
  channel->y2             = height;
Elliot Lee's avatar
Elliot Lee committed
174 175 176 177

  return channel;
}

scott's avatar
scott committed
178
Channel *
179
channel_copy (const Channel *channel)
Elliot Lee's avatar
Elliot Lee committed
180
{
181 182 183 184 185 186 187
  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
188 189

  /*  formulate the new channel name  */
190 191
  name = gimp_object_get_name (GIMP_OBJECT (channel));

192
  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,
                  const gchar *name)
234
{
235
  gimp_object_set_name (GIMP_OBJECT (channel), name);
236 237
}

238 239
const gchar *
channel_get_name (const Channel *channel)
240
{
241
  return gimp_object_get_name (GIMP_OBJECT (channel));
242 243
}

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

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

257 258
const guchar *
channel_get_color (const Channel *channel)
259
{
260
  return GIMP_CHANNEL (channel)->col;
261 262
}
 
263 264
gint
channel_get_opacity (const Channel *channel)
265 266 267 268
{ 
  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
  drawable = drawable_get_ID (ID);
283

284 285 286 287
  if (drawable && GIMP_IS_CHANNEL (drawable)) 
    return GIMP_CHANNEL (drawable);
  else
    return NULL;
Elliot Lee's avatar
Elliot Lee committed
288 289 290 291 292
}

void
channel_delete (Channel *channel)
{
293 294 295 296 297 298
  /*  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));
299 300 301 302 303 304 305 306 307 308 309 310
}

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
311 312 313 314 315 316
  /* free the segments?  */
  if (channel->segs_in)
    g_free (channel->segs_in);
  if (channel->segs_out)
    g_free (channel->segs_out);

317
  if (GTK_OBJECT_CLASS (parent_class)->destroy)
318
    GTK_OBJECT_CLASS (parent_class)->destroy (object);
Elliot Lee's avatar
Elliot Lee committed
319 320
}

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

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

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

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

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

  /*  Configure the new channel  */
369 370 371
  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
372 373 374 375 376

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

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

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

  if (!new_width || !new_height)
    return;

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

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

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

  /*  Configure the new channel  */
466 467 468
  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
469 470 471 472 473

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

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

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

490 491 492
/**********************/
/*  access functions  */
/**********************/
Elliot Lee's avatar
Elliot Lee committed
493

494
gboolean
Elliot Lee's avatar
Elliot Lee committed
495 496
channel_toggle_visibility (Channel *channel)
{
497
  GIMP_DRAWABLE (channel)->visible = !GIMP_DRAWABLE (channel)->visible;
Elliot Lee's avatar
Elliot Lee committed
498

499
  return GIMP_DRAWABLE (channel)->visible;
Elliot Lee's avatar
Elliot Lee committed
500 501
}

502 503 504 505
TempBuf *
channel_preview (Channel *channel,
		 gint     width,
		 gint     height)
Elliot Lee's avatar
Elliot Lee committed
506
{
507 508
  /* Ok prime the cache with a large preview if the cache is invalid */
  if (! GIMP_DRAWABLE (channel)->preview_valid &&
509 510
      width  <= PREVIEW_CACHE_PRIME_WIDTH      &&
      height <= PREVIEW_CACHE_PRIME_HEIGHT     &&
511 512 513 514 515 516 517 518 519 520 521 522 523 524
      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;
    }

525
  /* Second call - should NOT visit the tile cache... */
526 527 528 529 530 531 532 533 534 535 536 537 538
  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
539

540 541 542
  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
543
  /*  The easy way  */
544 545 546 547
  if (GIMP_DRAWABLE (channel)->preview_valid &&
      (ret_buf =
       gimp_preview_cache_get (& (GIMP_DRAWABLE (channel)->preview_cache),
			       width, height)))
548 549 550
    {
      return ret_buf;
    }
Elliot Lee's avatar
Elliot Lee committed
551 552 553 554 555
  /*  The hard way  */
  else
    {
      /*  calculate 'acceptable' subsample  */
      subsample = 1;
556 557
      if (width < 1) width = 1;
      if (height < 1) height = 1;
558 559
      while ((width  * (subsample + 1) * 2 < GIMP_DRAWABLE (channel)->width) &&
	     (height * (subsample + 1) * 2 < GIMP_DRAWABLE (channel)->height))
Elliot Lee's avatar
Elliot Lee committed
560 561
	subsample = subsample + 1;

562 563 564 565
      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
566 567

      preview_buf = mask_buf_new (width, height);
568 569 570 571 572
      destPR.bytes     = 1;
      destPR.x         = 0;
      destPR.y         = 0;
      destPR.w         = width;
      destPR.h         = height;
Elliot Lee's avatar
Elliot Lee committed
573
      destPR.rowstride = width;
574
      destPR.data      = mask_buf_data (preview_buf);
Elliot Lee's avatar
Elliot Lee committed
575 576 577

      subsample_region (&srcPR, &destPR, subsample);

578 579 580 581 582 583
      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);
584
      return preview_buf;
Elliot Lee's avatar
Elliot Lee committed
585 586 587 588
    }
}

void
589
channel_invalidate_previews (GimpImage *gimage)
Elliot Lee's avatar
Elliot Lee committed
590
{
591 592
  GSList  *tmp;
  Channel *channel;
593

Sven Neumann's avatar
Sven Neumann committed
594 595
  g_return_if_fail (gimage != NULL);

596
  tmp = gimage->channels;
Elliot Lee's avatar
Elliot Lee committed
597 598 599 600

  while (tmp)
    {
      channel = (Channel *) tmp->data;
601
      gimp_drawable_invalidate_preview (GIMP_DRAWABLE (channel), TRUE);
602
      tmp = g_slist_next (tmp);
Elliot Lee's avatar
Elliot Lee committed
603 604 605
    }
}

606
Tattoo
607
channel_get_tattoo (const Channel *channel)
608
{
609
  return gimp_drawable_get_tattoo (GIMP_DRAWABLE (channel));
610 611
}

612
void
613 614
channel_set_tattoo (Channel *channel, 
		    Tattoo   value)
615
{
616
  gimp_drawable_set_tattoo (GIMP_DRAWABLE (channel), value);
617 618
}

619 620 621
/******************************/
/*  selection mask functions  */
/******************************/
Elliot Lee's avatar
Elliot Lee committed
622 623

Channel *
624
channel_new_mask (GimpImage *gimage,
625 626
		  gint       width,
		  gint       height)
Elliot Lee's avatar
Elliot Lee committed
627
{
628
  guchar   black[3] = { 0, 0, 0 };
Elliot Lee's avatar
Elliot Lee committed
629 630 631
  Channel *new_channel;

  /*  Create the new channel  */
632 633
  new_channel = channel_new (gimage, width, height,
			     _("Selection Mask"), 127, black);
Elliot Lee's avatar
Elliot Lee committed
634 635

  /*  Set the validate procedure  */
636 637
  tile_manager_set_validate_proc (GIMP_DRAWABLE (new_channel)->tiles,
				  channel_validate);
Elliot Lee's avatar
Elliot Lee committed
638 639 640 641

  return new_channel;
}

642 643 644 645 646 647 648 649 650 651
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
652
{
653
  gint        x3, y3, x4, y4;
Elliot Lee's avatar
Elliot Lee committed
654 655 656 657 658 659 660 661 662 663 664 665
  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))
	{
666 667
	  pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles,
			     x3, y3, (x4 - x3), (y4 - y3), FALSE);
Elliot Lee's avatar
Elliot Lee committed
668 669 670 671
	  mask->segs_out = find_mask_boundary (&bPR, &mask->num_segs_out,
					       IgnoreBounds,
					       x1, y1,
					       x2, y2);
672 673 674 675
	  x1 = MAX (x1, x3);
	  y1 = MAX (y1, y3);
	  x2 = MIN (x2, x4);
	  y2 = MIN (y2, y4);
Elliot Lee's avatar
Elliot Lee committed
676 677 678

	  if (x2 > x1 && y2 > y1)
	    {
679 680 681 682
	      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
683 684 685 686 687 688 689
	      mask->segs_in =  find_mask_boundary (&bPR, &mask->num_segs_in,
						   WithinBounds,
						   x1, y1,
						   x2, y2);
	    }
	  else
	    {
690
	      mask->segs_in     = NULL;
Elliot Lee's avatar
Elliot Lee committed
691 692 693 694 695
	      mask->num_segs_in = 0;
	    }
	}
      else
	{
696 697 698
	  mask->segs_in      = NULL;
	  mask->segs_out     = NULL;
	  mask->num_segs_in  = 0;
Elliot Lee's avatar
Elliot Lee committed
699 700 701 702 703
	  mask->num_segs_out = 0;
	}
      mask->boundary_known = TRUE;
    }

704 705 706
  *segs_in      = mask->segs_in;
  *segs_out     = mask->segs_out;
  *num_segs_in  = mask->num_segs_in;
Elliot Lee's avatar
Elliot Lee committed
707 708 709 710 711
  *num_segs_out = mask->num_segs_out;

  return TRUE;
}

712 713 714 715
gint
channel_value (Channel *mask,
	       gint     x,
	       gint     y)
Elliot Lee's avatar
Elliot Lee committed
716 717
{
  Tile *tile;
718
  gint  val;
Elliot Lee's avatar
Elliot Lee committed
719 720 721 722 723 724 725 726 727 728 729

  /*  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
    {
730 731
      if (x < 0 || x >= GIMP_DRAWABLE (mask)->width ||
	  y < 0 || y >= GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
732 733 734
	return 0;
    }

735 736
  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));
737
  tile_release (tile, FALSE);
Elliot Lee's avatar
Elliot Lee committed
738 739 740 741

  return val;
}

742 743 744 745 746 747
gboolean
channel_bounds (Channel *mask,
		gint    *x1,
		gint    *y1,
		gint    *x2,
		gint    *y2)
Elliot Lee's avatar
Elliot Lee committed
748
{
749 750 751 752 753 754 755
  PixelRegion  maskPR;
  guchar      *data, *data1;
  gint         x, y;
  gint         ex, ey;
  gint         tx1, tx2, ty1, ty2;
  gint         minx, maxx;
  gpointer     pr;
756

Elliot Lee's avatar
Elliot Lee committed
757 758 759 760 761 762 763 764
  /*  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;

765
      return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
766 767 768
    }

  /*  go through and calculate the bounds  */
769 770
  tx1 = GIMP_DRAWABLE (mask)->width;
  ty1 = GIMP_DRAWABLE (mask)->height;
771 772
  tx2 = 0;
  ty2 = 0;
Elliot Lee's avatar
Elliot Lee committed
773

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

827 828
  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
829

830
  if (tx1 == GIMP_DRAWABLE (mask)->width && ty1 == GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
831 832
    {
      mask->empty = TRUE;
833 834 835 836
      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
837 838 839 840
    }
  else
    {
      mask->empty = FALSE;
841 842 843 844
      mask->x1    = tx1;
      mask->y1    = ty1;
      mask->x2    = tx2;
      mask->y2    = ty2;
Elliot Lee's avatar
Elliot Lee committed
845 846 847
    }
  mask->bounds_known = TRUE;

848 849 850 851 852
  *x1 = tx1;
  *x2 = tx2;
  *y1 = ty1;
  *y2 = ty2;

853
  return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
854 855
}

856
gboolean
Elliot Lee's avatar
Elliot Lee committed
857 858
channel_is_empty (Channel *mask)
{
859 860 861 862
  PixelRegion  maskPR;
  guchar      *data;
  gint         x, y;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
863 864 865 866

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

867 868 869 870 871 872 873
  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
874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891
    {
      /*  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);

892 893 894 895 896 897
  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
898
  mask->boundary_known = TRUE;
899 900 901 902
  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
903 904 905 906 907

  return TRUE;
}

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

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

929
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
930 931
    return;

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

void
951 952 953 954 955
channel_sub_segment (Channel *mask,
		     gint     x,
		     gint     y,
		     gint     width,
		     gint     value)
Elliot Lee's avatar
Elliot Lee committed
956
{
957 958 959 960 961
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
962 963 964 965

  /*  check horizontal extents...  */
  x2 = x + width;
  if (x2 < 0) x2 = 0;
966
  if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
967
  if (x < 0) x = 0;
968
  if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
969 970 971
  width = x2 - x;
  if (!width) return;

972
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
973 974
    return;

975 976 977 978
  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
979 980 981 982 983 984 985 986 987 988 989 990 991 992
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data - value;
	  if (val < 0)
	    val = 0;
	  *data++ = val;
	}
    }
}

void
993 994 995 996 997 998
channel_combine_rect (Channel    *mask,
		      ChannelOps  op,
		      gint        x,
		      gint        y,
		      gint        w,
		      gint        h)
Elliot Lee's avatar
Elliot Lee committed
999
{
1000
  gint        x2, y2;
Elliot Lee's avatar
Elliot Lee committed
1001
  PixelRegion maskPR;