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

48
#include "gimp-intl.h"
49 50


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
static GimpItem * gimp_channel_duplicate   (GimpItem         *item,
                                            GType             new_type,
                                            gboolean          add_alpha);
61 62 63 64 65 66
static void       gimp_channel_scale       (GimpItem         *item,
                                            gint              new_width,
                                            gint              new_height,
                                            gint              new_offset_x,
                                            gint              new_offset_y,
                                            GimpInterpolationType  interp_type);
67

68 69
static void       gimp_channel_push_undo   (GimpChannel      *mask,
                                            const gchar      *undo_desc);
70 71
static void       gimp_channel_validate    (TileManager      *tm,
                                            Tile             *tile);
72

73

74
static GimpDrawableClass * parent_class = NULL;
75

76

77
GType
78
gimp_channel_get_type (void)
79
{
80
  static GType channel_type = 0;
81

82
  if (! channel_type)
83
    {
84
      static const GTypeInfo channel_info =
85
      {
86 87 88 89 90 91
        sizeof (GimpChannelClass),
	(GBaseInitFunc) NULL,
	(GBaseFinalizeFunc) NULL,
	(GClassInitFunc) gimp_channel_class_init,
	NULL,		/* class_finalize */
	NULL,		/* class_data     */
92
	sizeof (GimpChannel),
93 94
	0,              /* n_preallocs    */
	(GInstanceInitFunc) gimp_channel_init,
95 96
      };

97 98 99
      channel_type = g_type_register_static (GIMP_TYPE_DRAWABLE,
					     "GimpChannel",
					     &channel_info, 0);
100 101 102 103 104 105
    }

  return channel_type;
}

static void
106
gimp_channel_class_init (GimpChannelClass *klass)
107
{
108 109 110 111
  GObjectClass      *object_class;
  GimpObjectClass   *gimp_object_class;
  GimpViewableClass *viewable_class;
  GimpItemClass     *item_class;
112

113 114
  object_class      = G_OBJECT_CLASS (klass);
  gimp_object_class = GIMP_OBJECT_CLASS (klass);
115
  viewable_class    = GIMP_VIEWABLE_CLASS (klass);
116
  item_class        = GIMP_ITEM_CLASS (klass);
117

118
  parent_class = g_type_class_peek_parent (klass);
119

120
  object_class->finalize           = gimp_channel_finalize;
121

122
  gimp_object_class->get_memsize   = gimp_channel_get_memsize;
123

124 125 126
  viewable_class->default_stock_id = "gimp-channel";

  item_class->duplicate            = gimp_channel_duplicate;
127
  item_class->scale                = gimp_channel_scale;
128 129
  item_class->default_name         = _("Channel");
  item_class->rename_desc          = _("Rename Channel");
130 131 132 133 134
}

static void
gimp_channel_init (GimpChannel *channel)
{
135
  gimp_rgba_set (&channel->color, 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE);
136 137 138 139

  channel->show_masked = FALSE;

  /*  Selection mask variables  */
140
  channel->boundary_known = FALSE;
141 142 143 144
  channel->segs_in        = NULL;
  channel->segs_out       = NULL;
  channel->num_segs_in    = 0;
  channel->num_segs_out   = 0;
145 146
  channel->empty          = FALSE;
  channel->bounds_known   = FALSE;
147 148 149 150
  channel->x1             = 0;
  channel->y1             = 0;
  channel->x2             = 0;
  channel->y2             = 0;
151
}
Elliot Lee's avatar
Elliot Lee committed
152

153
static void
154
gimp_channel_finalize (GObject *object)
155 156 157 158 159 160
{
  GimpChannel *channel;

  channel = GIMP_CHANNEL (object);

  if (channel->segs_in)
161 162 163 164 165
    {
      g_free (channel->segs_in);
      channel->segs_in = NULL;
    }

166
  if (channel->segs_out)
167 168 169 170
    {
      g_free (channel->segs_out);
      channel->segs_out = NULL;
    }
171

172
  G_OBJECT_CLASS (parent_class)->finalize (object);
173
}
Elliot Lee's avatar
Elliot Lee committed
174

175 176 177 178 179 180 181 182 183 184 185 186 187 188
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);
}

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 214 215 216 217 218 219 220 221 222 223 224 225 226 227
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;
}

228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
static void
gimp_channel_scale (GimpItem              *item,
		    gint                   new_width,
		    gint                   new_height,
                    gint                   new_offset_x,
                    gint                   new_offset_y,
                    GimpInterpolationType  interpolation_type)
{
  GimpChannel *channel;

  channel = GIMP_CHANNEL (item);

  gimp_image_undo_push_channel_mod (gimp_item_get_image (item),
                                    _("Scale Channel"),
                                    channel);

  GIMP_ITEM_CLASS (parent_class)->scale (item, new_width, new_height,
                                         new_offset_x, new_offset_y,
                                         interpolation_type);

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

252
static void
253 254
gimp_channel_push_undo (GimpChannel *mask,
                        const gchar *undo_desc)
255 256 257 258 259
{
  GimpImage *gimage;

  gimage = gimp_item_get_image (GIMP_ITEM (mask));

260 261
  g_return_if_fail (gimage != NULL);

262
  gimp_image_undo_push_mask (gimage, undo_desc, mask);
263 264 265 266 267 268 269

  mask->boundary_known = FALSE;

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

Elliot Lee's avatar
Elliot Lee committed
270
static void
Michael Natterer's avatar
Michael Natterer committed
271 272
gimp_channel_validate (TileManager *tm,
		       Tile        *tile)
Elliot Lee's avatar
Elliot Lee committed
273 274
{
  /*  Set the contents of the tile to empty  */
scott's avatar
scott committed
275
  memset (tile_data_pointer (tile, 0, 0), 
276
	  TRANSPARENT_OPACITY, tile_size (tile));
Elliot Lee's avatar
Elliot Lee committed
277 278
}

279 280 281

/*  public functions  */

Michael Natterer's avatar
Michael Natterer committed
282 283
GimpChannel *
gimp_channel_new (GimpImage     *gimage,
284 285
		  gint           width,
		  gint           height,
Michael Natterer's avatar
Michael Natterer committed
286 287
		  const gchar   *name,
		  const GimpRGB *color)
Elliot Lee's avatar
Elliot Lee committed
288
{
Michael Natterer's avatar
Michael Natterer committed
289
  GimpChannel *channel;
290

291
  g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);
292
  g_return_val_if_fail (color != NULL, NULL);
Elliot Lee's avatar
Elliot Lee committed
293

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

Michael Natterer's avatar
Michael Natterer committed
296
  gimp_drawable_configure (GIMP_DRAWABLE (channel),
297 298 299
			   gimage,
                           0, 0, width, height,
                           GIMP_GRAY_IMAGE, name);
Elliot Lee's avatar
Elliot Lee committed
300 301

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

304
  channel->show_masked = TRUE;
Elliot Lee's avatar
Elliot Lee committed
305 306

  /*  selection mask variables  */
307 308
  channel->x2          = width;
  channel->y2          = height;
Elliot Lee's avatar
Elliot Lee committed
309 310 311 312

  return channel;
}

313 314 315 316 317 318 319 320 321 322 323 324
GimpChannel *
gimp_channel_new_from_component (GimpImage       *gimage,
                                 GimpChannelType  type,
                                 const gchar     *name,
                                 const GimpRGB   *color)
{
  GimpChannel *channel;
  TileManager *projection;
  PixelRegion  src;
  PixelRegion  dest;
  gint         width;
  gint         height;
325
  gint         pixel;
326 327 328 329

  g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);
  g_return_val_if_fail (color != NULL, NULL);

330
  pixel = gimp_image_get_component_index (gimage, type);
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349

  g_return_val_if_fail (pixel != -1, NULL);

  projection = gimp_image_projection (gimage);
  width  = tile_manager_width  (projection);
  height = tile_manager_height (projection);

  channel = gimp_channel_new (gimage, width, height, name, color);

  pixel_region_init (&src, projection,
                     0, 0, width, height, FALSE);
  pixel_region_init (&dest, GIMP_DRAWABLE (channel)->tiles,
                     0, 0, width, height, TRUE);

  copy_component (&src, &dest, pixel);

  return channel;
}

350
void 
Michael Natterer's avatar
Michael Natterer committed
351
gimp_channel_set_color (GimpChannel   *channel,
352 353
			const GimpRGB *color,
                        gboolean       push_undo)
354
{
355 356
  g_return_if_fail (GIMP_IS_CHANNEL (channel));
  g_return_if_fail (color != NULL);
357

Michael Natterer's avatar
Michael Natterer committed
358 359
  if (gimp_rgba_distance (&channel->color, color) > 0.0001)
    {
360 361 362 363 364 365 366 367 368 369 370
      if (push_undo)
        {
          GimpImage *gimage;

          gimage = gimp_item_get_image (GIMP_ITEM (channel));

          if (gimage)
            gimp_image_undo_push_channel_color (gimage, _("Set Channel Color"),
                                                channel);
        }

Michael Natterer's avatar
Michael Natterer committed
371 372 373 374 375 376 377
      channel->color = *color;

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

380 381 382
void
gimp_channel_get_color (const GimpChannel *channel,
                        GimpRGB           *color)
383
{
384 385
  g_return_if_fail (GIMP_IS_CHANNEL (channel));
  g_return_if_fail (color != NULL);
386

387
  *color = channel->color;
388
}
389

390
gdouble
Michael Natterer's avatar
Michael Natterer committed
391
gimp_channel_get_opacity (const GimpChannel *channel)
392
{
393
  g_return_val_if_fail (GIMP_IS_CHANNEL (channel), GIMP_OPACITY_TRANSPARENT);
394

395
  return channel->color.a;
396 397
}

398
void
Michael Natterer's avatar
Michael Natterer committed
399
gimp_channel_set_opacity (GimpChannel *channel,
400 401
			  gdouble      opacity,
                          gboolean     push_undo)
402
{
403 404
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

405
  opacity = CLAMP (opacity, GIMP_OPACITY_TRANSPARENT, GIMP_OPACITY_OPAQUE);
406

407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
  if (channel->color.a != opacity)
    {
      if (push_undo)
        {
          GimpImage *gimage;

          gimage = gimp_item_get_image (GIMP_ITEM (channel));

          if (gimage)
            gimp_image_undo_push_channel_color (gimage, _("Set Channel Opacity"),
                                                channel);
        }

      channel->color.a = opacity;

      gimp_drawable_update (GIMP_DRAWABLE (channel),
			    0, 0,
			    GIMP_DRAWABLE (channel)->width,
			    GIMP_DRAWABLE (channel)->height);
    }
427 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
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
454
void
Michael Natterer's avatar
Michael Natterer committed
455
gimp_channel_resize (GimpChannel *channel,
456 457
		     gint         new_width,
		     gint         new_height,
Michael Natterer's avatar
Michael Natterer committed
458 459
		     gint         offx,
		     gint         offy)
Elliot Lee's avatar
Elliot Lee committed
460
{
461
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
462
  TileManager *new_tiles;
463 464 465 466
  guchar       bg = 0;
  gint         clear;
  gint         w, h;
  gint         x1, y1, x2, y2;
Elliot Lee's avatar
Elliot Lee committed
467

468 469
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

470
  if (new_width == 0 || new_height == 0)
Elliot Lee's avatar
Elliot Lee committed
471 472
    return;

473 474 475 476
  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);
477

Elliot Lee's avatar
Elliot Lee committed
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
  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
504 505 506 507
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
508 509

  /*  Configure the pixel regions  */
510 511
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
		     x1, y1, w, h, FALSE);
Elliot Lee's avatar
Elliot Lee committed
512 513

  /*  Determine whether the new channel needs to be initially cleared  */
514 515
  if ((new_width  > GIMP_DRAWABLE (channel)->width) ||
      (new_height > GIMP_DRAWABLE (channel)->height) ||
Elliot Lee's avatar
Elliot Lee committed
516 517 518 519 520 521 522 523 524 525 526
      (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)
    {
527 528 529 530 531
      pixel_region_init (&destPR, new_tiles,
                         0, 0,
                         new_width, new_height,
                         TRUE);

Elliot Lee's avatar
Elliot Lee committed
532 533 534 535 536 537 538 539 540
      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  */
541 542 543
  gimp_image_undo_push_channel_mod (gimp_item_get_image (GIMP_ITEM (channel)),
                                    _("Resize Channel"),
                                    channel);
Elliot Lee's avatar
Elliot Lee committed
544 545

  /*  Configure the new channel  */
546 547 548
  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
549 550 551 552 553

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

  /*  update the new channel area  */
Michael Natterer's avatar
Michael Natterer committed
554 555 556 557
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Michael Natterer's avatar
Michael Natterer committed
558 559

  gimp_viewable_size_changed (GIMP_VIEWABLE (channel));
Elliot Lee's avatar
Elliot Lee committed
560 561
}

562

563 564 565
/******************************/
/*  selection mask functions  */
/******************************/
Elliot Lee's avatar
Elliot Lee committed
566

Michael Natterer's avatar
Michael Natterer committed
567 568
GimpChannel *
gimp_channel_new_mask (GimpImage *gimage,
569 570
		       gint       width,
		       gint       height)
Elliot Lee's avatar
Elliot Lee committed
571
{
Michael Natterer's avatar
Michael Natterer committed
572 573
  GimpRGB      black = { 0.0, 0.0, 0.0, 0.5 };
  GimpChannel *new_channel;
Elliot Lee's avatar
Elliot Lee committed
574

575 576
  g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);

Elliot Lee's avatar
Elliot Lee committed
577
  /*  Create the new channel  */
Michael Natterer's avatar
Michael Natterer committed
578 579
  new_channel = gimp_channel_new (gimage, width, height,
				  _("Selection Mask"), &black);
Elliot Lee's avatar
Elliot Lee committed
580 581

  /*  Set the validate procedure  */
582
  tile_manager_set_validate_proc (GIMP_DRAWABLE (new_channel)->tiles,
Michael Natterer's avatar
Michael Natterer committed
583
				  gimp_channel_validate);
Elliot Lee's avatar
Elliot Lee committed
584 585 586 587

  return new_channel;
}

588
gboolean
Michael Natterer's avatar
Michael Natterer committed
589 590 591 592 593 594 595 596 597
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
598
{
599
  gint        x3, y3, x4, y4;
Elliot Lee's avatar
Elliot Lee committed
600 601
  PixelRegion bPR;

602
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);
603 604 605 606
  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);
607

Elliot Lee's avatar
Elliot Lee committed
608 609 610 611 612 613 614 615
  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
616
      if (gimp_channel_bounds (mask, &x3, &y3, &x4, &y4))
Elliot Lee's avatar
Elliot Lee committed
617
	{
618 619
	  pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles,
			     x3, y3, (x4 - x3), (y4 - y3), FALSE);
Elliot Lee's avatar
Elliot Lee committed
620 621 622 623
	  mask->segs_out = find_mask_boundary (&bPR, &mask->num_segs_out,
					       IgnoreBounds,
					       x1, y1,
					       x2, y2);
624 625 626 627
	  x1 = MAX (x1, x3);
	  y1 = MAX (y1, y3);
	  x2 = MIN (x2, x4);
	  y2 = MIN (y2, y4);
Elliot Lee's avatar
Elliot Lee committed
628 629 630

	  if (x2 > x1 && y2 > y1)
	    {
631 632 633 634
	      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
635 636 637 638 639 640 641
	      mask->segs_in =  find_mask_boundary (&bPR, &mask->num_segs_in,
						   WithinBounds,
						   x1, y1,
						   x2, y2);
	    }
	  else
	    {
642
	      mask->segs_in     = NULL;
Elliot Lee's avatar
Elliot Lee committed
643 644 645 646 647
	      mask->num_segs_in = 0;
	    }
	}
      else
	{
648 649 650
	  mask->segs_in      = NULL;
	  mask->segs_out     = NULL;
	  mask->num_segs_in  = 0;
Elliot Lee's avatar
Elliot Lee committed
651 652
	  mask->num_segs_out = 0;
	}
653

Elliot Lee's avatar
Elliot Lee committed
654 655 656
      mask->boundary_known = TRUE;
    }

657 658 659
  *segs_in      = mask->segs_in;
  *segs_out     = mask->segs_out;
  *num_segs_in  = mask->num_segs_in;
Elliot Lee's avatar
Elliot Lee committed
660 661 662 663 664
  *num_segs_out = mask->num_segs_out;

  return TRUE;
}

665
gint
Michael Natterer's avatar
Michael Natterer committed
666 667 668
gimp_channel_value (GimpChannel *mask,
		    gint         x,
		    gint         y)
Elliot Lee's avatar
Elliot Lee committed
669 670
{
  Tile *tile;
671
  gint  val;
Elliot Lee's avatar
Elliot Lee committed
672

673 674
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), 0);

Elliot Lee's avatar
Elliot Lee committed
675 676 677 678 679 680 681 682 683 684
  /*  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
    {
685 686
      if (x < 0 || x >= GIMP_DRAWABLE (mask)->width ||
	  y < 0 || y >= GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
687 688 689
	return 0;
    }

690 691
  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));
692
  tile_release (tile, FALSE);
Elliot Lee's avatar
Elliot Lee committed
693 694 695 696

  return val;
}

697
gboolean
Michael Natterer's avatar
Michael Natterer committed
698 699 700 701 702
gimp_channel_bounds (GimpChannel *mask,
		     gint        *x1,
		     gint        *y1,
		     gint        *x2,
		     gint        *y2)
Elliot Lee's avatar
Elliot Lee committed
703
{
704 705 706 707 708 709 710
  PixelRegion  maskPR;
  guchar      *data, *data1;
  gint         x, y;
  gint         ex, ey;
  gint         tx1, tx2, ty1, ty2;
  gint         minx, maxx;
  gpointer     pr;
711

712
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);
713 714 715 716
  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);
717

Elliot Lee's avatar
Elliot Lee committed
718 719 720 721 722 723 724 725
  /*  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;

726
      return ! mask->empty;
Elliot Lee's avatar
Elliot Lee committed
727 728 729
    }

  /*  go through and calculate the bounds  */
730 731
  tx1 = GIMP_DRAWABLE (mask)->width;
  ty1 = GIMP_DRAWABLE (mask)->height;
732 733
  tx2 = 0;
  ty2 = 0;
Elliot Lee's avatar
Elliot Lee committed
734

735 736 737 738
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (mask)->width,
		     GIMP_DRAWABLE (mask)->height, FALSE);
739

740 741 742
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
743
    {
744
      data1 = data = maskPR.data;
Elliot Lee's avatar
Elliot Lee committed
745 746
      ex = maskPR.x + maskPR.w;
      ey = maskPR.y + maskPR.h;
747 748
      /* only check the pixels if this tile is not fully within the currently
	 computed bounds */
749 750
      if (maskPR.x < tx1 || ex > tx2 ||
	  maskPR.y < ty1 || ey > ty2)
751
        {
752 753 754
	  /* 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])
755 756 757 758 759 760 761 762 763 764
            {
              if (maskPR.x < tx1)
                tx1 = maskPR.x;
              if (ex > tx2)
                tx2 = ex;
              if (maskPR.y < ty1)
                ty1 = maskPR.y;
              if (ey > ty2)
                ty2 = ey;
            }
765
	  else
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792
            {
              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
793 794
    }

795 796
  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
797

798
  if (tx1 == GIMP_DRAWABLE (mask)->width && ty1 == GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
799 800
    {
      mask->empty = TRUE;
801 802 803 804
      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
805 806 807 808
    }
  else
    {
      mask->empty = FALSE;
809 810 811 812
      mask->x1    = tx1;
      mask->y1    = ty1;
      mask->x2    = tx2;
      mask->y2    = ty2;
Elliot Lee's avatar
Elliot Lee committed
813
    }
814

Elliot Lee's avatar
Elliot Lee committed
815 816
  mask->bounds_known = TRUE;

817 818 819 820 821
  *x1 = tx1;
  *x2 = tx2;
  *y1 = ty1;
  *y2 = ty2;

822
  return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
823 824
}

825
gboolean
Michael Natterer's avatar
Michael Natterer committed
826
gimp_channel_is_empty (GimpChannel *mask)
Elliot Lee's avatar
Elliot Lee committed
827
{
828 829 830 831
  PixelRegion  maskPR;
  guchar      *data;
  gint         x, y;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
832

833 834
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);

Elliot Lee's avatar
Elliot Lee committed
835 836 837
  if (mask->bounds_known)
    return mask->empty;

838 839 840 841
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (mask)->width,
		     GIMP_DRAWABLE (mask)->height, FALSE);
842

843 844 845
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
846 847 848
    {
      /*  check if any pixel in the mask is non-zero  */
      data = maskPR.data;
849

Elliot Lee's avatar
Elliot Lee committed
850 851 852 853 854 855 856 857 858 859 860 861 862 863 864
      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);

865 866 867 868 869 870
  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
871
  mask->boundary_known = TRUE;
872 873 874 875
  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
876 877 878 879 880

  return TRUE;
}

void
Michael Natterer's avatar
Michael Natterer committed
881 882 883
gimp_channel_add_segment (GimpChannel *mask,
			  gint         x,
			  gint         y,
884 885
			  gint         width,
			  gint         value)
Elliot Lee's avatar
Elliot Lee committed
886
{
887 888 889 890 891
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
892

893 894
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

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

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

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

909 910 911
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
912 913 914 915 916 917 918 919 920 921 922 923 924 925
    {
      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
926 927 928
gimp_channel_sub_segment (GimpChannel *mask,
			  gint         x,
			  gint         y,
929 930
			  gint         width,
			  gint         value)
Elliot Lee's avatar
Elliot Lee committed
931
{
932 933 934 935 936
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
937

938 939
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

Elliot Lee's avatar
Elliot Lee committed
940 941
  /*  check horizontal extents...  */
  x2 = x + width;
942 943
  x2 = CLAMP (x2, 0, GIMP_DRAWABLE (mask)->width);
  x =  CLAMP (x,  0, GIMP_DRAWABLE (mask)->width);
Elliot Lee's avatar
Elliot Lee committed
944
  width = x2 - x;
945 946 947

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

949
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
950 951
    return;

952 953 954
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
                     x, y, width, 1, TRUE);

955 956 957
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
958 959 960 961 962 963 964 965 966 967 968 969 970 971
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data - value;
	  if (val < 0)
	    val = 0;
	  *data++ = val;
	}
    }
}

void
972 973 974 975 976 977
gimp_channel_combine_rect (GimpChannel    *mask,
			   GimpChannelOps  op,
			   gint            x,
			   gint            y,
			   gint            w,
			   gint            h)
Elliot Lee's avatar
Elliot Lee committed
978
{
979
  gint        x2, y2;
Elliot Lee's avatar
Elliot Lee committed
980
  PixelRegion maskPR;
981
  guchar      color;
982

983 984
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

985 986
  y2 = y + h;
  x2 = x + w;
Elliot Lee's avatar
Elliot Lee committed
987

988 989 990 991
  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
992

993
  if (x2 - x <= 0 || y2 - y <= 0)
Elliot Lee's avatar
Elliot Lee committed
994 995
    return;

996 997
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, x2 - x, y2 - y, TRUE);
998 999

  if (op == GIMP_CHANNEL_OP_ADD || op == GIMP_CHANNEL_OP_REPLACE)
1000
    color = OPAQUE_OPACITY;