gimpchannel.c 40 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
#include "config.h"

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

Elliot Lee's avatar
Elliot Lee committed
23
24
25
26
#include "appenv.h"
#include "channel.h"
#include "drawable.h"
#include "errors.h"
27
#include "gdisplay.h"
Elliot Lee's avatar
Elliot Lee committed
28
29
30
#include "gimage_mask.h"
#include "layer.h"
#include "paint_funcs.h"
31
#include "parasitelist.h"
Elliot Lee's avatar
Elliot Lee committed
32
33
#include "temp_buf.h"
#include "undo.h"
34
#include "gimppreviewcache.h"
Elliot Lee's avatar
Elliot Lee committed
35

36
#include "libgimp/gimpintl.h"
37
#include "libgimp/gimpmath.h"
38

39
#include "channel_pvt.h"
scott's avatar
scott committed
40
#include "tile.h"
41

42
43
44
#include "gimplut.h"
#include "lut_funcs.h"

scott's avatar
scott committed
45
/*
46
47
48
enum {
  LAST_SIGNAL
};
scott's avatar
scott committed
49
*/
50
51
52
53
54

static void gimp_channel_class_init (GimpChannelClass *klass);
static void gimp_channel_init       (GimpChannel      *channel);
static void gimp_channel_destroy    (GtkObject        *object);

scott's avatar
scott committed
55
/*
56
static gint channel_signals[LAST_SIGNAL] = { 0 };
scott's avatar
scott committed
57
*/
58
59
60

static GimpDrawableClass *parent_class = NULL;

61
GtkType
62
63
gimp_channel_get_type ()
{
64
  static GtkType channel_type = 0;
65
66
67
68
69
70
71
72
73
74

  if (!channel_type)
    {
      GtkTypeInfo channel_info =
      {
	"GimpChannel",
	sizeof (GimpChannel),
	sizeof (GimpChannelClass),
	(GtkClassInitFunc) gimp_channel_class_init,
	(GtkObjectInitFunc) gimp_channel_init,
75
76
        /* reserved_1 */ NULL,
	/* reserved_2 */ NULL,
77
	(GtkClassInitFunc) NULL,
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
      };

      channel_type = gtk_type_unique (gimp_drawable_get_type (), &channel_info);
    }

  return channel_type;
}

static void
gimp_channel_class_init (GimpChannelClass *class)
{
  GtkObjectClass *object_class;

  object_class = (GtkObjectClass*) class;
  parent_class = gtk_type_class (gimp_drawable_get_type ());

scott's avatar
scott committed
94
  /*
95
  gtk_object_class_add_signals (object_class, channel_signals, LAST_SIGNAL);
scott's avatar
scott committed
96
  */
97
98
99
100
101
102
103
104

  object_class->destroy = gimp_channel_destroy;
}

static void
gimp_channel_init (GimpChannel *channel)
{
}
Elliot Lee's avatar
Elliot Lee committed
105
106
107

/**************************/
/*  Function definitions  */
108
/**************************/
Elliot Lee's avatar
Elliot Lee committed
109
110

static void
111
112
channel_validate (TileManager *tm,
		  Tile        *tile)
Elliot Lee's avatar
Elliot Lee committed
113
114
{
  /*  Set the contents of the tile to empty  */
scott's avatar
scott committed
115
  memset (tile_data_pointer (tile, 0, 0), 
116
	  TRANSPARENT_OPACITY, tile_size (tile));
Elliot Lee's avatar
Elliot Lee committed
117
118
119
}

Channel *
120
121
122
123
124
125
channel_new (GimpImage *gimage,
	     gint       width,
	     gint       height,
	     gchar     *name,
	     gint       opacity,
	     guchar    *col)
Elliot Lee's avatar
Elliot Lee committed
126
127
{
  Channel * channel;
128
  gint i;
Elliot Lee's avatar
Elliot Lee committed
129

130
  channel = gtk_type_new (gimp_channel_get_type ());
Elliot Lee's avatar
Elliot Lee committed
131

132
  gimp_drawable_configure (GIMP_DRAWABLE (channel), 
133
			   gimage, width, height, GRAY_GIMAGE, name);
Elliot Lee's avatar
Elliot Lee committed
134
135
136
137

  /*  set the channel color and opacity  */
  for (i = 0; i < 3; i++)
    channel->col[i] = col[i];
138
139
140

  channel->opacity     = opacity;
  channel->show_masked = TRUE;
Elliot Lee's avatar
Elliot Lee committed
141
142

  /*  selection mask variables  */
143
144
145
146
147
148
  channel->empty          = TRUE;
  channel->segs_in        = NULL;
  channel->segs_out       = NULL;
  channel->num_segs_in    = 0;
  channel->num_segs_out   = 0;
  channel->bounds_known   = TRUE;
Elliot Lee's avatar
Elliot Lee committed
149
  channel->boundary_known = TRUE;
150
151
152
153
  channel->x1             = 0;
  channel->y1             = 0;
  channel->x2             = width;
  channel->y2             = height;
Elliot Lee's avatar
Elliot Lee committed
154
155
156
157

  return channel;
}

scott's avatar
scott committed
158
159
160
161
162
Channel *
channel_ref (Channel *channel)
{
  gtk_object_ref  (GTK_OBJECT (channel));
  gtk_object_sink (GTK_OBJECT (channel));
163

scott's avatar
scott committed
164
165
166
167
168
169
170
171
172
  return channel;
}

void
channel_unref (Channel *channel)
{
  gtk_object_unref (GTK_OBJECT (channel));
}

Elliot Lee's avatar
Elliot Lee committed
173
174
175
Channel *
channel_copy (Channel *channel)
{
176
177
  gchar *channel_name;
  Channel *new_channel;
Elliot Lee's avatar
Elliot Lee committed
178
  PixelRegion srcPR, destPR;
179
180
181
182
  gchar *ext;
  gint number;
  gchar *name;
  gint len;
Elliot Lee's avatar
Elliot Lee committed
183
184

  /*  formulate the new channel name  */
185
186
  name = channel_get_name (channel);
  ext = strrchr (name, '#');
187
  len = strlen (_("copy"));
188
189
190
191
  if ((strlen (name) >= len &&
       strcmp (&name[strlen (name) - len], _("copy")) == 0) ||
      (ext && (number = atoi (ext + 1)) > 0 && 
       ((int)(log10 (number) + 1)) == strlen (ext + 1)))
192
193
    /* don't have redundant "copy"s */
    channel_name = g_strdup (name);
194
  else
195
    channel_name = g_strdup_printf (_("%s copy"), name);
Elliot Lee's avatar
Elliot Lee committed
196
197

  /*  allocate a new channel object  */
198
199
200
  new_channel = channel_new (GIMP_DRAWABLE (channel)->gimage, 
			     GIMP_DRAWABLE (channel)->width, 
			     GIMP_DRAWABLE (channel)->height, 
201
			     channel_name, channel->opacity, channel->col);
202
  GIMP_DRAWABLE (new_channel)->visible = GIMP_DRAWABLE (channel)->visible;
Elliot Lee's avatar
Elliot Lee committed
203
204
205
  new_channel->show_masked = channel->show_masked;

  /*  copy the contents across channels  */
206
207
208
209
210
211
212
  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
213
214
  copy_region (&srcPR, &destPR);

215
  /* copy the parasites */
216
217
  GIMP_DRAWABLE (new_channel)->parasites 
    = parasite_list_copy (GIMP_DRAWABLE (channel)->parasites);
218

Elliot Lee's avatar
Elliot Lee committed
219
220
221
222
223
224
  /*  free up the channel_name memory  */
  g_free (channel_name);

  return new_channel;
}

225
void
226
227
channel_set_name (Channel *channel,
		  gchar   *name)
228
{
229
  gimp_drawable_set_name (GIMP_DRAWABLE (channel), name);
230
231
}

232
gchar *
233
234
channel_get_name (Channel *channel)
{
235
  return gimp_drawable_get_name (GIMP_DRAWABLE (channel));
236
237
}

238
void 
239
240
channel_set_color (Channel *channel,
		   gchar   *color)
241
{
242
243
  gint i;

244
245
  if (color)
    {  
246
247
      for (i = 0; i < 3; i++)
	channel->col[i] = color[i];
248
    }
249
}
250

251
gchar *
252
253
channel_get_color (Channel *channel)
{
254
  return (GIMP_CHANNEL (channel)->col); 
255
256
257
258
259
260
261
262
}
 
int
channel_get_opacity (Channel *channel)
{ 
  return channel->opacity;
}

263
void 
264
265
channel_set_opacity (Channel *channel,
		     gint     opacity)
266
267
{
  if (opacity >=0 && opacity <= 100)
268
    channel->opacity = (gint) (opacity * 255) / 100;
269
270
}

Elliot Lee's avatar
Elliot Lee committed
271
Channel *
272
channel_get_ID (gint ID)
Elliot Lee's avatar
Elliot Lee committed
273
{
274
  GimpDrawable *drawable;
275

276
277
278
279
280
  drawable = drawable_get_ID (ID);
  if (drawable && GIMP_IS_CHANNEL (drawable)) 
    return GIMP_CHANNEL (drawable);
  else
    return NULL;
Elliot Lee's avatar
Elliot Lee committed
281
282
283
284
285
}

void
channel_delete (Channel *channel)
{
scott's avatar
scott committed
286
  gtk_object_unref (GTK_OBJECT (channel));
287
288
289
290
291
292
293
294
295
296
297
298
}

static void
gimp_channel_destroy (GtkObject *object)
{
  GimpChannel *channel;

  g_return_if_fail (object != NULL);
  g_return_if_fail (GIMP_IS_CHANNEL (object));

  channel = GIMP_CHANNEL (object);

Elliot Lee's avatar
Elliot Lee committed
299
300
301
302
303
304
  /* free the segments?  */
  if (channel->segs_in)
    g_free (channel->segs_in);
  if (channel->segs_out)
    g_free (channel->segs_out);

305
  if (GTK_OBJECT_CLASS (parent_class)->destroy)
306
    (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
307
  
Elliot Lee's avatar
Elliot Lee committed
308
309
310
}

void
311
312
313
channel_scale (Channel *channel,
	       gint     new_width,
	       gint     new_height)
Elliot Lee's avatar
Elliot Lee committed
314
315
316
317
318
319
320
321
{
  PixelRegion srcPR, destPR;
  TileManager *new_tiles;

  if (new_width == 0 || new_height == 0)
    return;

  /*  Update the old channel position  */
322
323
324
325
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
326
327

  /*  Configure the pixel regions  */
328
329
330
331
  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
332
333
334
335
336
337
338
339
340

  /*  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  */
341
  undo_push_channel_mod (GIMP_DRAWABLE (channel)->gimage, channel);
Elliot Lee's avatar
Elliot Lee committed
342
343

  /*  Configure the new channel  */
344
345
346
  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
347
348
349
350
351

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

  /*  Update the new channel position  */
352
353
354
355
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
356
357
358
}

void
359
360
361
362
363
channel_resize (Channel *channel,
		gint     new_width,
		gint     new_height,
		gint     offx,
		gint     offy)
Elliot Lee's avatar
Elliot Lee committed
364
365
366
{
  PixelRegion srcPR, destPR;
  TileManager *new_tiles;
367
368
369
370
  guchar bg = 0;
  gint clear;
  gint w, h;
  gint x1, y1, x2, y2;
Elliot Lee's avatar
Elliot Lee committed
371
372
373
374
375
376

  if (!new_width || !new_height)
    return;

  x1 = BOUNDS (offx, 0, new_width);
  y1 = BOUNDS (offy, 0, new_height);
377
378
  x2 = BOUNDS ((offx + GIMP_DRAWABLE (channel)->width), 0, new_width);
  y2 = BOUNDS ((offy + GIMP_DRAWABLE (channel)->height), 0, new_height);
Elliot Lee's avatar
Elliot Lee committed
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
  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  */
405
406
407
408
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
409
410

  /*  Configure the pixel regions  */
411
412
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
		     x1, y1, w, h, FALSE);
Elliot Lee's avatar
Elliot Lee committed
413
414

  /*  Determine whether the new channel needs to be initially cleared  */
415
416
  if ((new_width  > GIMP_DRAWABLE (channel)->width) ||
      (new_height > GIMP_DRAWABLE (channel)->height) ||
Elliot Lee's avatar
Elliot Lee committed
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
      (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  */
438
  undo_push_channel_mod (GIMP_DRAWABLE (channel)->gimage, channel);
Elliot Lee's avatar
Elliot Lee committed
439
440

  /*  Configure the new channel  */
441
442
443
  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
444
445
446
447
448

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

  /*  update the new channel area  */
449
450
451
452
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
453
454
}

455
456
457
void            
channel_update (Channel *channel)
{
458
459
460
461
462
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
  gdisplays_flush ();
463
464
}

465
466
467
/**********************/
/*  access functions  */
/**********************/
Elliot Lee's avatar
Elliot Lee committed
468

469
gboolean
Elliot Lee's avatar
Elliot Lee committed
470
471
channel_toggle_visibility (Channel *channel)
{
472
  GIMP_DRAWABLE (channel)->visible = !GIMP_DRAWABLE (channel)->visible;
Elliot Lee's avatar
Elliot Lee committed
473

474
  return GIMP_DRAWABLE (channel)->visible;
Elliot Lee's avatar
Elliot Lee committed
475
476
}

477
478
479
480
static TempBuf *
channel_preview_private (Channel *channel,
			 gint     width,
			 gint     height)
Elliot Lee's avatar
Elliot Lee committed
481
482
483
{
  MaskBuf * preview_buf;
  PixelRegion srcPR, destPR;
484
  gint subsample;
485
  TempBuf *ret_buf;
Elliot Lee's avatar
Elliot Lee committed
486
487

  /*  The easy way  */
488
489
490
491
  if (GIMP_DRAWABLE (channel)->preview_valid &&
      (ret_buf =
       gimp_preview_cache_get (& (GIMP_DRAWABLE (channel)->preview_cache),
			       width, height)))
492
    return ret_buf;
Elliot Lee's avatar
Elliot Lee committed
493
494
495
496
497
  /*  The hard way  */
  else
    {
      /*  calculate 'acceptable' subsample  */
      subsample = 1;
498
499
      if (width < 1) width = 1;
      if (height < 1) height = 1;
500
501
      while ((width  * (subsample + 1) * 2 < GIMP_DRAWABLE (channel)->width) &&
	     (height * (subsample + 1) * 2 < GIMP_DRAWABLE (channel)->height))
Elliot Lee's avatar
Elliot Lee committed
502
503
	subsample = subsample + 1;

504
505
506
507
      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
508
509

      preview_buf = mask_buf_new (width, height);
510
511
512
513
514
      destPR.bytes     = 1;
      destPR.x         = 0;
      destPR.y         = 0;
      destPR.w         = width;
      destPR.h         = height;
Elliot Lee's avatar
Elliot Lee committed
515
      destPR.rowstride = width;
516
      destPR.data      = mask_buf_data (preview_buf);
Elliot Lee's avatar
Elliot Lee committed
517
518
519

      subsample_region (&srcPR, &destPR, subsample);

520
521
522
523
524
525
      if (!GIMP_DRAWABLE (channel)->preview_valid)
	gimp_preview_cache_invalidate (&(GIMP_DRAWABLE(channel)->preview_cache));

      GIMP_DRAWABLE (channel)->preview_valid = TRUE;
      gimp_preview_cache_add (&(GIMP_DRAWABLE (channel)->preview_cache),
			      preview_buf);
526
      return preview_buf;
Elliot Lee's avatar
Elliot Lee committed
527
528
529
    }
}

530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
TempBuf *
channel_preview (Channel *channel,
		 gint     width,
		 gint     height)
{
  /* Ok prime the cache with a large preview if the cache is invalid */
  if(!GIMP_DRAWABLE(channel)->preview_valid && 
     width <= PREVIEW_CACHE_PRIME_WIDTH &&
     height <= PREVIEW_CACHE_PRIME_HEIGHT)
    {
      TempBuf * tb = channel_preview_private(channel,
					     PREVIEW_CACHE_PRIME_WIDTH,
					     PREVIEW_CACHE_PRIME_HEIGHT);
      
      /* Save the 2nd call */
      if(width == PREVIEW_CACHE_PRIME_WIDTH &&
	 height == PREVIEW_CACHE_PRIME_HEIGHT)
	return tb;
    }

  /* Second call - should NOT visit the tile cache...*/
  return channel_preview_private(channel,width,height);
}


Elliot Lee's avatar
Elliot Lee committed
555
void
556
channel_invalidate_previews (GimpImage* gimage)
Elliot Lee's avatar
Elliot Lee committed
557
{
558
  GSList * tmp;
Elliot Lee's avatar
Elliot Lee committed
559
  Channel * channel;
560
561

  tmp = gimage->channels;
Elliot Lee's avatar
Elliot Lee committed
562
563
564
565

  while (tmp)
    {
      channel = (Channel *) tmp->data;
566
      drawable_invalidate_preview (GIMP_DRAWABLE (channel));
567
      tmp = g_slist_next (tmp);
Elliot Lee's avatar
Elliot Lee committed
568
569
570
    }
}

571
Tattoo
572
channel_get_tattoo (const Channel *channel)
573
{
574
  return (gimp_drawable_get_tattoo (GIMP_DRAWABLE (channel)));
575
576
}

577
578
579
/******************************/
/*  selection mask functions  */
/******************************/
Elliot Lee's avatar
Elliot Lee committed
580
581

Channel *
582
583
584
channel_new_mask (GimpImage* gimage,
		  gint       width,
		  gint       height)
Elliot Lee's avatar
Elliot Lee committed
585
{
586
  guchar black[3] = {0, 0, 0};
Elliot Lee's avatar
Elliot Lee committed
587
588
589
  Channel *new_channel;

  /*  Create the new channel  */
590
591
  new_channel = channel_new (gimage, width, height,
			     _("Selection Mask"), 127, black);
Elliot Lee's avatar
Elliot Lee committed
592
593

  /*  Set the validate procedure  */
594
595
  tile_manager_set_validate_proc (GIMP_DRAWABLE (new_channel)->tiles,
				  channel_validate);
Elliot Lee's avatar
Elliot Lee committed
596
597
598
599

  return new_channel;
}

600
601
602
603
604
605
606
607
608
609
gboolean
channel_boundary (Channel   *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
610
{
611
  gint x3, y3, x4, y4;
Elliot Lee's avatar
Elliot Lee committed
612
613
614
615
616
617
618
619
620
621
622
623
  PixelRegion bPR;

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

      if (channel_bounds (mask, &x3, &y3, &x4, &y4))
	{
624
625
	  pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles,
			     x3, y3, (x4 - x3), (y4 - y3), FALSE);
Elliot Lee's avatar
Elliot Lee committed
626
627
628
629
630
631
632
633
634
635
636
	  mask->segs_out = find_mask_boundary (&bPR, &mask->num_segs_out,
					       IgnoreBounds,
					       x1, y1,
					       x2, y2);
	  x1 = MAXIMUM (x1, x3);
	  y1 = MAXIMUM (y1, y3);
	  x2 = MINIMUM (x2, x4);
	  y2 = MINIMUM (y2, y4);

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

662
663
664
  *segs_in      = mask->segs_in;
  *segs_out     = mask->segs_out;
  *num_segs_in  = mask->num_segs_in;
Elliot Lee's avatar
Elliot Lee committed
665
666
667
668
669
  *num_segs_out = mask->num_segs_out;

  return TRUE;
}

670
671
672
673
gint
channel_value (Channel *mask,
	       gint     x,
	       gint     y)
Elliot Lee's avatar
Elliot Lee committed
674
675
{
  Tile *tile;
676
  gint val;
Elliot Lee's avatar
Elliot Lee committed
677
678
679
680
681
682
683
684
685
686
687

  /*  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
    {
688
689
      if (x < 0 || x >= GIMP_DRAWABLE (mask)->width ||
	  y < 0 || y >= GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
690
691
692
	return 0;
    }

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

  return val;
}

700
701
702
703
704
705
gboolean
channel_bounds (Channel *mask,
		gint    *x1,
		gint    *y1,
		gint    *x2,
		gint    *y2)
Elliot Lee's avatar
Elliot Lee committed
706
707
{
  PixelRegion maskPR;
708
709
710
  guchar *data, *data1;
  gint x, y;
  gint ex, ey;
Elliot Lee's avatar
Elliot Lee committed
711
  void *pr;
712
713
714
  gint tx1, tx2, ty1, ty2;
  gint minx, maxx;

Elliot Lee's avatar
Elliot Lee committed
715
716
717
718
719
720
721
722
  /*  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;

723
      return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
724
725
726
    }

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

732
733
734
735
736
737
738
  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
739
    {
740
      data1 = data = maskPR.data;
Elliot Lee's avatar
Elliot Lee committed
741
742
      ex = maskPR.x + maskPR.w;
      ey = maskPR.y + maskPR.h;
743
744
      /* only check the pixels if this tile is not fully within the currently
	 computed bounds */
745
746
      if (maskPR.x < tx1 || ex > tx2 ||
	  maskPR.y < ty1 || ey > ty2)
747
        {
748
749
750
751
752
753
754
755
756
757
758
759
760
761
	  /* 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
762
	    for (y = maskPR.y; y < ey; y++, data1 += maskPR.rowstride)
Elliot Lee's avatar
Elliot Lee committed
763
	    {
764
	      for (x = maskPR.x, data = data1; x < ex; x++, data++)
765
		if (*data)
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
		{
		  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
781
782
783
784
	    }
	}
    }

785
786
  tx2 = BOUNDS (tx2 + 1, 0, GIMP_DRAWABLE (mask)->width);
  ty2 = BOUNDS (ty2 + 1, 0, GIMP_DRAWABLE (mask)->height);
Elliot Lee's avatar
Elliot Lee committed
787

788
  if (tx1 == GIMP_DRAWABLE (mask)->width && ty1 == GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
789
790
    {
      mask->empty = TRUE;
791
792
793
794
      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
795
796
797
798
    }
  else
    {
      mask->empty = FALSE;
799
800
801
802
      mask->x1    = tx1;
      mask->y1    = ty1;
      mask->x2    = tx2;
      mask->y2    = ty2;
Elliot Lee's avatar
Elliot Lee committed
803
804
805
    }
  mask->bounds_known = TRUE;

806
807
808
809
810
  *x1 = tx1;
  *x2 = tx2;
  *y1 = ty1;
  *y2 = ty2;

811
  return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
812
813
}

814
gboolean
Elliot Lee's avatar
Elliot Lee committed
815
816
817
channel_is_empty (Channel *mask)
{
  PixelRegion maskPR;
818
819
  guchar * data;
  gint x, y;
Elliot Lee's avatar
Elliot Lee committed
820
821
822
823
824
  void * pr;

  if (mask->bounds_known)
    return mask->empty;

825
826
827
828
829
830
831
  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
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
    {
      /*  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);

850
851
852
853
854
855
  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
856
  mask->boundary_known = TRUE;
857
858
859
860
  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
861
862
863
864
865

  return TRUE;
}

void
866
867
868
869
870
channel_add_segment (Channel *mask,
		     gint     x,
		     gint     y,
		     gint     width,
		     gint     value)
Elliot Lee's avatar
Elliot Lee committed
871
872
{
  PixelRegion maskPR;
873
874
875
  guchar *data;
  gint val;
  gint x2;
Elliot Lee's avatar
Elliot Lee committed
876
877
878
879
880
  void * pr;

  /*  check horizontal extents...  */
  x2 = x + width;
  if (x2 < 0) x2 = 0;
881
  if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
882
  if (x < 0) x = 0;
883
  if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
884
885
886
  width = x2 - x;
  if (!width) return;

887
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
888
889
    return;

890
891
892
893
894
  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
895
896
897
898
899
900
901
902
903
904
905
906
907
908
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data + value;
	  if (val > 255)
	    val = 255;
	  *data++ = val;
	}
    }
}

void
909
910
911
912
913
channel_sub_segment (Channel *mask,
		     gint     x,
		     gint     y,
		     gint     width,
		     gint     value)
Elliot Lee's avatar
Elliot Lee committed
914
915
{
  PixelRegion maskPR;
916
917
918
  guchar *data;
  gint val;
  gint x2;
Elliot Lee's avatar
Elliot Lee committed
919
920
921
922
923
  void * pr;

  /*  check horizontal extents...  */
  x2 = x + width;
  if (x2 < 0) x2 = 0;
924
  if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
925
  if (x < 0) x = 0;
926
  if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
927
928
929
  width = x2 - x;
  if (!width) return;

930
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
931
932
    return;

933
934
935
936
  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
937
938
939
940
941
942
943
944
945
946
947
948
949
950
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data - value;
	  if (val < 0)
	    val = 0;
	  *data++ = val;
	}
    }
}

void
951
952
953
954
955
956
channel_combine_rect (Channel    *mask,
		      ChannelOps  op,
		      gint        x,
		      gint        y,
		      gint        w,
		      gint        h)
Elliot Lee's avatar
Elliot Lee committed
957
{
958
  gint x2, y2;
Elliot Lee's avatar
Elliot Lee committed
959
  PixelRegion maskPR;
960
961
  guchar color;

962
963
  y2 = y + h;
  x2 = x + w;
Elliot Lee's avatar
Elliot Lee committed
964

965
966
967
968
  x  = BOUNDS (x,  0, GIMP_DRAWABLE (mask)->width);
  y  = BOUNDS (y,  0, GIMP_DRAWABLE (mask)->height);
  x2 = BOUNDS (x2, 0, GIMP_DRAWABLE (mask)->width);
  y2 = BOUNDS (y2, 0, GIMP_DRAWABLE (mask)->height);
Elliot Lee's avatar
Elliot Lee committed
969

970
  if (x2 - x <= 0 || y2 - y <= 0)
Elliot Lee's avatar
Elliot Lee committed
971
972
    return;

973
974
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, x2 - x, y2 - y, TRUE);
975
976
977
978
  if (op == ADD  || op == REPLACE)
    color = 255;
  else
    color = 0;
979
  color_region (&maskPR, &color);
Elliot Lee's avatar
Elliot Lee committed
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003

  /*  Determine new boundary  */
  if (mask->bounds_known && (op == ADD) && !mask->empty)
    {
      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);
    }
  else if (op == REPLACE || mask->empty)
    {
      mask->empty = FALSE;
      mask->x1 = x;
      mask->y1 = y;
      mask->x2 = x + w;
      mask->y2 = y + h;
    }
  else
    mask->bounds_known = FALSE;

1004
1005
1006
1007
  mask->x1 = BOUNDS (mask->x1, 0, GIMP_DRAWABLE (mask)->width);
  mask->y1 = BOUNDS (mask->y1, 0, GIMP_DRAWABLE (mask)->height);
  mask->x2 = BOUNDS (mask->x2, 0, GIMP_DRAWABLE (mask)->width);
  mask->y2 = BOUNDS (mask->y2, 0, GIMP_DRAWABLE (mask)->height);
Elliot Lee's avatar
Elliot Lee committed
1008
1009
1010
}

void
1011
1012
1013
1014
1015
1016
1017
channel_combine_ellipse (Channel    *mask,
			 ChannelOps  op,
			 gint        x,
			 gint        y,
			 gint        w,
			 gint        h,
			 gboolean    aa /*  antialias selection?  */)
Elliot Lee's avatar
Elliot Lee committed
1018
{
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
  gint i, j;
  gint x0, x1, x2;
  gint val, last;
  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
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042

  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++)
    {
1043
      if (i >= 0 && i < GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
	{
	  /*  Non-antialiased code  */
	  if (!aa)
	    {
	      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)
		{
		case ADD: case REPLACE:
		  channel_add_segment (mask, x1, i, (x2 - x1), 255);
		  break;
		case SUB :
		  channel_sub_segment (mask, x1, i, (x2 - x1), 255);
		  break;
Sven Neumann's avatar
Sven Neumann committed
1061
1062
1063
		default:
		  g_warning ("Only ADD, REPLACE and SUB are valid for channel_combine!");
		  break;
Elliot Lee's avatar
Elliot Lee committed
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
		}
	    }
	  /*  antialiasing  */
	  else
	    {
	      x0 = x;
	      last = 0;
	      h_sqr = (i + 0.5 - cy) * (i + 0.5 - cy);
	      for (j = x; j < (x + w); j++)
		{
		  w_sqr = (j + 0.5 - cx) * (j + 0.5 - cx);

		  if (h_sqr != 0)
		    {
		      t0 = w_sqr / h_sqr;
		      t1 = a_sqr / (t0 + aob_sqr);
		      r = sqrt (t1 + t0 * t1);
		      rad = sqrt (w_sqr + h_sqr);
		      dist = rad - r;
		    }
		  else
		    dist = -1.0;

		  if (dist < -0.5)
		    val = 255;
		  else if (dist < 0.5)
		    val = (int) (255 * (1 - (dist + 0.5)));
		  else
		    val = 0;
1093
		  
Elliot Lee's avatar
Elliot Lee committed
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
		  if (last != val && last)
		    {
		      switch (op)
			{
			case ADD: case REPLACE:
			  channel_add_segment (mask, x0, i, j - x0, last);
			  break;
			case SUB:
			  channel_sub_segment (mask, x0, i, j - x0, last);
			  break;
Sven Neumann's avatar
Sven Neumann committed
1104
1105
1106
			default:
			  g_warning ("Only ADD, REPLACE and SUB are valid for channel_combine!");
			  break;
Elliot Lee's avatar
Elliot Lee committed
1107
1108
1109
1110
1111
1112
1113
			}
		    }

		  if (last != val)
		    {
		      x0 = j;
		      last = val;
1114
1115
1116
1117
		      /* because we are symetric accross the y axis we can
			 skip ahead a bit if we are inside the ellipse*/
		      if (val == 255 && j < cx)
			j = cx + (cx - j) - 1;
Elliot Lee's avatar
Elliot Lee committed
1118
1119
1120
1121
1122
1123
1124
1125
1126
		    }
		}

	      if (last)
		{
		  if (op == ADD || op == REPLACE)
		    channel_add_segment (mask, x0, i, j - x0, last);
		  else if (op == SUB)
		    channel_sub_segment (mask, x0, i, j - x0, last);
Sven Neumann's avatar
Sven Neumann committed
1127
1128
		  else
		    g_warning ("Only ADD, REPLACE and SUB are valid for channel_combine!");
Elliot Lee's avatar
Elliot Lee committed
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
		}
	    }

	}
    }

  /*  Determine new boundary  */
  if (mask->bounds_known && (op == ADD) && !mask->empty)
    {
      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);
    }
  else if (op == REPLACE || mask->empty)
    {
      mask->empty = FALSE;
      mask->x1 = x;
      mask->y1 = y;
      mask->x2 = x + w;
      mask->y2 = y + h;
    }
  else
    mask->bounds_known = FALSE;

1158
1159
1160
1161
  mask->x1 = BOUNDS (mask->x1, 0, GIMP_DRAWABLE (mask)->width);
  mask->y1 = BOUNDS (mask->y1, 0, GIMP_DRAWABLE (mask)->height);
  mask->x2 = BOUNDS (mask->x2, 0, GIMP_DRAWABLE (mask)->width);
  mask->y2 = BOUNDS (mask->y2, 0, GIMP_DRAWABLE (mask)->height);
Elliot Lee's avatar
Elliot Lee committed
1162
1163
}

1164
static void
1165
channel_combine_sub_region_add (void        *unused,
1166
1167
1168
				PixelRegion *srcPR,
				PixelRegion *destPR)
{
1169
1170
1171
1172
  guchar *src, *dest;
  gint x, y, val;

  src  = srcPR->data;
1173
  dest = destPR->data;
1174

1175
1176
  for (y = 0; y < srcPR->h; y++)
    {
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
      for (x = 0; x < srcPR->w; x++)
	{
	  val = dest[x] + src[x];
	  if (val > 255)
	    dest[x] = 255;
	  else
	    dest[x] = val;
	}
      src  += srcPR->rowstride;
      dest += destPR->rowstride;
1187
1188
1189
1190
    }
}

static void
1191
channel_combine_sub_region_sub (void        *unused,
1192
1193
1194
				PixelRegion *srcPR,
				PixelRegion *destPR)
{
1195
1196
1197
1198
  guchar *src, *dest;
  gint x, y;

  src  = srcPR->data;
1199
  dest = destPR->data;
1200

1201
1202
  for (y = 0; y < srcPR->h; y++)
    {
1203
1204
1205
1206
1207
1208
1209
1210
1211
      for (x = 0; x < srcPR->w; x++)
	{
	  if (src[x] > dest[x])
	    dest[x] = 0;
	  else
	    dest[x]-= src[x];
	}
      src  += srcPR->rowstride;
      dest += destPR->rowstride;
1212
1213
1214
1215
    }
}

static void
1216
channel_combine_sub_region_intersect (void        *unused,
1217
1218
1219
				      PixelRegion *srcPR,
				      PixelRegion *destPR)
{
1220
1221
1222
1223
  guchar *src, *dest;
  gint x, y;

  src  = srcPR->data;
1224
  dest = destPR->data;
1225

1226
1227
  for (y = 0; y < srcPR->h; y++)
    {
1228
1229
1230
1231
1232
1233
      for (x = 0; x < srcPR->w; x++)
	{
	  dest[x] = MINIMUM (dest[x], src[x]);
	}
      src  += srcPR->rowstride;
      dest += destPR->rowstride;
1234
1235
1236
  }
}

Elliot Lee's avatar
Elliot Lee committed
1237
void
1238
1239
1240
1241
1242
channel_combine_mask (Channel    *mask,
		      Channel    *add_on,
		      ChannelOps  op,
		      gint        off_x,
		      gint        off_y)
Elliot Lee's avatar
Elliot Lee committed
1243
1244
{
  PixelRegion srcPR, destPR;
1245
1246
  gint x1, y1, x2, y2;
  gint w, h;
Elliot Lee's avatar
Elliot Lee committed
1247

1248
1249
1250
1251
1252
1253
  x1 = BOUNDS (off_x, 0, GIMP_DRAWABLE (mask)->width);
  y1 = BOUNDS (off_y, 0, GIMP_DRAWABLE (mask)->height);
  x2 = BOUNDS (off_x + GIMP_DRAWABLE (add_on)->width, 0,
	       GIMP_DRAWABLE (mask)->width);
  y2 = BOUNDS (off_y + GIMP_DRAWABLE (add_on)->height, 0,
	       GIMP_DRAWABLE (mask)->height);
Elliot Lee's avatar
Elliot Lee committed
1254
1255
1256
1257

  w = (x2 - x1);
  h = (y2 - y1);

1258
1259
1260
  pixel_region_init (&srcPR, GIMP_DRAWABLE (add_on)->tiles,
		     (x1 - off_x), (y1 - off_y), w, h, FALSE);
  pixel_region_init (&destPR, GIMP_DRAWABLE (mask)->tiles, x1, y1, w, h, TRUE);
Elliot Lee's avatar
Elliot Lee committed
1261

1262
  switch (op)
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
    {
    case ADD: case REPLACE:
      pixel_regions_process_parallel ((p_func) channel_combine_sub_region_add,
				      NULL, 2, &srcPR, &destPR);
      break;
    case SUB:
      pixel_regions_process_parallel ((p_func) channel_combine_sub_region_sub,
				      NULL, 2, &srcPR, &destPR);
      break;
    case INTERSECT:
      pixel_regions_process_parallel ((p_func)
				      channel_combine_sub_region_intersect,
				      NULL, 2, &srcPR, &destPR);
      break;
    default:
      g_message ("Error: unknown opperation type in channel_combine_mask\n");
      break;
    }
Elliot Lee's avatar
Elliot Lee committed
1281
1282
1283
1284
  mask->bounds_known = FALSE;
}

void
1285
1286
1287
1288
1289
1290
1291
channel_feather (Channel    *input,
		 Channel    *output,
		 gdouble     radius_x,
		 gdouble     radius_y,
		 ChannelOps  op,
		 gint        off_x,
		 gint        off_y)
Elliot Lee's avatar
Elliot Lee committed
1292
{
1293
  gint x1, y1, x2, y2;
Elliot Lee's avatar
Elliot Lee committed
1294
1295
  PixelRegion srcPR;

1296
1297
1298
1299
1300
1301
  x1 = BOUNDS (off_x, 0, GIMP_DRAW