gimpchannel.c 45.3 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 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
static GimpItem * gimp_channel_duplicate   (GimpItem         *item,
                                            GType             new_type,
                                            gboolean          add_alpha);

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

67

68
static GimpDrawableClass * parent_class = NULL;
69

70

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

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

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

  return channel_type;
}

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

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

110
  parent_class = g_type_class_peek_parent (klass);
111

112 113 114
  object_class->finalize         = gimp_channel_finalize;

  gimp_object_class->get_memsize = gimp_channel_get_memsize;
115 116

  item_class->duplicate          = gimp_channel_duplicate;
117 118
  item_class->default_name       = _("Channel");
  item_class->rename_desc        = _("Rename Channel");
119 120 121 122 123
}

static void
gimp_channel_init (GimpChannel *channel)
{
124
  gimp_rgba_set (&channel->color, 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE);
125 126 127 128

  channel->show_masked = FALSE;

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

142
static void
143
gimp_channel_finalize (GObject *object)
144 145 146 147 148 149
{
  GimpChannel *channel;

  channel = GIMP_CHANNEL (object);

  if (channel->segs_in)
150 151 152 153 154
    {
      g_free (channel->segs_in);
      channel->segs_in = NULL;
    }

155
  if (channel->segs_out)
156 157 158 159
    {
      g_free (channel->segs_out);
      channel->segs_out = NULL;
    }
160

161
  G_OBJECT_CLASS (parent_class)->finalize (object);
162
}
Elliot Lee's avatar
Elliot Lee committed
163

164 165 166 167 168 169 170 171 172 173 174 175 176 177
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);
}

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 214 215 216
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;
}

217
static void
218 219
gimp_channel_push_undo (GimpChannel *mask,
                        const gchar *undo_desc)
220 221 222 223 224
{
  GimpImage *gimage;

  gimage = gimp_item_get_image (GIMP_ITEM (mask));

225 226
  g_return_if_fail (gimage != NULL);

227
  gimp_image_undo_push_mask (gimage, undo_desc, mask);
228 229 230 231 232 233 234

  mask->boundary_known = FALSE;

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

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

244 245 246

/*  public functions  */

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

256
  g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);
257
  g_return_val_if_fail (color != NULL, NULL);
Elliot Lee's avatar
Elliot Lee committed
258

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

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

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

269
  channel->show_masked = TRUE;
Elliot Lee's avatar
Elliot Lee committed
270 271

  /*  selection mask variables  */
272 273
  channel->x2          = width;
  channel->y2          = height;
Elliot Lee's avatar
Elliot Lee committed
274 275 276 277

  return channel;
}

278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
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;
  gint         pixel = -1;

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

  switch (type)
    {
    case GIMP_RED_CHANNEL:      pixel = RED_PIX;     break;
    case GIMP_GREEN_CHANNEL:    pixel = GREEN_PIX;   break;
    case GIMP_BLUE_CHANNEL:     pixel = BLUE_PIX;    break;
    case GIMP_GRAY_CHANNEL:     pixel = GRAY_PIX;    break;
    case GIMP_INDEXED_CHANNEL:  pixel = INDEXED_PIX; break;
    case GIMP_ALPHA_CHANNEL:
      switch (gimp_image_base_type (gimage))
	{
	case GIMP_RGB:     pixel = ALPHA_PIX;   break;
	case GIMP_GRAY:    pixel = ALPHA_G_PIX; break;
	case GIMP_INDEXED: pixel = ALPHA_I_PIX; break;
	}
      break;
    }

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

330
void 
Michael Natterer's avatar
Michael Natterer committed
331 332
gimp_channel_set_color (GimpChannel   *channel,
			const GimpRGB *color)
333
{
334 335
  g_return_if_fail (GIMP_IS_CHANNEL (channel));
  g_return_if_fail (color != NULL);
336

Michael Natterer's avatar
Michael Natterer committed
337 338 339 340 341 342 343 344 345
  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);
    }
346
}
347

348 349 350
void
gimp_channel_get_color (const GimpChannel *channel,
                        GimpRGB           *color)
351
{
352 353
  g_return_if_fail (GIMP_IS_CHANNEL (channel));
  g_return_if_fail (color != NULL);
354

355
  *color = channel->color;
356
}
357

358
gdouble
Michael Natterer's avatar
Michael Natterer committed
359
gimp_channel_get_opacity (const GimpChannel *channel)
360
{
361
  g_return_val_if_fail (GIMP_IS_CHANNEL (channel), GIMP_OPACITY_TRANSPARENT);
362

363
  return channel->color.a;
364 365
}

366
void
Michael Natterer's avatar
Michael Natterer committed
367
gimp_channel_set_opacity (GimpChannel *channel,
368
			  gdouble      opacity)
369
{
370 371
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

372
  opacity = CLAMP (opacity, GIMP_OPACITY_TRANSPARENT, GIMP_OPACITY_OPAQUE);
373

374
  channel->color.a = opacity;
375 376
}

377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
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
402
void
403 404 405 406
gimp_channel_scale (GimpChannel           *channel,
		    gint                   new_width,
		    gint                   new_height,
                    GimpInterpolationType  interpolation_type)
Elliot Lee's avatar
Elliot Lee committed
407
{
408
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
409 410
  TileManager *new_tiles;

411 412
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

Elliot Lee's avatar
Elliot Lee committed
413 414 415 416
  if (new_width == 0 || new_height == 0)
    return;

  /*  Update the old channel position  */
Michael Natterer's avatar
Michael Natterer committed
417 418 419 420
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
421

422 423 424
  /*  Allocate the new channel  */
  new_tiles = tile_manager_new (new_width, new_height, 1);

Elliot Lee's avatar
Elliot Lee committed
425
  /*  Configure the pixel regions  */
426 427 428
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (channel)->width,
429 430
		     GIMP_DRAWABLE (channel)->height,
                     FALSE);
Elliot Lee's avatar
Elliot Lee committed
431

432 433 434 435
  pixel_region_init (&destPR, new_tiles,
                     0, 0,
                     new_width, new_height,
                     TRUE);
Elliot Lee's avatar
Elliot Lee committed
436

437
  /*  Sclae the channel  */
438
  scale_region (&srcPR, &destPR, interpolation_type);
Elliot Lee's avatar
Elliot Lee committed
439 440

  /*  Push the channel on the undo stack  */
441 442 443
  gimp_image_undo_push_channel_mod (gimp_item_get_image (GIMP_ITEM (channel)),
                                    _("Scale Channel"),
                                    channel);
Elliot Lee's avatar
Elliot Lee committed
444 445

  /*  Configure the new channel  */
446 447
  GIMP_DRAWABLE (channel)->tiles  = new_tiles;
  GIMP_DRAWABLE (channel)->width  = new_width;
448
  GIMP_DRAWABLE (channel)->height = new_height;
Elliot Lee's avatar
Elliot Lee committed
449 450 451 452 453

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

  /*  Update the new 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);
Michael Natterer's avatar
Michael Natterer committed
458 459

  gimp_viewable_size_changed (GIMP_VIEWABLE (channel));
Elliot Lee's avatar
Elliot Lee committed
460 461 462
}

void
Michael Natterer's avatar
Michael Natterer committed
463
gimp_channel_resize (GimpChannel *channel,
464 465
		     gint         new_width,
		     gint         new_height,
Michael Natterer's avatar
Michael Natterer committed
466 467
		     gint         offx,
		     gint         offy)
Elliot Lee's avatar
Elliot Lee committed
468
{
469
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
470
  TileManager *new_tiles;
471 472 473 474
  guchar       bg = 0;
  gint         clear;
  gint         w, h;
  gint         x1, y1, x2, y2;
Elliot Lee's avatar
Elliot Lee committed
475

476 477
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

478
  if (new_width == 0 || new_height == 0)
Elliot Lee's avatar
Elliot Lee committed
479 480
    return;

481 482 483 484
  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);
485

Elliot Lee's avatar
Elliot Lee committed
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
  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
512 513 514 515
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
516 517

  /*  Configure the pixel regions  */
518 519
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
		     x1, y1, w, h, FALSE);
Elliot Lee's avatar
Elliot Lee committed
520 521

  /*  Determine whether the new channel needs to be initially cleared  */
522 523
  if ((new_width  > GIMP_DRAWABLE (channel)->width) ||
      (new_height > GIMP_DRAWABLE (channel)->height) ||
Elliot Lee's avatar
Elliot Lee committed
524 525 526 527 528 529 530 531 532 533 534
      (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)
    {
535 536 537 538 539
      pixel_region_init (&destPR, new_tiles,
                         0, 0,
                         new_width, new_height,
                         TRUE);

Elliot Lee's avatar
Elliot Lee committed
540 541 542 543 544 545 546 547 548
      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  */
549 550 551
  gimp_image_undo_push_channel_mod (gimp_item_get_image (GIMP_ITEM (channel)),
                                    _("Resize Channel"),
                                    channel);
Elliot Lee's avatar
Elliot Lee committed
552 553

  /*  Configure the new channel  */
554 555 556
  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
557 558 559 560 561

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

  /*  update the new channel area  */
Michael Natterer's avatar
Michael Natterer committed
562 563 564 565
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Michael Natterer's avatar
Michael Natterer committed
566 567

  gimp_viewable_size_changed (GIMP_VIEWABLE (channel));
Elliot Lee's avatar
Elliot Lee committed
568 569
}

570

571 572 573
/******************************/
/*  selection mask functions  */
/******************************/
Elliot Lee's avatar
Elliot Lee committed
574

Michael Natterer's avatar
Michael Natterer committed
575 576
GimpChannel *
gimp_channel_new_mask (GimpImage *gimage,
577 578
		       gint       width,
		       gint       height)
Elliot Lee's avatar
Elliot Lee committed
579
{
Michael Natterer's avatar
Michael Natterer committed
580 581
  GimpRGB      black = { 0.0, 0.0, 0.0, 0.5 };
  GimpChannel *new_channel;
Elliot Lee's avatar
Elliot Lee committed
582

583 584
  g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);

Elliot Lee's avatar
Elliot Lee committed
585
  /*  Create the new channel  */
Michael Natterer's avatar
Michael Natterer committed
586 587
  new_channel = gimp_channel_new (gimage, width, height,
				  _("Selection Mask"), &black);
Elliot Lee's avatar
Elliot Lee committed
588 589

  /*  Set the validate procedure  */
590
  tile_manager_set_validate_proc (GIMP_DRAWABLE (new_channel)->tiles,
Michael Natterer's avatar
Michael Natterer committed
591
				  gimp_channel_validate);
Elliot Lee's avatar
Elliot Lee committed
592 593 594 595

  return new_channel;
}

596
gboolean
Michael Natterer's avatar
Michael Natterer committed
597 598 599 600 601 602 603 604 605
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
606
{
607
  gint        x3, y3, x4, y4;
Elliot Lee's avatar
Elliot Lee committed
608 609
  PixelRegion bPR;

610
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);
611 612 613 614
  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);
615

Elliot Lee's avatar
Elliot Lee committed
616 617 618 619 620 621 622 623
  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
624
      if (gimp_channel_bounds (mask, &x3, &y3, &x4, &y4))
Elliot Lee's avatar
Elliot Lee committed
625
	{
626 627
	  pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles,
			     x3, y3, (x4 - x3), (y4 - y3), FALSE);
Elliot Lee's avatar
Elliot Lee committed
628 629 630 631
	  mask->segs_out = find_mask_boundary (&bPR, &mask->num_segs_out,
					       IgnoreBounds,
					       x1, y1,
					       x2, y2);
632 633 634 635
	  x1 = MAX (x1, x3);
	  y1 = MAX (y1, y3);
	  x2 = MIN (x2, x4);
	  y2 = MIN (y2, y4);
Elliot Lee's avatar
Elliot Lee committed
636 637 638

	  if (x2 > x1 && y2 > y1)
	    {
639 640 641 642
	      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
643 644 645 646 647 648 649
	      mask->segs_in =  find_mask_boundary (&bPR, &mask->num_segs_in,
						   WithinBounds,
						   x1, y1,
						   x2, y2);
	    }
	  else
	    {
650
	      mask->segs_in     = NULL;
Elliot Lee's avatar
Elliot Lee committed
651 652 653 654 655
	      mask->num_segs_in = 0;
	    }
	}
      else
	{
656 657 658
	  mask->segs_in      = NULL;
	  mask->segs_out     = NULL;
	  mask->num_segs_in  = 0;
Elliot Lee's avatar
Elliot Lee committed
659 660
	  mask->num_segs_out = 0;
	}
661

Elliot Lee's avatar
Elliot Lee committed
662 663 664
      mask->boundary_known = TRUE;
    }

665 666 667
  *segs_in      = mask->segs_in;
  *segs_out     = mask->segs_out;
  *num_segs_in  = mask->num_segs_in;
Elliot Lee's avatar
Elliot Lee committed
668 669 670 671 672
  *num_segs_out = mask->num_segs_out;

  return TRUE;
}

673
gint
Michael Natterer's avatar
Michael Natterer committed
674 675 676
gimp_channel_value (GimpChannel *mask,
		    gint         x,
		    gint         y)
Elliot Lee's avatar
Elliot Lee committed
677 678
{
  Tile *tile;
679
  gint  val;
Elliot Lee's avatar
Elliot Lee committed
680

681 682
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), 0);

Elliot Lee's avatar
Elliot Lee committed
683 684 685 686 687 688 689 690 691 692
  /*  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
    {
693 694
      if (x < 0 || x >= GIMP_DRAWABLE (mask)->width ||
	  y < 0 || y >= GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
695 696 697
	return 0;
    }

698 699
  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));
700
  tile_release (tile, FALSE);
Elliot Lee's avatar
Elliot Lee committed
701 702 703 704

  return val;
}

705
gboolean
Michael Natterer's avatar
Michael Natterer committed
706 707 708 709 710
gimp_channel_bounds (GimpChannel *mask,
		     gint        *x1,
		     gint        *y1,
		     gint        *x2,
		     gint        *y2)
Elliot Lee's avatar
Elliot Lee committed
711
{
712 713 714 715 716 717 718
  PixelRegion  maskPR;
  guchar      *data, *data1;
  gint         x, y;
  gint         ex, ey;
  gint         tx1, tx2, ty1, ty2;
  gint         minx, maxx;
  gpointer     pr;
719

720
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);
721 722 723 724
  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);
725

Elliot Lee's avatar
Elliot Lee committed
726 727 728 729 730 731 732 733
  /*  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;

734
      return ! mask->empty;
Elliot Lee's avatar
Elliot Lee committed
735 736 737
    }

  /*  go through and calculate the bounds  */
738 739
  tx1 = GIMP_DRAWABLE (mask)->width;
  ty1 = GIMP_DRAWABLE (mask)->height;
740 741
  tx2 = 0;
  ty2 = 0;
Elliot Lee's avatar
Elliot Lee committed
742

743 744 745 746
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (mask)->width,
		     GIMP_DRAWABLE (mask)->height, FALSE);
747

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

803 804
  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
805

806
  if (tx1 == GIMP_DRAWABLE (mask)->width && ty1 == GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
807 808
    {
      mask->empty = TRUE;
809 810 811 812
      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
813 814 815 816
    }
  else
    {
      mask->empty = FALSE;
817 818 819 820
      mask->x1    = tx1;
      mask->y1    = ty1;
      mask->x2    = tx2;
      mask->y2    = ty2;
Elliot Lee's avatar
Elliot Lee committed
821
    }
822

Elliot Lee's avatar
Elliot Lee committed
823 824
  mask->bounds_known = TRUE;

825 826 827 828 829
  *x1 = tx1;
  *x2 = tx2;
  *y1 = ty1;
  *y2 = ty2;

830
  return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
831 832
}

833
gboolean
Michael Natterer's avatar
Michael Natterer committed
834
gimp_channel_is_empty (GimpChannel *mask)
Elliot Lee's avatar
Elliot Lee committed
835
{
836 837 838 839
  PixelRegion  maskPR;
  guchar      *data;
  gint         x, y;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
840

841 842
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);

Elliot Lee's avatar
Elliot Lee committed
843 844 845
  if (mask->bounds_known)
    return mask->empty;

846 847 848 849
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (mask)->width,
		     GIMP_DRAWABLE (mask)->height, FALSE);
850

851 852 853
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
854 855 856
    {
      /*  check if any pixel in the mask is non-zero  */
      data = maskPR.data;
857

Elliot Lee's avatar
Elliot Lee committed
858 859 860 861 862 863 864 865 866 867 868 869 870 871 872
      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);

873 874 875 876 877 878
  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
879
  mask->boundary_known = TRUE;
880 881 882 883
  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
884 885 886 887 888

  return TRUE;
}

void
Michael Natterer's avatar
Michael Natterer committed
889 890 891
gimp_channel_add_segment (GimpChannel *mask,
			  gint         x,
			  gint         y,
892 893
			  gint         width,
			  gint         value)
Elliot Lee's avatar
Elliot Lee committed
894
{
895 896 897 898 899
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
900

901 902
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

Elliot Lee's avatar
Elliot Lee committed
903 904
  /*  check horizontal extents...  */
  x2 = x + width;
905 906
  x2 = CLAMP (x2, 0, GIMP_DRAWABLE (mask)->width);
  x  = CLAMP (x,  0, GIMP_DRAWABLE (mask)->width);
Elliot Lee's avatar
Elliot Lee committed
907
  width = x2 - x;
908 909
  if (!width)
    return;
Elliot Lee's avatar
Elliot Lee committed
910

911
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
912 913
    return;

914 915
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, width, 1, TRUE);
916

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

946 947
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

Elliot Lee's avatar
Elliot Lee committed
948 949
  /*  check horizontal extents...  */
  x2 = x + width;
950 951
  x2 = CLAMP (x2, 0, GIMP_DRAWABLE (mask)->width);
  x =  CLAMP (x,  0, GIMP_DRAWABLE (mask)->width);
Elliot Lee's avatar
Elliot Lee committed
952
  width = x2 - x;
953 954 955

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

957
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
958 959
    return;

960 961 962
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
                     x, y, width, 1, TRUE);

963 964 965
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
966 967 968 969 970 971 972 973 974 975 976 977 978 979
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data - value;
	  if (val < 0)
	    val = 0;
	  *data++ = val;
	}
    }
}

void
980 981 982 983 984 985
gimp_channel_combine_rect (GimpChannel    *mask,
			   GimpChannelOps  op,
			   gint            x,
			   gint            y,
			   gint            w,
			   gint            h)
Elliot Lee's avatar
Elliot Lee committed
986
{
987
  gint        x2, y2;
Elliot Lee's avatar
Elliot Lee committed
988
  PixelRegion maskPR;
989
  guchar      color;
990

991 992
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

jaycox's avatar