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 <gio/gio.h>
23 #include <gegl.h>
24
25 #include "libgimpbase/gimpbase.h"
26 #include "libgimpmath/gimpmath.h"
27
28 extern "C"
29 {
30
31 #include "gimp-gegl-types.h"
32
33 #include "gimp-babl.h"
34 #include "gimp-gegl-loops.h"
35 #include "gimp-gegl-mask-combine.h"
36
37
38 #define EPSILON 1e-6
39
40 #define PIXELS_PER_THREAD \
41 (/* each thread costs as much as */ 64.0 * 64.0 /* pixels */)
42
43
44 gboolean
gimp_gegl_mask_combine_rect(GeglBuffer * mask,GimpChannelOps op,gint x,gint y,gint w,gint h)45 gimp_gegl_mask_combine_rect (GeglBuffer *mask,
46 GimpChannelOps op,
47 gint x,
48 gint y,
49 gint w,
50 gint h)
51 {
52 GeglRectangle rect;
53 gfloat value;
54
55 g_return_val_if_fail (GEGL_IS_BUFFER (mask), FALSE);
56
57 if (! gegl_rectangle_intersect (&rect,
58 GEGL_RECTANGLE (x, y, w, h),
59 gegl_buffer_get_abyss (mask)))
60 {
61 return FALSE;
62 }
63
64 switch (op)
65 {
66 case GIMP_CHANNEL_OP_REPLACE:
67 case GIMP_CHANNEL_OP_ADD:
68 value = 1.0f;
69 break;
70
71 case GIMP_CHANNEL_OP_SUBTRACT:
72 value = 0.0f;
73 break;
74
75 case GIMP_CHANNEL_OP_INTERSECT:
76 return TRUE;
77 }
78
79 gegl_buffer_set_color_from_pixel (mask, &rect, &value,
80 babl_format ("Y float"));
81
82 return TRUE;
83 }
84
85 gboolean
gimp_gegl_mask_combine_ellipse(GeglBuffer * mask,GimpChannelOps op,gint x,gint y,gint w,gint h,gboolean antialias)86 gimp_gegl_mask_combine_ellipse (GeglBuffer *mask,
87 GimpChannelOps op,
88 gint x,
89 gint y,
90 gint w,
91 gint h,
92 gboolean antialias)
93 {
94 return gimp_gegl_mask_combine_ellipse_rect (mask, op, x, y, w, h,
95 w / 2.0, h / 2.0, antialias);
96 }
97
98 gboolean
gimp_gegl_mask_combine_ellipse_rect(GeglBuffer * mask,GimpChannelOps op,gint x,gint y,gint w,gint h,gdouble rx,gdouble ry,gboolean antialias)99 gimp_gegl_mask_combine_ellipse_rect (GeglBuffer *mask,
100 GimpChannelOps op,
101 gint x,
102 gint y,
103 gint w,
104 gint h,
105 gdouble rx,
106 gdouble ry,
107 gboolean antialias)
108 {
109 GeglRectangle rect;
110 const Babl *format;
111 gint bpp;
112 gfloat one_f = 1.0f;
113 gpointer one;
114 gdouble cx;
115 gdouble cy;
116 gint left;
117 gint right;
118 gint top;
119 gint bottom;
120
121 g_return_val_if_fail (GEGL_IS_BUFFER (mask), FALSE);
122
123 if (rx <= EPSILON || ry <= EPSILON)
124 return gimp_gegl_mask_combine_rect (mask, op, x, y, w, h);
125
126 left = x;
127 right = x + w;
128 top = y;
129 bottom = y + h;
130
131 cx = (left + right) / 2.0;
132 cy = (top + bottom) / 2.0;
133
134 rx = MIN (rx, w / 2.0);
135 ry = MIN (ry, h / 2.0);
136
137 if (! gegl_rectangle_intersect (&rect,
138 GEGL_RECTANGLE (x, y, w, h),
139 gegl_buffer_get_abyss (mask)))
140 {
141 return FALSE;
142 }
143
144 format = gegl_buffer_get_format (mask);
145
146 if (antialias)
147 {
148 format = gimp_babl_format_change_component_type (
149 format, GIMP_COMPONENT_TYPE_FLOAT);
150 }
151
152 bpp = babl_format_get_bytes_per_pixel (format);
153 one = g_alloca (bpp);
154
155 babl_process (babl_fish ("Y float", format), &one_f, one, 1);
156
157 /* coordinate-system transforms. (x, y) coordinates are in the image
158 * coordinate-system, and (u, v) coordinates are in a coordinate-system
159 * aligned with the center of one of the elliptic corners, with the positive
160 * directions pointing away from the rectangle. when converting from (x, y)
161 * to (u, v), we use the closest elliptic corner.
162 */
163 auto x_to_u = [=] (gdouble x)
164 {
165 if (x < cx)
166 return (left + rx) - x;
167 else
168 return x - (right - rx);
169 };
170
171 auto y_to_v = [=] (gdouble y)
172 {
173 if (y < cy)
174 return (top + ry) - y;
175 else
176 return y - (bottom - ry);
177 };
178
179 auto u_to_x_left = [=] (gdouble u)
180 {
181 return (left + rx) - u;
182 };
183
184 auto u_to_x_right = [=] (gdouble u)
185 {
186 return (right - rx) + u;
187 };
188
189 /* intersection of a horizontal line with the ellipse */
190 auto v_to_u = [=] (gdouble v)
191 {
192 if (v > 0.0)
193 return sqrt (MAX (SQR (rx) - SQR (rx * v / ry), 0.0));
194 else
195 return rx;
196 };
197
198 /* intersection of a vertical line with the ellipse */
199 auto u_to_v = [=] (gdouble u)
200 {
201 if (u > 0.0)
202 return sqrt (MAX (SQR (ry) - SQR (ry * u / rx), 0.0));
203 else
204 return ry;
205 };
206
207 /* signed, normalized distance of a point from the ellipse's circumference.
208 * the sign of the result determines if the point is inside (positive) or
209 * outside (negative) the ellipse. the result is normalized to the cross-
210 * section length of a pixel, in the direction of the closest point along the
211 * ellipse.
212 *
213 * we use the following method to approximate the distance: pass horizontal
214 * and vertical lines at the given point, P, and find their (positive) points
215 * of intersection with the ellipse, A and B. the segment AB is an
216 * approximation of the corresponding elliptic arc (see bug #147836). find
217 * the closest point, C, to P, along the segment AB. find the (positive)
218 * point of intersection, Q, of the line PC and the ellipse. Q is an
219 * approximation for the closest point to P along the ellipse, and the
220 * approximated distance is the distance from P to Q.
221 */
222 auto ellipse_distance = [=] (gdouble u,
223 gdouble v)
224 {
225 gdouble du;
226 gdouble dv;
227 gdouble t;
228 gdouble a, b, c;
229 gdouble d;
230
231 u = MAX (u, 0.0);
232 v = MAX (v, 0.0);
233
234 du = v_to_u (v) - u;
235 dv = u_to_v (u) - v;
236
237 t = SQR (du) / (SQR (du) + SQR (dv));
238
239 du *= 1.0 - t;
240 dv *= t;
241
242 v *= rx / ry;
243 dv *= rx / ry;
244
245 a = SQR (du) + SQR (dv);
246 b = u * du + v * dv;
247 c = SQR (u) + SQR (v) - SQR (rx);
248
249 if (a <= EPSILON)
250 return 0.0;
251
252 if (c < 0.0)
253 t = (-b + sqrt (MAX (SQR (b) - a * c, 0.0))) / a;
254 else
255 t = (-b - sqrt (MAX (SQR (b) - a * c, 0.0))) / a;
256
257 dv *= ry / rx;
258
259 d = sqrt (SQR (du * t) + SQR (dv * t));
260
261 if (c > 0.0)
262 d = -d;
263
264 d /= sqrt (SQR (MIN (du / dv, dv / du)) + 1.0);
265
266 return d;
267 };
268
269 /* anti-aliased value of a pixel */
270 auto pixel_value = [=] (gint x,
271 gint y)
272 {
273 gdouble u = x_to_u (x + 0.5);
274 gdouble v = y_to_v (y + 0.5);
275 gdouble d = ellipse_distance (u, v);
276
277 /* use the distance of the pixel's center from the ellipse to approximate
278 * the coverage
279 */
280 d = CLAMP (0.5 + d, 0.0, 1.0);
281
282 /* we're at the horizontal boundary of an elliptic corner */
283 if (u < 0.5)
284 d = d * (0.5 + u) + (0.5 - u);
285
286 /* we're at the vertical boundary of an elliptic corner */
287 if (v < 0.5)
288 d = d * (0.5 + v) + (0.5 - v);
289
290 /* opposite horizontal corners intersect the pixel */
291 if (x == (right - 1) - (x - left))
292 d = 2.0 * d - 1.0;
293
294 /* opposite vertical corners intersect the pixel */
295 if (y == (bottom - 1) - (y - top))
296 d = 2.0 * d - 1.0;
297
298 return d;
299 };
300
301 auto ellipse_range = [=] (gdouble y,
302 gdouble *x0,
303 gdouble *x1)
304 {
305 gdouble u = v_to_u (y_to_v (y));
306
307 *x0 = u_to_x_left (u);
308 *x1 = u_to_x_right (u);
309 };
310
311 auto fill0 = [=] (gpointer dest,
312 gint n)
313 {
314 switch (op)
315 {
316 case GIMP_CHANNEL_OP_REPLACE:
317 case GIMP_CHANNEL_OP_INTERSECT:
318 memset (dest, 0, bpp * n);
319 break;
320
321 case GIMP_CHANNEL_OP_ADD:
322 case GIMP_CHANNEL_OP_SUBTRACT:
323 break;
324 }
325
326 return (gpointer) ((guint8 *) dest + bpp * n);
327 };
328
329 auto fill1 = [=] (gpointer dest,
330 gint n)
331 {
332 switch (op)
333 {
334 case GIMP_CHANNEL_OP_REPLACE:
335 case GIMP_CHANNEL_OP_ADD:
336 gegl_memset_pattern (dest, one, bpp, n);
337 break;
338
339 case GIMP_CHANNEL_OP_SUBTRACT:
340 memset (dest, 0, bpp * n);
341 break;
342
343 case GIMP_CHANNEL_OP_INTERSECT:
344 break;
345 }
346
347 return (gpointer) ((guint8 *) dest + bpp * n);
348 };
349
350 auto set = [=] (gpointer dest,
351 gfloat value)
352 {
353 gfloat *p = (gfloat *) dest;
354
355 switch (op)
356 {
357 case GIMP_CHANNEL_OP_REPLACE:
358 *p = value;
359 break;
360
361 case GIMP_CHANNEL_OP_ADD:
362 *p = MIN (*p + value, 1.0);
363 break;
364
365 case GIMP_CHANNEL_OP_SUBTRACT:
366 *p = MAX (*p - value, 0.0);
367 break;
368
369 case GIMP_CHANNEL_OP_INTERSECT:
370 *p = MIN (*p, value);
371 break;
372 }
373
374 return (gpointer) (p + 1);
375 };
376
377 gegl_parallel_distribute_area (
378 &rect, PIXELS_PER_THREAD,
379 [=] (const GeglRectangle *area)
380 {
381 GeglBufferIterator *iter;
382
383 iter = gegl_buffer_iterator_new (
384 mask, area, 0, format,
385 op == GIMP_CHANNEL_OP_REPLACE ? GEGL_ACCESS_WRITE :
386 GEGL_ACCESS_READWRITE,
387 GEGL_ABYSS_NONE, 1);
388
389 while (gegl_buffer_iterator_next (iter))
390 {
391 const GeglRectangle *roi = &iter->items[0].roi;
392 gpointer d = iter->items[0].data;
393 gdouble tx0, ty0;
394 gdouble tx1, ty1;
395 gdouble x0;
396 gdouble x1;
397 gint y;
398
399 /* tile bounds */
400 tx0 = roi->x;
401 ty0 = roi->y;
402
403 tx1 = roi->x + roi->width;
404 ty1 = roi->y + roi->height;
405
406 if (! antialias)
407 {
408 tx0 += 0.5;
409 ty0 += 0.5;
410
411 tx1 -= 0.5;
412 ty1 -= 0.5;
413 }
414
415 /* if the tile is fully inside/outside the ellipse, fill it with 1/0,
416 * respectively, and skip the rest.
417 */
418 ellipse_range (ty0, &x0, &x1);
419
420 if (tx0 >= x0 && tx1 <= x1)
421 {
422 ellipse_range (ty1, &x0, &x1);
423
424 if (tx0 >= x0 && tx1 <= x1)
425 {
426 fill1 (d, iter->length);
427
428 continue;
429 }
430 }
431 else if (tx1 < x0 || tx0 > x1)
432 {
433 ellipse_range (ty1, &x0, &x1);
434
435 if (tx1 < x0 || tx0 > x1)
436 {
437 if ((ty0 - cy) * (ty1 - cy) >= 0.0)
438 {
439 fill0 (d, iter->length);
440
441 continue;
442 }
443 }
444 }
445
446 for (y = roi->y; y < roi->y + roi->height; y++)
447 {
448 gint a, b;
449
450 if (antialias)
451 {
452 gdouble v = y_to_v (y + 0.5);
453 gdouble u0 = v_to_u (v - 0.5);
454 gdouble u1 = v_to_u (v + 0.5);
455 gint x;
456
457 a = floor (u_to_x_left (u0)) - roi->x;
458 a = CLAMP (a, 0, roi->width);
459
460 b = ceil (u_to_x_left (u1)) - roi->x;
461 b = CLAMP (b, a, roi->width);
462
463 d = fill0 (d, a);
464
465 for (x = roi->x + a; x < roi->x + b; x++)
466 d = set (d, pixel_value (x, y));
467
468 a = floor (u_to_x_right (u1)) - roi->x;
469 a = CLAMP (a, b, roi->width);
470
471 d = fill1 (d, a - b);
472
473 b = ceil (u_to_x_right (u0)) - roi->x;
474 b = CLAMP (b, a, roi->width);
475
476 for (x = roi->x + a; x < roi->x + b; x++)
477 d = set (d, pixel_value (x, y));
478
479 d = fill0 (d, roi->width - b);
480 }
481 else
482 {
483 ellipse_range (y + 0.5, &x0, &x1);
484
485 a = ceil (x0 - 0.5) - roi->x;
486 a = CLAMP (a, 0, roi->width);
487
488 b = floor (x1 + 0.5) - roi->x;
489 b = CLAMP (b, 0, roi->width);
490
491 d = fill0 (d, a);
492 d = fill1 (d, b - a);
493 d = fill0 (d, roi->width - b);
494 }
495 }
496 }
497 });
498
499 return TRUE;
500 }
501
502 gboolean
gimp_gegl_mask_combine_buffer(GeglBuffer * mask,GeglBuffer * add_on,GimpChannelOps op,gint off_x,gint off_y)503 gimp_gegl_mask_combine_buffer (GeglBuffer *mask,
504 GeglBuffer *add_on,
505 GimpChannelOps op,
506 gint off_x,
507 gint off_y)
508 {
509 GeglRectangle mask_rect;
510 GeglRectangle add_on_rect;
511 const Babl *mask_format;
512 const Babl *add_on_format;
513
514 g_return_val_if_fail (GEGL_IS_BUFFER (mask), FALSE);
515 g_return_val_if_fail (GEGL_IS_BUFFER (add_on), FALSE);
516
517 if (! gegl_rectangle_intersect (&mask_rect,
518 GEGL_RECTANGLE (
519 off_x + gegl_buffer_get_x (add_on),
520 off_y + gegl_buffer_get_y (add_on),
521 gegl_buffer_get_width (add_on),
522 gegl_buffer_get_height (add_on)),
523 gegl_buffer_get_abyss (mask)))
524 {
525 return FALSE;
526 }
527
528 add_on_rect = mask_rect;
529 add_on_rect.x -= off_x;
530 add_on_rect.y -= off_y;
531
532 mask_format = gegl_buffer_get_format (mask);
533 add_on_format = gegl_buffer_get_format (add_on);
534
535 if (op == GIMP_CHANNEL_OP_REPLACE &&
536 (gimp_babl_is_bounded (gimp_babl_format_get_precision (add_on_format)) ||
537 gimp_babl_is_bounded (gimp_babl_format_get_precision (mask_format))))
538 {
539 /* See below: this additional hack is only needed for the
540 * gimp-channel-combine-masks procedure, it's the only place that
541 * allows to combine arbitrary channels with each other.
542 */
543 gegl_buffer_set_format (
544 add_on,
545 gimp_babl_format_change_linear (
546 add_on_format, gimp_babl_format_get_linear (mask_format)));
547
548 gimp_gegl_buffer_copy (add_on, &add_on_rect, GEGL_ABYSS_NONE,
549 mask, &mask_rect);
550
551 gegl_buffer_set_format (add_on, NULL);
552
553 return TRUE;
554 }
555
556 /* This is a hack: all selections/layer masks/channels are always
557 * linear except for channels in 8-bit images. We don't want these
558 * "Y' u8" to be converted to "Y float" because that would cause a
559 * gamma canversion and give unexpected results for
560 * "add/subtract/etc channel from selection". Instead, use all
561 * channel values "as-is", which makes no differce except in the
562 * 8-bit case where we need it.
563 *
564 * See https://bugzilla.gnome.org/show_bug.cgi?id=791519
565 */
566 mask_format = gimp_babl_format_change_component_type (
567 mask_format, GIMP_COMPONENT_TYPE_FLOAT);
568
569 add_on_format = gimp_babl_format_change_component_type (
570 add_on_format, GIMP_COMPONENT_TYPE_FLOAT);
571
572 gegl_parallel_distribute_area (
573 &mask_rect, PIXELS_PER_THREAD,
574 [=] (const GeglRectangle *mask_area)
575 {
576 GeglBufferIterator *iter;
577 GeglRectangle add_on_area;
578
579 add_on_area = *mask_area;
580 add_on_area.x -= off_x;
581 add_on_area.y -= off_y;
582
583 iter = gegl_buffer_iterator_new (mask, mask_area, 0,
584 mask_format,
585 op == GIMP_CHANNEL_OP_REPLACE ?
586 GEGL_ACCESS_WRITE :
587 GEGL_ACCESS_READWRITE,
588 GEGL_ABYSS_NONE, 2);
589
590 gegl_buffer_iterator_add (iter, add_on, &add_on_area, 0,
591 add_on_format,
592 GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
593
594 auto process = [=] (auto value)
595 {
596 while (gegl_buffer_iterator_next (iter))
597 {
598 gfloat *mask_data = (gfloat *) iter->items[0].data;
599 const gfloat *add_on_data = (const gfloat *) iter->items[1].data;
600 gint count = iter->length;
601
602 while (count--)
603 {
604 const gfloat val = value (mask_data, add_on_data);
605
606 *mask_data = CLAMP (val, 0.0f, 1.0f);
607
608 add_on_data++;
609 mask_data++;
610 }
611 }
612 };
613
614 switch (op)
615 {
616 case GIMP_CHANNEL_OP_REPLACE:
617 process ([] (const gfloat *mask,
618 const gfloat *add_on)
619 {
620 return *add_on;
621 });
622 break;
623
624 case GIMP_CHANNEL_OP_ADD:
625 process ([] (const gfloat *mask,
626 const gfloat *add_on)
627 {
628 return *mask + *add_on;
629 });
630 break;
631
632 case GIMP_CHANNEL_OP_SUBTRACT:
633 process ([] (const gfloat *mask,
634 const gfloat *add_on)
635 {
636 return *mask - *add_on;
637 });
638 break;
639
640 case GIMP_CHANNEL_OP_INTERSECT:
641 process ([] (const gfloat *mask,
642 const gfloat *add_on)
643 {
644 return MIN (*mask, *add_on);
645 });
646 break;
647 }
648 });
649
650 return TRUE;
651 }
652
653 } /* extern "C" */
654