gimpchannel.c 41.7 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/* The GIMP -- an image manipulation program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
16
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
17
 */
18

19 20
#include "config.h"

Elliot Lee's avatar
Elliot Lee committed
21 22
#include <stdlib.h>
#include <string.h>
23

24
#include <glib-object.h>
Sven Neumann's avatar
Sven Neumann committed
25

26
#include "libgimpcolor/gimpcolor.h"
27 28
#include "libgimpmath/gimpmath.h"

Michael Natterer's avatar
Michael Natterer committed
29
#include "core-types.h"
Sven Neumann's avatar
Sven Neumann committed
30

Michael Natterer's avatar
Michael Natterer committed
31 32
#include "base/boundary.h"
#include "base/gimplut.h"
33
#include "base/lut-funcs.h"
Michael Natterer's avatar
Michael Natterer committed
34 35 36 37 38
#include "base/pixel-processor.h"
#include "base/pixel-region.h"
#include "base/tile.h"
#include "base/tile-manager.h"

39 40
#include "paint-funcs/paint-funcs.h"

41
#include "gimpimage.h"
42 43
#include "gimpchannel.h"
#include "gimplayer.h"
44
#include "gimpparasitelist.h"
Michael Natterer's avatar
Michael Natterer committed
45

46
#include "undo.h"
47

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


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

54 55 56
static void    gimp_channel_finalize    (GObject          *object);

static gsize   gimp_channel_get_memsize (GimpObject       *object);
57

58 59 60 61
static void    gimp_channel_push_undo   (GimpChannel      *mask);
static void    gimp_channel_validate    (TileManager      *tm,
                                         Tile             *tile);

62

63
static GimpDrawableClass * parent_class = NULL;
64

65

66
GType
67
gimp_channel_get_type (void)
68
{
69
  static GType channel_type = 0;
70

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

86 87 88
      channel_type = g_type_register_static (GIMP_TYPE_DRAWABLE,
					     "GimpChannel",
					     &channel_info, 0);
89 90 91 92 93 94
    }

  return channel_type;
}

static void
95
gimp_channel_class_init (GimpChannelClass *klass)
96
{
97 98
  GObjectClass    *object_class;
  GimpObjectClass *gimp_object_class;
99

100 101
  object_class      = G_OBJECT_CLASS (klass);
  gimp_object_class = GIMP_OBJECT_CLASS (klass);
102

103
  parent_class = g_type_class_peek_parent (klass);
104

105 106 107
  object_class->finalize         = gimp_channel_finalize;

  gimp_object_class->get_memsize = gimp_channel_get_memsize;
108 109 110 111 112
}

static void
gimp_channel_init (GimpChannel *channel)
{
113
  gimp_rgba_set (&channel->color, 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE);
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128

  channel->show_masked = FALSE;

  /*  Selection mask variables  */
  channel->boundary_known = TRUE;
  channel->segs_in        = NULL;
  channel->segs_out       = NULL;
  channel->num_segs_in    = 0;
  channel->num_segs_out   = 0;
  channel->empty          = TRUE;
  channel->bounds_known   = TRUE;
  channel->x1             = 0;
  channel->y1             = 0;
  channel->x2             = 0;
  channel->y2             = 0;
129
}
Elliot Lee's avatar
Elliot Lee committed
130

131
static void
132
gimp_channel_finalize (GObject *object)
133 134 135 136 137 138 139 140
{
  GimpChannel *channel;

  g_return_if_fail (GIMP_IS_CHANNEL (object));

  channel = GIMP_CHANNEL (object);

  if (channel->segs_in)
141 142 143 144 145
    {
      g_free (channel->segs_in);
      channel->segs_in = NULL;
    }

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

152
  G_OBJECT_CLASS (parent_class)->finalize (object);
153
}
Elliot Lee's avatar
Elliot Lee committed
154

155 156 157 158 159 160 161 162 163 164 165 166 167 168
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);
}

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
static void
gimp_channel_push_undo (GimpChannel *mask)
{
  GimpImage *gimage;

  gimage = gimp_item_get_image (GIMP_ITEM (mask));

  undo_push_mask (gimage, mask);

  mask->boundary_known = FALSE;

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

Elliot Lee's avatar
Elliot Lee committed
184
static void
Michael Natterer's avatar
Michael Natterer committed
185 186
gimp_channel_validate (TileManager *tm,
		       Tile        *tile)
Elliot Lee's avatar
Elliot Lee committed
187 188
{
  /*  Set the contents of the tile to empty  */
scott's avatar
scott committed
189
  memset (tile_data_pointer (tile, 0, 0), 
190
	  TRANSPARENT_OPACITY, tile_size (tile));
Elliot Lee's avatar
Elliot Lee committed
191 192
}

193 194 195

/*  public functions  */

Michael Natterer's avatar
Michael Natterer committed
196 197
GimpChannel *
gimp_channel_new (GimpImage     *gimage,
198 199
		  gint           width,
		  gint           height,
Michael Natterer's avatar
Michael Natterer committed
200 201
		  const gchar   *name,
		  const GimpRGB *color)
Elliot Lee's avatar
Elliot Lee committed
202
{
Michael Natterer's avatar
Michael Natterer committed
203
  GimpChannel *channel;
204 205

  g_return_val_if_fail (color != NULL, NULL);
Elliot Lee's avatar
Elliot Lee committed
206

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

Michael Natterer's avatar
Michael Natterer committed
209
  gimp_drawable_configure (GIMP_DRAWABLE (channel),
210
			   gimage, width, height, GIMP_GRAY_IMAGE, name);
Elliot Lee's avatar
Elliot Lee committed
211 212

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

215
  channel->show_masked = TRUE;
Elliot Lee's avatar
Elliot Lee committed
216 217

  /*  selection mask variables  */
218 219
  channel->x2          = width;
  channel->y2          = height;
Elliot Lee's avatar
Elliot Lee committed
220 221 222 223

  return channel;
}

Michael Natterer's avatar
Michael Natterer committed
224
GimpChannel *
225
gimp_channel_copy (const GimpChannel *channel,
226
                   GType              new_type,
227 228 229 230 231 232
                   gboolean           dummy) /*  the dummy is for symmetry with
                                              *  gimp_layer_copy() because
                                              *  both functions are used as
                                              *  function pointers in
                                              *  GimpDrawableListView --Mitch
                                              */
Elliot Lee's avatar
Elliot Lee committed
233
{
Michael Natterer's avatar
Michael Natterer committed
234
  GimpChannel *new_channel;
235 236 237 238 239 240 241 242 243 244

  g_return_val_if_fail (GIMP_IS_CHANNEL (channel), NULL);
  g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_CHANNEL), NULL);

  new_channel = GIMP_CHANNEL (gimp_drawable_copy (GIMP_DRAWABLE (channel),
                                                  new_type,
                                                  FALSE));

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

Elliot Lee's avatar
Elliot Lee committed
246 247
  new_channel->show_masked = channel->show_masked;

248 249 250
  /*  selection mask variables  */
  new_channel->x2          = gimp_drawable_width (GIMP_DRAWABLE (new_channel));
  new_channel->y2          = gimp_drawable_height (GIMP_DRAWABLE (new_channel));
Elliot Lee's avatar
Elliot Lee committed
251 252 253 254

  return new_channel;
}

255
void 
Michael Natterer's avatar
Michael Natterer committed
256 257
gimp_channel_set_color (GimpChannel   *channel,
			const GimpRGB *color)
258
{
259 260
  g_return_if_fail (GIMP_IS_CHANNEL (channel));
  g_return_if_fail (color != NULL);
261

Michael Natterer's avatar
Michael Natterer committed
262 263 264 265 266 267 268 269 270
  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);
    }
271
}
272

273 274 275
void
gimp_channel_get_color (const GimpChannel *channel,
                        GimpRGB           *color)
276
{
277 278
  g_return_if_fail (GIMP_IS_CHANNEL (channel));
  g_return_if_fail (color != NULL);
279

280
  *color = channel->color;
281
}
282

283
gdouble
Michael Natterer's avatar
Michael Natterer committed
284
gimp_channel_get_opacity (const GimpChannel *channel)
285 286 287
{
  g_return_val_if_fail (GIMP_IS_CHANNEL (channel), 0);

288
  return channel->color.a;
289 290
}

291
void
Michael Natterer's avatar
Michael Natterer committed
292
gimp_channel_set_opacity (GimpChannel *channel,
293
			  gdouble      opacity)
294
{
295 296
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

297
  opacity = CLAMP (opacity, GIMP_OPACITY_TRANSPARENT, GIMP_OPACITY_OPAQUE);
298

299
  channel->color.a = opacity;
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
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
327
void
328 329 330 331
gimp_channel_scale (GimpChannel           *channel,
		    gint                   new_width,
		    gint                   new_height,
                    GimpInterpolationType  interpolation_type)
Elliot Lee's avatar
Elliot Lee committed
332
{
333
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
334 335
  TileManager *new_tiles;

336 337
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

Elliot Lee's avatar
Elliot Lee committed
338 339 340 341
  if (new_width == 0 || new_height == 0)
    return;

  /*  Update the old channel position  */
Michael Natterer's avatar
Michael Natterer committed
342 343 344 345
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
346

347 348 349
  /*  Allocate the new channel  */
  new_tiles = tile_manager_new (new_width, new_height, 1);

Elliot Lee's avatar
Elliot Lee committed
350
  /*  Configure the pixel regions  */
351 352 353
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (channel)->width,
354 355
		     GIMP_DRAWABLE (channel)->height,
                     FALSE);
Elliot Lee's avatar
Elliot Lee committed
356

357 358 359 360
  pixel_region_init (&destPR, new_tiles,
                     0, 0,
                     new_width, new_height,
                     TRUE);
Elliot Lee's avatar
Elliot Lee committed
361

362
  /*  Sclae the channel  */
363
  scale_region (&srcPR, &destPR, interpolation_type);
Elliot Lee's avatar
Elliot Lee committed
364 365

  /*  Push the channel on the undo stack  */
366
  undo_push_channel_mod (gimp_item_get_image (GIMP_ITEM (channel)), channel);
Elliot Lee's avatar
Elliot Lee committed
367 368

  /*  Configure the new channel  */
369 370
  GIMP_DRAWABLE (channel)->tiles  = new_tiles;
  GIMP_DRAWABLE (channel)->width  = new_width;
371
  GIMP_DRAWABLE (channel)->height = new_height;
Elliot Lee's avatar
Elliot Lee committed
372 373 374 375 376

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

  /*  Update the new channel position  */
Michael Natterer's avatar
Michael Natterer committed
377 378 379 380
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
381 382 383
}

void
Michael Natterer's avatar
Michael Natterer committed
384
gimp_channel_resize (GimpChannel *channel,
385 386
		     gint         new_width,
		     gint         new_height,
Michael Natterer's avatar
Michael Natterer committed
387 388
		     gint         offx,
		     gint         offy)
Elliot Lee's avatar
Elliot Lee committed
389
{
390
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
391
  TileManager *new_tiles;
392 393 394 395
  guchar       bg = 0;
  gint         clear;
  gint         w, h;
  gint         x1, y1, x2, y2;
Elliot Lee's avatar
Elliot Lee committed
396

397 398
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

399
  if (new_width == 0 || new_height == 0)
Elliot Lee's avatar
Elliot Lee committed
400 401
    return;

402 403 404 405
  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);
Elliot Lee's avatar
Elliot Lee committed
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
  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
432 433 434 435
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
436 437

  /*  Configure the pixel regions  */
438 439
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
		     x1, y1, w, h, FALSE);
Elliot Lee's avatar
Elliot Lee committed
440 441

  /*  Determine whether the new channel needs to be initially cleared  */
442 443
  if ((new_width  > GIMP_DRAWABLE (channel)->width) ||
      (new_height > GIMP_DRAWABLE (channel)->height) ||
Elliot Lee's avatar
Elliot Lee committed
444 445 446 447 448 449 450 451 452 453 454
      (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)
    {
455 456 457 458 459
      pixel_region_init (&destPR, new_tiles,
                         0, 0,
                         new_width, new_height,
                         TRUE);

Elliot Lee's avatar
Elliot Lee committed
460 461 462 463 464 465 466 467 468
      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  */
469
  undo_push_channel_mod (gimp_item_get_image (GIMP_ITEM (channel)), channel);
Elliot Lee's avatar
Elliot Lee committed
470 471

  /*  Configure the new channel  */
472 473 474
  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
475 476 477 478 479

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

  /*  update the new channel area  */
Michael Natterer's avatar
Michael Natterer committed
480 481 482 483
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
484 485
}

486

487 488 489
/******************************/
/*  selection mask functions  */
/******************************/
Elliot Lee's avatar
Elliot Lee committed
490

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

499 500
  g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);

Elliot Lee's avatar
Elliot Lee committed
501
  /*  Create the new channel  */
Michael Natterer's avatar
Michael Natterer committed
502 503
  new_channel = gimp_channel_new (gimage, width, height,
				  _("Selection Mask"), &black);
Elliot Lee's avatar
Elliot Lee committed
504 505

  /*  Set the validate procedure  */
506
  tile_manager_set_validate_proc (GIMP_DRAWABLE (new_channel)->tiles,
Michael Natterer's avatar
Michael Natterer committed
507
				  gimp_channel_validate);
Elliot Lee's avatar
Elliot Lee committed
508 509 510 511

  return new_channel;
}

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

526 527
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);

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 633
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);

Elliot Lee's avatar
Elliot Lee committed
634 635 636 637 638 639 640 641
  /*  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;

642
      return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
643 644 645
    }

  /*  go through and calculate the bounds  */
646 647
  tx1 = GIMP_DRAWABLE (mask)->width;
  ty1 = GIMP_DRAWABLE (mask)->height;
648 649
  tx2 = 0;
  ty2 = 0;
Elliot Lee's avatar
Elliot Lee committed
650

651 652 653 654 655 656 657
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (mask)->width,
		     GIMP_DRAWABLE (mask)->height, FALSE);
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
658
    {
659
      data1 = data = maskPR.data;
Elliot Lee's avatar
Elliot Lee committed
660 661
      ex = maskPR.x + maskPR.w;
      ey = maskPR.y + maskPR.h;
662 663
      /* only check the pixels if this tile is not fully within the currently
	 computed bounds */
664 665
      if (maskPR.x < tx1 || ex > tx2 ||
	  maskPR.y < ty1 || ey > ty2)
666
        {
667 668 669 670 671 672 673 674 675 676 677 678 679 680
	  /* 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])
	  {
	    if (maskPR.x < tx1)
	      tx1 = maskPR.x;
	    if (ex > tx2)
	      tx2 = ex;
	    if (maskPR.y < ty1)
	      ty1 = maskPR.y;
	    if (ey > ty2)
	      ty2 = ey;
	  }
	  else
681
	    for (y = maskPR.y; y < ey; y++, data1 += maskPR.rowstride)
Elliot Lee's avatar
Elliot Lee committed
682
	    {
683
	      for (x = maskPR.x, data = data1; x < ex; x++, data++)
684
		if (*data)
685 686 687 688 689 690 691 692 693 694 695 696 697 698 699
		{
		  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
700 701 702 703
	    }
	}
    }

704 705
  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
706

707
  if (tx1 == GIMP_DRAWABLE (mask)->width && ty1 == GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
708 709
    {
      mask->empty = TRUE;
710 711 712 713
      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
714 715 716 717
    }
  else
    {
      mask->empty = FALSE;
718 719 720 721
      mask->x1    = tx1;
      mask->y1    = ty1;
      mask->x2    = tx2;
      mask->y2    = ty2;
Elliot Lee's avatar
Elliot Lee committed
722
    }
723

Elliot Lee's avatar
Elliot Lee committed
724 725
  mask->bounds_known = TRUE;

726 727 728 729 730
  *x1 = tx1;
  *x2 = tx2;
  *y1 = ty1;
  *y2 = ty2;

731
  return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
732 733
}

734
gboolean
Michael Natterer's avatar
Michael Natterer committed
735
gimp_channel_is_empty (GimpChannel *mask)
Elliot Lee's avatar
Elliot Lee committed
736
{
737 738 739 740
  PixelRegion  maskPR;
  guchar      *data;
  gint         x, y;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
741

742 743
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);

Elliot Lee's avatar
Elliot Lee committed
744 745 746
  if (mask->bounds_known)
    return mask->empty;

747 748 749 750
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (mask)->width,
		     GIMP_DRAWABLE (mask)->height, FALSE);
751

752 753 754
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
    {
      /*  check if any pixel in the mask is non-zero  */
      data = maskPR.data;
      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);

773 774 775 776 777 778
  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
779
  mask->boundary_known = TRUE;
780 781 782 783
  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
784 785 786 787 788

  return TRUE;
}

void
Michael Natterer's avatar
Michael Natterer committed
789 790 791
gimp_channel_add_segment (GimpChannel *mask,
			  gint         x,
			  gint         y,
792 793
			  gint         width,
			  gint         value)
Elliot Lee's avatar
Elliot Lee committed
794
{
795 796 797 798 799
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
800

801 802
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

Elliot Lee's avatar
Elliot Lee committed
803 804 805
  /*  check horizontal extents...  */
  x2 = x + width;
  if (x2 < 0) x2 = 0;
806
  if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
807
  if (x < 0) x = 0;
808
  if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
809 810 811
  width = x2 - x;
  if (!width) return;

812
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
813 814
    return;

815 816 817 818 819
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, width, 1, TRUE);
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
820 821 822 823 824 825 826 827 828 829 830 831 832 833
    {
      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
834 835 836
gimp_channel_sub_segment (GimpChannel *mask,
			  gint         x,
			  gint         y,
837 838
			  gint         width,
			  gint         value)
Elliot Lee's avatar
Elliot Lee committed
839
{
840 841 842 843 844
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
845

846 847
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

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

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

860 861 862 863
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles, x, y, width, 1, TRUE);
  for (pr = pixel_regions_register (1, &maskPR);
       pr != NULL;
       pr = pixel_regions_process (pr))
Elliot Lee's avatar
Elliot Lee committed
864 865 866 867 868 869 870 871 872 873 874 875 876 877
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data - value;
	  if (val < 0)
	    val = 0;
	  *data++ = val;
	}
    }
}

void
878 879 880 881 882 883
gimp_channel_combine_rect (GimpChannel    *mask,
			   GimpChannelOps  op,
			   gint            x,
			   gint            y,
			   gint            w,
			   gint            h)
Elliot Lee's avatar
Elliot Lee committed
884
{
885
  gint        x2, y2;
Elliot Lee's avatar
Elliot Lee committed
886
  PixelRegion maskPR;
887
  guchar      color;
888

889 890
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

891 892
  y2 = y + h;
  x2 = x + w;
Elliot Lee's avatar
Elliot Lee committed
893

894 895 896 897
  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
898

899
  if (x2 - x <= 0 || y2 - y <= 0)
Elliot Lee's avatar
Elliot Lee committed
900 901
    return;

902 903
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, x2 - x, y2 - y, TRUE);
904 905

  if (op == GIMP_CHANNEL_OP_ADD || op == GIMP_CHANNEL_OP_REPLACE)
906 907 908
    color = 255;
  else
    color = 0;
909

910
  color_region (&maskPR, &color);
Elliot Lee's avatar
Elliot Lee committed
911 912

  /*  Determine new boundary  */
913
  if (mask->bounds_known && (op == GIMP_CHANNEL_OP_ADD) && !mask->empty)
Elliot Lee's avatar
Elliot Lee committed
914 915 916 917 918 919 920 921 922 923
    {
      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);
    }
924
  else if (op == GIMP_CHANNEL_OP_REPLACE || mask->empty)
Elliot Lee's avatar
Elliot Lee committed
925 926 927 928 929 930 931 932 933 934
    {
      mask->empty = FALSE;
      mask->x1 = x;
      mask->y1 = y;
      mask->x2 = x + w;
      mask->y2 = y + h;
    }
  else
    mask->bounds_known = FALSE;

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

void
942 943 944 945 946 947 948
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
949
{
950 951 952
  gint   i, j;
  gint   x0, x1, x2;
  gint   val, last;
953 954 955 956 957 958 959 960
  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
961

962 963
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

Elliot Lee's avatar
Elliot Lee committed
964 965 966 967 968 969 970 971 972 973 974 975
  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++)
    {
976
      if (i >= 0 && i < GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
977 978
	{
	  /*  Non-antialiased code  */
979
	  if (!antialias)