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

Sven Neumann's avatar
Sven Neumann committed
24
25
26
27
#include <gtk/gtk.h>

#include "apptypes.h"

Elliot Lee's avatar
Elliot Lee committed
28
29
30
#include "appenv.h"
#include "channel.h"
#include "drawable.h"
31
#include "gdisplay.h"
Elliot Lee's avatar
Elliot Lee committed
32
33
34
#include "gimage_mask.h"
#include "layer.h"
#include "paint_funcs.h"
35
#include "parasitelist.h"
Elliot Lee's avatar
Elliot Lee committed
36
37
#include "temp_buf.h"
#include "undo.h"
38
#include "gimpsignal.h"
39
#include "gimppreviewcache.h"
Elliot Lee's avatar
Elliot Lee committed
40

41
#include "channel_pvt.h"
scott's avatar
scott committed
42
#include "tile.h"
43

44
45
46
#include "gimplut.h"
#include "lut_funcs.h"

47
48
49
50
51
#include "libgimp/gimpmath.h"

#include "libgimp/gimpintl.h"


52
53
enum
{
54
  REMOVED,
55
56
57
  LAST_SIGNAL
};

58
59
60
static void      gimp_channel_class_init (GimpChannelClass *klass);
static void      gimp_channel_init       (GimpChannel      *channel);
static void      gimp_channel_destroy    (GtkObject        *object);
61

62
63
64
65
static TempBuf * channel_preview_private (Channel *channel,
					  gint     width,
					  gint     height);

66
static guint channel_signals[LAST_SIGNAL] = { 0 };
67
68
69

static GimpDrawableClass *parent_class = NULL;

70
GtkType
71
gimp_channel_get_type (void)
72
{
73
  static GtkType channel_type = 0;
74
75
76
77
78
79
80
81
82
83

  if (!channel_type)
    {
      GtkTypeInfo channel_info =
      {
	"GimpChannel",
	sizeof (GimpChannel),
	sizeof (GimpChannelClass),
	(GtkClassInitFunc) gimp_channel_class_init,
	(GtkObjectInitFunc) gimp_channel_init,
84
85
        /* reserved_1 */ NULL,
	/* reserved_2 */ NULL,
86
	(GtkClassInitFunc) NULL,
87
88
      };

89
      channel_type = gtk_type_unique (GIMP_TYPE_DRAWABLE, &channel_info);
90
91
92
93
94
95
96
97
98
99
100
101
102
    }

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

103
104
105
106
  channel_signals[REMOVED] =
	  gimp_signal_new ("removed",
			   0, object_class->type, 0, gimp_sigtype_void);

107
108
109
110
111
112
113
114
115
  gtk_object_class_add_signals (object_class, channel_signals, LAST_SIGNAL);

  object_class->destroy = gimp_channel_destroy;
}

static void
gimp_channel_init (GimpChannel *channel)
{
}
Elliot Lee's avatar
Elliot Lee committed
116
117
118

/**************************/
/*  Function definitions  */
119
/**************************/
Elliot Lee's avatar
Elliot Lee committed
120
121

static void
122
123
channel_validate (TileManager *tm,
		  Tile        *tile)
Elliot Lee's avatar
Elliot Lee committed
124
125
{
  /*  Set the contents of the tile to empty  */
scott's avatar
scott committed
126
  memset (tile_data_pointer (tile, 0, 0), 
127
	  TRANSPARENT_OPACITY, tile_size (tile));
Elliot Lee's avatar
Elliot Lee committed
128
129
130
}

Channel *
131
132
133
134
135
136
channel_new (GimpImage    *gimage,
	     gint          width,
	     gint          height,
	     const gchar  *name,
	     gint          opacity,
	     const guchar *col)
Elliot Lee's avatar
Elliot Lee committed
137
138
{
  Channel * channel;
139
  gint i;
Elliot Lee's avatar
Elliot Lee committed
140

141
  channel = gtk_type_new (gimp_channel_get_type ());
Elliot Lee's avatar
Elliot Lee committed
142

143
  gimp_drawable_configure (GIMP_DRAWABLE (channel), 
144
			   gimage, width, height, GRAY_GIMAGE, name);
Elliot Lee's avatar
Elliot Lee committed
145
146
147
148

  /*  set the channel color and opacity  */
  for (i = 0; i < 3; i++)
    channel->col[i] = col[i];
149
150
151

  channel->opacity     = opacity;
  channel->show_masked = TRUE;
Elliot Lee's avatar
Elliot Lee committed
152
153

  /*  selection mask variables  */
154
155
156
157
158
159
  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
160
  channel->boundary_known = TRUE;
161
162
163
164
  channel->x1             = 0;
  channel->y1             = 0;
  channel->x2             = width;
  channel->y2             = height;
Elliot Lee's avatar
Elliot Lee committed
165
166
167
168

  return channel;
}

scott's avatar
scott committed
169
Channel *
170
channel_copy (const Channel *channel)
Elliot Lee's avatar
Elliot Lee committed
171
{
172
173
174
175
176
177
178
  gchar       *channel_name;
  Channel     *new_channel;
  PixelRegion  srcPR, destPR;
  gchar       *ext;
  gint         number;
  const gchar *name;
  gint         len;
Elliot Lee's avatar
Elliot Lee committed
179
180

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

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

  /*  copy the contents across channels  */
202
203
204
205
206
207
208
  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
209
210
  copy_region (&srcPR, &destPR);

211
  /* copy the parasites */
212
213
  GIMP_DRAWABLE (new_channel)->parasites =
    parasite_list_copy (GIMP_DRAWABLE (channel)->parasites);
214

Elliot Lee's avatar
Elliot Lee committed
215
216
217
218
219
220
  /*  free up the channel_name memory  */
  g_free (channel_name);

  return new_channel;
}

221
void
222
223
channel_set_name (Channel     *channel,
                  const gchar *name)
224
{
225
  gimp_drawable_set_name (GIMP_DRAWABLE (channel), name);
226
227
}

228
229
const gchar *
channel_get_name (const Channel *channel)
230
{
231
  return gimp_drawable_get_name (GIMP_DRAWABLE (channel));
232
233
}

234
void 
235
236
channel_set_color (Channel      *channel,
		   const guchar *color)
237
{
238
239
  gint i;

240
  if (color)
241
    {
242
243
      for (i = 0; i < 3; i++)
	channel->col[i] = color[i];
244
    }
245
}
246

247
248
const guchar *
channel_get_color (const Channel *channel)
249
{
250
  return GIMP_CHANNEL (channel)->col;
251
252
}
 
253
254
gint
channel_get_opacity (const Channel *channel)
255
256
257
258
{ 
  return channel->opacity;
}

259
void 
260
261
channel_set_opacity (Channel *channel,
		     gint     opacity)
262
263
{
  if (opacity >=0 && opacity <= 100)
264
    channel->opacity = (gint) (opacity * 255) / 100;
265
266
}

Elliot Lee's avatar
Elliot Lee committed
267
Channel *
268
channel_get_ID (gint ID)
Elliot Lee's avatar
Elliot Lee committed
269
{
270
  GimpDrawable *drawable;
271

272
  drawable = drawable_get_ID (ID);
273

274
275
276
277
  if (drawable && GIMP_IS_CHANNEL (drawable)) 
    return GIMP_CHANNEL (drawable);
  else
    return NULL;
Elliot Lee's avatar
Elliot Lee committed
278
279
280
281
282
}

void
channel_delete (Channel *channel)
{
283
284
285
286
287
288
  /*  Channels are normally deleted by removing them from the associated
      image. The only case where channel_delete() is useful is if you want
      to remove a floating channel object that has not been added to an
      image yet. We use gtk_object_sink() for this reason here.
   */
  gtk_object_sink (GTK_OBJECT (channel));
289
290
291
292
293
294
295
296
297
298
299
300
}

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
301
302
303
304
305
306
  /* free the segments?  */
  if (channel->segs_in)
    g_free (channel->segs_in);
  if (channel->segs_out)
    g_free (channel->segs_out);

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

311
312
313
314
315
316
/* The removed signal is sent out when the channel is no longer
 * associcated with an image.  It's needed because channels aren't
 * destroyed immediately, but kept around for undo purposes.  Connect
 * to the removed signal to update bits of UI that are tied to a
 * particular layer. */
void
317
channel_removed (Channel *channel)
318
319
320
321
322
323
324
{
  g_return_if_fail (channel != NULL);
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

  gtk_signal_emit (GTK_OBJECT (channel), channel_signals[REMOVED]);
}

Elliot Lee's avatar
Elliot Lee committed
325
void
326
327
328
channel_scale (Channel *channel,
	       gint     new_width,
	       gint     new_height)
Elliot Lee's avatar
Elliot Lee committed
329
{
330
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
331
332
333
334
335
336
  TileManager *new_tiles;

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

  /*  Update the old channel position  */
337
338
339
340
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
341
342

  /*  Configure the pixel regions  */
343
344
345
346
  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
347
348
349
350
351
352
353
354
355

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

  /*  Configure the new channel  */
359
360
361
  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
362
363
364
365
366

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

  /*  Update the new channel position  */
367
368
369
370
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
371
372
373
}

void
374
375
376
377
378
channel_resize (Channel *channel,
		gint     new_width,
		gint     new_height,
		gint     offx,
		gint     offy)
Elliot Lee's avatar
Elliot Lee committed
379
{
380
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
381
  TileManager *new_tiles;
382
383
384
385
  guchar       bg = 0;
  gint         clear;
  gint         w, h;
  gint         x1, y1, x2, y2;
Elliot Lee's avatar
Elliot Lee committed
386
387
388
389

  if (!new_width || !new_height)
    return;

390
391
392
393
  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
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
  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  */
420
421
422
423
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
424
425

  /*  Configure the pixel regions  */
426
427
  pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles,
		     x1, y1, w, h, FALSE);
Elliot Lee's avatar
Elliot Lee committed
428
429

  /*  Determine whether the new channel needs to be initially cleared  */
430
431
  if ((new_width  > GIMP_DRAWABLE (channel)->width) ||
      (new_height > GIMP_DRAWABLE (channel)->height) ||
Elliot Lee's avatar
Elliot Lee committed
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
      (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  */
453
  undo_push_channel_mod (GIMP_DRAWABLE (channel)->gimage, channel);
Elliot Lee's avatar
Elliot Lee committed
454
455

  /*  Configure the new channel  */
456
457
458
  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
459
460
461
462
463

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

  /*  update the new channel area  */
464
465
466
467
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
468
469
}

470
471
472
void            
channel_update (Channel *channel)
{
473
474
475
476
477
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
  gdisplays_flush ();
478
479
}

480
481
482
/**********************/
/*  access functions  */
/**********************/
Elliot Lee's avatar
Elliot Lee committed
483

484
gboolean
Elliot Lee's avatar
Elliot Lee committed
485
486
channel_toggle_visibility (Channel *channel)
{
487
  GIMP_DRAWABLE (channel)->visible = !GIMP_DRAWABLE (channel)->visible;
Elliot Lee's avatar
Elliot Lee committed
488

489
  return GIMP_DRAWABLE (channel)->visible;
Elliot Lee's avatar
Elliot Lee committed
490
491
}

492
493
494
495
TempBuf *
channel_preview (Channel *channel,
		 gint     width,
		 gint     height)
Elliot Lee's avatar
Elliot Lee committed
496
{
497
498
  /* Ok prime the cache with a large preview if the cache is invalid */
  if (! GIMP_DRAWABLE (channel)->preview_valid &&
499
500
      width  <= PREVIEW_CACHE_PRIME_WIDTH      &&
      height <= PREVIEW_CACHE_PRIME_HEIGHT     &&
501
502
503
504
505
506
507
508
509
510
511
512
513
514
      GIMP_DRAWABLE (channel)->gimage          &&
      GIMP_DRAWABLE (channel)->gimage->width  > PREVIEW_CACHE_PRIME_WIDTH   &&
      GIMP_DRAWABLE (channel)->gimage->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;
    }

515
  /* Second call - should NOT visit the tile cache... */
516
517
518
519
520
521
522
523
524
525
526
527
528
  return channel_preview_private (channel, width, height);
}

static TempBuf *
channel_preview_private (Channel *channel,
			 gint     width,
			 gint     height)
{
  MaskBuf     *preview_buf;
  PixelRegion  srcPR;
  PixelRegion  destPR;
  gint         subsample;
  TempBuf     *ret_buf;
Elliot Lee's avatar
Elliot Lee committed
529

530
531
532
  g_return_val_if_fail (channel != NULL, NULL);
  g_return_val_if_fail (GIMP_IS_CHANNEL (channel), NULL);
  
Elliot Lee's avatar
Elliot Lee committed
533
  /*  The easy way  */
534
535
536
537
  if (GIMP_DRAWABLE (channel)->preview_valid &&
      (ret_buf =
       gimp_preview_cache_get (& (GIMP_DRAWABLE (channel)->preview_cache),
			       width, height)))
538
539
540
    {
      return ret_buf;
    }
Elliot Lee's avatar
Elliot Lee committed
541
542
543
544
545
  /*  The hard way  */
  else
    {
      /*  calculate 'acceptable' subsample  */
      subsample = 1;
546
547
      if (width < 1) width = 1;
      if (height < 1) height = 1;
548
549
      while ((width  * (subsample + 1) * 2 < GIMP_DRAWABLE (channel)->width) &&
	     (height * (subsample + 1) * 2 < GIMP_DRAWABLE (channel)->height))
Elliot Lee's avatar
Elliot Lee committed
550
551
	subsample = subsample + 1;

552
553
554
555
      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
556
557

      preview_buf = mask_buf_new (width, height);
558
559
560
561
562
      destPR.bytes     = 1;
      destPR.x         = 0;
      destPR.y         = 0;
      destPR.w         = width;
      destPR.h         = height;
Elliot Lee's avatar
Elliot Lee committed
563
      destPR.rowstride = width;
564
      destPR.data      = mask_buf_data (preview_buf);
Elliot Lee's avatar
Elliot Lee committed
565
566
567

      subsample_region (&srcPR, &destPR, subsample);

568
569
570
571
572
573
      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);
574
      return preview_buf;
Elliot Lee's avatar
Elliot Lee committed
575
576
577
578
    }
}

void
579
channel_invalidate_previews (GimpImage *gimage)
Elliot Lee's avatar
Elliot Lee committed
580
{
581
582
  GSList  *tmp;
  Channel *channel;
583

Sven Neumann's avatar
Sven Neumann committed
584
585
  g_return_if_fail (gimage != NULL);

586
  tmp = gimage->channels;
Elliot Lee's avatar
Elliot Lee committed
587
588
589
590

  while (tmp)
    {
      channel = (Channel *) tmp->data;
591
      gimp_drawable_invalidate_preview (GIMP_DRAWABLE (channel), TRUE);
592
      tmp = g_slist_next (tmp);
Elliot Lee's avatar
Elliot Lee committed
593
594
595
    }
}

596
Tattoo
597
channel_get_tattoo (const Channel *channel)
598
{
599
  return gimp_drawable_get_tattoo (GIMP_DRAWABLE (channel));
600
601
}

602
void
603
604
channel_set_tattoo (Channel *channel, 
		    Tattoo   value)
605
{
606
  gimp_drawable_set_tattoo (GIMP_DRAWABLE (channel), value);
607
608
}

609
610
611
/******************************/
/*  selection mask functions  */
/******************************/
Elliot Lee's avatar
Elliot Lee committed
612
613

Channel *
614
channel_new_mask (GimpImage *gimage,
615
616
		  gint       width,
		  gint       height)
Elliot Lee's avatar
Elliot Lee committed
617
{
618
  guchar   black[3] = { 0, 0, 0 };
Elliot Lee's avatar
Elliot Lee committed
619
620
621
  Channel *new_channel;

  /*  Create the new channel  */
622
623
  new_channel = channel_new (gimage, width, height,
			     _("Selection Mask"), 127, black);
Elliot Lee's avatar
Elliot Lee committed
624
625

  /*  Set the validate procedure  */
626
627
  tile_manager_set_validate_proc (GIMP_DRAWABLE (new_channel)->tiles,
				  channel_validate);
Elliot Lee's avatar
Elliot Lee committed
628
629
630
631

  return new_channel;
}

632
633
634
635
636
637
638
639
640
641
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
642
{
643
  gint        x3, y3, x4, y4;
Elliot Lee's avatar
Elliot Lee committed
644
645
646
647
648
649
650
651
652
653
654
655
  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))
	{
656
657
	  pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles,
			     x3, y3, (x4 - x3), (y4 - y3), FALSE);
Elliot Lee's avatar
Elliot Lee committed
658
659
660
661
	  mask->segs_out = find_mask_boundary (&bPR, &mask->num_segs_out,
					       IgnoreBounds,
					       x1, y1,
					       x2, y2);
662
663
664
665
	  x1 = MAX (x1, x3);
	  y1 = MAX (y1, y3);
	  x2 = MIN (x2, x4);
	  y2 = MIN (y2, y4);
Elliot Lee's avatar
Elliot Lee committed
666
667
668

	  if (x2 > x1 && y2 > y1)
	    {
669
670
671
672
	      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
673
674
675
676
677
678
679
	      mask->segs_in =  find_mask_boundary (&bPR, &mask->num_segs_in,
						   WithinBounds,
						   x1, y1,
						   x2, y2);
	    }
	  else
	    {
680
	      mask->segs_in     = NULL;
Elliot Lee's avatar
Elliot Lee committed
681
682
683
684
685
	      mask->num_segs_in = 0;
	    }
	}
      else
	{
686
687
688
	  mask->segs_in      = NULL;
	  mask->segs_out     = NULL;
	  mask->num_segs_in  = 0;
Elliot Lee's avatar
Elliot Lee committed
689
690
691
692
693
	  mask->num_segs_out = 0;
	}
      mask->boundary_known = TRUE;
    }

694
695
696
  *segs_in      = mask->segs_in;
  *segs_out     = mask->segs_out;
  *num_segs_in  = mask->num_segs_in;
Elliot Lee's avatar
Elliot Lee committed
697
698
699
700
701
  *num_segs_out = mask->num_segs_out;

  return TRUE;
}

702
703
704
705
gint
channel_value (Channel *mask,
	       gint     x,
	       gint     y)
Elliot Lee's avatar
Elliot Lee committed
706
707
{
  Tile *tile;
708
  gint  val;
Elliot Lee's avatar
Elliot Lee committed
709
710
711
712
713
714
715
716
717
718
719

  /*  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
    {
720
721
      if (x < 0 || x >= GIMP_DRAWABLE (mask)->width ||
	  y < 0 || y >= GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
722
723
724
	return 0;
    }

725
726
  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));
727
  tile_release (tile, FALSE);
Elliot Lee's avatar
Elliot Lee committed
728
729
730
731

  return val;
}

732
733
734
735
736
737
gboolean
channel_bounds (Channel *mask,
		gint    *x1,
		gint    *y1,
		gint    *x2,
		gint    *y2)
Elliot Lee's avatar
Elliot Lee committed
738
{
739
740
741
742
743
744
745
  PixelRegion  maskPR;
  guchar      *data, *data1;
  gint         x, y;
  gint         ex, ey;
  gint         tx1, tx2, ty1, ty2;
  gint         minx, maxx;
  gpointer     pr;
746

Elliot Lee's avatar
Elliot Lee committed
747
748
749
750
751
752
753
754
  /*  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;

755
      return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
756
757
758
    }

  /*  go through and calculate the bounds  */
759
760
  tx1 = GIMP_DRAWABLE (mask)->width;
  ty1 = GIMP_DRAWABLE (mask)->height;
761
762
  tx2 = 0;
  ty2 = 0;
Elliot Lee's avatar
Elliot Lee committed
763

764
765
766
767
768
769
770
  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
771
    {
772
      data1 = data = maskPR.data;
Elliot Lee's avatar
Elliot Lee committed
773
774
      ex = maskPR.x + maskPR.w;
      ey = maskPR.y + maskPR.h;
775
776
      /* only check the pixels if this tile is not fully within the currently
	 computed bounds */
777
778
      if (maskPR.x < tx1 || ex > tx2 ||
	  maskPR.y < ty1 || ey > ty2)
779
        {
780
781
782
783
784
785
786
787
788
789
790
791
792
793
	  /* 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
794
	    for (y = maskPR.y; y < ey; y++, data1 += maskPR.rowstride)
Elliot Lee's avatar
Elliot Lee committed
795
	    {
796
	      for (x = maskPR.x, data = data1; x < ex; x++, data++)
797
		if (*data)
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
		{
		  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
813
814
815
816
	    }
	}
    }

817
818
  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
819

820
  if (tx1 == GIMP_DRAWABLE (mask)->width && ty1 == GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
821
822
    {
      mask->empty = TRUE;
823
824
825
826
      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
827
828
829
830
    }
  else
    {
      mask->empty = FALSE;
831
832
833
834
      mask->x1    = tx1;
      mask->y1    = ty1;
      mask->x2    = tx2;
      mask->y2    = ty2;
Elliot Lee's avatar
Elliot Lee committed
835
836
837
    }
  mask->bounds_known = TRUE;

838
839
840
841
842
  *x1 = tx1;
  *x2 = tx2;
  *y1 = ty1;
  *y2 = ty2;

843
  return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
844
845
}

846
gboolean
Elliot Lee's avatar
Elliot Lee committed
847
848
channel_is_empty (Channel *mask)
{
849
850
851
852
  PixelRegion  maskPR;
  guchar      *data;
  gint         x, y;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
853
854
855
856

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

857
858
859
860
861
862
863
  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
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
    {
      /*  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);

882
883
884
885
886
887
  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
888
  mask->boundary_known = TRUE;
889
890
891
892
  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
893
894
895
896
897

  return TRUE;
}

void
898
899
900
901
902
channel_add_segment (Channel *mask,
		     gint     x,
		     gint     y,
		     gint     width,
		     gint     value)
Elliot Lee's avatar
Elliot Lee committed
903
{
904
905
906
907
908
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
909
910
911
912

  /*  check horizontal extents...  */
  x2 = x + width;
  if (x2 < 0) x2 = 0;
913
  if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
914
  if (x < 0) x = 0;
915
  if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
916
917
918
  width = x2 - x;
  if (!width) return;

919
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
920
921
    return;

922
923
924
925
926
  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
927
928
929
930
931
932
933
934
935
936
937
938
939
940
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data + value;
	  if (val > 255)
	    val = 255;
	  *data++ = val;
	}
    }
}

void
941
942
943
944
945
channel_sub_segment (Channel *mask,
		     gint     x,
		     gint     y,
		     gint     width,
		     gint     value)
Elliot Lee's avatar
Elliot Lee committed
946
{
947
948
949
950
951
  PixelRegion  maskPR;
  guchar      *data;
  gint         val;
  gint         x2;
  gpointer     pr;
Elliot Lee's avatar
Elliot Lee committed
952
953
954
955

  /*  check horizontal extents...  */
  x2 = x + width;
  if (x2 < 0) x2 = 0;
956
  if (x2 > GIMP_DRAWABLE (mask)->width) x2 = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
957
  if (x < 0) x = 0;
958
  if (x > GIMP_DRAWABLE (mask)->width) x = GIMP_DRAWABLE (mask)->width;
Elliot Lee's avatar
Elliot Lee committed
959
960
961
  width = x2 - x;
  if (!width) return;

962
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
963
964
    return;

965
966
967
968
  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
969
970
971
972
973
974
975
976
977
978
979
980
981
982
    {
      data = maskPR.data;
      width = maskPR.w;
      while (width--)
	{
	  val = *data - value;
	  if (val < 0)
	    val = 0;
	  *data++ = val;
	}
    }
}

void
983
984
985
986
987
988
channel_combine_rect (Channel    *mask,
		      ChannelOps  op,
		      gint        x,
		      gint        y,
		      gint        w,
		      gint        h)
Elliot Lee's avatar
Elliot Lee committed
989
{
990
  gint        x2, y2;
Elliot Lee's avatar
Elliot Lee committed
991
  PixelRegion maskPR;
992
  guchar      color;
993

994
995
  y2 = y + h;
  x2 = x + w;
Elliot Lee's avatar
Elliot Lee committed
996

997
998
999
1000
  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
1001

1002
  if (x2 - x <= 0 || y2 - y <= 0)
Elliot Lee's avatar
Elliot Lee committed
1003
1004
    return;

1005
1006
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, x2 - x, y2 - y, TRUE);
1007
1008
1009
1010
  if (op == ADD  || op == REPLACE)
    color = 255;
  else
    color = 0;
1011
  color_region (&maskPR, &color);
Elliot Lee's avatar
Elliot Lee committed
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035

  /*  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;

1036
1037
1038
1039
  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
1040
1041
1042
}

void
1043
1044
1045
1046
1047
1048
channel_combine_ellipse (Channel    *mask,
			 ChannelOps  op,
			 gint        x,
			 gint        y,
			 gint        w,
			 gint        h,
1049
			 gboolean    antialias)
Elliot Lee's avatar
Elliot Lee committed
1050
{
1051
1052
1053
  gint   i, j;
  gint   x0, x1, x2;
  gint   val, last;
1054
1055
1056
1057
1058
1059
1060
1061
  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
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074

  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++)
    {
1075
      if (i >= 0 && i < GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
1076
1077
	{
	  /*  Non-antialiased code  */
1078
	  if (!antialias)
Elliot Lee's avatar
Elliot Lee committed
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
	    {
	      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
1093
1094
1095
		default:
		  g_warning ("Only ADD, REPLACE and SUB are valid for channel_combine!");
		  break;
Elliot Lee's avatar
Elliot Lee committed
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
		}
	    }
	  /*  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;
1125
		  
Elliot Lee's avatar
Elliot Lee committed
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
		  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
1136
1137
1138
			default:
			  g_warning ("Only ADD, REPLACE and SUB are valid for channel_combine!");
			  break;
Elliot Lee's avatar
Elliot Lee committed
1139
1140
1141
1142
1143
1144
1145
			}
		    }

		  if (last != val)
		    {
		      x0 = j;
		      last = val;
1146
1147
1148
1149
		      /* 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
1150
1151
1152
1153
1154
1155
1156
1157
1158
		    }
		}

	      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
1159
1160
		  else
		    g_warning ("Only ADD, REPLACE and SUB are valid for channel_combine!");
Elliot Lee's avatar
Elliot Lee committed
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
		}
	    }

	}
    }

  /*  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;

1190
1191
1192
1193
  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
1194
1195
}

1196
static void
1197
channel_combine_sub_region_add (void        *unused,
1198
1199
1200
				PixelRegion *srcPR,
				PixelRegion *destPR)
{
1201
  guchar *src, *dest;
1202
  gint    x, y, val;
1203
1204

  src  = srcPR->data;
1205
  dest = destPR->data;
1206

1207
1208
  for (y = 0; y < srcPR->h; y++)
    {
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
      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;
1219
1220
1221
1222
    }
}

static void
1223
channel_combine_sub_region_sub (void        *unused,
1224
1225
1226
				PixelRegion *srcPR,
				PixelRegion *destPR)
{
1227
  guchar *src, *dest;
1228
  gint    x, y;
1229
1230

  src  = srcPR->data;
1231
  dest = destPR->data;
1232

1233
1234
  for (y = 0; y < srcPR->h; y++)
    {
1235
1236
1237
1238
1239
1240
1241
1242
1243
      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;
1244
1245
1246
1247
    }
}

static void
1248
channel_combine_sub_region_intersect (void        *unused,
1249
1250
1251
				      PixelRegion *srcPR,
				      PixelRegion *destPR)
{
1252
  guchar *src, *dest;