gimpchannel-combine.c 43.7 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
#include "gimpimage-undo-push.h"
43 44
#include "gimpchannel.h"
#include "gimplayer.h"
45
#include "gimpparasitelist.h"
Michael Natterer's avatar
Michael Natterer committed
46

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


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

53
static void       gimp_channel_finalize    (GObject          *object);
54

55
static gsize      gimp_channel_get_memsize (GimpObject       *object);
56

57 58 59 60
static GimpItem * gimp_channel_duplicate   (GimpItem         *item,
                                            GType             new_type,
                                            gboolean          add_alpha);

61 62
static void       gimp_channel_push_undo   (GimpChannel      *mask,
                                            const gchar      *undo_desc);
63 64
static void       gimp_channel_validate    (TileManager      *tm,
                                            Tile             *tile);
65

66

67
static GimpDrawableClass * parent_class = NULL;
68

69

70
GType
71
gimp_channel_get_type (void)
72
{
73
  static GType channel_type = 0;
74

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

90 91 92
      channel_type = g_type_register_static (GIMP_TYPE_DRAWABLE,
					     "GimpChannel",
					     &channel_info, 0);
93 94 95 96 97 98
    }

  return channel_type;
}

static void
99
gimp_channel_class_init (GimpChannelClass *klass)
100
{
101 102
  GObjectClass    *object_class;
  GimpObjectClass *gimp_object_class;
103
  GimpItemClass   *item_class;
104

105 106
  object_class      = G_OBJECT_CLASS (klass);
  gimp_object_class = GIMP_OBJECT_CLASS (klass);
107
  item_class        = GIMP_ITEM_CLASS (klass);
108

109
  parent_class = g_type_class_peek_parent (klass);
110

111 112 113
  object_class->finalize         = gimp_channel_finalize;

  gimp_object_class->get_memsize = gimp_channel_get_memsize;
114 115

  item_class->duplicate          = gimp_channel_duplicate;
116 117 118 119 120
}

static void
gimp_channel_init (GimpChannel *channel)
{
121
  gimp_rgba_set (&channel->color, 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE);
122 123 124 125

  channel->show_masked = FALSE;

  /*  Selection mask variables  */
126
  channel->boundary_known = FALSE;
127 128 129 130
  channel->segs_in        = NULL;
  channel->segs_out       = NULL;
  channel->num_segs_in    = 0;
  channel->num_segs_out   = 0;
131 132
  channel->empty          = FALSE;
  channel->bounds_known   = FALSE;
133 134 135 136
  channel->x1             = 0;
  channel->y1             = 0;
  channel->x2             = 0;
  channel->y2             = 0;
137
}
Elliot Lee's avatar
Elliot Lee committed
138

139
static void
140
gimp_channel_finalize (GObject *object)
141 142 143 144 145 146
{
  GimpChannel *channel;

  channel = GIMP_CHANNEL (object);

  if (channel->segs_in)
147 148 149 150 151
    {
      g_free (channel->segs_in);
      channel->segs_in = NULL;
    }

152
  if (channel->segs_out)
153 154 155 156
    {
      g_free (channel->segs_out);
      channel->segs_out = NULL;
    }
157

158
  G_OBJECT_CLASS (parent_class)->finalize (object);
159
}
Elliot Lee's avatar
Elliot Lee committed
160

161 162 163 164 165 166 167 168 169 170 171 172 173 174
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);
}

175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
static GimpItem *
gimp_channel_duplicate (GimpItem *item,
                        GType     new_type,
                        gboolean  add_alpha)
{
  GimpChannel *channel;
  GimpItem    *new_item;
  GimpChannel *new_channel;

  g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_DRAWABLE), NULL);

  if (g_type_is_a (new_type, GIMP_TYPE_CHANNEL))
    add_alpha = FALSE;

  new_item = GIMP_ITEM_CLASS (parent_class)->duplicate (item, new_type,
                                                        add_alpha);

  if (! GIMP_IS_CHANNEL (new_item))
    return new_item;

  channel     = GIMP_CHANNEL (item);
  new_channel = GIMP_CHANNEL (new_item);

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

  new_channel->show_masked  = channel->show_masked;

  /*  selection mask variables  */
  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;

  return new_item;
}

214
static void
215 216
gimp_channel_push_undo (GimpChannel *mask,
                        const gchar *undo_desc)
217 218 219 220 221
{
  GimpImage *gimage;

  gimage = gimp_item_get_image (GIMP_ITEM (mask));

222 223
  g_return_if_fail (gimage != NULL);

224
  gimp_image_undo_push_mask (gimage, undo_desc, mask);
225 226 227 228 229 230 231

  mask->boundary_known = FALSE;

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

Elliot Lee's avatar
Elliot Lee committed
232
static void
Michael Natterer's avatar
Michael Natterer committed
233 234
gimp_channel_validate (TileManager *tm,
		       Tile        *tile)
Elliot Lee's avatar
Elliot Lee committed
235 236
{
  /*  Set the contents of the tile to empty  */
scott's avatar
scott committed
237
  memset (tile_data_pointer (tile, 0, 0), 
238
	  TRANSPARENT_OPACITY, tile_size (tile));
Elliot Lee's avatar
Elliot Lee committed
239 240
}

241 242 243

/*  public functions  */

Michael Natterer's avatar
Michael Natterer committed
244 245
GimpChannel *
gimp_channel_new (GimpImage     *gimage,
246 247
		  gint           width,
		  gint           height,
Michael Natterer's avatar
Michael Natterer committed
248 249
		  const gchar   *name,
		  const GimpRGB *color)
Elliot Lee's avatar
Elliot Lee committed
250
{
Michael Natterer's avatar
Michael Natterer committed
251
  GimpChannel *channel;
252

253
  g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);
254
  g_return_val_if_fail (color != NULL, NULL);
Elliot Lee's avatar
Elliot Lee committed
255

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

Michael Natterer's avatar
Michael Natterer committed
258
  gimp_drawable_configure (GIMP_DRAWABLE (channel),
259 260 261
			   gimage,
                           0, 0, width, height,
                           GIMP_GRAY_IMAGE, name);
Elliot Lee's avatar
Elliot Lee committed
262 263

  /*  set the channel color and opacity  */
264
  channel->color       = *color;
265

266
  channel->show_masked = TRUE;
Elliot Lee's avatar
Elliot Lee committed
267 268

  /*  selection mask variables  */
269 270
  channel->x2          = width;
  channel->y2          = height;
Elliot Lee's avatar
Elliot Lee committed
271 272 273 274

  return channel;
}

275
void 
Michael Natterer's avatar
Michael Natterer committed
276 277
gimp_channel_set_color (GimpChannel   *channel,
			const GimpRGB *color)
278
{
279 280
  g_return_if_fail (GIMP_IS_CHANNEL (channel));
  g_return_if_fail (color != NULL);
281

Michael Natterer's avatar
Michael Natterer committed
282 283 284 285 286 287 288 289 290
  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);
    }
291
}
292

293 294 295
void
gimp_channel_get_color (const GimpChannel *channel,
                        GimpRGB           *color)
296
{
297 298
  g_return_if_fail (GIMP_IS_CHANNEL (channel));
  g_return_if_fail (color != NULL);
299

300
  *color = channel->color;
301
}
302

303
gdouble
Michael Natterer's avatar
Michael Natterer committed
304
gimp_channel_get_opacity (const GimpChannel *channel)
305
{
306
  g_return_val_if_fail (GIMP_IS_CHANNEL (channel), GIMP_OPACITY_TRANSPARENT);
307

308
  return channel->color.a;
309 310
}

311
void
Michael Natterer's avatar
Michael Natterer committed
312
gimp_channel_set_opacity (GimpChannel *channel,
313
			  gdouble      opacity)
314
{
315 316
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

317
  opacity = CLAMP (opacity, GIMP_OPACITY_TRANSPARENT, GIMP_OPACITY_OPAQUE);
318

319
  channel->color.a = opacity;
320 321
}

322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
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
347
void
348 349 350 351
gimp_channel_scale (GimpChannel           *channel,
		    gint                   new_width,
		    gint                   new_height,
                    GimpInterpolationType  interpolation_type)
Elliot Lee's avatar
Elliot Lee committed
352
{
353
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
354 355
  TileManager *new_tiles;

356 357
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

Elliot Lee's avatar
Elliot Lee committed
358 359 360 361
  if (new_width == 0 || new_height == 0)
    return;

  /*  Update the old channel position  */
Michael Natterer's avatar
Michael Natterer committed
362 363 364 365
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
366

367 368 369
  /*  Allocate the new channel  */
  new_tiles = tile_manager_new (new_width, new_height, 1);

Elliot Lee's avatar
Elliot Lee committed
370
  /*  Configure the pixel regions  */
371 372 373
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (channel)->width,
374 375
		     GIMP_DRAWABLE (channel)->height,
                     FALSE);
Elliot Lee's avatar
Elliot Lee committed
376

377 378 379 380
  pixel_region_init (&destPR, new_tiles,
                     0, 0,
                     new_width, new_height,
                     TRUE);
Elliot Lee's avatar
Elliot Lee committed
381

382
  /*  Sclae the channel  */
383
  scale_region (&srcPR, &destPR, interpolation_type);
Elliot Lee's avatar
Elliot Lee committed
384 385

  /*  Push the channel on the undo stack  */
386 387 388
  gimp_image_undo_push_channel_mod (gimp_item_get_image (GIMP_ITEM (channel)),
                                    _("Scale Channel"),
                                    channel);
Elliot Lee's avatar
Elliot Lee committed
389 390

  /*  Configure the new channel  */
391 392
  GIMP_DRAWABLE (channel)->tiles  = new_tiles;
  GIMP_DRAWABLE (channel)->width  = new_width;
393
  GIMP_DRAWABLE (channel)->height = new_height;
Elliot Lee's avatar
Elliot Lee committed
394 395 396 397 398

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

  /*  Update the new channel position  */
Michael Natterer's avatar
Michael Natterer committed
399 400 401 402
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Michael Natterer's avatar
Michael Natterer committed
403 404

  gimp_viewable_size_changed (GIMP_VIEWABLE (channel));
Elliot Lee's avatar
Elliot Lee committed
405 406 407
}

void
Michael Natterer's avatar
Michael Natterer committed
408
gimp_channel_resize (GimpChannel *channel,
409 410
		     gint         new_width,
		     gint         new_height,
Michael Natterer's avatar
Michael Natterer committed
411 412
		     gint         offx,
		     gint         offy)
Elliot Lee's avatar
Elliot Lee committed
413
{
414
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
415
  TileManager *new_tiles;
416 417 418 419
  guchar       bg = 0;
  gint         clear;
  gint         w, h;
  gint         x1, y1, x2, y2;
Elliot Lee's avatar
Elliot Lee committed
420

421 422
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

423
  if (new_width == 0 || new_height == 0)
Elliot Lee's avatar
Elliot Lee committed
424 425
    return;

426 427 428 429
  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);
430

Elliot Lee's avatar
Elliot Lee committed
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
  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
457 458 459 460
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
461 462

  /*  Configure the pixel regions  */
463 464
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
		     x1, y1, w, h, FALSE);
Elliot Lee's avatar
Elliot Lee committed
465 466

  /*  Determine whether the new channel needs to be initially cleared  */
467 468
  if ((new_width  > GIMP_DRAWABLE (channel)->width) ||
      (new_height > GIMP_DRAWABLE (channel)->height) ||
Elliot Lee's avatar
Elliot Lee committed
469 470 471 472 473 474 475 476 477 478 479
      (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)
    {
480 481 482 483 484
      pixel_region_init (&destPR, new_tiles,
                         0, 0,
                         new_width, new_height,
                         TRUE);

Elliot Lee's avatar
Elliot Lee committed
485 486 487 488 489 490 491 492 493
      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  */
494 495 496
  gimp_image_undo_push_channel_mod (gimp_item_get_image (GIMP_ITEM (channel)),
                                    _("Resize Channel"),
                                    channel);
Elliot Lee's avatar
Elliot Lee committed
497 498

  /*  Configure the new channel  */
499 500 501
  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
502 503 504 505 506

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

  /*  update the new channel area  */
Michael Natterer's avatar
Michael Natterer committed
507 508 509 510
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Michael Natterer's avatar
Michael Natterer committed
511 512

  gimp_viewable_size_changed (GIMP_VIEWABLE (channel));
Elliot Lee's avatar
Elliot Lee committed
513 514
}

515

516 517 518
/******************************/
/*  selection mask functions  */
/******************************/
Elliot Lee's avatar
Elliot Lee committed
519

Michael Natterer's avatar
Michael Natterer committed
520 521
GimpChannel *
gimp_channel_new_mask (GimpImage *gimage,
522 523
		       gint       width,
		       gint       height)
Elliot Lee's avatar
Elliot Lee committed
524
{
Michael Natterer's avatar
Michael Natterer committed
525 526
  GimpRGB      black = { 0.0, 0.0, 0.0, 0.5 };
  GimpChannel *new_channel;
Elliot Lee's avatar
Elliot Lee committed
527

528 529
  g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);

Elliot Lee's avatar
Elliot Lee committed
530
  /*  Create the new channel  */
Michael Natterer's avatar
Michael Natterer committed
531 532
  new_channel = gimp_channel_new (gimage, width, height,
				  _("Selection Mask"), &black);
Elliot Lee's avatar
Elliot Lee committed
533 534

  /*  Set the validate procedure  */
535
  tile_manager_set_validate_proc (GIMP_DRAWABLE (new_channel)->tiles,
Michael Natterer's avatar
Michael Natterer committed
536
				  gimp_channel_validate);
Elliot Lee's avatar
Elliot Lee committed
537 538 539 540

  return new_channel;
}

541
gboolean
Michael Natterer's avatar
Michael Natterer committed
542 543 544 545 546 547 548 549 550
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
551
{
552
  gint        x3, y3, x4, y4;
Elliot Lee's avatar
Elliot Lee committed
553 554
  PixelRegion bPR;

555
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);
556 557 558 559
  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);
560

Elliot Lee's avatar
Elliot Lee committed
561 562 563 564 565 566 567 568
  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
569
      if (gimp_channel_bounds (mask, &x3, &y3, &x4, &y4))
Elliot Lee's avatar
Elliot Lee committed
570
	{
571 572
	  pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles,
			     x3, y3, (x4 - x3), (y4 - y3), FALSE);
Elliot Lee's avatar
Elliot Lee committed
573 574 575 576
	  mask->segs_out = find_mask_boundary (&bPR, &mask->num_segs_out,
					       IgnoreBounds,
					       x1, y1,
					       x2, y2);
577 578 579 580
	  x1 = MAX (x1, x3);
	  y1 = MAX (y1, y3);
	  x2 = MIN (x2, x4);
	  y2 = MIN (y2, y4);
Elliot Lee's avatar
Elliot Lee committed
581 582 583

	  if (x2 > x1 && y2 > y1)
	    {
584 585 586 587
	      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
588 589 590 591 592 593 594
	      mask->segs_in =  find_mask_boundary (&bPR, &mask->num_segs_in,
						   WithinBounds,
						   x1, y1,
						   x2, y2);
	    }
	  else
	    {
595
	      mask->segs_in     = NULL;
Elliot Lee's avatar
Elliot Lee committed
596 597 598 599 600
	      mask->num_segs_in = 0;
	    }
	}
      else
	{
601 602 603
	  mask->segs_in      = NULL;
	  mask->segs_out     = NULL;
	  mask->num_segs_in  = 0;
Elliot Lee's avatar
Elliot Lee committed
604 605
	  mask->num_segs_out = 0;
	}
606

Elliot Lee's avatar
Elliot Lee committed
607 608 609
      mask->boundary_known = TRUE;
    }

610 611 612
  *segs_in      = mask->segs_in;
  *segs_out     = mask->segs_out;
  *num_segs_in  = mask->num_segs_in;
Elliot Lee's avatar
Elliot Lee committed
613 614 615 616 617
  *num_segs_out = mask->num_segs_out;

  return TRUE;
}

618
gint
Michael Natterer's avatar
Michael Natterer committed
619 620 621
gimp_channel_value (GimpChannel *mask,
		    gint         x,
		    gint         y)
Elliot Lee's avatar
Elliot Lee committed
622 623
{
  Tile *tile;
624
  gint  val;
Elliot Lee's avatar
Elliot Lee committed
625

626 627
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), 0);

Elliot Lee's avatar
Elliot Lee committed
628 629 630 631 632 633 634 635 636 637
  /*  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
    {
638 639
      if (x < 0 || x >= GIMP_DRAWABLE (mask)->width ||
	  y < 0 || y >= GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
640 641 642
	return 0;
    }

643 644
  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));
645
  tile_release (tile, FALSE);
Elliot Lee's avatar
Elliot Lee committed
646 647 648 649

  return val;
}

650
gboolean
Michael Natterer's avatar
Michael Natterer committed
651 652 653 654 655
gimp_channel_bounds (GimpChannel *mask,
		     gint        *x1,
		     gint        *y1,
		     gint        *x2,
		     gint        *y2)
Elliot Lee's avatar
Elliot Lee committed
656
{
657 658 659 660 661 662 663
  PixelRegion  maskPR;
  guchar      *data, *data1;
  gint         x, y;
  gint         ex, ey;
  gint         tx1, tx2, ty1, ty2;
  gint         minx, maxx;
  gpointer     pr;
664

665
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);
666 667 668 669
  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);
670

Elliot Lee's avatar
Elliot Lee committed
671 672 673 674 675 676 677 678
  /*  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;

679
      return ! mask->empty;
Elliot Lee's avatar
Elliot Lee committed
680 681 682
    }

  /*  go through and calculate the bounds  */
683 684
  tx1 = GIMP_DRAWABLE (mask)->width;
  ty1 = GIMP_DRAWABLE (mask)->height;
685 686
  tx2 = 0;
  ty2 = 0;
Elliot Lee's avatar
Elliot Lee committed
687

688 689 690 691
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (mask)->width,
		     GIMP_DRAWABLE (mask)->height, FALSE);
692

693 694 695
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
696
    {
697
      data1 = data = maskPR.data;
Elliot Lee's avatar
Elliot Lee committed
698 699
      ex = maskPR.x + maskPR.w;
      ey = maskPR.y + maskPR.h;
700 701
      /* only check the pixels if this tile is not fully within the currently
	 computed bounds */
702 703
      if (maskPR.x < tx1 || ex > tx2 ||
	  maskPR.y < ty1 || ey > ty2)
704
        {
705 706 707
	  /* 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])
708 709 710 711 712 713 714 715 716 717
            {
              if (maskPR.x < tx1)
                tx1 = maskPR.x;
              if (ex > tx2)
                tx2 = ex;
              if (maskPR.y < ty1)
                ty1 = maskPR.y;
              if (ey > ty2)
                ty2 = ey;
            }
718
	  else
719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
            {
              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
746 747
    }

748 749
  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
750

751
  if (tx1 == GIMP_DRAWABLE (mask)->width && ty1 == GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
752 753
    {
      mask->empty = TRUE;
754 755 756 757
      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
758 759 760 761
    }
  else
    {
      mask->empty = FALSE;
762 763 764 765
      mask->x1    = tx1;
      mask->y1    = ty1;
      mask->x2    = tx2;
      mask->y2    = ty2;
Elliot Lee's avatar
Elliot Lee committed
766
    }
767

Elliot Lee's avatar
Elliot Lee committed
768 769
  mask->bounds_known = TRUE;

770 771 772 773 774
  *x1 = tx1;
  *x2 = tx2;
  *y1 = ty1;
  *y2 = ty2;

775
  return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
776 777
}

778
gboolean
Michael Natterer's avatar
Michael Natterer committed
779
gimp_channel_is_empty (GimpChannel *mask)
Elliot Lee's avatar
Elliot Lee committed
780
{
781 782 783 784
  PixelRegion  maskPR;
  guchar      *data;
  gint         x, y;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
785

786 787
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);

Elliot Lee's avatar
Elliot Lee committed
788 789 790
  if (mask->bounds_known)
    return mask->empty;

791 792 793 794
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (mask)->width,
		     GIMP_DRAWABLE (mask)->height, FALSE);
795

796 797 798
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
799 800 801
    {
      /*  check if any pixel in the mask is non-zero  */
      data = maskPR.data;
802

Elliot Lee's avatar
Elliot Lee committed
803 804 805 806 807 808 809 810 811 812 813 814 815 816 817
      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);

818 819 820 821 822 823
  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
824
  mask->boundary_known = TRUE;
825 826 827 828
  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
829 830 831 832 833

  return TRUE;
}

void
Michael Natterer's avatar
Michael Natterer committed
834 835 836
gimp_channel_add_segment (GimpChannel *mask,
			  gint         x,
			  gint         y,
837 838
			  gint         width,
			  gint         value)
Elliot Lee's avatar
Elliot Lee committed
839
{
840 841 842 843 844
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
845

846 847
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

Elliot Lee's avatar
Elliot Lee committed
848 849
  /*  check horizontal extents...  */
  x2 = x + width;
850 851
  x2 = CLAMP (x2, 0, GIMP_DRAWABLE (mask)->width);
  x  = CLAMP (x,  0, GIMP_DRAWABLE (mask)->width);
Elliot Lee's avatar
Elliot Lee committed
852
  width = x2 - x;
853 854
  if (!width)
    return;
Elliot Lee's avatar
Elliot Lee committed
855

856
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
857 858
    return;

859 860
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, width, 1, TRUE);
861

862 863 864
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
865 866 867 868 869 870 871 872 873 874 875 876 877 878
    {
      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
879 880 881
gimp_channel_sub_segment (GimpChannel *mask,
			  gint         x,
			  gint         y,
882 883
			  gint         width,
			  gint         value)
Elliot Lee's avatar
Elliot Lee committed
884
{
885 886 887 888 889
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
890

891 892
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

Elliot Lee's avatar
Elliot Lee committed
893 894
  /*  check horizontal extents...  */
  x2 = x + width;
895 896
  x2 = CLAMP (x2, 0, GIMP_DRAWABLE (mask)->width);
  x =  CLAMP (x,  0, GIMP_DRAWABLE (mask)->width);
Elliot Lee's avatar
Elliot Lee committed
897
  width = x2 - x;
898 899 900

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

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

905 906 907
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
                     x, y, width, 1, TRUE);

908 909 910
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
911 912 913 914 915 916 917 918 919 920 921 922 923 924
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data - value;
	  if (val < 0)
	    val = 0;
	  *data++ = val;
	}
    }
}

void
925 926 927 928 929 930
gimp_channel_combine_rect (GimpChannel    *mask,
			   GimpChannelOps  op,
			   gint            x,
			   gint            y,
			   gint            w,
			   gint            h)
Elliot Lee's avatar
Elliot Lee committed
931
{
932
  gint        x2, y2;
Elliot Lee's avatar
Elliot Lee committed
933
  PixelRegion maskPR;
934
  guchar      color;
935

936 937
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

938 939
  y2 = y + h;
  x2 = x + w;
Elliot Lee's avatar
Elliot Lee committed
940

941 942 943 944
  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
945

946
  if (x2 - x <= 0 || y2 - y <= 0)
Elliot Lee's avatar
Elliot Lee committed
947 948
    return;

949 950
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, x2 - x, y2 - y, TRUE);
951 952

  if (op == GIMP_CHANNEL_OP_ADD || op == GIMP_CHANNEL_OP_REPLACE)
953
    color = OPAQUE_OPACITY;
954
  else
955
    color = TRANSPARENT_OPACITY;
956

957
  color_region (&maskPR, &color);