1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18 #include "config.h"
19
20 #include <string.h>
21
22 #include <cairo.h>
23 #include <gdk-pixbuf/gdk-pixbuf.h>
24 #include <gegl.h>
25
26 #include "libgimpbase/gimpbase.h"
27 #include "libgimpmath/gimpmath.h"
28
29 #include "core-types.h"
30
31 #include "gegl/gimp-gegl-apply-operation.h"
32 #include "gegl/gimp-gegl-loops.h"
33 #include "gegl/gimp-gegl-mask-combine.h"
34
35 #include "gimpchannel.h"
36 #include "gimpchannel-select.h"
37 #include "gimpchannel-combine.h"
38 #include "gimppickable.h"
39 #include "gimppickable-contiguous-region.h"
40 #include "gimpscanconvert.h"
41
42 #include "vectors/gimpstroke.h"
43 #include "vectors/gimpvectors.h"
44
45 #include "gimp-intl.h"
46
47
48 /* basic selection functions */
49
50 void
gimp_channel_select_rectangle(GimpChannel * channel,gint x,gint y,gint w,gint h,GimpChannelOps op,gboolean feather,gdouble feather_radius_x,gdouble feather_radius_y,gboolean push_undo)51 gimp_channel_select_rectangle (GimpChannel *channel,
52 gint x,
53 gint y,
54 gint w,
55 gint h,
56 GimpChannelOps op,
57 gboolean feather,
58 gdouble feather_radius_x,
59 gdouble feather_radius_y,
60 gboolean push_undo)
61 {
62 g_return_if_fail (GIMP_IS_CHANNEL (channel));
63 g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)));
64
65 if (push_undo)
66 gimp_channel_push_undo (channel, C_("undo-type", "Rectangle Select"));
67
68 /* if feathering for rect, make a new mask with the
69 * rectangle and feather that with the old mask
70 */
71 if (feather)
72 {
73 GimpItem *item = GIMP_ITEM (channel);
74 GeglBuffer *add_on;
75
76 add_on = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
77 gimp_item_get_width (item),
78 gimp_item_get_height (item)),
79 babl_format ("Y float"));
80
81 gimp_gegl_mask_combine_rect (add_on, GIMP_CHANNEL_OP_REPLACE, x, y, w, h);
82
83 gimp_gegl_apply_feather (add_on, NULL, NULL, add_on, NULL,
84 feather_radius_x,
85 feather_radius_y,
86 TRUE);
87
88 gimp_channel_combine_buffer (channel, add_on, op, 0, 0);
89 g_object_unref (add_on);
90 }
91 else
92 {
93 gimp_channel_combine_rect (channel, op, x, y, w, h);
94 }
95 }
96
97 void
gimp_channel_select_ellipse(GimpChannel * channel,gint x,gint y,gint w,gint h,GimpChannelOps op,gboolean antialias,gboolean feather,gdouble feather_radius_x,gdouble feather_radius_y,gboolean push_undo)98 gimp_channel_select_ellipse (GimpChannel *channel,
99 gint x,
100 gint y,
101 gint w,
102 gint h,
103 GimpChannelOps op,
104 gboolean antialias,
105 gboolean feather,
106 gdouble feather_radius_x,
107 gdouble feather_radius_y,
108 gboolean push_undo)
109 {
110 g_return_if_fail (GIMP_IS_CHANNEL (channel));
111 g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)));
112
113 if (push_undo)
114 gimp_channel_push_undo (channel, C_("undo-type", "Ellipse Select"));
115
116 /* if feathering for rect, make a new mask with the
117 * rectangle and feather that with the old mask
118 */
119 if (feather)
120 {
121 GimpItem *item = GIMP_ITEM (channel);
122 GeglBuffer *add_on;
123
124 add_on = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
125 gimp_item_get_width (item),
126 gimp_item_get_height (item)),
127 babl_format ("Y float"));
128
129 gimp_gegl_mask_combine_ellipse (add_on, GIMP_CHANNEL_OP_REPLACE,
130 x, y, w, h, antialias);
131
132 gimp_gegl_apply_feather (add_on, NULL, NULL, add_on, NULL,
133 feather_radius_x,
134 feather_radius_y,
135 TRUE);
136
137 gimp_channel_combine_buffer (channel, add_on, op, 0, 0);
138 g_object_unref (add_on);
139 }
140 else
141 {
142 gimp_channel_combine_ellipse (channel, op, x, y, w, h, antialias);
143 }
144 }
145
146 void
gimp_channel_select_round_rect(GimpChannel * channel,gint x,gint y,gint w,gint h,gdouble corner_radius_x,gdouble corner_radius_y,GimpChannelOps op,gboolean antialias,gboolean feather,gdouble feather_radius_x,gdouble feather_radius_y,gboolean push_undo)147 gimp_channel_select_round_rect (GimpChannel *channel,
148 gint x,
149 gint y,
150 gint w,
151 gint h,
152 gdouble corner_radius_x,
153 gdouble corner_radius_y,
154 GimpChannelOps op,
155 gboolean antialias,
156 gboolean feather,
157 gdouble feather_radius_x,
158 gdouble feather_radius_y,
159 gboolean push_undo)
160 {
161 g_return_if_fail (GIMP_IS_CHANNEL (channel));
162 g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)));
163
164 if (push_undo)
165 gimp_channel_push_undo (channel, C_("undo-type", "Rounded Rectangle Select"));
166
167 /* if feathering for rect, make a new mask with the
168 * rectangle and feather that with the old mask
169 */
170 if (feather)
171 {
172 GimpItem *item = GIMP_ITEM (channel);
173 GeglBuffer *add_on;
174
175 add_on = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
176 gimp_item_get_width (item),
177 gimp_item_get_height (item)),
178 babl_format ("Y float"));
179
180 gimp_gegl_mask_combine_ellipse_rect (add_on, GIMP_CHANNEL_OP_REPLACE,
181 x, y, w, h,
182 corner_radius_x, corner_radius_y,
183 antialias);
184
185 gimp_gegl_apply_feather (add_on, NULL, NULL, add_on, NULL,
186 feather_radius_x,
187 feather_radius_y,
188 TRUE);
189
190 gimp_channel_combine_buffer (channel, add_on, op, 0, 0);
191 g_object_unref (add_on);
192 }
193 else
194 {
195 gimp_channel_combine_ellipse_rect (channel, op, x, y, w, h,
196 corner_radius_x, corner_radius_y,
197 antialias);
198 }
199 }
200
201 /* select by GimpScanConvert functions */
202
203 void
gimp_channel_select_scan_convert(GimpChannel * channel,const gchar * undo_desc,GimpScanConvert * scan_convert,gint offset_x,gint offset_y,GimpChannelOps op,gboolean antialias,gboolean feather,gdouble feather_radius_x,gdouble feather_radius_y,gboolean push_undo)204 gimp_channel_select_scan_convert (GimpChannel *channel,
205 const gchar *undo_desc,
206 GimpScanConvert *scan_convert,
207 gint offset_x,
208 gint offset_y,
209 GimpChannelOps op,
210 gboolean antialias,
211 gboolean feather,
212 gdouble feather_radius_x,
213 gdouble feather_radius_y,
214 gboolean push_undo)
215 {
216 GimpItem *item;
217 GeglBuffer *add_on;
218
219 g_return_if_fail (GIMP_IS_CHANNEL (channel));
220 g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)));
221 g_return_if_fail (undo_desc != NULL);
222 g_return_if_fail (scan_convert != NULL);
223
224 if (push_undo)
225 gimp_channel_push_undo (channel, undo_desc);
226
227 item = GIMP_ITEM (channel);
228
229 add_on = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
230 gimp_item_get_width (item),
231 gimp_item_get_height (item)),
232 babl_format ("Y float"));
233
234 gimp_scan_convert_render (scan_convert, add_on,
235 offset_x, offset_y, antialias);
236
237 if (feather)
238 gimp_gegl_apply_feather (add_on, NULL, NULL, add_on, NULL,
239 feather_radius_x,
240 feather_radius_y,
241 TRUE);
242
243 gimp_channel_combine_buffer (channel, add_on, op, 0, 0);
244 g_object_unref (add_on);
245 }
246
247 void
gimp_channel_select_polygon(GimpChannel * channel,const gchar * undo_desc,gint n_points,const GimpVector2 * points,GimpChannelOps op,gboolean antialias,gboolean feather,gdouble feather_radius_x,gdouble feather_radius_y,gboolean push_undo)248 gimp_channel_select_polygon (GimpChannel *channel,
249 const gchar *undo_desc,
250 gint n_points,
251 const GimpVector2 *points,
252 GimpChannelOps op,
253 gboolean antialias,
254 gboolean feather,
255 gdouble feather_radius_x,
256 gdouble feather_radius_y,
257 gboolean push_undo)
258 {
259 GimpScanConvert *scan_convert;
260
261 g_return_if_fail (GIMP_IS_CHANNEL (channel));
262 g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)));
263 g_return_if_fail (undo_desc != NULL);
264
265 scan_convert = gimp_scan_convert_new ();
266
267 gimp_scan_convert_add_polyline (scan_convert, n_points, points, TRUE);
268
269 gimp_channel_select_scan_convert (channel, undo_desc, scan_convert, 0, 0,
270 op, antialias, feather,
271 feather_radius_x, feather_radius_y,
272 push_undo);
273
274 gimp_scan_convert_free (scan_convert);
275 }
276
277 void
gimp_channel_select_vectors(GimpChannel * channel,const gchar * undo_desc,GimpVectors * vectors,GimpChannelOps op,gboolean antialias,gboolean feather,gdouble feather_radius_x,gdouble feather_radius_y,gboolean push_undo)278 gimp_channel_select_vectors (GimpChannel *channel,
279 const gchar *undo_desc,
280 GimpVectors *vectors,
281 GimpChannelOps op,
282 gboolean antialias,
283 gboolean feather,
284 gdouble feather_radius_x,
285 gdouble feather_radius_y,
286 gboolean push_undo)
287 {
288 const GimpBezierDesc *bezier;
289
290 g_return_if_fail (GIMP_IS_CHANNEL (channel));
291 g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)));
292 g_return_if_fail (undo_desc != NULL);
293 g_return_if_fail (GIMP_IS_VECTORS (vectors));
294
295 bezier = gimp_vectors_get_bezier (vectors);
296
297 if (bezier && bezier->num_data > 4)
298 {
299 GimpScanConvert *scan_convert;
300
301 scan_convert = gimp_scan_convert_new ();
302 gimp_scan_convert_add_bezier (scan_convert, bezier);
303
304 gimp_channel_select_scan_convert (channel, undo_desc, scan_convert, 0, 0,
305 op, antialias, feather,
306 feather_radius_x, feather_radius_y,
307 push_undo);
308
309 gimp_scan_convert_free (scan_convert);
310 }
311 }
312
313
314 /* select by GimpChannel functions */
315
316 void
gimp_channel_select_buffer(GimpChannel * channel,const gchar * undo_desc,GeglBuffer * add_on,gint offset_x,gint offset_y,GimpChannelOps op,gboolean feather,gdouble feather_radius_x,gdouble feather_radius_y)317 gimp_channel_select_buffer (GimpChannel *channel,
318 const gchar *undo_desc,
319 GeglBuffer *add_on,
320 gint offset_x,
321 gint offset_y,
322 GimpChannelOps op,
323 gboolean feather,
324 gdouble feather_radius_x,
325 gdouble feather_radius_y)
326 {
327 g_return_if_fail (GIMP_IS_CHANNEL (channel));
328 g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)));
329 g_return_if_fail (undo_desc != NULL);
330 g_return_if_fail (GEGL_IS_BUFFER (add_on));
331
332 gimp_channel_push_undo (channel, undo_desc);
333
334 if (feather)
335 {
336 GimpItem *item = GIMP_ITEM (channel);
337 GeglBuffer *add_on2;
338
339 add_on2 = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
340 gimp_item_get_width (item),
341 gimp_item_get_height (item)),
342 babl_format ("Y float"));
343
344 gimp_gegl_mask_combine_buffer (add_on2, add_on,
345 GIMP_CHANNEL_OP_REPLACE,
346 offset_x, offset_y);
347
348 gimp_gegl_apply_feather (add_on2, NULL, NULL, add_on2, NULL,
349 feather_radius_x,
350 feather_radius_y,
351 TRUE);
352
353 gimp_channel_combine_buffer (channel, add_on2, op, 0, 0);
354 g_object_unref (add_on2);
355 }
356 else
357 {
358 gimp_channel_combine_buffer (channel, add_on, op, offset_x, offset_y);
359 }
360 }
361
362 void
gimp_channel_select_channel(GimpChannel * channel,const gchar * undo_desc,GimpChannel * add_on,gint offset_x,gint offset_y,GimpChannelOps op,gboolean feather,gdouble feather_radius_x,gdouble feather_radius_y)363 gimp_channel_select_channel (GimpChannel *channel,
364 const gchar *undo_desc,
365 GimpChannel *add_on,
366 gint offset_x,
367 gint offset_y,
368 GimpChannelOps op,
369 gboolean feather,
370 gdouble feather_radius_x,
371 gdouble feather_radius_y)
372 {
373 g_return_if_fail (GIMP_IS_CHANNEL (channel));
374 g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)));
375 g_return_if_fail (undo_desc != NULL);
376 g_return_if_fail (GIMP_IS_CHANNEL (add_on));
377
378 gimp_channel_select_buffer (channel, undo_desc,
379 gimp_drawable_get_buffer (GIMP_DRAWABLE (add_on)),
380 offset_x, offset_y, op,
381 feather,
382 feather_radius_x, feather_radius_y);
383 }
384
385 void
gimp_channel_select_alpha(GimpChannel * channel,GimpDrawable * drawable,GimpChannelOps op,gboolean feather,gdouble feather_radius_x,gdouble feather_radius_y)386 gimp_channel_select_alpha (GimpChannel *channel,
387 GimpDrawable *drawable,
388 GimpChannelOps op,
389 gboolean feather,
390 gdouble feather_radius_x,
391 gdouble feather_radius_y)
392 {
393 GimpItem *item;
394 GimpChannel *add_on;
395 gint off_x, off_y;
396
397 g_return_if_fail (GIMP_IS_CHANNEL (channel));
398 g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)));
399 g_return_if_fail (GIMP_IS_DRAWABLE (drawable));
400
401 item = GIMP_ITEM (channel);
402
403 if (gimp_drawable_has_alpha (drawable))
404 {
405 add_on = gimp_channel_new_from_alpha (gimp_item_get_image (item),
406 drawable, NULL, NULL);
407 }
408 else
409 {
410 /* no alpha is equivalent to completely opaque alpha,
411 * so simply select the whole layer's extents. --mitch
412 */
413 add_on = gimp_channel_new_mask (gimp_item_get_image (item),
414 gimp_item_get_width (GIMP_ITEM (drawable)),
415 gimp_item_get_height (GIMP_ITEM (drawable)));
416 gimp_channel_all (add_on, FALSE);
417 }
418
419 gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);
420
421 gimp_channel_select_channel (channel, C_("undo-type", "Alpha to Selection"), add_on,
422 off_x, off_y,
423 op, feather,
424 feather_radius_x,
425 feather_radius_y);
426 g_object_unref (add_on);
427 }
428
429 void
gimp_channel_select_component(GimpChannel * channel,GimpChannelType component,GimpChannelOps op,gboolean feather,gdouble feather_radius_x,gdouble feather_radius_y)430 gimp_channel_select_component (GimpChannel *channel,
431 GimpChannelType component,
432 GimpChannelOps op,
433 gboolean feather,
434 gdouble feather_radius_x,
435 gdouble feather_radius_y)
436 {
437 GimpItem *item;
438 GimpChannel *add_on;
439 const gchar *desc;
440 gchar *undo_desc;
441
442 g_return_if_fail (GIMP_IS_CHANNEL (channel));
443 g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)));
444
445 item = GIMP_ITEM (channel);
446
447 add_on = gimp_channel_new_from_component (gimp_item_get_image (item),
448 component, NULL, NULL);
449
450 if (feather)
451 gimp_channel_feather (add_on,
452 feather_radius_x,
453 feather_radius_y,
454 TRUE,
455 FALSE /* no undo */);
456
457 gimp_enum_get_value (GIMP_TYPE_CHANNEL_TYPE, component,
458 NULL, NULL, &desc, NULL);
459
460 undo_desc = g_strdup_printf (C_("undo-type", "%s Channel to Selection"), desc);
461
462 gimp_channel_select_channel (channel, undo_desc, add_on,
463 0, 0, op,
464 FALSE, 0.0, 0.0);
465
466 g_free (undo_desc);
467 g_object_unref (add_on);
468 }
469
470 void
gimp_channel_select_fuzzy(GimpChannel * channel,GimpDrawable * drawable,gboolean sample_merged,gint x,gint y,gfloat threshold,gboolean select_transparent,GimpSelectCriterion select_criterion,gboolean diagonal_neighbors,GimpChannelOps op,gboolean antialias,gboolean feather,gdouble feather_radius_x,gdouble feather_radius_y)471 gimp_channel_select_fuzzy (GimpChannel *channel,
472 GimpDrawable *drawable,
473 gboolean sample_merged,
474 gint x,
475 gint y,
476 gfloat threshold,
477 gboolean select_transparent,
478 GimpSelectCriterion select_criterion,
479 gboolean diagonal_neighbors,
480 GimpChannelOps op,
481 gboolean antialias,
482 gboolean feather,
483 gdouble feather_radius_x,
484 gdouble feather_radius_y)
485 {
486 GimpPickable *pickable;
487 GeglBuffer *add_on;
488 gint add_on_x = 0;
489 gint add_on_y = 0;
490
491 g_return_if_fail (GIMP_IS_CHANNEL (channel));
492 g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)));
493 g_return_if_fail (GIMP_IS_DRAWABLE (drawable));
494
495 if (sample_merged)
496 pickable = GIMP_PICKABLE (gimp_item_get_image (GIMP_ITEM (drawable)));
497 else
498 pickable = GIMP_PICKABLE (drawable);
499
500 add_on = gimp_pickable_contiguous_region_by_seed (pickable,
501 antialias,
502 threshold,
503 select_transparent,
504 select_criterion,
505 diagonal_neighbors,
506 x, y);
507
508 if (! sample_merged)
509 gimp_item_get_offset (GIMP_ITEM (drawable), &add_on_x, &add_on_y);
510
511 gimp_channel_select_buffer (channel, C_("undo-type", "Fuzzy Select"),
512 add_on, add_on_x, add_on_y,
513 op,
514 feather,
515 feather_radius_x,
516 feather_radius_y);
517 g_object_unref (add_on);
518 }
519
520 void
gimp_channel_select_by_color(GimpChannel * channel,GimpDrawable * drawable,gboolean sample_merged,const GimpRGB * color,gfloat threshold,gboolean select_transparent,GimpSelectCriterion select_criterion,GimpChannelOps op,gboolean antialias,gboolean feather,gdouble feather_radius_x,gdouble feather_radius_y)521 gimp_channel_select_by_color (GimpChannel *channel,
522 GimpDrawable *drawable,
523 gboolean sample_merged,
524 const GimpRGB *color,
525 gfloat threshold,
526 gboolean select_transparent,
527 GimpSelectCriterion select_criterion,
528 GimpChannelOps op,
529 gboolean antialias,
530 gboolean feather,
531 gdouble feather_radius_x,
532 gdouble feather_radius_y)
533 {
534 GimpPickable *pickable;
535 GeglBuffer *add_on;
536 gint add_on_x = 0;
537 gint add_on_y = 0;
538
539 g_return_if_fail (GIMP_IS_CHANNEL (channel));
540 g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)));
541 g_return_if_fail (GIMP_IS_DRAWABLE (drawable));
542 g_return_if_fail (color != NULL);
543
544 if (sample_merged)
545 pickable = GIMP_PICKABLE (gimp_item_get_image (GIMP_ITEM (drawable)));
546 else
547 pickable = GIMP_PICKABLE (drawable);
548
549 add_on = gimp_pickable_contiguous_region_by_color (pickable,
550 antialias,
551 threshold,
552 select_transparent,
553 select_criterion,
554 color);
555
556 if (! sample_merged)
557 gimp_item_get_offset (GIMP_ITEM (drawable), &add_on_x, &add_on_y);
558
559 gimp_channel_select_buffer (channel, C_("undo-type", "Select by Color"),
560 add_on, add_on_x, add_on_y,
561 op,
562 feather,
563 feather_radius_x,
564 feather_radius_y);
565 g_object_unref (add_on);
566 }
567
568 void
gimp_channel_select_by_index(GimpChannel * channel,GimpDrawable * drawable,gint index,GimpChannelOps op,gboolean feather,gdouble feather_radius_x,gdouble feather_radius_y)569 gimp_channel_select_by_index (GimpChannel *channel,
570 GimpDrawable *drawable,
571 gint index,
572 GimpChannelOps op,
573 gboolean feather,
574 gdouble feather_radius_x,
575 gdouble feather_radius_y)
576 {
577 GeglBuffer *add_on;
578 gint add_on_x = 0;
579 gint add_on_y = 0;
580
581 g_return_if_fail (GIMP_IS_CHANNEL (channel));
582 g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)));
583 g_return_if_fail (GIMP_IS_DRAWABLE (drawable));
584 g_return_if_fail (gimp_drawable_is_indexed (drawable));
585
586 add_on = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
587 gimp_item_get_width (GIMP_ITEM (drawable)),
588 gimp_item_get_height (GIMP_ITEM (drawable))),
589 babl_format ("Y float"));
590
591 gimp_gegl_index_to_mask (gimp_drawable_get_buffer (drawable), NULL,
592 gimp_drawable_get_format_without_alpha (drawable),
593 add_on, NULL,
594 index);
595
596 gimp_item_get_offset (GIMP_ITEM (drawable), &add_on_x, &add_on_y);
597
598 gimp_channel_select_buffer (channel, C_("undo-type", "Select by Indexed Color"),
599 add_on, add_on_x, add_on_y,
600 op,
601 feather,
602 feather_radius_x,
603 feather_radius_y);
604 g_object_unref (add_on);
605 }
606