gimpchannel.c 43.8 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 70 71
static void       gimp_channel_resize      (GimpItem         *item,
                                            gint              new_width,
                                            gint              new_height,
                                            gint              offx,
                                            gint              offy);
72

73 74
static void       gimp_channel_push_undo   (GimpChannel      *mask,
                                            const gchar      *undo_desc);
75 76
static void       gimp_channel_validate    (TileManager      *tm,
                                            Tile             *tile);
77

78

79
static GimpDrawableClass * parent_class = NULL;
80

81

82
GType
83
gimp_channel_get_type (void)
84
{
85
  static GType channel_type = 0;
86

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

102 103 104
      channel_type = g_type_register_static (GIMP_TYPE_DRAWABLE,
					     "GimpChannel",
					     &channel_info, 0);
105 106 107 108 109 110
    }

  return channel_type;
}

static void
111
gimp_channel_class_init (GimpChannelClass *klass)
112
{
113 114 115 116
  GObjectClass      *object_class;
  GimpObjectClass   *gimp_object_class;
  GimpViewableClass *viewable_class;
  GimpItemClass     *item_class;
117

118 119
  object_class      = G_OBJECT_CLASS (klass);
  gimp_object_class = GIMP_OBJECT_CLASS (klass);
120
  viewable_class    = GIMP_VIEWABLE_CLASS (klass);
121
  item_class        = GIMP_ITEM_CLASS (klass);
122

123
  parent_class = g_type_class_peek_parent (klass);
124

125
  object_class->finalize           = gimp_channel_finalize;
126

127
  gimp_object_class->get_memsize   = gimp_channel_get_memsize;
128

129 130 131
  viewable_class->default_stock_id = "gimp-channel";

  item_class->duplicate            = gimp_channel_duplicate;
132
  item_class->scale                = gimp_channel_scale;
133
  item_class->resize               = gimp_channel_resize;
134 135
  item_class->default_name         = _("Channel");
  item_class->rename_desc          = _("Rename Channel");
136 137 138 139 140
}

static void
gimp_channel_init (GimpChannel *channel)
{
141
  gimp_rgba_set (&channel->color, 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE);
142 143 144 145

  channel->show_masked = FALSE;

  /*  Selection mask variables  */
146
  channel->boundary_known = FALSE;
147 148 149 150
  channel->segs_in        = NULL;
  channel->segs_out       = NULL;
  channel->num_segs_in    = 0;
  channel->num_segs_out   = 0;
151 152
  channel->empty          = FALSE;
  channel->bounds_known   = FALSE;
153 154 155 156
  channel->x1             = 0;
  channel->y1             = 0;
  channel->x2             = 0;
  channel->y2             = 0;
157
}
Elliot Lee's avatar
Elliot Lee committed
158

159
static void
160
gimp_channel_finalize (GObject *object)
161 162 163 164 165 166
{
  GimpChannel *channel;

  channel = GIMP_CHANNEL (object);

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

172
  if (channel->segs_out)
173 174 175 176
    {
      g_free (channel->segs_out);
      channel->segs_out = NULL;
    }
177

178
  G_OBJECT_CLASS (parent_class)->finalize (object);
179
}
Elliot Lee's avatar
Elliot Lee committed
180

181 182 183 184 185 186 187 188 189 190 191 192 193 194
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);
}

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 228 229 230 231 232 233
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;
}

234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
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;
}

258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
static void
gimp_channel_resize (GimpItem *item,
		     gint      new_width,
		     gint      new_height,
		     gint      offset_x,
		     gint      offset_y)
{
  GimpChannel *channel;

  channel = GIMP_CHANNEL (item);

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

  GIMP_ITEM_CLASS (parent_class)->resize (item, new_width, new_height,
                                          offset_x, offset_y);

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

280
static void
281 282
gimp_channel_push_undo (GimpChannel *mask,
                        const gchar *undo_desc)
283 284 285 286 287
{
  GimpImage *gimage;

  gimage = gimp_item_get_image (GIMP_ITEM (mask));

288 289
  g_return_if_fail (gimage != NULL);

290
  gimp_image_undo_push_mask (gimage, undo_desc, mask);
291 292 293 294 295 296 297

  mask->boundary_known = FALSE;

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

Elliot Lee's avatar
Elliot Lee committed
298
static void
Michael Natterer's avatar
Michael Natterer committed
299 300
gimp_channel_validate (TileManager *tm,
		       Tile        *tile)
Elliot Lee's avatar
Elliot Lee committed
301 302
{
  /*  Set the contents of the tile to empty  */
scott's avatar
scott committed
303
  memset (tile_data_pointer (tile, 0, 0), 
304
	  TRANSPARENT_OPACITY, tile_size (tile));
Elliot Lee's avatar
Elliot Lee committed
305 306
}

307 308 309

/*  public functions  */

Michael Natterer's avatar
Michael Natterer committed
310 311
GimpChannel *
gimp_channel_new (GimpImage     *gimage,
312 313
		  gint           width,
		  gint           height,
Michael Natterer's avatar
Michael Natterer committed
314 315
		  const gchar   *name,
		  const GimpRGB *color)
Elliot Lee's avatar
Elliot Lee committed
316
{
Michael Natterer's avatar
Michael Natterer committed
317
  GimpChannel *channel;
318

319
  g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);
320
  g_return_val_if_fail (color != NULL, NULL);
Elliot Lee's avatar
Elliot Lee committed
321

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

Michael Natterer's avatar
Michael Natterer committed
324
  gimp_drawable_configure (GIMP_DRAWABLE (channel),
325 326 327
			   gimage,
                           0, 0, width, height,
                           GIMP_GRAY_IMAGE, name);
Elliot Lee's avatar
Elliot Lee committed
328 329

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

332
  channel->show_masked = TRUE;
Elliot Lee's avatar
Elliot Lee committed
333 334

  /*  selection mask variables  */
335 336
  channel->x2          = width;
  channel->y2          = height;
Elliot Lee's avatar
Elliot Lee committed
337 338 339 340

  return channel;
}

341 342 343 344 345 346 347 348 349 350 351 352
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;
353
  gint         pixel;
354 355 356 357

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

358
  pixel = gimp_image_get_component_index (gimage, type);
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377

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

378
void 
Michael Natterer's avatar
Michael Natterer committed
379
gimp_channel_set_color (GimpChannel   *channel,
380 381
			const GimpRGB *color,
                        gboolean       push_undo)
382
{
383 384
  g_return_if_fail (GIMP_IS_CHANNEL (channel));
  g_return_if_fail (color != NULL);
385

Michael Natterer's avatar
Michael Natterer committed
386 387
  if (gimp_rgba_distance (&channel->color, color) > 0.0001)
    {
388 389 390 391 392 393 394 395 396 397 398
      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
399 400 401 402 403 404 405
      channel->color = *color;

      gimp_drawable_update (GIMP_DRAWABLE (channel),
			    0, 0,
			    GIMP_DRAWABLE (channel)->width,
			    GIMP_DRAWABLE (channel)->height);
    }
406
}
407

408 409 410
void
gimp_channel_get_color (const GimpChannel *channel,
                        GimpRGB           *color)
411
{
412 413
  g_return_if_fail (GIMP_IS_CHANNEL (channel));
  g_return_if_fail (color != NULL);
414

415
  *color = channel->color;
416
}
417

418
gdouble
Michael Natterer's avatar
Michael Natterer committed
419
gimp_channel_get_opacity (const GimpChannel *channel)
420
{
421
  g_return_val_if_fail (GIMP_IS_CHANNEL (channel), GIMP_OPACITY_TRANSPARENT);
422

423
  return channel->color.a;
424 425
}

426
void
Michael Natterer's avatar
Michael Natterer committed
427
gimp_channel_set_opacity (GimpChannel *channel,
428 429
			  gdouble      opacity,
                          gboolean     push_undo)
430
{
431 432
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

433
  opacity = CLAMP (opacity, GIMP_OPACITY_TRANSPARENT, GIMP_OPACITY_OPAQUE);
434

435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
  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);
    }
455 456
}

457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
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);
    }
}

482

483 484 485
/******************************/
/*  selection mask functions  */
/******************************/
Elliot Lee's avatar
Elliot Lee committed
486

Michael Natterer's avatar
Michael Natterer committed
487 488
GimpChannel *
gimp_channel_new_mask (GimpImage *gimage,
489 490
		       gint       width,
		       gint       height)
Elliot Lee's avatar
Elliot Lee committed
491
{
Michael Natterer's avatar
Michael Natterer committed
492 493
  GimpRGB      black = { 0.0, 0.0, 0.0, 0.5 };
  GimpChannel *new_channel;
Elliot Lee's avatar
Elliot Lee committed
494

495 496
  g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);

Elliot Lee's avatar
Elliot Lee committed
497
  /*  Create the new channel  */
Michael Natterer's avatar
Michael Natterer committed
498 499
  new_channel = gimp_channel_new (gimage, width, height,
				  _("Selection Mask"), &black);
Elliot Lee's avatar
Elliot Lee committed
500 501

  /*  Set the validate procedure  */
502
  tile_manager_set_validate_proc (GIMP_DRAWABLE (new_channel)->tiles,
Michael Natterer's avatar
Michael Natterer committed
503
				  gimp_channel_validate);
Elliot Lee's avatar
Elliot Lee committed
504 505 506 507

  return new_channel;
}

508
gboolean
Michael Natterer's avatar
Michael Natterer committed
509 510 511 512 513 514 515 516 517
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
518
{
519
  gint        x3, y3, x4, y4;
Elliot Lee's avatar
Elliot Lee committed
520 521
  PixelRegion bPR;

522
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);
523 524 525 526
  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);
527

Elliot Lee's avatar
Elliot Lee committed
528 529 530 531 532 533 534 535
  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
536
      if (gimp_channel_bounds (mask, &x3, &y3, &x4, &y4))
Elliot Lee's avatar
Elliot Lee committed
537
	{
538 539
	  pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles,
			     x3, y3, (x4 - x3), (y4 - y3), FALSE);
Elliot Lee's avatar
Elliot Lee committed
540 541 542 543
	  mask->segs_out = find_mask_boundary (&bPR, &mask->num_segs_out,
					       IgnoreBounds,
					       x1, y1,
					       x2, y2);
544 545 546 547
	  x1 = MAX (x1, x3);
	  y1 = MAX (y1, y3);
	  x2 = MIN (x2, x4);
	  y2 = MIN (y2, y4);
Elliot Lee's avatar
Elliot Lee committed
548 549 550

	  if (x2 > x1 && y2 > y1)
	    {
551 552 553 554
	      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
555 556 557 558 559 560 561
	      mask->segs_in =  find_mask_boundary (&bPR, &mask->num_segs_in,
						   WithinBounds,
						   x1, y1,
						   x2, y2);
	    }
	  else
	    {
562
	      mask->segs_in     = NULL;
Elliot Lee's avatar
Elliot Lee committed
563 564 565 566 567
	      mask->num_segs_in = 0;
	    }
	}
      else
	{
568 569 570
	  mask->segs_in      = NULL;
	  mask->segs_out     = NULL;
	  mask->num_segs_in  = 0;
Elliot Lee's avatar
Elliot Lee committed
571 572
	  mask->num_segs_out = 0;
	}
573

Elliot Lee's avatar
Elliot Lee committed
574 575 576
      mask->boundary_known = TRUE;
    }

577 578 579
  *segs_in      = mask->segs_in;
  *segs_out     = mask->segs_out;
  *num_segs_in  = mask->num_segs_in;
Elliot Lee's avatar
Elliot Lee committed
580 581 582 583 584
  *num_segs_out = mask->num_segs_out;

  return TRUE;
}

585
gint
Michael Natterer's avatar
Michael Natterer committed
586 587 588
gimp_channel_value (GimpChannel *mask,
		    gint         x,
		    gint         y)
Elliot Lee's avatar
Elliot Lee committed
589 590
{
  Tile *tile;
591
  gint  val;
Elliot Lee's avatar
Elliot Lee committed
592

593 594
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), 0);

Elliot Lee's avatar
Elliot Lee committed
595 596 597 598 599 600 601 602 603 604
  /*  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
    {
605 606
      if (x < 0 || x >= GIMP_DRAWABLE (mask)->width ||
	  y < 0 || y >= GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
607 608 609
	return 0;
    }

610 611
  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));
612
  tile_release (tile, FALSE);
Elliot Lee's avatar
Elliot Lee committed
613 614 615 616

  return val;
}

617
gboolean
Michael Natterer's avatar
Michael Natterer committed
618 619 620 621 622
gimp_channel_bounds (GimpChannel *mask,
		     gint        *x1,
		     gint        *y1,
		     gint        *x2,
		     gint        *y2)
Elliot Lee's avatar
Elliot Lee committed
623
{
624 625 626 627 628 629 630
  PixelRegion  maskPR;
  guchar      *data, *data1;
  gint         x, y;
  gint         ex, ey;
  gint         tx1, tx2, ty1, ty2;
  gint         minx, maxx;
  gpointer     pr;
631

632
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);
633 634 635 636
  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);
637

Elliot Lee's avatar
Elliot Lee committed
638 639 640 641 642 643 644 645
  /*  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;

646
      return ! mask->empty;
Elliot Lee's avatar
Elliot Lee committed
647 648 649
    }

  /*  go through and calculate the bounds  */
650 651
  tx1 = GIMP_DRAWABLE (mask)->width;
  ty1 = GIMP_DRAWABLE (mask)->height;
652 653
  tx2 = 0;
  ty2 = 0;
Elliot Lee's avatar
Elliot Lee committed
654

655 656 657 658
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (mask)->width,
		     GIMP_DRAWABLE (mask)->height, FALSE);
659

660 661 662
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
663
    {
664
      data1 = data = maskPR.data;
Elliot Lee's avatar
Elliot Lee committed
665 666
      ex = maskPR.x + maskPR.w;
      ey = maskPR.y + maskPR.h;
667 668
      /* only check the pixels if this tile is not fully within the currently
	 computed bounds */
669 670
      if (maskPR.x < tx1 || ex > tx2 ||
	  maskPR.y < ty1 || ey > ty2)
671
        {
672 673 674
	  /* 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])
675 676 677 678 679 680 681 682 683 684
            {
              if (maskPR.x < tx1)
                tx1 = maskPR.x;
              if (ex > tx2)
                tx2 = ex;
              if (maskPR.y < ty1)
                ty1 = maskPR.y;
              if (ey > ty2)
                ty2 = ey;
            }
685
	  else
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
            {
              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
713 714
    }

715 716
  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
717

718
  if (tx1 == GIMP_DRAWABLE (mask)->width && ty1 == GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
719 720
    {
      mask->empty = TRUE;
721 722 723 724
      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
725 726 727 728
    }
  else
    {
      mask->empty = FALSE;
729 730 731 732
      mask->x1    = tx1;
      mask->y1    = ty1;
      mask->x2    = tx2;
      mask->y2    = ty2;
Elliot Lee's avatar
Elliot Lee committed
733
    }
734

Elliot Lee's avatar
Elliot Lee committed
735 736
  mask->bounds_known = TRUE;

737 738 739 740 741
  *x1 = tx1;
  *x2 = tx2;
  *y1 = ty1;
  *y2 = ty2;

742
  return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
743 744
}

745
gboolean
Michael Natterer's avatar
Michael Natterer committed
746
gimp_channel_is_empty (GimpChannel *mask)
Elliot Lee's avatar
Elliot Lee committed
747
{
748 749 750 751
  PixelRegion  maskPR;
  guchar      *data;
  gint         x, y;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
752

753 754
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);

Elliot Lee's avatar
Elliot Lee committed
755 756 757
  if (mask->bounds_known)
    return mask->empty;

758 759 760 761
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (mask)->width,
		     GIMP_DRAWABLE (mask)->height, FALSE);
762

763 764 765
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
766 767 768
    {
      /*  check if any pixel in the mask is non-zero  */
      data = maskPR.data;
769

Elliot Lee's avatar
Elliot Lee committed
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
      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);

785 786 787 788 789 790
  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
791
  mask->boundary_known = TRUE;
792 793 794 795
  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
796 797 798 799 800

  return TRUE;
}

void
Michael Natterer's avatar
Michael Natterer committed
801 802 803
gimp_channel_add_segment (GimpChannel *mask,
			  gint         x,
			  gint         y,
804 805
			  gint         width,
			  gint         value)
Elliot Lee's avatar
Elliot Lee committed
806
{
807 808 809 810 811
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
812

813 814
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

Elliot Lee's avatar
Elliot Lee committed
815 816
  /*  check horizontal extents...  */
  x2 = x + width;
817 818
  x2 = CLAMP (x2, 0, GIMP_DRAWABLE (mask)->width);
  x  = CLAMP (x,  0, GIMP_DRAWABLE (mask)->width);
Elliot Lee's avatar
Elliot Lee committed
819
  width = x2 - x;
820 821
  if (!width)
    return;
Elliot Lee's avatar
Elliot Lee committed
822

823
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
824 825
    return;

826 827
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, width, 1, TRUE);
828

829 830 831
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
832 833 834 835 836 837 838 839 840 841 842 843 844 845
    {
      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
846 847 848
gimp_channel_sub_segment (GimpChannel *mask,
			  gint         x,
			  gint         y,
849 850
			  gint         width,
			  gint         value)
Elliot Lee's avatar
Elliot Lee committed
851
{
852 853 854 855 856
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
857

858 859
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

Elliot Lee's avatar
Elliot Lee committed
860 861
  /*  check horizontal extents...  */
  x2 = x + width;
862 863
  x2 = CLAMP (x2, 0, GIMP_DRAWABLE (mask)->width);
  x =  CLAMP (x,  0, GIMP_DRAWABLE (mask)->width);
Elliot Lee's avatar
Elliot Lee committed
864
  width = x2 - x;
865 866 867

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

869
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
870 871
    return;

872 873 874
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
                     x, y, width, 1, TRUE);

875 876 877
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
878 879 880 881 882 883 884 885 886 887 888 889 890 891
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data - value;
	  if (val < 0)
	    val = 0;
	  *data++ = val;
	}
    }
}

void
892 893 894 895 896 897
gimp_channel_combine_rect (GimpChannel    *mask,
			   GimpChannelOps  op,
			   gint            x,
			   gint            y,
			   gint            w,
			   gint            h)
Elliot Lee's avatar
Elliot Lee committed
898
{
899
  gint        x2, y2;
Elliot Lee's avatar
Elliot Lee committed
900
  PixelRegion maskPR;
901
  guchar      color;
902

903 904
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

905 906
  y2 = y + h;
  x2 = x + w;
Elliot Lee's avatar
Elliot Lee committed
907

908 909 910 911
  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
912

913
  if (x2 - x <= 0 || y2 - y <= 0)
Elliot Lee's avatar
Elliot Lee committed
914 915
    return;

916 917
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, x2 - x, y2 - y, TRUE);
918 919

  if (op == GIMP_CHANNEL_OP_ADD || op == GIMP_CHANNEL_OP_REPLACE)
920
    color = OPAQUE_OPACITY;
921
  else
922
    color = TRANSPARENT_OPACITY;
923

924
  color_region (&maskPR, &color);
Elliot Lee's avatar
Elliot Lee committed
925 926

  /*  Determine new boundary  */
927
  if (mask->bounds_known && (op == GIMP_CHANNEL_OP_ADD) && !mask->empty)
Elliot Lee's avatar
Elliot Lee committed
928 929 930 931 932 933 934 935 936 937
    {
      if (x < mask->x1)
	mask->x1 = x;
      if (y < mask->y1)
	mask->y1 = y;
      if ((x + w) > mask->x2)
	mask->x2 = (x + w);
      if ((y + h) > mask->y2)
	mask->y2 = (y + h);
    }
938
  else if (op == GIMP_CHANNEL_OP_REPLACE || mask->empty)
Elliot Lee's avatar
Elliot Lee committed
939 940
    {
      mask->empty = FALSE;
941 942 943 944
      mask->x1    = x;
      mask->y1    = y;
      mask->x2    = x + w;
      mask->y2    = y + h;
Elliot Lee's avatar
Elliot Lee committed
945 946 947 948
    }
  else
    mask->bounds_known = FALSE;

949 950 951 952
  mask->x1 = CLAMP (mask->x1, 0, GIMP_DRAWABLE (mask)->width);
  mask->y1 = CLAMP (mask->y1, 0, GIMP_DRAWABLE (mask)->height);
  mask->x2 = CLAMP (mask->x2, 0, GIMP_DRAWABLE (mask)->width);
  mask->y2 = CLAMP (mask->y2, 0, GIMP_DRAWABLE (mask)->height);
Elliot Lee's avatar
Elliot Lee committed
953 954 955
}

void
956 957 958 959 960 961 962
gimp_channel_combine_ellipse (GimpChannel    *mask,
			      GimpChannelOps  op,
			      gint            x,
			      gint            y,
			      gint            w,
			      gint            h,
			      gboolean        antialias)
Elliot Lee's avatar
Elliot Lee committed
963
{
964 965 966
  gint   i, j;
  gint   x0, x1, x2;
  gint   val, last;
967 968 969 970 971 972 973 974
  gfloat a_sqr, b_sqr, aob_sqr;
  gfloat w_sqr, h_sqr;
  gfloat y_sqr;
  gfloat t0, t1;
  gfloat r;
  gfloat cx, cy;
  gfloat rad;
  gfloat dist;
Elliot Lee's avatar
Elliot Lee committed
975

976 977
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

Elliot Lee's avatar
Elliot Lee committed
978 979 980 981 982 983 984 985 986 987 988 989
  if (!w || !h)
    return;

  a_sqr = (w * w / 4.0);
  b_sqr = (h * h / 4.0);
  aob_sqr = a_sqr / b_sqr;

  cx = x + w / 2.0;
  cy = y + h / 2.0;

  for (i = y; i < (y + h); i++)
    {
990
      if (i >= 0 && i < GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
991 992
	{
	  /*  Non-antialiased code  */
993
	  if (!antialias)
Elliot Lee's avatar
Elliot Lee committed
994 995 996 997 998 999 1000 1001
	    {
	      y_sqr = (i + 0.5 - cy) * (i + 0.5 - cy);
	      rad = sqrt (a_sqr - a_sqr * y_sqr / (double) b_sqr);
	      x1 = ROUND (cx - rad);
	      x2 = ROUND (cx + rad);

	      switch (op)
		{