1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimp-gegl-loops.c
5 * Copyright (C) 2012 Michael Natterer <mitch@gimp.org>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include <string.h>
24
25 #include <cairo.h>
26 #include <gdk-pixbuf/gdk-pixbuf.h>
27 #include <gegl.h>
28 #include <gegl-buffer-backend.h>
29
30 extern "C"
31 {
32
33 #include "libgimpbase/gimpbase.h"
34 #include "libgimpcolor/gimpcolor.h"
35 #include "libgimpmath/gimpmath.h"
36
37 #include "gimp-gegl-types.h"
38
39 #include "gimp-babl.h"
40 #include "gimp-gegl-loops.h"
41 #include "gimp-gegl-loops-sse2.h"
42
43 #include "core/gimp-atomic.h"
44 #include "core/gimp-utils.h"
45 #include "core/gimpprogress.h"
46
47
48 #define PIXELS_PER_THREAD \
49 (/* each thread costs as much as */ 64.0 * 64.0 /* pixels */)
50
51 #define SHIFTED_AREA(dest, src) \
52 const GeglRectangle dest##_area_ = { \
53 src##_area->x + (dest##_rect->x - src##_rect->x), \
54 src##_area->y + (dest##_rect->y - src##_rect->y), \
55 src##_area->width, src##_area->height \
56 }; \
57 const GeglRectangle * const dest##_area = &dest##_area_
58
59
60 void
gimp_gegl_buffer_copy(GeglBuffer * src_buffer,const GeglRectangle * src_rect,GeglAbyssPolicy abyss_policy,GeglBuffer * dest_buffer,const GeglRectangle * dest_rect)61 gimp_gegl_buffer_copy (GeglBuffer *src_buffer,
62 const GeglRectangle *src_rect,
63 GeglAbyssPolicy abyss_policy,
64 GeglBuffer *dest_buffer,
65 const GeglRectangle *dest_rect)
66 {
67 GeglRectangle real_dest_rect;
68
69 g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
70 g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
71
72 if (! src_rect)
73 src_rect = gegl_buffer_get_extent (src_buffer);
74
75 if (! dest_rect)
76 dest_rect = src_rect;
77
78 real_dest_rect = *dest_rect;
79 real_dest_rect.width = src_rect->width;
80 real_dest_rect.height = src_rect->height;
81
82 dest_rect = &real_dest_rect;
83
84 if (gegl_buffer_get_format (src_buffer) ==
85 gegl_buffer_get_format (dest_buffer))
86 {
87 gboolean skip_abyss = FALSE;
88 GeglRectangle src_abyss;
89 GeglRectangle dest_abyss;
90
91 if (abyss_policy == GEGL_ABYSS_NONE)
92 {
93 src_abyss = *gegl_buffer_get_abyss (src_buffer);
94 dest_abyss = *gegl_buffer_get_abyss (dest_buffer);
95
96 skip_abyss = ! (gegl_rectangle_contains (&src_abyss, src_rect) &&
97 gegl_rectangle_contains (&dest_abyss, dest_rect));
98 }
99
100 if (skip_abyss)
101 {
102 if (src_buffer < dest_buffer)
103 {
104 gegl_tile_handler_lock (GEGL_TILE_HANDLER (src_buffer));
105 gegl_tile_handler_lock (GEGL_TILE_HANDLER (dest_buffer));
106 }
107 else
108 {
109 gegl_tile_handler_lock (GEGL_TILE_HANDLER (dest_buffer));
110 gegl_tile_handler_lock (GEGL_TILE_HANDLER (src_buffer));
111 }
112
113 gegl_buffer_set_abyss (src_buffer, src_rect);
114 gegl_buffer_set_abyss (dest_buffer, dest_rect);
115 }
116
117 gegl_buffer_copy (src_buffer, src_rect, abyss_policy,
118 dest_buffer, dest_rect);
119
120 if (skip_abyss)
121 {
122 gegl_buffer_set_abyss (src_buffer, &src_abyss);
123 gegl_buffer_set_abyss (dest_buffer, &dest_abyss);
124
125 gegl_tile_handler_unlock (GEGL_TILE_HANDLER (src_buffer));
126 gegl_tile_handler_unlock (GEGL_TILE_HANDLER (dest_buffer));
127 }
128 }
129 else
130 {
131 gegl_parallel_distribute_area (
132 src_rect, PIXELS_PER_THREAD,
133 [=] (const GeglRectangle *src_area)
134 {
135 SHIFTED_AREA (dest, src);
136
137 gegl_buffer_copy (src_buffer, src_area, abyss_policy,
138 dest_buffer, dest_area);
139 });
140 }
141 }
142
143 void
gimp_gegl_clear(GeglBuffer * buffer,const GeglRectangle * rect)144 gimp_gegl_clear (GeglBuffer *buffer,
145 const GeglRectangle *rect)
146 {
147 const Babl *format;
148 gint bpp;
149 gint n_components;
150 gint bpc;
151 gint alpha_offset;
152
153 g_return_if_fail (GEGL_IS_BUFFER (buffer));
154
155 if (! rect)
156 rect = gegl_buffer_get_extent (buffer);
157
158 format = gegl_buffer_get_format (buffer);
159
160 if (! babl_format_has_alpha (format))
161 return;
162
163 bpp = babl_format_get_bytes_per_pixel (format);
164 n_components = babl_format_get_n_components (format);
165 bpc = bpp / n_components;
166 alpha_offset = (n_components - 1) * bpc;
167
168 gegl_parallel_distribute_area (
169 rect, PIXELS_PER_THREAD,
170 [=] (const GeglRectangle *area)
171 {
172 GeglBufferIterator *iter;
173
174 iter = gegl_buffer_iterator_new (buffer, area, 0, format,
175 GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE,
176 1);
177
178 while (gegl_buffer_iterator_next (iter))
179 {
180 guint8 *data = (guint8 *) iter->items[0].data;
181 gint i;
182
183 data += alpha_offset;
184
185 for (i = 0; i < iter->length; i++)
186 {
187 memset (data, 0, bpc);
188
189 data += bpp;
190 }
191 }
192 });
193 }
194
195 void
gimp_gegl_convolve(GeglBuffer * src_buffer,const GeglRectangle * src_rect,GeglBuffer * dest_buffer,const GeglRectangle * dest_rect,const gfloat * kernel,gint kernel_size,gdouble divisor,GimpConvolutionType mode,gboolean alpha_weighting)196 gimp_gegl_convolve (GeglBuffer *src_buffer,
197 const GeglRectangle *src_rect,
198 GeglBuffer *dest_buffer,
199 const GeglRectangle *dest_rect,
200 const gfloat *kernel,
201 gint kernel_size,
202 gdouble divisor,
203 GimpConvolutionType mode,
204 gboolean alpha_weighting)
205 {
206 gfloat *src;
207 gint src_rowstride;
208
209 const Babl *src_format;
210 const Babl *dest_format;
211 gint src_components;
212 gint dest_components;
213 gfloat offset;
214
215 if (! src_rect)
216 src_rect = gegl_buffer_get_extent (src_buffer);
217
218 if (! dest_rect)
219 dest_rect = gegl_buffer_get_extent (dest_buffer);
220
221 src_format = gegl_buffer_get_format (src_buffer);
222
223 if (babl_format_is_palette (src_format))
224 src_format = gimp_babl_format (GIMP_RGB,
225 GIMP_PRECISION_FLOAT_LINEAR,
226 babl_format_has_alpha (src_format));
227 else
228 src_format = gimp_babl_format (gimp_babl_format_get_base_type (src_format),
229 GIMP_PRECISION_FLOAT_LINEAR,
230 babl_format_has_alpha (src_format));
231
232 dest_format = gegl_buffer_get_format (dest_buffer);
233
234 if (babl_format_is_palette (dest_format))
235 dest_format = gimp_babl_format (GIMP_RGB,
236 GIMP_PRECISION_FLOAT_LINEAR,
237 babl_format_has_alpha (dest_format));
238 else
239 dest_format = gimp_babl_format (gimp_babl_format_get_base_type (dest_format),
240 GIMP_PRECISION_FLOAT_LINEAR,
241 babl_format_has_alpha (dest_format));
242
243 src_components = babl_format_get_n_components (src_format);
244 dest_components = babl_format_get_n_components (dest_format);
245
246 /* Get source pixel data */
247 src_rowstride = src_components * src_rect->width;
248 src = g_new (gfloat, src_rowstride * src_rect->height);
249 gegl_buffer_get (src_buffer, src_rect, 1.0, src_format, src,
250 GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
251
252 /* If the mode is NEGATIVE_CONVOL, the offset should be 0.5 */
253 if (mode == GIMP_NEGATIVE_CONVOL)
254 {
255 offset = 0.5;
256 mode = GIMP_NORMAL_CONVOL;
257 }
258 else
259 {
260 offset = 0.0;
261 }
262
263 gegl_parallel_distribute_area (
264 dest_rect, PIXELS_PER_THREAD,
265 [=] (const GeglRectangle *dest_area)
266 {
267 const gint components = src_components;
268 const gint a_component = components - 1;
269 const gint margin = kernel_size / 2;
270 GeglBufferIterator *dest_iter;
271
272 /* Set up dest iterator */
273 dest_iter = gegl_buffer_iterator_new (dest_buffer, dest_area, 0, dest_format,
274 GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1);
275
276 while (gegl_buffer_iterator_next (dest_iter))
277 {
278 /* Convolve the src image using the convolution kernel, writing
279 * to dest Convolve is not tile-enabled--use accordingly
280 */
281 gfloat *dest = (gfloat *) dest_iter->items[0].data;
282 const gint x1 = 0;
283 const gint y1 = 0;
284 const gint x2 = src_rect->width - 1;
285 const gint y2 = src_rect->height - 1;
286 const gint dest_x1 = dest_iter->items[0].roi.x;
287 const gint dest_y1 = dest_iter->items[0].roi.y;
288 const gint dest_x2 = dest_iter->items[0].roi.x + dest_iter->items[0].roi.width;
289 const gint dest_y2 = dest_iter->items[0].roi.y + dest_iter->items[0].roi.height;
290 gint x, y;
291
292 for (y = dest_y1; y < dest_y2; y++)
293 {
294 gfloat *d = dest;
295
296 if (alpha_weighting)
297 {
298 for (x = dest_x1; x < dest_x2; x++)
299 {
300 const gfloat *m = kernel;
301 gdouble total[4] = { 0.0, 0.0, 0.0, 0.0 };
302 gdouble weighted_divisor = 0.0;
303 gint i, j, b;
304
305 for (j = y - margin; j <= y + margin; j++)
306 {
307 for (i = x - margin; i <= x + margin; i++, m++)
308 {
309 gint xx = CLAMP (i, x1, x2);
310 gint yy = CLAMP (j, y1, y2);
311 const gfloat *s = src + yy * src_rowstride + xx * components;
312 const gfloat a = s[a_component];
313
314 if (a)
315 {
316 gdouble mult_alpha = *m * a;
317
318 weighted_divisor += mult_alpha;
319
320 for (b = 0; b < a_component; b++)
321 total[b] += mult_alpha * s[b];
322
323 total[a_component] += mult_alpha;
324 }
325 }
326 }
327
328 if (weighted_divisor == 0.0)
329 weighted_divisor = divisor;
330
331 for (b = 0; b < a_component; b++)
332 total[b] /= weighted_divisor;
333
334 total[a_component] /= divisor;
335
336 for (b = 0; b < components; b++)
337 {
338 total[b] += offset;
339
340 if (mode != GIMP_NORMAL_CONVOL && total[b] < 0.0)
341 total[b] = - total[b];
342
343 *d++ = CLAMP (total[b], 0.0, 1.0);
344 }
345 }
346 }
347 else
348 {
349 for (x = dest_x1; x < dest_x2; x++)
350 {
351 const gfloat *m = kernel;
352 gdouble total[4] = { 0.0, 0.0, 0.0, 0.0 };
353 gint i, j, b;
354
355 for (j = y - margin; j <= y + margin; j++)
356 {
357 for (i = x - margin; i <= x + margin; i++, m++)
358 {
359 gint xx = CLAMP (i, x1, x2);
360 gint yy = CLAMP (j, y1, y2);
361 const gfloat *s = src + yy * src_rowstride + xx * components;
362
363 for (b = 0; b < components; b++)
364 total[b] += *m * s[b];
365 }
366 }
367
368 for (b = 0; b < components; b++)
369 {
370 total[b] = total[b] / divisor + offset;
371
372 if (mode != GIMP_NORMAL_CONVOL && total[b] < 0.0)
373 total[b] = - total[b];
374
375 *d++ = CLAMP (total[b], 0.0, 1.0);
376 }
377 }
378 }
379
380 dest += dest_iter->items[0].roi.width * dest_components;
381 }
382 }
383 });
384
385 g_free (src);
386 }
387
388 static inline gfloat
odd_powf(gfloat x,gfloat y)389 odd_powf (gfloat x,
390 gfloat y)
391 {
392 if (x >= 0.0f)
393 return powf ( x, y);
394 else
395 return -powf (-x, y);
396 }
397
398 void
gimp_gegl_dodgeburn(GeglBuffer * src_buffer,const GeglRectangle * src_rect,GeglBuffer * dest_buffer,const GeglRectangle * dest_rect,gdouble exposure,GimpDodgeBurnType type,GimpTransferMode mode)399 gimp_gegl_dodgeburn (GeglBuffer *src_buffer,
400 const GeglRectangle *src_rect,
401 GeglBuffer *dest_buffer,
402 const GeglRectangle *dest_rect,
403 gdouble exposure,
404 GimpDodgeBurnType type,
405 GimpTransferMode mode)
406 {
407 if (type == GIMP_DODGE_BURN_TYPE_BURN)
408 exposure = -exposure;
409
410 if (! src_rect)
411 src_rect = gegl_buffer_get_extent (src_buffer);
412
413 if (! dest_rect)
414 dest_rect = gegl_buffer_get_extent (dest_buffer);
415
416 gegl_parallel_distribute_area (
417 src_rect, PIXELS_PER_THREAD,
418 [=] (const GeglRectangle *src_area)
419 {
420 GeglBufferIterator *iter;
421
422 SHIFTED_AREA (dest, src);
423
424 iter = gegl_buffer_iterator_new (src_buffer, src_area, 0,
425 babl_format ("R'G'B'A float"),
426 GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2);
427
428 gegl_buffer_iterator_add (iter, dest_buffer, dest_area, 0,
429 babl_format ("R'G'B'A float"),
430 GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
431
432 switch (mode)
433 {
434 gfloat factor;
435
436 case GIMP_TRANSFER_HIGHLIGHTS:
437 factor = 1.0 + exposure * (0.333333);
438
439 while (gegl_buffer_iterator_next (iter))
440 {
441 gfloat *src = (gfloat *) iter->items[0].data;
442 gfloat *dest = (gfloat *) iter->items[1].data;
443 gint count = iter->length;
444
445 while (count--)
446 {
447 *dest++ = *src++ * factor;
448 *dest++ = *src++ * factor;
449 *dest++ = *src++ * factor;
450
451 *dest++ = *src++;
452 }
453 }
454 break;
455
456 case GIMP_TRANSFER_MIDTONES:
457 if (exposure < 0)
458 factor = 1.0 - exposure * (0.333333);
459 else
460 factor = 1.0 / (1.0 + exposure);
461
462 while (gegl_buffer_iterator_next (iter))
463 {
464 gfloat *src = (gfloat *) iter->items[0].data;
465 gfloat *dest = (gfloat *) iter->items[1].data;
466 gint count = iter->length;
467
468 while (count--)
469 {
470 *dest++ = odd_powf (*src++, factor);
471 *dest++ = odd_powf (*src++, factor);
472 *dest++ = odd_powf (*src++, factor);
473
474 *dest++ = *src++;
475 }
476 }
477 break;
478
479 case GIMP_TRANSFER_SHADOWS:
480 if (exposure >= 0)
481 factor = 0.333333 * exposure;
482 else
483 factor = -0.333333 * exposure;
484
485 while (gegl_buffer_iterator_next (iter))
486 {
487 gfloat *src = (gfloat *) iter->items[0].data;
488 gfloat *dest = (gfloat *) iter->items[1].data;
489 gint count = iter->length;
490
491 while (count--)
492 {
493 if (exposure >= 0)
494 {
495 gfloat s;
496
497 s = *src++; *dest++ = factor + s - factor * s;
498 s = *src++; *dest++ = factor + s - factor * s;
499 s = *src++; *dest++ = factor + s - factor * s;
500 }
501 else
502 {
503 gfloat s;
504
505 s = *src++;
506 if (s < factor)
507 *dest++ = 0;
508 else /* factor <= value <=1 */
509 *dest++ = (s - factor) / (1.0 - factor);
510
511 s = *src++;
512 if (s < factor)
513 *dest++ = 0;
514 else /* factor <= value <=1 */
515 *dest++ = (s - factor) / (1.0 - factor);
516
517 s = *src++;
518 if (s < factor)
519 *dest++ = 0;
520 else /* factor <= value <=1 */
521 *dest++ = (s - factor) / (1.0 - factor);
522 }
523
524 *dest++ = *src++;
525 }
526 }
527 break;
528 }
529 });
530 }
531
532 /* helper function of gimp_gegl_smudge_with_paint_process()
533 src and dest can be the same address
534 */
535 static inline void
gimp_gegl_smudge_with_paint_blend(const gfloat * src1,gfloat src1_rate,const gfloat * src2,gfloat src2_rate,gfloat * dest,gboolean no_erasing_src2)536 gimp_gegl_smudge_with_paint_blend (const gfloat *src1,
537 gfloat src1_rate,
538 const gfloat *src2,
539 gfloat src2_rate,
540 gfloat *dest,
541 gboolean no_erasing_src2)
542 {
543 gfloat orginal_src2_alpha;
544 gfloat src1_alpha;
545 gfloat src2_alpha;
546 gfloat result_alpha;
547 gint b;
548
549 orginal_src2_alpha = src2[3];
550 src1_alpha = src1_rate * src1[3];
551 src2_alpha = src2_rate * orginal_src2_alpha;
552 result_alpha = src1_alpha + src2_alpha;
553
554 if (result_alpha == 0)
555 {
556 memset (dest, 0, sizeof (gfloat) * 4);
557 return;
558 }
559
560 for (b = 0; b < 3; b++)
561 dest[b] = (src1[b] * src1_alpha + src2[b] * src2_alpha) / result_alpha;
562
563 if (no_erasing_src2)
564 {
565 result_alpha = MAX (result_alpha, orginal_src2_alpha);
566 }
567
568 dest[3] = result_alpha;
569 }
570
571 /* helper function of gimp_gegl_smudge_with_paint() */
572 static void
gimp_gegl_smudge_with_paint_process(gfloat * accum,const gfloat * canvas,gfloat * paint,gint count,const gfloat * brush_color,gfloat brush_a,gboolean no_erasing,gfloat flow,gfloat rate)573 gimp_gegl_smudge_with_paint_process (gfloat *accum,
574 const gfloat *canvas,
575 gfloat *paint,
576 gint count,
577 const gfloat *brush_color,
578 gfloat brush_a,
579 gboolean no_erasing,
580 gfloat flow,
581 gfloat rate)
582 {
583 while (count--)
584 {
585 /* blend accum_buffer and canvas_buffer to accum_buffer */
586 gimp_gegl_smudge_with_paint_blend (accum, rate, canvas, 1 - rate,
587 accum, no_erasing);
588
589 /* blend accum_buffer and brush color/pixmap to paint_buffer */
590 if (brush_a == 0) /* pure smudge */
591 {
592 memcpy (paint, accum, sizeof (gfloat) * 4);
593 }
594 else
595 {
596 const gfloat *src1 = brush_color ? brush_color : paint;
597
598 gimp_gegl_smudge_with_paint_blend (src1, flow, accum, 1 - flow,
599 paint, no_erasing);
600 }
601
602 accum += 4;
603 canvas += 4;
604 paint += 4;
605 }
606 }
607
608 /* smudge painting calculation. Currently only smudge tool uses this function
609 * Accum = rate*Accum + (1-rate)*Canvas
610 * if brush_color!=NULL
611 * Paint = flow*brushColor + (1-flow)*Accum
612 * else
613 * Paint = flow*Paint + (1-flow)*Accum
614 */
615 void
gimp_gegl_smudge_with_paint(GeglBuffer * accum_buffer,const GeglRectangle * accum_rect,GeglBuffer * canvas_buffer,const GeglRectangle * canvas_rect,const GimpRGB * brush_color,GeglBuffer * paint_buffer,gboolean no_erasing,gdouble flow,gdouble rate)616 gimp_gegl_smudge_with_paint (GeglBuffer *accum_buffer,
617 const GeglRectangle *accum_rect,
618 GeglBuffer *canvas_buffer,
619 const GeglRectangle *canvas_rect,
620 const GimpRGB *brush_color,
621 GeglBuffer *paint_buffer,
622 gboolean no_erasing,
623 gdouble flow,
624 gdouble rate)
625 {
626 gfloat brush_color_float[4];
627 gfloat brush_a = flow;
628 GeglAccessMode paint_buffer_access_mode = (brush_color ?
629 GEGL_ACCESS_WRITE :
630 GEGL_ACCESS_READWRITE);
631 #if COMPILE_SSE2_INTRINISICS
632 gboolean sse2 = (gimp_cpu_accel_get_support () &
633 GIMP_CPU_ACCEL_X86_SSE2);
634 #endif
635
636 if (! accum_rect)
637 accum_rect = gegl_buffer_get_extent (accum_buffer);
638
639 if (! canvas_rect)
640 canvas_rect = gegl_buffer_get_extent (canvas_buffer);
641
642 /* convert brush color from double to float */
643 if (brush_color)
644 {
645 const gdouble *brush_color_ptr = &brush_color->r;
646 gint b;
647
648 for (b = 0; b < 4; b++)
649 brush_color_float[b] = brush_color_ptr[b];
650
651 brush_a *= brush_color_ptr[3];
652 }
653
654 gegl_parallel_distribute_area (
655 accum_rect, PIXELS_PER_THREAD,
656 [=] (const GeglRectangle *accum_area)
657 {
658 GeglBufferIterator *iter;
659
660 SHIFTED_AREA (canvas, accum);
661
662 iter = gegl_buffer_iterator_new (accum_buffer, accum_area, 0,
663 babl_format ("RGBA float"),
664 GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 3);
665
666 gegl_buffer_iterator_add (iter, canvas_buffer, canvas_area, 0,
667 babl_format ("RGBA float"),
668 GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
669
670 gegl_buffer_iterator_add (iter, paint_buffer,
671 GEGL_RECTANGLE (accum_area->x - accum_rect->x,
672 accum_area->y - accum_rect->y,
673 0, 0),
674 0,
675 babl_format ("RGBA float"),
676 paint_buffer_access_mode, GEGL_ABYSS_NONE);
677
678 while (gegl_buffer_iterator_next (iter))
679 {
680 gfloat *accum = (gfloat *) iter->items[0].data;
681 const gfloat *canvas = (const gfloat *) iter->items[1].data;
682 gfloat *paint = (gfloat *) iter->items[2].data;
683 gint count = iter->length;
684
685 #if COMPILE_SSE2_INTRINISICS
686 if (sse2 && ((guintptr) accum |
687 (guintptr) canvas |
688 (guintptr) (brush_color ? brush_color_float : paint) |
689 (guintptr) paint) % 16 == 0)
690 {
691 gimp_gegl_smudge_with_paint_process_sse2 (accum, canvas, paint, count,
692 brush_color ? brush_color_float :
693 NULL,
694 brush_a,
695 no_erasing, flow, rate);
696 }
697 else
698 #endif
699 {
700 gimp_gegl_smudge_with_paint_process (accum, canvas, paint, count,
701 brush_color ? brush_color_float :
702 NULL,
703 brush_a,
704 no_erasing, flow, rate);
705 }
706 }
707 });
708 }
709
710 void
gimp_gegl_apply_mask(GeglBuffer * mask_buffer,const GeglRectangle * mask_rect,GeglBuffer * dest_buffer,const GeglRectangle * dest_rect,gdouble opacity)711 gimp_gegl_apply_mask (GeglBuffer *mask_buffer,
712 const GeglRectangle *mask_rect,
713 GeglBuffer *dest_buffer,
714 const GeglRectangle *dest_rect,
715 gdouble opacity)
716 {
717 if (! mask_rect)
718 mask_rect = gegl_buffer_get_extent (mask_buffer);
719
720 if (! dest_rect)
721 dest_rect = gegl_buffer_get_extent (dest_buffer);
722
723 gegl_parallel_distribute_area (
724 mask_rect, PIXELS_PER_THREAD,
725 [=] (const GeglRectangle *mask_area)
726 {
727 GeglBufferIterator *iter;
728
729 SHIFTED_AREA (dest, mask);
730
731 iter = gegl_buffer_iterator_new (mask_buffer, mask_area, 0,
732 babl_format ("Y float"),
733 GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2);
734
735 gegl_buffer_iterator_add (iter, dest_buffer, dest_area, 0,
736 babl_format ("RGBA float"),
737 GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE);
738
739 while (gegl_buffer_iterator_next (iter))
740 {
741 const gfloat *mask = (const gfloat *) iter->items[0].data;
742 gfloat *dest = (gfloat *) iter->items[1].data;
743 gint count = iter->length;
744
745 while (count--)
746 {
747 dest[3] *= *mask * opacity;
748
749 mask += 1;
750 dest += 4;
751 }
752 }
753 });
754 }
755
756 void
gimp_gegl_combine_mask(GeglBuffer * mask_buffer,const GeglRectangle * mask_rect,GeglBuffer * dest_buffer,const GeglRectangle * dest_rect,gdouble opacity)757 gimp_gegl_combine_mask (GeglBuffer *mask_buffer,
758 const GeglRectangle *mask_rect,
759 GeglBuffer *dest_buffer,
760 const GeglRectangle *dest_rect,
761 gdouble opacity)
762 {
763 if (! mask_rect)
764 mask_rect = gegl_buffer_get_extent (mask_buffer);
765
766 if (! dest_rect)
767 dest_rect = gegl_buffer_get_extent (dest_buffer);
768
769 gegl_parallel_distribute_area (
770 mask_rect, PIXELS_PER_THREAD,
771 [=] (const GeglRectangle *mask_area)
772 {
773 GeglBufferIterator *iter;
774
775 SHIFTED_AREA (dest, mask);
776
777 iter = gegl_buffer_iterator_new (mask_buffer, mask_area, 0,
778 babl_format ("Y float"),
779 GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2);
780
781 gegl_buffer_iterator_add (iter, dest_buffer, dest_area, 0,
782 babl_format ("Y float"),
783 GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE);
784
785 while (gegl_buffer_iterator_next (iter))
786 {
787 const gfloat *mask = (const gfloat *) iter->items[0].data;
788 gfloat *dest = (gfloat *) iter->items[1].data;
789 gint count = iter->length;
790
791 while (count--)
792 {
793 *dest *= *mask * opacity;
794
795 mask += 1;
796 dest += 1;
797 }
798 }
799 });
800 }
801
802 void
gimp_gegl_combine_mask_weird(GeglBuffer * mask_buffer,const GeglRectangle * mask_rect,GeglBuffer * dest_buffer,const GeglRectangle * dest_rect,gdouble opacity,gboolean stipple)803 gimp_gegl_combine_mask_weird (GeglBuffer *mask_buffer,
804 const GeglRectangle *mask_rect,
805 GeglBuffer *dest_buffer,
806 const GeglRectangle *dest_rect,
807 gdouble opacity,
808 gboolean stipple)
809 {
810 if (! mask_rect)
811 mask_rect = gegl_buffer_get_extent (mask_buffer);
812
813 if (! dest_rect)
814 dest_rect = gegl_buffer_get_extent (dest_buffer);
815
816 gegl_parallel_distribute_area (
817 mask_rect, PIXELS_PER_THREAD,
818 [=] (const GeglRectangle *mask_area)
819 {
820 GeglBufferIterator *iter;
821
822 SHIFTED_AREA (dest, mask);
823
824 iter = gegl_buffer_iterator_new (mask_buffer, mask_area, 0,
825 babl_format ("Y float"),
826 GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2);
827
828 gegl_buffer_iterator_add (iter, dest_buffer, dest_area, 0,
829 babl_format ("Y float"),
830 GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE);
831
832 while (gegl_buffer_iterator_next (iter))
833 {
834 const gfloat *mask = (const gfloat *) iter->items[0].data;
835 gfloat *dest = (gfloat *) iter->items[1].data;
836 gint count = iter->length;
837
838 if (stipple)
839 {
840 while (count--)
841 {
842 dest[0] += (1.0 - dest[0]) * *mask * opacity;
843
844 mask += 1;
845 dest += 1;
846 }
847 }
848 else
849 {
850 while (count--)
851 {
852 if (opacity > dest[0])
853 dest[0] += (opacity - dest[0]) * *mask * opacity;
854
855 mask += 1;
856 dest += 1;
857 }
858 }
859 }
860 });
861 }
862
863 void
gimp_gegl_index_to_mask(GeglBuffer * indexed_buffer,const GeglRectangle * indexed_rect,const Babl * indexed_format,GeglBuffer * mask_buffer,const GeglRectangle * mask_rect,gint index)864 gimp_gegl_index_to_mask (GeglBuffer *indexed_buffer,
865 const GeglRectangle *indexed_rect,
866 const Babl *indexed_format,
867 GeglBuffer *mask_buffer,
868 const GeglRectangle *mask_rect,
869 gint index)
870 {
871 if (! indexed_rect)
872 indexed_rect = gegl_buffer_get_extent (indexed_buffer);
873
874 if (! mask_rect)
875 mask_rect = gegl_buffer_get_extent (mask_buffer);
876
877 gegl_parallel_distribute_area (
878 indexed_rect, PIXELS_PER_THREAD,
879 [=] (const GeglRectangle *indexed_area)
880 {
881 GeglBufferIterator *iter;
882
883 SHIFTED_AREA (mask, indexed);
884
885 iter = gegl_buffer_iterator_new (indexed_buffer, indexed_area, 0,
886 indexed_format,
887 GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2);
888
889 gegl_buffer_iterator_add (iter, mask_buffer, mask_area, 0,
890 babl_format ("Y float"),
891 GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
892
893 while (gegl_buffer_iterator_next (iter))
894 {
895 const guchar *indexed = (const guchar *) iter->items[0].data;
896 gfloat *mask = (gfloat *) iter->items[1].data;
897 gint count = iter->length;
898
899 while (count--)
900 {
901 if (*indexed == index)
902 *mask = 1.0;
903 else
904 *mask = 0.0;
905
906 indexed++;
907 mask++;
908 }
909 }
910 });
911 }
912
913 static void
gimp_gegl_convert_color_profile_progress(GimpProgress * progress,gdouble value)914 gimp_gegl_convert_color_profile_progress (GimpProgress *progress,
915 gdouble value)
916 {
917 if (gegl_is_main_thread ())
918 gimp_progress_set_value (progress, value);
919 }
920
921 void
gimp_gegl_convert_color_profile(GeglBuffer * src_buffer,const GeglRectangle * src_rect,GimpColorProfile * src_profile,GeglBuffer * dest_buffer,const GeglRectangle * dest_rect,GimpColorProfile * dest_profile,GimpColorRenderingIntent intent,gboolean bpc,GimpProgress * progress)922 gimp_gegl_convert_color_profile (GeglBuffer *src_buffer,
923 const GeglRectangle *src_rect,
924 GimpColorProfile *src_profile,
925 GeglBuffer *dest_buffer,
926 const GeglRectangle *dest_rect,
927 GimpColorProfile *dest_profile,
928 GimpColorRenderingIntent intent,
929 gboolean bpc,
930 GimpProgress *progress)
931 {
932 GimpColorTransform *transform;
933 guint flags = 0;
934 const Babl *src_format;
935 const Babl *dest_format;
936
937 src_format = gegl_buffer_get_format (src_buffer);
938 dest_format = gegl_buffer_get_format (dest_buffer);
939
940 if (bpc)
941 flags |= GIMP_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION;
942
943 flags |= GIMP_COLOR_TRANSFORM_FLAGS_NOOPTIMIZE;
944
945 transform = gimp_color_transform_new (src_profile, src_format,
946 dest_profile, dest_format,
947 intent,
948 (GimpColorTransformFlags) flags);
949
950 if (! src_rect)
951 src_rect = gegl_buffer_get_extent (src_buffer);
952
953 if (! dest_rect)
954 dest_rect = gegl_buffer_get_extent (dest_buffer);
955
956 if (transform)
957 {
958 if (progress)
959 {
960 g_signal_connect_swapped (
961 transform, "progress",
962 G_CALLBACK (gimp_gegl_convert_color_profile_progress),
963 progress);
964 }
965
966 GIMP_TIMER_START ();
967
968 gegl_parallel_distribute_area (
969 src_rect, PIXELS_PER_THREAD,
970 [=] (const GeglRectangle *src_area)
971 {
972 SHIFTED_AREA (dest, src);
973
974 gimp_color_transform_process_buffer (transform,
975 src_buffer, src_area,
976 dest_buffer, dest_area);
977 });
978
979 GIMP_TIMER_END ("converting buffer");
980
981 g_object_unref (transform);
982 }
983 else
984 {
985 gimp_gegl_buffer_copy (src_buffer, src_rect, GEGL_ABYSS_NONE,
986 dest_buffer, dest_rect);
987
988 if (progress)
989 gimp_progress_set_value (progress, 1.0);
990 }
991 }
992
993 void
gimp_gegl_average_color(GeglBuffer * buffer,const GeglRectangle * rect,gboolean clip_to_buffer,GeglAbyssPolicy abyss_policy,const Babl * format,gpointer color)994 gimp_gegl_average_color (GeglBuffer *buffer,
995 const GeglRectangle *rect,
996 gboolean clip_to_buffer,
997 GeglAbyssPolicy abyss_policy,
998 const Babl *format,
999 gpointer color)
1000 {
1001 typedef struct
1002 {
1003 gfloat color[4];
1004 gint n;
1005 } Sum;
1006
1007 const Babl *average_format = babl_format ("RaGaBaA float");
1008 GeglRectangle roi;
1009 GSList * volatile sums = NULL;
1010 GSList *list;
1011 Sum average = {};
1012 gint c;
1013
1014 g_return_if_fail (GEGL_IS_BUFFER (buffer));
1015 g_return_if_fail (color != NULL);
1016
1017 if (! rect)
1018 rect = gegl_buffer_get_extent (buffer);
1019
1020 if (! format)
1021 format = gegl_buffer_get_format (buffer);
1022
1023 if (clip_to_buffer)
1024 gegl_rectangle_intersect (&roi, rect, gegl_buffer_get_extent (buffer));
1025 else
1026 roi = *rect;
1027
1028 gegl_parallel_distribute_area (
1029 &roi, PIXELS_PER_THREAD,
1030 [&] (const GeglRectangle *area)
1031 {
1032 Sum *sum;
1033 GeglBufferIterator *iter;
1034 gfloat color[4] = {};
1035 gint n = 0;
1036
1037 iter = gegl_buffer_iterator_new (buffer, area, 0, average_format,
1038 GEGL_BUFFER_READ, abyss_policy, 1);
1039
1040 while (gegl_buffer_iterator_next (iter))
1041 {
1042 const gfloat *p = (const gfloat *) iter->items[0].data;
1043 gint i;
1044
1045 for (i = 0; i < iter->length; i++)
1046 {
1047 gint c;
1048
1049 for (c = 0; c < 4; c++)
1050 color[c] += p[c];
1051
1052 p += 4;
1053 }
1054
1055 n += iter->length;
1056 }
1057
1058 sum = g_slice_new (Sum);
1059
1060 memcpy (sum->color, color, sizeof (color));
1061 sum->n = n;
1062
1063 gimp_atomic_slist_push_head (&sums, sum);
1064 });
1065
1066 for (list = sums; list; list = g_slist_next (list))
1067 {
1068 Sum *sum = (Sum *) list->data;
1069
1070 for (c = 0; c < 4; c++)
1071 average.color[c] += sum->color[c];
1072
1073 average.n += sum->n;
1074
1075 g_slice_free (Sum, sum);
1076 }
1077
1078 g_slist_free (sums);
1079
1080 if (average.n > 0)
1081 {
1082 for (c = 0; c < 4; c++)
1083 average.color[c] /= average.n;
1084 }
1085
1086 babl_process (babl_fish (average_format, format), average.color, color, 1);
1087 }
1088
1089 } /* extern "C" */
1090