1 /*
2     This file is part of darktable,
3     Copyright (C) 2015-2021 darktable developers.
4 
5     (based on code by johannes hanika)
6 
7     darktable is free software: you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation, either version 3 of the License, or
10     (at your option) any later version.
11 
12     darktable is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with darktable.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 #include "bauhaus/bauhaus.h"
24 #include "common/imageio_rawspeed.h" // for dt_rawspeed_crop_dcraw_filters
25 #include "common/opencl.h"
26 #include "common/imagebuf.h"
27 #include "develop/imageop.h"
28 #include "develop/imageop_gui.h"
29 #include "develop/tiling.h"
30 #include "common/image_cache.h"
31 #include "gui/accelerators.h"
32 #include "gui/gtk.h"
33 #include "gui/presets.h"
34 #include "iop/iop_api.h"
35 
36 #include <gtk/gtk.h>
37 #include <stdint.h>
38 #include <stdlib.h>
39 #if defined(__SSE__)
40 #include <xmmintrin.h>
41 #endif
42 
43 DT_MODULE_INTROSPECTION(1, dt_iop_rawprepare_params_t)
44 
45 typedef struct dt_iop_rawprepare_params_t
46 {
47   int32_t x; // $MIN: 0 $MAX: UINT16_MAX $DESCRIPTION: "crop left"
48   int32_t y; // $MIN: 0 $MAX: UINT16_MAX $DESCRIPTION: "crop top"
49   int32_t width; // $MIN: 0 $MAX: UINT16_MAX $DESCRIPTION: "crop right"
50   int32_t height; // $MIN: 0 $MAX: UINT16_MAX $DESCRIPTION: "crop bottom"
51   uint16_t raw_black_level_separate[4]; // $MIN: 0 $MAX: UINT16_MAX $DESCRIPTION: "black level"
52   uint16_t raw_white_point; // $MIN: 0 $MAX: UINT16_MAX $DESCRIPTION: "white point"
53 } dt_iop_rawprepare_params_t;
54 
55 typedef struct dt_iop_rawprepare_gui_data_t
56 {
57   GtkWidget *black_level_separate[4];
58   GtkWidget *white_point;
59   GtkWidget *x, *y, *width, *height;
60 } dt_iop_rawprepare_gui_data_t;
61 
62 typedef struct dt_iop_rawprepare_data_t
63 {
64   int32_t x, y, width, height; // crop, now unused, for future expansion
65   float sub[4];
66   float div[4];
67 
68   // cached for dt_iop_buffer_dsc_t::rawprepare
69   struct
70   {
71     uint16_t raw_black_level;
72     uint16_t raw_white_point;
73   } rawprepare;
74 } dt_iop_rawprepare_data_t;
75 
76 typedef struct dt_iop_rawprepare_global_data_t
77 {
78   int kernel_rawprepare_1f;
79   int kernel_rawprepare_1f_unnormalized;
80   int kernel_rawprepare_4f;
81 } dt_iop_rawprepare_global_data_t;
82 
83 
name()84 const char *name()
85 {
86   return C_("modulename", "raw black/white point");
87 }
88 
operation_tags()89 int operation_tags()
90 {
91   return IOP_TAG_DISTORT;
92 }
93 
flags()94 int flags()
95 {
96   return IOP_FLAGS_ALLOW_TILING | IOP_FLAGS_TILING_FULL_ROI | IOP_FLAGS_ONE_INSTANCE
97     | IOP_FLAGS_UNSAFE_COPY;
98 }
99 
default_group()100 int default_group()
101 {
102   return IOP_GROUP_BASIC | IOP_GROUP_TECHNICAL;
103 }
104 
default_colorspace(dt_iop_module_t * self,dt_dev_pixelpipe_t * pipe,dt_dev_pixelpipe_iop_t * piece)105 int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
106 {
107   return iop_cs_RAW;
108 }
109 
description(struct dt_iop_module_t * self)110 const char *description(struct dt_iop_module_t *self)
111 {
112   return dt_iop_set_description(self, _("sets technical specificities of the raw sensor.\n\ntouch with great care!"),
113                                       _("mandatory"),
114                                       _("linear, raw, scene-referred"),
115                                       _("linear, raw"),
116                                       _("linear, raw, scene-referred"));
117 }
118 
init_presets(dt_iop_module_so_t * self)119 void init_presets(dt_iop_module_so_t *self)
120 {
121   DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "BEGIN", NULL, NULL, NULL);
122 
123   dt_gui_presets_add_generic(_("passthrough"), self->op, self->version(),
124                              &(dt_iop_rawprepare_params_t){.x = 0,
125                                                            .y = 0,
126                                                            .width = 0,
127                                                            .height = 0,
128                                                            .raw_black_level_separate[0] = 0,
129                                                            .raw_black_level_separate[1] = 0,
130                                                            .raw_black_level_separate[2] = 0,
131                                                            .raw_black_level_separate[3] = 0,
132                                                            .raw_white_point = UINT16_MAX },
133                              sizeof(dt_iop_rawprepare_params_t), 1, DEVELOP_BLEND_CS_NONE);
134 
135   DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "COMMIT", NULL, NULL, NULL);
136 }
137 
compute_proper_crop(dt_dev_pixelpipe_iop_t * piece,const dt_iop_roi_t * const roi_in,int value)138 static int compute_proper_crop(dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *const roi_in, int value)
139 {
140   const float scale = roi_in->scale / piece->iscale;
141   return (int)roundf((float)value * scale);
142 }
143 
distort_transform(dt_iop_module_t * self,dt_dev_pixelpipe_iop_t * piece,float * const restrict points,size_t points_count)144 int distort_transform(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, float *const restrict points, size_t points_count)
145 {
146   dt_iop_rawprepare_data_t *d = (dt_iop_rawprepare_data_t *)piece->data;
147 
148   // nothing to be done if parameters are set to neutral values (no top/left crop)
149   if (d->x == 0 && d->y == 0) return 1;
150 
151   const float scale = piece->buf_in.scale / piece->iscale;
152 
153   const float x = (float)d->x * scale, y = (float)d->y * scale;
154 
155 #ifdef _OPENMP
156 #pragma omp parallel for simd default(none) \
157     dt_omp_firstprivate(points_count, points, y, x) \
158     schedule(static) \
159     aligned(points:64) if(points_count > 100)
160 #endif
161   for(size_t i = 0; i < points_count * 2; i += 2)
162   {
163     points[i] -= x;
164     points[i + 1] -= y;
165   }
166 
167   return 1;
168 }
169 
distort_backtransform(dt_iop_module_t * self,dt_dev_pixelpipe_iop_t * piece,float * const restrict points,size_t points_count)170 int distort_backtransform(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, float *const restrict points,
171                           size_t points_count)
172 {
173   dt_iop_rawprepare_data_t *d = (dt_iop_rawprepare_data_t *)piece->data;
174 
175   // nothing to be done if parameters are set to neutral values (no top/left crop)
176   if (d->x == 0 && d->y == 0) return 1;
177 
178   const float scale = piece->buf_in.scale / piece->iscale;
179 
180   const float x = (float)d->x * scale, y = (float)d->y * scale;
181 
182 #ifdef _OPENMP
183 #pragma omp parallel for simd default(none) \
184     dt_omp_firstprivate(points_count, points, y, x) \
185     schedule(static) \
186     aligned(points:64) if(points_count > 100)
187 #endif
188   for(size_t i = 0; i < points_count * 2; i += 2)
189   {
190     points[i] += x;
191     points[i + 1] += y;
192   }
193 
194   return 1;
195 }
196 
distort_mask(struct dt_iop_module_t * self,struct dt_dev_pixelpipe_iop_t * piece,const float * const in,float * const out,const dt_iop_roi_t * const roi_in,const dt_iop_roi_t * const roi_out)197 void distort_mask(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece, const float *const in,
198                   float *const out, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
199 {
200   dt_iop_copy_image_roi(out, in, 1, roi_in, roi_out, TRUE);
201 }
202 
203 // we're not scaling here (bayer input), so just crop borders
modify_roi_out(dt_iop_module_t * self,dt_dev_pixelpipe_iop_t * piece,dt_iop_roi_t * roi_out,const dt_iop_roi_t * const roi_in)204 void modify_roi_out(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t *roi_out,
205                     const dt_iop_roi_t *const roi_in)
206 {
207   *roi_out = *roi_in;
208   dt_iop_rawprepare_data_t *d = (dt_iop_rawprepare_data_t *)piece->data;
209 
210   roi_out->x = roi_out->y = 0;
211 
212   const int32_t x = d->x + d->width, y = d->y + d->height;
213 
214   const float scale = roi_in->scale / piece->iscale;
215   roi_out->width -= (int)roundf((float)x * scale);
216   roi_out->height -= (int)roundf((float)y * scale);
217 }
218 
modify_roi_in(dt_iop_module_t * self,dt_dev_pixelpipe_iop_t * piece,const dt_iop_roi_t * const roi_out,dt_iop_roi_t * roi_in)219 void modify_roi_in(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *const roi_out,
220                    dt_iop_roi_t *roi_in)
221 {
222   *roi_in = *roi_out;
223   dt_iop_rawprepare_data_t *d = (dt_iop_rawprepare_data_t *)piece->data;
224 
225   const int32_t x = d->x + d->width, y = d->y + d->height;
226 
227   const float scale = roi_in->scale / piece->iscale;
228   roi_in->width += (int)roundf((float)x * scale);
229   roi_in->height += (int)roundf((float)y * scale);
230 }
231 
output_format(dt_iop_module_t * self,dt_dev_pixelpipe_t * pipe,dt_dev_pixelpipe_iop_t * piece,dt_iop_buffer_dsc_t * dsc)232 void output_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece,
233                    dt_iop_buffer_dsc_t *dsc)
234 {
235   default_output_format(self, pipe, piece, dsc);
236 
237   dt_iop_rawprepare_data_t *d = (dt_iop_rawprepare_data_t *)piece->data;
238 
239   dsc->rawprepare.raw_black_level = d->rawprepare.raw_black_level;
240   dsc->rawprepare.raw_white_point = d->rawprepare.raw_white_point;
241 }
242 
adjust_xtrans_filters(dt_dev_pixelpipe_t * pipe,uint32_t crop_x,uint32_t crop_y)243 static void adjust_xtrans_filters(dt_dev_pixelpipe_t *pipe,
244                                   uint32_t crop_x, uint32_t crop_y)
245 {
246   for(int i = 0; i < 6; ++i)
247   {
248     for(int j = 0; j < 6; ++j)
249     {
250       pipe->dsc.xtrans[j][i] = pipe->image.buf_dsc.xtrans[(j + crop_y) % 6][(i + crop_x) % 6];
251     }
252   }
253 }
254 
BL(const dt_iop_roi_t * const roi_out,const dt_iop_rawprepare_data_t * const d,const int row,const int col)255 static int BL(const dt_iop_roi_t *const roi_out, const dt_iop_rawprepare_data_t *const d, const int row,
256               const int col)
257 {
258   return ((((row + roi_out->y + d->y) & 1) << 1) + ((col + roi_out->x + d->x) & 1));
259 }
260 
process(struct dt_iop_module_t * self,dt_dev_pixelpipe_iop_t * piece,const void * const ivoid,void * const ovoid,const dt_iop_roi_t * const roi_in,const dt_iop_roi_t * const roi_out)261 void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
262              void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
263 {
264   const dt_iop_rawprepare_data_t *const d = (dt_iop_rawprepare_data_t *)piece->data;
265 
266   // fprintf(stderr, "roi in %d %d %d %d\n", roi_in->x, roi_in->y, roi_in->width, roi_in->height);
267   // fprintf(stderr, "roi out %d %d %d %d\n", roi_out->x, roi_out->y, roi_out->width, roi_out->height);
268 
269   const int csx = compute_proper_crop(piece, roi_in, d->x), csy = compute_proper_crop(piece, roi_in, d->y);
270 
271   if(piece->pipe->dsc.filters && piece->dsc_in.channels == 1
272      && piece->dsc_in.datatype == TYPE_UINT16)
273   { // raw mosaic
274 
275     const uint16_t *const in = (const uint16_t *const)ivoid;
276     float *const out = (float *const)ovoid;
277 
278 #ifdef _OPENMP
279 #pragma omp parallel for SIMD() default(none) \
280     dt_omp_firstprivate(csx, csy, d, in, out, roi_in, roi_out) \
281     schedule(static) \
282     collapse(2)
283 #endif
284     for(int j = 0; j < roi_out->height; j++)
285     {
286       for(int i = 0; i < roi_out->width; i++)
287       {
288         const size_t pin = (size_t)(roi_in->width * (j + csy) + csx) + i;
289         const size_t pout = (size_t)j * roi_out->width + i;
290 
291         const int id = BL(roi_out, d, j, i);
292         out[pout] = (in[pin] - d->sub[id]) / d->div[id];
293       }
294     }
295 
296     piece->pipe->dsc.filters = dt_rawspeed_crop_dcraw_filters(self->dev->image_storage.buf_dsc.filters, csx, csy);
297     adjust_xtrans_filters(piece->pipe, csx, csy);
298   }
299   else if(piece->pipe->dsc.filters && piece->dsc_in.channels == 1
300           && piece->dsc_in.datatype == TYPE_FLOAT)
301   { // raw mosaic, fp, unnormalized
302 
303     const float *const in = (const float *const)ivoid;
304     float *const out = (float *const)ovoid;
305 
306 #ifdef _OPENMP
307 #pragma omp parallel for SIMD() default(none) \
308     dt_omp_firstprivate(csx, csy, d, in, out, roi_in, roi_out) \
309     schedule(static) \
310     collapse(2)
311 #endif
312     for(int j = 0; j < roi_out->height; j++)
313     {
314       for(int i = 0; i < roi_out->width; i++)
315       {
316         const size_t pin = (size_t)(roi_in->width * (j + csy) + csx) + i;
317         const size_t pout = (size_t)j * roi_out->width + i;
318 
319         const int id = BL(roi_out, d, j, i);
320         out[pout] = (in[pin] - d->sub[id]) / d->div[id];
321       }
322     }
323 
324     piece->pipe->dsc.filters = dt_rawspeed_crop_dcraw_filters(self->dev->image_storage.buf_dsc.filters, csx, csy);
325     adjust_xtrans_filters(piece->pipe, csx, csy);
326   }
327   else
328   { // pre-downsampled buffer that needs black/white scaling
329 
330     const float *const in = (const float *const)ivoid;
331     float *const out = (float *const)ovoid;
332 
333     const float sub = d->sub[0], div = d->div[0];
334 
335     const int ch = piece->colors;
336 
337 #ifdef _OPENMP
338 #pragma omp parallel for SIMD() default(none) \
339     dt_omp_firstprivate(ch, csx, csy, div, in, out, roi_in, roi_out, sub) \
340     schedule(static) collapse(3)
341 #endif
342     for(int j = 0; j < roi_out->height; j++)
343     {
344       for(int i = 0; i < roi_out->width; i++)
345       {
346         for(int c = 0; c < ch; c++)
347         {
348           const size_t pin = (size_t)ch * (roi_in->width * (j + csy) + csx + i) + c;
349           const size_t pout = (size_t)ch * (j * roi_out->width + i) + c;
350 
351           out[pout] = (in[pin] - sub) / div;
352         }
353       }
354     }
355   }
356 
357   dt_dev_write_rawdetail_mask(piece, (float *const)ovoid, roi_in, DT_DEV_DETAIL_MASK_RAWPREPARE);
358 
359   for(int k = 0; k < 4; k++) piece->pipe->dsc.processed_maximum[k] = 1.0f;
360 }
361 
362 #if defined(__SSE2__)
process_sse2(dt_iop_module_t * self,dt_dev_pixelpipe_iop_t * piece,const void * const ivoid,void * const ovoid,const dt_iop_roi_t * const roi_in,const dt_iop_roi_t * const roi_out)363 void process_sse2(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
364                   void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
365 {
366   const dt_iop_rawprepare_data_t *const d = (dt_iop_rawprepare_data_t *)piece->data;
367 
368   // fprintf(stderr, "roi in %d %d %d %d\n", roi_in->x, roi_in->y, roi_in->width, roi_in->height);
369   // fprintf(stderr, "roi out %d %d %d %d\n", roi_out->x, roi_out->y, roi_out->width, roi_out->height);
370 
371   const int csx = compute_proper_crop(piece, roi_in, d->x), csy = compute_proper_crop(piece, roi_in, d->y);
372 
373   if(piece->pipe->dsc.filters && piece->dsc_in.channels == 1
374      && piece->dsc_in.datatype == TYPE_UINT16)
375   { // raw mosaic
376 #ifdef _OPENMP
377 #pragma omp parallel for default(none) \
378     dt_omp_firstprivate(csx, csy, d, ivoid, ovoid, roi_in, roi_out) \
379     schedule(static)
380 #endif
381     for(int j = 0; j < roi_out->height; j++)
382     {
383       const uint16_t *in = ((uint16_t *)ivoid) + ((size_t)roi_in->width * (j + csy) + csx);
384       float *out = ((float *)ovoid) + (size_t)roi_out->width * j;
385 
386       int i = 0;
387 
388       // FIXME: figure alignment!  !!! replace with for !!!
389       while((!dt_is_aligned(in, 16) || !dt_is_aligned(out, 16)) && (i < roi_out->width))
390       {
391         const int id = BL(roi_out, d, j, i);
392         *out = (((float)(*in)) - d->sub[id]) / d->div[id];
393         i++;
394         in++;
395         out++;
396       }
397 
398       const __m128 sub = _mm_set_ps(d->sub[BL(roi_out, d, j, i + 3)], d->sub[BL(roi_out, d, j, i + 2)],
399                                     d->sub[BL(roi_out, d, j, i + 1)], d->sub[BL(roi_out, d, j, i)]);
400 
401       const __m128 div = _mm_set_ps(d->div[BL(roi_out, d, j, i + 3)], d->div[BL(roi_out, d, j, i + 2)],
402                                     d->div[BL(roi_out, d, j, i + 1)], d->div[BL(roi_out, d, j, i)]);
403 
404       // process aligned pixels with SSE
405       for(; i < roi_out->width - (8 - 1); i += 8, in += 8)
406       {
407         const __m128i input = _mm_load_si128((__m128i *)in);
408 
409         __m128i ilo = _mm_unpacklo_epi16(input, _mm_set1_epi16(0));
410         __m128i ihi = _mm_unpackhi_epi16(input, _mm_set1_epi16(0));
411 
412         __m128 flo = _mm_cvtepi32_ps(ilo);
413         __m128 fhi = _mm_cvtepi32_ps(ihi);
414 
415         flo = _mm_div_ps(_mm_sub_ps(flo, sub), div);
416         fhi = _mm_div_ps(_mm_sub_ps(fhi, sub), div);
417 
418         _mm_stream_ps(out, flo);
419         out += 4;
420         _mm_stream_ps(out, fhi);
421         out += 4;
422       }
423 
424       // process the rest
425       for(; i < roi_out->width; i++, in++, out++)
426       {
427         const int id = BL(roi_out, d, j, i);
428         *out = (((float)(*in)) - d->sub[id]) / d->div[id];
429       }
430     }
431 
432     piece->pipe->dsc.filters = dt_rawspeed_crop_dcraw_filters(self->dev->image_storage.buf_dsc.filters, csx, csy);
433     adjust_xtrans_filters(piece->pipe, csx, csy);
434   }
435   else if(piece->pipe->dsc.filters
436           && piece->dsc_in.channels == 1 && piece->dsc_in.datatype == TYPE_FLOAT)
437   { // raw mosaic, fp, unnormalized
438 #ifdef _OPENMP
439 #pragma omp parallel for default(none) \
440     dt_omp_firstprivate(d, csx, csy, ivoid, ovoid, roi_in, roi_out) \
441     schedule(static)
442 #endif
443     for(int j = 0; j < roi_out->height; j++)
444     {
445       const float *in = ((float *)ivoid) + ((size_t)roi_in->width * (j + csy) + csx);
446       float *out = ((float *)ovoid) + (size_t)roi_out->width * j;
447 
448       int i = 0;
449 
450       // FIXME: figure alignment!  !!! replace with for !!!
451       while((!dt_is_aligned(in, 16) || !dt_is_aligned(out, 16)) && (i < roi_out->width))
452       {
453         const int id = BL(roi_out, d, j, i);
454         *out = (*in - d->sub[id]) / d->div[id];
455         i++;
456         in++;
457         out++;
458       }
459 
460       const __m128 sub = _mm_set_ps(d->sub[BL(roi_out, d, j, i + 3)], d->sub[BL(roi_out, d, j, i + 2)],
461                                     d->sub[BL(roi_out, d, j, i + 1)], d->sub[BL(roi_out, d, j, i)]);
462 
463       const __m128 div = _mm_set_ps(d->div[BL(roi_out, d, j, i + 3)], d->div[BL(roi_out, d, j, i + 2)],
464                                     d->div[BL(roi_out, d, j, i + 1)], d->div[BL(roi_out, d, j, i)]);
465 
466       // process aligned pixels with SSE
467       for(; i < roi_out->width - (4 - 1); i += 4, in += 4, out += 4)
468       {
469         const __m128 input = _mm_load_ps(in);
470 
471         const __m128 scaled = _mm_div_ps(_mm_sub_ps(input, sub), div);
472 
473         _mm_stream_ps(out, scaled);
474       }
475 
476       // process the rest
477       for(; i < roi_out->width; i++, in++, out++)
478       {
479         const int id = BL(roi_out, d, j, i);
480         *out = (*in - d->sub[id]) / d->div[id];
481       }
482     }
483 
484     piece->pipe->dsc.filters = dt_rawspeed_crop_dcraw_filters(self->dev->image_storage.buf_dsc.filters, csx, csy);
485     adjust_xtrans_filters(piece->pipe, csx, csy);
486   }
487   else
488   { // pre-downsampled buffer that needs black/white scaling
489 
490     const __m128 sub = _mm_load_ps(d->sub), div = _mm_load_ps(d->div);
491 
492 #ifdef _OPENMP
493 #pragma omp parallel for default(none) \
494     dt_omp_firstprivate(csx, csy, div, ivoid, ovoid, roi_in, roi_out, sub) \
495     schedule(static)
496 #endif
497     for(int j = 0; j < roi_out->height; j++)
498     {
499       const float *in = ((float *)ivoid) + (size_t)4 * (roi_in->width * (j + csy) + csx);
500       float *out = ((float *)ovoid) + (size_t)4 * roi_out->width * j;
501 
502       // process aligned pixels with SSE
503       for(int i = 0; i < roi_out->width; i++, in += 4, out += 4)
504       {
505         const __m128 input = _mm_load_ps(in);
506 
507         const __m128 scaled = _mm_div_ps(_mm_sub_ps(input, sub), div);
508 
509         _mm_stream_ps(out, scaled);
510       }
511     }
512   }
513 
514   for(int k = 0; k < 4; k++) piece->pipe->dsc.processed_maximum[k] = 1.0f;
515 
516   _mm_sfence();
517   dt_dev_write_rawdetail_mask(piece, (float *const)ovoid, roi_in, DT_DEV_DETAIL_MASK_RAWPREPARE);
518 }
519 #endif
520 
521 #ifdef HAVE_OPENCL
process_cl(dt_iop_module_t * self,dt_dev_pixelpipe_iop_t * piece,cl_mem dev_in,cl_mem dev_out,const dt_iop_roi_t * const roi_in,const dt_iop_roi_t * const roi_out)522 int process_cl(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out,
523                const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
524 {
525   dt_iop_rawprepare_data_t *d = (dt_iop_rawprepare_data_t *)piece->data;
526   dt_iop_rawprepare_global_data_t *gd = (dt_iop_rawprepare_global_data_t *)self->global_data;
527 
528   const int devid = piece->pipe->devid;
529   cl_mem dev_sub = NULL;
530   cl_mem dev_div = NULL;
531   cl_int err = -999;
532 
533   int kernel = -1;
534 
535   if(piece->pipe->dsc.filters && piece->dsc_in.channels == 1 && piece->dsc_in.datatype == TYPE_UINT16)
536   {
537     kernel = gd->kernel_rawprepare_1f;
538   }
539   else if(piece->pipe->dsc.filters && piece->dsc_in.channels == 1 && piece->dsc_in.datatype == TYPE_FLOAT)
540   {
541     kernel = gd->kernel_rawprepare_1f_unnormalized;
542   }
543   else
544   {
545     kernel = gd->kernel_rawprepare_4f;
546   }
547 
548   const int csx = compute_proper_crop(piece, roi_in, d->x), csy = compute_proper_crop(piece, roi_in, d->y);
549 
550   dev_sub = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 4, d->sub);
551   if(dev_sub == NULL) goto error;
552 
553   dev_div = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 4, d->div);
554   if(dev_div == NULL) goto error;
555 
556   const int width = roi_out->width;
557   const int height = roi_out->height;
558 
559   size_t sizes[] = { ROUNDUPWD(roi_in->width), ROUNDUPHT(roi_in->height), 1 };
560   dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_in);
561   dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&dev_out);
562   dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&(width));
563   dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), (void *)&(height));
564   dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), (void *)&csx);
565   dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), (void *)&csy);
566   dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(cl_mem), (void *)&dev_sub);
567   dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(cl_mem), (void *)&dev_div);
568   dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(uint32_t), (void *)&roi_out->x);
569   dt_opencl_set_kernel_arg(devid, kernel, 9, sizeof(uint32_t), (void *)&roi_out->y);
570   err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
571   if(err != CL_SUCCESS) goto error;
572 
573   dt_opencl_release_mem_object(dev_sub);
574   dt_opencl_release_mem_object(dev_div);
575 
576   if(piece->pipe->dsc.filters)
577   {
578     piece->pipe->dsc.filters = dt_rawspeed_crop_dcraw_filters(self->dev->image_storage.buf_dsc.filters, csx, csy);
579     adjust_xtrans_filters(piece->pipe, csx, csy);
580   }
581 
582   for(int k = 0; k < 4; k++) piece->pipe->dsc.processed_maximum[k] = 1.0f;
583 
584   err = dt_dev_write_rawdetail_mask_cl(piece, dev_out, roi_in, DT_DEV_DETAIL_MASK_RAWPREPARE);
585   if(err != CL_SUCCESS) goto error;
586 
587   return TRUE;
588 
589 error:
590   dt_opencl_release_mem_object(dev_sub);
591   dt_opencl_release_mem_object(dev_div);
592   dt_print(DT_DEBUG_OPENCL, "[opencl_rawprepare] couldn't enqueue kernel! %d\n", err);
593   return FALSE;
594 }
595 #endif
596 
image_is_normalized(const dt_image_t * const image)597 static int image_is_normalized(const dt_image_t *const image)
598 {
599   // if raw with floating-point data, if not special magic whitelevel, then it needs normalization
600   if((image->flags & DT_IMAGE_HDR) == DT_IMAGE_HDR)
601   {
602     union {
603         float f;
604         uint32_t u;
605     } normalized;
606     normalized.f = 1.0f;
607 
608     // dng spec is just broken here.
609     return image->raw_white_point == normalized.u;
610   }
611 
612   // else, assume normalized
613   return image->buf_dsc.channels == 1 && image->buf_dsc.datatype == TYPE_FLOAT;
614 }
615 
image_set_rawcrops(const uint32_t imgid,int dx,int dy)616 static gboolean image_set_rawcrops(const uint32_t imgid, int dx, int dy)
617 {
618   dt_image_t *img = NULL;
619   img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
620   const gboolean test = (img->p_width == img->width - dx)
621                      && (img->p_height == img->height - dy);
622 
623   dt_image_cache_read_release(darktable.image_cache, img);
624   if(test) return FALSE;
625 
626   img = dt_image_cache_get(darktable.image_cache, imgid, 'w');
627   img->p_width = img->width - dx;
628   img->p_height = img->height - dy;
629   dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_RELAXED);
630   return TRUE;
631 }
632 
commit_params(dt_iop_module_t * self,dt_iop_params_t * params,dt_dev_pixelpipe_t * pipe,dt_dev_pixelpipe_iop_t * piece)633 void commit_params(dt_iop_module_t *self, dt_iop_params_t *params, dt_dev_pixelpipe_t *pipe,
634                    dt_dev_pixelpipe_iop_t *piece)
635 {
636   const dt_iop_rawprepare_params_t *const p = (dt_iop_rawprepare_params_t *)params;
637   dt_iop_rawprepare_data_t *d = (dt_iop_rawprepare_data_t *)piece->data;
638 
639   d->x = p->x;
640   d->y = p->y;
641   d->width = p->width;
642   d->height = p->height;
643 
644   if(piece->pipe->dsc.filters)
645   {
646     const float white = (float)p->raw_white_point;
647 
648     for(int i = 0; i < 4; i++)
649     {
650       d->sub[i] = (float)p->raw_black_level_separate[i];
651       d->div[i] = (white - d->sub[i]);
652     }
653   }
654   else
655   {
656     const float normalizer
657         = ((piece->pipe->image.flags & DT_IMAGE_HDR) == DT_IMAGE_HDR) ? 1.0f : (float)UINT16_MAX;
658     const float white = (float)p->raw_white_point / normalizer;
659     float black = 0;
660     for(int i = 0; i < 4; i++)
661     {
662       black += p->raw_black_level_separate[i] / normalizer;
663     }
664     black /= 4.0f;
665 
666     for(int i = 0; i < 4; i++)
667     {
668       d->sub[i] = black;
669       d->div[i] = (white - black);
670     }
671   }
672 
673   float black = 0.0f;
674   for(uint8_t i = 0; i < 4; i++)
675   {
676     black += (float)p->raw_black_level_separate[i];
677   }
678   d->rawprepare.raw_black_level = (uint16_t)(black / 4.0f);
679   d->rawprepare.raw_white_point = p->raw_white_point;
680 
681   if(image_set_rawcrops(pipe->image.id, d->x + d->width, d->y + d->height))
682     DT_DEBUG_CONTROL_SIGNAL_RAISE(darktable.signals, DT_SIGNAL_METADATA_UPDATE);
683 
684   if(!(dt_image_is_rawprepare_supported(&piece->pipe->image))
685      || image_is_normalized(&piece->pipe->image))
686     piece->enabled = 0;
687 
688   if(piece->pipe->want_detail_mask == (DT_DEV_DETAIL_MASK_REQUIRED | DT_DEV_DETAIL_MASK_RAWPREPARE))
689     piece->process_tiling_ready = 0;
690 }
691 
init_pipe(dt_iop_module_t * self,dt_dev_pixelpipe_t * pipe,dt_dev_pixelpipe_iop_t * piece)692 void init_pipe(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
693 {
694   piece->data = calloc(1, sizeof(dt_iop_rawprepare_data_t));
695 }
696 
cleanup_pipe(dt_iop_module_t * self,dt_dev_pixelpipe_t * pipe,dt_dev_pixelpipe_iop_t * piece)697 void cleanup_pipe(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
698 {
699   free(piece->data);
700   piece->data = NULL;
701 }
702 
reload_defaults(dt_iop_module_t * self)703 void reload_defaults(dt_iop_module_t *self)
704 {
705   dt_iop_rawprepare_params_t *d = self->default_params;
706   const dt_image_t *const image = &(self->dev->image_storage);
707 
708   *d = (dt_iop_rawprepare_params_t){.x = image->crop_x,
709                                     .y = image->crop_y,
710                                     .width = image->crop_width,
711                                     .height = image->crop_height,
712                                     .raw_black_level_separate[0] = image->raw_black_level_separate[0],
713                                     .raw_black_level_separate[1] = image->raw_black_level_separate[1],
714                                     .raw_black_level_separate[2] = image->raw_black_level_separate[2],
715                                     .raw_black_level_separate[3] = image->raw_black_level_separate[3],
716                                     .raw_white_point = image->raw_white_point };
717 
718   self->hide_enable_button = 1;
719   self->default_enabled = dt_image_is_rawprepare_supported(image) && !image_is_normalized(image);
720 
721   if(self->widget)
722     gtk_stack_set_visible_child_name(GTK_STACK(self->widget), self->default_enabled ? "raw" : "non_raw");
723 }
724 
init_global(dt_iop_module_so_t * self)725 void init_global(dt_iop_module_so_t *self)
726 {
727   const int program = 2; // basic.cl, from programs.conf
728   self->data = malloc(sizeof(dt_iop_rawprepare_global_data_t));
729 
730   dt_iop_rawprepare_global_data_t *gd = self->data;
731   gd->kernel_rawprepare_1f = dt_opencl_create_kernel(program, "rawprepare_1f");
732   gd->kernel_rawprepare_1f_unnormalized = dt_opencl_create_kernel(program, "rawprepare_1f_unnormalized");
733   gd->kernel_rawprepare_4f = dt_opencl_create_kernel(program, "rawprepare_4f");
734 }
735 
cleanup_global(dt_iop_module_so_t * self)736 void cleanup_global(dt_iop_module_so_t *self)
737 {
738   dt_iop_rawprepare_global_data_t *gd = (dt_iop_rawprepare_global_data_t *)self->data;
739   dt_opencl_free_kernel(gd->kernel_rawprepare_4f);
740   dt_opencl_free_kernel(gd->kernel_rawprepare_1f_unnormalized);
741   dt_opencl_free_kernel(gd->kernel_rawprepare_1f);
742   free(self->data);
743   self->data = NULL;
744 }
745 
gui_update(dt_iop_module_t * self)746 void gui_update(dt_iop_module_t *self)
747 {
748   dt_iop_rawprepare_gui_data_t *g = (dt_iop_rawprepare_gui_data_t *)self->gui_data;
749   dt_iop_rawprepare_params_t *p = (dt_iop_rawprepare_params_t *)self->params;
750 
751   for(int i = 0; i < 4; i++)
752   {
753     dt_bauhaus_slider_set_soft(g->black_level_separate[i], p->raw_black_level_separate[i]);
754   }
755 
756   dt_bauhaus_slider_set_soft(g->white_point, p->raw_white_point);
757 
758   if(dt_conf_get_bool("plugins/darkroom/rawprepare/allow_editing_crop"))
759   {
760     dt_bauhaus_slider_set_soft(g->x, p->x);
761     dt_bauhaus_slider_set_soft(g->y, p->y);
762     dt_bauhaus_slider_set_soft(g->width, p->width);
763     dt_bauhaus_slider_set_soft(g->height, p->height);
764   }
765 }
766 
767 const gchar *black_label[]
768   =  { N_("black level 0"),
769        N_("black level 1"),
770        N_("black level 2"),
771        N_("black level 3") };
772 
gui_init(dt_iop_module_t * self)773 void gui_init(dt_iop_module_t *self)
774 {
775   dt_iop_rawprepare_gui_data_t *g = IOP_GUI_ALLOC(rawprepare);
776 
777   GtkWidget *box_raw = self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_BAUHAUS_SPACE);
778 
779   for(int i = 0; i < 4; i++)
780   {
781     gchar *par = g_strdup_printf("raw_black_level_separate[%i]", i);
782 
783     g->black_level_separate[i] = dt_bauhaus_slider_from_params(self, par);
784     dt_bauhaus_widget_set_label(g->black_level_separate[i], NULL, black_label[i]);
785     gtk_widget_set_tooltip_text(g->black_level_separate[i], _(black_label[i]));
786     dt_bauhaus_slider_set_soft_max(g->black_level_separate[i], 16384);
787 
788     g_free(par);
789   }
790 
791   g->white_point = dt_bauhaus_slider_from_params(self, "raw_white_point");
792   gtk_widget_set_tooltip_text(g->white_point, _("white point"));
793   dt_bauhaus_slider_set_soft_max(g->white_point, 16384);
794 
795   if(dt_conf_get_bool("plugins/darkroom/rawprepare/allow_editing_crop"))
796   {
797     g->x = dt_bauhaus_slider_from_params(self, "x");
798     gtk_widget_set_tooltip_text(g->x, _("crop from left border"));
799     dt_bauhaus_slider_set_soft_max(g->x, 256);
800 
801     g->y = dt_bauhaus_slider_from_params(self, "y");
802     gtk_widget_set_tooltip_text(g->y, _("crop from top"));
803     dt_bauhaus_slider_set_soft_max(g->y, 256);
804 
805     g->width = dt_bauhaus_slider_from_params(self, "width");
806     gtk_widget_set_tooltip_text(g->width, _("crop from right border"));
807     dt_bauhaus_slider_set_soft_max(g->width, 256);
808 
809     g->height = dt_bauhaus_slider_from_params(self, "height");
810     gtk_widget_set_tooltip_text(g->height, _("crop from bottom"));
811     dt_bauhaus_slider_set_soft_max(g->height, 256);
812   }
813 
814   // start building top level widget
815   self->widget = gtk_stack_new();
816   gtk_stack_set_homogeneous(GTK_STACK(self->widget), FALSE);
817 
818   GtkWidget *label_non_raw = dt_ui_label_new(_("raw black/white point correction\nonly works for the sensors that need it."));
819 
820   gtk_stack_add_named(GTK_STACK(self->widget), label_non_raw, "non_raw");
821   gtk_stack_add_named(GTK_STACK(self->widget), box_raw, "raw");
822 }
823 
824 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
825 // vim: shiftwidth=2 expandtab tabstop=2 cindent
826 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
827