1 /*
2 This file is part of darktable,
3 Copyright (C) 2011-2021 darktable developers.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #if defined(__GNUC__)
20 #pragma GCC optimize("unroll-loops", "tree-loop-if-convert", "tree-loop-distribution", "no-strict-aliasing", \
21 "loop-interchange", "tree-loop-im", "unswitch-loops", \
22 "tree-loop-ivcanon", "ira-loop-pressure", "split-ivs-in-unroller", "tree-loop-vectorize", \
23 "variable-expansion-in-unroller", "split-loops", "ivopts", "predictive-commoning", \
24 "finite-math-only", "fp-contract=fast", \
25 "fast-math", "no-math-errno")
26 #endif
27
28 #include "common/imagebuf.h"
29 #include "common/math.h"
30 #include "develop/blend.h"
31 #include "develop/imageop.h"
32 #include "develop/openmp_maths.h"
33 #include <math.h>
34
35
36 typedef void(_blend_row_func)(const float *const restrict a, const float *const restrict b,
37 float *const restrict out, const float *const restrict mask, const size_t stride);
38
39
dt_develop_blendif_raw_make_mask(struct dt_dev_pixelpipe_iop_t * piece,const float * const restrict a,const float * const restrict b,const struct dt_iop_roi_t * const roi_in,const struct dt_iop_roi_t * const roi_out,float * const restrict mask)40 void dt_develop_blendif_raw_make_mask(struct dt_dev_pixelpipe_iop_t *piece, const float *const restrict a,
41 const float *const restrict b, const struct dt_iop_roi_t *const roi_in,
42 const struct dt_iop_roi_t *const roi_out, float *const restrict mask)
43 {
44 const dt_develop_blend_params_t *const d = (const dt_develop_blend_params_t *const)piece->blendop_data;
45
46 if(piece->colors != 1) return;
47
48 const int owidth = roi_out->width;
49 const int oheight = roi_out->height;
50 const size_t buffsize = (size_t)owidth * oheight;
51
52 // get the clipped opacity value 0 - 1
53 const float global_opacity = fminf(fmaxf(0.0f, (d->opacity / 100.0f)), 1.0f);
54
55 // get parametric mask (if any) and apply global opacity
56 if(d->mask_combine & DEVELOP_COMBINE_INV)
57 {
58 #ifdef _OPENMP
59 #pragma omp parallel for simd schedule(static) default(none) aligned(mask: 64) \
60 dt_omp_firstprivate(mask, buffsize, global_opacity)
61 #endif
62 for(size_t x = 0; x < buffsize; x++) mask[x] = global_opacity * (1.0f - mask[x]);
63 }
64 else
65 {
66 dt_iop_image_mul_const(mask,global_opacity,owidth,oheight,1); // mask[k] *= global_opacity;
67 }
68 }
69
70
71 /* normal blend with clamping */
72 #ifdef _OPENMP
73 #pragma omp declare simd aligned(a, b, out:16) uniform(stride)
74 #endif
_blend_normal_bounded(const float * const restrict a,const float * const restrict b,float * const restrict out,const float * const restrict mask,const size_t stride)75 static void _blend_normal_bounded(const float *const restrict a, const float *const restrict b,
76 float *const restrict out, const float *const restrict mask, const size_t stride)
77 {
78 for(size_t j = 0; j < stride; j++)
79 {
80 const float local_opacity = mask[j];
81 out[j] = clamp_simd(a[j] * (1.0f - local_opacity) + b[j] * local_opacity);
82 }
83 }
84
85 /* normal blend without any clamping */
86 #ifdef _OPENMP
87 #pragma omp declare simd aligned(a, b, out:16) uniform(stride)
88 #endif
_blend_normal_unbounded(const float * const restrict a,const float * const restrict b,float * const restrict out,const float * const restrict mask,const size_t stride)89 static void _blend_normal_unbounded(const float *const restrict a, const float *const restrict b,
90 float *const restrict out, const float *const restrict mask,
91 const size_t stride)
92 {
93 for(size_t j = 0; j < stride; j++)
94 {
95 const float local_opacity = mask[j];
96 out[j] = a[j] * (1.0f - local_opacity) + b[j] * local_opacity;
97 }
98 }
99
100 /* lighten */
101 #ifdef _OPENMP
102 #pragma omp declare simd aligned(a, b, out:16) uniform(stride)
103 #endif
_blend_lighten(const float * const restrict a,const float * const restrict b,float * const restrict out,const float * const restrict mask,const size_t stride)104 static void _blend_lighten(const float *const restrict a, const float *const restrict b,
105 float *const restrict out, const float *const restrict mask, const size_t stride)
106 {
107 for(size_t j = 0; j < stride; j++)
108 {
109 const float local_opacity = mask[j];
110 out[j] = clamp_simd(a[j] * (1.0f - local_opacity) + fmaxf(a[j], b[j]) * local_opacity);
111 }
112 }
113
114 /* darken */
115 #ifdef _OPENMP
116 #pragma omp declare simd aligned(a, b, out:16) uniform(stride)
117 #endif
_blend_darken(const float * const restrict a,const float * const restrict b,float * const restrict out,const float * const restrict mask,const size_t stride)118 static void _blend_darken(const float *const restrict a, const float *const restrict b,
119 float *const restrict out, const float *const restrict mask, const size_t stride)
120 {
121 for(size_t j = 0; j < stride; j++)
122 {
123 const float local_opacity = mask[j];
124 out[j] = clamp_simd(a[j] * (1.0f - local_opacity) + fminf(a[j], b[j]) * local_opacity);
125 }
126 }
127
128 /* multiply */
129 #ifdef _OPENMP
130 #pragma omp declare simd aligned(a, b, out:16) uniform(stride)
131 #endif
_blend_multiply(const float * const restrict a,const float * const restrict b,float * const restrict out,const float * const restrict mask,const size_t stride)132 static void _blend_multiply(const float *const restrict a, const float *const restrict b,
133 float *const restrict out, const float *const restrict mask, const size_t stride)
134 {
135 for(size_t j = 0; j < stride; j++)
136 {
137 const float local_opacity = mask[j];
138 out[j] = clamp_simd(a[j] * (1.0f - local_opacity) + (a[j] * b[j]) * local_opacity);
139 }
140 }
141
142 /* average */
143 #ifdef _OPENMP
144 #pragma omp declare simd aligned(a, b, out:16) uniform(stride)
145 #endif
_blend_average(const float * const restrict a,const float * const restrict b,float * const restrict out,const float * const restrict mask,const size_t stride)146 static void _blend_average(const float *const restrict a, const float *const restrict b,
147 float *const restrict out, const float *const restrict mask, const size_t stride)
148 {
149 for(size_t j = 0; j < stride; j++)
150 {
151 const float local_opacity = mask[j];
152 out[j] = clamp_simd(a[j] * (1.0f - local_opacity) + (a[j] + b[j]) / 2.0f * local_opacity);
153 }
154 }
155
156 /* add */
157 #ifdef _OPENMP
158 #pragma omp declare simd aligned(a, b, out:16) uniform(stride)
159 #endif
_blend_add(const float * const restrict a,const float * const restrict b,float * const restrict out,const float * const restrict mask,const size_t stride)160 static void _blend_add(const float *const restrict a, const float *const restrict b,
161 float *const restrict out, const float *const restrict mask, const size_t stride)
162 {
163 for(size_t j = 0; j < stride; j++)
164 {
165 const float local_opacity = mask[j];
166 out[j] = clamp_simd(a[j] * (1.0f - local_opacity) + (a[j] + b[j]) * local_opacity);
167 }
168 }
169
170 /* subtract */
171 #ifdef _OPENMP
172 #pragma omp declare simd aligned(a, b, out:16) uniform(stride)
173 #endif
_blend_subtract(const float * const restrict a,const float * const restrict b,float * const restrict out,const float * const restrict mask,const size_t stride)174 static void _blend_subtract(const float *const restrict a, const float *const restrict b,
175 float *const restrict out, const float *const restrict mask, const size_t stride)
176 {
177 for(size_t j = 0; j < stride; j++)
178 {
179 const float local_opacity = mask[j];
180 out[j] = clamp_simd(a[j] * (1.0f - local_opacity) + ((b[j] + a[j]) - 1.0f) * local_opacity);
181 }
182 }
183
184 /* difference */
185 #ifdef _OPENMP
186 #pragma omp declare simd aligned(a, b, out:16) uniform(stride)
187 #endif
_blend_difference(const float * const restrict a,const float * const restrict b,float * const restrict out,const float * const restrict mask,const size_t stride)188 static void _blend_difference(const float *const restrict a, const float *const restrict b,
189 float *const restrict out, const float *const restrict mask, const size_t stride)
190 {
191 for(size_t j = 0; j < stride; j++)
192 {
193 const float local_opacity = mask[j];
194 out[j] = clamp_simd(a[j] * (1.0f - local_opacity) + fabsf(a[j] - b[j]) * local_opacity);
195 }
196 }
197
198 /* screen */
199 #ifdef _OPENMP
200 #pragma omp declare simd aligned(a, b, out:16) uniform(stride)
201 #endif
_blend_screen(const float * const restrict a,const float * const restrict b,float * const restrict out,const float * const restrict mask,const size_t stride)202 static void _blend_screen(const float *const restrict a, const float *const restrict b,
203 float *const restrict out, const float *const restrict mask, const size_t stride)
204 {
205 for(size_t j = 0; j < stride; j++)
206 {
207 const float local_opacity = mask[j];
208 const float la = clamp_simd(a[j]);
209 const float lb = clamp_simd(b[j]);
210 out[j] = clamp_simd(la * (1.0f - local_opacity) + (1.0f - (1.0f - la) * (1.0f - lb)) * local_opacity);
211 }
212 }
213
214 /* overlay */
215 #ifdef _OPENMP
216 #pragma omp declare simd aligned(a, b, out:16) uniform(stride)
217 #endif
_blend_overlay(const float * const restrict a,const float * const restrict b,float * const restrict out,const float * const restrict mask,const size_t stride)218 static void _blend_overlay(const float *const restrict a, const float *const restrict b,
219 float *const restrict out, const float *const restrict mask, const size_t stride)
220 {
221 for(size_t j = 0; j < stride; j++)
222 {
223 const float local_opacity = mask[j];
224 const float local_opacity2 = local_opacity * local_opacity;
225 const float la = clamp_simd(a[j]);
226 const float lb = clamp_simd(b[j]);
227 out[j] = clamp_simd(
228 la * (1.0f - local_opacity2)
229 + (la > 0.5f ? 1.0f - (1.0f - 2.0f * (la - 0.5f)) * (1.0f - lb) : 2.0f * la * lb) * local_opacity2);
230 }
231 }
232
233 /* softlight */
234 #ifdef _OPENMP
235 #pragma omp declare simd aligned(a, b, out:16) uniform(stride)
236 #endif
_blend_softlight(const float * const restrict a,const float * const restrict b,float * const restrict out,const float * const restrict mask,const size_t stride)237 static void _blend_softlight(const float *const restrict a, const float *const restrict b,
238 float *const restrict out, const float *const restrict mask, const size_t stride)
239 {
240 for(size_t j = 0; j < stride; j++)
241 {
242 const float local_opacity = mask[j];
243 const float local_opacity2 = local_opacity * local_opacity;
244 const float la = clamp_simd(a[j]);
245 const float lb = clamp_simd(b[j]);
246 out[j] = clamp_simd(
247 la * (1.0f - local_opacity2)
248 + (lb > 0.5f ? 1.0f - (1.0f - la) * (1.0f - (lb - 0.5f)) : la * (lb + 0.5f)) * local_opacity2);
249 }
250 }
251
252 /* hardlight */
253 #ifdef _OPENMP
254 #pragma omp declare simd aligned(a, b, out:16) uniform(stride)
255 #endif
_blend_hardlight(const float * const restrict a,const float * const restrict b,float * const restrict out,const float * const restrict mask,const size_t stride)256 static void _blend_hardlight(const float *const restrict a, const float *const restrict b,
257 float *const restrict out, const float *const restrict mask, const size_t stride)
258 {
259 for(size_t j = 0; j < stride; j++)
260 {
261 const float local_opacity = mask[j];
262 const float local_opacity2 = local_opacity * local_opacity;
263 const float la = clamp_simd(a[j]);
264 const float lb = clamp_simd(b[j]);
265 out[j] = clamp_simd(
266 la * (1.0f - local_opacity2)
267 + (lb > 0.5f ? 1.0f - (1.0f - 2.0f * (la - 0.5f)) * (1.0f - lb) : 2.0f * la * lb) * local_opacity2);
268 }
269 }
270
271 /* vividlight */
272 #ifdef _OPENMP
273 #pragma omp declare simd aligned(a, b, out:16) uniform(stride)
274 #endif
_blend_vividlight(const float * const restrict a,const float * const restrict b,float * const restrict out,const float * const restrict mask,const size_t stride)275 static void _blend_vividlight(const float *const restrict a, const float *const restrict b,
276 float *const restrict out, const float *const restrict mask, const size_t stride)
277 {
278 for(size_t j = 0; j < stride; j++)
279 {
280 const float local_opacity = mask[j];
281 const float local_opacity2 = local_opacity * local_opacity;
282 const float la = clamp_simd(a[j]);
283 const float lb = clamp_simd(b[j]);
284 out[j] = clamp_simd(
285 la * (1.0f - local_opacity2)
286 + (lb > 0.5f ? (lb >= 1.0f ? 1.0f : la / (2.0f * (1.0f - lb)))
287 : (lb <= 0.0f ? 0.0f : 1.0f - (1.0f - la) / (2.0f * lb)))
288 * local_opacity2);
289 }
290 }
291
292 /* linearlight */
293 #ifdef _OPENMP
294 #pragma omp declare simd aligned(a, b, out:16) uniform(stride)
295 #endif
_blend_linearlight(const float * const restrict a,const float * const restrict b,float * const restrict out,const float * const restrict mask,const size_t stride)296 static void _blend_linearlight(const float *const restrict a, const float *const restrict b,
297 float *const restrict out, const float *const restrict mask, const size_t stride)
298 {
299 for(size_t j = 0; j < stride; j++)
300 {
301 const float local_opacity = mask[j];
302 const float local_opacity2 = local_opacity * local_opacity;
303 const float la = clamp_simd(a[j]);
304 const float lb = clamp_simd(b[j]);
305 out[j] = clamp_simd(la * (1.0f - local_opacity2) + (la + 2.0f * lb - 1.0f) * local_opacity2);
306 }
307 }
308
309 /* pinlight */
310 #ifdef _OPENMP
311 #pragma omp declare simd aligned(a, b, out:16) uniform(stride)
312 #endif
_blend_pinlight(const float * const restrict a,const float * const restrict b,float * const restrict out,const float * const restrict mask,const size_t stride)313 static void _blend_pinlight(const float *const restrict a, const float *const restrict b,
314 float *const restrict out, const float *const restrict mask, const size_t stride)
315 {
316 for(size_t j = 0; j < stride; j++)
317 {
318 const float local_opacity = mask[j];
319 const float local_opacity2 = local_opacity * local_opacity;
320 const float la = clamp_simd(a[j]);
321 const float lb = clamp_simd(b[j]);
322 out[j] = clamp_simd(
323 la * (1.0f - local_opacity2)
324 + (lb > 0.5f ? fmaxf(la, 2.0f * (lb - 0.5f)) : fminf(la, 2.0f * lb)) * local_opacity2);
325 }
326 }
327
328
_choose_blend_func(const unsigned int blend_mode)329 static _blend_row_func *_choose_blend_func(const unsigned int blend_mode)
330 {
331 _blend_row_func *blend = NULL;
332
333 /* select the blend operator */
334 switch(blend_mode & DEVELOP_BLEND_MODE_MASK)
335 {
336 case DEVELOP_BLEND_LIGHTEN:
337 blend = _blend_lighten;
338 break;
339 case DEVELOP_BLEND_DARKEN:
340 blend = _blend_darken;
341 break;
342 case DEVELOP_BLEND_MULTIPLY:
343 blend = _blend_multiply;
344 break;
345 case DEVELOP_BLEND_AVERAGE:
346 blend = _blend_average;
347 break;
348 case DEVELOP_BLEND_ADD:
349 blend = _blend_add;
350 break;
351 case DEVELOP_BLEND_SUBTRACT:
352 blend = _blend_subtract;
353 break;
354 case DEVELOP_BLEND_DIFFERENCE:
355 case DEVELOP_BLEND_DIFFERENCE2:
356 blend = _blend_difference;
357 break;
358 case DEVELOP_BLEND_SCREEN:
359 blend = _blend_screen;
360 break;
361 case DEVELOP_BLEND_OVERLAY:
362 blend = _blend_overlay;
363 break;
364 case DEVELOP_BLEND_SOFTLIGHT:
365 blend = _blend_softlight;
366 break;
367 case DEVELOP_BLEND_HARDLIGHT:
368 blend = _blend_hardlight;
369 break;
370 case DEVELOP_BLEND_VIVIDLIGHT:
371 blend = _blend_vividlight;
372 break;
373 case DEVELOP_BLEND_LINEARLIGHT:
374 blend = _blend_linearlight;
375 break;
376 case DEVELOP_BLEND_PINLIGHT:
377 blend = _blend_pinlight;
378 break;
379 case DEVELOP_BLEND_BOUNDED:
380 blend = _blend_normal_bounded;
381 break;
382
383 /* fallback to normal blend */
384 case DEVELOP_BLEND_NORMAL2:
385 default:
386 blend = _blend_normal_unbounded;
387 break;
388 }
389
390 return blend;
391 }
392
393
dt_develop_blendif_raw_blend(struct dt_dev_pixelpipe_iop_t * piece,const float * const restrict a,float * const restrict b,const struct dt_iop_roi_t * const roi_in,const struct dt_iop_roi_t * const roi_out,const float * const restrict mask,const dt_dev_pixelpipe_display_mask_t request_mask_display)394 void dt_develop_blendif_raw_blend(struct dt_dev_pixelpipe_iop_t *piece,
395 const float *const restrict a, float *const restrict b,
396 const struct dt_iop_roi_t *const roi_in,
397 const struct dt_iop_roi_t *const roi_out,
398 const float *const restrict mask,
399 const dt_dev_pixelpipe_display_mask_t request_mask_display)
400 {
401 const dt_develop_blend_params_t *const d = (const dt_develop_blend_params_t *const)piece->blendop_data;
402
403 if(piece->colors != 1) return;
404
405 const int xoffs = roi_out->x - roi_in->x;
406 const int yoffs = roi_out->y - roi_in->y;
407 const int iwidth = roi_in->width;
408 const int owidth = roi_out->width;
409 const int oheight = roi_out->height;
410
411 if(request_mask_display & DT_DEV_PIXELPIPE_DISPLAY_ANY)
412 {
413 dt_iop_image_fill(b, 0.0f, owidth, oheight, 1); //b[k] = 0.0f;
414 }
415 else
416 {
417 _blend_row_func *const blend = _choose_blend_func(d->blend_mode);
418
419 float *tmp_buffer = dt_alloc_align_float((size_t)owidth * oheight);
420 if (tmp_buffer != NULL)
421 {
422 dt_iop_image_copy(tmp_buffer, b, (size_t)owidth * oheight);
423 if((d->blend_mode & DEVELOP_BLEND_REVERSE) == DEVELOP_BLEND_REVERSE)
424 {
425 #ifdef _OPENMP
426 #pragma omp parallel for schedule(static) default(none) \
427 dt_omp_firstprivate(blend, a, b, tmp_buffer, mask, oheight, owidth, iwidth, xoffs, yoffs)
428 #endif
429 for(size_t y = 0; y < oheight; y++)
430 {
431 const size_t a_start = (y + yoffs) * iwidth + xoffs;
432 const size_t bm_start = y * owidth;
433 blend(tmp_buffer + bm_start, a + a_start, b + bm_start, mask + bm_start, owidth);
434 }
435 }
436 else
437 {
438 #ifdef _OPENMP
439 #pragma omp parallel for schedule(static) default(none) \
440 dt_omp_firstprivate(blend, a, b, tmp_buffer, mask, oheight, owidth, iwidth, xoffs, yoffs)
441 #endif
442 for(size_t y = 0; y < oheight; y++)
443 {
444 const size_t a_start = (y + yoffs) * iwidth + xoffs;
445 const size_t bm_start = y * owidth;
446 blend(a + a_start, tmp_buffer + bm_start, b + bm_start, mask + bm_start, owidth);
447 }
448 }
449 dt_free_align(tmp_buffer);
450 }
451 }
452 }
453
454 // tools/update_modelines.sh
455 // remove-trailing-space on;
456 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
457 // vim: shiftwidth=2 expandtab tabstop=2 cindent
458 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
459