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

19 20
#include "config.h"

21
#include <gtk/gtk.h>
Sven Neumann's avatar
Sven Neumann committed
22 23 24

#include "apptypes.h"

25
#include "tools/gimppainttool.h"
26 27 28 29
#include "tools/paint_options.h"
#include "tools/tool.h"
#include "tools/tool_manager.h"

30
#include "boundary.h"
Elliot Lee's avatar
Elliot Lee committed
31 32 33
#include "drawable.h"
#include "floating_sel.h"
#include "gdisplay.h"
34
#include "gimage_mask.h"
35
#include "gimpchannel.h"
36
#include "gimpimage.h"
37
#include "gimplayer.h"
38
#include "gimplayermask.h"
39
#include "gimprc.h"
40 41 42 43
#include "paint_funcs.h"
#include "pixel_region.h"
#include "tile_manager.h"
#include "undo.h"
44

45 46
#include "pdb/procedural_db.h"

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


50 51
/*  local variables  */
static int gimage_mask_stroking = FALSE;
Elliot Lee's avatar
Elliot Lee committed
52 53

/*  functions  */
54
gboolean
55 56 57
gimage_mask_boundary (GImage    *gimage,
		      BoundSeg **segs_in,
		      BoundSeg **segs_out,
58 59
		      gint      *num_segs_in,
		      gint      *num_segs_out)
Elliot Lee's avatar
Elliot Lee committed
60
{
61
  GimpDrawable *d;
62
  GimpLayer    *layer;
63 64
  gint          x1, y1;
  gint          x2, y2;
Elliot Lee's avatar
Elliot Lee committed
65

66
  if ((layer = gimp_image_floating_sel (gimage)))
Elliot Lee's avatar
Elliot Lee committed
67 68 69 70 71 72 73 74 75 76 77
    {
      /*  If there is a floating selection, then
       *  we need to do some slightly different boundaries.
       *  Instead of inside and outside boundaries being defined
       *  by the extents of the layer, the inside boundary (the one
       *  that actually marches and is black/white) is the boundary of
       *  the floating selection.  The outside boundary (doesn't move,
       *  is black/gray) is defined as the normal selection mask
       */

      /*  Find the selection mask boundary  */
78 79 80 81
      gimp_channel_boundary (gimp_image_get_mask (gimage),
			     segs_in, segs_out,
			     num_segs_in, num_segs_out,
			     0, 0, 0, 0);
Elliot Lee's avatar
Elliot Lee committed
82 83 84 85 86 87 88

      /*  Find the floating selection boundary  */
      *segs_in = floating_sel_boundary (layer, num_segs_in);

      return TRUE;
    }
  /*  Otherwise, return the boundary...if a channel is active  */
89
  else if ((d = gimp_image_active_drawable (gimage)) &&
90
	   GIMP_IS_CHANNEL (d))
Elliot Lee's avatar
Elliot Lee committed
91
    {
92 93 94 95
      return gimp_channel_boundary (gimp_image_get_mask (gimage),
				    segs_in, segs_out,
				    num_segs_in, num_segs_out,
				    0, 0, gimage->width, gimage->height);
Elliot Lee's avatar
Elliot Lee committed
96
    }
97
  /* if a layer is active, we return multiple boundaries based on the extents */
98
  else if ((layer = gimp_image_get_active_layer (gimage)))
Elliot Lee's avatar
Elliot Lee committed
99
    {
100 101
      gint off_x, off_y;

102
      gimp_drawable_offsets (GIMP_DRAWABLE(layer), &off_x, &off_y);
103 104
      x1 = CLAMP (off_x, 0, gimage->width);
      y1 = CLAMP (off_y, 0, gimage->height);
105
      x2 = CLAMP (off_x + gimp_drawable_width (GIMP_DRAWABLE(layer)), 0,
106
		  gimage->width);
107
      y2 = CLAMP (off_y + gimp_drawable_height (GIMP_DRAWABLE(layer)), 0,
108
		  gimage->height);
Elliot Lee's avatar
Elliot Lee committed
109

110 111 112 113
      return gimp_channel_boundary (gimp_image_get_mask (gimage),
				    segs_in, segs_out,
				    num_segs_in, num_segs_out,
				    x1, y1, x2, y2);
Elliot Lee's avatar
Elliot Lee committed
114 115 116 117 118 119 120 121 122 123 124 125
    }
  else
    {
      *segs_in = NULL;
      *segs_out = NULL;
      *num_segs_in = 0;
      *num_segs_out = 0;
      return FALSE;
    }
}


126
gboolean
127
gimage_mask_bounds (GImage *gimage,
128 129 130 131
		    gint   *x1,
		    gint   *y1,
		    gint   *x2,
		    gint   *y2)
Elliot Lee's avatar
Elliot Lee committed
132
{
133
  return gimp_channel_bounds (gimp_image_get_mask (gimage), x1, y1, x2, y2);
Elliot Lee's avatar
Elliot Lee committed
134 135 136 137
}


void
138
gimage_mask_invalidate (GImage *gimage)
Elliot Lee's avatar
Elliot Lee committed
139
{
140 141
  GimpLayer   *layer;
  GimpChannel *mask;
Elliot Lee's avatar
Elliot Lee committed
142 143

  /*  Turn the current selection off  */
144
  gdisplays_selection_visibility (gimage, SelectionOff);
Elliot Lee's avatar
Elliot Lee committed
145

146
  mask = gimp_image_get_mask (gimage);
Elliot Lee's avatar
Elliot Lee committed
147 148 149 150 151 152
  mask->boundary_known = FALSE;

  /*  If there is a floating selection, update it's area...
   *  we need to do this since this selection mask can act as an additional
   *  mask in the composition of the floating selection
   */
153
  layer = gimp_image_get_active_layer (gimage);
154

155
  if (layer && gimp_layer_is_floating_sel (layer))
156 157 158 159
    drawable_update (GIMP_DRAWABLE (layer),
		     0, 0,
		     GIMP_DRAWABLE (layer)->width,
		     GIMP_DRAWABLE (layer)->height);
Elliot Lee's avatar
Elliot Lee committed
160 161 162
}


163
gint
164
gimage_mask_value (GImage *gimage,
165 166
		   gint    x,
		   gint    y)
Elliot Lee's avatar
Elliot Lee committed
167
{
168
  return gimp_channel_value (gimp_image_get_mask (gimage), x, y);
Elliot Lee's avatar
Elliot Lee committed
169 170 171
}


172
gboolean
173
gimage_mask_is_empty (GImage *gimage)
Elliot Lee's avatar
Elliot Lee committed
174 175 176 177 178 179 180 181
{
  /*  in order to allow stroking of selections, we need to pretend here
   *  that the selection mask is empty so that it doesn't mask the paint
   *  during the stroke operation.
   */
  if (gimage_mask_stroking)
    return TRUE;
  else
182
    return gimp_channel_is_empty (gimp_image_get_mask (gimage));
Elliot Lee's avatar
Elliot Lee committed
183 184 185 186
}


void
187
gimage_mask_translate (GImage *gimage,
188 189
		       gint    off_x,
		       gint    off_y)
Elliot Lee's avatar
Elliot Lee committed
190
{
191
  gimp_channel_translate (gimp_image_get_mask (gimage), off_x, off_y);
Elliot Lee's avatar
Elliot Lee committed
192 193 194 195
}


TileManager *
196 197
gimage_mask_extract (GImage       *gimage,
		     GimpDrawable *drawable,
198
		     gboolean      cut_gimage,
199 200
		     gboolean      keep_indexed,
		     gboolean      add_alpha)
Elliot Lee's avatar
Elliot Lee committed
201
{
202 203 204 205 206 207 208 209 210
  TileManager *tiles;
  GimpChannel *sel_mask;
  PixelRegion  srcPR, destPR, maskPR;
  guchar       bg[MAX_CHANNELS];
  gint         bytes, type;
  gint         x1, y1;
  gint         x2, y2;
  gint         off_x, off_y;
  gboolean     non_empty;
Elliot Lee's avatar
Elliot Lee committed
211

212 213 214
  if (!drawable) 
    return NULL;

Elliot Lee's avatar
Elliot Lee committed
215 216 217 218 219 220
  /*  If there are no bounds, then just extract the entire image
   *  This may not be the correct behavior, but after getting rid
   *  of floating selections, it's still tempting to "cut" or "copy"
   *  a small layer and expect it to work, even though there is no
   *  actual selection mask
   */
221
  non_empty = gimp_drawable_mask_bounds (drawable, &x1, &y1, &x2, &y2);
Elliot Lee's avatar
Elliot Lee committed
222 223
  if (non_empty && (!(x2 - x1) || !(y2 - y1)))
    {
224 225
      g_message (_("Unable to cut/copy because the selected\n"
		   "region is empty."));
Elliot Lee's avatar
Elliot Lee committed
226 227 228 229
      return NULL;
    }

  /*  How many bytes in the temp buffer?  */
230
  switch (gimp_drawable_type (drawable))
Elliot Lee's avatar
Elliot Lee committed
231 232
    {
    case RGB_GIMAGE: case RGBA_GIMAGE:
233 234 235
      bytes = add_alpha ? 4 : drawable->bytes;
      type = RGB;
      break;
Elliot Lee's avatar
Elliot Lee committed
236
    case GRAY_GIMAGE: case GRAYA_GIMAGE:
237 238 239
      bytes = add_alpha ? 2 : drawable->bytes;
      type = GRAY;
      break;
Elliot Lee's avatar
Elliot Lee committed
240 241 242
    case INDEXED_GIMAGE: case INDEXEDA_GIMAGE:
      if (keep_indexed)
	{
243 244
	  bytes = add_alpha ? 2 : drawable->bytes;
	  type = GRAY;
Elliot Lee's avatar
Elliot Lee committed
245 246 247
	}
      else
	{
248 249
	  bytes = add_alpha ? 4 : drawable->bytes;
	  type = INDEXED;
Elliot Lee's avatar
Elliot Lee committed
250 251 252 253 254 255 256 257 258 259
	}
      break;
    default:
      bytes = 3;
      type  = RGB;
      break;
    }

  /*  get the selection mask */
  if (non_empty)
260
    sel_mask = gimp_image_get_mask (gimage);
Elliot Lee's avatar
Elliot Lee committed
261 262 263
  else
    sel_mask = NULL;

264
  gimp_image_get_background (gimage, drawable, bg);
Elliot Lee's avatar
Elliot Lee committed
265

266 267 268
  /*  If a cut was specified, and the selection mask is not empty,
   *  push an undo
   */
Elliot Lee's avatar
Elliot Lee committed
269
  if (cut_gimage && non_empty)
270
    drawable_apply_image (drawable, x1, y1, x2, y2, NULL, FALSE);
Elliot Lee's avatar
Elliot Lee committed
271

272
  gimp_drawable_offsets (drawable, &off_x, &off_y);
Elliot Lee's avatar
Elliot Lee committed
273 274 275

  /*  Allocate the temp buffer  */
  tiles = tile_manager_new ((x2 - x1), (y2 - y1), bytes);
276
  tile_manager_set_offsets (tiles, x1 + off_x, y1 + off_y);
Elliot Lee's avatar
Elliot Lee committed
277 278

  /* configure the pixel regions  */
279
  pixel_region_init (&srcPR, gimp_drawable_data (drawable),
280
		     x1, y1, (x2 - x1), (y2 - y1), cut_gimage);
Elliot Lee's avatar
Elliot Lee committed
281 282 283 284 285
  pixel_region_init (&destPR, tiles, 0, 0, (x2 - x1), (y2 - y1), TRUE);

  /*  If there is a selection, extract from it  */
  if (non_empty)
    {
286 287 288
      pixel_region_init (&maskPR, GIMP_DRAWABLE(sel_mask)->tiles,
			 (x1 + off_x), (y1 + off_y), (x2 - x1), (y2 - y1),
			 FALSE);
Elliot Lee's avatar
Elliot Lee committed
289

290 291 292 293
      extract_from_region (&srcPR, &destPR, &maskPR,
			   gimp_drawable_cmap (drawable),
			   bg, type,
			   gimp_drawable_has_alpha (drawable), cut_gimage);
Elliot Lee's avatar
Elliot Lee committed
294 295 296 297

      if (cut_gimage)
	{
	  /*  Clear the region  */
298
	  gimp_channel_clear (gimp_image_get_mask (gimage));
Elliot Lee's avatar
Elliot Lee committed
299 300

	  /*  Update the region  */
301 302 303
	  gdisplays_update_area (gimage, 
				 x1 + off_x, y1 + off_y,
				 (x2 - x1), (y2 - y1));
Elliot Lee's avatar
Elliot Lee committed
304 305

	  /*  Invalidate the preview  */
306
	  gimp_viewable_invalidate_preview (GIMP_VIEWABLE (drawable));
Elliot Lee's avatar
Elliot Lee committed
307 308 309 310 311 312 313
	}
    }
  /*  Otherwise, get the entire active layer  */
  else
    {
      /*  If the layer is indexed...we need to extract pixels  */
      if (type == INDEXED && !keep_indexed)
314 315 316 317
	extract_from_region (&srcPR, &destPR, NULL,
			     gimp_drawable_cmap (drawable),
			     bg, type,
			     gimp_drawable_has_alpha (drawable), FALSE);
Elliot Lee's avatar
Elliot Lee committed
318 319 320 321 322 323 324 325 326 327
      /*  If the layer doesn't have an alpha channel, add one  */
      else if (bytes > srcPR.bytes)
	add_alpha_region (&srcPR, &destPR);
      /*  Otherwise, do a straight copy  */
      else
	copy_region (&srcPR, &destPR);

      /*  If we're cutting, remove either the layer (or floating selection),
       *  the layer mask, or the channel
       */
328
      if (cut_gimage && GIMP_IS_LAYER (drawable))
Elliot Lee's avatar
Elliot Lee committed
329
	{
330
	  if (gimp_layer_is_floating_sel (GIMP_LAYER (drawable)))
331
	    floating_sel_remove (GIMP_LAYER (drawable));
Elliot Lee's avatar
Elliot Lee committed
332
	  else
333
	    gimp_image_remove_layer (gimage, GIMP_LAYER (drawable));
Elliot Lee's avatar
Elliot Lee committed
334
	}
335
      else if (cut_gimage && GIMP_IS_LAYER_MASK (drawable))
Elliot Lee's avatar
Elliot Lee committed
336
	{
337
	  gimp_image_remove_layer_mask (gimage, 
338
					gimp_layer_mask_get_layer (GIMP_LAYER_MASK (drawable)),
339
					DISCARD);
Elliot Lee's avatar
Elliot Lee committed
340
	}
341
      else if (cut_gimage && GIMP_IS_CHANNEL (drawable))
342
	gimp_image_remove_channel (gimage, GIMP_CHANNEL (drawable));
Elliot Lee's avatar
Elliot Lee committed
343 344 345 346 347
    }

  return tiles;
}

348
GimpLayer *
349 350
gimage_mask_float (GImage       *gimage,
		   GimpDrawable *drawable,
351 352
		   gint          off_x,    /* optional offset */
		   gint          off_y)
Elliot Lee's avatar
Elliot Lee committed
353
{
354
  GimpLayer   *layer;
355
  GimpChannel *mask = gimp_image_get_mask (gimage);
356 357 358 359
  TileManager *tiles;
  gboolean     non_empty;
  gint         x1, y1;
  gint         x2, y2;
Elliot Lee's avatar
Elliot Lee committed
360 361

  /*  Make sure there is a region to float...  */
362
  non_empty = gimp_drawable_mask_bounds ( (drawable), &x1, &y1, &x2, &y2);
Elliot Lee's avatar
Elliot Lee committed
363 364
  if (! non_empty || (x2 - x1) == 0 || (y2 - y1) == 0)
    {
365
      g_message (_("Float Selection: No selection to float."));
Elliot Lee's avatar
Elliot Lee committed
366 367 368 369 370 371 372
      return NULL;
    }

  /*  Start an undo group  */
  undo_push_group_start (gimage, FLOAT_MASK_UNDO);

  /*  Cut the selected region  */
373
  tiles = gimage_mask_extract (gimage, drawable, TRUE, FALSE, TRUE);
Elliot Lee's avatar
Elliot Lee committed
374 375

  /*  Create a new layer from the buffer  */
376 377 378 379 380
  layer = gimp_layer_new_from_tiles (gimage,
				     gimp_drawable_type_with_alpha (drawable),
				     tiles, 
				     _("Floated Layer"),
				     OPAQUE_OPACITY, NORMAL_MODE);
Elliot Lee's avatar
Elliot Lee committed
381 382

  /*  Set the offsets  */
383 384 385
  tile_manager_get_offsets (tiles, &x1, &y1);
  GIMP_DRAWABLE (layer)->offset_x = x1 + off_x;
  GIMP_DRAWABLE (layer)->offset_y = y1 + off_y;
Elliot Lee's avatar
Elliot Lee committed
386 387 388 389 390

  /*  Free the temp buffer  */
  tile_manager_destroy (tiles);

  /*  Add the floating layer to the gimage  */
391
  floating_sel_attach (layer, drawable);
Elliot Lee's avatar
Elliot Lee committed
392 393 394 395 396 397 398 399 400 401 402 403

  /*  End an undo group  */
  undo_push_group_end (gimage);

  /*  invalidate the gimage's boundary variables  */
  mask->boundary_known = FALSE;

  return layer;
}


void
404
gimage_mask_clear (GImage *gimage)
Elliot Lee's avatar
Elliot Lee committed
405
{
406
  gimp_channel_clear (gimp_image_get_mask (gimage));
Elliot Lee's avatar
Elliot Lee committed
407 408 409 410
}


void
411
gimage_mask_undo (GImage *gimage)
Elliot Lee's avatar
Elliot Lee committed
412
{
413
  gimp_channel_push_undo (gimp_image_get_mask (gimage));
Elliot Lee's avatar
Elliot Lee committed
414 415 416 417
}


void
418
gimage_mask_invert (GImage *gimage)
Elliot Lee's avatar
Elliot Lee committed
419
{
420
  gimp_channel_invert (gimp_image_get_mask (gimage));
Elliot Lee's avatar
Elliot Lee committed
421 422 423 424
}


void
425
gimage_mask_sharpen (GImage *gimage)
Elliot Lee's avatar
Elliot Lee committed
426 427 428 429
{
  /*  No need to play with the selection visibility
   *  because sharpen will not change the outline
   */
430
  gimp_channel_sharpen (gimp_image_get_mask (gimage));
Elliot Lee's avatar
Elliot Lee committed
431 432 433 434
}


void
435
gimage_mask_all (GImage *gimage)
Elliot Lee's avatar
Elliot Lee committed
436
{
437
  gimp_channel_all (gimp_image_get_mask (gimage));
Elliot Lee's avatar
Elliot Lee committed
438 439 440 441
}


void
442
gimage_mask_none (GImage *gimage)
Elliot Lee's avatar
Elliot Lee committed
443
{
444
  gimp_channel_clear (gimp_image_get_mask (gimage));
Elliot Lee's avatar
Elliot Lee committed
445 446 447 448
}


void
449 450 451
gimage_mask_feather (GImage  *gimage,
		     gdouble  feather_radius_x,
		     gdouble  feather_radius_y)
Elliot Lee's avatar
Elliot Lee committed
452 453
{
  /*  push the current mask onto the undo stack--need to do this here because
454
   *  gimp_channel_feather doesn't do it
Elliot Lee's avatar
Elliot Lee committed
455
   */
456
  gimp_channel_push_undo (gimp_image_get_mask (gimage));
Elliot Lee's avatar
Elliot Lee committed
457 458

  /*  feather the region  */
459 460 461 462 463
  gimp_channel_feather (gimp_image_get_mask (gimage),
			gimp_image_get_mask (gimage),
			feather_radius_x,
			feather_radius_y,
			CHANNEL_OP_REPLACE, 0, 0);
Elliot Lee's avatar
Elliot Lee committed
464 465 466 467
}


void
468
gimage_mask_border (GImage *gimage,
469 470
		    gint    border_radius_x,
		    gint    border_radius_y)
Elliot Lee's avatar
Elliot Lee committed
471 472
{
  /*  feather the region  */
473 474 475
  gimp_channel_border (gimp_image_get_mask (gimage),
		       border_radius_x,
		       border_radius_y);
Elliot Lee's avatar
Elliot Lee committed
476 477 478 479
}


void
480
gimage_mask_grow (GImage *gimage,
481 482
		  int     grow_pixels_x,
		  int     grow_pixels_y)
Elliot Lee's avatar
Elliot Lee committed
483 484
{
  /*  feather the region  */
485 486 487
  gimp_channel_grow (gimp_image_get_mask (gimage),
		     grow_pixels_x,
		     grow_pixels_y);
Elliot Lee's avatar
Elliot Lee committed
488 489 490 491
}


void
492 493 494 495
gimage_mask_shrink (GImage   *gimage,
		    gint      shrink_pixels_x,
		    gint      shrink_pixels_y,
		    gboolean  edge_lock)
Elliot Lee's avatar
Elliot Lee committed
496 497
{
  /*  feather the region  */
498 499 500 501
  gimp_channel_shrink (gimp_image_get_mask (gimage),
		       shrink_pixels_x,
		       shrink_pixels_y,
		       edge_lock);
Elliot Lee's avatar
Elliot Lee committed
502 503 504 505
}


void
506 507
gimage_mask_layer_alpha (GimpImage *gimage,
			 GimpLayer *layer)
Elliot Lee's avatar
Elliot Lee committed
508 509
{
  /*  extract the layer's alpha channel  */
510
  if (gimp_drawable_has_alpha (GIMP_DRAWABLE (layer)))
Elliot Lee's avatar
Elliot Lee committed
511 512
    {
      /*  load the mask with the given layer's alpha channel  */
513
      gimp_channel_layer_alpha (gimp_image_get_mask (gimage), layer);
Elliot Lee's avatar
Elliot Lee committed
514 515 516
    }
  else
    {
517 518
      g_message (_("The active layer has no alpha channel\n"
		   "to convert to a selection."));
Elliot Lee's avatar
Elliot Lee committed
519 520 521 522 523 524
      return;
    }
}


void
525 526
gimage_mask_layer_mask (GimpImage *gimage,
			GimpLayer *layer)
Elliot Lee's avatar
Elliot Lee committed
527 528
{
  /*  extract the layer's alpha channel  */
529
  if (gimp_layer_get_mask (layer))
Elliot Lee's avatar
Elliot Lee committed
530 531
    {
      /*  load the mask with the given layer's alpha channel  */
532
      gimp_channel_layer_mask (gimp_image_get_mask (gimage), layer);
Elliot Lee's avatar
Elliot Lee committed
533 534 535
    }
  else
    {
536 537
      g_message (_("The active layer has no mask\n"
		   "to convert to a selection."));
Elliot Lee's avatar
Elliot Lee committed
538 539 540 541 542 543
      return;
    }
}


void
544 545
gimage_mask_load (GImage      *gimage,
		  GimpChannel *channel)
Elliot Lee's avatar
Elliot Lee committed
546 547
{
  /*  Load the specified channel to the gimage mask  */
548
  gimp_channel_load (gimp_image_get_mask (gimage), (channel));
Elliot Lee's avatar
Elliot Lee committed
549 550 551
}


552
GimpChannel *
553
gimage_mask_save (GImage *gimage)
Elliot Lee's avatar
Elliot Lee committed
554
{
555
  GimpChannel *new_channel;
Elliot Lee's avatar
Elliot Lee committed
556

557
  new_channel = gimp_channel_copy (gimp_image_get_mask (gimage));
Elliot Lee's avatar
Elliot Lee committed
558 559

  /*  saved selections are not visible by default  */
560
  GIMP_DRAWABLE(new_channel)->visible = FALSE;
561
  gimp_image_add_channel (gimage, new_channel, -1);
Elliot Lee's avatar
Elliot Lee committed
562 563 564 565 566

  return new_channel;
}


567
gboolean
568 569
gimage_mask_stroke (GImage       *gimage,
		    GimpDrawable *drawable)
Elliot Lee's avatar
Elliot Lee committed
570
{
571 572 573 574 575 576 577 578 579 580 581 582 583
  BoundSeg  *bs_in;
  BoundSeg  *bs_out;
  gint       num_segs_in;
  gint       num_segs_out;
  BoundSeg  *stroke_segs;
  gint       num_strokes;
  gint       seg;
  gint       offx, offy;
  gint       i;
  gdouble   *stroke_points;
  gint       cpnt;
  Argument  *return_vals;
  gint       nreturn_vals;
Elliot Lee's avatar
Elliot Lee committed
584

585 586
  if (! gimage_mask_boundary (gimage, &bs_in, &bs_out,
			      &num_segs_in, &num_segs_out))
Elliot Lee's avatar
Elliot Lee committed
587
    {
588
      g_message (_("No selection to stroke!"));
Elliot Lee's avatar
Elliot Lee committed
589 590 591 592 593 594 595 596
      return FALSE;
    }

  stroke_segs = sort_boundary (bs_in, num_segs_in, &num_strokes);
  if (num_strokes == 0)
    return TRUE;

  /*  find the drawable offsets  */
597
  gimp_drawable_offsets (drawable, &offx, &offy);
Elliot Lee's avatar
Elliot Lee committed
598 599
  gimage_mask_stroking = TRUE;

600 601
  /*  Start an undo group  */
  undo_push_group_start (gimage, PAINT_CORE_UNDO);
Elliot Lee's avatar
Elliot Lee committed
602 603

  seg = 0;
604 605
  cpnt = 0;
  /* Largest array required (may be used in segments!) */
606
  stroke_points = g_malloc (sizeof (gdouble) * 2 * (num_segs_in + 4));
607

608 609 610 611
  /* we offset all coordinates by 0.5 to align the brush with the path */

  stroke_points[cpnt++] = (gdouble)(stroke_segs[0].x1 - offx + 0.5);
  stroke_points[cpnt++] = (gdouble)(stroke_segs[0].y1 - offy + 0.5);
612

Elliot Lee's avatar
Elliot Lee committed
613 614
  for (i = 0; i < num_strokes; i++)
    {
615 616 617 618
      while ((stroke_segs[seg].x1 != -1 ||
	      stroke_segs[seg].x2 != -1 ||
	      stroke_segs[seg].y1 != -1 ||
	      stroke_segs[seg].y2 != -1))
Elliot Lee's avatar
Elliot Lee committed
619
	{
620 621
	  stroke_points[cpnt++] = (gdouble)(stroke_segs[seg].x2 - offx + 0.5);
	  stroke_points[cpnt++] = (gdouble)(stroke_segs[seg].y2 - offy + 0.5);
Elliot Lee's avatar
Elliot Lee committed
622 623 624
	  seg ++;
	}

625
      /* Close the stroke points up */
626 627 628 629
      stroke_points[cpnt++] = stroke_points[0];
      stroke_points[cpnt++] = stroke_points[1];

      /* Stroke with the correct tool */
630
      return_vals =
631
	procedural_db_run_proc (tool_manager_active_get_PDB_string (),
632 633 634 635 636
				&nreturn_vals,
				PDB_DRAWABLE, gimp_drawable_get_ID (drawable),
				PDB_INT32, (gint32) cpnt,
				PDB_FLOATARRAY, stroke_points,
				PDB_END);
637 638 639 640 641 642 643 644 645 646 647 648
      
      if (return_vals && return_vals[0].value.pdb_int == PDB_SUCCESS)
	{
	  /* Not required */
	  /*gdisplays_flush ();*/
	}
      else
	g_message (_("Paintbrush operation failed."));
      
      procedural_db_destroy_args (return_vals, nreturn_vals);
      
      cpnt = 0;
Elliot Lee's avatar
Elliot Lee committed
649
      seg ++;
650 651
      stroke_points[cpnt++] = (gdouble)(stroke_segs[seg].x1 - offx + 0.5);
      stroke_points[cpnt++] = (gdouble)(stroke_segs[seg].y1 - offy + 0.5);
Elliot Lee's avatar
Elliot Lee committed
652 653 654 655
    }

  /*  cleanup  */
  gimage_mask_stroking = FALSE;
656
  g_free (stroke_points);
Elliot Lee's avatar
Elliot Lee committed
657 658
  g_free (stroke_segs);

659 660
  /*  End an undo group  */
  undo_push_group_end (gimage);
Elliot Lee's avatar
Elliot Lee committed
661

662
  return TRUE;
Elliot Lee's avatar
Elliot Lee committed
663
}