gimpchannel.c 42.9 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 212 213
			   gimage,
                           0, 0, width, height,
                           GIMP_GRAY_IMAGE, name);
Elliot Lee's avatar
Elliot Lee committed
214 215

  /*  set the channel color and opacity  */
216
  channel->color       = *color;
217

218
  channel->show_masked = TRUE;
Elliot Lee's avatar
Elliot Lee committed
219 220

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

  return channel;
}

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

  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  */
247
  new_channel->color        = channel->color;
248

249
  new_channel->show_masked  = channel->show_masked;
Elliot Lee's avatar
Elliot Lee committed
250

251
  /*  selection mask variables  */
252 253 254 255 256 257
  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
258 259 260 261

  return new_channel;
}

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

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

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

287
  *color = channel->color;
288
}
289

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

295
  return channel->color.a;
296 297
}

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

304
  opacity = CLAMP (opacity, GIMP_OPACITY_TRANSPARENT, GIMP_OPACITY_OPAQUE);
305

306
  channel->color.a = opacity;
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 332 333
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
334
void
335 336 337 338
gimp_channel_scale (GimpChannel           *channel,
		    gint                   new_width,
		    gint                   new_height,
                    GimpInterpolationType  interpolation_type)
Elliot Lee's avatar
Elliot Lee committed
339
{
340
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
341 342
  TileManager *new_tiles;

343 344
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

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

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

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

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

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

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

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

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

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

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

  gimp_viewable_size_changed (GIMP_VIEWABLE (channel));
Elliot Lee's avatar
Elliot Lee committed
390 391 392
}

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

406 407
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

408
  if (new_width == 0 || new_height == 0)
Elliot Lee's avatar
Elliot Lee committed
409 410
    return;

411 412 413 414
  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);
415

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

  /*  Configure the pixel regions  */
448 449
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
		     x1, y1, w, h, FALSE);
Elliot Lee's avatar
Elliot Lee committed
450 451

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

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

  /*  Configure the new channel  */
482 483 484
  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
485 486 487 488 489

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

  /*  update the new channel area  */
Michael Natterer's avatar
Michael Natterer committed
490 491 492 493
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Michael Natterer's avatar
Michael Natterer committed
494 495

  gimp_viewable_size_changed (GIMP_VIEWABLE (channel));
Elliot Lee's avatar
Elliot Lee committed
496 497
}

498

499 500 501
/******************************/
/*  selection mask functions  */
/******************************/
Elliot Lee's avatar
Elliot Lee committed
502

Michael Natterer's avatar
Michael Natterer committed
503 504
GimpChannel *
gimp_channel_new_mask (GimpImage *gimage,
505 506
		       gint       width,
		       gint       height)
Elliot Lee's avatar
Elliot Lee committed
507
{
Michael Natterer's avatar
Michael Natterer committed
508 509
  GimpRGB      black = { 0.0, 0.0, 0.0, 0.5 };
  GimpChannel *new_channel;
Elliot Lee's avatar
Elliot Lee committed
510

511 512
  g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);

Elliot Lee's avatar
Elliot Lee committed
513
  /*  Create the new channel  */
Michael Natterer's avatar
Michael Natterer committed
514 515
  new_channel = gimp_channel_new (gimage, width, height,
				  _("Selection Mask"), &black);
Elliot Lee's avatar
Elliot Lee committed
516 517

  /*  Set the validate procedure  */
518
  tile_manager_set_validate_proc (GIMP_DRAWABLE (new_channel)->tiles,
Michael Natterer's avatar
Michael Natterer committed
519
				  gimp_channel_validate);
Elliot Lee's avatar
Elliot Lee committed
520 521 522 523

  return new_channel;
}

524
gboolean
Michael Natterer's avatar
Michael Natterer committed
525 526 527 528 529 530 531 532 533
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
534
{
535
  gint        x3, y3, x4, y4;
Elliot Lee's avatar
Elliot Lee committed
536 537
  PixelRegion bPR;

538
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);
539 540 541 542
  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);
543

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

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

Elliot Lee's avatar
Elliot Lee committed
590 591 592
      mask->boundary_known = TRUE;
    }

593 594 595
  *segs_in      = mask->segs_in;
  *segs_out     = mask->segs_out;
  *num_segs_in  = mask->num_segs_in;
Elliot Lee's avatar
Elliot Lee committed
596 597 598 599 600
  *num_segs_out = mask->num_segs_out;

  return TRUE;
}

601
gint
Michael Natterer's avatar
Michael Natterer committed
602 603 604
gimp_channel_value (GimpChannel *mask,
		    gint         x,
		    gint         y)
Elliot Lee's avatar
Elliot Lee committed
605 606
{
  Tile *tile;
607
  gint  val;
Elliot Lee's avatar
Elliot Lee committed
608

609 610
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), 0);

Elliot Lee's avatar
Elliot Lee committed
611 612 613 614 615 616 617 618 619 620
  /*  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
    {
621 622
      if (x < 0 || x >= GIMP_DRAWABLE (mask)->width ||
	  y < 0 || y >= GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
623 624 625
	return 0;
    }

626 627
  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));
628
  tile_release (tile, FALSE);
Elliot Lee's avatar
Elliot Lee committed
629 630 631 632

  return val;
}

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

648
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);
649 650 651 652
  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);
653

Elliot Lee's avatar
Elliot Lee committed
654 655 656 657 658 659 660 661
  /*  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;

662
      return ! mask->empty;
Elliot Lee's avatar
Elliot Lee committed
663 664 665
    }

  /*  go through and calculate the bounds  */
666 667
  tx1 = GIMP_DRAWABLE (mask)->width;
  ty1 = GIMP_DRAWABLE (mask)->height;
668 669
  tx2 = 0;
  ty2 = 0;
Elliot Lee's avatar
Elliot Lee committed
670

671 672 673 674
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (mask)->width,
		     GIMP_DRAWABLE (mask)->height, FALSE);
675

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

731 732
  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
733

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

Elliot Lee's avatar
Elliot Lee committed
751 752
  mask->bounds_known = TRUE;

753 754 755 756 757
  *x1 = tx1;
  *x2 = tx2;
  *y1 = ty1;
  *y2 = ty2;

758
  return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
759 760
}

761
gboolean
Michael Natterer's avatar
Michael Natterer committed
762
gimp_channel_is_empty (GimpChannel *mask)
Elliot Lee's avatar
Elliot Lee committed
763
{
764 765 766 767
  PixelRegion  maskPR;
  guchar      *data;
  gint         x, y;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
768

769 770
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);

Elliot Lee's avatar
Elliot Lee committed
771 772 773
  if (mask->bounds_known)
    return mask->empty;

774 775 776 777
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (mask)->width,
		     GIMP_DRAWABLE (mask)->height, FALSE);
778

779 780 781
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
782 783 784
    {
      /*  check if any pixel in the mask is non-zero  */
      data = maskPR.data;
785

Elliot Lee's avatar
Elliot Lee committed
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
      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);

801 802 803 804 805 806
  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
807
  mask->boundary_known = TRUE;
808 809 810 811
  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
812 813 814 815 816

  return TRUE;
}

void
Michael Natterer's avatar
Michael Natterer committed
817 818 819
gimp_channel_add_segment (GimpChannel *mask,
			  gint         x,
			  gint         y,
820 821
			  gint         width,
			  gint         value)
Elliot Lee's avatar
Elliot Lee committed
822
{
823 824 825 826 827
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
828

829 830
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

Elliot Lee's avatar
Elliot Lee committed
831 832
  /*  check horizontal extents...  */
  x2 = x + width;
833 834
  x2 = CLAMP (x2, 0, GIMP_DRAWABLE (mask)->width);
  x  = CLAMP (x,  0, GIMP_DRAWABLE (mask)->width);
Elliot Lee's avatar
Elliot Lee committed
835
  width = x2 - x;
836 837
  if (!width)
    return;
Elliot Lee's avatar
Elliot Lee committed
838

839
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
840 841
    return;

842 843
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, width, 1, TRUE);
844

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

874 875
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

Elliot Lee's avatar
Elliot Lee committed
876 877
  /*  check horizontal extents...  */
  x2 = x + width;
878 879
  x2 = CLAMP (x2, 0, GIMP_DRAWABLE (mask)->width);
  x =  CLAMP (x,  0, GIMP_DRAWABLE (mask)->width);
Elliot Lee's avatar
Elliot Lee committed
880
  width = x2 - x;
881 882 883

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

885
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
886 887
    return;

888 889 890
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
                     x, y, width, 1, TRUE);

891 892 893
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
894 895 896 897 898 899 900 901 902 903 904 905 906 907
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data - value;
	  if (val < 0)
	    val = 0;
	  *data++ = val;
	}
    }
}

void
908 909 910 911 912 913
gimp_channel_combine_rect (GimpChannel    *mask,
			   GimpChannelOps  op,
			   gint            x,
			   gint            y,
			   gint            w,
			   gint            h)
Elliot Lee's avatar
Elliot Lee committed
914
{
915
  gint        x2, y2;
Elliot Lee's avatar
Elliot Lee committed
916
  PixelRegion maskPR;
917
  guchar      color;
918

919 920
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

921 922
  y2 = y + h;
  x2 = x + w;
Elliot Lee's avatar
Elliot Lee committed
923

924 925 926 927
  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
928

929
  if (x2 - x <= 0 || y2 - y <= 0)
Elliot Lee's avatar
Elliot Lee committed
930 931
    return;

932 933
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, x2 - x, y2 - y, TRUE);
934 935

  if (op == GIMP_CHANNEL_OP_ADD || op == GIMP_CHANNEL_OP_REPLACE)
936
    color = OPAQUE_OPACITY;
937
  else
938
    color = TRANSPARENT_OPACITY;
939

940
  color_region (&maskPR, &color);
Elliot Lee's avatar
Elliot Lee committed
941 942

  /*  Determine new boundary  */
943
  if (mask->bounds_known && (op == GIMP_CHANNEL_OP_ADD) && !mask->empty)
Elliot Lee's avatar