Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
blendif_rgb_hsl.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2020 Harold le Clément de Saint-Marcq.
4 Copyright (C) 2020-2021 Hubert Kowalski.
5 Copyright (C) 2020-2021 Ralf Brown.
6 Copyright (C) 2021 Chris Elston.
7 Copyright (C) 2021 Pascal Obry.
8 Copyright (C) 2022 Martin Bařinka.
9 Copyright (C) 2026 Aurélien PIERRE.
10
11 darktable is free software: you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version 3 of the License, or
14 (at your option) any later version.
15
16 darktable is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with darktable. If not, see <http://www.gnu.org/licenses/>.
23*/
24
27#include "common/imagebuf.h"
28#include "math/math.h"
29#include "develop/blend.h"
30#include "develop/imageop.h"
31#include "math/openmp_maths.h"
32#include <math.h>
33
34#define DT_BLENDIF_RGB_CH 4
35#define DT_BLENDIF_RGB_BCH 3
36
37
38typedef void(_blend_row_func)(const float *const restrict a, const float *const restrict b,
39 float *const restrict out, const float *const restrict mask, const size_t stride);
40
41
42__OMP_DECLARE_SIMD__(aligned(XYZ: 16))
43static inline void _CLAMP_XYZ(float *const restrict XYZ)
44{
45 for(size_t i = 0; i < 3; i++) XYZ[i] = clamp_simd(XYZ[i]);
46}
47
48__OMP_DECLARE_SIMD__(aligned(src, dst: 16))
49static inline void _PX_COPY(const float *const restrict src, float *const restrict dst)
50{
51 for(size_t i = 0; i < 3; i++) dst[i] = src[i];
52}
53
54
55__OMP_DECLARE_SIMD__(uniform(parameters, invert_mask))
56static inline float _blendif_compute_factor(const float value, const unsigned int invert_mask,
57 const float *const restrict parameters)
58{
59 float factor = 0.0f;
60 if(value <= parameters[0])
61 {
62 // we are below the keyframe
63 factor = 0.0f;
64 }
65 else if(value < parameters[1])
66 {
67 // we are on the bottom slope of the keyframe
68 factor = (value - parameters[0]) * parameters[4];
69 }
70 else if(value <= parameters[2])
71 {
72 // we are on the ramp - constant part - of the keyframe
73 factor = 1.0f;
74 }
75 else if(value < parameters[3])
76 {
77 // we are on the top slope of the keyframe
78 factor = 1.0f - (value - parameters[2]) * parameters[5];
79 }
80 else
81 {
82 // we are above the keyframe
83 factor = 0.0f;
84 }
85 return invert_mask ? 1.0f - factor : factor; // inverted channel?
86}
87
88__OMP_DECLARE_SIMD__(aligned(pixels: 16) uniform(parameters, invert_mask, stride, profile))
89static inline void _blendif_gray(const float *const restrict pixels, float *const restrict mask,
90 const size_t stride, const float *const restrict parameters,
91 const unsigned int invert_mask,
92 const dt_iop_order_iccprofile_info_t *const restrict profile)
93{
94 for(size_t x = 0, j = 0; x < stride; x++, j += DT_BLENDIF_RGB_CH)
95 {
96 const float value = dt_ioppr_get_rgb_matrix_luminance(pixels + j, profile->matrix_in, profile->lut_in,
97 profile->unbounded_coeffs_in, profile->lutsize,
98 profile->nonlinearlut);
99 mask[x] *= _blendif_compute_factor(value, invert_mask, parameters);
100 }
101}
102
103__OMP_DECLARE_SIMD__(aligned(pixels: 16) uniform(parameters, invert_mask, stride))
104static inline void _blendif_gray_fb(const float *const restrict pixels, float *const restrict mask,
105 const size_t stride, const float *const restrict parameters,
106 const unsigned int invert_mask)
107{
108 for(size_t x = 0, j = 0; x < stride; x++, j += DT_BLENDIF_RGB_CH)
109 {
110 const float value = 0.3f * pixels[j + 0] + 0.59f * pixels[j + 1] + 0.11f * pixels[j + 2];
111 mask[x] *= _blendif_compute_factor(value, invert_mask, parameters);
112 }
113}
114
115__OMP_DECLARE_SIMD__(aligned(pixels: 16) uniform(parameters, invert_mask, stride))
116static inline void _blendif_rgb_red(const float *const restrict pixels, float *const restrict mask,
117 const size_t stride, const float *const restrict parameters,
118 const unsigned int invert_mask)
119{
120 for(size_t x = 0, j = 0; x < stride; x++, j += DT_BLENDIF_RGB_CH)
121 {
122 mask[x] *= _blendif_compute_factor(pixels[j + 0], invert_mask, parameters);
123 }
124}
125
126__OMP_DECLARE_SIMD__(aligned(pixels: 16) uniform(parameters, invert_mask, stride))
127static inline void _blendif_rgb_green(const float *const restrict pixels, float *const restrict mask,
128 const size_t stride, const float *const restrict parameters,
129 const unsigned int invert_mask)
130{
131 for(size_t x = 0, j = 0; x < stride; x++, j += DT_BLENDIF_RGB_CH)
132 {
133 mask[x] *= _blendif_compute_factor(pixels[j + 1], invert_mask, parameters);
134 }
135}
136
137__OMP_DECLARE_SIMD__(aligned(pixels: 16) uniform(parameters, invert_mask, stride))
138static inline void _blendif_rgb_blue(const float *const restrict pixels, float *const restrict mask,
139 const size_t stride, const float *const restrict parameters,
140 const unsigned int invert_mask)
141{
142 for(size_t x = 0, j = 0; x < stride; x++, j += DT_BLENDIF_RGB_CH)
143 {
144 mask[x] *= _blendif_compute_factor(pixels[j + 2], invert_mask, parameters);
145 }
146}
147
148__OMP_DECLARE_SIMD__(aligned(pixels, invert_mask: 16) uniform(parameters, invert_mask, stride))
149static inline void _blendif_hsl(const float *const restrict pixels, float *const restrict mask,
150 const size_t stride, const float *const restrict parameters,
151 const unsigned int *const restrict invert_mask)
152{
153 for(size_t x = 0, j = 0; x < stride; x++, j += DT_BLENDIF_RGB_CH)
154 {
156 dt_RGB_2_HSL(pixels + j, HSL);
157 float factor = 1.0f;
158 for(size_t i = 0; i < 3; i++)
159 factor *= _blendif_compute_factor(HSL[i], invert_mask[i], parameters + DEVELOP_BLENDIF_PARAMETER_ITEMS * i);
160 mask[x] *= factor;
161 }
162}
163
164__OMP_DECLARE_SIMD__(aligned(pixels: 16) uniform(stride, blendif, parameters, profile))
165static void _blendif_combine_channels(const float *const restrict pixels, float *const restrict mask,
166 const size_t stride, const unsigned int blendif,
167 const float *const restrict parameters,
168 const dt_iop_order_iccprofile_info_t *const restrict profile)
169{
170 if(blendif & (1 << DEVELOP_BLENDIF_GRAY_in))
171 {
172 const unsigned int invert_mask = (blendif >> 16) & (1 << DEVELOP_BLENDIF_GRAY_in);
173 if(!IS_NULL_PTR(profile))
174 {
175 _blendif_gray(pixels, mask, stride, parameters + DEVELOP_BLENDIF_PARAMETER_ITEMS * DEVELOP_BLENDIF_GRAY_in,
176 invert_mask, profile);
177 }
178 else
179 {
180 _blendif_gray_fb(pixels, mask, stride,
181 parameters + DEVELOP_BLENDIF_PARAMETER_ITEMS * DEVELOP_BLENDIF_GRAY_in, invert_mask);
182 }
183 }
184
185 if(blendif & (1 << DEVELOP_BLENDIF_RED_in))
186 {
187 const unsigned int invert_mask = (blendif >> 16) & (1 << DEVELOP_BLENDIF_RED_in);
188 _blendif_rgb_red(pixels, mask, stride, parameters + DEVELOP_BLENDIF_PARAMETER_ITEMS * DEVELOP_BLENDIF_RED_in,
189 invert_mask);
190 }
191
192 if(blendif & (1 << DEVELOP_BLENDIF_GREEN_in))
193 {
194 const unsigned int invert_mask = (blendif >> 16) & (1 << DEVELOP_BLENDIF_GREEN_in);
195 _blendif_rgb_green(pixels, mask, stride,
197 }
198
199 if(blendif & (1 << DEVELOP_BLENDIF_BLUE_in))
200 {
201 const unsigned int invert_mask = (blendif >> 16) & (1 << DEVELOP_BLENDIF_BLUE_in);
202 _blendif_rgb_blue(pixels, mask, stride, parameters + DEVELOP_BLENDIF_PARAMETER_ITEMS * DEVELOP_BLENDIF_BLUE_in,
203 invert_mask);
204 }
205
206 if(blendif & ((1 << DEVELOP_BLENDIF_H_in) | (1 << DEVELOP_BLENDIF_S_in) | (1 << DEVELOP_BLENDIF_l_in)))
207 {
208 const unsigned int invert_mask[3] DT_ALIGNED_PIXEL = {
209 (blendif >> 16) & (1 << DEVELOP_BLENDIF_H_in),
210 (blendif >> 16) & (1 << DEVELOP_BLENDIF_S_in),
211 (blendif >> 16) & (1 << DEVELOP_BLENDIF_l_in),
212 };
213 _blendif_hsl(pixels, mask, stride, parameters + DEVELOP_BLENDIF_PARAMETER_ITEMS * DEVELOP_BLENDIF_H_in,
214 invert_mask);
215 }
216}
217
219 const struct dt_dev_pixelpipe_iop_t *piece,
220 const float *const restrict a,
221 const float *const restrict b, float *const restrict mask)
222{
223 const dt_iop_roi_t *const roi_in = &piece->roi_in;
224 const dt_iop_roi_t *const roi_out = &piece->roi_out;
225 const dt_develop_blend_params_t *const d = (const dt_develop_blend_params_t *const)piece->blendop_data;
226
227 if(piece->dsc_in.channels != DT_BLENDIF_RGB_CH) return;
228
229 const int xoffs = roi_out->x - roi_in->x;
230 const int yoffs = roi_out->y - roi_in->y;
231 const int iwidth = roi_in->width;
232 const int owidth = roi_out->width;
233 const int oheight = roi_out->height;
234
235 const unsigned int any_channel_active = d->blendif & DEVELOP_BLENDIF_RGB_MASK;
236 const unsigned int mask_inclusive = d->mask_combine & DEVELOP_COMBINE_INCL;
237 const unsigned int mask_inversed = d->mask_combine & DEVELOP_COMBINE_INV;
238
239 // invert the individual channels if the combine mode is inclusive
240 const unsigned int blendif = d->blendif ^ (mask_inclusive ? DEVELOP_BLENDIF_RGB_MASK << 16 : 0);
241
242 // a channel cancels the mask if the whole span is selected and the channel is inverted
243 const unsigned int canceling_channel = (blendif >> 16) & ~blendif & DEVELOP_BLENDIF_RGB_MASK;
244
245 const size_t buffsize = (size_t)owidth * oheight;
246
247 // get the clipped opacity value 0 - 1
248 const float global_opacity = clamp_simd(d->opacity / 100.0f);
249
250 if(!(d->mask_mode & DEVELOP_MASK_PARAMETRIC) || (!canceling_channel && !any_channel_active))
251 {
252 // mask is not conditional, invert the mask if required
253 if(mask_inversed)
254 {
256 for(size_t x = 0; x < buffsize; x++) mask[x] = global_opacity * (1.0f - mask[x]);
257 }
258 else
259 {
260 dt_iop_image_mul_const(mask,global_opacity,owidth,oheight,1); // mask[k] *= global_opacity;
261 }
262 }
263 else if(canceling_channel || !any_channel_active)
264 {
265 // one of the conditional channel selects nothing
266 // this means that the conditional opacity of all pixels is the same
267 // and depends on whether the mask combination is inclusive and whether the mask is inverted
268 const float opac = ((mask_inversed == 0) ^ (mask_inclusive == 0)) ? global_opacity : 0.0f;
269 dt_iop_image_fill(mask,opac,owidth,oheight,1); // mask[k] = opac;
270 }
271 else
272 {
273 // we need to process all conditional channels
274
275 // parameters, for every channel the 4 limits + pre-computed increasing slope and decreasing slope
278
279 dt_iop_order_iccprofile_info_t blend_profile;
280 const int use_profile = dt_develop_blendif_init_masking_profile(pipe, piece, &blend_profile,
282 const dt_iop_order_iccprofile_info_t *profile = use_profile ? &blend_profile : NULL;
283
284 // allocate space for a temporary mask buffer to split the computation of every channel
285 float *const restrict temp_mask = dt_pixelpipe_cache_alloc_align_float_cache(buffsize, 0);
286 if(IS_NULL_PTR(temp_mask))
287 {
288 return;
289 }
291 {
292 // initialize the parametric mask
293 __OMP_FOR_SIMD__(aligned(temp_mask:64))
294 for(size_t x = 0; x < buffsize; x++) temp_mask[x] = 1.0f;
295
296 // combine channels
298 for(size_t y = 0; y < oheight; y++)
299 {
300 const size_t start = ((y + yoffs) * iwidth + xoffs) * DT_BLENDIF_RGB_CH;
301 _blendif_combine_channels(a + start, temp_mask + (y * owidth), owidth, blendif, parameters, profile);
302 }
304 for(size_t y = 0; y < oheight; y++)
305 {
306 const size_t start = (y * owidth) * DT_BLENDIF_RGB_CH;
307 _blendif_combine_channels(b + start, temp_mask + (y * owidth), owidth, blendif >> DEVELOP_BLENDIF_GRAY_out,
309 profile);
310 }
311
312 // apply global opacity
313 if(mask_inclusive)
314 {
315 if(mask_inversed)
316 {
317 __OMP_FOR_SIMD__(aligned(mask, temp_mask:64))
318 for(size_t x = 0; x < buffsize; x++) mask[x] = global_opacity * (1.0f - mask[x]) * temp_mask[x];
319 }
320 else
321 {
322 __OMP_FOR_SIMD__(aligned(mask, temp_mask:64))
323 for(size_t x = 0; x < buffsize; x++) mask[x] = global_opacity * (1.0f - (1.0f - mask[x]) * temp_mask[x]);
324 }
325 }
326 else
327 {
328 if(mask_inversed)
329 {
330 __OMP_FOR_SIMD__(aligned(mask, temp_mask:64))
331 for(size_t x = 0; x < buffsize; x++) mask[x] = global_opacity * (1.0f - mask[x] * temp_mask[x]);
332 }
333 else
334 {
335 __OMP_FOR_SIMD__(aligned(mask, temp_mask:64))
336 for(size_t x = 0; x < buffsize; x++) mask[x] = global_opacity * mask[x] * temp_mask[x];
337 }
338 }
339 }
340
342 }
343}
344
345
346/* normal blend with clamping */
347__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
348static void _blend_normal_bounded(const float *const restrict a, const float *const restrict b,
349 float *const restrict out, const float *const restrict mask, const size_t stride)
350{
351 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
352 {
353 const float local_opacity = mask[i];
354 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++)
355 {
356 out[j + k] = clamp_simd(a[j + k] * (1.0f - local_opacity) + b[j + k] * local_opacity);
357 }
358 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
359 }
360}
361
362/* normal blend without any clamping */
363__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
364static void _blend_normal_unbounded(const float *const restrict a, const float *const restrict b,
365 float *const restrict out, const float *const restrict mask,
366 const size_t stride)
367{
368 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
369 {
370 const float local_opacity = mask[i];
371 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++)
372 {
373 out[j + k] = a[j + k] * (1.0f - local_opacity) + b[j + k] * local_opacity;
374 }
375 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
376 }
377}
378
379/* lighten */
380__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
381static void _blend_lighten(const float *const restrict a, const float *const restrict b,
382 float *const restrict out, const float *const restrict mask, const size_t stride)
383{
384 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
385 {
386 const float local_opacity = mask[i];
387 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++)
388 {
389 out[j + k] = clamp_simd(a[j + k] * (1.0f - local_opacity) + fmaxf(a[j + k], b[j + k]) * local_opacity);
390 }
391 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
392 }
393}
394
395/* darken */
396__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
397static void _blend_darken(const float *const restrict a, const float *const restrict b,
398 float *const restrict out, const float *const restrict mask, const size_t stride)
399{
400 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
401 {
402 const float local_opacity = mask[i];
403 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++)
404 {
405 out[j + k] = clamp_simd(a[j + k] * (1.0f - local_opacity) + fminf(a[j + k], b[j + k]) * local_opacity);
406 }
407 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
408 }
409}
410
411/* multiply */
412__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
413static void _blend_multiply(const float *const restrict a, const float *const restrict b,
414 float *const restrict out, const float *const restrict mask, const size_t stride)
415{
416 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
417 {
418 const float local_opacity = mask[i];
419 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++)
420 {
421 out[j + k] = clamp_simd(a[j + k] * (1.0f - local_opacity) + (a[j + k] * b[j + k]) * local_opacity);
422 }
423 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
424 }
425}
426
427/* average */
428__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
429static void _blend_average(const float *const restrict a, const float *const restrict b,
430 float *const restrict out, const float *const restrict mask, const size_t stride)
431{
432 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
433 {
434 const float local_opacity = mask[i];
435 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++)
436 {
437 out[j + k] = clamp_simd(a[j + k] * (1.0f - local_opacity) + (a[j + k] + b[j + k]) / 2.0f * local_opacity);
438 }
439 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
440 }
441}
442
443/* add */
444__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
445static void _blend_add(const float *const restrict a, const float *const restrict b,
446 float *const restrict out, const float *const restrict mask, const size_t stride)
447{
448 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
449 {
450 const float local_opacity = mask[i];
451 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++)
452 {
453 out[j + k] = clamp_simd(a[j + k] * (1.0f - local_opacity) + (a[j + k] + b[j + k]) * local_opacity);
454 }
455 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
456 }
457}
458
459/* subtract */
460__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
461static void _blend_subtract(const float *const restrict a, const float *const restrict b,
462 float *const restrict out, const float *const restrict mask, const size_t stride)
463{
464 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
465 {
466 const float local_opacity = mask[i];
467 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++)
468 {
469 out[j + k] = clamp_simd(a[j + k] * (1.0f - local_opacity) + ((b[j + k] + a[j + k]) - 1.0f) * local_opacity);
470 }
471 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
472 }
473}
474
475/* difference */
476__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
477static void _blend_difference(const float *const restrict a, const float *const restrict b,
478 float *const restrict out, const float *const restrict mask, const size_t stride)
479{
480 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
481 {
482 const float local_opacity = mask[i];
483 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++)
484 {
485 out[j + k] = clamp_simd(a[j + k] * (1.0f - local_opacity) + fabsf(a[j + k] - b[j + k]) * local_opacity);
486 }
487 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
488 }
489}
490
491/* screen */
492__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
493static void _blend_screen(const float *const restrict a, const float *const restrict b,
494 float *const restrict out, const float *const restrict mask, const size_t stride)
495{
496 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
497 {
498 const float local_opacity = mask[i];
499 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++)
500 {
501 const float la = clamp_simd(a[j + k]);
502 const float lb = clamp_simd(b[j + k]);
503 out[j + k] = clamp_simd(la * (1.0f - local_opacity) + (1.0f - (1.0f - la) * (1.0f - lb)) * local_opacity);
504 }
505 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
506 }
507}
508
509/* overlay */
510__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
511static void _blend_overlay(const float *const restrict a, const float *const restrict b,
512 float *const restrict out, const float *const restrict mask, const size_t stride)
513{
514 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
515 {
516 const float local_opacity = mask[i];
517 const float local_opacity2 = local_opacity * local_opacity;
518 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++)
519 {
520 const float la = clamp_simd(a[j + k]);
521 const float lb = clamp_simd(b[j + k]);
522 out[j + k] = clamp_simd(
523 la * (1.0f - local_opacity2)
524 + (la > 0.5f ? 1.0f - (1.0f - 2.0f * (la - 0.5f)) * (1.0f - lb)
525 : 2.0f * la * lb)
526 * local_opacity2);
527 }
528 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
529 }
530}
531
532/* softlight */
533__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
534static void _blend_softlight(const float *const restrict a, const float *const restrict b,
535 float *const restrict out, const float *const restrict mask, const size_t stride)
536{
537 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
538 {
539 const float local_opacity = mask[i];
540 const float local_opacity2 = local_opacity * local_opacity;
541 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++)
542 {
543 const float la = clamp_simd(a[j + k]);
544 const float lb = clamp_simd(b[j + k]);
545 out[j + k] = clamp_simd(
546 la * (1.0f - local_opacity2)
547 + (lb > 0.5f ? 1.0f - (1.0f - la) * (1.0f - (lb - 0.5f))
548 : la * (lb + 0.5f))
549 * local_opacity2);
550 }
551 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
552 }
553}
554
555/* hardlight */
556__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
557static void _blend_hardlight(const float *const restrict a, const float *const restrict b,
558 float *const restrict out, const float *const restrict mask, const size_t stride)
559{
560 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
561 {
562 const float local_opacity = mask[i];
563 const float local_opacity2 = local_opacity * local_opacity;
564 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++)
565 {
566 const float la = clamp_simd(a[j + k]);
567 const float lb = clamp_simd(b[j + k]);
568 out[j + k] = clamp_simd(
569 la * (1.0f - local_opacity2)
570 + (lb > 0.5f ? 1.0f - (1.0f - 2.0f * (la - 0.5f)) * (1.0f - lb)
571 : 2.0f * la * lb)
572 * local_opacity2);
573 }
574 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
575 }
576}
577
578/* vividlight */
579__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
580static void _blend_vividlight(const float *const restrict a, const float *const restrict b,
581 float *const restrict out, const float *const restrict mask, const size_t stride)
582{
583 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
584 {
585 float local_opacity = mask[i];
586 float local_opacity2 = local_opacity * local_opacity;
587 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++)
588 {
589 const float la = clamp_simd(a[j + k]);
590 const float lb = clamp_simd(b[j + k]);
591 out[j + k] = clamp_simd(
592 la * (1.0f - local_opacity2)
593 + (lb > 0.5f ? (lb >= 1.0f ? 1.0f : la / (2.0f * (1.0f - lb)))
594 : (lb <= 0.0f ? 0.0f : 1.0f - (1.0f - la) / (2.0f * lb)))
595 * local_opacity2);
596 }
597 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
598 }
599}
600
601/* linearlight */
602__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
603static void _blend_linearlight(const float *const restrict a, const float *const restrict b,
604 float *const restrict out, const float *const restrict mask, const size_t stride)
605{
606 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
607 {
608 const float local_opacity = mask[i];
609 const float local_opacity2 = local_opacity * local_opacity;
610 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++)
611 {
612 const float la = clamp_simd(a[j + k]);
613 const float lb = clamp_simd(b[j + k]);
614 out[j + k] = clamp_simd(la * (1.0f - local_opacity2) + (la + 2.0f * lb - 1.0f) * local_opacity2);
615 }
616 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
617 }
618}
619
620/* pinlight */
621__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
622static void _blend_pinlight(const float *const restrict a, const float *const restrict b,
623 float *const restrict out, const float *const restrict mask, const size_t stride)
624{
625 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
626 {
627 const float local_opacity = mask[i];
628 const float local_opacity2 = local_opacity * local_opacity;
629 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++)
630 {
631 const float la = clamp_simd(a[j + k]);
632 const float lb = clamp_simd(b[j + k]);
633 out[j + k] = clamp_simd(
634 la * (1.0f - local_opacity2)
635 + (lb > 0.5f ? fmaxf(la, 2.0f * (lb - 0.5f))
636 : fminf(la, 2.0f * lb))
637 * local_opacity2);
638 }
639 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
640 }
641}
642
643/* lightness blend */
644__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
645static void _blend_lightness(const float *const restrict a, const float *const restrict b,
646 float *const restrict out, const float *const restrict mask, const size_t stride)
647{
648 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
649 {
650 const float local_opacity = mask[i];
651 dt_aligned_pixel_t ta, tb;
652 dt_aligned_pixel_t tta, ttb;
653
654 _PX_COPY(a + j, ta);
655 _PX_COPY(b + j, tb);
656
657 _CLAMP_XYZ(ta);
658 _CLAMP_XYZ(tb);
659
660 dt_RGB_2_HSL(ta, tta);
661 dt_RGB_2_HSL(tb, ttb);
662
663 ttb[0] = tta[0];
664 ttb[1] = tta[1];
665 ttb[2] = (tta[2] * (1.0f - local_opacity)) + ttb[2] * local_opacity;
666
667 dt_HSL_2_RGB(ttb, out + j);
668 _CLAMP_XYZ(out + j);
669
670 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
671 }
672}
673
674/* chromaticity blend */
675__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
676static void _blend_chromaticity(const float *const restrict a, const float *const restrict b,
677 float *const restrict out, const float *const restrict mask, const size_t stride)
678{
679 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
680 {
681 const float local_opacity = mask[i];
682 dt_aligned_pixel_t ta, tb;
683 dt_aligned_pixel_t tta, ttb;
684
685 _PX_COPY(a + j, ta);
686 _PX_COPY(b + j, tb);
687
688 _CLAMP_XYZ(ta);
689 _CLAMP_XYZ(tb);
690
691 dt_RGB_2_HSL(ta, tta);
692 dt_RGB_2_HSL(tb, ttb);
693
694 ttb[0] = tta[0];
695 ttb[1] = (tta[1] * (1.0f - local_opacity)) + ttb[1] * local_opacity;
696 ttb[2] = tta[2];
697
698 dt_HSL_2_RGB(ttb, out + j);
699 _CLAMP_XYZ(out + j);
700
701 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
702 }
703}
704
705/* hue blend */
706__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
707static void _blend_hue(const float *const restrict a, const float *const restrict b,
708 float *const restrict out, const float *const restrict mask, const size_t stride)
709{
710 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
711 {
712 const float local_opacity = mask[i];
713 dt_aligned_pixel_t ta, tb;
714 dt_aligned_pixel_t tta, ttb;
715
716 _PX_COPY(a + j, ta);
717 _PX_COPY(b + j, tb);
718
719 _CLAMP_XYZ(ta);
720 _CLAMP_XYZ(tb);
721
722 dt_RGB_2_HSL(ta, tta);
723 dt_RGB_2_HSL(tb, ttb);
724
725 /* blend hue along shortest distance on color circle */
726 float d = fabsf(tta[0] - ttb[0]);
727 float s = d > 0.5f ? -local_opacity * (1.0f - d) / d : local_opacity;
728 ttb[0] = fmodf((tta[0] * (1.0f - s)) + ttb[0] * s + 1.0f, 1.0f);
729 ttb[1] = tta[1];
730 ttb[2] = tta[2];
731
732 dt_HSL_2_RGB(ttb, out + j);
733 _CLAMP_XYZ(out + j);
734
735 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
736 }
737}
738
739/* color blend; blend hue and chroma, but not lightness */
740__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
741static void _blend_color(const float *const restrict a, const float *const restrict b,
742 float *const restrict out, const float *const restrict mask, const size_t stride)
743{
744 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
745 {
746 float local_opacity = mask[i];
747 dt_aligned_pixel_t ta, tb;
748 dt_aligned_pixel_t tta, ttb;
749
750 _PX_COPY(a + j, ta);
751 _PX_COPY(b + j, tb);
752
753 _CLAMP_XYZ(ta);
754 _CLAMP_XYZ(tb);
755
756 dt_RGB_2_HSL(ta, tta);
757 dt_RGB_2_HSL(tb, ttb);
758
759 /* blend hue along shortest distance on color circle */
760 float d = fabsf(tta[0] - ttb[0]);
761 float s = d > 0.5f ? -local_opacity * (1.0f - d) / d : local_opacity;
762 ttb[0] = fmodf((tta[0] * (1.0f - s)) + ttb[0] * s + 1.0f, 1.0f);
763
764 ttb[1] = (tta[1] * (1.0f - local_opacity)) + ttb[1] * local_opacity;
765 ttb[2] = tta[2];
766
767 dt_HSL_2_RGB(ttb, out + j);
768 _CLAMP_XYZ(out + j);
769
770 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
771 }
772}
773
774/* color adjustment; blend hue and chroma; take lightness from module output */
775__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
776static void _blend_coloradjust(const float *const restrict a, const float *const restrict b,
777 float *const restrict out, const float *const restrict mask, const size_t stride)
778{
779 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
780 {
781 const float local_opacity = mask[i];
782 dt_aligned_pixel_t ta, tb;
783 dt_aligned_pixel_t tta, ttb;
784
785 _PX_COPY(a + j, ta);
786 _PX_COPY(b + j, tb);
787
788 _CLAMP_XYZ(ta);
789 _CLAMP_XYZ(tb);
790
791 dt_RGB_2_HSL(ta, tta);
792 dt_RGB_2_HSL(tb, ttb);
793
794 /* blend hue along shortest distance on color circle */
795 const float d = fabsf(tta[0] - ttb[0]);
796 const float s = d > 0.5f ? -local_opacity * (1.0f - d) / d : local_opacity;
797 ttb[0] = fmodf((tta[0] * (1.0f - s)) + ttb[0] * s + 1.0f, 1.0f);
798
799 ttb[1] = (tta[1] * (1.0f - local_opacity)) + ttb[1] * local_opacity;
800 // ttb[2] (output lightness) unchanged
801
802 dt_HSL_2_RGB(ttb, out + j);
803 _CLAMP_XYZ(out + j);
804
805 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
806 }
807}
808
809/* blend only lightness in HSV color space without any clamping */
810__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
811static void _blend_HSV_value(const float *const restrict a, const float *const restrict b,
812 float *const restrict out, const float *const restrict mask, const size_t stride)
813{
814 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
815 {
816 const float local_opacity = mask[i];
817 dt_aligned_pixel_t ta, tb;
818
819 dt_RGB_2_HSV(a + j, ta);
820 dt_RGB_2_HSV(b + j, tb);
821
822 // hue and saturation from input image
823 tb[0] = ta[0];
824 tb[1] = ta[1];
825
826 // blend lightness between input and output
827 tb[2] = ta[2] * (1.0f - local_opacity) + tb[2] * local_opacity;
828
829 dt_HSV_2_RGB(tb, out + j);
830 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
831 }
832}
833
834/* blend only color in HSV color space without any clamping */
835__OMP_DECLARE_SIMD__(aligned(a, b:16) uniform(stride))
836static void _blend_HSV_color(const float *const restrict a, const float *const restrict b,
837 float *const restrict out, const float *const restrict mask, const size_t stride)
838{
839 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
840 {
841 const float local_opacity = mask[i];
842 dt_aligned_pixel_t ta, tb;
843
844 dt_RGB_2_HSV(a + j, ta);
845 dt_RGB_2_HSV(b + j, tb);
846
847 // convert from polar to cartesian coordinates
848 const float xa = ta[1] * cosf(2.0f * DT_M_PI_F * ta[0]);
849 const float ya = ta[1] * sinf(2.0f * DT_M_PI_F * ta[0]);
850 const float xb = tb[1] * cosf(2.0f * DT_M_PI_F * tb[0]);
851 const float yb = tb[1] * sinf(2.0f * DT_M_PI_F * tb[0]);
852
853 // blend color vectors of input and output
854 const float xc = xa * (1.0f - local_opacity) + xb * local_opacity;
855 const float yc = ya * (1.0f - local_opacity) + yb * local_opacity;
856
857 tb[0] = atan2f(yc, xc) / (2.0f * DT_M_PI_F);
858 if(tb[0] < 0.0f) tb[0] += 1.0f;
859 tb[1] = sqrtf(xc * xc + yc * yc);
860
861 // lightness from input image
862 tb[2] = ta[2];
863
864 dt_HSV_2_RGB(tb, out + j);
865 out[j + DT_BLENDIF_RGB_BCH] = local_opacity;
866 }
867}
868
869/* blend only R-channel in RGB color space without any clamping */
870__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
871static void _blend_RGB_R(const float *const restrict a, const float *const restrict b,
872 float *const restrict out, const float *const restrict mask, const size_t stride)
873{
874 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
875 {
876 const float local_opacity = mask[i];
877 out[j + 0] = a[j + 0] * (1.0f - local_opacity) + b[j + 0] * local_opacity;
878 out[j + 1] = a[j + 1];
879 out[j + 2] = a[j + 2];
880 out[j + 3] = local_opacity;
881 }
882}
883
884/* blend only R-channel in RGB color space without any clamping */
885__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
886static void _blend_RGB_G(const float *const restrict a, const float *const restrict b,
887 float *const restrict out, const float *const restrict mask, const size_t stride)
888{
889 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
890 {
891 const float local_opacity = mask[i];
892 out[j + 0] = a[j + 0];
893 out[j + 1] = a[j + 1] * (1.0f - local_opacity) + b[j + 1] * local_opacity;
894 out[j + 2] = a[j + 2];
895 out[j + 3] = local_opacity;
896 }
897}
898
899/* blend only R-channel in RGB color space without any clamping */
900__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
901static void _blend_RGB_B(const float *const restrict a, const float *const restrict b,
902 float *const restrict out, const float *const restrict mask, const size_t stride)
903{
904 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
905 {
906 const float local_opacity = mask[i];
907 out[j + 0] = a[j + 0];
908 out[j + 1] = a[j + 1];
909 out[j + 2] = a[j + 2] * (1.0f - local_opacity) + b[j + 2] * local_opacity;
910 out[j + 3] = local_opacity;
911 }
912}
913
914
915static _blend_row_func *_choose_blend_func(const unsigned int blend_mode)
916{
917 _blend_row_func *blend = NULL;
918
919 /* select the blend operator */
920 switch(blend_mode & DEVELOP_BLEND_MODE_MASK)
921 {
923 blend = _blend_lighten;
924 break;
926 blend = _blend_darken;
927 break;
929 blend = _blend_multiply;
930 break;
932 blend = _blend_average;
933 break;
935 blend = _blend_add;
936 break;
938 blend = _blend_subtract;
939 break;
942 blend = _blend_difference;
943 break;
945 blend = _blend_screen;
946 break;
948 blend = _blend_overlay;
949 break;
951 blend = _blend_softlight;
952 break;
954 blend = _blend_hardlight;
955 break;
957 blend = _blend_vividlight;
958 break;
960 blend = _blend_linearlight;
961 break;
963 blend = _blend_pinlight;
964 break;
966 blend = _blend_lightness;
967 break;
969 blend = _blend_chromaticity;
970 break;
972 blend = _blend_hue;
973 break;
975 blend = _blend_color;
976 break;
978 blend = _blend_normal_bounded;
979 break;
981 blend = _blend_coloradjust;
982 break;
984 blend = _blend_HSV_value;
985 break;
987 blend = _blend_HSV_color;
988 break;
990 blend = _blend_RGB_R;
991 break;
993 blend = _blend_RGB_G;
994 break;
996 blend = _blend_RGB_B;
997 break;
998
999 /* fallback to normal blend */
1001 default:
1002 blend = _blend_normal_unbounded;
1003 break;
1004 }
1005
1006 return blend;
1007}
1008
1009
1010__OMP_DECLARE_SIMD__(aligned(rgb: 16) uniform(profile))
1011static inline float _rgb_luminance(const float *const restrict rgb,
1012 const dt_iop_order_iccprofile_info_t *const restrict profile)
1013{
1014 float value = 0.0f;
1015 if(!IS_NULL_PTR(profile))
1016 value = dt_ioppr_get_rgb_matrix_luminance(rgb, profile->matrix_in, profile->lut_in,
1017 profile->unbounded_coeffs_in, profile->lutsize,
1018 profile->nonlinearlut);
1019 else
1020 value = 0.3f * rgb[0] + 0.59f * rgb[1] + 0.11f * rgb[2];
1021 return value;
1022}
1023
1024__OMP_DECLARE_SIMD__(aligned(a, b:16) uniform(channel, profile, stride))
1025static void _display_channel(const float *const restrict a, float *const restrict b,
1026 const float *const restrict mask, const size_t stride, const int channel,
1027 const float *const restrict boost_factors,
1028 const dt_iop_order_iccprofile_info_t *const profile)
1029{
1030 switch(channel)
1031 {
1033 {
1034 const float factor = 1.0f / exp2f(boost_factors[DEVELOP_BLENDIF_RED_in]);
1035 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
1036 {
1037 const float c = clamp_simd(a[j + 0] * factor);
1038 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++) b[j + k] = c;
1039 b[j + DT_BLENDIF_RGB_BCH] = mask[i];
1040 }
1041 break;
1042 }
1044 {
1045 const float factor = 1.0f / exp2f(boost_factors[DEVELOP_BLENDIF_RED_out]);
1046 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
1047 {
1048 const float c = clamp_simd(b[j + 0] * factor);
1049 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++) b[j + k] = c;
1050 b[j + DT_BLENDIF_RGB_BCH] = mask[i];
1051 }
1052 break;
1053 }
1055 {
1056 const float factor = 1.0f / exp2f(boost_factors[DEVELOP_BLENDIF_GREEN_in]);
1057 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
1058 {
1059 const float c = clamp_simd(a[j + 1] * factor);
1060 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++) b[j + k] = c;
1061 b[j + DT_BLENDIF_RGB_BCH] = mask[i];
1062 }
1063 break;
1064 }
1066 {
1067 const float factor = 1.0f / exp2f(boost_factors[DEVELOP_BLENDIF_GREEN_out]);
1068 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
1069 {
1070 const float c = clamp_simd(b[j + 1] * factor);
1071 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++) b[j + k] = c;
1072 b[j + DT_BLENDIF_RGB_BCH] = mask[i];
1073 }
1074 break;
1075 }
1077 {
1078 const float factor = 1.0f / exp2f(boost_factors[DEVELOP_BLENDIF_BLUE_in]);
1079 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
1080 {
1081 const float c = clamp_simd(a[j + 2] * factor);
1082 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++) b[j + k] = c;
1083 b[j + DT_BLENDIF_RGB_BCH] = mask[i];
1084 }
1085 break;
1086 }
1088 {
1089 const float factor = 1.0f / exp2f(boost_factors[DEVELOP_BLENDIF_BLUE_out]);
1090 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
1091 {
1092 const float c = clamp_simd(b[j + 2] * factor);
1093 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++) b[j + k] = c;
1094 b[j + DT_BLENDIF_RGB_BCH] = mask[i];
1095 }
1096 break;
1097 }
1099 {
1100 const float factor = 1.0f / exp2f(boost_factors[DEVELOP_BLENDIF_GRAY_in]);
1101 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
1102 {
1103 const float c = clamp_simd(_rgb_luminance(a + j, profile) * factor);
1104 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++) b[j + k] = c;
1105 b[j + DT_BLENDIF_RGB_BCH] = mask[i];
1106 }
1107 break;
1108 }
1110 {
1111 const float factor = 1.0f / exp2f(boost_factors[DEVELOP_BLENDIF_GRAY_out]);
1112 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
1113 {
1114 const float c = clamp_simd(_rgb_luminance(b + j, profile) * factor);
1115 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++) b[j + k] = c;
1116 b[j + DT_BLENDIF_RGB_BCH] = mask[i];
1117 }
1118 break;
1119 }
1121 // no boost factors for HSL
1122 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
1123 {
1125 dt_RGB_2_HSL(a + j, HSL);
1126 const float c = clamp_simd(HSL[0]);
1127 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++) b[j + k] = c;
1128 b[j + DT_BLENDIF_RGB_BCH] = mask[i];
1129 }
1130 break;
1132 // no boost factors for HSL
1133 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
1134 {
1136 dt_RGB_2_HSL(b + j, HSL);
1137 const float c = clamp_simd(HSL[0]);
1138 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++) b[j + k] = c;
1139 b[j + DT_BLENDIF_RGB_BCH] = mask[i];
1140 }
1141 break;
1143 // no boost factors for HSL
1144 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
1145 {
1147 dt_RGB_2_HSL(a + j, HSL);
1148 const float c = clamp_simd(HSL[1]);
1149 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++) b[j + k] = c;
1150 b[j + DT_BLENDIF_RGB_BCH] = mask[i];
1151 }
1152 break;
1154 // no boost factors for HSL
1155 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
1156 {
1158 dt_RGB_2_HSL(b + j, HSL);
1159 const float c = clamp_simd(HSL[1]);
1160 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++) b[j + k] = c;
1161 b[j + DT_BLENDIF_RGB_BCH] = mask[i];
1162 }
1163 break;
1165 // no boost factors for HSL
1166 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
1167 {
1169 dt_RGB_2_HSL(a + j, HSL);
1170 const float c = clamp_simd(HSL[2]);
1171 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++) b[j + k] = c;
1172 b[j + DT_BLENDIF_RGB_BCH] = mask[i];
1173 }
1174 break;
1176 // no boost factors for HSL
1177 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
1178 {
1180 dt_RGB_2_HSL(b + j, HSL);
1181 const float c = clamp_simd(HSL[2]);
1182 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++) b[j + k] = c;
1183 b[j + DT_BLENDIF_RGB_BCH] = mask[i];
1184 }
1185 break;
1186 default:
1187 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_RGB_CH)
1188 {
1189 for(int k = 0; k < DT_BLENDIF_RGB_BCH; k++) b[j + k] = 0.0f;
1190 b[j + DT_BLENDIF_RGB_BCH] = mask[i];
1191 }
1192 break;
1193 }
1194}
1195
1196
1197__OMP_DECLARE_SIMD__(aligned(a, b:16) uniform(stride))
1198static inline void _copy_mask(const float *const restrict a, float *const restrict b, const size_t stride)
1199{
1200 __OMP_SIMD__(aligned(a, b: 16))
1201 for(size_t x = DT_BLENDIF_RGB_BCH; x < stride; x += DT_BLENDIF_RGB_CH) b[x] = a[x];
1202}
1203
1205 const struct dt_dev_pixelpipe_iop_t *piece,
1206 const float *const restrict a, float *const restrict b,
1207 const float *const restrict mask,
1208 const dt_dev_pixelpipe_display_mask_t request_mask_display)
1209{
1210 const dt_iop_roi_t *const roi_in = &piece->roi_in;
1211 const dt_iop_roi_t *const roi_out = &piece->roi_out;
1212 const dt_develop_blend_params_t *const d = (const dt_develop_blend_params_t *const)piece->blendop_data;
1213
1214 if(piece->dsc_in.channels != DT_BLENDIF_RGB_CH) return;
1215
1216 const int xoffs = roi_out->x - roi_in->x;
1217 const int yoffs = roi_out->y - roi_in->y;
1218 const int iwidth = roi_in->width;
1219 const int owidth = roi_out->width;
1220 const int oheight = roi_out->height;
1221
1222 // only non-zero if mask_display was set by an _earlier_ module
1223 const dt_dev_pixelpipe_display_mask_t mask_display = pipe->mask_display;
1224
1225 // process the blending operator
1226 if(request_mask_display & DT_DEV_PIXELPIPE_DISPLAY_ANY)
1227 {
1228 dt_iop_order_iccprofile_info_t blend_profile;
1229 const int use_profile = dt_develop_blendif_init_masking_profile(pipe, piece, &blend_profile,
1231 const dt_iop_order_iccprofile_info_t *profile = use_profile ? &blend_profile : NULL;
1232 const float *const restrict boost_factors = d->blendif_boost_factors;
1233 const dt_dev_pixelpipe_display_mask_t channel = request_mask_display & DT_DEV_PIXELPIPE_DISPLAY_ANY;
1235 for(size_t y = 0; y < oheight; y++)
1236 {
1237 const size_t a_start = ((y + yoffs) * iwidth + xoffs) * DT_BLENDIF_RGB_CH;
1238 const size_t b_start = y * owidth * DT_BLENDIF_RGB_CH;
1239 const size_t m_start = y * owidth;
1240 _display_channel(a + a_start, b + b_start, mask + m_start, owidth, channel, boost_factors, profile);
1241 }
1242 }
1243 else
1244 {
1245 _blend_row_func *const blend = _choose_blend_func(d->blend_mode);
1246
1247 float *tmp_buffer = dt_pixelpipe_cache_alloc_align_float_cache((size_t)owidth * oheight * DT_BLENDIF_RGB_CH, 0);
1248 if (!IS_NULL_PTR(tmp_buffer))
1249 {
1250 dt_iop_image_copy(tmp_buffer, b, (size_t)owidth * oheight * DT_BLENDIF_RGB_CH);
1251 if((d->blend_mode & DEVELOP_BLEND_REVERSE) == DEVELOP_BLEND_REVERSE)
1252 {
1254 for(size_t y = 0; y < oheight; y++)
1255 {
1256 const size_t a_start = ((y + yoffs) * iwidth + xoffs) * DT_BLENDIF_RGB_CH;
1257 const size_t b_start = y * owidth * DT_BLENDIF_RGB_CH;
1258 const size_t m_start = y * owidth;
1259 blend(tmp_buffer + b_start, a + a_start, b + b_start, mask + m_start, owidth);
1260 }
1261 }
1262 else
1263 {
1265 for(size_t y = 0; y < oheight; y++)
1266 {
1267 const size_t a_start = ((y + yoffs) * iwidth + xoffs) * DT_BLENDIF_RGB_CH;
1268 const size_t b_start = y * owidth * DT_BLENDIF_RGB_CH;
1269 const size_t m_start = y * owidth;
1270 blend(a + a_start, tmp_buffer + b_start, b + b_start, mask + m_start, owidth);
1271 }
1272 }
1274 }
1275 }
1276
1277 if(mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK)
1278 {
1279 const size_t stride = owidth * DT_BLENDIF_RGB_CH;
1281 for(size_t y = 0; y < oheight; y++)
1282 {
1283 const size_t a_start = ((y + yoffs) * iwidth + xoffs) * DT_BLENDIF_RGB_CH;
1284 const size_t b_start = y * stride;
1285 _copy_mask(a + a_start, b + b_start, stride);
1286 }
1287 }
1288}
1289
1290// tools/update_modelines.sh
1291// remove-trailing-space on;
1292// clang-format off
1293// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1294// vim: shiftwidth=2 expandtab tabstop=2 cindent
1295// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1296// clang-format on
void dt_develop_blendif_process_parameters(float *const restrict parameters, const dt_develop_blend_params_t *const params)
Definition blend.c:253
int dt_develop_blendif_init_masking_profile(const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, dt_iop_order_iccprofile_info_t *blending_profile, dt_develop_blend_colorspace_t cst)
Definition blend.c:361
@ DEVELOP_BLEND_CS_RGB_DISPLAY
Definition blend.h:57
@ DEVELOP_COMBINE_INV
Definition blend.h:123
@ DEVELOP_COMBINE_INCL
Definition blend.h:125
@ DEVELOP_BLENDIF_GRAY_out
Definition blend.h:156
@ DEVELOP_BLENDIF_RED_in
Definition blend.h:152
@ DEVELOP_BLENDIF_l_in
Definition blend.h:169
@ DEVELOP_BLENDIF_GREEN_in
Definition blend.h:153
@ DEVELOP_BLENDIF_H_in
Definition blend.h:167
@ DEVELOP_BLENDIF_RED_out
Definition blend.h:157
@ DEVELOP_BLENDIF_BLUE_in
Definition blend.h:154
@ DEVELOP_BLENDIF_BLUE_out
Definition blend.h:159
@ DEVELOP_BLENDIF_RGB_MASK
Definition blend.h:191
@ DEVELOP_BLENDIF_S_in
Definition blend.h:168
@ DEVELOP_BLENDIF_GRAY_in
Definition blend.h:151
@ DEVELOP_BLENDIF_GREEN_out
Definition blend.h:158
#define DEVELOP_BLENDIF_PARAMETER_ITEMS
Definition blend.h:353
@ DEVELOP_BLEND_LIGHTEN
Definition blend.h:65
@ DEVELOP_BLEND_COLOR
Definition blend.h:82
@ DEVELOP_BLEND_CHROMATICITY
Definition blend.h:80
@ DEVELOP_BLEND_DIFFERENCE
Definition blend.h:71
@ DEVELOP_BLEND_RGB_B
Definition blend.h:98
@ DEVELOP_BLEND_LIGHTNESS
Definition blend.h:79
@ DEVELOP_BLEND_BOUNDED
Definition blend.h:88
@ DEVELOP_BLEND_SUBTRACT
Definition blend.h:70
@ DEVELOP_BLEND_MODE_MASK
Definition blend.h:107
@ DEVELOP_BLEND_NORMAL2
Definition blend.h:87
@ DEVELOP_BLEND_HARDLIGHT
Definition blend.h:75
@ DEVELOP_BLEND_HUE
Definition blend.h:81
@ DEVELOP_BLEND_OVERLAY
Definition blend.h:73
@ DEVELOP_BLEND_RGB_R
Definition blend.h:96
@ DEVELOP_BLEND_REVERSE
Definition blend.h:106
@ DEVELOP_BLEND_AVERAGE
Definition blend.h:68
@ DEVELOP_BLEND_HSV_COLOR
Definition blend.h:92
@ DEVELOP_BLEND_MULTIPLY
Definition blend.h:67
@ DEVELOP_BLEND_SCREEN
Definition blend.h:72
@ DEVELOP_BLEND_PINLIGHT
Definition blend.h:78
@ DEVELOP_BLEND_HSV_VALUE
Definition blend.h:91
@ DEVELOP_BLEND_LINEARLIGHT
Definition blend.h:77
@ DEVELOP_BLEND_ADD
Definition blend.h:69
@ DEVELOP_BLEND_VIVIDLIGHT
Definition blend.h:76
@ DEVELOP_BLEND_SOFTLIGHT
Definition blend.h:74
@ DEVELOP_BLEND_COLORADJUST
Definition blend.h:85
@ DEVELOP_BLEND_DARKEN
Definition blend.h:66
@ DEVELOP_BLEND_DIFFERENCE2
Definition blend.h:86
@ DEVELOP_BLEND_RGB_G
Definition blend.h:97
@ DEVELOP_MASK_PARAMETRIC
Definition blend.h:115
static _blend_row_func * _choose_blend_func(const unsigned int blend_mode)
static float _blendif_compute_factor(const float value, const unsigned int invert_mask, const float *const restrict parameters)
static void _CLAMP_XYZ(float *const restrict XYZ)
static void _PX_COPY(const float *const restrict src, float *const restrict dst)
void dt_develop_blendif_rgb_hsl_make_mask(const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, const float *const restrict a, const float *const restrict b, float *const restrict mask)
void dt_develop_blendif_rgb_hsl_blend(const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, const float *const restrict a, float *const restrict b, const float *const restrict mask, const dt_dev_pixelpipe_display_mask_t request_mask_display)
void() _blend_row_func(const float *const restrict a, const float *const restrict b, float *const restrict out, const float *const restrict mask, const size_t stride)
#define DT_BLENDIF_RGB_BCH
#define DT_BLENDIF_RGB_CH
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
static const float x
static dt_aligned_pixel_t rgb
static dt_aligned_pixel_t HSL
static dt_aligned_pixel_t XYZ
const dt_colormatrix_t dt_aligned_pixel_t out
for(size_t c=0;c< 3;c++) sRGB[c]
dt_dev_pixelpipe_display_mask_t
Definition develop.h:121
@ DT_DEV_PIXELPIPE_DISPLAY_OUTPUT
Definition develop.h:125
@ DT_DEV_PIXELPIPE_DISPLAY_G
Definition develop.h:130
@ DT_DEV_PIXELPIPE_DISPLAY_ANY
Definition develop.h:143
@ DT_DEV_PIXELPIPE_DISPLAY_HSL_H
Definition develop.h:135
@ DT_DEV_PIXELPIPE_DISPLAY_HSL_S
Definition develop.h:136
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:123
@ DT_DEV_PIXELPIPE_DISPLAY_HSL_l
Definition develop.h:137
@ DT_DEV_PIXELPIPE_DISPLAY_GRAY
Definition develop.h:132
@ DT_DEV_PIXELPIPE_DISPLAY_B
Definition develop.h:131
@ DT_DEV_PIXELPIPE_DISPLAY_R
Definition develop.h:129
__DT_CLONE_TARGETS__ void dt_iop_image_mul_const(float *const buf, const float mul_value, const size_t width, const size_t height, const size_t ch)
Definition imagebuf.c:355
__DT_CLONE_TARGETS__ void dt_iop_image_copy(float *const __restrict__ out, const float *const __restrict__ in, const size_t nfloats)
Definition imagebuf.c:142
__DT_CLONE_TARGETS__ void dt_iop_image_fill(float *const buf, const float fill_value, const size_t width, const size_t height, const size_t ch)
Definition imagebuf.c:218
#define DEVELOP_BLENDIF_SIZE
Definition lightroom.c:236
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define DT_M_PI_F
Definition math.h:54
#define DT_ALIGNED_PIXEL
Align a 4-float pixel on 16 bytes, enough for SSE. Same struct-member caveat as DT_ALIGNED_ARRAY,...
Definition mem_alloc.h:85
#define DT_ALIGNED_ARRAY
Align an object on a cacheline boundary, so AVX2 can load it whole.
Definition mem_alloc.h:80
#define __OMP_SIMD__(...)
Definition openmp.h:99
#define __OMP_FOR__(...)
Definition openmp.h:98
#define __OMP_DECLARE_SIMD__(...)
Definition openmp.h:100
#define __OMP_PARALLEL__(...)
Definition openmp.h:94
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
#define __OMP_FOR_SIMD__(...)
Definition openmp.h:97
#define __OMP_PARALLEL_FOR_SIMD__(...)
Definition openmp.h:96
static float clamp_simd(const float x)
const float factor
Definition pdf.h:91
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
#define dt_pixelpipe_cache_free_align(mem)
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
dt_iop_buffer_dsc_t dsc_in
unsigned int channels
Definition format.h:83
A profile reduced to the arithmetic the pixel loop can run: two matrices and six tone-curve LUTs,...
Region of interest passed through the pixelpipe.
Definition format.h:49
int width
Definition format.h:50
int height
Definition format.h:50