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