gimpchannel.c 40.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 39
#include "base/pixel-processor.h"
#include "base/pixel-region.h"
#include "base/temp-buf.h"
#include "base/tile.h"
#include "base/tile-manager.h"

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

42
#include "gimpimage.h"
43
#include "gimpimage-mask.h"
44 45
#include "gimpchannel.h"
#include "gimplayer.h"
46
#include "gimpparasitelist.h"
Michael Natterer's avatar
Michael Natterer committed
47

48
#include "undo.h"
49

50 51 52
#include "libgimp/gimpintl.h"


53 54
static void   gimp_channel_class_init (GimpChannelClass *klass);
static void   gimp_channel_init       (GimpChannel      *channel);
55 56

static void   gimp_channel_finalize   (GObject          *object);
57

58

59
static GimpDrawableClass * parent_class = NULL;
60

61

62
GType
63
gimp_channel_get_type (void)
64
{
65
  static GType channel_type = 0;
66

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

82 83 84
      channel_type = g_type_register_static (GIMP_TYPE_DRAWABLE,
					     "GimpChannel",
					     &channel_info, 0);
85 86 87 88 89 90
    }

  return channel_type;
}

static void
91
gimp_channel_class_init (GimpChannelClass *klass)
92
{
93
  GObjectClass *object_class;
94

95
  object_class = G_OBJECT_CLASS (klass);
96

97
  parent_class = g_type_class_peek_parent (klass);
98

99
  object_class->finalize = gimp_channel_finalize;
100 101 102 103 104
}

static void
gimp_channel_init (GimpChannel *channel)
{
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
  gimp_rgba_set (&channel->color, 0.0, 0.0, 0.0, 1.0);

  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;
121
  
122
}
Elliot Lee's avatar
Elliot Lee committed
123

124
static void
125
gimp_channel_finalize (GObject *object)
126 127 128 129 130 131 132 133
{
  GimpChannel *channel;

  g_return_if_fail (GIMP_IS_CHANNEL (object));

  channel = GIMP_CHANNEL (object);

  if (channel->segs_in)
134 135 136 137 138
    {
      g_free (channel->segs_in);
      channel->segs_in = NULL;
    }

139
  if (channel->segs_out)
140 141 142 143
    {
      g_free (channel->segs_out);
      channel->segs_out = NULL;
    }
144

145
  G_OBJECT_CLASS (parent_class)->finalize (object);
146
}
Elliot Lee's avatar
Elliot Lee committed
147 148

static void
Michael Natterer's avatar
Michael Natterer committed
149 150
gimp_channel_validate (TileManager *tm,
		       Tile        *tile)
Elliot Lee's avatar
Elliot Lee committed
151 152
{
  /*  Set the contents of the tile to empty  */
scott's avatar
scott committed
153
  memset (tile_data_pointer (tile, 0, 0), 
154
	  TRANSPARENT_OPACITY, tile_size (tile));
Elliot Lee's avatar
Elliot Lee committed
155 156
}

Michael Natterer's avatar
Michael Natterer committed
157 158
GimpChannel *
gimp_channel_new (GimpImage     *gimage,
159 160
		  gint           width,
		  gint           height,
Michael Natterer's avatar
Michael Natterer committed
161 162
		  const gchar   *name,
		  const GimpRGB *color)
Elliot Lee's avatar
Elliot Lee committed
163
{
Michael Natterer's avatar
Michael Natterer committed
164
  GimpChannel *channel;
165 166

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

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

Michael Natterer's avatar
Michael Natterer committed
170
  gimp_drawable_configure (GIMP_DRAWABLE (channel),
171
			   gimage, width, height, GIMP_GRAY_IMAGE, name);
Elliot Lee's avatar
Elliot Lee committed
172 173

  /*  set the channel color and opacity  */
174
  channel->color = *color;
175

176
  channel->show_masked = TRUE;
Elliot Lee's avatar
Elliot Lee committed
177 178

  /*  selection mask variables  */
179 180
  channel->x2 = width;
  channel->y2 = height;
Elliot Lee's avatar
Elliot Lee committed
181 182 183 184

  return channel;
}

Michael Natterer's avatar
Michael Natterer committed
185
GimpChannel *
186 187 188 189 190 191 192
gimp_channel_copy (const GimpChannel *channel,
                   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
193
{
194
  gchar       *channel_name;
Michael Natterer's avatar
Michael Natterer committed
195
  GimpChannel *new_channel;
196 197 198 199 200
  PixelRegion  srcPR, destPR;
  gchar       *ext;
  gint         number;
  const gchar *name;
  gint         len;
Elliot Lee's avatar
Elliot Lee committed
201 202

  /*  formulate the new channel name  */
203 204
  name = gimp_object_get_name (GIMP_OBJECT (channel));

205
  ext = strrchr (name, '#');
206
  len = strlen (_("copy"));
207 208 209 210
  if ((strlen (name) >= len &&
       strcmp (&name[strlen (name) - len], _("copy")) == 0) ||
      (ext && (number = atoi (ext + 1)) > 0 && 
       ((int)(log10 (number) + 1)) == strlen (ext + 1)))
211 212
    /* don't have redundant "copy"s */
    channel_name = g_strdup (name);
213
  else
214
    channel_name = g_strdup_printf (_("%s copy"), name);
Elliot Lee's avatar
Elliot Lee committed
215 216

  /*  allocate a new channel object  */
Michael Natterer's avatar
Michael Natterer committed
217 218 219 220 221
  new_channel = gimp_channel_new (GIMP_DRAWABLE (channel)->gimage, 
				  GIMP_DRAWABLE (channel)->width, 
				  GIMP_DRAWABLE (channel)->height, 
				  channel_name,
				  &channel->color);
222

223
  GIMP_DRAWABLE (new_channel)->visible = GIMP_DRAWABLE (channel)->visible;
224

Elliot Lee's avatar
Elliot Lee committed
225 226 227
  new_channel->show_masked = channel->show_masked;

  /*  copy the contents across channels  */
228 229 230 231 232 233 234
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles, 0, 0, 
		     GIMP_DRAWABLE (channel)->width, 
		     GIMP_DRAWABLE (channel)->height, FALSE);
  pixel_region_init (&destPR, GIMP_DRAWABLE (new_channel)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (channel)->width,
		     GIMP_DRAWABLE (channel)->height, TRUE);
Elliot Lee's avatar
Elliot Lee committed
235 236
  copy_region (&srcPR, &destPR);

237
  /* copy the parasites */
238
  GIMP_DRAWABLE (new_channel)->parasites =
239
    gimp_parasite_list_copy (GIMP_DRAWABLE (channel)->parasites);
240

Elliot Lee's avatar
Elliot Lee committed
241 242 243 244 245 246
  /*  free up the channel_name memory  */
  g_free (channel_name);

  return new_channel;
}

247
void 
Michael Natterer's avatar
Michael Natterer committed
248 249
gimp_channel_set_color (GimpChannel   *channel,
			const GimpRGB *color)
250
{
251 252
  g_return_if_fail (GIMP_IS_CHANNEL (channel));
  g_return_if_fail (color != NULL);
253

Michael Natterer's avatar
Michael Natterer committed
254 255 256 257 258 259 260 261 262
  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);
    }
263
}
264

265
const GimpRGB *
Michael Natterer's avatar
Michael Natterer committed
266
gimp_channel_get_color (const GimpChannel *channel)
267
{
268 269 270
  g_return_val_if_fail (GIMP_IS_CHANNEL (channel), NULL);

  return &channel->color;
271
}
272

273
gint
Michael Natterer's avatar
Michael Natterer committed
274
gimp_channel_get_opacity (const GimpChannel *channel)
275 276 277
{
  g_return_val_if_fail (GIMP_IS_CHANNEL (channel), 0);

278
  return (gint) (channel->color.a * 100.999);
279 280
}

281
void 
Michael Natterer's avatar
Michael Natterer committed
282
gimp_channel_set_opacity (GimpChannel *channel,
283
			  gint         opacity)
284
{
285 286 287 288 289
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

  opacity = CLAMP (opacity, 0, 100);

  channel->color.a = opacity / 100.0;
290 291
}

Elliot Lee's avatar
Elliot Lee committed
292
void
Michael Natterer's avatar
Michael Natterer committed
293
gimp_channel_scale (GimpChannel *channel,
294 295
		    gint         new_width,
		    gint         new_height)
Elliot Lee's avatar
Elliot Lee committed
296
{
297
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
298 299
  TileManager *new_tiles;

300 301
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

Elliot Lee's avatar
Elliot Lee committed
302 303 304 305
  if (new_width == 0 || new_height == 0)
    return;

  /*  Update the old channel position  */
Michael Natterer's avatar
Michael Natterer committed
306 307 308 309
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
310 311

  /*  Configure the pixel regions  */
312 313 314 315
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
		     0, 0,
		     GIMP_DRAWABLE (channel)->width,
		     GIMP_DRAWABLE (channel)->height, FALSE);
Elliot Lee's avatar
Elliot Lee committed
316 317 318 319 320 321 322 323 324

  /*  Allocate the new channel, configure dest region  */
  new_tiles = tile_manager_new (new_width, new_height, 1);
  pixel_region_init (&destPR, new_tiles, 0, 0, new_width, new_height, TRUE);

  /*  Add an alpha channel  */
  scale_region (&srcPR, &destPR);

  /*  Push the channel on the undo stack  */
325
  undo_push_channel_mod (GIMP_DRAWABLE (channel)->gimage, channel);
Elliot Lee's avatar
Elliot Lee committed
326 327

  /*  Configure the new channel  */
328 329 330
  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
331 332 333 334 335

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

  /*  Update the new channel position  */
Michael Natterer's avatar
Michael Natterer committed
336 337 338 339
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
340 341 342
}

void
Michael Natterer's avatar
Michael Natterer committed
343
gimp_channel_resize (GimpChannel *channel,
344 345
		     gint         new_width,
		     gint         new_height,
Michael Natterer's avatar
Michael Natterer committed
346 347
		     gint         offx,
		     gint         offy)
Elliot Lee's avatar
Elliot Lee committed
348
{
349
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
350
  TileManager *new_tiles;
351 352 353 354
  guchar       bg = 0;
  gint         clear;
  gint         w, h;
  gint         x1, y1, x2, y2;
Elliot Lee's avatar
Elliot Lee committed
355

356 357
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

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

361 362 363 364
  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
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
  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
391 392 393 394
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
395 396

  /*  Configure the pixel regions  */
397 398
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
		     x1, y1, w, h, FALSE);
Elliot Lee's avatar
Elliot Lee committed
399 400

  /*  Determine whether the new channel needs to be initially cleared  */
401 402
  if ((new_width  > GIMP_DRAWABLE (channel)->width) ||
      (new_height > GIMP_DRAWABLE (channel)->height) ||
Elliot Lee's avatar
Elliot Lee committed
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
      (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)
    {
      pixel_region_init (&destPR, new_tiles, 0, 0, new_width, new_height, TRUE);
      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  */
424
  undo_push_channel_mod (GIMP_DRAWABLE (channel)->gimage, channel);
Elliot Lee's avatar
Elliot Lee committed
425 426

  /*  Configure the new channel  */
427 428 429
  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
430 431 432 433 434

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

  /*  update the new channel area  */
Michael Natterer's avatar
Michael Natterer committed
435 436 437 438
  gimp_drawable_update (GIMP_DRAWABLE (channel),
			0, 0,
			GIMP_DRAWABLE (channel)->width,
			GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
439 440
}

441

442 443 444
/******************************/
/*  selection mask functions  */
/******************************/
Elliot Lee's avatar
Elliot Lee committed
445

Michael Natterer's avatar
Michael Natterer committed
446 447
GimpChannel *
gimp_channel_new_mask (GimpImage *gimage,
448 449
		       gint       width,
		       gint       height)
Elliot Lee's avatar
Elliot Lee committed
450
{
Michael Natterer's avatar
Michael Natterer committed
451 452
  GimpRGB      black = { 0.0, 0.0, 0.0, 0.5 };
  GimpChannel *new_channel;
Elliot Lee's avatar
Elliot Lee committed
453

454 455
  g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);

Elliot Lee's avatar
Elliot Lee committed
456
  /*  Create the new channel  */
Michael Natterer's avatar
Michael Natterer committed
457 458
  new_channel = gimp_channel_new (gimage, width, height,
				  _("Selection Mask"), &black);
Elliot Lee's avatar
Elliot Lee committed
459 460

  /*  Set the validate procedure  */
461
  tile_manager_set_validate_proc (GIMP_DRAWABLE (new_channel)->tiles,
Michael Natterer's avatar
Michael Natterer committed
462
				  gimp_channel_validate);
Elliot Lee's avatar
Elliot Lee committed
463 464 465 466

  return new_channel;
}

467
gboolean
Michael Natterer's avatar
Michael Natterer committed
468 469 470 471 472 473 474 475 476
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
477
{
478
  gint        x3, y3, x4, y4;
Elliot Lee's avatar
Elliot Lee committed
479 480
  PixelRegion bPR;

481 482
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);

Elliot Lee's avatar
Elliot Lee committed
483 484 485 486 487 488 489 490
  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
491
      if (gimp_channel_bounds (mask, &x3, &y3, &x4, &y4))
Elliot Lee's avatar
Elliot Lee committed
492
	{
493 494
	  pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles,
			     x3, y3, (x4 - x3), (y4 - y3), FALSE);
Elliot Lee's avatar
Elliot Lee committed
495 496 497 498
	  mask->segs_out = find_mask_boundary (&bPR, &mask->num_segs_out,
					       IgnoreBounds,
					       x1, y1,
					       x2, y2);
499 500 501 502
	  x1 = MAX (x1, x3);
	  y1 = MAX (y1, y3);
	  x2 = MIN (x2, x4);
	  y2 = MIN (y2, y4);
Elliot Lee's avatar
Elliot Lee committed
503 504 505

	  if (x2 > x1 && y2 > y1)
	    {
506 507 508 509
	      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
510 511 512 513 514 515 516
	      mask->segs_in =  find_mask_boundary (&bPR, &mask->num_segs_in,
						   WithinBounds,
						   x1, y1,
						   x2, y2);
	    }
	  else
	    {
517
	      mask->segs_in     = NULL;
Elliot Lee's avatar
Elliot Lee committed
518 519 520 521 522
	      mask->num_segs_in = 0;
	    }
	}
      else
	{
523 524 525
	  mask->segs_in      = NULL;
	  mask->segs_out     = NULL;
	  mask->num_segs_in  = 0;
Elliot Lee's avatar
Elliot Lee committed
526 527 528 529 530
	  mask->num_segs_out = 0;
	}
      mask->boundary_known = TRUE;
    }

531 532 533
  *segs_in      = mask->segs_in;
  *segs_out     = mask->segs_out;
  *num_segs_in  = mask->num_segs_in;
Elliot Lee's avatar
Elliot Lee committed
534 535 536 537 538
  *num_segs_out = mask->num_segs_out;

  return TRUE;
}

539
gint
Michael Natterer's avatar
Michael Natterer committed
540 541 542
gimp_channel_value (GimpChannel *mask,
		    gint         x,
		    gint         y)
Elliot Lee's avatar
Elliot Lee committed
543 544
{
  Tile *tile;
545
  gint  val;
Elliot Lee's avatar
Elliot Lee committed
546

547 548
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), 0);

Elliot Lee's avatar
Elliot Lee committed
549 550 551 552 553 554 555 556 557 558
  /*  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
    {
559 560
      if (x < 0 || x >= GIMP_DRAWABLE (mask)->width ||
	  y < 0 || y >= GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
561 562 563
	return 0;
    }

564 565
  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));
566
  tile_release (tile, FALSE);
Elliot Lee's avatar
Elliot Lee committed
567 568 569 570

  return val;
}

571
gboolean
Michael Natterer's avatar
Michael Natterer committed
572 573 574 575 576
gimp_channel_bounds (GimpChannel *mask,
		     gint        *x1,
		     gint        *y1,
		     gint        *x2,
		     gint        *y2)
Elliot Lee's avatar
Elliot Lee committed
577
{
578 579 580 581 582 583 584
  PixelRegion  maskPR;
  guchar      *data, *data1;
  gint         x, y;
  gint         ex, ey;
  gint         tx1, tx2, ty1, ty2;
  gint         minx, maxx;
  gpointer     pr;
585

586 587
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);

Elliot Lee's avatar
Elliot Lee committed
588 589 590 591 592 593 594 595
  /*  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;

596
      return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
597 598 599
    }

  /*  go through and calculate the bounds  */
600 601
  tx1 = GIMP_DRAWABLE (mask)->width;
  ty1 = GIMP_DRAWABLE (mask)->height;
602 603
  tx2 = 0;
  ty2 = 0;
Elliot Lee's avatar
Elliot Lee committed
604

605 606 607 608 609 610 611
  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
612
    {
613
      data1 = data = maskPR.data;
Elliot Lee's avatar
Elliot Lee committed
614 615
      ex = maskPR.x + maskPR.w;
      ey = maskPR.y + maskPR.h;
616 617
      /* only check the pixels if this tile is not fully within the currently
	 computed bounds */
618 619
      if (maskPR.x < tx1 || ex > tx2 ||
	  maskPR.y < ty1 || ey > ty2)
620
        {
621 622 623 624 625 626 627 628 629 630 631 632 633 634
	  /* 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
635
	    for (y = maskPR.y; y < ey; y++, data1 += maskPR.rowstride)
Elliot Lee's avatar
Elliot Lee committed
636
	    {
637
	      for (x = maskPR.x, data = data1; x < ex; x++, data++)
638
		if (*data)
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653
		{
		  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
654 655 656 657
	    }
	}
    }

658 659
  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
660

661
  if (tx1 == GIMP_DRAWABLE (mask)->width && ty1 == GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
662 663
    {
      mask->empty = TRUE;
664 665 666 667
      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
668 669 670 671
    }
  else
    {
      mask->empty = FALSE;
672 673 674 675
      mask->x1    = tx1;
      mask->y1    = ty1;
      mask->x2    = tx2;
      mask->y2    = ty2;
Elliot Lee's avatar
Elliot Lee committed
676 677 678
    }
  mask->bounds_known = TRUE;

679 680 681 682 683
  *x1 = tx1;
  *x2 = tx2;
  *y1 = ty1;
  *y2 = ty2;

684
  return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
685 686
}

687
gboolean
Michael Natterer's avatar
Michael Natterer committed
688
gimp_channel_is_empty (GimpChannel *mask)
Elliot Lee's avatar
Elliot Lee committed
689
{
690 691 692 693
  PixelRegion  maskPR;
  guchar      *data;
  gint         x, y;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
694

695 696
  g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE);

Elliot Lee's avatar
Elliot Lee committed
697 698 699
  if (mask->bounds_known)
    return mask->empty;

700 701 702 703 704 705 706
  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
707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
    {
      /*  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);

725 726 727 728 729 730
  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
731
  mask->boundary_known = TRUE;
732 733 734 735
  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
736 737 738 739 740

  return TRUE;
}

void
Michael Natterer's avatar
Michael Natterer committed
741 742 743
gimp_channel_add_segment (GimpChannel *mask,
			  gint         x,
			  gint         y,
744 745
			  gint         width,
			  gint         value)
Elliot Lee's avatar
Elliot Lee committed
746
{
747 748 749 750 751
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
752

753 754
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

Elliot Lee's avatar
Elliot Lee committed
755 756 757
  /*  check horizontal extents...  */
  x2 = x + width;
  if (x2 < 0) x2 = 0;
758
  if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
759
  if (x < 0) x = 0;
760
  if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
761 762 763
  width = x2 - x;
  if (!width) return;

764
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
765 766
    return;

767 768 769 770 771
  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
772 773 774 775 776 777 778 779 780 781 782 783 784 785
    {
      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
786 787 788
gimp_channel_sub_segment (GimpChannel *mask,
			  gint         x,
			  gint         y,
789 790
			  gint         width,
			  gint         value)
Elliot Lee's avatar
Elliot Lee committed
791
{
792 793 794 795 796
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
797

798 799
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

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

809
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
810 811
    return;

812 813 814 815
  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
816 817 818 819 820 821 822 823 824 825 826 827 828 829
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data - value;
	  if (val < 0)
	    val = 0;
	  *data++ = val;
	}
    }
}

void
Michael Natterer's avatar
Michael Natterer committed
830 831 832 833
gimp_channel_combine_rect (GimpChannel *mask,
			   ChannelOps   op,
			   gint         x,
			   gint         y,
834 835
			   gint         w,
			   gint         h)
Elliot Lee's avatar
Elliot Lee committed
836
{
837
  gint        x2, y2;
Elliot Lee's avatar
Elliot Lee committed
838
  PixelRegion maskPR;
839
  guchar      color;
840

841 842
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

843 844
  y2 = y + h;
  x2 = x + w;
Elliot Lee's avatar
Elliot Lee committed
845

846 847 848 849
  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
850

851
  if (x2 - x <= 0 || y2 - y <= 0)
Elliot Lee's avatar
Elliot Lee committed
852 853
    return;

854 855
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, x2 - x, y2 - y, TRUE);
856
  if (op == CHANNEL_OP_ADD  || op == CHANNEL_OP_REPLACE)
857 858 859
    color = 255;
  else
    color = 0;
860
  color_region (&maskPR, &color);
Elliot Lee's avatar
Elliot Lee committed
861 862

  /*  Determine new boundary  */
863
  if (mask->bounds_known && (op == CHANNEL_OP_ADD) && !mask->empty)
Elliot Lee's avatar
Elliot Lee committed
864 865 866 867 868 869 870 871 872 873
    {
      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);
    }
874
  else if (op == CHANNEL_OP_REPLACE || mask->empty)
Elliot Lee's avatar
Elliot Lee committed
875 876 877 878 879 880 881 882 883 884
    {
      mask->empty = FALSE;
      mask->x1 = x;
      mask->y1 = y;
      mask->x2 = x + w;
      mask->y2 = y + h;
    }
  else
    mask->bounds_known = FALSE;

885 886 887 888
  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
889 890 891
}

void
Michael Natterer's avatar
Michael Natterer committed
892 893 894 895
gimp_channel_combine_ellipse (GimpChannel *mask,
			      ChannelOps   op,
			      gint         x,
			      gint         y,
896 897
			      gint         w,
			      gint         h,
Michael Natterer's avatar
Michael Natterer committed
898
			      gboolean     antialias)
Elliot Lee's avatar
Elliot Lee committed
899
{
900 901 902
  gint   i, j;
  gint   x0, x1, x2;
  gint   val, last;
903 904 905 906 907 908 909 910
  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
911

912 913
  g_return_if_fail (GIMP_IS_CHANNEL (mask));

Elliot Lee's avatar
Elliot Lee committed
914 915 916 917 918 919 920 921 922 923 924 925
  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++)
    {
926
      if (i >= 0 && i < GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
927 928
	{
	  /*  Non-antialiased code  */
929
	  if (!antialias)
Elliot Lee's avatar
Elliot Lee committed
930 931 932 933 934 935 936 937
	    {
	      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)
		{
938 939
		case CHANNEL_OP_ADD:
		case CHANNEL_OP_REPLACE:
Michael Natterer's avatar
Michael Natterer committed
940
		  gimp_channel_add_segment (mask, x1, i, (x2 - x1), 255);
Elliot Lee's avatar
Elliot Lee committed
941
		  break;
942
		case CHANNEL_OP_SUB:
Michael Natterer's avatar
Michael Natterer committed
943
		  gimp_channel_sub_segment (mask, x1, i, (x2 - x1), 255);
Elliot Lee's avatar
Elliot Lee committed
944
		  break;
Sven Neumann's avatar
Sven Neumann committed
945
		default:
946
		  g_warning ("Only ADD, REPLACE, and SUB are valid for channel_combine!");
Sven Neumann's avatar
Sven Neumann committed
947
		  break;
Elliot Lee's avatar
Elliot Lee committed
948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972