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