gimpchannel-combine.c 43.1 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
static void       gimp_channel_finalize    (GObject          *object);
55

56
static gsize      gimp_channel_get_memsize (GimpObject       *object);
57

58 59 60 61 62 63 64
static GimpItem * gimp_channel_duplicate   (GimpItem         *item,
                                            GType             new_type,
                                            gboolean          add_alpha);

static void       gimp_channel_push_undo   (GimpChannel      *mask);
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 215 216 217 218 219 220
static void
gimp_channel_push_undo (GimpChannel *mask)
{
  GimpImage *gimage;

  gimage = gimp_item_get_image (GIMP_ITEM (mask));

221 222
  g_return_if_fail (gimage != NULL);

223 224 225 226 227 228 229 230
  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
231
static void
Michael Natterer's avatar
Michael Natterer committed
232 233
gimp_channel_validate (TileManager *tm,
		       Tile        *tile)
Elliot Lee's avatar
Elliot Lee committed
234 235
{
  /*  Set the contents of the tile to empty  */
scott's avatar
scott committed
236
  memset (tile_data_pointer (tile, 0, 0), 
237
	  TRANSPARENT_OPACITY, tile_size (tile));
Elliot Lee's avatar
Elliot Lee committed
238 239
}

240 241 242

/*  public functions  */

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

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

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

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

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

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

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

  return channel;
}

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

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

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

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

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

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

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

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

318
  channel->color.a = opacity;
319 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
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
346
void
347 348 349 350
gimp_channel_scale (GimpChannel           *channel,
		    gint                   new_width,
		    gint                   new_height,
                    GimpInterpolationType  interpolation_type)
Elliot Lee's avatar
Elliot Lee committed
351
{
352
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
353 354
  TileManager *new_tiles;

355 356
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

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

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

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

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

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

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

  /*  Push the channel on the undo stack  */
385
  undo_push_channel_mod (gimp_item_get_image (GIMP_ITEM (channel)), channel);
Elliot Lee's avatar
Elliot Lee committed
386 387

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

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

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

  gimp_viewable_size_changed (GIMP_VIEWABLE (channel));
Elliot Lee's avatar
Elliot Lee committed
402 403 404
}

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

418 419
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

420
  if (new_width == 0 || new_height == 0)
Elliot Lee's avatar
Elliot Lee committed
421 422
    return;

423 424 425 426
  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);
427

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

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

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

Elliot Lee's avatar
Elliot Lee committed
482 483 484 485 486 487 488 489 490
      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  */
491
  undo_push_channel_mod (gimp_item_get_image (GIMP_ITEM (channel)), channel);
Elliot Lee's avatar
Elliot Lee committed
492 493

  /*  Configure the new channel  */
494 495 496
  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
497 498 499 500 501

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

  /*  update the new channel area  */
Michael Natterer's avatar
Michael Natterer committed
502 503 504 505
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Michael Natterer's avatar
Michael Natterer committed
506 507

  gimp_viewable_size_changed (GIMP_VIEWABLE (channel));
Elliot Lee's avatar
Elliot Lee committed
508 509
}

510

511 512 513
/******************************/
/*  selection mask functions  */
/******************************/
Elliot Lee's avatar
Elliot Lee committed
514

Michael Natterer's avatar
Michael Natterer committed
515 516
GimpChannel *
gimp_channel_new_mask (GimpImage *gimage,
517 518
		       gint       width,
		       gint       height)
Elliot Lee's avatar
Elliot Lee committed
519
{
Michael Natterer's avatar
Michael Natterer committed
520 521
  GimpRGB      black = { 0.0, 0.0, 0.0, 0.5 };
  GimpChannel *new_channel;
Elliot Lee's avatar
Elliot Lee committed
522

523 524
  g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);

Elliot Lee's avatar
Elliot Lee committed
525
  /*  Create the new channel  */
Michael Natterer's avatar
Michael Natterer committed
526 527
  new_channel = gimp_channel_new (gimage, width, height,
				  _("Selection Mask"), &black);
Elliot Lee's avatar
Elliot Lee committed
528 529

  /*  Set the validate procedure  */
530
  tile_manager_set_validate_proc (GIMP_DRAWABLE (new_channel)->tiles,
Michael Natterer's avatar
Michael Natterer committed
531
				  gimp_channel_validate);
Elliot Lee's avatar
Elliot Lee committed
532 533 534 535

  return new_channel;
}

536
gboolean
Michael Natterer's avatar
Michael Natterer committed
537 538 539 540 541 542 543 544 545
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
546
{
547
  gint        x3, y3, x4, y4;
Elliot Lee's avatar
Elliot Lee committed
548 549
  PixelRegion bPR;

550
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);
551 552 553 554
  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);
555

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

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

Elliot Lee's avatar
Elliot Lee committed
602 603 604
      mask->boundary_known = TRUE;
    }

605 606 607
  *segs_in      = mask->segs_in;
  *segs_out     = mask->segs_out;
  *num_segs_in  = mask->num_segs_in;
Elliot Lee's avatar
Elliot Lee committed
608 609 610 611 612
  *num_segs_out = mask->num_segs_out;

  return TRUE;
}

613
gint
Michael Natterer's avatar
Michael Natterer committed
614 615 616
gimp_channel_value (GimpChannel *mask,
		    gint         x,
		    gint         y)
Elliot Lee's avatar
Elliot Lee committed
617 618
{
  Tile *tile;
619
  gint  val;
Elliot Lee's avatar
Elliot Lee committed
620

621 622
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), 0);

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

638 639
  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));
640
  tile_release (tile, FALSE);
Elliot Lee's avatar
Elliot Lee committed
641 642 643 644

  return val;
}

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

660
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);
661 662 663 664
  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);
665

Elliot Lee's avatar
Elliot Lee committed
666 667 668 669 670 671 672 673
  /*  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;

674
      return ! mask->empty;
Elliot Lee's avatar
Elliot Lee committed
675 676 677
    }

  /*  go through and calculate the bounds  */
678 679
  tx1 = GIMP_DRAWABLE (mask)->width;
  ty1 = GIMP_DRAWABLE (mask)->height;
680 681
  tx2 = 0;
  ty2 = 0;
Elliot Lee's avatar
Elliot Lee committed
682

683 684 685 686
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (mask)->width,
		     GIMP_DRAWABLE (mask)->height, FALSE);
687

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

743 744
  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
745

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

Elliot Lee's avatar
Elliot Lee committed
763 764
  mask->bounds_known = TRUE;

765 766 767 768 769
  *x1 = tx1;
  *x2 = tx2;
  *y1 = ty1;
  *y2 = ty2;

770
  return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
771 772
}

773
gboolean
Michael Natterer's avatar
Michael Natterer committed
774
gimp_channel_is_empty (GimpChannel *mask)
Elliot Lee's avatar
Elliot Lee committed
775
{
776 777 778 779
  PixelRegion  maskPR;
  guchar      *data;
  gint         x, y;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
780

781 782
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);

Elliot Lee's avatar
Elliot Lee committed
783 784 785
  if (mask->bounds_known)
    return mask->empty;

786 787 788 789
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (mask)->width,
		     GIMP_DRAWABLE (mask)->height, FALSE);
790

791 792 793
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
794 795 796
    {
      /*  check if any pixel in the mask is non-zero  */
      data = maskPR.data;
797

Elliot Lee's avatar
Elliot Lee committed
798 799 800 801 802 803 804 805 806 807 808 809 810 811 812
      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);

813 814 815 816 817 818
  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
819
  mask->boundary_known = TRUE;
820 821 822 823
  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
824 825 826 827 828

  return TRUE;
}

void
Michael Natterer's avatar
Michael Natterer committed
829 830 831
gimp_channel_add_segment (GimpChannel *mask,
			  gint         x,
			  gint         y,
832 833
			  gint         width,
			  gint         value)
Elliot Lee's avatar
Elliot Lee committed
834
{
835 836 837 838 839
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
840

841 842
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

Elliot Lee's avatar
Elliot Lee committed
843 844
  /*  check horizontal extents...  */
  x2 = x + width;
845 846
  x2 = CLAMP (x2, 0, GIMP_DRAWABLE (mask)->width);
  x  = CLAMP (x,  0, GIMP_DRAWABLE (mask)->width);
Elliot Lee's avatar
Elliot Lee committed
847
  width = x2 - x;
848 849
  if (!width)
    return;
Elliot Lee's avatar
Elliot Lee committed
850

851
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
852 853
    return;

854 855
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, width, 1, TRUE);
856

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

886 887
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

Elliot Lee's avatar
Elliot Lee committed
888 889
  /*  check horizontal extents...  */
  x2 = x + width;
890 891
  x2 = CLAMP (x2, 0, GIMP_DRAWABLE (mask)->width);
  x =  CLAMP (x,  0, GIMP_DRAWABLE (mask)->width);
Elliot Lee's avatar
Elliot Lee committed
892
  width = x2 - x;
893 894 895

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

897
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
898 899
    return;

900 901 902
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
                     x, y, width, 1, TRUE);

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

void
920 921 922 923 924 925
gimp_channel_combine_rect (GimpChannel    *mask,
			   GimpChannelOps  op,
			   gint            x,
			   gint            y,
			   gint            w,
			   gint            h)
Elliot Lee's avatar
Elliot Lee committed
926
{
927
  gint        x2, y2;
Elliot Lee's avatar
Elliot Lee committed
928
  PixelRegion maskPR;
929
  guchar      color;
930

931 932
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

933 934
  y2 = y + h;
  x2 = x + w;
Elliot Lee's avatar
Elliot Lee committed
935

936 937 938 939
  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
940

941
  if (x2 - x <= 0 || y2 - y <= 0)
Elliot Lee's avatar
Elliot Lee committed
942 943
    return;

944 945
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, x2 - x, y2 - y, TRUE);
946 947

  if (op == GIMP_CHANNEL_OP_ADD || op == GIMP_CHANNEL_OP_REPLACE)
948
    color = OPAQUE_OPACITY;
949
  else
950
    color = TRANSPARENT_OPACITY;
951

952
  color_region (&maskPR, &color);
Elliot Lee's avatar
Elliot Lee committed
953 954

  /*  Determine new boundary  */
955
  if (mask->bounds_known && (op == GIMP_CHANNEL_OP_ADD) && !mask->empty)
Elliot Lee's avatar
Elliot Lee committed
956 957 958 959 960 961 962 963 964 965