gimpchannel.c 40.6 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
#include "config.h"

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

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

37
#include "libgimp/gimpintl.h"
38
#include "libgimp/gimpmath.h"
39

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

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

46
enum {
47
  REMOVED,
48 49 50 51 52 53 54
  LAST_SIGNAL
};

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

55
static guint channel_signals[LAST_SIGNAL] = { 0 };
56 57 58

static GimpDrawableClass *parent_class = NULL;

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

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

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

  return channel_type;
}

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

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

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

96 97 98 99 100 101 102 103 104
  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
105 106 107

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

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

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

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

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

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

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

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

  return channel;
}

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

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

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

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

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

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

  /*  copy the contents across channels  */
206 207 208 209 210 211 212
  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
213 214
  copy_region (&srcPR, &destPR);

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

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

  return new_channel;
}

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

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

238
void 
239
channel_set_color (Channel *channel,
Sven Neumann's avatar
Sven Neumann committed
240
		   guchar   *color)
241
{
242 243
  gint i;

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

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

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

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

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

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

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
299 300 301 302 303 304
  /* free the segments?  */
  if (channel->segs_in)
    g_free (channel->segs_in);
  if (channel->segs_out)
    g_free (channel->segs_out);

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

310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
/* The removed signal is sent out when the channel is no longer
 * associcated with an image.  It's needed because channels aren't
 * destroyed immediately, but kept around for undo purposes.  Connect
 * to the removed signal to update bits of UI that are tied to a
 * particular layer. */
void
channel_removed (Channel  *channel,
		 gpointer  image)
{
  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
326
void
327 328 329
channel_scale (Channel *channel,
	       gint     new_width,
	       gint     new_height)
Elliot Lee's avatar
Elliot Lee committed
330 331 332 333 334 335 336 337
{
  PixelRegion srcPR, destPR;
  TileManager *new_tiles;

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

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

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

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

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

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

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

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

  if (!new_width || !new_height)
    return;

  x1 = BOUNDS (offx, 0, new_width);
  y1 = BOUNDS (offy, 0, new_height);
393 394
  x2 = BOUNDS ((offx + GIMP_DRAWABLE (channel)->width), 0, new_width);
  y2 = BOUNDS ((offy + GIMP_DRAWABLE (channel)->height), 0, new_height);
Elliot Lee's avatar
Elliot Lee committed
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 420
  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  */
421 422 423 424
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
425 426

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

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

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

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

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

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

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

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

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

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

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

520 521 522 523
      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
524 525

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

      subsample_region (&srcPR, &destPR, subsample);

536 537 538 539 540 541
      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);
542
      return preview_buf;
Elliot Lee's avatar
Elliot Lee committed
543 544 545
    }
}

546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
TempBuf *
channel_preview (Channel *channel,
		 gint     width,
		 gint     height)
{
  /* Ok prime the cache with a large preview if the cache is invalid */
  if(!GIMP_DRAWABLE(channel)->preview_valid && 
     width <= PREVIEW_CACHE_PRIME_WIDTH &&
     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;
    }

  /* Second call - should NOT visit the tile cache...*/
  return channel_preview_private(channel,width,height);
}


Elliot Lee's avatar
Elliot Lee committed
571
void
572
channel_invalidate_previews (GimpImage* gimage)
Elliot Lee's avatar
Elliot Lee committed
573
{
574
  GSList * tmp;
Elliot Lee's avatar
Elliot Lee committed
575
  Channel * channel;
576 577

  tmp = gimage->channels;
Elliot Lee's avatar
Elliot Lee committed
578 579 580 581

  while (tmp)
    {
      channel = (Channel *) tmp->data;
582
      drawable_invalidate_preview (GIMP_DRAWABLE (channel));
583
      tmp = g_slist_next (tmp);
Elliot Lee's avatar
Elliot Lee committed
584 585 586
    }
}

587
Tattoo
588
channel_get_tattoo (const Channel *channel)
589
{
590
  return (gimp_drawable_get_tattoo (GIMP_DRAWABLE (channel)));
591 592
}

593 594 595
/******************************/
/*  selection mask functions  */
/******************************/
Elliot Lee's avatar
Elliot Lee committed
596 597

Channel *
598 599 600
channel_new_mask (GimpImage* gimage,
		  gint       width,
		  gint       height)
Elliot Lee's avatar
Elliot Lee committed
601
{
602
  guchar black[3] = {0, 0, 0};
Elliot Lee's avatar
Elliot Lee committed
603 604 605
  Channel *new_channel;

  /*  Create the new channel  */
606 607
  new_channel = channel_new (gimage, width, height,
			     _("Selection Mask"), 127, black);
Elliot Lee's avatar
Elliot Lee committed
608 609

  /*  Set the validate procedure  */
610 611
  tile_manager_set_validate_proc (GIMP_DRAWABLE (new_channel)->tiles,
				  channel_validate);
Elliot Lee's avatar
Elliot Lee committed
612 613 614 615

  return new_channel;
}

616 617 618 619 620 621 622 623 624 625
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
626
{
627
  gint x3, y3, x4, y4;
Elliot Lee's avatar
Elliot Lee committed
628 629 630 631 632 633 634 635 636 637 638 639
  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))
	{
640 641
	  pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles,
			     x3, y3, (x4 - x3), (y4 - y3), FALSE);
Elliot Lee's avatar
Elliot Lee committed
642 643 644 645 646 647 648 649 650 651 652
	  mask->segs_out = find_mask_boundary (&bPR, &mask->num_segs_out,
					       IgnoreBounds,
					       x1, y1,
					       x2, y2);
	  x1 = MAXIMUM (x1, x3);
	  y1 = MAXIMUM (y1, y3);
	  x2 = MINIMUM (x2, x4);
	  y2 = MINIMUM (y2, y4);

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

678 679 680
  *segs_in      = mask->segs_in;
  *segs_out     = mask->segs_out;
  *num_segs_in  = mask->num_segs_in;
Elliot Lee's avatar
Elliot Lee committed
681 682 683 684 685
  *num_segs_out = mask->num_segs_out;

  return TRUE;
}

686 687 688 689
gint
channel_value (Channel *mask,
	       gint     x,
	       gint     y)
Elliot Lee's avatar
Elliot Lee committed
690 691
{
  Tile *tile;
692
  gint val;
Elliot Lee's avatar
Elliot Lee committed
693 694 695 696 697 698 699 700 701 702 703

  /*  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
    {
704 705
      if (x < 0 || x >= GIMP_DRAWABLE (mask)->width ||
	  y < 0 || y >= GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
706 707 708
	return 0;
    }

709 710
  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));
711
  tile_release (tile, FALSE);
Elliot Lee's avatar
Elliot Lee committed
712 713 714 715

  return val;
}

716 717 718 719 720 721
gboolean
channel_bounds (Channel *mask,
		gint    *x1,
		gint    *y1,
		gint    *x2,
		gint    *y2)
Elliot Lee's avatar
Elliot Lee committed
722 723
{
  PixelRegion maskPR;
724 725 726
  guchar *data, *data1;
  gint x, y;
  gint ex, ey;
Elliot Lee's avatar
Elliot Lee committed
727
  void *pr;
728 729 730
  gint tx1, tx2, ty1, ty2;
  gint minx, maxx;

Elliot Lee's avatar
Elliot Lee committed
731 732 733 734 735 736 737 738
  /*  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;

739
      return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
740 741 742
    }

  /*  go through and calculate the bounds  */
743 744
  tx1 = GIMP_DRAWABLE (mask)->width;
  ty1 = GIMP_DRAWABLE (mask)->height;
745 746
  tx2 = 0;
  ty2 = 0;
Elliot Lee's avatar
Elliot Lee committed
747

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

801 802
  tx2 = BOUNDS (tx2 + 1, 0, GIMP_DRAWABLE (mask)->width);
  ty2 = BOUNDS (ty2 + 1, 0, GIMP_DRAWABLE (mask)->height);
Elliot Lee's avatar
Elliot Lee committed
803

804
  if (tx1 == GIMP_DRAWABLE (mask)->width && ty1 == GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
805 806
    {
      mask->empty = TRUE;
807 808 809 810
      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
811 812 813 814
    }
  else
    {
      mask->empty = FALSE;
815 816 817 818
      mask->x1    = tx1;
      mask->y1    = ty1;
      mask->x2    = tx2;
      mask->y2    = ty2;
Elliot Lee's avatar
Elliot Lee committed
819 820 821
    }
  mask->bounds_known = TRUE;

822 823 824 825 826
  *x1 = tx1;
  *x2 = tx2;
  *y1 = ty1;
  *y2 = ty2;

827
  return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
828 829
}

830
gboolean
Elliot Lee's avatar
Elliot Lee committed
831 832 833
channel_is_empty (Channel *mask)
{
  PixelRegion maskPR;
834 835
  guchar * data;
  gint x, y;
Elliot Lee's avatar
Elliot Lee committed
836 837 838 839 840
  void * pr;

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

841 842 843 844 845 846 847
  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
848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865
    {
      /*  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);

866 867 868 869 870 871
  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
872
  mask->boundary_known = TRUE;
873 874 875 876
  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
877 878 879 880 881

  return TRUE;
}

void
882 883 884 885 886
channel_add_segment (Channel *mask,
		     gint     x,
		     gint     y,
		     gint     width,
		     gint     value)
Elliot Lee's avatar
Elliot Lee committed
887 888
{
  PixelRegion maskPR;
889 890 891
  guchar *data;
  gint val;
  gint x2;
Elliot Lee's avatar
Elliot Lee committed
892 893 894 895 896
  void * pr;

  /*  check horizontal extents...  */
  x2 = x + width;
  if (x2 < 0) x2 = 0;
897
  if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
898
  if (x < 0) x = 0;
899
  if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
900 901 902
  width = x2 - x;
  if (!width) return;

903
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
904 905
    return;

906 907 908 909 910
  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
911 912 913 914 915 916 917 918 919 920 921 922 923 924
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data + value;
	  if (val > 255)
	    val = 255;
	  *data++ = val;
	}
    }
}

void
925 926 927 928 929
channel_sub_segment (Channel *mask,
		     gint     x,
		     gint     y,
		     gint     width,
		     gint     value)
Elliot Lee's avatar
Elliot Lee committed
930 931
{
  PixelRegion maskPR;
932 933 934
  guchar *data;
  gint val;
  gint x2;
Elliot Lee's avatar
Elliot Lee committed
935 936 937 938 939
  void * pr;

  /*  check horizontal extents...  */
  x2 = x + width;
  if (x2 < 0) x2 = 0;
940
  if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
941
  if (x < 0) x = 0;
942
  if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
943 944 945
  width = x2 - x;
  if (!width) return;

946
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
947 948
    return;

949 950 951 952
  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
953 954 955 956 957 958 959 960 961 962 963 964 965 966
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data - value;
	  if (val < 0)
	    val = 0;
	  *data++ = val;
	}
    }
}

void
967 968 969 970 971 972
channel_combine_rect (Channel    *mask,
		      ChannelOps  op,
		      gint        x,
		      gint        y,
		      gint        w,
		      gint        h)
Elliot Lee's avatar
Elliot Lee committed
973
{
974
  gint x2, y2;
Elliot Lee's avatar
Elliot Lee committed
975
  PixelRegion maskPR;
976 977
  guchar color;

978 979
  y2 = y + h;
  x2 = x + w;
Elliot Lee's avatar
Elliot Lee committed
980

981 982 983 984
  x  = BOUNDS (x,  0, GIMP_DRAWABLE (mask)->width);
  y  = BOUNDS (y,  0, GIMP_DRAWABLE (mask)->height);
  x2 = BOUNDS (x2, 0, GIMP_DRAWABLE (mask)->width);
  y2 = BOUNDS (y2, 0, GIMP_DRAWABLE (mask)->height);
Elliot Lee's avatar
Elliot Lee committed
985

986
  if (x2 - x <= 0 || y2 - y <= 0)
Elliot Lee's avatar
Elliot Lee committed
987 988
    return;

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

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

1020 1021 1022 1023
  mask->x1 = BOUNDS (mask->x1, 0, GIMP_DRAWABLE (mask)->width);
  mask->y1 = BOUNDS (mask->y1, 0, GIMP_DRAWABLE (mask)->height);
  mask->x2 = BOUNDS (mask->x2, 0, GIMP_DRAWABLE (mask)->width);
  mask->y2 = BOUNDS (mask->y2, 0, GIMP_DRAWABLE (mask)->height);
Elliot Lee's avatar
Elliot Lee committed
1024 1025 1026
}

void
1027 1028 1029 1030 1031 1032 1033
channel_combine_ellipse (Channel    *mask,
			 ChannelOps  op,
			 gint        x,
			 gint        y,
			 gint        w,
			 gint        h,
			 gboolean    aa /*  antialias selection?  */)
Elliot Lee's avatar
Elliot Lee committed
1034
{
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045
  gint i, j;
  gint x0, x1, x2;
  gint val, last;
  gfloat a_sqr, b_sqr, aob_sqr;
  gfloat w_sqr, h_sqr;
  gfloat y_sqr;
  gfloat t0, t1;
  gfloat r;
  gfloat cx, cy;
  gfloat rad;
  gfloat dist;
Elliot Lee's avatar
Elliot Lee committed
1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058