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