Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
blendif_raw.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 Ralf Brown.
5 Copyright (C) 2021 Hubert Kowalski.
6 Copyright (C) 2021 Pascal Obry.
7 Copyright (C) 2022 Martin Bařinka.
8 Copyright (C) 2026 Aurélien PIERRE.
9
10 darktable is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 darktable is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with darktable. If not, see <http://www.gnu.org/licenses/>.
22*/
23
24#include "common/imagebuf.h"
25#include "common/math.h"
26#include "develop/blend.h"
27#include "develop/imageop.h"
29#include <math.h>
30
31
32typedef void(_blend_row_func)(const float *const restrict a, const float *const restrict b,
33 float *const restrict out, const float *const restrict mask, const size_t stride);
34
35
36void dt_develop_blendif_raw_make_mask(const struct dt_dev_pixelpipe_iop_t *piece, const float *const restrict a,
37 const float *const restrict b, float *const restrict mask)
38{
39 const dt_iop_roi_t *const roi_out = &piece->roi_out;
40 const dt_develop_blend_params_t *const d = (const dt_develop_blend_params_t *const)piece->blendop_data;
41
42 if(piece->dsc_in.channels != 1) return;
43
44 const int owidth = roi_out->width;
45 const int oheight = roi_out->height;
46 const size_t buffsize = (size_t)owidth * oheight;
47
48 // get the clipped opacity value 0 - 1
49 const float global_opacity = fminf(fmaxf(0.0f, (d->opacity / 100.0f)), 1.0f);
50
51 // get parametric mask (if any) and apply global opacity
52 if(d->mask_combine & DEVELOP_COMBINE_INV)
53 {
54 __OMP_FOR_SIMD__(aligned(mask: 64))
55 for(size_t x = 0; x < buffsize; x++) mask[x] = global_opacity * (1.0f - mask[x]);
56 }
57 else
58 {
59 dt_iop_image_mul_const(mask,global_opacity,owidth,oheight,1); // mask[k] *= global_opacity;
60 }
61}
62
63
64/* normal blend with clamping */
65__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
66static void _blend_normal_bounded(const float *const restrict a, const float *const restrict b,
67 float *const restrict out, const float *const restrict mask, const size_t stride)
68{
69 for(size_t j = 0; j < stride; j++)
70 {
71 const float local_opacity = mask[j];
72 out[j] = clamp_simd(a[j] * (1.0f - local_opacity) + b[j] * local_opacity);
73 }
74}
75
76/* normal blend without any clamping */
77__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
78static void _blend_normal_unbounded(const float *const restrict a, const float *const restrict b,
79 float *const restrict out, const float *const restrict mask,
80 const size_t stride)
81{
82 for(size_t j = 0; j < stride; j++)
83 {
84 const float local_opacity = mask[j];
85 out[j] = a[j] * (1.0f - local_opacity) + b[j] * local_opacity;
86 }
87}
88
89/* lighten */
90__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
91static void _blend_lighten(const float *const restrict a, const float *const restrict b,
92 float *const restrict out, const float *const restrict mask, const size_t stride)
93{
94 for(size_t j = 0; j < stride; j++)
95 {
96 const float local_opacity = mask[j];
97 out[j] = clamp_simd(a[j] * (1.0f - local_opacity) + fmaxf(a[j], b[j]) * local_opacity);
98 }
99}
100
101/* darken */
102__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
103static void _blend_darken(const float *const restrict a, const float *const restrict b,
104 float *const restrict out, const float *const restrict mask, const size_t stride)
105{
106 for(size_t j = 0; j < stride; j++)
107 {
108 const float local_opacity = mask[j];
109 out[j] = clamp_simd(a[j] * (1.0f - local_opacity) + fminf(a[j], b[j]) * local_opacity);
110 }
111}
112
113/* multiply */
114__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
115static void _blend_multiply(const float *const restrict a, const float *const restrict b,
116 float *const restrict out, const float *const restrict mask, const size_t stride)
117{
118 for(size_t j = 0; j < stride; j++)
119 {
120 const float local_opacity = mask[j];
121 out[j] = clamp_simd(a[j] * (1.0f - local_opacity) + (a[j] * b[j]) * local_opacity);
122 }
123}
124
125/* average */
126__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
127static void _blend_average(const float *const restrict a, const float *const restrict b,
128 float *const restrict out, const float *const restrict mask, const size_t stride)
129{
130 for(size_t j = 0; j < stride; j++)
131 {
132 const float local_opacity = mask[j];
133 out[j] = clamp_simd(a[j] * (1.0f - local_opacity) + (a[j] + b[j]) / 2.0f * local_opacity);
134 }
135}
136
137/* add */
138__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
139static void _blend_add(const float *const restrict a, const float *const restrict b,
140 float *const restrict out, const float *const restrict mask, const size_t stride)
141{
142 for(size_t j = 0; j < stride; j++)
143 {
144 const float local_opacity = mask[j];
145 out[j] = clamp_simd(a[j] * (1.0f - local_opacity) + (a[j] + b[j]) * local_opacity);
146 }
147}
148
149/* subtract */
150__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
151static void _blend_subtract(const float *const restrict a, const float *const restrict b,
152 float *const restrict out, const float *const restrict mask, const size_t stride)
153{
154 for(size_t j = 0; j < stride; j++)
155 {
156 const float local_opacity = mask[j];
157 out[j] = clamp_simd(a[j] * (1.0f - local_opacity) + ((b[j] + a[j]) - 1.0f) * local_opacity);
158 }
159}
160
161/* difference */
162__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
163static void _blend_difference(const float *const restrict a, const float *const restrict b,
164 float *const restrict out, const float *const restrict mask, const size_t stride)
165{
166 for(size_t j = 0; j < stride; j++)
167 {
168 const float local_opacity = mask[j];
169 out[j] = clamp_simd(a[j] * (1.0f - local_opacity) + fabsf(a[j] - b[j]) * local_opacity);
170 }
171}
172
173/* screen */
174__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
175static void _blend_screen(const float *const restrict a, const float *const restrict b,
176 float *const restrict out, const float *const restrict mask, const size_t stride)
177{
178 for(size_t j = 0; j < stride; j++)
179 {
180 const float local_opacity = mask[j];
181 const float la = clamp_simd(a[j]);
182 const float lb = clamp_simd(b[j]);
183 out[j] = clamp_simd(la * (1.0f - local_opacity) + (1.0f - (1.0f - la) * (1.0f - lb)) * local_opacity);
184 }
185}
186
187/* overlay */
188__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
189static void _blend_overlay(const float *const restrict a, const float *const restrict b,
190 float *const restrict out, const float *const restrict mask, const size_t stride)
191{
192 for(size_t j = 0; j < stride; j++)
193 {
194 const float local_opacity = mask[j];
195 const float local_opacity2 = local_opacity * local_opacity;
196 const float la = clamp_simd(a[j]);
197 const float lb = clamp_simd(b[j]);
198 out[j] = clamp_simd(
199 la * (1.0f - local_opacity2)
200 + (la > 0.5f ? 1.0f - (1.0f - 2.0f * (la - 0.5f)) * (1.0f - lb) : 2.0f * la * lb) * local_opacity2);
201 }
202}
203
204/* softlight */
205__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
206static void _blend_softlight(const float *const restrict a, const float *const restrict b,
207 float *const restrict out, const float *const restrict mask, const size_t stride)
208{
209 for(size_t j = 0; j < stride; j++)
210 {
211 const float local_opacity = mask[j];
212 const float local_opacity2 = local_opacity * local_opacity;
213 const float la = clamp_simd(a[j]);
214 const float lb = clamp_simd(b[j]);
215 out[j] = clamp_simd(
216 la * (1.0f - local_opacity2)
217 + (lb > 0.5f ? 1.0f - (1.0f - la) * (1.0f - (lb - 0.5f)) : la * (lb + 0.5f)) * local_opacity2);
218 }
219}
220
221/* hardlight */
222__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
223static void _blend_hardlight(const float *const restrict a, const float *const restrict b,
224 float *const restrict out, const float *const restrict mask, const size_t stride)
225{
226 for(size_t j = 0; j < stride; j++)
227 {
228 const float local_opacity = mask[j];
229 const float local_opacity2 = local_opacity * local_opacity;
230 const float la = clamp_simd(a[j]);
231 const float lb = clamp_simd(b[j]);
232 out[j] = clamp_simd(
233 la * (1.0f - local_opacity2)
234 + (lb > 0.5f ? 1.0f - (1.0f - 2.0f * (la - 0.5f)) * (1.0f - lb) : 2.0f * la * lb) * local_opacity2);
235 }
236}
237
238/* vividlight */
239__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
240static void _blend_vividlight(const float *const restrict a, const float *const restrict b,
241 float *const restrict out, const float *const restrict mask, const size_t stride)
242{
243 for(size_t j = 0; j < stride; j++)
244 {
245 const float local_opacity = mask[j];
246 const float local_opacity2 = local_opacity * local_opacity;
247 const float la = clamp_simd(a[j]);
248 const float lb = clamp_simd(b[j]);
249 out[j] = clamp_simd(
250 la * (1.0f - local_opacity2)
251 + (lb > 0.5f ? (lb >= 1.0f ? 1.0f : la / (2.0f * (1.0f - lb)))
252 : (lb <= 0.0f ? 0.0f : 1.0f - (1.0f - la) / (2.0f * lb)))
253 * local_opacity2);
254 }
255}
256
257/* linearlight */
258__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
259static void _blend_linearlight(const float *const restrict a, const float *const restrict b,
260 float *const restrict out, const float *const restrict mask, const size_t stride)
261{
262 for(size_t j = 0; j < stride; j++)
263 {
264 const float local_opacity = mask[j];
265 const float local_opacity2 = local_opacity * local_opacity;
266 const float la = clamp_simd(a[j]);
267 const float lb = clamp_simd(b[j]);
268 out[j] = clamp_simd(la * (1.0f - local_opacity2) + (la + 2.0f * lb - 1.0f) * local_opacity2);
269 }
270}
271
272/* pinlight */
273__OMP_DECLARE_SIMD__(aligned(a, b, out:16) uniform(stride))
274static void _blend_pinlight(const float *const restrict a, const float *const restrict b,
275 float *const restrict out, const float *const restrict mask, const size_t stride)
276{
277 for(size_t j = 0; j < stride; j++)
278 {
279 const float local_opacity = mask[j];
280 const float local_opacity2 = local_opacity * local_opacity;
281 const float la = clamp_simd(a[j]);
282 const float lb = clamp_simd(b[j]);
283 out[j] = clamp_simd(
284 la * (1.0f - local_opacity2)
285 + (lb > 0.5f ? fmaxf(la, 2.0f * (lb - 0.5f)) : fminf(la, 2.0f * lb)) * local_opacity2);
286 }
287}
288
289
290static _blend_row_func *_choose_blend_func(const unsigned int blend_mode)
291{
292 _blend_row_func *blend = NULL;
293
294 /* select the blend operator */
295 switch(blend_mode & DEVELOP_BLEND_MODE_MASK)
296 {
298 blend = _blend_lighten;
299 break;
301 blend = _blend_darken;
302 break;
304 blend = _blend_multiply;
305 break;
307 blend = _blend_average;
308 break;
310 blend = _blend_add;
311 break;
313 blend = _blend_subtract;
314 break;
317 blend = _blend_difference;
318 break;
320 blend = _blend_screen;
321 break;
323 blend = _blend_overlay;
324 break;
326 blend = _blend_softlight;
327 break;
329 blend = _blend_hardlight;
330 break;
332 blend = _blend_vividlight;
333 break;
335 blend = _blend_linearlight;
336 break;
338 blend = _blend_pinlight;
339 break;
341 blend = _blend_normal_bounded;
342 break;
343
344 /* fallback to normal blend */
346 default:
347 blend = _blend_normal_unbounded;
348 break;
349 }
350
351 return blend;
352}
353
354
356 const struct dt_dev_pixelpipe_iop_t *piece,
357 const float *const restrict a, float *const restrict b,
358 const float *const restrict mask,
359 const dt_dev_pixelpipe_display_mask_t request_mask_display)
360{
361 (void)pipe;
362 const dt_iop_roi_t *const roi_in = &piece->roi_in;
363 const dt_iop_roi_t *const roi_out = &piece->roi_out;
364 const dt_develop_blend_params_t *const d = (const dt_develop_blend_params_t *const)piece->blendop_data;
365
366 if(piece->dsc_in.channels != 1) return;
367
368 const int xoffs = roi_out->x - roi_in->x;
369 const int yoffs = roi_out->y - roi_in->y;
370 const int iwidth = roi_in->width;
371 const int owidth = roi_out->width;
372 const int oheight = roi_out->height;
373
374 if(request_mask_display & DT_DEV_PIXELPIPE_DISPLAY_ANY)
375 {
376 dt_iop_image_fill(b, 0.0f, owidth, oheight, 1); //b[k] = 0.0f;
377 }
378 else
379 {
380 _blend_row_func *const blend = _choose_blend_func(d->blend_mode);
381
382 float *tmp_buffer = dt_pixelpipe_cache_alloc_align_float_cache((size_t)owidth * oheight, 0);
383 if (!IS_NULL_PTR(tmp_buffer))
384 {
385 dt_iop_image_copy(tmp_buffer, b, (size_t)owidth * oheight);
386 if((d->blend_mode & DEVELOP_BLEND_REVERSE) == DEVELOP_BLEND_REVERSE)
387 {
389 for(size_t y = 0; y < oheight; y++)
390 {
391 const size_t a_start = (y + yoffs) * iwidth + xoffs;
392 const size_t bm_start = y * owidth;
393 blend(tmp_buffer + bm_start, a + a_start, b + bm_start, mask + bm_start, owidth);
394 }
395 }
396 else
397 {
399 for(size_t y = 0; y < oheight; y++)
400 {
401 const size_t a_start = (y + yoffs) * iwidth + xoffs;
402 const size_t bm_start = y * owidth;
403 blend(a + a_start, tmp_buffer + bm_start, b + bm_start, mask + bm_start, owidth);
404 }
405 }
407 }
408 }
409}
410
411// tools/update_modelines.sh
412// remove-trailing-space on;
413// clang-format off
414// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
415// vim: shiftwidth=2 expandtab tabstop=2 cindent
416// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
417// clang-format on
@ DEVELOP_COMBINE_INV
Definition blend.h:125
@ DEVELOP_BLEND_LIGHTEN
Definition blend.h:67
@ DEVELOP_BLEND_DIFFERENCE
Definition blend.h:73
@ 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_OVERLAY
Definition blend.h:75
@ 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_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_DARKEN
Definition blend.h:68
@ DEVELOP_BLEND_DIFFERENCE2
Definition blend.h:88
static _blend_row_func * _choose_blend_func(const unsigned int blend_mode)
void dt_develop_blendif_raw_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)
Definition blendif_raw.c:32
void dt_develop_blendif_raw_make_mask(const struct dt_dev_pixelpipe_iop_t *piece, const float *const restrict a, const float *const restrict b, float *const restrict mask)
Definition blendif_raw.c:36
const dt_colormatrix_t dt_aligned_pixel_t out
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
Definition darktable.h:447
#define __OMP_DECLARE_SIMD__(...)
Definition darktable.h:263
#define dt_pixelpipe_cache_free_align(mem)
Definition darktable.h:453
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
#define __OMP_FOR_SIMD__(...)
Definition darktable.h:260
#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_ANY
Definition develop.h:138
__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
static float clamp_simd(const float x)
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