gimpchannel.c 42.8 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
#include "base/pixel-processor.h"
#include "base/pixel-region.h"
#include "base/tile.h"
#include "base/tile-manager.h"

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

41
#include "gimpimage.h"
42 43
#include "gimpchannel.h"
#include "gimplayer.h"
44
#include "gimpparasitelist.h"
Michael Natterer's avatar
Michael Natterer committed
45

46
#include "undo.h"
47

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


51 52
static void    gimp_channel_class_init  (GimpChannelClass *klass);
static void    gimp_channel_init        (GimpChannel      *channel);
53

54 55 56
static void    gimp_channel_finalize    (GObject          *object);

static gsize   gimp_channel_get_memsize (GimpObject       *object);
57

58 59 60 61
static void    gimp_channel_push_undo   (GimpChannel      *mask);
static void    gimp_channel_validate    (TileManager      *tm,
                                         Tile             *tile);

62

63
static GimpDrawableClass * parent_class = NULL;
64

65

66
GType
67
gimp_channel_get_type (void)
68
{
69
  static GType channel_type = 0;
70

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

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

  return channel_type;
}

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

100 101
  object_class      = G_OBJECT_CLASS (klass);
  gimp_object_class = GIMP_OBJECT_CLASS (klass);
102

103
  parent_class = g_type_class_peek_parent (klass);
104

105 106 107
  object_class->finalize         = gimp_channel_finalize;

  gimp_object_class->get_memsize = gimp_channel_get_memsize;
108 109 110 111 112
}

static void
gimp_channel_init (GimpChannel *channel)
{
113
  gimp_rgba_set (&channel->color, 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE);
114 115 116 117

  channel->show_masked = FALSE;

  /*  Selection mask variables  */
118
  channel->boundary_known = FALSE;
119 120 121 122
  channel->segs_in        = NULL;
  channel->segs_out       = NULL;
  channel->num_segs_in    = 0;
  channel->num_segs_out   = 0;
123 124
  channel->empty          = FALSE;
  channel->bounds_known   = FALSE;
125 126 127 128
  channel->x1             = 0;
  channel->y1             = 0;
  channel->x2             = 0;
  channel->y2             = 0;
129
}
Elliot Lee's avatar
Elliot Lee committed
130

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

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

167 168 169 170 171 172 173
static void
gimp_channel_push_undo (GimpChannel *mask)
{
  GimpImage *gimage;

  gimage = gimp_item_get_image (GIMP_ITEM (mask));

174 175
  g_return_if_fail (gimage != NULL);

176 177 178 179 180 181 182 183
  undo_push_mask (gimage, mask);

  mask->boundary_known = FALSE;

  /*  invalidate the preview  */
  GIMP_DRAWABLE (mask)->preview_valid = FALSE;
}

Elliot Lee's avatar
Elliot Lee committed
184
static void
Michael Natterer's avatar
Michael Natterer committed
185 186
gimp_channel_validate (TileManager *tm,
		       Tile        *tile)
Elliot Lee's avatar
Elliot Lee committed
187 188
{
  /*  Set the contents of the tile to empty  */
scott's avatar
scott committed
189
  memset (tile_data_pointer (tile, 0, 0), 
190
	  TRANSPARENT_OPACITY, tile_size (tile));
Elliot Lee's avatar
Elliot Lee committed
191 192
}

193 194 195

/*  public functions  */

Michael Natterer's avatar
Michael Natterer committed
196 197
GimpChannel *
gimp_channel_new (GimpImage     *gimage,
198 199
		  gint           width,
		  gint           height,
Michael Natterer's avatar
Michael Natterer committed
200 201
		  const gchar   *name,
		  const GimpRGB *color)
Elliot Lee's avatar
Elliot Lee committed
202
{
Michael Natterer's avatar
Michael Natterer committed
203
  GimpChannel *channel;
204

205
  g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);
206
  g_return_val_if_fail (color != NULL, NULL);
Elliot Lee's avatar
Elliot Lee committed
207

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

Michael Natterer's avatar
Michael Natterer committed
210
  gimp_drawable_configure (GIMP_DRAWABLE (channel),
211
			   gimage, width, height, GIMP_GRAY_IMAGE, name);
Elliot Lee's avatar
Elliot Lee committed
212 213

  /*  set the channel color and opacity  */
214
  channel->color       = *color;
215

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

  /*  selection mask variables  */
219 220
  channel->x2          = width;
  channel->y2          = height;
Elliot Lee's avatar
Elliot Lee committed
221 222 223 224

  return channel;
}

Michael Natterer's avatar
Michael Natterer committed
225
GimpChannel *
226
gimp_channel_copy (const GimpChannel *channel,
227
                   GType              new_type,
228 229 230 231 232 233
                   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
234
{
Michael Natterer's avatar
Michael Natterer committed
235
  GimpChannel *new_channel;
236 237 238 239 240 241 242 243 244

  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  */
245
  new_channel->color        = channel->color;
246

247
  new_channel->show_masked  = channel->show_masked;
Elliot Lee's avatar
Elliot Lee committed
248

249
  /*  selection mask variables  */
250 251 252 253 254 255
  new_channel->bounds_known = channel->bounds_known;
  new_channel->empty        = channel->empty;
  new_channel->x1           = channel->x1;
  new_channel->y1           = channel->y1;
  new_channel->x2           = channel->x2;
  new_channel->y2           = channel->y2;
Elliot Lee's avatar
Elliot Lee committed
256 257 258 259

  return new_channel;
}

260
void 
Michael Natterer's avatar
Michael Natterer committed
261 262
gimp_channel_set_color (GimpChannel   *channel,
			const GimpRGB *color)
263
{
264 265
  g_return_if_fail (GIMP_IS_CHANNEL (channel));
  g_return_if_fail (color != NULL);
266

Michael Natterer's avatar
Michael Natterer committed
267 268 269 270 271 272 273 274 275
  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);
    }
276
}
277

278 279 280
void
gimp_channel_get_color (const GimpChannel *channel,
                        GimpRGB           *color)
281
{
282 283
  g_return_if_fail (GIMP_IS_CHANNEL (channel));
  g_return_if_fail (color != NULL);
284

285
  *color = channel->color;
286
}
287

288
gdouble
Michael Natterer's avatar
Michael Natterer committed
289
gimp_channel_get_opacity (const GimpChannel *channel)
290
{
291
  g_return_val_if_fail (GIMP_IS_CHANNEL (channel), GIMP_OPACITY_TRANSPARENT);
292

293
  return channel->color.a;
294 295
}

296
void
Michael Natterer's avatar
Michael Natterer committed
297
gimp_channel_set_opacity (GimpChannel *channel,
298
			  gdouble      opacity)
299
{
300 301
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

302
  opacity = CLAMP (opacity, GIMP_OPACITY_TRANSPARENT, GIMP_OPACITY_OPAQUE);
303

304
  channel->color.a = opacity;
305 306
}

307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
gboolean
gimp_channel_get_show_masked (GimpChannel *channel)
{
  g_return_val_if_fail (GIMP_IS_CHANNEL (channel), FALSE);

  return channel->show_masked;
}

void
gimp_channel_set_show_masked (GimpChannel *channel,
                              gboolean     show_masked)
{
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

  if (show_masked != channel->show_masked)
    {
      channel->show_masked = show_masked ? TRUE : FALSE;

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

Elliot Lee's avatar
Elliot Lee committed
332
void
333 334 335 336
gimp_channel_scale (GimpChannel           *channel,
		    gint                   new_width,
		    gint                   new_height,
                    GimpInterpolationType  interpolation_type)
Elliot Lee's avatar
Elliot Lee committed
337
{
338
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
339 340
  TileManager *new_tiles;

341 342
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

Elliot Lee's avatar
Elliot Lee committed
343 344 345 346
  if (new_width == 0 || new_height == 0)
    return;

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

352 353 354
  /*  Allocate the new channel  */
  new_tiles = tile_manager_new (new_width, new_height, 1);

Elliot Lee's avatar
Elliot Lee committed
355
  /*  Configure the pixel regions  */
356 357 358
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (channel)->width,
359 360
		     GIMP_DRAWABLE (channel)->height,
                     FALSE);
Elliot Lee's avatar
Elliot Lee committed
361

362 363 364 365
  pixel_region_init (&destPR, new_tiles,
                     0, 0,
                     new_width, new_height,
                     TRUE);
Elliot Lee's avatar
Elliot Lee committed
366

367
  /*  Sclae the channel  */
368
  scale_region (&srcPR, &destPR, interpolation_type);
Elliot Lee's avatar
Elliot Lee committed
369 370

  /*  Push the channel on the undo stack  */
371
  undo_push_channel_mod (gimp_item_get_image (GIMP_ITEM (channel)), channel);
Elliot Lee's avatar
Elliot Lee committed
372 373

  /*  Configure the new channel  */
374 375
  GIMP_DRAWABLE (channel)->tiles  = new_tiles;
  GIMP_DRAWABLE (channel)->width  = new_width;
376
  GIMP_DRAWABLE (channel)->height = new_height;
Elliot Lee's avatar
Elliot Lee committed
377 378 379 380 381

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

  /*  Update the new channel position  */
Michael Natterer's avatar
Michael Natterer committed
382 383 384 385
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
386 387 388
}

void
Michael Natterer's avatar
Michael Natterer committed
389
gimp_channel_resize (GimpChannel *channel,
390 391
		     gint         new_width,
		     gint         new_height,
Michael Natterer's avatar
Michael Natterer committed
392 393
		     gint         offx,
		     gint         offy)
Elliot Lee's avatar
Elliot Lee committed
394
{
395
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
396
  TileManager *new_tiles;
397 398 399 400
  guchar       bg = 0;
  gint         clear;
  gint         w, h;
  gint         x1, y1, x2, y2;
Elliot Lee's avatar
Elliot Lee committed
401

402 403
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

404
  if (new_width == 0 || new_height == 0)
Elliot Lee's avatar
Elliot Lee committed
405 406
    return;

407 408 409 410
  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);
411

Elliot Lee's avatar
Elliot Lee committed
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
  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
438 439 440 441
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
442 443

  /*  Configure the pixel regions  */
444 445
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
		     x1, y1, w, h, FALSE);
Elliot Lee's avatar
Elliot Lee committed
446 447

  /*  Determine whether the new channel needs to be initially cleared  */
448 449
  if ((new_width  > GIMP_DRAWABLE (channel)->width) ||
      (new_height > GIMP_DRAWABLE (channel)->height) ||
Elliot Lee's avatar
Elliot Lee committed
450 451 452 453 454 455 456 457 458 459 460
      (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)
    {
461 462 463 464 465
      pixel_region_init (&destPR, new_tiles,
                         0, 0,
                         new_width, new_height,
                         TRUE);

Elliot Lee's avatar
Elliot Lee committed
466 467 468 469 470 471 472 473 474
      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  */
475
  undo_push_channel_mod (gimp_item_get_image (GIMP_ITEM (channel)), channel);
Elliot Lee's avatar
Elliot Lee committed
476 477

  /*  Configure the new channel  */
478 479 480
  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
481 482 483 484 485

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

  /*  update the new channel area  */
Michael Natterer's avatar
Michael Natterer committed
486 487 488 489
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
490 491
}

492

493 494 495
/******************************/
/*  selection mask functions  */
/******************************/
Elliot Lee's avatar
Elliot Lee committed
496

Michael Natterer's avatar
Michael Natterer committed
497 498
GimpChannel *
gimp_channel_new_mask (GimpImage *gimage,
499 500
		       gint       width,
		       gint       height)
Elliot Lee's avatar
Elliot Lee committed
501
{
Michael Natterer's avatar
Michael Natterer committed
502 503
  GimpRGB      black = { 0.0, 0.0, 0.0, 0.5 };
  GimpChannel *new_channel;
Elliot Lee's avatar
Elliot Lee committed
504

505 506
  g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);

Elliot Lee's avatar
Elliot Lee committed
507
  /*  Create the new channel  */
Michael Natterer's avatar
Michael Natterer committed
508 509
  new_channel = gimp_channel_new (gimage, width, height,
				  _("Selection Mask"), &black);
Elliot Lee's avatar
Elliot Lee committed
510 511

  /*  Set the validate procedure  */
512
  tile_manager_set_validate_proc (GIMP_DRAWABLE (new_channel)->tiles,
Michael Natterer's avatar
Michael Natterer committed
513
				  gimp_channel_validate);
Elliot Lee's avatar
Elliot Lee committed
514 515 516 517

  return new_channel;
}

518
gboolean
Michael Natterer's avatar
Michael Natterer committed
519 520 521 522 523 524 525 526 527
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
528
{
529
  gint        x3, y3, x4, y4;
Elliot Lee's avatar
Elliot Lee committed
530 531
  PixelRegion bPR;

532
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);
533 534 535 536
  g_return_val_if_fail (segs_in != NULL, FALSE);
  g_return_val_if_fail (segs_out != NULL, FALSE);
  g_return_val_if_fail (num_segs_in != NULL, FALSE);
  g_return_val_if_fail (num_segs_out != NULL, FALSE);
537

Elliot Lee's avatar
Elliot Lee committed
538 539 540 541 542 543 544 545
  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
546
      if (gimp_channel_bounds (mask, &x3, &y3, &x4, &y4))
Elliot Lee's avatar
Elliot Lee committed
547
	{
548 549
	  pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles,
			     x3, y3, (x4 - x3), (y4 - y3), FALSE);
Elliot Lee's avatar
Elliot Lee committed
550 551 552 553
	  mask->segs_out = find_mask_boundary (&bPR, &mask->num_segs_out,
					       IgnoreBounds,
					       x1, y1,
					       x2, y2);
554 555 556 557
	  x1 = MAX (x1, x3);
	  y1 = MAX (y1, y3);
	  x2 = MIN (x2, x4);
	  y2 = MIN (y2, y4);
Elliot Lee's avatar
Elliot Lee committed
558 559 560

	  if (x2 > x1 && y2 > y1)
	    {
561 562 563 564
	      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
565 566 567 568 569 570 571
	      mask->segs_in =  find_mask_boundary (&bPR, &mask->num_segs_in,
						   WithinBounds,
						   x1, y1,
						   x2, y2);
	    }
	  else
	    {
572
	      mask->segs_in     = NULL;
Elliot Lee's avatar
Elliot Lee committed
573 574 575 576 577
	      mask->num_segs_in = 0;
	    }
	}
      else
	{
578 579 580
	  mask->segs_in      = NULL;
	  mask->segs_out     = NULL;
	  mask->num_segs_in  = 0;
Elliot Lee's avatar
Elliot Lee committed
581 582
	  mask->num_segs_out = 0;
	}
583

Elliot Lee's avatar
Elliot Lee committed
584 585 586
      mask->boundary_known = TRUE;
    }

587 588 589
  *segs_in      = mask->segs_in;
  *segs_out     = mask->segs_out;
  *num_segs_in  = mask->num_segs_in;
Elliot Lee's avatar
Elliot Lee committed
590 591 592 593 594
  *num_segs_out = mask->num_segs_out;

  return TRUE;
}

595
gint
Michael Natterer's avatar
Michael Natterer committed
596 597 598
gimp_channel_value (GimpChannel *mask,
		    gint         x,
		    gint         y)
Elliot Lee's avatar
Elliot Lee committed
599 600
{
  Tile *tile;
601
  gint  val;
Elliot Lee's avatar
Elliot Lee committed
602

603 604
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), 0);

Elliot Lee's avatar
Elliot Lee committed
605 606 607 608 609 610 611 612 613 614
  /*  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
    {
615 616
      if (x < 0 || x >= GIMP_DRAWABLE (mask)->width ||
	  y < 0 || y >= GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
617 618 619
	return 0;
    }

620 621
  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));
622
  tile_release (tile, FALSE);
Elliot Lee's avatar
Elliot Lee committed
623 624 625 626

  return val;
}

627
gboolean
Michael Natterer's avatar
Michael Natterer committed
628 629 630 631 632
gimp_channel_bounds (GimpChannel *mask,
		     gint        *x1,
		     gint        *y1,
		     gint        *x2,
		     gint        *y2)
Elliot Lee's avatar
Elliot Lee committed
633
{
634 635 636 637 638 639 640
  PixelRegion  maskPR;
  guchar      *data, *data1;
  gint         x, y;
  gint         ex, ey;
  gint         tx1, tx2, ty1, ty2;
  gint         minx, maxx;
  gpointer     pr;
641

642
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);
643 644 645 646
  g_return_val_if_fail (x1 != NULL, FALSE);
  g_return_val_if_fail (y1 != NULL, FALSE);
  g_return_val_if_fail (x2 != NULL, FALSE);
  g_return_val_if_fail (y2 != NULL, FALSE);
647

Elliot Lee's avatar
Elliot Lee committed
648 649 650 651 652 653 654 655
  /*  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;

656
      return ! mask->empty;
Elliot Lee's avatar
Elliot Lee committed
657 658 659
    }

  /*  go through and calculate the bounds  */
660 661
  tx1 = GIMP_DRAWABLE (mask)->width;
  ty1 = GIMP_DRAWABLE (mask)->height;
662 663
  tx2 = 0;
  ty2 = 0;
Elliot Lee's avatar
Elliot Lee committed
664

665 666 667 668
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (mask)->width,
		     GIMP_DRAWABLE (mask)->height, FALSE);
669

670 671 672
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
673
    {
674
      data1 = data = maskPR.data;
Elliot Lee's avatar
Elliot Lee committed
675 676
      ex = maskPR.x + maskPR.w;
      ey = maskPR.y + maskPR.h;
677 678
      /* only check the pixels if this tile is not fully within the currently
	 computed bounds */
679 680
      if (maskPR.x < tx1 || ex > tx2 ||
	  maskPR.y < ty1 || ey > ty2)
681
        {
682 683 684
	  /* 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])
685 686 687 688 689 690 691 692 693 694
            {
              if (maskPR.x < tx1)
                tx1 = maskPR.x;
              if (ex > tx2)
                tx2 = ex;
              if (maskPR.y < ty1)
                ty1 = maskPR.y;
              if (ey > ty2)
                ty2 = ey;
            }
695
	  else
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
            {
              for (y = maskPR.y; y < ey; y++, data1 += maskPR.rowstride)
                {
                  for (x = maskPR.x, data = data1; x < ex; x++, data++)
                    {
                      if (*data)
                        {
                          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
723 724
    }

725 726
  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
727

728
  if (tx1 == GIMP_DRAWABLE (mask)->width && ty1 == GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
729 730
    {
      mask->empty = TRUE;
731 732 733 734
      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
735 736 737 738
    }
  else
    {
      mask->empty = FALSE;
739 740 741 742
      mask->x1    = tx1;
      mask->y1    = ty1;
      mask->x2    = tx2;
      mask->y2    = ty2;
Elliot Lee's avatar
Elliot Lee committed
743
    }
744

Elliot Lee's avatar
Elliot Lee committed
745 746
  mask->bounds_known = TRUE;

747 748 749 750 751
  *x1 = tx1;
  *x2 = tx2;
  *y1 = ty1;
  *y2 = ty2;

752
  return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
753 754
}

755
gboolean
Michael Natterer's avatar
Michael Natterer committed
756
gimp_channel_is_empty (GimpChannel *mask)
Elliot Lee's avatar
Elliot Lee committed
757
{
758 759 760 761
  PixelRegion  maskPR;
  guchar      *data;
  gint         x, y;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
762

763 764
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);

Elliot Lee's avatar
Elliot Lee committed
765 766 767
  if (mask->bounds_known)
    return mask->empty;

768 769 770 771
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (mask)->width,
		     GIMP_DRAWABLE (mask)->height, FALSE);
772

773 774 775
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
776 777 778
    {
      /*  check if any pixel in the mask is non-zero  */
      data = maskPR.data;
779

Elliot Lee's avatar
Elliot Lee committed
780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
      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);

795 796 797 798 799 800
  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
801
  mask->boundary_known = TRUE;
802 803 804 805
  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
806 807 808 809 810

  return TRUE;
}

void
Michael Natterer's avatar
Michael Natterer committed
811 812 813
gimp_channel_add_segment (GimpChannel *mask,
			  gint         x,
			  gint         y,
814 815
			  gint         width,
			  gint         value)
Elliot Lee's avatar
Elliot Lee committed
816
{
817 818 819 820 821
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
822

823 824
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

Elliot Lee's avatar
Elliot Lee committed
825 826
  /*  check horizontal extents...  */
  x2 = x + width;
827 828
  x2 = CLAMP (x2, 0, GIMP_DRAWABLE (mask)->width);
  x  = CLAMP (x,  0, GIMP_DRAWABLE (mask)->width);
Elliot Lee's avatar
Elliot Lee committed
829
  width = x2 - x;
830 831
  if (!width)
    return;
Elliot Lee's avatar
Elliot Lee committed
832

833
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
834 835
    return;

836 837
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, width, 1, TRUE);
838

839 840 841
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
842 843 844 845 846 847 848 849 850 851 852 853 854 855
    {
      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
856 857 858
gimp_channel_sub_segment (GimpChannel *mask,
			  gint         x,
			  gint         y,
859 860
			  gint         width,
			  gint         value)
Elliot Lee's avatar
Elliot Lee committed
861
{
862 863 864 865 866
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
867

868 869
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

Elliot Lee's avatar
Elliot Lee committed
870 871
  /*  check horizontal extents...  */
  x2 = x + width;
872 873
  x2 = CLAMP (x2, 0, GIMP_DRAWABLE (mask)->width);
  x =  CLAMP (x,  0, GIMP_DRAWABLE (mask)->width);
Elliot Lee's avatar
Elliot Lee committed
874
  width = x2 - x;
875 876 877

  if (! width)
    return;
Elliot Lee's avatar
Elliot Lee committed
878

879
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
880 881
    return;

882 883 884
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
                     x, y, width, 1, TRUE);

885 886 887
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
888 889 890 891 892 893 894 895 896 897 898 899 900 901
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data - value;
	  if (val < 0)
	    val = 0;
	  *data++ = val;
	}
    }
}

void
902 903 904 905 906 907
gimp_channel_combine_rect (GimpChannel    *mask,
			   GimpChannelOps  op,
			   gint            x,
			   gint            y,
			   gint            w,
			   gint            h)
Elliot Lee's avatar
Elliot Lee committed
908
{
909
  gint        x2, y2;
Elliot Lee's avatar
Elliot Lee committed
910
  PixelRegion maskPR;
911
  guchar      color;
912

913 914
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

915 916
  y2 = y + h;
  x2 = x + w;
Elliot Lee's avatar
Elliot Lee committed
917

918 919 920 921
  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
922

923
  if (x2 - x <= 0 || y2 - y <= 0)
Elliot Lee's avatar
Elliot Lee committed
924 925
    return;

926 927
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, x2 - x, y2 - y, TRUE);
928 929

  if (op == GIMP_CHANNEL_OP_ADD || op == GIMP_CHANNEL_OP_REPLACE)
930
    color = OPAQUE_OPACITY;
931
  else
932
    color = TRANSPARENT_OPACITY;
933

934
  color_region (&maskPR, &color);
Elliot Lee's avatar
Elliot Lee committed
935 936

  /*  Determine new boundary  */
937
  if (mask->bounds_known && (op == GIMP_CHANNEL_OP_ADD) && !mask->empty)
Elliot Lee's avatar
Elliot Lee committed
938 939 940 941 942 943 944 945 946 947
    {
      if (x < mask->x1)
	mask->x1 = x;
      if (y < mask->y1)
	mask->y1 = y;
      if ((x + w) > mask->x2)
	mask->x2 = (x