Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
blendif_lab.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_LAB_CH 4
34#define DT_BLENDIF_LAB_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,
40
41
43static inline float _CLAMP(const float x, const float min, const float max)
44{
45 return fminf(fmaxf(x, min), max);
46}
47
48__OMP_DECLARE_SIMD__(aligned(XYZ, min, max: 16))
50{
51 for_each_channel(i) XYZ[i] = fminf(fmaxf(XYZ[i], min[i]), max[i]);
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))
88static inline void _blendif_lab_l(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{
92 for(size_t x = 0, j = 0; x < stride; x++, j += DT_BLENDIF_LAB_CH)
93 {
94 mask[x] *= _blendif_compute_factor(pixels[j + 0] / 100.0f, invert_mask, parameters);
95 }
96}
97
98__OMP_DECLARE_SIMD__(aligned(pixels: 16) uniform(parameters, invert_mask, stride))
99static inline void _blendif_lab_a(const float *const restrict pixels, float *const restrict mask,
100 const size_t stride, const float *const restrict parameters,
101 const unsigned int invert_mask)
102{
103 for(size_t x = 0, j = 0; x < stride; x++, j += DT_BLENDIF_LAB_CH)
104 {
105 mask[x] *= _blendif_compute_factor(pixels[j + 1] / 256.0f, invert_mask, parameters);
106 }
107}
108
109__OMP_DECLARE_SIMD__(aligned(pixels: 16) uniform(parameters, invert_mask, stride))
110static inline void _blendif_lab_b(const float *const restrict pixels, float *const restrict mask,
111 const size_t stride, const float *const restrict parameters,
112 const unsigned int invert_mask)
113{
114 for(size_t x = 0, j = 0; x < stride; x++, j += DT_BLENDIF_LAB_CH)
115 {
116 mask[x] *= _blendif_compute_factor(pixels[j + 2] / 256.0f, invert_mask, parameters);
117 }
118}
119
120__OMP_DECLARE_SIMD__(aligned(pixels, invert_mask: 16) uniform(parameters, invert_mask, stride))
121static inline void _blendif_lch(const float *const restrict pixels, float *const restrict mask,
122 const size_t stride, const float *const restrict parameters,
123 const unsigned int *const restrict invert_mask)
124{
125 const float c_scale = 1.0f / (128.0f * sqrtf(2.0f));
126 for(size_t x = 0, j = 0; x < stride; x++, j += DT_BLENDIF_LAB_CH)
127 {
129 dt_Lab_2_LCH(pixels + j, LCH);
130 float factor = 1.0f;
131 factor *= _blendif_compute_factor(LCH[1] * c_scale, invert_mask[0], parameters);
132 factor *= _blendif_compute_factor(LCH[2], invert_mask[1], parameters + DEVELOP_BLENDIF_PARAMETER_ITEMS);
133 mask[x] *= factor;
134 }
135}
136
137__OMP_DECLARE_SIMD__(aligned(pixels: 16) uniform(stride, blendif, parameters))
138static void _blendif_combine_channels(const float *const restrict pixels, float *const restrict mask,
139 const size_t stride, const unsigned int blendif,
140 const float *const restrict parameters)
141{
142 if(blendif & (1 << DEVELOP_BLENDIF_L_in))
143 {
144 const unsigned int invert_mask = (blendif >> 16) & (1 << DEVELOP_BLENDIF_L_in);
145 _blendif_lab_l(pixels, mask, stride, parameters + DEVELOP_BLENDIF_PARAMETER_ITEMS * DEVELOP_BLENDIF_L_in,
146 invert_mask);
147 }
148
149 if(blendif & (1 << DEVELOP_BLENDIF_A_in))
150 {
151 const unsigned int invert_mask = (blendif >> 16) & (1 << DEVELOP_BLENDIF_A_in);
152 _blendif_lab_a(pixels, mask, stride, parameters + DEVELOP_BLENDIF_PARAMETER_ITEMS * DEVELOP_BLENDIF_A_in,
153 invert_mask);
154 }
155
156 if(blendif & (1 << DEVELOP_BLENDIF_B_in))
157 {
158 const unsigned int invert_mask = (blendif >> 16) & (1 << DEVELOP_BLENDIF_B_in);
159 _blendif_lab_b(pixels, mask, stride, parameters + DEVELOP_BLENDIF_PARAMETER_ITEMS * DEVELOP_BLENDIF_B_in,
160 invert_mask);
161 }
162
163 if(blendif & ((1 << DEVELOP_BLENDIF_C_in) | (1 << DEVELOP_BLENDIF_h_in)))
164 {
165 const unsigned int invert_mask[2] DT_ALIGNED_PIXEL = {
166 (blendif >> 16) & (1 << DEVELOP_BLENDIF_C_in),
167 (blendif >> 16) & (1 << DEVELOP_BLENDIF_h_in),
168 };
169 _blendif_lch(pixels, mask, stride, parameters + DEVELOP_BLENDIF_PARAMETER_ITEMS * DEVELOP_BLENDIF_C_in,
170 invert_mask);
171 }
172}
173
174void dt_develop_blendif_lab_make_mask(const struct dt_dev_pixelpipe_iop_t *piece, const float *const restrict a,
175 const float *const restrict b, float *const restrict mask)
176{
177 const dt_iop_roi_t *const roi_in = &piece->roi_in;
178 const dt_iop_roi_t *const roi_out = &piece->roi_out;
179 const dt_develop_blend_params_t *const d = (const dt_develop_blend_params_t *const)piece->blendop_data;
180
181 if(piece->dsc_in.channels != DT_BLENDIF_LAB_CH) return;
182
183 const int xoffs = roi_out->x - roi_in->x;
184 const int yoffs = roi_out->y - roi_in->y;
185 const int iwidth = roi_in->width;
186 const int owidth = roi_out->width;
187 const int oheight = roi_out->height;
188
189 const unsigned int any_channel_active = d->blendif & DEVELOP_BLENDIF_Lab_MASK;
190 const unsigned int mask_inclusive = d->mask_combine & DEVELOP_COMBINE_INCL;
191 const unsigned int mask_inversed = d->mask_combine & DEVELOP_COMBINE_INV;
192
193 // invert the individual channels if the combine mode is inclusive
194 const unsigned int blendif = d->blendif ^ (mask_inclusive ? DEVELOP_BLENDIF_Lab_MASK << 16 : 0);
195
196 // a channel cancels the mask if the whole span is selected and the channel is inverted
197 const unsigned int canceling_channel = (blendif >> 16) & ~blendif & DEVELOP_BLENDIF_Lab_MASK;
198
199 const size_t buffsize = (size_t)owidth * oheight;
200
201 // get the clipped opacity value 0 - 1
202 const float global_opacity = clamp_simd(d->opacity / 100.0f);
203
204 if(!(d->mask_mode & DEVELOP_MASK_PARAMETRIC) || (!canceling_channel && !any_channel_active))
205 {
206 // mask is not conditional, invert the mask if required
207 if(mask_inversed)
208 {
210 for(size_t x = 0; x < buffsize; x++) mask[x] = global_opacity * (1.0f - mask[x]);
211 }
212 else
213 {
214 dt_iop_image_mul_const(mask,global_opacity,owidth,oheight,1); //mask[k] *= global_opacity;
215 }
216 }
217 else if(canceling_channel || !any_channel_active)
218 {
219 // one of the conditional channel selects nothing
220 // this means that the conditional opacity of all pixels is the same
221 // and depends on whether the mask combination is inclusive and whether the mask is inverted
222 if((mask_inversed == 0) ^ (mask_inclusive == 0))
223 {
224 dt_iop_image_fill(mask,global_opacity,owidth,oheight,1); //mask[k] = global_opacity;
225 }
226 else
227 {
228 dt_iop_image_fill(mask,0.0f,owidth,oheight,1); //mask[k] = 0.0f;
229 }
230 }
231 else
232 {
233 // we need to process all conditional channels
234
235 // parameters, for every channel the 4 limits + pre-computed increasing slope and decreasing slope
238
239 // allocate space for a temporary mask buffer to split the computation of every channel
240 float *const restrict temp_mask = dt_pixelpipe_cache_alloc_align_float_cache(buffsize, 0);
241 if(IS_NULL_PTR(temp_mask))
242 {
243 return;
244 }
246 {
247 // initialize the parametric mask
248 __OMP_FOR_SIMD__(aligned(temp_mask:64))
249 for(size_t x = 0; x < buffsize; x++) temp_mask[x] = 1.0f;
250
251 // combine channels
253 for(size_t y = 0; y < oheight; y++)
254 {
255 const size_t start = ((y + yoffs) * iwidth + xoffs) * DT_BLENDIF_LAB_CH;
256 _blendif_combine_channels(a + start, temp_mask + (y * owidth), owidth, blendif, parameters);
257 }
259 for(size_t y = 0; y < oheight; y++)
260 {
261 const size_t start = (y * owidth) * DT_BLENDIF_LAB_CH;
262 _blendif_combine_channels(b + start, temp_mask + (y * owidth), owidth, blendif >> DEVELOP_BLENDIF_L_out,
264 }
265
266 // apply global opacity
267 if(mask_inclusive)
268 {
269 if(mask_inversed)
270 {
271 __OMP_FOR_SIMD__(aligned(mask, temp_mask:64))
272 for(size_t x = 0; x < buffsize; x++) mask[x] = global_opacity * (1.0f - mask[x]) * temp_mask[x];
273 }
274 else
275 {
276 __OMP_FOR_SIMD__(aligned(mask, temp_mask:64))
277 for(size_t x = 0; x < buffsize; x++) mask[x] = global_opacity * (1.0f - (1.0f - mask[x]) * temp_mask[x]);
278 }
279 }
280 else
281 {
282 if(mask_inversed)
283 {
284 __OMP_FOR_SIMD__(aligned(mask, temp_mask:64))
285 for(size_t x = 0; x < buffsize; x++) mask[x] = global_opacity * (1.0f - mask[x] * temp_mask[x]);
286 }
287 else
288 {
289 __OMP_FOR_SIMD__(aligned(mask, temp_mask:64))
290 for(size_t x = 0; x < buffsize; x++) mask[x] = global_opacity * mask[x] * temp_mask[x];
291 }
292 }
293 }
294
296 }
297}
298
299
300__OMP_DECLARE_SIMD__(aligned(i, o: 16))
301static inline void _blend_Lab_scale(const float *i, float *o)
302{
303 const dt_aligned_pixel_t scale = { 1/100.0f, 1/128.0f, 1/128.0f, 1.0f };
305 o[c] = i[c] * scale[c];
306}
307
308__OMP_DECLARE_SIMD__(aligned(i, o: 16))
309static inline void _blend_Lab_rescale(const float *i, float *o)
310{
311 const dt_aligned_pixel_t scale = { 100.0f, 128.0f, 128.0f, 1.0f };
313 o[c] = i[c] * scale[c];
314}
315
316
317/* normal blend with clamping */
318__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
319static void _blend_normal_bounded(const float *const restrict a, const float *const restrict b,
320 float *const restrict out, const float *const restrict mask, const size_t stride,
322{
323 for(size_t i = 0; i < stride; i++)
324 {
325 size_t j = i * DT_BLENDIF_LAB_CH;
326 const float local_opacity = mask[i];
327 dt_aligned_pixel_t ta, tb;
328
329 _blend_Lab_scale(a + j, ta);
330 _blend_Lab_scale(b + j, tb);
331
333 tb[x] = _CLAMP(ta[x] * (1.0f - local_opacity) + tb[x] * local_opacity, min[x], max[x]);
334
335 _blend_Lab_rescale(tb, out + j);
336 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
337 }
338}
339
340/* normal blend without any clamping */
341__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
342static void _blend_normal_unbounded(const float *const restrict a, const float *const restrict b,
343 float *const restrict out,
344 const float *const restrict mask, const size_t stride,
346{
347 for(size_t i = 0; i < stride; i++)
348 {
349 size_t j = i * DT_BLENDIF_LAB_CH;
350 const float local_opacity = mask[i];
351 dt_aligned_pixel_t ta, tb;
352
353 _blend_Lab_scale(a + j, ta);
354 _blend_Lab_scale(b + j, tb);
355
357 tb[x] = ta[x] * (1.0f - local_opacity) + tb[x] * local_opacity;
358
359 _blend_Lab_rescale(tb, out + j);
360 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
361 }
362}
363
364/* lighten */
365__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
366static void _blend_lighten(const float *const restrict a, const float *const restrict b,
367 float *const restrict out, const float *const restrict mask, const size_t stride,
369{
370 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
371 {
372 const float local_opacity = mask[i];
373 dt_aligned_pixel_t ta, tb;
374
375 _blend_Lab_scale(a + j, ta);
376 _blend_Lab_scale(b + j, tb);
377
378 tb[0] = _CLAMP(ta[0] * (1.0f - local_opacity) + (ta[0] > tb[0] ? ta[0] : tb[0]) * local_opacity,
379 min[0], max[0]);
380 tb[1] = _CLAMP(ta[1] * (1.0f - fabsf(tb[0] - ta[0])) + 0.5f * (ta[1] + tb[1]) * fabsf(tb[0] - ta[0]),
381 min[1], max[1]);
382 tb[2] = _CLAMP(ta[2] * (1.0f - fabsf(tb[0] - ta[0])) + 0.5f * (ta[2] + tb[2]) * fabsf(tb[0] - ta[0]),
383 min[2], max[2]);
384
385 _blend_Lab_rescale(tb, out + j);
386 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
387 }
388}
389
390/* darken */
391__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
392static void _blend_darken(const float *const restrict a, const float *const restrict b,
393 float *const restrict out, const float *const restrict mask, const size_t stride,
395{
396 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
397 {
398 const float local_opacity = mask[i];
399 dt_aligned_pixel_t ta, tb;
400
401 _blend_Lab_scale(a + j, ta);
402 _blend_Lab_scale(b + j, tb);
403
404 tb[0] = _CLAMP(ta[0] * (1.0f - local_opacity) + (ta[0] < tb[0] ? ta[0] : tb[0]) * local_opacity,
405 min[0], max[0]);
406 tb[1] = _CLAMP(ta[1] * (1.0f - fabsf(tb[0] - ta[0])) + 0.5f * (ta[1] + tb[1]) * fabsf(tb[0] - ta[0]),
407 min[1], max[1]);
408 tb[2] = _CLAMP(ta[2] * (1.0f - fabsf(tb[0] - ta[0])) + 0.5f * (ta[2] + tb[2]) * fabsf(tb[0] - ta[0]),
409 min[2], max[2]);
410
411 _blend_Lab_rescale(tb, out + j);
412 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
413 }
414}
415
416/* multiply */
417__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
418static void _blend_multiply(const float *const restrict a, const float *const restrict b,
419 float *const restrict out, const float *const restrict mask, const size_t stride,
421{
422 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
423 {
424 const float local_opacity = mask[i];
425 dt_aligned_pixel_t ta, tb;
426
427 _blend_Lab_scale(a + j, ta);
428 _blend_Lab_scale(b + j, tb);
429
430 tb[0] = _CLAMP(ta[0] * (1.0f - local_opacity) + (ta[0] * tb[0]) * local_opacity, min[0], max[0]);
431
432 const float f = fmaxf(ta[0], 0.01f);
433 tb[1] = _CLAMP(ta[1] * (1.0f - local_opacity) + (ta[1] + tb[1]) * tb[0] / f * local_opacity, min[1], max[1]);
434 tb[2] = _CLAMP(ta[2] * (1.0f - local_opacity) + (ta[2] + tb[2]) * tb[0] / f * local_opacity, min[2], max[2]);
435
436 _blend_Lab_rescale(tb, out + j);
437 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
438 }
439}
440
441/* average */
442__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
443static void _blend_average(const float *const restrict a, const float *const restrict b,
444 float *const restrict out, const float *const restrict mask, const size_t stride,
446{
447 for(size_t i = 0; i < stride; i++)
448 {
449 size_t j = i * DT_BLENDIF_LAB_CH;
450 const float local_opacity = mask[i];
451 dt_aligned_pixel_t ta, tb;
452
453 _blend_Lab_scale(a + j, ta);
454 _blend_Lab_scale(b + j, tb);
455
457 tb[x] = _CLAMP(ta[x] * (1.0f - local_opacity) + (ta[x] + tb[x]) / 2.0f * local_opacity, min[x], max[x]);
458
459 _blend_Lab_rescale(tb, out + j);
460 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
461 }
462}
463
464/* add */
465__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
466static void _blend_add(const float *const restrict a, const float *const restrict b,
467 float *const restrict out, const float *const restrict mask, const size_t stride,
469{
470 for(size_t i = 0; i < stride; i++)
471 {
472 size_t j = i * DT_BLENDIF_LAB_CH;
473 const float local_opacity = mask[i];
474 dt_aligned_pixel_t ta, tb;
475
476 _blend_Lab_scale(a + j, ta);
477 _blend_Lab_scale(b + j, tb);
478
480 tb[x] = _CLAMP(ta[x] * (1.0f - local_opacity) + (ta[x] + tb[x]) * local_opacity, min[x], max[x]);
481
482 _blend_Lab_rescale(tb, out + j);
483 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
484 }
485}
486
487/* subtract */
488__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
489static void _blend_subtract(const float *const restrict a, const float *const restrict b,
490 float *const restrict out, const float *const restrict mask, const size_t stride,
492{
493 for(size_t i = 0; i < stride; i++)
494 {
495 size_t j = i * DT_BLENDIF_LAB_CH;
496 float local_opacity = mask[i];
497 dt_aligned_pixel_t ta, tb;
498
499 _blend_Lab_scale(a + j, ta);
500 _blend_Lab_scale(b + j, tb);
501
503 tb[x] = _CLAMP(ta[x] * (1.0f - local_opacity) + ((tb[x] + ta[x]) - (fabsf(min[x] + max[x]))) * local_opacity,
504 min[x], max[x]);
505
506 _blend_Lab_rescale(tb, out + j);
507 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
508 }
509}
510
511/* difference (deprecated) */
512__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
513static void _blend_difference(const float *const restrict a, const float *const restrict b,
514 float *const restrict out, const float *const restrict mask, const size_t stride,
516{
517 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
518 {
519 const float local_opacity = mask[i];
520 dt_aligned_pixel_t ta, tb;
521
522 _blend_Lab_scale(a + j, ta);
523 _blend_Lab_scale(b + j, tb);
524
525 const float lmin = 0.0f;
526 for(size_t x = 0; x < 3; x++)
527 {
528 float lmax = max[x] + fabsf(min[x]);
529 float la = _CLAMP(ta[x] + fabsf(min[x]), lmin, lmax);
530 float lb = _CLAMP(tb[x] + fabsf(min[x]), lmin, lmax);
531 tb[x] = _CLAMP(la * (1.0f - local_opacity) + fabsf(la - lb) * local_opacity, lmin, lmax) - fabsf(min[x]);
532 }
533
534 _blend_Lab_rescale(tb, out + j);
535 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
536 }
537}
538
539/* difference 2 (new) */
540__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
541static void _blend_difference2(const float *const restrict a, const float *const restrict b,
542 float *const restrict out, const float *const restrict mask, const size_t stride,
544{
545 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
546 {
547 const float local_opacity = mask[i];
548 dt_aligned_pixel_t ta, tb;
549
550 _blend_Lab_scale(a + j, ta);
551 _blend_Lab_scale(b + j, tb);
552
554 tb[x] = fabsf(ta[x] - tb[x]) / fabsf(max[x] - min[x]);
555 tb[0] = fmaxf(tb[0], fmaxf(tb[1], tb[2]));
556
557 tb[0] = _CLAMP(ta[0] * (1.0f - local_opacity) + tb[0] * local_opacity, min[0], max[0]);
558 tb[1] = 0.0f;
559 tb[2] = 0.0f;
560
561 _blend_Lab_rescale(tb, out + j);
562 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
563 }
564}
565
566/* screen */
567__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
568static void _blend_screen(const float *const restrict a, const float *const restrict b,
569 float *const restrict out, const float *const restrict mask, const size_t stride,
571{
572 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
573 {
574 const float local_opacity = mask[i];
575 dt_aligned_pixel_t ta, tb;
576
577 _blend_Lab_scale(a + j, ta);
578 _blend_Lab_scale(b + j, tb);
579
580 const float lmin = 0.0f;
581 const float lmax = max[0] + fabsf(min[0]);
582 const float la = _CLAMP(ta[0] + fabsf(min[0]), lmin, lmax);
583 const float lb = _CLAMP(tb[0] + fabsf(min[0]), lmin, lmax);
584
585 tb[0] = _CLAMP(la * (1.0f - local_opacity) + ((lmax - (lmax - la) * (lmax - lb))) * local_opacity, lmin, lmax)
586 - fabsf(min[0]);
587
588 const float f = fmaxf(ta[0], 0.01f);
589 tb[1] = _CLAMP(ta[1] * (1.0f - local_opacity) + 0.5f * (ta[1] + tb[1]) * tb[0] / f * local_opacity,
590 min[1], max[1]);
591 tb[2] = _CLAMP(ta[2] * (1.0f - local_opacity) + 0.5f * (ta[2] + tb[2]) * tb[0] / f * local_opacity,
592 min[2], max[2]);
593
594 _blend_Lab_rescale(tb, out + j);
595 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
596 }
597}
598
599/* overlay */
600__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
601static void _blend_overlay(const float *const restrict a, const float *const restrict b,
602 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_LAB_CH)
606 {
607 const float local_opacity = mask[i];
608 const float local_opacity2 = local_opacity * local_opacity;
609 dt_aligned_pixel_t ta, tb;
610
611 _blend_Lab_scale(&a[j], ta);
612 _blend_Lab_scale(&b[j], tb);
613
614 const float lmin = 0.0f;
615 const float lmax = max[0] + fabsf(min[0]);
616 const float la = _CLAMP(ta[0] + fabsf(min[0]), lmin, lmax);
617 const float lb = _CLAMP(tb[0] + fabsf(min[0]), lmin, lmax);
618 const float halfmax = lmax / 2.0f;
619 const float doublemax = lmax * 2.0f;
620
621 tb[0] = _CLAMP(la * (1.0f - local_opacity2)
622 + (la > halfmax ? lmax - (lmax - doublemax * (la - halfmax)) * (lmax - lb)
623 : (doublemax * la) * lb)
624 * local_opacity2, lmin, lmax)
625 - fabsf(min[0]);
626
627 const float f = fmaxf(ta[0], 0.01f);
628 tb[1] = _CLAMP(ta[1] * (1.0f - local_opacity2) + (ta[1] + tb[1]) * tb[0] / f * local_opacity2, min[1], max[1]);
629 tb[2] = _CLAMP(ta[2] * (1.0f - local_opacity2) + (ta[2] + tb[2]) * tb[0] / f * local_opacity2, min[2], max[2]);
630
631 _blend_Lab_rescale(tb, out + j);
632 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
633 }
634}
635
636/* softlight */
637__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
638static void _blend_softlight(const float *const restrict a, const float *const restrict b,
639 float *const restrict out, const float *const restrict mask, const size_t stride,
641{
642 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
643 {
644 const float local_opacity = mask[i];
645 const float local_opacity2 = local_opacity * local_opacity;
646 dt_aligned_pixel_t ta, tb;
647
648 _blend_Lab_scale(a + j, ta);
649 _blend_Lab_scale(b + j, tb);
650
651 const float lmin = 0.0f;
652 const float lmax = max[0] + fabsf(min[0]);
653 const float la = _CLAMP(ta[0] + fabsf(min[0]), lmin, lmax);
654 const float lb = _CLAMP(tb[0] + fabsf(min[0]), lmin, lmax);
655 const float halfmax = lmax / 2.0f;
656
657 tb[0] = _CLAMP(la * (1.0f - local_opacity2)
658 + (lb > halfmax ? lmax - (lmax - la) * (lmax - (lb - halfmax))
659 : la * (lb + halfmax))
660 * local_opacity2, lmin, lmax)
661 - fabsf(min[0]);
662
663 const float f = fmaxf(ta[0], 0.01f);
664 tb[1] = _CLAMP(ta[1] * (1.0f - local_opacity2) + (ta[1] + tb[1]) * tb[0] / f * local_opacity2, min[1], max[1]);
665 tb[2] = _CLAMP(ta[2] * (1.0f - local_opacity2) + (ta[2] + tb[2]) * tb[0] / f * local_opacity2, min[2], max[2]);
666
667 _blend_Lab_rescale(tb, out + j);
668 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
669 }
670}
671
672/* hardlight */
673__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
674static void _blend_hardlight(const float *const restrict a, const float *const restrict b,
675 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_LAB_CH)
679 {
680 const float local_opacity = mask[i];
681 const float local_opacity2 = local_opacity * local_opacity;
682 dt_aligned_pixel_t ta, tb;
683
684 _blend_Lab_scale(a + j, ta);
685 _blend_Lab_scale(b + j, tb);
686
687 const float lmin = 0.0f;
688 const float lmax = max[0] + fabsf(min[0]);
689 const float la = _CLAMP(ta[0] + fabsf(min[0]), lmin, lmax);
690 const float lb = _CLAMP(tb[0] + fabsf(min[0]), lmin, lmax);
691 const float halfmax = lmax / 2.0f;
692 const float doublemax = lmax * 2.0f;
693
694 tb[0] = _CLAMP(la * (1.0f - local_opacity2)
695 + (lb > halfmax ? lmax - (lmax - doublemax * (la - halfmax)) * (lmax - lb)
696 : doublemax * la * lb)
697 * local_opacity2, lmin, lmax)
698 - fabsf(min[0]);
699
700 const float f = fmaxf(ta[0], 0.01f);
701 tb[1] = _CLAMP(ta[1] * (1.0f - local_opacity2) + (ta[1] + tb[1]) * tb[0] / f * local_opacity2, min[1], max[1]);
702 tb[2] = _CLAMP(ta[2] * (1.0f - local_opacity2) + (ta[2] + tb[2]) * tb[0] / f * local_opacity2, min[2], max[2]);
703
704 _blend_Lab_rescale(tb, out + j);
705 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
706 }
707}
708
709/* vividlight */
710__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
711static void _blend_vividlight(const float *const restrict a, const float *const restrict b,
712 float *const restrict out, const float *const restrict mask, const size_t stride,
714{
715 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
716 {
717 const float local_opacity = mask[i];
718 const float local_opacity2 = local_opacity * local_opacity;
719 dt_aligned_pixel_t ta, tb;
720
721 _blend_Lab_scale(a + j, ta);
722 _blend_Lab_scale(b + j, tb);
723
724 const float lmin = 0.0f;
725 const float lmax = max[0] + fabsf(min[0]);
726 const float la = _CLAMP(ta[0] + fabsf(min[0]), lmin, lmax);
727 const float lb = _CLAMP(tb[0] + fabsf(min[0]), lmin, lmax);
728 const float halfmax = lmax / 2.0f;
729 const float doublemax = lmax * 2.0f;
730
731 tb[0] = _CLAMP(la * (1.0f - local_opacity2)
732 + (lb > halfmax ? (lb >= lmax ? lmax : la / (doublemax * (lmax - lb)))
733 : (lb <= lmin ? lmin : lmax - (lmax - la) / (doublemax * lb)))
734 * local_opacity2, lmin, lmax)
735 - fabsf(min[0]);
736
737 const float f = fmaxf(ta[0], 0.01f);
738 tb[1] = _CLAMP(ta[1] * (1.0f - local_opacity2) + (ta[1] + tb[1]) * tb[0] / f * local_opacity2, min[1], max[1]);
739 tb[2] = _CLAMP(ta[2] * (1.0f - local_opacity2) + (ta[2] + tb[2]) * tb[0] / f * local_opacity2, min[2], max[2]);
740
741 _blend_Lab_rescale(tb, out + j);
742 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
743 }
744}
745
746/* linearlight */
747__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
748static void _blend_linearlight(const float *const restrict a, const float *const restrict b,
749 float *const restrict out, const float *const restrict mask, const size_t stride,
751{
752 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
753 {
754 const float local_opacity = mask[i];
755 const float local_opacity2 = local_opacity * local_opacity;
756 dt_aligned_pixel_t ta, tb;
757
758 _blend_Lab_scale(a + j, ta);
759 _blend_Lab_scale(b + j, tb);
760
761 const float lmin = 0.0f;
762 const float lmax = max[0] + fabsf(min[0]);
763 const float la = _CLAMP(ta[0] + fabsf(min[0]), lmin, lmax);
764 const float lb = _CLAMP(tb[0] + fabsf(min[0]), lmin, lmax);
765 const float doublemax = lmax * 2.0f;
766
767 tb[0] = _CLAMP(la * (1.0f - local_opacity2) + (la + doublemax * lb - lmax) * local_opacity2, lmin, lmax)
768 - fabsf(min[0]);
769
770 const float f = fmaxf(ta[0], 0.01f);
771 tb[1] = _CLAMP(ta[1] * (1.0f - local_opacity2) + (ta[1] + tb[1]) * tb[0] / f * local_opacity2, min[1], max[1]);
772 tb[2] = _CLAMP(ta[2] * (1.0f - local_opacity2) + (ta[2] + tb[2]) * tb[0] / f * local_opacity2, min[2], max[2]);
773
774 _blend_Lab_rescale(tb, out + j);
775 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
776 }
777}
778
779/* pinlight */
780__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
781static void _blend_pinlight(const float *const restrict a, const float *const restrict b,
782 float *const restrict out, const float *const restrict mask, const size_t stride,
784{
785 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
786 {
787 const float local_opacity = mask[i];
788 const float local_opacity2 = local_opacity * local_opacity;
789 dt_aligned_pixel_t ta, tb;
790
791 _blend_Lab_scale(a + j, ta);
792 _blend_Lab_scale(b + j, tb);
793
794 const float lmin = 0.0f;
795 const float lmax = max[0] + fabsf(min[0]);
796 const float la = _CLAMP(ta[0] + fabsf(min[0]), lmin, lmax);
797 const float lb = _CLAMP(tb[0] + fabsf(min[0]), lmin, lmax);
798 const float halfmax = lmax / 2.0f;
799 const float doublemax = lmax * 2.0f;
800
801 tb[0] = _CLAMP(la * (1.0f - local_opacity2)
802 + (lb > halfmax ? fmaxf(la, doublemax * (lb - halfmax))
803 : fminf(la, doublemax * lb))
804 * local_opacity2, lmin, lmax)
805 - fabsf(min[0]);
806
807 tb[1] = _CLAMP(ta[1], min[1], max[1]);
808 tb[2] = _CLAMP(ta[2], min[2], max[2]);
809
810 _blend_Lab_rescale(tb, out + j);
811 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
812 }
813}
814
815/* lightness blend */
816__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
817static void _blend_lightness(const float *const restrict a, const float *const restrict b,
818 float *const restrict out, const float *const restrict mask, const size_t stride,
820{
821 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
822 {
823 const float local_opacity = mask[i];
824 dt_aligned_pixel_t ta, tb;
825
826 _blend_Lab_scale(a + j, ta);
827 _blend_Lab_scale(b + j, tb);
828
829 // no need to transfer to LCH as L is the same as in Lab, and C and H
830 // remain unchanged
831 tb[0] = _CLAMP(ta[0] * (1.0f - local_opacity) + tb[0] * local_opacity, min[0], max[0]);
832 tb[1] = _CLAMP(ta[1], min[1], max[1]);
833 tb[2] = _CLAMP(ta[2], min[2], max[2]);
834
835 _blend_Lab_rescale(tb, out + j);
836 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
837 }
838}
839
840/* chroma blend */
841__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
842static void _blend_chromaticity(const float *const restrict a, const float *const restrict b,
843 float *const restrict out, const float *const restrict mask, const size_t stride,
845{
846 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
847 {
848 const float local_opacity = mask[i];
849 dt_aligned_pixel_t ta, tb;
850 dt_aligned_pixel_t tta, ttb;
851
852 _blend_Lab_scale(a + j, ta);
853 _CLAMP_XYZ(ta, min, max);
854 dt_Lab_2_LCH(ta, tta);
855
856 _blend_Lab_scale(b + j, tb);
857 _CLAMP_XYZ(tb, min, max);
858 dt_Lab_2_LCH(tb, ttb);
859
860 ttb[0] = tta[0];
861 ttb[1] = (tta[1] * (1.0f - local_opacity)) + ttb[1] * local_opacity;
862 ttb[2] = tta[2];
863
864 dt_LCH_2_Lab(ttb, tb);
865 _CLAMP_XYZ(tb, min, max);
866 _blend_Lab_rescale(tb, out + j);
867 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
868 }
869}
870
871/* hue blend */
872__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
873static void _blend_hue(const float *const restrict a, const float *const restrict b,
874 float *const restrict out, const float *const restrict mask, const size_t stride,
876{
877 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
878 {
879 const float local_opacity = mask[i];
880 dt_aligned_pixel_t ta, tb;
881 dt_aligned_pixel_t tta, ttb;
882
883 _blend_Lab_scale(a + j, ta);
884 _CLAMP_XYZ(ta, min, max);
885 dt_Lab_2_LCH(ta, tta);
886
887 _blend_Lab_scale(b + j, tb);
888 _CLAMP_XYZ(tb, min, max);
889 dt_Lab_2_LCH(tb, ttb);
890
891 ttb[0] = tta[0];
892 ttb[1] = tta[1];
893 /* blend hue along shortest distance on color circle */
894 const float d = fabsf(tta[2] - ttb[2]);
895 const float s = d > 0.5f ? -local_opacity * (1.0f - d) / d : local_opacity;
896 ttb[2] = fmodf((tta[2] * (1.0f - s)) + ttb[2] * s + 1.0f, 1.0f);
897
898 dt_LCH_2_Lab(ttb, tb);
899 _CLAMP_XYZ(tb, min, max);
900 _blend_Lab_rescale(tb, out + j);
901 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
902 }
903}
904
905/* color blend; blend hue and chroma, but not lightness */
906__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
907static void _blend_color(const float *const restrict a, const float *const restrict b,
908 float *const restrict out, const float *const restrict mask, const size_t stride,
910{
911 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
912 {
913 const float local_opacity = mask[i];
914 dt_aligned_pixel_t ta, tb;
915 dt_aligned_pixel_t tta, ttb;
916
917 _blend_Lab_scale(a + j, ta);
918 _CLAMP_XYZ(ta, min, max);
919 dt_Lab_2_LCH(ta, tta);
920
921 _blend_Lab_scale(b + j, tb);
922 _CLAMP_XYZ(tb, min, max);
923 dt_Lab_2_LCH(tb, ttb);
924
925 ttb[0] = tta[0];
926 ttb[1] = (tta[1] * (1.0f - local_opacity)) + ttb[1] * local_opacity;
927
928 /* blend hue along shortest distance on color circle */
929 const float d = fabsf(tta[2] - ttb[2]);
930 const float s = d > 0.5f ? -local_opacity * (1.0f - d) / d : local_opacity;
931 ttb[2] = fmodf((tta[2] * (1.0f - s)) + ttb[2] * s + 1.0f, 1.0f);
932
933 dt_LCH_2_Lab(ttb, tb);
934 _CLAMP_XYZ(tb, min, max);
935 _blend_Lab_rescale(tb, out + j);
936 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
937 }
938}
939
940/* color adjustment; blend hue and chroma; take lightness from module output */
941__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
942static void _blend_coloradjust(const float *const restrict a, const float *const restrict b,
943 float *const restrict out, const float *const restrict mask, const size_t stride,
945{
946 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
947 {
948 const float local_opacity = mask[i];
949 dt_aligned_pixel_t ta, tb;
950 dt_aligned_pixel_t tta, ttb;
951
952 _blend_Lab_scale(a + j, ta);
953 _CLAMP_XYZ(ta, min, max);
954 dt_Lab_2_LCH(ta, tta);
955
956 _blend_Lab_scale(b + j, tb);
957 _CLAMP_XYZ(tb, min, max);
958 dt_Lab_2_LCH(tb, ttb);
959
960 // ttb[0] (output lightness) unchanged
961 ttb[1] = (tta[1] * (1.0f - local_opacity)) + ttb[1] * local_opacity;
962
963 /* blend hue along shortest distance on color circle */
964 const float d = fabsf(tta[2] - ttb[2]);
965 const float s = d > 0.5f ? -local_opacity * (1.0f - d) / d : local_opacity;
966 ttb[2] = fmodf((tta[2] * (1.0f - s)) + ttb[2] * s + 1.0f, 1.0f);
967
968 dt_LCH_2_Lab(ttb, tb);
969 _CLAMP_XYZ(tb, min, max);
970 _blend_Lab_rescale(tb, out + j);
971 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
972 }
973}
974
975/* blend only lightness in Lab color space without any clamping */
976__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
977static void _blend_Lab_lightness(const float *const restrict a, const float *const restrict b,
978 float *const restrict out, const float *const restrict mask, const size_t stride,
980{
981 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
982 {
983 const float local_opacity = mask[i];
984 dt_aligned_pixel_t ta, tb;
985
986 _blend_Lab_scale(a + j, ta);
987 _blend_Lab_scale(b + j, tb);
988
989 tb[0] = ta[0] * (1.0f - local_opacity) + tb[0] * local_opacity;
990 tb[1] = ta[1];
991 tb[2] = ta[2];
992
993 _blend_Lab_rescale(tb, out + j);
994 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
995 }
996}
997
998/* blend only a-channel in Lab color space without any clamping */
999__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
1000static void _blend_Lab_a(const float *const restrict a, const float *const restrict b,
1001 float *const restrict out, const float *const restrict mask, const size_t stride,
1003{
1004 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
1005 {
1006 const float local_opacity = mask[i];
1007 dt_aligned_pixel_t ta, tb;
1008
1009 _blend_Lab_scale(a + j, ta);
1010 _blend_Lab_scale(b + j, tb);
1011
1012 tb[0] = ta[0];
1013 tb[1] = ta[1] * (1.0f - local_opacity) + tb[1] * local_opacity;
1014 tb[2] = ta[2];
1015
1016 _blend_Lab_rescale(tb, out + j);
1017 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
1018 }
1019}
1020
1021/* blend only b-channel in Lab color space without any clamping */
1022__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
1023static void _blend_Lab_b(const float *const restrict a, const float *const restrict b,
1024 float *const restrict out, const float *const restrict mask, const size_t stride,
1026{
1027 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
1028 {
1029 const float local_opacity = mask[i];
1030 dt_aligned_pixel_t ta, tb;
1031
1032 _blend_Lab_scale(a + j, ta);
1033 _blend_Lab_scale(b + j, tb);
1034
1035 tb[0] = ta[0];
1036 tb[1] = ta[1];
1037 tb[2] = ta[2] * (1.0f - local_opacity) + tb[2] * local_opacity;
1038
1039 _blend_Lab_rescale(tb, out + j);
1040 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
1041 }
1042}
1043
1044
1045/* blend only color in Lab color space without any clamping */
1046__OMP_DECLARE_SIMD__(aligned(a, b, out, min, max: 16) uniform(stride, min, max))
1047static void _blend_Lab_color(const float *const restrict a, const float *const restrict b,
1048 float *const restrict out, const float *const restrict mask, const size_t stride,
1050{
1051 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
1052 {
1053 float local_opacity = mask[i];
1054 dt_aligned_pixel_t ta, tb;
1055
1056 _blend_Lab_scale(a + j, ta);
1057 _blend_Lab_scale(b + j, tb);
1058
1059 tb[0] = ta[0];
1060 tb[1] = ta[1] * (1.0f - local_opacity) + tb[1] * local_opacity;
1061 tb[2] = ta[2] * (1.0f - local_opacity) + tb[2] * local_opacity;
1062
1063 _blend_Lab_rescale(tb, out + j);
1064 out[j + DT_BLENDIF_LAB_BCH] = local_opacity;
1065 }
1066}
1067
1068
1069static _blend_row_func *_choose_blend_func(const unsigned int blend_mode)
1070{
1071 _blend_row_func *blend = NULL;
1072
1073 /* select the blend operator */
1074 switch(blend_mode & DEVELOP_BLEND_MODE_MASK)
1075 {
1077 blend = _blend_lighten;
1078 break;
1080 blend = _blend_darken;
1081 break;
1083 blend = _blend_multiply;
1084 break;
1086 blend = _blend_average;
1087 break;
1088 case DEVELOP_BLEND_ADD:
1089 blend = _blend_add;
1090 break;
1092 blend = _blend_subtract;
1093 break;
1095 blend = _blend_difference;
1096 break;
1098 blend = _blend_difference2;
1099 break;
1101 blend = _blend_screen;
1102 break;
1104 blend = _blend_overlay;
1105 break;
1107 blend = _blend_softlight;
1108 break;
1110 blend = _blend_hardlight;
1111 break;
1113 blend = _blend_vividlight;
1114 break;
1116 blend = _blend_linearlight;
1117 break;
1119 blend = _blend_pinlight;
1120 break;
1122 blend = _blend_lightness;
1123 break;
1125 blend = _blend_chromaticity;
1126 break;
1127 case DEVELOP_BLEND_HUE:
1128 blend = _blend_hue;
1129 break;
1131 blend = _blend_color;
1132 break;
1134 blend = _blend_normal_bounded;
1135 break;
1137 blend = _blend_coloradjust;
1138 break;
1141 blend = _blend_Lab_lightness;
1142 break;
1144 blend = _blend_Lab_a;
1145 break;
1147 blend = _blend_Lab_b;
1148 break;
1150 blend = _blend_Lab_color;
1151 break;
1152
1153 /* fallback to normal blend */
1155 default:
1156 blend = _blend_normal_unbounded;
1157 break;
1158 }
1159
1160 return blend;
1161}
1162
1163
1164__OMP_DECLARE_SIMD__(aligned(out:16))
1165static inline void _display_channel_value(dt_aligned_pixel_t out, const float value, const float mask)
1166{
1167 out[0] = value;
1168 out[1] = value;
1169 out[2] = value;
1170 out[3] = mask;
1171}
1172
1173__OMP_DECLARE_SIMD__(aligned(a, b:16) uniform(channel, stride))
1174static void _display_channel(const float *const restrict a, float *const restrict b,
1175 const float *const restrict mask, const size_t stride, const int channel,
1176 const float *const restrict boost_factors)
1177{
1178 switch(channel)
1179 {
1181 {
1182 const float factor = 1.0f / (100.0f * exp2f(boost_factors[DEVELOP_BLENDIF_L_in]));
1183 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
1184 {
1185 const float c = clamp_simd(a[j + 0] * factor);
1186 _display_channel_value(b + j, c, mask[i]);
1187 }
1188 break;
1189 }
1191 {
1192 const float factor = 1.0f / (100.0f * exp2f(boost_factors[DEVELOP_BLENDIF_L_out]));
1193 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
1194 {
1195 const float c = clamp_simd(b[j + 0] * factor);
1196 _display_channel_value(b + j, c, mask[i]);
1197 }
1198 break;
1199 }
1201 {
1202 const float factor = 1.0f / (256.0f * exp2f(boost_factors[DEVELOP_BLENDIF_A_in]));
1203 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
1204 {
1205 const float c = clamp_simd(a[j + 1] * factor + 0.5f);
1206 _display_channel_value(b + j, c, mask[i]);
1207 }
1208 break;
1209 }
1211 {
1212 const float factor = 1.0f / (256.0f * exp2f(boost_factors[DEVELOP_BLENDIF_A_out]));
1213 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
1214 {
1215 const float c = clamp_simd(b[j + 1] * factor + 0.5f);
1216 _display_channel_value(b + j, c, mask[i]);
1217 }
1218 break;
1219 }
1221 {
1222 const float factor = 1.0f / (256.0f * exp2f(boost_factors[DEVELOP_BLENDIF_B_in]));
1223 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
1224 {
1225 const float c = clamp_simd(a[j + 2] * factor + 0.5f);
1226 _display_channel_value(b + j, c, mask[i]);
1227 }
1228 break;
1229 }
1231 {
1232 const float factor = 1.0f / (256.0f * exp2f(boost_factors[DEVELOP_BLENDIF_B_out]));
1233 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
1234 {
1235 const float c = clamp_simd(b[j + 2] * factor + 0.5f);
1236 _display_channel_value(b + j, c, mask[i]);
1237 }
1238 break;
1239 }
1241 {
1242 const float factor = 1.0f / (128.0f * sqrtf(2.0f) * exp2f(boost_factors[DEVELOP_BLENDIF_C_in]));
1243 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
1244 {
1246 dt_Lab_2_LCH(a + j, LCH);
1247 const float c = clamp_simd(LCH[1] * factor);
1248 _display_channel_value(b + j, c, mask[i]);
1249 }
1250 break;
1251 }
1253 {
1254 const float factor = 1.0f / (128.0f * sqrtf(2.0f) * exp2f(boost_factors[DEVELOP_BLENDIF_C_out]));
1255 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
1256 {
1258 dt_Lab_2_LCH(b + j, LCH);
1259 const float c = clamp_simd(LCH[1] * factor);
1260 _display_channel_value(b + j, c, mask[i]);
1261 }
1262 break;
1263 }
1265 // no boost factor for hues
1266 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
1267 {
1269 dt_Lab_2_LCH(a + j, LCH);
1270 const float c = clamp_simd(LCH[2]);
1271 _display_channel_value(b + j, c, mask[i]);
1272 }
1273 break;
1275 // no boost factor for hues
1276 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
1277 {
1279 dt_Lab_2_LCH(b + j, LCH);
1280 const float c = clamp_simd(LCH[2]);
1281 _display_channel_value(b + j, c, mask[i]);
1282 }
1283 break;
1284 default:
1285 for(size_t i = 0, j = 0; i < stride; i++, j += DT_BLENDIF_LAB_CH)
1286 {
1287 _display_channel_value(b + j, 0.0f, mask[i]);
1288 }
1289 break;
1290 }
1291}
1292
1293
1294__OMP_DECLARE_SIMD__(aligned(a, b:16) uniform(stride))
1295static inline void _copy_mask(const float *const restrict a, float *const restrict b, const size_t stride)
1296{
1297 __OMP_SIMD__(aligned(a, b: 16))
1298 for(size_t x = DT_BLENDIF_LAB_BCH; x < stride; x += DT_BLENDIF_LAB_CH) b[x] = a[x];
1299}
1300
1302 const struct dt_dev_pixelpipe_iop_t *piece,
1303 const float *const a, float *const b,
1304 const float *const restrict mask,
1305 const dt_dev_pixelpipe_display_mask_t request_mask_display)
1306{
1307 const dt_iop_roi_t *const roi_in = &piece->roi_in;
1308 const dt_iop_roi_t *const roi_out = &piece->roi_out;
1309 const dt_develop_blend_params_t *const d = (const dt_develop_blend_params_t *const)piece->blendop_data;
1310
1311 if(piece->dsc_in.channels != DT_BLENDIF_LAB_CH) return;
1312
1313 const int xoffs = roi_out->x - roi_in->x;
1314 const int yoffs = roi_out->y - roi_in->y;
1315 const int iwidth = roi_in->width;
1316 const int owidth = roi_out->width;
1317 const int oheight = roi_out->height;
1318
1319 // only non-zero if mask_display was set by an _earlier_ module
1320 const dt_dev_pixelpipe_display_mask_t mask_display = pipe->mask_display;
1321
1322 // process the blending operator
1323 if(request_mask_display & DT_DEV_PIXELPIPE_DISPLAY_ANY)
1324 {
1325 const float *const restrict boost_factors = d->blendif_boost_factors;
1326 const dt_dev_pixelpipe_display_mask_t channel = request_mask_display & DT_DEV_PIXELPIPE_DISPLAY_ANY;
1329 for(size_t y = 0; y < oheight; y++)
1330 {
1331 const size_t a_start = ((y + yoffs) * iwidth + xoffs) * DT_BLENDIF_LAB_CH;
1332 const size_t b_start = y * owidth * DT_BLENDIF_LAB_CH;
1333 const size_t m_start = y * owidth;
1334 _display_channel(a + a_start, b + b_start, mask + m_start, owidth, channel, boost_factors);
1335 }
1336
1337 // the generated output of the channel masks is expressed in RGB but this blending needs to output pixels in
1338 // the Lab color space. A conversion needs thus to be performed. As the pipe is using the work profile to
1339 // convert between Lab and the gamma module (which works in RGB), we need to use use that profile for the
1340 // conversion.
1341 const size_t buffsize = (size_t)owidth * oheight * DT_BLENDIF_LAB_CH;
1342 if(!IS_NULL_PTR(profile))
1343 {
1345 for(size_t j = 0; j < buffsize; j += DT_BLENDIF_LAB_CH)
1346 {
1347 dt_aligned_pixel_t pixel;
1348 for_each_channel(c,aligned(b))
1349 pixel[c] = b[j+c];
1350 const float yellow_mask = b[j+3]; // preserve alpha for code which does in-place conversion
1351 dt_ioppr_rgb_matrix_to_lab(pixel, b + j, profile->matrix_in_transposed, profile->lut_in,
1352 profile->unbounded_coeffs_in, profile->lutsize, profile->nonlinearlut);
1353 b[j+3] = yellow_mask;
1354 }
1355 }
1356 else
1357 {
1358 __OMP_FOR_SIMD__(aligned(b:64))
1359 for(size_t j = 0; j < buffsize; j += DT_BLENDIF_LAB_CH)
1360 {
1362 const float yellow_mask = b[j+3]; // preserve alpha for code which does in-place conversion
1363 dt_Rec709_to_XYZ_D50(b + j, XYZ);
1364 dt_XYZ_to_Lab(XYZ, b + j);
1365 b[j+3] = yellow_mask;
1366 }
1367 }
1368 }
1369 else
1370 {
1371 _blend_row_func *const blend = _choose_blend_func(d->blend_mode);
1372 // minimum and maximum values after scaling !!!
1373 const dt_aligned_pixel_t min = { 0.0f, -1.0f, -1.0f, 0.0f };
1374 const dt_aligned_pixel_t max = { 1.0f, 1.0f, 1.0f, 1.0f };
1375
1376 float *tmp_buffer = dt_pixelpipe_cache_alloc_align_float_cache((size_t)owidth * oheight * DT_BLENDIF_LAB_CH, 0);
1377 if (!IS_NULL_PTR(tmp_buffer))
1378 {
1379 dt_iop_image_copy(tmp_buffer, b, (size_t)owidth * oheight * DT_BLENDIF_LAB_CH);
1380 if((d->blend_mode & DEVELOP_BLEND_REVERSE) == DEVELOP_BLEND_REVERSE)
1381 {
1383 for(size_t y = 0; y < oheight; y++)
1384 {
1385 const size_t a_start = ((y + yoffs) * iwidth + xoffs) * DT_BLENDIF_LAB_CH;
1386 const size_t b_start = y * owidth * DT_BLENDIF_LAB_CH;
1387 const size_t m_start = y * owidth;
1388 blend(tmp_buffer + b_start, a + a_start, b + b_start, mask + m_start, owidth, min, max);
1389 }
1390 }
1391 else
1392 {
1394 for(size_t y = 0; y < oheight; y++)
1395 {
1396 const size_t a_start = ((y + yoffs) * iwidth + xoffs) * DT_BLENDIF_LAB_CH;
1397 const size_t b_start = y * owidth * DT_BLENDIF_LAB_CH;
1398 const size_t m_start = y * owidth;
1399 blend(a + a_start, tmp_buffer + b_start, b + b_start, mask + m_start, owidth, min, max);
1400 }
1401 }
1403 }
1404 }
1405
1406 if(mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK)
1407 {
1408 const size_t stride = owidth * DT_BLENDIF_LAB_CH;
1410 for(size_t y = 0; y < oheight; y++)
1411 {
1412 const size_t a_start = ((y + yoffs) * iwidth + xoffs) * DT_BLENDIF_LAB_CH;
1413 const size_t b_start = y * stride;
1414 _copy_mask(a + a_start, b + b_start, stride);
1415 }
1416 }
1417}
1418
1419// tools/update_modelines.sh
1420// remove-trailing-space on;
1421// clang-format off
1422// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1423// vim: shiftwidth=2 expandtab tabstop=2 cindent
1424// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1425// 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
@ DEVELOP_COMBINE_INV
Definition blend.h:125
@ DEVELOP_COMBINE_INCL
Definition blend.h:127
@ DEVELOP_BLENDIF_C_out
Definition blend.h:166
@ DEVELOP_BLENDIF_A_in
Definition blend.h:146
@ DEVELOP_BLENDIF_C_in
Definition blend.h:163
@ DEVELOP_BLENDIF_L_out
Definition blend.h:149
@ DEVELOP_BLENDIF_h_in
Definition blend.h:164
@ DEVELOP_BLENDIF_Lab_MASK
Definition blend.h:192
@ DEVELOP_BLENDIF_B_in
Definition blend.h:147
@ DEVELOP_BLENDIF_A_out
Definition blend.h:150
@ DEVELOP_BLENDIF_B_out
Definition blend.h:151
@ DEVELOP_BLENDIF_L_in
Definition blend.h:145
#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_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_LAB_A
Definition blend.h:96
@ DEVELOP_BLEND_LAB_COLOR
Definition blend.h:92
@ DEVELOP_BLEND_REVERSE
Definition blend.h:108
@ DEVELOP_BLEND_AVERAGE
Definition blend.h:70
@ DEVELOP_BLEND_MULTIPLY
Definition blend.h:69
@ DEVELOP_BLEND_SCREEN
Definition blend.h:74
@ DEVELOP_BLEND_PINLIGHT
Definition blend.h:80
@ DEVELOP_BLEND_LAB_L
Definition blend.h:95
@ 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_LAB_B
Definition blend.h:97
@ DEVELOP_BLEND_LAB_LIGHTNESS
Definition blend.h:91
@ DEVELOP_BLEND_DIFFERENCE2
Definition blend.h:88
@ DEVELOP_MASK_PARAMETRIC
Definition blend.h:117
#define DT_BLENDIF_LAB_BCH
Definition blendif_lab.c:34
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)
Definition blendif_lab.c:55
#define DT_BLENDIF_LAB_CH
Definition blendif_lab.c:33
void dt_develop_blendif_lab_blend(const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, const float *const a, float *const 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, const dt_aligned_pixel_t min, const dt_aligned_pixel_t max)
Definition blendif_lab.c:37
static void _CLAMP_XYZ(dt_aligned_pixel_t XYZ, const dt_aligned_pixel_t min, const dt_aligned_pixel_t max)
Definition blendif_lab.c:49
static float _CLAMP(const float x, const float min, const float max)
Definition blendif_lab.c:43
void dt_develop_blendif_lab_make_mask(const struct dt_dev_pixelpipe_iop_t *piece, const float *const restrict a, const float *const restrict b, float *const restrict mask)
static void _display_channel_value(dt_aligned_pixel_t out, const float value, const float mask)
static void _blend_Lab_rescale(const float *i, float *o)
static void _blend_Lab_scale(const float *i, float *o)
const dt_aligned_pixel_t f
static dt_aligned_pixel_t LCH
static const float const float const float min
static dt_aligned_pixel_t XYZ
const float max
const dt_colormatrix_t dt_aligned_pixel_t out
dt_Rec709_to_XYZ_D50(rgb, XYZ)
dt_XYZ_to_Lab(XYZ, Lab)
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 for_each_channel(_var,...)
Definition darktable.h:662
#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_a
Definition develop.h:122
@ DT_DEV_PIXELPIPE_DISPLAY_OUTPUT
Definition develop.h:120
@ DT_DEV_PIXELPIPE_DISPLAY_L
Definition develop.h:121
@ DT_DEV_PIXELPIPE_DISPLAY_ANY
Definition develop.h:138
@ DT_DEV_PIXELPIPE_DISPLAY_LCH_h
Definition develop.h:129
@ DT_DEV_PIXELPIPE_DISPLAY_LCH_C
Definition develop.h:128
@ DT_DEV_PIXELPIPE_DISPLAY_b
Definition develop.h:123
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:118
__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
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_work_profile_info(const struct dt_dev_pixelpipe_t *pipe)
static const float x
#define DEVELOP_BLENDIF_SIZE
Definition lightroom.c:235
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
dt_colormatrix_t matrix_in_transposed
Definition iop_profile.h:65
Region of interest passed through the pixelpipe.
Definition imageop.h:72