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 20
#include "config.h"

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

24
#include <glib-object.h>
Sven Neumann's avatar
Sven Neumann committed
25

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

Michael Natterer's avatar
Michael Natterer committed
29
#include "core-types.h"
Sven Neumann's avatar
Sven Neumann committed
30

Michael Natterer's avatar
Michael Natterer committed
31 32
#include "base/boundary.h"
#include "base/gimplut.h"
33
#include "base/lut-funcs.h"
Michael Natterer's avatar
Michael Natterer committed
34 35 36 37 38 39
#include "base/pixel-processor.h"
#include "base/pixel-region.h"
#include "base/temp-buf.h"
#include "base/tile.h"
#include "base/tile-manager.h"

40 41
#include "paint-funcs/paint-funcs.h"

42
#include "gimpimage.h"
43
#include "gimpimage-mask.h"
44 45
#include "gimpchannel.h"
#include "gimplayer.h"
46
#include "gimpparasitelist.h"
Michael Natterer's avatar
Michael Natterer committed
47

48
#include "undo.h"
49

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


53 54
static void    gimp_channel_class_init  (GimpChannelClass *klass);
static void    gimp_channel_init        (GimpChannel      *channel);
55

56 57 58
static void    gimp_channel_finalize    (GObject          *object);

static gsize   gimp_channel_get_memsize (GimpObject       *object);
59

60

61
static GimpDrawableClass * parent_class = NULL;
62

63

64
GType
65
gimp_channel_get_type (void)
66
{
67
  static GType channel_type = 0;
68

69
  if (! channel_type)
70
    {
71
      static const GTypeInfo channel_info =
72
      {
73 74 75 76 77 78
        sizeof (GimpChannelClass),
	(GBaseInitFunc) NULL,
	(GBaseFinalizeFunc) NULL,
	(GClassInitFunc) gimp_channel_class_init,
	NULL,		/* class_finalize */
	NULL,		/* class_data     */
79
	sizeof (GimpChannel),
80 81
	0,              /* n_preallocs    */
	(GInstanceInitFunc) gimp_channel_init,
82 83
      };

84 85 86
      channel_type = g_type_register_static (GIMP_TYPE_DRAWABLE,
					     "GimpChannel",
					     &channel_info, 0);
87 88 89 90 91 92
    }

  return channel_type;
}

static void
93
gimp_channel_class_init (GimpChannelClass *klass)
94
{
95 96
  GObjectClass    *object_class;
  GimpObjectClass *gimp_object_class;
97

98 99
  object_class      = G_OBJECT_CLASS (klass);
  gimp_object_class = GIMP_OBJECT_CLASS (klass);
100

101
  parent_class = g_type_class_peek_parent (klass);
102

103 104 105
  object_class->finalize         = gimp_channel_finalize;

  gimp_object_class->get_memsize = gimp_channel_get_memsize;
106 107 108 109 110
}

static void
gimp_channel_init (GimpChannel *channel)
{
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
  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;
127
}
Elliot Lee's avatar
Elliot Lee committed
128

129
static void
130
gimp_channel_finalize (GObject *object)
131 132 133 134 135 136 137 138
{
  GimpChannel *channel;

  g_return_if_fail (GIMP_IS_CHANNEL (object));

  channel = GIMP_CHANNEL (object);

  if (channel->segs_in)
139 140 141 142 143
    {
      g_free (channel->segs_in);
      channel->segs_in = NULL;
    }

144
  if (channel->segs_out)
145 146 147 148
    {
      g_free (channel->segs_out);
      channel->segs_out = NULL;
    }
149

150
  G_OBJECT_CLASS (parent_class)->finalize (object);
151
}
Elliot Lee's avatar
Elliot Lee committed
152

153 154 155 156 157 158 159 160 161 162 163 164 165 166
static gsize
gimp_channel_get_memsize (GimpObject *object)
{
  GimpChannel *channel;
  gsize        memsize = 0;

  channel = GIMP_CHANNEL (object);

  memsize += channel->num_segs_in  * sizeof (BoundSeg);
  memsize += channel->num_segs_out * sizeof (BoundSeg);

  return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object);
}

Elliot Lee's avatar
Elliot Lee committed
167
static void
Michael Natterer's avatar
Michael Natterer committed
168 169
gimp_channel_validate (TileManager *tm,
		       Tile        *tile)
Elliot Lee's avatar
Elliot Lee committed
170 171
{
  /*  Set the contents of the tile to empty  */
scott's avatar
scott committed
172
  memset (tile_data_pointer (tile, 0, 0), 
173
	  TRANSPARENT_OPACITY, tile_size (tile));
Elliot Lee's avatar
Elliot Lee committed
174 175
}

Michael Natterer's avatar
Michael Natterer committed
176 177
GimpChannel *
gimp_channel_new (GimpImage     *gimage,
178 179
		  gint           width,
		  gint           height,
Michael Natterer's avatar
Michael Natterer committed
180 181
		  const gchar   *name,
		  const GimpRGB *color)
Elliot Lee's avatar
Elliot Lee committed
182
{
Michael Natterer's avatar
Michael Natterer committed
183
  GimpChannel *channel;
184 185

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

Michael Natterer's avatar
Michael Natterer committed
187
  channel = g_object_new (GIMP_TYPE_CHANNEL, NULL);
Elliot Lee's avatar
Elliot Lee committed
188

Michael Natterer's avatar
Michael Natterer committed
189
  gimp_drawable_configure (GIMP_DRAWABLE (channel),
190
			   gimage, width, height, GIMP_GRAY_IMAGE, name);
Elliot Lee's avatar
Elliot Lee committed
191 192

  /*  set the channel color and opacity  */
193
  channel->color       = *color;
194

195
  channel->show_masked = TRUE;
Elliot Lee's avatar
Elliot Lee committed
196 197

  /*  selection mask variables  */
198 199
  channel->x2          = width;
  channel->y2          = height;
Elliot Lee's avatar
Elliot Lee committed
200 201 202 203

  return channel;
}

Michael Natterer's avatar
Michael Natterer committed
204
GimpChannel *
205
gimp_channel_copy (const GimpChannel *channel,
206
                   GType              new_type,
207 208 209 210 211 212
                   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
213
{
Michael Natterer's avatar
Michael Natterer committed
214
  GimpChannel *new_channel;
215 216 217 218 219 220 221 222 223 224

  g_return_val_if_fail (GIMP_IS_CHANNEL (channel), NULL);
  g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_CHANNEL), NULL);

  new_channel = GIMP_CHANNEL (gimp_drawable_copy (GIMP_DRAWABLE (channel),
                                                  new_type,
                                                  FALSE));

  /*  set the channel color and opacity  */
  new_channel->color       = channel->color;
225

Elliot Lee's avatar
Elliot Lee committed
226 227
  new_channel->show_masked = channel->show_masked;

228 229 230
  /*  selection mask variables  */
  new_channel->x2          = gimp_drawable_width (GIMP_DRAWABLE (new_channel));
  new_channel->y2          = gimp_drawable_height (GIMP_DRAWABLE (new_channel));
Elliot Lee's avatar
Elliot Lee committed
231 232 233 234

  return new_channel;
}

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

Michael Natterer's avatar
Michael Natterer committed
242 243 244 245 246 247 248 249 250
  if (gimp_rgba_distance (&channel->color, color) > 0.0001)
    {
      channel->color = *color;

      gimp_drawable_update (GIMP_DRAWABLE (channel),
			    0, 0,
			    GIMP_DRAWABLE (channel)->width,
			    GIMP_DRAWABLE (channel)->height);
    }
251
}
252

253
const GimpRGB *
Michael Natterer's avatar
Michael Natterer committed
254
gimp_channel_get_color (const GimpChannel *channel)
255
{
256 257 258
  g_return_val_if_fail (GIMP_IS_CHANNEL (channel), NULL);

  return &channel->color;
259
}
260

261
gint
Michael Natterer's avatar
Michael Natterer committed
262
gimp_channel_get_opacity (const GimpChannel *channel)
263 264 265
{
  g_return_val_if_fail (GIMP_IS_CHANNEL (channel), 0);

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

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

  opacity = CLAMP (opacity, 0, 100);

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

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

289 290
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

Elliot Lee's avatar
Elliot Lee committed
291 292 293 294
  if (new_width == 0 || new_height == 0)
    return;

  /*  Update the old channel position  */
Michael Natterer's avatar
Michael Natterer committed
295 296 297 298
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
299

300 301 302
  /*  Allocate the new channel  */
  new_tiles = tile_manager_new (new_width, new_height, 1);

Elliot Lee's avatar
Elliot Lee committed
303
  /*  Configure the pixel regions  */
304 305 306
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (channel)->width,
307 308
		     GIMP_DRAWABLE (channel)->height,
                     FALSE);
Elliot Lee's avatar
Elliot Lee committed
309

310 311 312 313
  pixel_region_init (&destPR, new_tiles,
                     0, 0,
                     new_width, new_height,
                     TRUE);
Elliot Lee's avatar
Elliot Lee committed
314

315
  /*  Sclae the channel  */
316
  scale_region (&srcPR, &destPR, interpolation_type);
Elliot Lee's avatar
Elliot Lee committed
317 318

  /*  Push the channel on the undo stack  */
319
  undo_push_channel_mod (gimp_item_get_image (GIMP_ITEM (channel)), channel);
Elliot Lee's avatar
Elliot Lee committed
320 321

  /*  Configure the new channel  */
322 323
  GIMP_DRAWABLE (channel)->tiles  = new_tiles;
  GIMP_DRAWABLE (channel)->width  = new_width;
324
  GIMP_DRAWABLE (channel)->height = new_height;
Elliot Lee's avatar
Elliot Lee committed
325 326 327 328 329

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

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

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

350 351
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

352
  if (new_width == 0 || new_height == 0)
Elliot Lee's avatar
Elliot Lee committed
353 354
    return;

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

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

  /*  Determine whether the new channel needs to be initially cleared  */
395 396
  if ((new_width  > GIMP_DRAWABLE (channel)->width) ||
      (new_height > GIMP_DRAWABLE (channel)->height) ||
Elliot Lee's avatar
Elliot Lee committed
397 398 399 400 401 402 403 404 405 406 407
      (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)
    {
408 409 410 411 412
      pixel_region_init (&destPR, new_tiles,
                         0, 0,
                         new_width, new_height,
                         TRUE);

Elliot Lee's avatar
Elliot Lee committed
413 414 415 416 417 418 419 420 421
      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  */
422
  undo_push_channel_mod (gimp_item_get_image (GIMP_ITEM (channel)), channel);
Elliot Lee's avatar
Elliot Lee committed
423 424

  /*  Configure the new channel  */
425 426 427
  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
428 429 430 431 432

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

  /*  update the new channel area  */
Michael Natterer's avatar
Michael Natterer committed
433 434 435 436
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
437 438
}

439

440 441 442
/******************************/
/*  selection mask functions  */
/******************************/
Elliot Lee's avatar
Elliot Lee committed
443

Michael Natterer's avatar
Michael Natterer committed
444 445
GimpChannel *
gimp_channel_new_mask (GimpImage *gimage,
446 447
		       gint       width,
		       gint       height)
Elliot Lee's avatar
Elliot Lee committed
448
{
Michael Natterer's avatar
Michael Natterer committed
449 450
  GimpRGB      black = { 0.0, 0.0, 0.0, 0.5 };
  GimpChannel *new_channel;
Elliot Lee's avatar
Elliot Lee committed
451

452 453
  g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);

Elliot Lee's avatar
Elliot Lee committed
454
  /*  Create the new channel  */
Michael Natterer's avatar
Michael Natterer committed
455 456
  new_channel = gimp_channel_new (gimage, width, height,
				  _("Selection Mask"), &black);
Elliot Lee's avatar
Elliot Lee committed
457 458

  /*  Set the validate procedure  */
459
  tile_manager_set_validate_proc (GIMP_DRAWABLE (new_channel)->tiles,
Michael Natterer's avatar
Michael Natterer committed
460
				  gimp_channel_validate);
Elliot Lee's avatar
Elliot Lee committed
461 462 463 464

  return new_channel;
}

465
gboolean
Michael Natterer's avatar
Michael Natterer committed
466 467 468 469 470 471 472 473 474
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
475
{
476
  gint        x3, y3, x4, y4;
Elliot Lee's avatar
Elliot Lee committed
477 478
  PixelRegion bPR;

479 480
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);

Elliot Lee's avatar
Elliot Lee committed
481 482 483 484 485 486 487 488
  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
489
      if (gimp_channel_bounds (mask, &x3, &y3, &x4, &y4))
Elliot Lee's avatar
Elliot Lee committed
490
	{
491 492
	  pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles,
			     x3, y3, (x4 - x3), (y4 - y3), FALSE);
Elliot Lee's avatar
Elliot Lee committed
493 494 495 496
	  mask->segs_out = find_mask_boundary (&bPR, &mask->num_segs_out,
					       IgnoreBounds,
					       x1, y1,
					       x2, y2);
497 498 499 500
	  x1 = MAX (x1, x3);
	  y1 = MAX (y1, y3);
	  x2 = MIN (x2, x4);
	  y2 = MIN (y2, y4);
Elliot Lee's avatar
Elliot Lee committed
501 502 503

	  if (x2 > x1 && y2 > y1)
	    {
504 505 506 507
	      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
508 509 510 511 512 513 514
	      mask->segs_in =  find_mask_boundary (&bPR, &mask->num_segs_in,
						   WithinBounds,
						   x1, y1,
						   x2, y2);
	    }
	  else
	    {
515
	      mask->segs_in     = NULL;
Elliot Lee's avatar
Elliot Lee committed
516 517 518 519 520
	      mask->num_segs_in = 0;
	    }
	}
      else
	{
521 522 523
	  mask->segs_in      = NULL;
	  mask->segs_out     = NULL;
	  mask->num_segs_in  = 0;
Elliot Lee's avatar
Elliot Lee committed
524 525 526 527 528
	  mask->num_segs_out = 0;
	}
      mask->boundary_known = TRUE;
    }

529 530 531
  *segs_in      = mask->segs_in;
  *segs_out     = mask->segs_out;
  *num_segs_in  = mask->num_segs_in;
Elliot Lee's avatar
Elliot Lee committed
532 533 534 535 536
  *num_segs_out = mask->num_segs_out;

  return TRUE;
}

537
gint
Michael Natterer's avatar
Michael Natterer committed
538 539 540
gimp_channel_value (GimpChannel *mask,
		    gint         x,
		    gint         y)
Elliot Lee's avatar
Elliot Lee committed
541 542
{
  Tile *tile;
543
  gint  val;
Elliot Lee's avatar
Elliot Lee committed
544

545 546
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), 0);

Elliot Lee's avatar
Elliot Lee committed
547 548 549 550 551 552 553 554 555 556
  /*  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
    {
557 558
      if (x < 0 || x >= GIMP_DRAWABLE (mask)->width ||
	  y < 0 || y >= GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
559 560 561
	return 0;
    }

562 563
  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));
564
  tile_release (tile, FALSE);
Elliot Lee's avatar
Elliot Lee committed
565 566 567 568

  return val;
}

569
gboolean
Michael Natterer's avatar
Michael Natterer committed
570 571 572 573 574
gimp_channel_bounds (GimpChannel *mask,
		     gint        *x1,
		     gint        *y1,
		     gint        *x2,
		     gint        *y2)
Elliot Lee's avatar
Elliot Lee committed
575
{
576 577 578 579 580 581 582
  PixelRegion  maskPR;
  guchar      *data, *data1;
  gint         x, y;
  gint         ex, ey;
  gint         tx1, tx2, ty1, ty2;
  gint         minx, maxx;
  gpointer     pr;
583

584 585
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);

Elliot Lee's avatar
Elliot Lee committed
586 587 588 589 590 591 592 593
  /*  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;

594
      return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
595 596 597
    }

  /*  go through and calculate the bounds  */
598 599
  tx1 = GIMP_DRAWABLE (mask)->width;
  ty1 = GIMP_DRAWABLE (mask)->height;
600 601
  tx2 = 0;
  ty2 = 0;
Elliot Lee's avatar
Elliot Lee committed
602

603 604 605 606 607 608 609
  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
610
    {
611
      data1 = data = maskPR.data;
Elliot Lee's avatar
Elliot Lee committed
612 613
      ex = maskPR.x + maskPR.w;
      ey = maskPR.y + maskPR.h;
614 615
      /* only check the pixels if this tile is not fully within the currently
	 computed bounds */
616 617
      if (maskPR.x < tx1 || ex > tx2 ||
	  maskPR.y < ty1 || ey > ty2)
618
        {
619 620 621 622 623 624 625 626 627 628 629 630 631 632
	  /* 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
633
	    for (y = maskPR.y; y < ey; y++, data1 += maskPR.rowstride)
Elliot Lee's avatar
Elliot Lee committed
634
	    {
635
	      for (x = maskPR.x, data = data1; x < ex; x++, data++)
636
		if (*data)
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
		{
		  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
652 653 654 655
	    }
	}
    }

656 657
  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
658

659
  if (tx1 == GIMP_DRAWABLE (mask)->width && ty1 == GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
660 661
    {
      mask->empty = TRUE;
662 663 664 665
      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
666 667 668 669
    }
  else
    {
      mask->empty = FALSE;
670 671 672 673
      mask->x1    = tx1;
      mask->y1    = ty1;
      mask->x2    = tx2;
      mask->y2    = ty2;
Elliot Lee's avatar
Elliot Lee committed
674 675 676
    }
  mask->bounds_known = TRUE;

677 678 679 680 681
  *x1 = tx1;
  *x2 = tx2;
  *y1 = ty1;
  *y2 = ty2;

682
  return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
683 684
}

685
gboolean
Michael Natterer's avatar
Michael Natterer committed
686
gimp_channel_is_empty (GimpChannel *mask)
Elliot Lee's avatar
Elliot Lee committed
687
{
688 689 690 691
  PixelRegion  maskPR;
  guchar      *data;
  gint         x, y;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
692

693 694
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);

Elliot Lee's avatar
Elliot Lee committed
695 696 697
  if (mask->bounds_known)
    return mask->empty;

698 699 700 701 702 703 704
  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
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
    {
      /*  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);

723 724 725 726 727 728
  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
729
  mask->boundary_known = TRUE;
730 731 732 733
  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
734 735 736 737 738

  return TRUE;
}

void
Michael Natterer's avatar
Michael Natterer committed
739 740 741
gimp_channel_add_segment (GimpChannel *mask,
			  gint         x,
			  gint         y,
742 743
			  gint         width,
			  gint         value)
Elliot Lee's avatar
Elliot Lee committed
744
{
745 746 747 748 749
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
750

751 752
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

Elliot Lee's avatar
Elliot Lee committed
753 754 755
  /*  check horizontal extents...  */
  x2 = x + width;
  if (x2 < 0) x2 = 0;
756
  if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
757
  if (x < 0) x = 0;
758
  if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
759 760 761
  width = x2 - x;
  if (!width) return;

762
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
763 764
    return;

765 766 767 768 769
  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
770 771 772 773 774 775 776 777 778 779 780 781 782 783
    {
      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
784 785 786
gimp_channel_sub_segment (GimpChannel *mask,
			  gint         x,
			  gint         y,
787 788
			  gint         width,
			  gint         value)
Elliot Lee's avatar
Elliot Lee committed
789
{
790 791 792 793 794
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
795

796 797
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

Elliot Lee's avatar
Elliot Lee committed
798 799 800
  /*  check horizontal extents...  */
  x2 = x + width;
  if (x2 < 0) x2 = 0;
801
  if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
802
  if (x < 0) x = 0;
803
  if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
804 805 806
  width = x2 - x;
  if (!width) return;

807
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
808 809
    return;

810 811 812 813
  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
814 815 816 817 818 819 820 821 822 823 824 825 826 827
    {
      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
828 829 830 831
gimp_channel_combine_rect (GimpChannel *mask,
			   ChannelOps   op,
			   gint         x,
			   gint         y,
832 833
			   gint         w,
			   gint         h)
Elliot Lee's avatar
Elliot Lee committed
834
{
835
  gint        x2, y2;
Elliot Lee's avatar
Elliot Lee committed
836
  PixelRegion maskPR;
837
  guchar      color;
838

839 840
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

841 842
  y2 = y + h;
  x2 = x + w;
Elliot Lee's avatar
Elliot Lee committed
843

844 845 846 847
  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
848

849
  if (x2 - x <= 0 || y2 - y <= 0)
Elliot Lee's avatar
Elliot Lee committed
850 851
    return;

852 853
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, x2 - x, y2 - y, TRUE);
854
  if (op == CHANNEL_OP_ADD  || op == CHANNEL_OP_REPLACE)
855 856 857
    color = 255;
  else
    color = 0;
858
  color_region (&maskPR, &color);
Elliot Lee's avatar
Elliot Lee committed
859 860

  /*  Determine new boundary  */
861
  if (mask->bounds_known && (op == CHANNEL_OP_ADD) && !mask->empty)
Elliot Lee's avatar
Elliot Lee committed
862 863 864 865 866 867 868 869 870 871
    {
      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);
    }
872
  else if (op == CHANNEL_OP_REPLACE || mask->empty)
Elliot Lee's avatar
Elliot Lee committed
873 874 875 876 877 878 879 880 881 882
    {
      mask->empty = FALSE;
      mask->x1 = x;
      mask->y1 = y;
      mask->x2 = x + w;
      mask->y2 = y + h;
    }
  else
    mask->bounds_known = FALSE;

883 884 885 886
  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
887 888 889
}

void
Michael Natterer's avatar
Michael Natterer committed
890 891 892 893
gimp_channel_combine_ellipse (GimpChannel *mask,
			      ChannelOps   op,
			      gint         x,
			      gint         y,
894 895
			      gint         w,
			      gint         h,
Michael Natterer's avatar
Michael Natterer committed
896
			      gboolean     antialias)
Elliot Lee's avatar
Elliot Lee committed
897
{
898 899 900
  gint   i, j;
  gint   x0, x1, x2;
  gint   val, last;
901 902 903 904 905 906 907 908
  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
909

910 911
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

Elliot Lee's avatar
Elliot Lee committed
912 913 914 915 916 917 918 919 920 921 922 923
  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++)
    {
924
      if (i >= 0 && i < GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
925 926
	{
	  /*  Non-antialiased code  */
927
	  if (!antialias)
Elliot Lee's avatar
Elliot Lee committed
928 929 930 931 932 933 934 935
	    {
	      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)
		{
936 937
		case CHANNEL_OP_ADD:
		case CHANNEL_OP_REPLACE:
Michael Natterer's avatar
Michael Natterer committed
938
		  gimp_channel_add_segment (mask, x1, i, (x2 - x1), 255);
Elliot Lee's avatar
Elliot Lee committed
939
		  break;
940
		case CHANNEL_OP_SUB:
Michael Natterer's avatar
Michael Natterer committed
941
		  gimp_channel_sub_segment (mask, x1, i, (x2 - x1), 255);
Elliot Lee's avatar
Elliot Lee committed
942
		  break;
Sven Neumann's avatar
Sven Neumann committed
943
		default:
944
		  g_warning ("Only ADD, REPLACE, and SUB are valid for channel_combine!");
Sven Neumann's avatar
Sven Neumann committed
945
		  break;
Elliot Lee's avatar
Elliot Lee committed
946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974
		}
	    }
	  /*  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;