Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
rawprepare.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2014-2017, 2020 Roman Lebedev.
4 Copyright (C) 2016 Dan Torop.
5 Copyright (C) 2016, 2018 johannes hanika.
6 Copyright (C) 2016, 2018-2019 Tobias Ellinghaus.
7 Copyright (C) 2016 Ulrich Pegelow.
8 Copyright (C) 2018 Edgardo Hoszowski.
9 Copyright (C) 2018 Maurizio Paglia.
10 Copyright (C) 2018, 2020-2022 Pascal Obry.
11 Copyright (C) 2018 rawfiner.
12 Copyright (C) 2019 Andreas Schneider.
13 Copyright (C) 2019-2022 Hanno Schwalm.
14 Copyright (C) 2020 Aldric Renaudin.
15 Copyright (C) 2020-2021, 2023, 2025-2026 Aurélien PIERRE.
16 Copyright (C) 2020, 2022 Diederik Ter Rahe.
17 Copyright (C) 2020 Hubert Kowalski.
18 Copyright (C) 2020, 2022 Ralf Brown.
19 Copyright (C) 2021 luzpaz.
20 Copyright (C) 2022 Martin Bařinka.
21 Copyright (C) 2022 paolodepetrillo.
22 Copyright (C) 2022 Philipp Lutz.
23
24 darktable is free software: you can redistribute it and/or modify
25 it under the terms of the GNU General Public License as published by
26 the Free Software Foundation, either version 3 of the License, or
27 (at your option) any later version.
28
29 darktable is distributed in the hope that it will be useful,
30 but WITHOUT ANY WARRANTY; without even the implied warranty of
31 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 GNU General Public License for more details.
33
34 You should have received a copy of the GNU General Public License
35 along with darktable. If not, see <http://www.gnu.org/licenses/>.
36*/
37#ifdef HAVE_CONFIG_H
38#include "config.h"
39#endif
40#include "widgets/bauhaus.h"
41#include "system/macros.h"
42#include "system/openmp.h"
44#include "system/mem_alloc.h"
45#include "common/logging.h"
47#include "database/database.h"
48#include "imageio/imageio_rawspeed.h" // for dt_rawspeed_crop_dcraw_filters
49#include "common/opencl.h"
50#include "common/imagebuf.h"
51#include "common/image.h"
53#include "develop/imageop.h"
54#include "develop/imageop_gui.h"
55#include "caches/image_cache.h"
56
57#include "gui/presets.h"
58#include "iop/iop_api.h"
59#include "common/dng_opcode.h"
60
61#include <gtk/gtk.h>
62#include <stdint.h>
63#include <stdlib.h>
64#include "widgets/label.h"
65#include "control/signal.h"
66
68
70{
71 FLAT_FIELD_OFF = 0, // $DESCRIPTION: "disabled"
72 FLAT_FIELD_EMBEDDED = 1 // $DESCRIPTION: "embedded GainMap"
74
76{
77 int32_t x; // $MIN: 0 $MAX: UINT16_MAX $DESCRIPTION: "crop left"
78 int32_t y; // $MIN: 0 $MAX: UINT16_MAX $DESCRIPTION: "crop top"
79 int32_t width; // $MIN: 0 $MAX: UINT16_MAX $DESCRIPTION: "crop right"
80 int32_t height; // $MIN: 0 $MAX: UINT16_MAX $DESCRIPTION: "crop bottom"
81 uint16_t raw_black_level_separate[4]; // $MIN: 0 $MAX: UINT16_MAX $DESCRIPTION: "black level"
82 uint16_t raw_white_point; // $MIN: 0 $MAX: UINT16_MAX $DESCRIPTION: "white point"
83 dt_iop_rawprepare_flat_field_t flat_field; // $DEFAULT: FLAT_FIELD_OFF $DESCRIPTION: "flat field correction"
85
93
95{
96 int32_t x, y, width, height; // crop, now unused, for future expansion
97 float sub[4];
98 float div[4];
99
100 // cached for dt_iop_buffer_dsc_t::rawprepare
101 struct
102 {
106
107 // image contains GainMaps that should be applied
109 // GainMap for each filter of RGGB Bayer pattern
112
121
122
123const char *name()
124{
125 return C_("modulename", "Raw settings");
126}
127
129{
130 return IOP_TAG_DISTORT;
131}
132
138
140{
141 return IOP_GROUP_TECHNICAL;
142}
143
145{
146 if(piece && piece->dsc_in.cst != IOP_CS_RAW)
147 return IOP_CS_RGB;
148 return IOP_CS_RAW;
149}
150
151int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
152 void *new_params, const int new_version)
153{
154 typedef struct dt_iop_rawprepare_params_t dt_iop_rawprepare_params_v2_t;
155 typedef struct dt_iop_rawprepare_params_v1_t
156 {
157 int32_t x;
158 int32_t y;
159 int32_t width;
160 int32_t height;
161 uint16_t raw_black_level_separate[4];
162 uint16_t raw_white_point;
163 } dt_iop_rawprepare_params_v1_t;
164
165 if(old_version == 1 && new_version == 2)
166 {
167 dt_iop_rawprepare_params_v1_t *o = (dt_iop_rawprepare_params_v1_t *)old_params;
168 dt_iop_rawprepare_params_v2_t *n = (dt_iop_rawprepare_params_v2_t *)new_params;
169 memcpy(n, o, sizeof *o);
170 n->flat_field = FLAT_FIELD_OFF;
171 return 0;
172 }
173
174 return 1;
175}
176
177const char **description(struct dt_iop_module_t *self)
178{
179 return dt_iop_set_description(self, _("sets technical specificities of the raw sensor.\n"
180 "touch with great care!"),
181 _("mandatory"),
182 _("linear, raw, scene-referred"),
183 _("linear, raw"),
184 _("linear, raw, scene-referred"));
185}
186
188{
190
191 dt_gui_presets_add_generic(_("passthrough"), self->op, self->version(),
193 .y = 0,
194 .width = 0,
195 .height = 0,
196 .raw_black_level_separate[0] = 0,
197 .raw_black_level_separate[1] = 0,
198 .raw_black_level_separate[2] = 0,
199 .raw_black_level_separate[3] = 0,
200 .raw_white_point = UINT16_MAX },
201 sizeof(dt_iop_rawprepare_params_t), 1);
202
204}
205
206static inline __attribute__((always_inline)) int compute_proper_crop(const dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *const roi_in, int value)
207{
208 const double scale = roi_in->scale;
209 return (int)roundf((double)value * scale);
210}
211
213 const dt_dev_pixelpipe_iop_t *piece,
214 const dt_iop_roi_t *const roi_in,
216{
217 if(IS_NULL_PTR(pipe) || IS_NULL_PTR(piece) || IS_NULL_PTR(roi_in) || IS_NULL_PTR(dsc)) return;
218
219 dsc->filters = pipe->dev->image_storage.dsc.filters;
220 memcpy(dsc->xtrans, pipe->dev->image_storage.dsc.xtrans, sizeof(dsc->xtrans));
221
222 if(!pipe->dev->image_storage.dsc.filters) return;
223
224 /* Rawprepare is the stage that converts the immutable sensor-aligned RAW descriptor
225 * attached to the input image into the runtime descriptor seen by downstream RAW
226 * modules. Rebuild that contract from the pipe image each time instead of chaining
227 * shifts from `piece->dsc_in`, otherwise repeated ROI planning can compound the
228 * Bayer/X-Trans phase offset.
229 *
230 * Only the fixed sensor border trim (d->x/d->y) is folded in here: that offset is
231 * constant for a given image, regardless of how the user pans/zooms/crops downstream.
232 * The dynamic, ROI-dependent part of the phase shift is applied downstream, fresh on
233 * every process()/process_cl() call, from the module's own current roi_in (see the
234 * matching comment at the top of demosaic.c's process()) — not here, and not from any
235 * ROI callback, since piece->dsc_in is not guaranteed to be populated yet at that point. */
236
238 const uint32_t crop_x = compute_proper_crop(piece, roi_in, d->x);
239 const uint32_t crop_y = compute_proper_crop(piece, roi_in, d->y);
240
241 dsc->filters = dt_rawspeed_crop_dcraw_filters(pipe->dev->image_storage.dsc.filters, crop_x, crop_y);
242
244 "[rawprepare] pipe %p (%s) thread %lu piece %p dsc %p roi_in=(%d,%d) scale=%f d->x,y=(%d,%d) -> crop=(%u,%u) filters=0x%x -> 0x%x\n",
245 (void *)pipe, pipe->type == DT_DEV_PIXELPIPE_FULL ? "full" :
246 pipe->type == DT_DEV_PIXELPIPE_PREVIEW ? "preview" :
247 pipe->type == DT_DEV_PIXELPIPE_EXPORT ? "export" :
248 pipe->type == DT_DEV_PIXELPIPE_THUMBNAIL ? "thumbnail" : "none",
249 (unsigned long)pthread_self(), (void *)piece, (void *)dsc,
250 roi_in->x, roi_in->y, roi_in->scale, d->x, d->y, crop_x, crop_y,
251 pipe->dev->image_storage.dsc.filters, dsc->filters);
252
253 if(pipe->dev->image_storage.dsc.filters != 9u) return;
254
263 for(int i = 0; i < 6; ++i)
264 {
265 for(int j = 0; j < 6; ++j)
266 {
267 dsc->xtrans[j][i] = pipe->dev->image_storage.dsc.xtrans[(j + crop_y) % 6][(i + crop_x) % 6];
268 }
269 }
270}
271
272/* --- the shared geometry core ----------------------------------------------------------
273 *
274 * rawprepare's geometry is the fixed sensor border trim: a translation by the top/left margin,
275 * and an output shrunk by the total margin. Expressed once here for the pipeline callbacks and
276 * for geometry_record() (develop/geometry/geometry.h) alike.
277 */
278
283
285static void _rawprepare_offset(const int32_t crop_x, const int32_t crop_y, const double scale, double *dx,
286 double *dy)
287{
288 *dx = (double)crop_x * scale;
289 *dy = (double)crop_y * scale;
290}
291
294 const dt_iop_roi_t *const roi_in, dt_iop_roi_t *roi_out)
295{
296 *roi_out = *roi_in;
297 roi_out->x = roi_out->y = 0;
298
299 const double x = g->x + g->width;
300 const double y = g->y + g->height;
301 const double scale = roi_in->scale;
302 roi_out->width = (int)round((double)roi_out->width - x * scale);
303 roi_out->height = (int)round((double)roi_out->height - y * scale);
304}
305
307 float *const restrict points, size_t points_count)
308{
309 (void)self;
310 (void)pipe;
312
313 // nothing to be done if parameters are set to neutral values (no top/left crop)
314 if (d->x == 0 && d->y == 0) return 1;
315
316 double x = 0.0, y = 0.0;
317 _rawprepare_offset(d->x, d->y, piece->buf_in.scale, &x, &y);
318 __OMP_PARALLEL_FOR_SIMD__(aligned(points:64) if(points_count > 100))
319 for(size_t i = 0; i < points_count * 2; i += 2)
320 {
321 points[i] -= x;
322 points[i + 1] -= y;
323 }
324
325
326 return 1;
327}
328
330 float *const restrict points, size_t points_count)
331{
332 (void)self;
333 (void)pipe;
335
336 // nothing to be done if parameters are set to neutral values (no top/left crop)
337 if (d->x == 0 && d->y == 0) return 1;
338
339 double x = 0.0, y = 0.0;
340 _rawprepare_offset(d->x, d->y, piece->buf_in.scale, &x, &y);
341 __OMP_PARALLEL_FOR_SIMD__(aligned(points:64) if(points_count > 100))
342 for(size_t i = 0; i < points_count * 2; i += 2)
343 {
344 points[i] += x;
345 points[i + 1] += y;
346 }
347
348
349 return 1;
350}
351
352void distort_mask(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
353 struct dt_dev_pixelpipe_iop_t *piece, const float *const in, float *const out,
354 const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
355{
356 (void)self;
357 (void)pipe;
358 (void)piece;
359 dt_iop_copy_image_roi(out, in, 1, roi_in, roi_out, TRUE);
360}
361
362// we're not scaling here (bayer input), so just crop borders
363// see ../../doc/resizing-scaling.md for details
365 dt_iop_roi_t *roi_out,
366 const dt_iop_roi_t *const roi_in)
367{
369
370 const dt_iop_rawprepare_geometry_t geo = { d->x, d->y, d->width, d->height };
371 _rawprepare_map_size(&geo, roi_in, roi_out);
372
373 /* Rawprepare changes the CFA phase according to the effective crop on the current ROI scale.
374 * That contract cannot be authored at history resync time because `piece->roi_in` is not known yet.
375 * Bind the crop-dependent descriptor fields here, when ROI planning has provided the real input ROI. */
376 _update_output_cfa_descriptor(pipe, piece, roi_in, &piece->dsc_out);
377}
378
379// see ../../doc/resizing-scaling.md for details
381 const dt_iop_roi_t *const roi_out,
382 dt_iop_roi_t *roi_in)
383{
384 *roi_in = *roi_out;
386
387 const double x = d->x + d->width;
388 const double y = d->y + d->height;
389 const double scale = roi_in->scale;
390 roi_in->width = (int)round((double)roi_in->width + x * scale);
391 roi_in->height = (int)round((double)roi_in->height + y * scale);
392
393 /* Same reasoning as in modify_roi_out(): the CFA/X-Trans descriptor depends on the input ROI scale,
394 * so finalize it here once the upstream ROI has been computed. */
395 _update_output_cfa_descriptor(pipe, piece, roi_in, &piece->dsc_out);
396}
397
400{
401 default_output_format(self, pipe, piece, dsc);
402 _update_output_cfa_descriptor(pipe, piece, &piece->roi_in, &piece->dsc_out);
403}
404
405
408{
409 memcpy(dsc, &pipe->dev->image_storage.dsc, sizeof(dt_iop_buffer_dsc_t));
410}
411
412
413static inline __attribute__((always_inline)) int BL(const dt_iop_roi_t *const roi_out,
414 const int row, const int col,
415 const int32_t x, const int32_t y)
416{
417 return ((((row + roi_out->y + y) & 1) << 1) + ((col + roi_out->x + x) & 1));
418}
419
432void autoset(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
433 const struct dt_dev_pixelpipe_iop_t *piece, const void *input)
434{
436 const dt_iop_roi_t *const roi_out = &piece->roi_out;
437 const dt_iop_roi_t *const roi_in = &piece->roi_in;
438 const int csx = compute_proper_crop(piece, roi_in, p->x);
439 const int csy = compute_proper_crop(piece, roi_in, p->y);
440
441 if(piece->dsc_in.filters && piece->dsc_in.channels == 1 && piece->dsc_in.datatype == TYPE_UINT16)
442 {
443 const uint16_t *const restrict in = (const uint16_t *const restrict)input;
444 // 4 channels : R, G sampled on R rows, G sampled on B rows, B.
445 int max_RGB[4] = { 0 };
446
447 __OMP_PARALLEL_FOR__(reduction(max: max_RGB[:4]) collapse(2))
448 for(int i = 0; i < roi_out->height; i++)
449 for(int j = 0; j < roi_out->width; j++)
450 {
451 const size_t channel = BL(roi_out, i, j, p->x, p->y);
452 const size_t pin = roi_in->width * (i + csy) + j + csx;
453 const int pixel = in[pin];
454 max_RGB[channel] = MAX(max_RGB[channel], pixel);
455 }
456
457 p->raw_white_point = MAX(MAX(max_RGB[0], MAX(max_RGB[1], MAX(max_RGB[2], max_RGB[3]))), pipe->dev->image_storage.raw_white_point);
458 }
459 // Do we need to handle float mosaiced images and non-mosaiced (sRAW) images too ?
460}
461
462/* Some comments about the cpu code path; tests with gcc 10.x show a clear performance gain for the
463 compile generated code vs SSE specific code. This depends slightly on the cpu but it's 1.2 to 3-fold
464 better for all tested cases.
465*/
467int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece,
468 const void *const ivoid, void *const ovoid)
469{
470 const dt_iop_roi_t *const roi_in = &piece->roi_in;
471 const dt_iop_roi_t *const roi_out = &piece->roi_out;
473 const int width = roi_out->width;
474 const int height = roi_out->height;
475 const int input_width = roi_in->width;
476 const int roi_x = roi_out->x;
477 const int roi_y = roi_out->y;
478 const int cfa_x = roi_x + d->x;
479 const int cfa_y = roi_y + d->y;
480 // fprintf(stderr, "roi in %d %d %d %d\n", roi_in->x, roi_in->y, roi_in->width, roi_in->height);
481 // fprintf(stderr, "roi out %d %d %d %d\n", roi_out->x, roi_out->y, roi_out->width, roi_out->height);
482
483 const int csx = compute_proper_crop(piece, roi_in, d->x);
484 const int csy = compute_proper_crop(piece, roi_in, d->y);
485
486 if(piece->dsc_in.filters && piece->dsc_in.channels == 1
487 && piece->dsc_in.datatype == TYPE_UINT16)
488 { // raw mosaic
489
490 const uint16_t *const in = (const uint16_t *const)ivoid;
491 float *const out = (float *const)ovoid;
492 const int x_phase = cfa_x & 1;
493 float inv_div[4];
494 for(int k = 0; k < 4; k++) inv_div[k] = 1.0f / d->div[k];
496 for(int j = 0; j < height; j++)
497 {
498 /* Keep the 2-sensel Bayer period explicit per row, but use scalar
499 normalization so the compiler can choose its own vectorization. */
500 const size_t pin = (size_t)input_width * (j + csy) + csx;
501 const size_t pout = (size_t)j * width;
502 const int row_phase = ((j + cfa_y) & 1) << 1;
503 const int id0 = row_phase + x_phase;
504 const int id1 = row_phase + (x_phase ^ 1);
505 const float sub0 = d->sub[id0];
506 const float sub1 = d->sub[id1];
507 const float inv0 = inv_div[id0];
508 const float inv1 = inv_div[id1];
509 int i = 0;
510
511 for(; i + 1 < width; i += 2)
512 {
513 out[pout + i + 0] = ((float)in[pin + i + 0] - sub0) * inv0;
514 out[pout + i + 1] = ((float)in[pin + i + 1] - sub1) * inv1;
515 }
516
517 for(; i < width; i++)
518 {
519 const int id = row_phase + ((x_phase + i) & 1);
520 out[pout + i] = ((float)in[pin + i] - d->sub[id]) * inv_div[id];
521 }
522 }
523
524 }
525 else if(piece->dsc_in.filters && piece->dsc_in.channels == 1
526 && piece->dsc_in.datatype == TYPE_FLOAT)
527 { // raw mosaic, fp, unnormalized
528
529 const float *const in = (const float *const)ivoid;
530 float *const out = (float *const)ovoid;
531 const int x_phase = cfa_x & 1;
532 float inv_div[4];
533 for(int k = 0; k < 4; k++) inv_div[k] = 1.0f / d->div[k];
534
536 for(int j = 0; j < height; j++)
537 {
538 /* Keep the 2-sensel Bayer period explicit per row, but use scalar
539 normalization so the compiler can choose its own vectorization. */
540 const size_t pin = (size_t)input_width * (j + csy) + csx;
541 const size_t pout = (size_t)j * width;
542 const int row_phase = ((j + cfa_y) & 1) << 1;
543 const int id0 = row_phase + x_phase;
544 const int id1 = row_phase + (x_phase ^ 1);
545 const float sub0 = d->sub[id0];
546 const float sub1 = d->sub[id1];
547 const float inv0 = inv_div[id0];
548 const float inv1 = inv_div[id1];
549 int i = 0;
550
551 for(; i + 1 < width; i += 2)
552 {
553 out[pout + i + 0] = (in[pin + i + 0] - sub0) * inv0;
554 out[pout + i + 1] = (in[pin + i + 1] - sub1) * inv1;
555 }
556
557 for(; i < width; i++)
558 {
559 const int id = row_phase + ((x_phase + i) & 1);
560 out[pout + i] = (in[pin + i] - d->sub[id]) * inv_div[id];
561 }
562 }
563
564 }
565 else
566 { // pre-downsampled buffer that needs black/white scaling
567
568 const float *const in = (const float *const)ivoid;
569 float *const out = (float *const)ovoid;
570
571 const float sub = d->sub[0];
572 const float div = d->div[0];
573
574 const int ch = piece->dsc_in.channels;
575 __OMP_PARALLEL_FOR__(collapse(3))
576 for(int j = 0; j < height; j++)
577 {
578 for(int i = 0; i < width; i++)
579 {
580 for(int c = 0; c < ch; c++)
581 {
582 const size_t pin = (size_t)ch * (input_width * (j + csy) + csx + i) + c;
583 const size_t pout = (size_t)ch * (j * width + i) + c;
584
585 out[pout] = (in[pin] - sub) / div;
586 }
587 }
588 }
589
590 }
591
592 if(piece->dsc_in.filters && piece->dsc_in.channels == 1 && d->apply_gainmaps)
593 {
594 const uint32_t map_w = d->gainmaps[0]->map_points_h;
595 const uint32_t map_h = d->gainmaps[0]->map_points_v;
596 const float im_to_rel_x = 1.0f / piece->buf_in.width;
597 const float im_to_rel_y = 1.0f / piece->buf_in.height;
598 const float rel_to_map_x = 1.0f / d->gainmaps[0]->map_spacing_h;
599 const float rel_to_map_y = 1.0f / d->gainmaps[0]->map_spacing_v;
600 const float map_origin_h = d->gainmaps[0]->map_origin_h;
601 const float map_origin_v = d->gainmaps[0]->map_origin_v;
602 float *const out = (float *const)ovoid;
604 for(int j = 0; j < height; j++)
605 {
606 const float y_map = CLAMP(((roi_y + csy + j) * im_to_rel_y - map_origin_v) * rel_to_map_y, 0, map_h);
607 const uint32_t y_i0 = MIN(y_map, map_h - 1);
608 const uint32_t y_i1 = MIN(y_i0 + 1, map_h - 1);
609 const float y_frac = y_map - y_i0;
610 const float * restrict map_row0[4];
611 const float * restrict map_row1[4];
612 for(int f = 0; f < 4; f++)
613 {
614 map_row0[f] = &d->gainmaps[f]->map_gain[y_i0 * map_w];
615 map_row1[f] = &d->gainmaps[f]->map_gain[y_i1 * map_w];
616 }
617 for(int i = 0; i < width; i++)
618 {
619 const int id = BL(roi_out, j, i, d->x, d->y);
620 const float x_map = CLAMP(((roi_x + csx + i) * im_to_rel_x - map_origin_h) * rel_to_map_x, 0, map_w);
621 const uint32_t x_i0 = MIN(x_map, map_w - 1);
622 const uint32_t x_i1 = MIN(x_i0 + 1, map_w - 1);
623 const float x_frac = x_map - x_i0;
624 const float gain_top = (1.0f - x_frac) * map_row0[id][x_i0] + x_frac * map_row0[id][x_i1];
625 const float gain_bottom = (1.0f - x_frac) * map_row1[id][x_i0] + x_frac * map_row1[id][x_i1];
626 out[j * width + i] *= (1.0f - y_frac) * gain_top + y_frac * gain_bottom;
627 }
628 }
629
630 }
631
632 return 0;
633}
634
635#ifdef HAVE_OPENCL
637 cl_mem dev_in, cl_mem dev_out)
638{
639 const dt_iop_roi_t *const roi_in = &piece->roi_in;
640 const dt_iop_roi_t *const roi_out = &piece->roi_out;
643
644 // Scanner DNGs and already-demosaiced files have no CFA; OpenCL path assumes CFA.
645 if(piece->dsc_in.filters == 0) return FALSE;
646
647 const int devid = pipe->devid;
648 cl_mem dev_sub = NULL;
649 cl_mem dev_div = NULL;
650 cl_mem dev_gainmap[4] = {0};
651 cl_int err = -999;
652
653 int kernel = -1;
654 gboolean gainmap_args = FALSE;
655
656 // Monochrome raws (no CFA) also come in as 1 channel; use the 1f kernels regardless of filters.
657 if(piece->dsc_in.channels == 1 && piece->dsc_in.datatype == TYPE_UINT16)
658 {
659 if(d->apply_gainmaps)
660 {
662 gainmap_args = TRUE;
663 }
664 else
665 {
667 }
668 }
669 else if(piece->dsc_in.channels == 1 && piece->dsc_in.datatype == TYPE_FLOAT)
670 {
671 if(d->apply_gainmaps)
672 {
674 gainmap_args = TRUE;
675 }
676 else
677 {
679 }
680 }
681 else
682 {
684 }
685
686 const int csx = compute_proper_crop(piece, roi_in, d->x), csy = compute_proper_crop(piece, roi_in, d->y);
687
688 dev_sub = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 4, d->sub);
689 if(IS_NULL_PTR(dev_sub)) goto error;
690
691 dev_div = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 4, d->div);
692 if(IS_NULL_PTR(dev_div)) goto error;
693
694 const int width = roi_out->width;
695 const int height = roi_out->height;
696
697 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
698 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_in);
699 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&dev_out);
700 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&(width));
701 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), (void *)&(height));
702 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), (void *)&csx);
703 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), (void *)&csy);
704 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(cl_mem), (void *)&dev_sub);
705 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(cl_mem), (void *)&dev_div);
706 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(uint32_t), (void *)&roi_out->x);
707 dt_opencl_set_kernel_arg(devid, kernel, 9, sizeof(uint32_t), (void *)&roi_out->y);
708 if(gainmap_args)
709 {
710 const int map_size[2] = { d->gainmaps[0]->map_points_h, d->gainmaps[0]->map_points_v };
711 const float im_to_rel[2] = { 1.0 / piece->buf_in.width, 1.0 / piece->buf_in.height };
712 const float rel_to_map[2] = { 1.0 / d->gainmaps[0]->map_spacing_h, 1.0 / d->gainmaps[0]->map_spacing_v };
713 const float map_origin[2] = { d->gainmaps[0]->map_origin_h, d->gainmaps[0]->map_origin_v };
714
715 for(int i = 0; i < 4; i++)
716 {
717 dev_gainmap[i] = dt_opencl_alloc_device(devid, map_size[0], map_size[1], sizeof(float));
718 if(dev_gainmap[i] == NULL) goto error;
719 err = dt_opencl_write_host_to_device(devid, d->gainmaps[i]->map_gain, dev_gainmap[i],
720 map_size[0], map_size[1], sizeof(float));
721 if(err != CL_SUCCESS) goto error;
722 }
723
724 dt_opencl_set_kernel_arg(devid, kernel, 10, sizeof(cl_mem), &dev_gainmap[0]);
725 dt_opencl_set_kernel_arg(devid, kernel, 11, sizeof(cl_mem), &dev_gainmap[1]);
726 dt_opencl_set_kernel_arg(devid, kernel, 12, sizeof(cl_mem), &dev_gainmap[2]);
727 dt_opencl_set_kernel_arg(devid, kernel, 13, sizeof(cl_mem), &dev_gainmap[3]);
728 dt_opencl_set_kernel_arg(devid, kernel, 14, 2 * sizeof(int), &map_size);
729 dt_opencl_set_kernel_arg(devid, kernel, 15, 2 * sizeof(float), &im_to_rel);
730 dt_opencl_set_kernel_arg(devid, kernel, 16, 2 * sizeof(float), &rel_to_map);
731 dt_opencl_set_kernel_arg(devid, kernel, 17, 2 * sizeof(float), &map_origin);
732 }
733 err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
734 if(err != CL_SUCCESS) goto error;
735
738 for(int i = 0; i < 4; i++) dt_opencl_release_mem_object(dev_gainmap[i]);
739
740 return TRUE;
741
742error:
745 for(int i = 0; i < 4; i++) dt_opencl_release_mem_object(dev_gainmap[i]);
746 dt_print(DT_DEBUG_OPENCL, "[opencl_rawprepare] couldn't enqueue kernel! %d\n", err);
747 return FALSE;
748}
749#endif
750
751static int image_is_normalized(const dt_image_t *const image)
752{
753 // if raw with floating-point data, if not special magic whitelevel, then it needs normalization
754 if(dt_image_is_hdr(image))
755 {
756 union {
757 float f;
758 uint32_t u;
759 } normalized;
760 normalized.f = 1.0f;
761
762 // dng spec is just broken here.
763 return image->raw_white_point == normalized.u;
764 }
765
766 // else, assume normalized
767 return image->dsc.channels == 1 && image->dsc.datatype == TYPE_FLOAT;
768}
769
770static gboolean image_set_rawcrops(const int32_t imgid, int dx, int dy)
771{
772 if(imgid <= 0) return FALSE;
773
774 dt_image_t *img = NULL;
775 img = dt_image_cache_get(imgid, 'r');
776 if(IS_NULL_PTR(img)) return FALSE;
777 const gboolean test = (img->p_width == img->width - dx)
778 && (img->p_height == img->height - dy);
779
781 if(test) return FALSE;
782
783 img = dt_image_cache_get(imgid, 'w');
784 if(IS_NULL_PTR(img)) return FALSE;
785 img->p_width = img->width - dx;
786 img->p_height = img->height - dy;
788 return TRUE;
789}
790
791// Check whether `image` carries GainMaps of the exact type we can apply here. We may reject some
792// GainMaps that are valid per the Adobe DNG spec but that we do not support.
793//
794// The caller MUST hold an image-cache read lock on `image` for the whole call, and for as long as it
795// then uses `gainmaps_out`: both the list and the maps it points at belong to the cache entry, which
796// frees them in `dt_image_cache_deallocate()` on eviction. Never pass `&dev->image_storage` here --
797// that is a SHALLOW struct copy taken by `_dt_dev_refresh_image_storage()`, which drops the cache
798// lock on the line after copying, so its `dng_gain_maps` is a borrowed pointer with no owner. Walking
799// it while a thumbnail run evicted the entry is the segfault in Sentry 140771917.
800static gboolean _validate_gain_maps(const dt_image_t *const image, dt_dng_gain_map_t **gainmaps_out)
801{
802 dt_dng_gain_map_t *gainmaps[4] = {0};
803
804 if(g_list_length(image->dng_gain_maps) != 4)
805 return FALSE;
806
807 for(int i = 0; i < 4; i++)
808 {
809 // check that each GainMap applies to one filter of a Bayer image,
810 // covers the entire image, and is not a 1x1 no-op
811 dt_dng_gain_map_t *g = (dt_dng_gain_map_t *)g_list_nth_data(image->dng_gain_maps, i);
812 if(IS_NULL_PTR(g) ||
813 g->plane != 0 || g->planes != 1 || g->map_planes != 1 ||
814 g->row_pitch != 2 || g->col_pitch != 2 ||
815 g->map_points_v < 2 || g->map_points_h < 2 ||
816 g->top > 1 || g->left > 1 ||
817 g->bottom != image->height || g->right != image->width)
818 return FALSE;
819 /* The grid dimensions come from the file and are not cross-checked against the number of gain
820 * values the opcode actually carried, so a truncated or malformed DNG can declare a grid larger
821 * than its payload. Everything downstream then indexes map_gain[] as map_points_h * map_points_v
822 * -- process() reads row map_points_v - 1, and _own_gainmaps() memcpy's that many floats out of a
823 * source block that only ever held map_gain_count. Reject the map instead of reading past it. */
824 if((uint64_t)g->map_points_h * (uint64_t)g->map_points_v > (uint64_t)g->map_gain_count)
825 return FALSE;
826 uint32_t filter = ((g->top & 1) << 1) + (g->left & 1);
827 gainmaps[filter] = g;
828 }
829
830 // check that there is a GainMap for each filter of the Bayer pattern
831 if(gainmaps[0] == NULL || gainmaps[1] == NULL || gainmaps[2] == NULL || gainmaps[3] == NULL)
832 return FALSE;
833
834 // check that each GainMap has the same shape
835 for(int i = 1; i < 4; i++)
836 {
837 if(gainmaps[i]->map_points_h != gainmaps[0]->map_points_h ||
838 gainmaps[i]->map_points_v != gainmaps[0]->map_points_v ||
839 gainmaps[i]->map_spacing_h != gainmaps[0]->map_spacing_h ||
840 gainmaps[i]->map_spacing_v != gainmaps[0]->map_spacing_v ||
841 gainmaps[i]->map_origin_h != gainmaps[0]->map_origin_h ||
842 gainmaps[i]->map_origin_v != gainmaps[0]->map_origin_v)
843 return FALSE;
844 }
845
846 if(gainmaps_out)
847 memcpy(gainmaps_out, gainmaps, sizeof(gainmaps));
848
849 return TRUE;
850}
851
852// Does this image carry GainMaps we can apply? Answers against the cache entry, under its read lock,
853// and lets no pointer escape.
854gboolean check_gain_maps(const int32_t imgid)
855{
856 const dt_image_t *const image = dt_image_cache_get(imgid, 'r');
857 if(IS_NULL_PTR(image)) return FALSE;
858
859 const gboolean supported = _validate_gain_maps(image, NULL);
861 return supported;
862}
863
864// Free the pipe-owned deep copies of the GainMaps held in piece->data (see commit_params).
866{
867 for(int i = 0; i < 4; i++)
868 {
869 g_free(d->gainmaps[i]);
870 d->gainmaps[i] = NULL;
871 }
872}
873
874// Build pipe-owned deep copies of the image's GainMaps into d->gainmaps[]. The maps belong to the
875// image-cache entry, which frees them on eviction (thumbnail-generation pressure is enough), so
876// `process()` must not read them directly -- that was Sentry #129880848, fixed by owning a private
877// copy. The validation and the copy must BOTH happen under the cache read lock taken here: doing the
878// copy after releasing it just moves the same use-after-free into the memcpy, and validating through
879// `dev->image_storage` -- a shallow copy whose lock was dropped when it was made -- moves it into the
880// list walk, which is Sentry 140771917.
881// Caller must have freed/NULLed d->gainmaps[] first. Returns TRUE only if all four were copied.
882static gboolean _own_gainmaps(const int32_t imgid, dt_iop_rawprepare_data_t *d)
883{
884 const dt_image_t *const image = dt_image_cache_get(imgid, 'r');
885 if(IS_NULL_PTR(image)) return FALSE;
886
887 dt_dng_gain_map_t *src[4] = { 0 };
888 gboolean owned = _validate_gain_maps(image, src);
889
890 for(int i = 0; owned && i < 4; i++)
891 {
892 // map_planes == 1 is enforced by _validate_gain_maps(), so the gain payload is
893 // map_points_h * map_points_v floats -- exactly what process() indexes.
894 const size_t sz = sizeof(dt_dng_gain_map_t)
895 + (size_t)src[i]->map_points_h * src[i]->map_points_v * sizeof(float);
896 d->gainmaps[i] = g_malloc(sz);
897 if(IS_NULL_PTR(d->gainmaps[i]))
898 {
899 _free_owned_gainmaps(d); // partial copy (OOM): drop everything, don't apply
900 owned = FALSE;
901 break;
902 }
903 memcpy(d->gainmaps[i], src[i], sz);
904 }
905
907 return owned;
908}
909
912{
915 const dt_image_t *const img = &pipe->dev->image_storage;
916
917 d->x = p->x;
918 d->y = p->y;
919 d->width = p->width;
920 d->height = p->height;
921
922 if(img->dsc.filters)
923 {
924 const float white = (float)p->raw_white_point;
925
926 for(int i = 0; i < 4; i++)
927 {
928 d->sub[i] = (float)p->raw_black_level_separate[i];
929 d->div[i] = (white - d->sub[i]);
930 }
931 }
932 else
933 {
934 const float normalizer
935 = dt_image_is_hdr(&pipe->dev->image_storage) ? 1.0f : (float)UINT16_MAX;
936 const float white = (float)p->raw_white_point / normalizer;
937 float black = 0;
938 for(int i = 0; i < 4; i++)
939 {
940 black += p->raw_black_level_separate[i] / normalizer;
941 }
942 black /= 4.0f;
943
944 for(int i = 0; i < 4; i++)
945 {
946 d->sub[i] = black;
947 d->div[i] = (white - black);
948 }
949 }
950
951 float black = 0.0f;
952 for(uint8_t i = 0; i < 4; i++)
953 {
954 black += (float)p->raw_black_level_separate[i];
955 }
956 d->rawprepare.raw_black_level = (uint16_t)(black / 4.0f);
957 d->rawprepare.raw_white_point = p->raw_white_point;
958 piece->dsc_out.rawprepare.raw_black_level = d->rawprepare.raw_black_level;
959 piece->dsc_out.rawprepare.raw_white_point = d->rawprepare.raw_white_point;
960 for(int k = 0; k < 4; k++) piece->dsc_out.processed_maximum[k] = 1.0f;
961
962 // Refresh our private, pipe-owned copy of the GainMaps (see _own_gainmaps() for why we must own
963 // them rather than borrow image_storage.dng_gain_maps). Free the previous copy in all cases.
965 d->apply_gainmaps = (p->flat_field == FLAT_FIELD_EMBEDDED) && _own_gainmaps(img->id, d);
966
967 if(image_set_rawcrops(pipe->dev->image_storage.id, d->x + d->width, d->y + d->height))
969
970 // Image-type gating (needs_rawprepare && !normalized) is handled at history level by
971 // enable()/force_enable()/reload_defaults(); it is no longer duplicated here.
972
973 // OpenCL path only for RAW, single-channel, CFA images (runtime/format decision).
974 const gboolean cl_ok = (img->dsc.cst == IOP_CS_RAW && img->dsc.channels == 1 && img->dsc.filters);
975 if(!cl_ok) piece->process_cl_ready = FALSE;
976
977 dt_iop_fmt_log(self, "commit: class=%s needs_rawprepare=%d normalized=%d cl_ok=%d -> enabled=%d",
979 dt_image_needs_rawprepare(img), image_is_normalized(img), cl_ok, piece->enabled);
980}
981
987
988/* --- the geometry service's view of this module (develop/geometry/geometry.h) --------- */
989
990static void _rawprepare_geometry_map_size(const void *data, const dt_iop_roi_t *const in, dt_iop_roi_t *out)
991{
993}
994
995static int _rawprepare_geometry_transform(const void *data, const dt_geometry_record_t *const record,
996 dt_geometry_chain_t *chain, float *points, size_t points_count)
997{
999 if(g->x == 0 && g->y == 0) return 1;
1000
1001 double x = 0.0, y = 0.0;
1002 _rawprepare_offset(g->x, g->y, record->in.scale, &x, &y);
1003 for(size_t i = 0; i < points_count * 2; i += 2)
1004 {
1005 points[i] -= x;
1006 points[i + 1] -= y;
1007 }
1008 return 1;
1009}
1010
1011static int _rawprepare_geometry_backtransform(const void *data, const dt_geometry_record_t *const record,
1012 dt_geometry_chain_t *chain, float *points, size_t points_count)
1013{
1014 const dt_iop_rawprepare_geometry_t *const g = (const dt_iop_rawprepare_geometry_t *)data;
1015 if(g->x == 0 && g->y == 0) return 1;
1016
1017 double x = 0.0, y = 0.0;
1018 _rawprepare_offset(g->x, g->y, record->in.scale, &x, &y);
1019 for(size_t i = 0; i < points_count * 2; i += 2)
1020 {
1021 points[i] += x;
1022 points[i + 1] += y;
1023 }
1024 return 1;
1025}
1026
1032
1033gboolean geometry_record(dt_iop_module_t *self, const void *params, dt_geometry_record_t *record)
1034{
1035 const dt_iop_rawprepare_params_t *const p = (const dt_iop_rawprepare_params_t *)params;
1036
1039 if(IS_NULL_PTR(data)) return FALSE;
1040
1041 /* commit_params() copies these four verbatim; nothing else about them is derived. */
1042 data->x = p->x;
1043 data->y = p->y;
1044 data->width = p->width;
1045 data->height = p->height;
1046
1047 record->data = data;
1048 record->free_data = dt_free_gpointer;
1050 return TRUE;
1051}
1052
1054{
1057 dt_free_align(piece->data);
1058 piece->data = NULL;
1059}
1060
1061
1062static gboolean enable(const dt_image_t *image)
1063{
1064 // rawprepare (black/white normalization) applies to any raw colorimetry: mosaiced raw and
1065 // already-demosaiced sRAW / linear DNG alike, unless the buffer is already normalized.
1066 return dt_image_needs_rawprepare(image)
1067 && !image_is_normalized(image);
1068}
1069
1070gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
1071{
1072 // History sanitization: rawprepare must be off for non-raw or already-normalized buffers,
1073 // decided here from image metadata using the same enable() rule as reload_defaults().
1074 const gboolean active = enable(&self->dev->image_storage);
1075 const gboolean state = active && current_state;
1076 dt_iop_fmt_log(self, "force_enable: class=%s supported=%d current=%d -> %d",
1078 active, current_state, state);
1079 return state;
1080}
1081
1083{
1085 const dt_image_t *const image = &(self->dev->image_storage);
1086
1087 // if there are embedded GainMaps, they should be applied by default to avoid uneven color cast
1088 gboolean has_gainmaps = check_gain_maps(self->dev->image_storage.id);
1089
1090 *d = (dt_iop_rawprepare_params_t){.x = image->crop_x,
1091 .y = image->crop_y,
1092 .width = image->crop_width,
1093 .height = image->crop_height,
1094 .raw_black_level_separate[0] = image->raw_black_level_separate[0],
1095 .raw_black_level_separate[1] = image->raw_black_level_separate[1],
1096 .raw_black_level_separate[2] = image->raw_black_level_separate[2],
1097 .raw_black_level_separate[3] = image->raw_black_level_separate[3],
1098 .raw_white_point = image->raw_white_point,
1099 .flat_field = has_gainmaps ? FLAT_FIELD_EMBEDDED : FLAT_FIELD_OFF };
1100
1101 self->hide_enable_button = 1;
1102 self->default_enabled = enable(image);
1103
1104 dt_image_print_debug_info(image, "rawprepare.reload_defaults");
1105 // Stack visibility (raw vs non_raw) is applied from default_enabled in gui_update(), on the GUI
1106 // thread when the widget exists; reload_defaults() stays params-only.
1107}
1108
1110{
1111 const int program = 2; // basic.cl, from programs.conf
1112 self->data = malloc(sizeof(dt_iop_rawprepare_global_data_t));
1113
1115 gd->kernel_rawprepare_1f = dt_opencl_create_kernel(program, "rawprepare_1f");
1116 gd->kernel_rawprepare_1f_gainmap = dt_opencl_create_kernel(program, "rawprepare_1f_gainmap");
1117 gd->kernel_rawprepare_1f_unnormalized = dt_opencl_create_kernel(program, "rawprepare_1f_unnormalized");
1118 gd->kernel_rawprepare_1f_unnormalized_gainmap = dt_opencl_create_kernel(program, "rawprepare_1f_unnormalized_gainmap");
1119 gd->kernel_rawprepare_4f = dt_opencl_create_kernel(program, "rawprepare_4f");
1120}
1121
1130
1132{
1135
1136 const gboolean is_monochrome = (self->dev->image_storage.flags & (DT_IMAGE_MONOCHROME | DT_IMAGE_MONOCHROME_BAYER)) != 0;
1137 if(is_monochrome)
1138 {
1139 // we might have to deal with old edits, so get average first
1140 int av = 2; // for rounding
1141 for(int i = 0; i < 4; i++)
1142 av += p->raw_black_level_separate[i];
1143
1144 for(int i = 0; i < 4; i++)
1145 dt_bauhaus_slider_set(g->black_level_separate[i], av / 4);
1146 }
1147
1148 // don't show upper three black levels for monochromes
1149 for(int i = 1; i < 4; i++)
1150 gtk_widget_set_visible(g->black_level_separate[i], !is_monochrome);
1151
1152 gtk_widget_set_visible(g->flat_field, check_gain_maps(self->dev->image_storage.id));
1153 dt_bauhaus_combobox_set(g->flat_field, p->flat_field);
1154
1155 // raw vs non_raw page, from the per-image default computed by reload_defaults()
1156 gtk_stack_set_visible_child_name(GTK_STACK(self->gui->widget), self->default_enabled ? "raw" : "non_raw");
1157}
1158
1159void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
1160{
1163
1164 const gboolean is_monochrome = (self->dev->image_storage.flags & (DT_IMAGE_MONOCHROME | DT_IMAGE_MONOCHROME_BAYER)) != 0;
1165 if(is_monochrome)
1166 {
1167 if(w == g->black_level_separate[0])
1168 {
1169 const int val = p->raw_black_level_separate[0];
1170 for(int i = 1; i < 4; i++)
1171 dt_bauhaus_slider_set(g->black_level_separate[i], val);
1172 }
1173 }
1174}
1175
1176const gchar *black_label[]
1177 = { N_("black level 0"),
1178 N_("black level 1"),
1179 N_("black level 2"),
1180 N_("black level 3") };
1181
1183{
1185
1186 GtkWidget *box_raw = self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1187
1188 for(int i = 0; i < 4; i++)
1189 {
1190 gchar *par = g_strdup_printf("raw_black_level_separate[%i]", i);
1191
1192 g->black_level_separate[i] = dt_bauhaus_slider_from_params(self, par);
1193 dt_bauhaus_widget_set_label(g->black_level_separate[i], black_label[i]);
1194 gtk_widget_set_tooltip_text(g->black_level_separate[i], _(black_label[i]));
1195 dt_bauhaus_slider_set_soft_max(g->black_level_separate[i], 16384);
1196
1197 dt_free(par);
1198 }
1199
1200 g->white_point = dt_bauhaus_slider_from_params(self, "raw_white_point");
1201 gtk_widget_set_tooltip_text(g->white_point, _("white point"));
1202 dt_bauhaus_slider_set_soft_max(g->white_point, 16384);
1203
1204 g->flat_field = dt_bauhaus_combobox_from_params(self, "flat_field");
1205 gtk_widget_set_tooltip_text(g->flat_field, _("flat field correction to compensate for lens shading"));
1206
1207 gtk_box_pack_start(GTK_BOX(self->gui->widget),
1208 dt_ui_section_label_new(_("In-camera crop")), FALSE, FALSE, 0);
1209
1210 g->x = dt_bauhaus_slider_from_params(self, "x");
1211 gtk_widget_set_tooltip_text(g->x, _("crop from left border"));
1213
1214 g->y = dt_bauhaus_slider_from_params(self, "y");
1215 gtk_widget_set_tooltip_text(g->y, _("crop from top"));
1217
1218 g->width = dt_bauhaus_slider_from_params(self, "width");
1219 gtk_widget_set_tooltip_text(g->width, _("crop from right border"));
1220 dt_bauhaus_slider_set_soft_max(g->width, 256);
1221
1222 g->height = dt_bauhaus_slider_from_params(self, "height");
1223 gtk_widget_set_tooltip_text(g->height, _("crop from bottom"));
1224 dt_bauhaus_slider_set_soft_max(g->height, 256);
1225
1226 // start building top level widget
1227 self->gui->widget = gtk_stack_new();
1228 gtk_stack_set_homogeneous(GTK_STACK(self->gui->widget), FALSE);
1229
1230 GtkWidget *label_non_raw = dt_ui_label_new(_("raw black/white point correction\nonly works for the sensors that need it."));
1231
1232 gtk_stack_add_named(GTK_STACK(self->gui->widget), label_non_raw, "non_raw");
1233 gtk_stack_add_named(GTK_STACK(self->gui->widget), box_raw, "raw");
1234}
1235
1236// clang-format off
1237// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1238// vim: shiftwidth=2 expandtab tabstop=2 cindent
1239// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1240// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_bauhaus_slider_set_soft_max(GtkWidget *widget, float val)
Definition bauhaus.c:1474
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3331
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2090
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1504
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
@ IOP_CS_RAW
@ IOP_CS_RGB
static const float x
const float f
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
const float max
const dt_colormatrix_t dt_aligned_pixel_t out
static const int row
dt_image_pipe_class_t dt_image_pipe_class(const dt_image_t *img)
const char * dt_image_pipe_class_name(const dt_image_pipe_class_t klass)
gboolean dt_image_is_hdr(const dt_image_t *img)
void dt_image_print_debug_info(const dt_image_t *img, const char *context)
gboolean dt_image_needs_rawprepare(const dt_image_t *img)
#define dt_database_start_transaction()
Definition database.h:290
#define dt_database_release_transaction()
Definition database.h:291
void dt_iop_params_t
Definition dev_history.h:43
void default_output_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
int supported(struct dt_imageio_module_storage_t *storage, struct dt_imageio_module_format_t *format)
Definition example.c:273
@ TYPE_FLOAT
Definition format.h:56
@ TYPE_UINT16
Definition format.h:57
Where things are on the image, answered without a pipeline.
void dt_gui_presets_add_generic(const char *name, dt_dev_operation_t op, const int32_t version, const void *params, const int32_t params_size, const int32_t enabled)
@ DT_IMAGE_MONOCHROME_BAYER
Definition image.h:151
@ DT_IMAGE_MONOCHROME
Definition image.h:142
void dt_image_cache_write_release(dt_image_t *img, dt_image_cache_write_mode_t mode)
dt_image_t * dt_image_cache_get(const int32_t imgid, char mode)
void dt_image_cache_read_release(const dt_image_t *img)
@ DT_IMAGE_CACHE_RELAXED
Definition image_cache.h:50
void dt_iop_copy_image_roi(float *const __restrict__ out, const float *const __restrict__ in, const size_t ch, const dt_iop_roi_t *const __restrict__ roi_in, const dt_iop_roi_t *const __restrict__ roi_out, const int zero_pad)
Definition imagebuf.c:163
uint32_t dt_rawspeed_crop_dcraw_filters(uint32_t filters, uint32_t crop_x, uint32_t crop_y)
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
Definition imageop.c:1893
#define dt_iop_fmt_log(module, fmt,...)
Debug helper to trace a module's input-format-driven decisions on the -d pipe channel (DT_DEBUG_PIPE)...
Definition imageop.h:535
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:188
@ IOP_FLAGS_UNSAFE_COPY
Definition imageop.h:196
@ IOP_FLAGS_ONE_INSTANCE
Definition imageop.h:191
@ IOP_FLAGS_TILING_FULL_ROI
Definition imageop.h:190
@ IOP_GROUP_TECHNICAL
Definition imageop.h:162
@ IOP_TAG_DISTORT
Definition imageop.h:170
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
static dt_iop_gui_data_t * dt_iop_gui_data(const struct dt_iop_module_t *m)
The module's GUI data blob, NULL-safe for headless callers: IOP process() implementations read it for...
Definition imageop_gui.h:81
#define IOP_GUI_ALLOC(module)
Definition imageop_gui.h:93
void *const ovoid
static float kernel(const float *x, const float *y)
GtkWidget * dt_ui_section_label_new(const gchar *str)
Definition label.c:114
GtkWidget * dt_ui_label_new(const gchar *str)
Definition label.c:125
@ DT_DEBUG_OPENCL
Definition logging.h:57
@ DT_DEBUG_DEMOSAIC
Definition logging.h:72
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
Definition mem_alloc.h:214
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
Definition mem_alloc.h:225
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
Definition mem_alloc.h:184
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2554
void * dt_opencl_alloc_device(const int devid, const int width, const int height, const int bpp)
Definition opencl.c:2894
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2448
void * dt_opencl_copy_host_to_device_constant(const int devid, const size_t size, void *host)
Definition opencl.c:2750
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2491
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
Definition opencl.c:2545
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2805
int dt_opencl_write_host_to_device(const int devid, void *host, void *device, const int width, const int height, const int bpp)
Definition opencl.c:2634
#define ROUNDUPDHT(a, b)
Definition opencl.h:86
#define ROUNDUPDWD(a, b)
Definition opencl.h:85
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
#define __OMP_PARALLEL_FOR_SIMD__(...)
Definition openmp.h:96
@ DT_DEV_PIXELPIPE_THUMBNAIL
Definition pixelpipe.h:45
@ DT_DEV_PIXELPIPE_EXPORT
Definition pixelpipe.h:42
@ DT_DEV_PIXELPIPE_PREVIEW
Definition pixelpipe.h:44
@ DT_DEV_PIXELPIPE_FULL
Definition pixelpipe.h:43
int operation_tags()
Definition rawprepare.c:128
static int _rawprepare_geometry_transform(const void *data, const dt_geometry_record_t *const record, dt_geometry_chain_t *chain, float *points, size_t points_count)
Definition rawprepare.c:995
void distort_mask(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, 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)
Definition rawprepare.c:352
const char ** description(struct dt_iop_module_t *self)
Definition rawprepare.c:177
void modify_roi_out(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t *roi_out, const dt_iop_roi_t *const roi_in)
Definition rawprepare.c:364
int default_group()
Definition rawprepare.c:139
int distort_backtransform(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, float *const restrict points, size_t points_count)
Definition rawprepare.c:329
__DT_CLONE_TARGETS__ int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid)
Definition rawprepare.c:467
static gboolean _own_gainmaps(const int32_t imgid, dt_iop_rawprepare_data_t *d)
Definition rawprepare.c:882
int distort_transform(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, float *const restrict points, size_t points_count)
Definition rawprepare.c:306
static void _rawprepare_offset(const int32_t crop_x, const int32_t crop_y, const double scale, double *dx, double *dy)
The translation the trim applies, at scale.
Definition rawprepare.c:285
void gui_update(dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
static void _free_owned_gainmaps(dt_iop_rawprepare_data_t *d)
Definition rawprepare.c:865
dt_iop_rawprepare_flat_field_t
Definition rawprepare.c:70
@ FLAT_FIELD_OFF
Definition rawprepare.c:71
@ FLAT_FIELD_EMBEDDED
Definition rawprepare.c:72
void modify_roi_in(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *const roi_out, dt_iop_roi_t *roi_in)
Definition rawprepare.c:380
static gboolean enable(const dt_image_t *image)
static const dt_geometry_vtable_t _rawprepare_geometry_vtable
int process_cl(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
Definition rawprepare.c:636
static void _rawprepare_geometry_map_size(const void *data, const dt_iop_roi_t *const in, dt_iop_roi_t *out)
Definition rawprepare.c:990
const char * name()
Definition rawprepare.c:123
void output_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
Definition rawprepare.c:398
gboolean geometry_record(dt_iop_module_t *self, const void *params, dt_geometry_record_t *record)
void cleanup_global(dt_iop_module_so_t *self)
void gui_init(dt_iop_module_t *self)
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
void reload_defaults(dt_iop_module_t *self)
void cleanup_pipe(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition rawprepare.c:144
void input_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
Definition rawprepare.c:406
int flags()
Definition rawprepare.c:133
void autoset(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, const void *input)
RawSpeed tends to under-evaluate the white point of RAW images, which leads to RGB values > 1 after n...
Definition rawprepare.c:432
static void _rawprepare_map_size(const dt_iop_rawprepare_geometry_t *const g, const dt_iop_roi_t *const roi_in, dt_iop_roi_t *roi_out)
Input rect -> output rect, shrunk by the total margin at the input's own scale.
Definition rawprepare.c:293
void init_presets(dt_iop_module_so_t *self)
Definition rawprepare.c:187
gboolean check_gain_maps(const int32_t imgid)
Definition rawprepare.c:854
static gboolean _validate_gain_maps(const dt_image_t *const image, dt_dng_gain_map_t **gainmaps_out)
Definition rawprepare.c:800
void commit_params(dt_iop_module_t *self, dt_iop_params_t *params, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition rawprepare.c:910
gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
void init_pipe(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition rawprepare.c:982
static void _update_output_cfa_descriptor(const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *const roi_in, dt_iop_buffer_dsc_t *dsc)
Definition rawprepare.c:212
static gboolean image_set_rawcrops(const int32_t imgid, int dx, int dy)
Definition rawprepare.c:770
static int _rawprepare_geometry_backtransform(const void *data, const dt_geometry_record_t *const record, dt_geometry_chain_t *chain, float *points, size_t points_count)
static int image_is_normalized(const dt_image_t *const image)
Definition rawprepare.c:751
const gchar * black_label[]
void init_global(dt_iop_module_so_t *self)
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
Definition rawprepare.c:151
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:386
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:616
@ DT_SIGNAL_METADATA_UPDATE
Definition signal.h:308
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Apply one channel's tone curve to each of the three colour channels, or pass the channel through unto...
Definition simd.h:55
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
const float uint32_t state[4]
unsigned __int64 uint64_t
Definition strptime.c:75
dt_iop_buffer_dsc_t dsc_out
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
dt_dev_pixelpipe_type_t type
struct dt_develop_t * dev
dt_image_t image_storage
Definition develop.h:225
uint32_t map_points_v
Definition dng_opcode.h:55
uint32_t map_points_h
Definition dng_opcode.h:56
One module instance's contribution, as data.
Definition geometry.h:99
dt_iop_roi_t in
Definition geometry.h:117
const dt_geometry_vtable_t * vtable
Definition geometry.h:110
void(* free_data)(void *data)
Definition geometry.h:112
A module's geometry, evaluated. Pure functions of the record's own data.
Definition geometry.h:75
void(* map_size)(const void *data, const dt_iop_roi_t *const in, dt_iop_roi_t *out)
Full-resolution input rect -> output rect. Mirrors modify_roi_out() at scale 1.
Definition geometry.h:77
int32_t height
Definition image.h:397
GList * dng_gain_maps
Definition image.h:464
uint32_t raw_white_point
Definition image.h:439
int32_t crop_height
Definition image.h:398
int32_t flags
Definition image.h:401
int32_t width
Definition image.h:397
int32_t crop_y
Definition image.h:398
int32_t crop_x
Definition image.h:398
int32_t p_height
Definition image.h:397
dt_iop_buffer_dsc_t dsc
Definition image.h:419
int32_t p_width
Definition image.h:397
int32_t crop_width
Definition image.h:398
int32_t id
Definition image.h:401
uint16_t raw_black_level_separate[4]
Definition image.h:438
uint32_t filters
Definition format.h:89
uint16_t raw_black_level
Definition format.h:103
unsigned int channels
Definition format.h:83
uint8_t xtrans[6][6]
Definition format.h:99
struct dt_iop_buffer_dsc_t::@55 rawprepare
dt_iop_buffer_type_t datatype
Definition format.h:85
dt_aligned_pixel_t processed_maximum
Definition format.h:114
uint16_t raw_white_point
Definition format.h:104
GtkWidget * widget
Definition imageop_gui.h:47
dt_dev_operation_t op
Definition imageop.h:235
dt_iop_global_data_t * data
Definition imageop.h:238
int32_t hide_enable_button
Definition imageop.h:270
dt_iop_params_t * default_params
Definition imageop.h:333
struct dt_iop_module_gui_t * gui
Definition imageop.h:346
struct dt_develop_t * dev
Definition imageop.h:311
gboolean default_enabled
Definition imageop.h:318
dt_iop_global_data_t * global_data
Definition imageop.h:337
dt_iop_params_t * params
Definition imageop.h:333
struct dt_iop_rawprepare_data_t::@45 rawprepare
dt_dng_gain_map_t * gainmaps[4]
Definition rawprepare.c:110
GtkWidget * black_level_separate[4]
Definition rawprepare.c:88
uint16_t raw_black_level_separate[4]
Definition rawprepare.c:81
dt_iop_rawprepare_flat_field_t flat_field
Definition rawprepare.c:83
Region of interest passed through the pixelpipe.
Definition format.h:49
double scale
Definition format.h:51
int width
Definition format.h:50
int height
Definition format.h:50
#define __DT_CLONE_TARGETS__
typedef double((*spd)(unsigned long int wavelength, double TempK))
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
#define DT_GUI_BOX_SPACING