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