gimpchannel.c 41.3 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
#include "appenv.h"
29
#include "boundary.h"
Elliot Lee's avatar
Elliot Lee committed
30
31
#include "channel.h"
#include "drawable.h"
32
#include "gdisplay.h"
33
#include "gimpimage.h"
Elliot Lee's avatar
Elliot Lee committed
34
#include "gimage_mask.h"
35
36
#include "gimppreviewcache.h"
#include "gimplut.h"
Elliot Lee's avatar
Elliot Lee committed
37
38
#include "layer.h"
#include "paint_funcs.h"
39
#include "parasitelist.h"
40
41
42
#include "pixel_processor.h"
#include "pixel_region.h"
#include "lut_funcs.h"
Elliot Lee's avatar
Elliot Lee committed
43
#include "temp_buf.h"
scott's avatar
scott committed
44
#include "tile.h"
45
46
#include "tile_manager.h"
#include "undo.h"
47

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

#include "libgimp/gimpintl.h"


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

57
58
59
60
static TempBuf * channel_preview_private (Channel *channel,
					  gint     width,
					  gint     height);

61
62
63

static GimpDrawableClass *parent_class = NULL;

64

65
GtkType
66
gimp_channel_get_type (void)
67
{
68
  static GtkType channel_type = 0;
69
70
71
72
73
74
75
76
77
78

  if (!channel_type)
    {
      GtkTypeInfo channel_info =
      {
	"GimpChannel",
	sizeof (GimpChannel),
	sizeof (GimpChannelClass),
	(GtkClassInitFunc) gimp_channel_class_init,
	(GtkObjectInitFunc) gimp_channel_init,
79
80
        /* reserved_1 */ NULL,
	/* reserved_2 */ NULL,
81
	(GtkClassInitFunc) NULL,
82
83
      };

84
      channel_type = gtk_type_unique (GIMP_TYPE_DRAWABLE, &channel_info);
85
86
87
88
89
90
    }

  return channel_type;
}

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

95
96
97
  object_class = (GtkObjectClass *) klass;

  parent_class = gtk_type_class (GIMP_TYPE_DRAWABLE);
98
99
100
101
102
103
104
105

  object_class->destroy = gimp_channel_destroy;
}

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

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

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

Channel *
121
122
123
124
125
channel_new (GimpImage     *gimage,
	     gint           width,
	     gint           height,
	     const gchar   *name,
	     const GimpRGB *color)
Elliot Lee's avatar
Elliot Lee committed
126
{
127
128
129
  Channel *channel;

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

131
  channel = gtk_type_new (GIMP_TYPE_CHANNEL);
Elliot Lee's avatar
Elliot Lee committed
132

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

  /*  set the channel color and opacity  */
137
  channel->color = *color;
138
139

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

  /*  selection mask variables  */
142
143
144
145
146
147
  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
148
  channel->boundary_known = TRUE;
149
150
151
152
  channel->x1             = 0;
  channel->y1             = 0;
  channel->x2             = width;
  channel->y2             = height;
Elliot Lee's avatar
Elliot Lee committed
153
154
155
156

  return channel;
}

scott's avatar
scott committed
157
Channel *
158
channel_copy (const Channel *channel)
Elliot Lee's avatar
Elliot Lee committed
159
{
160
161
162
163
164
165
166
  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
167
168

  /*  formulate the new channel name  */
169
170
  name = gimp_object_get_name (GIMP_OBJECT (channel));

171
  ext = strrchr (name, '#');
172
  len = strlen (_("copy"));
173
174
175
176
  if ((strlen (name) >= len &&
       strcmp (&name[strlen (name) - len], _("copy")) == 0) ||
      (ext && (number = atoi (ext + 1)) > 0 && 
       ((int)(log10 (number) + 1)) == strlen (ext + 1)))
177
178
    /* don't have redundant "copy"s */
    channel_name = g_strdup (name);
179
  else
180
    channel_name = g_strdup_printf (_("%s copy"), name);
Elliot Lee's avatar
Elliot Lee committed
181
182

  /*  allocate a new channel object  */
183
184
185
  new_channel = channel_new (GIMP_DRAWABLE (channel)->gimage, 
			     GIMP_DRAWABLE (channel)->width, 
			     GIMP_DRAWABLE (channel)->height, 
186
187
			     channel_name,
			     &channel->color);
188
  GIMP_DRAWABLE (new_channel)->visible = GIMP_DRAWABLE (channel)->visible;
Elliot Lee's avatar
Elliot Lee committed
189
190
191
  new_channel->show_masked = channel->show_masked;

  /*  copy the contents across channels  */
192
193
194
195
196
197
198
  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
199
200
  copy_region (&srcPR, &destPR);

201
  /* copy the parasites */
202
203
  GIMP_DRAWABLE (new_channel)->parasites =
    parasite_list_copy (GIMP_DRAWABLE (channel)->parasites);
204

Elliot Lee's avatar
Elliot Lee committed
205
206
207
208
209
210
  /*  free up the channel_name memory  */
  g_free (channel_name);

  return new_channel;
}

211
void
212
213
channel_set_name (Channel     *channel,
                  const gchar *name)
214
{
215
  gimp_object_set_name (GIMP_OBJECT (channel), name);
216
217
}

218
219
const gchar *
channel_get_name (const Channel *channel)
220
{
221
  return gimp_object_get_name (GIMP_OBJECT (channel));
222
223
}

224
void 
225
226
channel_set_color (Channel       *channel,
		   const GimpRGB *color)
227
{
228
229
230
  g_return_if_fail (channel != NULL);
  g_return_if_fail (GIMP_IS_CHANNEL (channel));
  g_return_if_fail (color != NULL);
231

232
  channel->color = *color;
233
}
234

235
const GimpRGB *
236
channel_get_color (const Channel *channel)
237
{
238
239
240
241
  g_return_val_if_fail (channel != NULL, NULL);
  g_return_val_if_fail (GIMP_IS_CHANNEL (channel), NULL);

  return &channel->color;
242
243
}
 
244
245
gint
channel_get_opacity (const Channel *channel)
246
247
248
249
250
{
  g_return_val_if_fail (channel != NULL, 0);
  g_return_val_if_fail (GIMP_IS_CHANNEL (channel), 0);

  return (gint) (channel->color.a * 100.999);
251
252
}

253
void 
254
255
channel_set_opacity (Channel *channel,
		     gint     opacity)
256
{
257
258
259
260
261
262
  g_return_if_fail (channel != NULL);
  g_return_if_fail (GIMP_IS_CHANNEL (channel));

  opacity = CLAMP (opacity, 0, 100);

  channel->color.a = opacity / 100.0;
263
264
}

Elliot Lee's avatar
Elliot Lee committed
265
266
267
void
channel_delete (Channel *channel)
{
268
269
270
271
272
273
  /*  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));
274
275
276
277
278
279
280
281
282
283
284
285
}

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
286
287
288
289
290
291
  /* free the segments?  */
  if (channel->segs_in)
    g_free (channel->segs_in);
  if (channel->segs_out)
    g_free (channel->segs_out);

292
  if (GTK_OBJECT_CLASS (parent_class)->destroy)
293
    GTK_OBJECT_CLASS (parent_class)->destroy (object);
Elliot Lee's avatar
Elliot Lee committed
294
295
296
}

void
297
298
299
channel_scale (Channel *channel,
	       gint     new_width,
	       gint     new_height)
Elliot Lee's avatar
Elliot Lee committed
300
{
301
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
302
303
304
305
306
307
  TileManager *new_tiles;

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

  /*  Update the old channel position  */
308
309
310
311
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
Elliot Lee's avatar
Elliot Lee committed
312
313

  /*  Configure the pixel regions  */
314
315
316
317
  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
318
319
320
321
322
323
324
325
326

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

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

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

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

void
345
346
347
348
349
channel_resize (Channel *channel,
		gint     new_width,
		gint     new_height,
		gint     offx,
		gint     offy)
Elliot Lee's avatar
Elliot Lee committed
350
{
351
  PixelRegion  srcPR, destPR;
Elliot Lee's avatar
Elliot Lee committed
352
  TileManager *new_tiles;
353
354
355
356
  guchar       bg = 0;
  gint         clear;
  gint         w, h;
  gint         x1, y1, x2, y2;
Elliot Lee's avatar
Elliot Lee committed
357
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  */
391
392
393
394
  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  */
435
436
437
438
  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
void            
channel_update (Channel *channel)
{
444
445
446
447
448
  drawable_update (GIMP_DRAWABLE (channel),
		   0, 0,
		   GIMP_DRAWABLE (channel)->width,
		   GIMP_DRAWABLE (channel)->height);
  gdisplays_flush ();
449
450
}

451
452
453
/**********************/
/*  access functions  */
/**********************/
Elliot Lee's avatar
Elliot Lee committed
454

455
gboolean
Elliot Lee's avatar
Elliot Lee committed
456
457
channel_toggle_visibility (Channel *channel)
{
458
  GIMP_DRAWABLE (channel)->visible = !GIMP_DRAWABLE (channel)->visible;
Elliot Lee's avatar
Elliot Lee committed
459

460
  return GIMP_DRAWABLE (channel)->visible;
Elliot Lee's avatar
Elliot Lee committed
461
462
}

463
464
465
466
TempBuf *
channel_preview (Channel *channel,
		 gint     width,
		 gint     height)
Elliot Lee's avatar
Elliot Lee committed
467
{
468
469
  /* Ok prime the cache with a large preview if the cache is invalid */
  if (! GIMP_DRAWABLE (channel)->preview_valid &&
470
471
      width  <= PREVIEW_CACHE_PRIME_WIDTH      &&
      height <= PREVIEW_CACHE_PRIME_HEIGHT     &&
472
473
474
475
476
477
478
479
480
481
482
483
484
485
      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;
    }

486
  /* Second call - should NOT visit the tile cache... */
487
488
489
490
491
492
493
494
495
496
497
498
499
  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
500

501
502
503
  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
504
  /*  The easy way  */
505
506
507
508
  if (GIMP_DRAWABLE (channel)->preview_valid &&
      (ret_buf =
       gimp_preview_cache_get (& (GIMP_DRAWABLE (channel)->preview_cache),
			       width, height)))
509
510
511
    {
      return ret_buf;
    }
Elliot Lee's avatar
Elliot Lee committed
512
513
514
515
516
  /*  The hard way  */
  else
    {
      /*  calculate 'acceptable' subsample  */
      subsample = 1;
517
518
      if (width < 1) width = 1;
      if (height < 1) height = 1;
519
520
      while ((width  * (subsample + 1) * 2 < GIMP_DRAWABLE (channel)->width) &&
	     (height * (subsample + 1) * 2 < GIMP_DRAWABLE (channel)->height))
Elliot Lee's avatar
Elliot Lee committed
521
522
	subsample = subsample + 1;

523
524
525
526
      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
527
528

      preview_buf = mask_buf_new (width, height);
529
530
531
532
533
      destPR.bytes     = 1;
      destPR.x         = 0;
      destPR.y         = 0;
      destPR.w         = width;
      destPR.h         = height;
Elliot Lee's avatar
Elliot Lee committed
534
      destPR.rowstride = width;
535
      destPR.data      = mask_buf_data (preview_buf);
Elliot Lee's avatar
Elliot Lee committed
536
537
538

      subsample_region (&srcPR, &destPR, subsample);

539
540
541
542
543
544
      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);
545
      return preview_buf;
Elliot Lee's avatar
Elliot Lee committed
546
547
548
549
    }
}

void
550
channel_invalidate_previews (GimpImage *gimage)
Elliot Lee's avatar
Elliot Lee committed
551
{
552
553
  GSList  *tmp;
  Channel *channel;
554

Sven Neumann's avatar
Sven Neumann committed
555
556
  g_return_if_fail (gimage != NULL);

557
  tmp = gimage->channels;
Elliot Lee's avatar
Elliot Lee committed
558
559
560
561

  while (tmp)
    {
      channel = (Channel *) tmp->data;
562
      gimp_drawable_invalidate_preview (GIMP_DRAWABLE (channel), TRUE);
563
      tmp = g_slist_next (tmp);
Elliot Lee's avatar
Elliot Lee committed
564
565
566
    }
}

567
Tattoo
568
channel_get_tattoo (const Channel *channel)
569
{
570
  return gimp_drawable_get_tattoo (GIMP_DRAWABLE (channel));
571
572
}

573
void
574
575
channel_set_tattoo (Channel *channel, 
		    Tattoo   value)
576
{
577
  gimp_drawable_set_tattoo (GIMP_DRAWABLE (channel), value);
578
579
}

580
581
582
/******************************/
/*  selection mask functions  */
/******************************/
Elliot Lee's avatar
Elliot Lee committed
583
584

Channel *
585
channel_new_mask (GimpImage *gimage,
586
587
		  gint       width,
		  gint       height)
Elliot Lee's avatar
Elliot Lee committed
588
{
589
  GimpRGB  black = { 0.0, 0.0, 0.0, 0.5 };
Elliot Lee's avatar
Elliot Lee committed
590
591
592
  Channel *new_channel;

  /*  Create the new channel  */
593
  new_channel = channel_new (gimage, width, height,
594
			     _("Selection Mask"), &black);
Elliot Lee's avatar
Elliot Lee committed
595
596

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

  return new_channel;
}

603
604
605
606
607
608
609
610
611
612
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
613
{
614
  gint        x3, y3, x4, y4;
Elliot Lee's avatar
Elliot Lee committed
615
616
617
618
619
620
621
622
623
624
625
626
  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))
	{
627
628
	  pixel_region_init (&bPR, GIMP_DRAWABLE (mask)->tiles,
			     x3, y3, (x4 - x3), (y4 - y3), FALSE);
Elliot Lee's avatar
Elliot Lee committed
629
630
631
632
	  mask->segs_out = find_mask_boundary (&bPR, &mask->num_segs_out,
					       IgnoreBounds,
					       x1, y1,
					       x2, y2);
633
634
635
636
	  x1 = MAX (x1, x3);
	  y1 = MAX (y1, y3);
	  x2 = MIN (x2, x4);
	  y2 = MIN (y2, y4);
Elliot Lee's avatar
Elliot Lee committed
637
638
639

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

665
666
667
  *segs_in      = mask->segs_in;
  *segs_out     = mask->segs_out;
  *num_segs_in  = mask->num_segs_in;
Elliot Lee's avatar
Elliot Lee committed
668
669
670
671
672
  *num_segs_out = mask->num_segs_out;

  return TRUE;
}

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

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

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

  return val;
}

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

Elliot Lee's avatar
Elliot Lee committed
718
719
720
721
722
723
724
725
  /*  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;

726
      return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
727
728
729
    }

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

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

788
789
  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
790

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

809
810
811
812
813
  *x1 = tx1;
  *x2 = tx2;
  *y1 = ty1;
  *y2 = ty2;

814
  return !mask->empty;
Elliot Lee's avatar
Elliot Lee committed
815
816
}

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

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

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

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

  return TRUE;
}

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

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

890
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
891
892
    return;

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

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

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

933
  if (y < 0 || y > GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
934
935
    return;

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

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

965
966
  y2 = y + h;
  x2 = x + w;
Elliot Lee's avatar
Elliot Lee committed
967

968
969
970
971
  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
972

973
  if (x2 - x <= 0 || y2 - y <= 0)
Elliot Lee's avatar
Elliot Lee committed
974
975
    return;

976
977
  pixel_region_init (&maskPR, GIMP_DRAWABLE (mask)->tiles,
		     x, y, x2 - x, y2 - y, TRUE);
978
  if (op == CHANNEL_OP_ADD  || op == CHANNEL_OP_REPLACE)
979
980
981
    color = 255;
  else
    color = 0;
982
  color_region (&maskPR, &color);
Elliot Lee's avatar
Elliot Lee committed
983
984

  /*  Determine new boundary  */
985
  if (mask->bounds_known && (op == CHANNEL_OP_ADD) && !mask->empty)
Elliot Lee's avatar
Elliot Lee committed
986
987
988
989
990
991
992
993
994
995
    {
      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);
    }
996
  else if (op == CHANNEL_OP_REPLACE || mask->empty)
Elliot Lee's avatar
Elliot Lee committed
997
998
999
1000
1001
1002
1003
1004
1005
1006
    {
      mask->empty = FALSE;
      mask->x1 = x;
      mask->y1 = y;
      mask->x2 = x + w;
      mask->y2 = y + h;
    }
  else
    mask->bounds_known = FALSE;

1007
1008
1009
1010
  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
1011
1012
1013
}

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

  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++)
    {
1046
      if (i >= 0 && i < GIMP_DRAWABLE (mask)->height)
Elliot Lee's avatar
Elliot Lee committed
1047
1048
	{
	  /*  Non-antialiased code  */
1049
	  if (!antialias)
Elliot Lee's avatar
Elliot Lee committed
1050
1051
1052
1053
1054
1055
1056
1057
	    {
	      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)
		{
1058
1059
		case CHANNEL_OP_ADD:
		case CHANNEL_OP_REPLACE:
Elliot Lee's avatar
Elliot Lee committed
1060
1061
		  channel_add_segment (mask, x1, i, (x2 - x1), 255);
		  break;
1062
		case CHANNEL_OP_SUB:
Elliot Lee's avatar
Elliot Lee committed
1063
1064
		  channel_sub_segment (mask, x1, i, (x2 - x1), 255);
		  break;
Sven Neumann's avatar
Sven Neumann committed
1065
1066
1067
		default:
		  g_warning ("Only ADD, REPLACE and SUB are valid for channel_combine!");
		  break;
Elliot Lee's avatar
Elliot Lee committed
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
1093
1094
1095
1096
		}
	    }
	  /*  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;
1097
		  
Elliot Lee's avatar
Elliot Lee committed
1098
1099
1100
1101
		  if (last != val && last)
		    {
		      switch (op)
			{
1102
1103
			case CHANNEL_OP_ADD:
			case CHANNEL_OP_REPLACE:
Elliot Lee's avatar
Elliot Lee committed
1104
1105
			  channel_add_segment (mask, x0, i, j - x0, last);
			  break;
1106
			case CHANNEL_OP_SUB:
Elliot Lee's avatar
Elliot Lee committed
1107
1108
			  channel_sub_segment (mask, x0, i, j - x0, last);
			  break;
Sven Neumann's avatar
Sven Neumann committed
1109
1110
1111
			default:
			  g_warning ("Only ADD, REPLACE and SUB are valid for channel_combine!");
			  break;
Elliot Lee's avatar
Elliot Lee committed
1112
1113
1114
1115
1116
1117
1118
			}
		    }

		  if (last != val)
		    {
		      x0 = j;
		      last = val;
1119
1120
1121
1122
		      /* 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
1123
1124
1125
1126
1127
		    }
		}

	      if (last)
		{
1128
		  if (op == CHANNEL_OP_ADD || op == CHANNEL_OP_REPLACE)
Elliot Lee's avatar
Elliot Lee committed
1129
		    channel_add_segment (mask, x0, i, j - x0, last);
1130
		  else if (op == CHANNEL_OP_SUB)
Elliot Lee's avatar
Elliot Lee committed
1131
		    channel_sub_segment (mask, x0, i, j - x0, last);
Sven Neumann's avatar
Sven Neumann committed
1132
1133
		  else
		    g_warning ("Only ADD, REPLACE and SUB are valid for channel_combine!");
Elliot Lee's avatar
Elliot Lee committed
1134
1135
1136
1137
1138
1139
1140
		}
	    }

	}
    }

  /*  Determine new boundary  */