gimpchannel.c 39.5 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
#include <gtk/gtk.h>

26
#include "libgimpcolor/gimpcolor.h"
27 28
#include "libgimpmath/gimpmath.h"

Sven Neumann's avatar
Sven Neumann committed
29 30
#include "apptypes.h"

Elliot Lee's avatar
Elliot Lee committed
31
#include "appenv.h"
32
#include "boundary.h"
Elliot Lee's avatar
Elliot Lee committed
33
#include "drawable.h"
34
#include "gimpimage.h"
Elliot Lee's avatar
Elliot Lee committed
35
#include "gimage_mask.h"
36 37
#include "gimpchannel.h"
#include "gimplayer.h"
38
#include "gimplut.h"
Elliot Lee's avatar
Elliot Lee committed
39
#include "paint_funcs.h"
40
#include "parasitelist.h"
41 42 43
#include "pixel_processor.h"
#include "pixel_region.h"
#include "lut_funcs.h"
Elliot Lee's avatar
Elliot Lee committed
44
#include "temp_buf.h"
scott's avatar
scott committed
45
#include "tile.h"
46 47
#include "tile_manager.h"
#include "undo.h"
48

49 50 51
#include "libgimp/gimpintl.h"


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

56

57
static GimpDrawableClass * parent_class = NULL;
58

59

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

  if (!channel_type)
    {
      GtkTypeInfo channel_info =
      {
	"GimpChannel",
	sizeof (GimpChannel),
	sizeof (GimpChannelClass),
	(GtkClassInitFunc) gimp_channel_class_init,
	(GtkObjectInitFunc) gimp_channel_init,
74 75
        /* reserved_1 */ NULL,
	/* reserved_2 */ NULL,
76
	(GtkClassInitFunc) NULL,
77 78
      };

79
      channel_type = gtk_type_unique (GIMP_TYPE_DRAWABLE, &channel_info);
80 81 82 83 84 85
    }

  return channel_type;
}

static void
86
gimp_channel_class_init (GimpChannelClass *klass)
87 88 89
{
  GtkObjectClass *object_class;

90 91 92
  object_class = (GtkObjectClass *) klass;

  parent_class = gtk_type_class (GIMP_TYPE_DRAWABLE);
93 94 95 96 97 98 99

  object_class->destroy = gimp_channel_destroy;
}

static void
gimp_channel_init (GimpChannel *channel)
{
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
  gimp_rgba_set (&channel->color, 0.0, 0.0, 0.0, 1.0);

  channel->show_masked = FALSE;

  /*  Selection mask variables  */
  channel->boundary_known = TRUE;
  channel->segs_in        = NULL;
  channel->segs_out       = NULL;
  channel->num_segs_in    = 0;
  channel->num_segs_out   = 0;
  channel->empty          = TRUE;
  channel->bounds_known   = TRUE;
  channel->x1             = 0;
  channel->y1             = 0;
  channel->x2             = 0;
  channel->y2             = 0;
  
117
}
Elliot Lee's avatar
Elliot Lee committed
118

119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
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);

  /* free the segments?  */
  if (channel->segs_in)
    g_free (channel->segs_in);
  if (channel->segs_out)
    g_free (channel->segs_out);

  if (GTK_OBJECT_CLASS (parent_class)->destroy)
    GTK_OBJECT_CLASS (parent_class)->destroy (object);
}
Elliot Lee's avatar
Elliot Lee committed
138 139

static void
Michael Natterer's avatar
Michael Natterer committed
140 141
gimp_channel_validate (TileManager *tm,
		       Tile        *tile)
Elliot Lee's avatar
Elliot Lee committed
142 143
{
  /*  Set the contents of the tile to empty  */
scott's avatar
scott committed
144
  memset (tile_data_pointer (tile, 0, 0), 
145
	  TRANSPARENT_OPACITY, tile_size (tile));
Elliot Lee's avatar
Elliot Lee committed
146 147
}

Michael Natterer's avatar
Michael Natterer committed
148 149 150 151 152 153
GimpChannel *
gimp_channel_new (GimpImage     *gimage,
		  gint           width,
		  gint           height,
		  const gchar   *name,
		  const GimpRGB *color)
Elliot Lee's avatar
Elliot Lee committed
154
{
Michael Natterer's avatar
Michael Natterer committed
155
  GimpChannel *channel;
156 157

  g_return_val_if_fail (color != NULL, NULL);
Elliot Lee's avatar
Elliot Lee committed
158

159
  channel = gtk_type_new (GIMP_TYPE_CHANNEL);
Elliot Lee's avatar
Elliot Lee committed
160

Michael Natterer's avatar
Michael Natterer committed
161
  gimp_drawable_configure (GIMP_DRAWABLE (channel),
162
			   gimage, width, height, GRAY_GIMAGE, name);
Elliot Lee's avatar
Elliot Lee committed
163 164

  /*  set the channel color and opacity  */
165
  channel->color = *color;
166 167

  channel->show_masked = TRUE;
Elliot Lee's avatar
Elliot Lee committed
168 169

  /*  selection mask variables  */
170 171
  channel->x2 = width;
  channel->y2 = height;
Elliot Lee's avatar
Elliot Lee committed
172 173 174 175

  return channel;
}

Michael Natterer's avatar
Michael Natterer committed
176
GimpChannel *
177 178 179 180 181 182 183
gimp_channel_copy (const GimpChannel *channel,
                   gboolean           dummy) /*  the dummy is for symmetry with
                                              *  gimp_layer_copy() because
                                              *  both functions are used as
                                              *  function pointers in
                                              *  GimpDrawableListView --Mitch
                                              */
Elliot Lee's avatar
Elliot Lee committed
184
{
185
  gchar       *channel_name;
Michael Natterer's avatar
Michael Natterer committed
186
  GimpChannel *new_channel;
187 188 189 190 191
  PixelRegion  srcPR, destPR;
  gchar       *ext;
  gint         number;
  const gchar *name;
  gint         len;
Elliot Lee's avatar
Elliot Lee committed
192 193

  /*  formulate the new channel name  */
194 195
  name = gimp_object_get_name (GIMP_OBJECT (channel));

196
  ext = strrchr (name, '#');
197
  len = strlen (_("copy"));
198 199 200 201
  if ((strlen (name) >= len &&
       strcmp (&name[strlen (name) - len], _("copy")) == 0) ||
      (ext && (number = atoi (ext + 1)) > 0 && 
       ((int)(log10 (number) + 1)) == strlen (ext + 1)))
202 203
    /* don't have redundant "copy"s */
    channel_name = g_strdup (name);
204
  else
205
    channel_name = g_strdup_printf (_("%s copy"), name);
Elliot Lee's avatar
Elliot Lee committed
206 207

  /*  allocate a new channel object  */
Michael Natterer's avatar
Michael Natterer committed
208 209 210 211 212
  new_channel = gimp_channel_new (GIMP_DRAWABLE (channel)->gimage, 
				  GIMP_DRAWABLE (channel)->width, 
				  GIMP_DRAWABLE (channel)->height, 
				  channel_name,
				  &channel->color);
213

214
  GIMP_DRAWABLE (new_channel)->visible = GIMP_DRAWABLE (channel)->visible;
215

Elliot Lee's avatar
Elliot Lee committed
216 217 218
  new_channel->show_masked = channel->show_masked;

  /*  copy the contents across channels  */
219 220 221 222 223 224 225
  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
226 227
  copy_region (&srcPR, &destPR);

228
  /* copy the parasites */
229 230
  GIMP_DRAWABLE (new_channel)->parasites =
    parasite_list_copy (GIMP_DRAWABLE (channel)->parasites);
231

Elliot Lee's avatar
Elliot Lee committed
232 233 234 235 236 237
  /*  free up the channel_name memory  */
  g_free (channel_name);

  return new_channel;
}

238
void 
Michael Natterer's avatar
Michael Natterer committed
239 240
gimp_channel_set_color (GimpChannel   *channel,
			const GimpRGB *color)
241
{
242 243 244
  g_return_if_fail (channel != NULL);
  g_return_if_fail (GIMP_IS_CHANNEL (channel));
  g_return_if_fail (color != NULL);
245

246
  channel->color = *color;
247
}
248

249
const GimpRGB *
Michael Natterer's avatar
Michael Natterer committed
250
gimp_channel_get_color (const GimpChannel *channel)
251
{
252 253 254 255
  g_return_val_if_fail (channel != NULL, NULL);
  g_return_val_if_fail (GIMP_IS_CHANNEL (channel), NULL);

  return &channel->color;
256
}
257

258
gint
Michael Natterer's avatar
Michael Natterer committed
259
gimp_channel_get_opacity (const GimpChannel *channel)
260 261 262 263 264
{
  g_return_val_if_fail (channel != NULL, 0);
  g_return_val_if_fail (GIMP_IS_CHANNEL (channel), 0);

  return (gint) (channel->color.a * 100.999);
265 266
}

267
void 
Michael Natterer's avatar
Michael Natterer committed
268 269
gimp_channel_set_opacity (GimpChannel *channel,
			  gint         opacity)
270
{
271 272 273 274 275 276
  g_return_if_fail (channel != NULL);
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

  opacity = CLAMP (opacity, 0, 100);

  channel->color.a = opacity / 100.0;
277 278
}

Elliot Lee's avatar
Elliot Lee committed
279
void
Michael Natterer's avatar
Michael Natterer committed
280 281 282
gimp_channel_scale (GimpChannel *channel,
		    gint         new_width,
		    gint         new_height)
Elliot Lee's avatar
Elliot Lee committed
283
{
284
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
285 286 287 288 289 290
  TileManager *new_tiles;

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

  /*  Update the old channel position  */
291 292 293 294
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
295 296

  /*  Configure the pixel regions  */
297 298 299 300
  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
301 302 303 304 305 306 307 308 309

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

  /*  Configure the new channel  */
313 314 315
  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
316 317 318 319 320

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

  /*  Update the new channel position  */
321 322 323 324
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
325 326 327
}

void
Michael Natterer's avatar
Michael Natterer committed
328 329 330 331 332
gimp_channel_resize (GimpChannel *channel,
		     gint         new_width,
		     gint         new_height,
		     gint         offx,
		     gint         offy)
Elliot Lee's avatar
Elliot Lee committed
333
{
334
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
335
  TileManager *new_tiles;
336 337 338 339
  guchar       bg = 0;
  gint         clear;
  gint         w, h;
  gint         x1, y1, x2, y2;
Elliot Lee's avatar
Elliot Lee committed
340 341 342 343

  if (!new_width || !new_height)
    return;

344 345 346 347
  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
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
  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  */
374 375 376 377
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
378 379

  /*  Configure the pixel regions  */
380 381
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
		     x1, y1, w, h, FALSE);
Elliot Lee's avatar
Elliot Lee committed
382 383

  /*  Determine whether the new channel needs to be initially cleared  */
384 385
  if ((new_width  > GIMP_DRAWABLE (channel)->width) ||
      (new_height > GIMP_DRAWABLE (channel)->height) ||
Elliot Lee's avatar
Elliot Lee committed
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
      (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  */
407
  undo_push_channel_mod (GIMP_DRAWABLE (channel)->gimage, channel);
Elliot Lee's avatar
Elliot Lee committed
408 409

  /*  Configure the new channel  */
410 411 412
  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
413 414 415 416 417

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

  /*  update the new channel area  */
418 419 420 421
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
422 423
}

424

425 426 427
/******************************/
/*  selection mask functions  */
/******************************/
Elliot Lee's avatar
Elliot Lee committed
428

Michael Natterer's avatar
Michael Natterer committed
429 430 431 432
GimpChannel *
gimp_channel_new_mask (GimpImage *gimage,
		       gint       width,
		       gint       height)
Elliot Lee's avatar
Elliot Lee committed
433
{
Michael Natterer's avatar
Michael Natterer committed
434 435
  GimpRGB      black = { 0.0, 0.0, 0.0, 0.5 };
  GimpChannel *new_channel;
Elliot Lee's avatar
Elliot Lee committed
436 437

  /*  Create the new channel  */
Michael Natterer's avatar
Michael Natterer committed
438 439
  new_channel = gimp_channel_new (gimage, width, height,
				  _("Selection Mask"), &black);
Elliot Lee's avatar
Elliot Lee committed
440 441

  /*  Set the validate procedure  */
442
  tile_manager_set_validate_proc (GIMP_DRAWABLE (new_channel)->tiles,
Michael Natterer's avatar
Michael Natterer committed
443
				  gimp_channel_validate);
Elliot Lee's avatar
Elliot Lee committed
444 445 446 447

  return new_channel;
}

448
gboolean
Michael Natterer's avatar
Michael Natterer committed
449 450 451 452 453 454 455 456 457
gimp_channel_boundary (GimpChannel  *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
458
{
459
  gint        x3, y3, x4, y4;
Elliot Lee's avatar
Elliot Lee committed
460 461 462 463 464 465 466 467 468 469
  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);

Michael Natterer's avatar
Michael Natterer committed
470
      if (gimp_channel_bounds (mask, &x3, &y3, &x4, &y4))
Elliot Lee's avatar
Elliot Lee committed
471
	{
472 473
	  pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles,
			     x3, y3, (x4 - x3), (y4 - y3), FALSE);
Elliot Lee's avatar
Elliot Lee committed
474 475 476 477
	  mask->segs_out = find_mask_boundary (&bPR, &mask->num_segs_out,
					       IgnoreBounds,
					       x1, y1,
					       x2, y2);
478 479 480 481
	  x1 = MAX (x1, x3);
	  y1 = MAX (y1, y3);
	  x2 = MIN (x2, x4);
	  y2 = MIN (y2, y4);
Elliot Lee's avatar
Elliot Lee committed
482 483 484

	  if (x2 > x1 && y2 > y1)
	    {
485 486 487 488
	      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
489 490 491 492 493 494 495
	      mask->segs_in =  find_mask_boundary (&bPR, &mask->num_segs_in,
						   WithinBounds,
						   x1, y1,
						   x2, y2);
	    }
	  else
	    {
496
	      mask->segs_in     = NULL;
Elliot Lee's avatar
Elliot Lee committed
497 498 499 500 501
	      mask->num_segs_in = 0;
	    }
	}
      else
	{
502 503 504
	  mask->segs_in      = NULL;
	  mask->segs_out     = NULL;
	  mask->num_segs_in  = 0;
Elliot Lee's avatar
Elliot Lee committed
505 506 507 508 509
	  mask->num_segs_out = 0;
	}
      mask->boundary_known = TRUE;
    }

510 511 512
  *segs_in      = mask->segs_in;
  *segs_out     = mask->segs_out;
  *num_segs_in  = mask->num_segs_in;
Elliot Lee's avatar
Elliot Lee committed
513 514 515 516 517
  *num_segs_out = mask->num_segs_out;

  return TRUE;
}

518
gint
Michael Natterer's avatar
Michael Natterer committed
519 520 521
gimp_channel_value (GimpChannel *mask,
		    gint         x,
		    gint         y)
Elliot Lee's avatar
Elliot Lee committed
522 523
{
  Tile *tile;
524
  gint  val;
Elliot Lee's avatar
Elliot Lee committed
525 526 527 528 529 530 531 532 533 534 535

  /*  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
    {
536 537
      if (x < 0 || x >= GIMP_DRAWABLE (mask)->width ||
	  y < 0 || y >= GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
538 539 540
	return 0;
    }

541 542
  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));
543
  tile_release (tile, FALSE);
Elliot Lee's avatar
Elliot Lee committed
544 545 546 547

  return val;
}

548
gboolean
Michael Natterer's avatar
Michael Natterer committed
549 550 551 552 553
gimp_channel_bounds (GimpChannel *mask,
		     gint        *x1,
		     gint        *y1,
		     gint        *x2,
		     gint        *y2)
Elliot Lee's avatar
Elliot Lee committed
554
{
555 556 557 558 559 560 561
  PixelRegion  maskPR;
  guchar      *data, *data1;
  gint         x, y;
  gint         ex, ey;
  gint         tx1, tx2, ty1, ty2;
  gint         minx, maxx;
  gpointer     pr;
562

Elliot Lee's avatar
Elliot Lee committed
563 564 565 566 567 568 569 570
  /*  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;

571
      return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
572 573 574
    }

  /*  go through and calculate the bounds  */
575 576
  tx1 = GIMP_DRAWABLE (mask)->width;
  ty1 = GIMP_DRAWABLE (mask)->height;
577 578
  tx2 = 0;
  ty2 = 0;
Elliot Lee's avatar
Elliot Lee committed
579

580 581 582 583 584 585 586
  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
587
    {
588
      data1 = data = maskPR.data;
Elliot Lee's avatar
Elliot Lee committed
589 590
      ex = maskPR.x + maskPR.w;
      ey = maskPR.y + maskPR.h;
591 592
      /* only check the pixels if this tile is not fully within the currently
	 computed bounds */
593 594
      if (maskPR.x < tx1 || ex > tx2 ||
	  maskPR.y < ty1 || ey > ty2)
595
        {
596 597 598 599 600 601 602 603 604 605 606 607 608 609
	  /* 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
610
	    for (y = maskPR.y; y < ey; y++, data1 += maskPR.rowstride)
Elliot Lee's avatar
Elliot Lee committed
611
	    {
612
	      for (x = maskPR.x, data = data1; x < ex; x++, data++)
613
		if (*data)
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
		{
		  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
629 630 631 632
	    }
	}
    }

633 634
  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
635

636
  if (tx1 == GIMP_DRAWABLE (mask)->width && ty1 == GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
637 638
    {
      mask->empty = TRUE;
639 640 641 642
      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
643 644 645 646
    }
  else
    {
      mask->empty = FALSE;
647 648 649 650
      mask->x1    = tx1;
      mask->y1    = ty1;
      mask->x2    = tx2;
      mask->y2    = ty2;
Elliot Lee's avatar
Elliot Lee committed
651 652 653
    }
  mask->bounds_known = TRUE;

654 655 656 657 658
  *x1 = tx1;
  *x2 = tx2;
  *y1 = ty1;
  *y2 = ty2;

659
  return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
660 661
}

662
gboolean
Michael Natterer's avatar
Michael Natterer committed
663
gimp_channel_is_empty (GimpChannel *mask)
Elliot Lee's avatar
Elliot Lee committed
664
{
665 666 667 668
  PixelRegion  maskPR;
  guchar      *data;
  gint         x, y;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
669 670 671 672

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

673 674 675 676 677 678 679
  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
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
    {
      /*  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);

698 699 700 701 702 703
  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
704
  mask->boundary_known = TRUE;
705 706 707 708
  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
709 710 711 712 713

  return TRUE;
}

void
Michael Natterer's avatar
Michael Natterer committed
714 715 716 717 718
gimp_channel_add_segment (GimpChannel *mask,
			  gint         x,
			  gint         y,
			  gint         width,
			  gint         value)
Elliot Lee's avatar
Elliot Lee committed
719
{
720 721 722 723 724
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
725 726 727 728

  /*  check horizontal extents...  */
  x2 = x + width;
  if (x2 < 0) x2 = 0;
729
  if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
730
  if (x < 0) x = 0;
731
  if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
732 733 734
  width = x2 - x;
  if (!width) return;

735
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
736 737
    return;

738 739 740 741 742
  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
743 744 745 746 747 748 749 750 751 752 753 754 755 756
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data + value;
	  if (val > 255)
	    val = 255;
	  *data++ = val;
	}
    }
}

void
Michael Natterer's avatar
Michael Natterer committed
757 758 759 760 761
gimp_channel_sub_segment (GimpChannel *mask,
			  gint         x,
			  gint         y,
			  gint         width,
			  gint         value)
Elliot Lee's avatar
Elliot Lee committed
762
{
763 764 765 766 767
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
768 769 770 771

  /*  check horizontal extents...  */
  x2 = x + width;
  if (x2 < 0) x2 = 0;
772
  if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
773
  if (x < 0) x = 0;
774
  if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
775 776 777
  width = x2 - x;
  if (!width) return;

778
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
779 780
    return;

781 782 783 784
  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
785 786 787 788 789 790 791 792 793 794 795 796 797 798
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data - value;
	  if (val < 0)
	    val = 0;
	  *data++ = val;
	}
    }
}

void
Michael Natterer's avatar
Michael Natterer committed
799 800 801 802 803 804
gimp_channel_combine_rect (GimpChannel *mask,
			   ChannelOps   op,
			   gint         x,
			   gint         y,
			   gint         w,
			   gint         h)
Elliot Lee's avatar
Elliot Lee committed
805
{
806
  gint        x2, y2;
Elliot Lee's avatar
Elliot Lee committed
807
  PixelRegion maskPR;
808
  guchar      color;
809

810 811
  y2 = y + h;
  x2 = x + w;
Elliot Lee's avatar
Elliot Lee committed
812

813 814 815 816
  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
817

818
  if (x2 - x <= 0 || y2 - y <= 0)
Elliot Lee's avatar
Elliot Lee committed
819 820
    return;

821 822
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, x2 - x, y2 - y, TRUE);
823
  if (op == CHANNEL_OP_ADD  || op == CHANNEL_OP_REPLACE)
824 825 826
    color = 255;
  else
    color = 0;
827
  color_region (&maskPR, &color);
Elliot Lee's avatar
Elliot Lee committed
828 829

  /*  Determine new boundary  */
830
  if (mask->bounds_known && (op == CHANNEL_OP_ADD) && !mask->empty)
Elliot Lee's avatar
Elliot Lee committed
831 832 833 834 835 836 837 838 839 840
    {
      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);
    }
841
  else if (op == CHANNEL_OP_REPLACE || mask->empty)
Elliot Lee's avatar
Elliot Lee committed
842 843 844 845 846 847 848 849 850 851
    {
      mask->empty = FALSE;
      mask->x1 = x;
      mask->y1 = y;
      mask->x2 = x + w;
      mask->y2 = y + h;
    }
  else
    mask->bounds_known = FALSE;

852 853 854 855
  mask->x1 = CLAMP (mask->x1, 0, GIMP_DRAWABLE (mask)->width);
  mask->y1 = CLAMP (mask->y1, 0, GIMP_DRAWABLE (mask)->height);
  mask->x2 = CLAMP (mask->x2, 0, GIMP_DRAWABLE (mask)->width);
  mask->y2 = CLAMP (mask->y2, 0, GIMP_DRAWABLE (mask)->height);
Elliot Lee's avatar
Elliot Lee committed
856 857 858
}

void
Michael Natterer's avatar
Michael Natterer committed
859 860 861 862 863 864 865
gimp_channel_combine_ellipse (GimpChannel *mask,
			      ChannelOps   op,
			      gint         x,
			      gint         y,
			      gint         w,
			      gint         h,
			      gboolean     antialias)
Elliot Lee's avatar
Elliot Lee committed
866
{
867 868 869
  gint   i, j;
  gint   x0, x1, x2;
  gint   val, last;
870 871 872 873 874 875 876 877
  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
878 879 880 881 882 883 884 885 886 887 888 889 890

  if (!w || !h)
    return;

  a_sqr = (w * w / 4.0);
  b_sqr = (h * h / 4.0);
  aob_sqr = a_sqr / b_sqr;

  cx = x + w / 2.0;
  cy = y + h / 2.0;

  for (i = y; i < (y + h); i++)
    {
891
      if (i >= 0 && i < GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
892 893
	{
	  /*  Non-antialiased code  */
894
	  if (!antialias)
Elliot Lee's avatar
Elliot Lee committed
895 896 897 898 899 900 901 902
	    {
	      y_sqr = (i + 0.5 - cy) * (i + 0.5 - cy);
	      rad = sqrt (a_sqr - a_sqr * y_sqr / (double) b_sqr);
	      x1 = ROUND (cx - rad);
	      x2 = ROUND (cx + rad);

	      switch (op)
		{
903 904
		case CHANNEL_OP_ADD:
		case CHANNEL_OP_REPLACE:
Michael Natterer's avatar
Michael Natterer committed
905
		  gimp_channel_add_segment (mask, x1, i, (x2 - x1), 255);
Elliot Lee's avatar
Elliot Lee committed
906
		  break;
907
		case CHANNEL_OP_SUB:
Michael Natterer's avatar
Michael Natterer committed
908
		  gimp_channel_sub_segment (mask, x1, i, (x2 - x1), 255);
Elliot Lee's avatar
Elliot Lee committed
909
		  break;
Sven Neumann's avatar
Sven Neumann committed
910 911 912
		default:
		  g_warning ("Only ADD, REPLACE and SUB are valid for channel_combine!");
		  break;
Elliot Lee's avatar
Elliot Lee committed
913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941
		}
	    }
	  /*  antialiasing  */
	  else
	    {
	      x0 = x;
	      last = 0;
	      h_sqr = (i + 0.5 - cy) * (i + 0.5 - cy);
	      for (j = x; j < (x + w); j++)
		{
		  w_sqr = (j + 0.5 - cx) * (j + 0.5 - cx);

		  if (h_sqr != 0)
		    {
		      t0 = w_sqr / h_sqr;
		      t1 = a_sqr / (t0 + aob_sqr);
		      r = sqrt (t1 + t0 * t1);
		      rad = sqrt (w_sqr + h_sqr);
		      dist = rad - r;
		    }
		  else
		    dist = -1.0;

		  if (dist < -0.5)
		    val = 255;
		  else if (dist < 0.5)
		    val = (int) (255 * (1 - (dist + 0.5)));
		  else
		    val = 0;
942
		  
Elliot Lee's avatar
Elliot Lee committed
943 944 945 946
		  if (last != val && last)
		    {
		      switch (op)
			{
947 948
			case CHANNEL_OP_ADD:
			case CHANNEL_OP_REPLACE:
Michael Natterer's avatar
Michael Natterer committed
949
			  gimp_channel_add_segment (mask, x0, i, j - x0, last);
Elliot Lee's avatar
Elliot Lee committed
950
			  break;
951
			case CHANNEL_OP_SUB:
Michael Natterer's avatar
Michael Natterer committed
952
			  gimp_channel_sub_segment (mask, x0, i, j - x0, last);
Elliot Lee's avatar
Elliot Lee committed
953
			  break;
Sven Neumann's avatar
Sven Neumann committed
954 955 956
			default:
			  g_warning ("Only ADD, REPLACE and SUB are valid for channel_combine!");
			  break;
Elliot Lee's avatar
Elliot Lee committed
957 958 959 960 961 962 963
			}
		    }

		  if (last != val)
		    {
		      x0 = j;
		      last = val;
964 965 966 967
		      /* because we are symetric accross the y axis we can
			 skip ahead a bit if we are inside the ellipse*/
		      if (val == 255 && j < cx)
			j = cx + (cx - j) - 1;
Elliot Lee's avatar
Elliot Lee committed
968 969 970 971 972
		    }
		}

	      if (last)
		{
973
		  if (op == CHANNEL_OP_ADD || op == CHANNEL_OP_REPLACE)
Michael Natterer's avatar
Michael Natterer committed
974
		    gimp_channel_add_segment (mask, x0, i, j - x0, last);
975
		  else if (op == CHANNEL_OP_SUB)
Michael Natterer's avatar
Michael Natterer committed
976
		    gimp_channel_sub_segment (mask, x0, i, j - x0, last);
Sven Neumann's avatar
Sven Neumann committed
977 978
		  else
		    g_warning ("Only ADD, REPLACE and SUB are valid for channel_combine!");
Elliot Lee's avatar
Elliot Lee committed
979 980 981 982 983 984 985
		}
	    }

	}
    }

  /*  Determine new boundary  */
986
  if (mask->bounds_known && (op == CHANNEL_OP_ADD) && !mask->empty)
Elliot Lee's avatar
Elliot Lee committed
987 988 989 990 991 992 993 994 995 996
    {
      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);
    }
997
  else if (op == CHANNEL_OP_REPLACE || mask->empty)
Elliot Lee's avatar
Elliot Lee committed
998 999 1000 1001 1002 1003 1004 1005 1006 1007
    {
      mask->empty = FALSE;
      mask->x1 = x;
      mask->y1 = y;
      mask->x2 = x + w;
      mask->y2 = y + h;
    }
  else
    mask->bounds_known = FALSE;

1008 1009 1010 1011
  mask->x1 = CLAMP (mask->x1, 0, GIMP_DRAWABLE (mask)->width);
  mask->y1 = CLAMP (mask->y1, 0, GIMP_DRAWABLE (mask)->height);
  mask->x2 = CLAMP (mask->x2, 0, GIMP_DRAWABLE (mask)->width);
  mask->y2 = CLAMP (mask->y2, 0, GIMP_DRAWABLE (mask)->height);
Elliot Lee's avatar
Elliot Lee committed
1012 1013
}

1014
static void
Michael Natterer's avatar
Michael Natterer committed
1015 1016 1017
gimp_channel_combine_sub_region_add (void        *unused,
				     PixelRegion *srcPR,
				     PixelRegion *destPR)
1018
{
1019
  guchar *src, *dest;