Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
develop.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2009-2015, 2018 johannes hanika.
4 Copyright (C) 2010 Alexandre Prokoudine.
5 Copyright (C) 2010-2011 Bruce Guenter.
6 Copyright (C) 2010-2012 Henrik Andersson.
7 Copyright (C) 2011 Karl Mikaelsson.
8 Copyright (C) 2011 Mikko Ruohola.
9 Copyright (C) 2011 Omari Stephens.
10 Copyright (C) 2011 Robert Bieber.
11 Copyright (C) 2011 Rostyslav Pidgornyi.
12 Copyright (C) 2011-2019 Tobias Ellinghaus.
13 Copyright (C) 2012-2014, 2016, 2020-2021 Aldric Renaudin.
14 Copyright (C) 2012 Antony Dovgal.
15 Copyright (C) 2012 Moritz Lipp.
16 Copyright (C) 2012 Richard Wonka.
17 Copyright (C) 2012-2014, 2016-2017 Ulrich Pegelow.
18 Copyright (C) 2013-2022 Pascal Obry.
19 Copyright (C) 2014, 2020 Dan Torop.
20 Copyright (C) 2014 parafin.
21 Copyright (C) 2014-2015 Pedro Côrte-Real.
22 Copyright (C) 2014-2017 Roman Lebedev.
23 Copyright (C) 2016 Alexander V. Smal.
24 Copyright (C) 2017, 2021 luzpaz.
25 Copyright (C) 2018-2019 Edgardo Hoszowski.
26 Copyright (C) 2019 Alexander Blinne.
27 Copyright (C) 2019-2020, 2022-2026 Aurélien PIERRE.
28 Copyright (C) 2019-2021 Diederik Ter Rahe.
29 Copyright (C) 2019-2022 Hanno Schwalm.
30 Copyright (C) 2019 Heiko Bauke.
31 Copyright (C) 2019-2020 Philippe Weyland.
32 Copyright (C) 2020-2021 Chris Elston.
33 Copyright (C) 2020 GrahamByrnes.
34 Copyright (C) 2020 Harold le Clément de Saint-Marcq.
35 Copyright (C) 2020 Hubert Kowalski.
36 Copyright (C) 2020 JP Verrue.
37 Copyright (C) 2020-2021 Ralf Brown.
38 Copyright (C) 2021 paolodepetrillo.
39 Copyright (C) 2021 Sakari Kapanen.
40 Copyright (C) 2022 Martin Bařinka.
41 Copyright (C) 2023 Alynx Zhou.
42 Copyright (C) 2023 lologor.
43 Copyright (C) 2023 Luca Zulberti.
44 Copyright (C) 2023 Ricky Moon.
45 Copyright (C) 2025-2026 Guillaume Stutin.
46
47 darktable is free software: you can redistribute it and/or modify
48 it under the terms of the GNU General Public License as published by
49 the Free Software Foundation, either version 3 of the License, or
50 (at your option) any later version.
51
52 darktable is distributed in the hope that it will be useful,
53 but WITHOUT ANY WARRANTY; without even the implied warranty of
54 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
55 GNU General Public License for more details.
56
57 You should have received a copy of the GNU General Public License
58 along with darktable. If not, see <http://www.gnu.org/licenses/>.
59*/
60#include "common/darktable.h"
61#include <assert.h>
62#include <stddef.h>
63#include <glib/gprintf.h>
64#include <inttypes.h>
65#include <math.h>
66#include <stdint.h>
67#include <stdlib.h>
68#include <string.h>
69#include <strings.h>
70#include <unistd.h>
71
72#include "common/atomic.h"
73#include "common/datetime.h"
74#include "common/debug.h"
75#include "common/history.h"
76#include "common/image_cache.h"
77#include "common/imageio.h"
78#include "common/mipmap_cache.h"
79#include "common/opencl.h"
80#include "common/tags.h"
81#include "control/conf.h"
82#include "control/control.h"
83#include "control/signal.h"
84#include "control/jobs.h"
85#include "develop/blend.h"
86#include "develop/develop.h"
87#include "develop/imageop.h"
88#include "develop/lightroom.h"
89#include "develop/masks.h"
91#include "gui/gtk.h"
92#include "gui/gui_throttle.h"
93#include "gui/presets.h"
94#include "libs/colorpicker.h"
95
96#define DT_IOP_ORDER_INFO (darktable.unmuted & DT_DEBUG_IOPORDER)
97
99{
100 GList *res = NULL;
101 dt_iop_module_t *module;
102 dt_iop_module_so_t *module_so;
103 GList *iop = g_list_first(darktable.iop);
104 while(iop)
105 {
106 module_so = (dt_iop_module_so_t *)iop->data;
107 module = (dt_iop_module_t *)calloc(1, sizeof(dt_iop_module_t));
108 if(dt_iop_load_module_by_so(module, module_so, dev))
109 {
110 dt_free(module);
111 continue;
112 }
113 res = g_list_insert_sorted(res, module, dt_sort_iop_by_order);
114 module->global_data = module_so->data;
115 module->so = module_so;
116 iop = g_list_next(iop);
117 }
118
119 GList *it = res;
120 while(it)
121 {
122 module = (dt_iop_module_t *)it->data;
123 it = g_list_next(it);
124 }
125 return res;
126}
127
128void dt_dev_init(dt_develop_t *dev, int32_t gui_attached)
129{
130 memset(dev, 0, sizeof(dt_develop_t));
133 dt_pthread_rwlock_set_name(&dev->history_mutex, "history_mutex"); // find_history_mutex_blocker, temporary
136
137 dev->gui_attached = gui_attached;
138 dev->roi.width = -1;
139 dev->roi.height = -1;
140
142
143 if(dev->gui_attached)
144 {
145 dev->pipe = (dt_dev_pixelpipe_t *)malloc(sizeof(dt_dev_pixelpipe_t));
146 dev->preview_pipe = (dt_dev_pixelpipe_t *)malloc(sizeof(dt_dev_pixelpipe_t));
147 // Virtual pipe mirrors preview_pipe for geometry, but is never processed.
148 dev->virtual_pipe = (dt_dev_pixelpipe_t *)malloc(sizeof(dt_dev_pixelpipe_t));
149 dt_dev_pixelpipe_init(dev->pipe, dev);
152 dev->histogram_pre_tonecurve = (uint32_t *)calloc(4 * 256, sizeof(uint32_t));
153 dev->histogram_pre_levels = (uint32_t *)calloc(4 * 256, sizeof(uint32_t));
154
155 // FIXME: these are uint32_t, setting to -1 is confusing
157 dev->histogram_pre_levels_max = -1;
158 }
159
160 dt_dev_set_backbuf(&dev->raw_histogram, 0, 0, 0, -1, -1);
161 dt_dev_set_backbuf(&dev->output_histogram, 0, 0, 0, -1, -1);
162 dt_dev_set_backbuf(&dev->display_histogram, 0, 0, 0, -1, -1);
163
164 dev->proxy.wb_is_D65 = TRUE; // don't display error messages until we know for sure it's FALSE
165 dev->proxy.wb_coeffs[0] = 0.f;
166
167 // Overlay toolbar prefs have been found corrupted on disk (heap-looking int garbage, zeroed
168 // floats). An out-of-enum mode/colorscheme indexes the fixed-size color tables of
169 // iop/rawoverexposed.c out of bounds and selects diverging CPU/OpenCL preview paths (the CL
170 // switch defaults to false color), painting the whole image as a CFA checkerboard. An invalid
171 // enum means the whole stored group is untrustworthy: restore the group defaults instead of
172 // clamping, so a corrupted threshold does not survive as a legal-looking value.
173 const int raw_mode = dt_conf_get_int("darkroom/ui/rawoverexposed/mode");
174 const int raw_colorscheme = dt_conf_get_int("darkroom/ui/rawoverexposed/colorscheme");
175 const float raw_threshold = dt_conf_get_float("darkroom/ui/rawoverexposed/threshold");
177 && raw_colorscheme >= DT_DEV_RAWOVEREXPOSED_RED && raw_colorscheme <= DT_DEV_RAWOVEREXPOSED_BLACK
178 && raw_threshold >= 0.f && raw_threshold <= 2.f)
179 {
180 dev->rawoverexposed.mode = raw_mode;
181 dev->rawoverexposed.colorscheme = raw_colorscheme;
182 dev->rawoverexposed.threshold = raw_threshold;
183 }
184 else
185 {
188 dev->rawoverexposed.threshold = 1.f;
189 }
190
191 const int over_mode = dt_conf_get_int("darkroom/ui/overexposed/mode");
192 const int over_colorscheme = dt_conf_get_int("darkroom/ui/overexposed/colorscheme");
193 const float over_lower = dt_conf_get_float("darkroom/ui/overexposed/lower");
194 const float over_upper = dt_conf_get_float("darkroom/ui/overexposed/upper");
195 if(over_mode >= DT_CLIPPING_PREVIEW_GAMUT && over_mode <= DT_CLIPPING_PREVIEW_SATURATION
196 && over_colorscheme >= DT_DEV_OVEREXPOSED_BLACKWHITE && over_colorscheme <= DT_DEV_OVEREXPOSED_PURPLEGREEN
197 && over_lower >= -32.f && over_lower <= -4.f && over_upper >= 0.f && over_upper <= 100.f)
198 {
199 dev->overexposed.mode = over_mode;
200 dev->overexposed.colorscheme = over_colorscheme;
201 dev->overexposed.lower = over_lower;
202 dev->overexposed.upper = over_upper;
203 }
204 else
205 {
208 dev->overexposed.lower = -12.69f;
209 dev->overexposed.upper = 99.99f;
210 }
211
212 if(dev->gui_attached)
213 {
214 dev->color_picker.primary_sample = g_malloc0(sizeof(dt_colorpicker_sample_t));
215 dev->color_picker.display_samples = dt_conf_get_bool("ui_last/colorpicker_display_samples");
217 dev->color_picker.restrict_histogram = dt_conf_get_bool("ui_last/colorpicker_restrict_histogram");
218 }
219
220 dt_dev_reset_roi(dev);
221
222 dev->iop = dt_dev_load_modules(dev);
223}
224
226{
227 if(IS_NULL_PTR(dev)) return;
228 // image_cache does not have to be unref'd, this is done outside develop module.
229
231
232 dev->proxy.chroma_adaptation = NULL;
233 dev->proxy.wb_coeffs[0] = 0.f;
234 if(dev->pipe)
235 {
237 dt_free(dev->pipe);
238 }
239 if(dev->preview_pipe)
240 {
242 dt_free(dev->preview_pipe);
243 }
244 if(dev->virtual_pipe)
245 {
246 // Virtual pipe has nodes and committed params but no pixel buffers.
248 dt_free(dev->virtual_pipe);
249 }
250
252 while(dev->history)
253 {
255 dev->history = g_list_delete_link(dev->history, dev->history);
256 }
259
260 // free pending "before" snapshots for history undo
261 dev->undo_history_depth = 0;
267
268 // free the transient param channel
271 dev->transient_params.params = NULL;
273 dev->transient_params.blend_params = NULL;
274 dev->transient_params.module = NULL;
277
278 while(dev->iop)
279 {
281 dt_free(dev->iop->data);
282 dev->iop = g_list_delete_link(dev->iop, dev->iop);
283 }
284 while(dev->alliop)
285 {
287 dt_free(dev->alliop->data);
288 dev->alliop = g_list_delete_link(dev->alliop, dev->alliop);
289 }
290 g_list_free_full(dev->iop_order_list, dt_free_gpointer);
291 dev->iop_order_list = NULL;
292 while(dev->allprofile_info)
293 {
295 dt_free_align(dev->allprofile_info->data);
296 dev->allprofile_info->data = NULL;
297 dev->allprofile_info = g_list_delete_link(dev->allprofile_info, dev->allprofile_info);
298 }
299
302
304 {
306 dev->color_picker.primary_sample = NULL;
307 }
308
310 // dev->forms and dev->allforms are independent claims on possibly-shared objects
311 // (dt_masks_append_form/dt_masks_create_ext each take their own reference): release both,
312 // do not unconditionally free -- a form referenced by both only reaches refcount 0 once.
313 g_list_free_full(dev->forms, (void (*)(void *))dt_masks_form_unref);
314 dev->forms = NULL;
315 g_list_free_full(dev->allforms, (void (*)(void *))dt_masks_form_unref);
316 dev->allforms = NULL;
318
320
321 // Overlay toolbar prefs may only be persisted by a gui_attached develop: transient devs
322 // (export, styles, snapshots, history copies) never expose the toolbar, run on worker
323 // threads, and would keep re-writing these keys on every background job — one corrupted
324 // or racing write is then resurrected as the user's settings at every later startup.
325 if(dev->gui_attached)
326 {
327 dt_conf_set_int("darkroom/ui/rawoverexposed/mode", dev->rawoverexposed.mode);
328 dt_conf_set_int("darkroom/ui/rawoverexposed/colorscheme", dev->rawoverexposed.colorscheme);
329 dt_conf_set_float("darkroom/ui/rawoverexposed/threshold", dev->rawoverexposed.threshold);
330
331 dt_conf_set_int("darkroom/ui/overexposed/mode", dev->overexposed.mode);
332 dt_conf_set_int("darkroom/ui/overexposed/colorscheme", dev->overexposed.colorscheme);
333 dt_conf_set_float("darkroom/ui/overexposed/lower", dev->overexposed.lower);
334 dt_conf_set_float("darkroom/ui/overexposed/upper", dev->overexposed.upper);
335 }
336}
337
338static gboolean _update_darkroom_roi(dt_develop_t *dev, dt_dev_pixelpipe_t *pipe, int *x, int *y, int *wd, int *ht,
339 float *scale);
340
342{
343 if(IS_NULL_PTR(dev)) return FALSE;
344
345 return dev->image_storage.id > 0
346 && dev->roi.width >= 32
347 && dev->roi.height >= 32
348 && dev->roi.raw_width >= 32
349 && dev->roi.raw_height >= 32
350 && dev->roi.processed_width >= 32
351 && dev->roi.processed_height >= 32
352 && dev->roi.preview_width >= 32
353 && dev->roi.preview_height >= 32;
354}
355
357{
358 if(!dev->roi.raw_inited || !dev->roi.gui_inited) return 1;
359
360 // Keep the virtual pipe synced so ROI computations on the GUI thread
361 // always use up-to-date history and input sizes.
362 if(dev->virtual_pipe->imgid != dev->image_storage.id
363 || dev->virtual_pipe->iwidth != dev->roi.raw_width
364 || dev->virtual_pipe->iheight != dev->roi.raw_height
367 dev->roi.raw_width, dev->roi.raw_height, 1.0f, DT_MIPMAP_FULL);
368
369 if(!dev->virtual_pipe->nodes)
372 {
375 else
377 }
378
381
382 // Compute the virtual full-res output. This needs an inited history
385
386 // Compute the scaling factor that makes full-res output fit within widget
388
389 // The preview backbuffer and the pipeline ROI both live in raster pixels.
390 // `natural_scale` therefore directly maps the processed image size to the
391 // raster backbuffer size, without any GUI-density factor mixed in.
392 // Use roundf() — NOT a plain (int) truncation — so these match the ROI the
393 // worker actually requests in `_update_darkroom_roi()` (which rounds too) and
394 // therefore the size of the backbuffer the pipe produces. A truncation here
395 // disagreed with that rounding by 1px whenever the fractional part was >= 0.5,
396 // which is image-dependent: it silently broke `dt_dev_pixelpipe_has_preview_output()`
397 // (hence ashift structure detection / drawing) on some images but not others,
398 // and resetting the module to neutral did not help because the mismatch does
399 // not depend on the module parameters at all.
400 dev->roi.preview_width = roundf(dev->roi.natural_scale * dev->roi.processed_width);
401 dev->roi.preview_height = roundf(dev->roi.natural_scale * dev->roi.processed_height);
402 dev->roi.output_inited = TRUE;
403
405
407 "[pixelpipe] thumbnail sizes raw %dx%d -> processed %dx%d -> preview %dx%d (scale %.5f)\n",
410
411 return 0;
412}
413
415 const dt_iop_roi_t *roi)
416{
417 if(IS_NULL_PTR(dev) || IS_NULL_PTR(pipe) || !dev->gui_attached || !dev->roi.output_inited) return FALSE;
418
419 int x = 0;
420 int y = 0;
421 int width = 0;
422 int height = 0;
423 float scale = dev->roi.natural_scale;
424
425 if(!IS_NULL_PTR(roi))
426 {
427 x = roi->x;
428 y = roi->y;
429 width = roi->width;
430 height = roi->height;
431 scale = roi->scale;
432 }
433 else
434 {
435 // Recompute the current darkroom output geometry so callers that run ahead of process()
436 // still classify the pipe from the image they are about to produce, not the last backbuffer.
437 _update_darkroom_roi((dt_develop_t *)dev, (dt_dev_pixelpipe_t *)pipe, &x, &y, &width, &height, &scale);
438 }
439
440 // A module upstream of the orientation swap (the "flip" module) — e.g. ashift, demosaic,
441 // highlights — produces output whose width/height are swapped relative to the final, post-flip
442 // preview dimensions on portrait images. Accept that swapped match too: otherwise the
443 // "is this the full preview image?" test wrongly fails for every pre-flip module on portrait,
444 // and `roi` here is the module's own (pre-flip) `roi_out`. This is why ashift never captured its
445 // GUI buffer on portrait images, breaking structure detection and manual drawing (#710).
446 //
447 // Tolerate a couple of pixels of slack on the dimensions. `dev->roi.preview_*` is derived from the
448 // virtual pipe at scale 1.0, whereas `roi` is produced at `natural_scale`; geometric modules
449 // (ashift, lens) round their transformed bounding box with floorf() independently at each scale,
450 // so the two legitimately disagree by ~1px for the very same full image. The real discriminators
451 // are the origin and scale tests below: a zoomed or panned ROI has a non-zero x/y and a scale
452 // strictly greater than natural_scale, so loosening the size match cannot misclassify those.
453 const int tol = 2;
454 const gboolean dims_match
455 = (abs(width - dev->roi.preview_width) <= tol && abs(height - dev->roi.preview_height) <= tol)
456 || (abs(width - dev->roi.preview_height) <= tol && abs(height - dev->roi.preview_width) <= tol);
457 if(!dims_match) return FALSE;
458 return x == 0 && y == 0 && fabsf(scale - dev->roi.natural_scale) < 1e-4f;
459}
460
461
462// Return TRUE if ROI changed since previous computation
463static gboolean _update_darkroom_roi(dt_develop_t *dev, dt_dev_pixelpipe_t *pipe, int *x, int *y, int *wd, int *ht,
464 float *scale)
465{
466 if(!dev->roi.output_inited) return 1;
467
468 // Store previous values
469 int x_old = *x;
470 int y_old = *y;
471 int wd_old = *wd;
472 int ht_old = *ht;
473 float old_scale = *scale;
474
475 // roi->scale is the pipeline sampling ratio against the processed image and
476 // therefore excludes the GUI backing-store density.
477 *scale = dev->roi.natural_scale;
478 const gboolean preview_pipe = (pipe == dev->preview_pipe);
479 if(!preview_pipe) *scale *= dev->roi.scaling;
480
481 // Width, height, x and y are already expressed in raster pixels, so they
482 // must follow the same raster-space sampling ratio as roi->scale.
483 int roi_width = roundf(*scale * dev->roi.processed_width);
484 int roi_height = roundf(*scale * dev->roi.processed_height);
485 int widget_wd = dev->roi.width;
486 int widget_ht = dev->roi.height;
487
488 *wd = roundf(fminf(roi_width, widget_wd));
489 *ht = roundf(fminf(roi_height, widget_ht));
490
491 // dev->roi.x,y are the relative coordinates of the ROI center.
492 // in preview pipe, we always render a full image, so x,y = 0,0
493 // otherwise, x,y here are the top-left corner. Translate:
494 *x = preview_pipe ? 0 : roundf(dev->roi.x * roi_width - *wd * .5f);
495 *y = preview_pipe ? 0 : roundf(dev->roi.y * roi_height - *ht * .5f);
496
497/* fprintf (stderr, "_update_darkroom_roi: dev %.2f %.2f type %s xy %d %d dim %d %d"
498 " ppd:%.4f scale:%.4f nat_scale:%.4f * scaling:%.4f\n",
499 dev->roi.x, dev->roi.y, dt_pipe_type_to_str(pipe->type), *x, *y, *wd, *ht, darktable.gui->ppd, *scale, dev->roi.natural_scale, dev->roi.scaling);
500*/
501 return x_old != *x || y_old != *y || wd_old != *wd || ht_old != *ht || old_scale != *scale;
502}
503
505{
506 if(IS_NULL_PTR(dev) || !dev->gui_attached || IS_NULL_PTR(dev->pipe) || IS_NULL_PTR(dev->preview_pipe) || !dev->roi.output_inited) return FALSE;
507
508 float preview_scale = 1.0f;
509 float main_scale = 1.0f;
510 int preview_x = 0, preview_y = 0, preview_wd = 0, preview_ht = 0;
511 int main_x = 0, main_y = 0, main_wd = 0, main_ht = 0;
512
513 _update_darkroom_roi(dev, dev->preview_pipe, &preview_x, &preview_y, &preview_wd, &preview_ht, &preview_scale);
514 _update_darkroom_roi(dev, dev->pipe, &main_x, &main_y, &main_wd, &main_ht, &main_scale);
515
516 return preview_x == main_x && preview_y == main_y && preview_wd == main_wd && preview_ht == main_ht
517 && fabsf(preview_scale - main_scale) < 1e-4f;
518}
519
520
522{
524 const int32_t imgid = pipe->dev->image_storage.id;
525
526 // Get the mip size that is at most as big as our pipeline backbuf
528
529 // Flush backup to mipmap_cache. This runs after dt_dev_pixelpipe_process() released the OpenCL device
530 // lock, so we must NOT pass pipe->devid (now stale/unlocked): a device-only payload would otherwise be
531 // materialized from the GPU without owning it. The final display backbuffer is always host-resident,
532 // so preferred_devid = -1 returns it directly; anything else is simply skipped.
533 uint8_t *data = NULL;
534 dt_pixel_cache_entry_t *entry = NULL;
536 && data)
537 {
542 }
543 else if(entry)
544 {
546 }
547}
548
549gboolean _resync_pipe_with_history(dt_develop_t *dev, dt_dev_pixelpipe_t *pipe, dt_iop_roi_t *roi, gboolean *needs_update)
550{
551 // When in realtime mode, preview pipe gets paused at the benefit of main pipeline.
552 // This is a transient state.
553 if(pipe->pause) return FALSE;
554
555 // We recompute if history hash changed or ROI has changed.
556 // If we know history changed, ensure at least the last step is resynced.
557 const uint64_t pipe_hash = dt_dev_pixelpipe_get_history_hash(pipe);
558 const uint64_t dev_hash = dt_dev_get_history_hash(dev);
559 if(pipe_hash != dev_hash)
560 {
562 dt_print(DT_DEBUG_PIPE | DT_DEBUG_DEV, "dev history hash = %" PRIu64 ", pipe history hash %" PRIu64 "\n", dev_hash, pipe_hash);
563 }
564
565 *needs_update = (dt_dev_pixelpipe_get_changed(pipe) != DT_DEV_PIPE_UNCHANGED);
566 if(!*needs_update) return FALSE;
567
569 pipe->processing = 1;
570
571 // Commit history to pipeline.
572 // This can take 40-80 ms or much more with masks on weak hardware,
573 // so user may have changed the history again during that lapse.
574 gboolean pipe_resynced = FALSE;
576 {
578 pipe_resynced = TRUE;
579 }
580
581 // Plan the ROI for this run and finalize the cumulative global hash now, while the pipe is
582 // settled and not yet publishing pixels. dt_dev_pixelpipe_process() recomputes both at its
583 // entry (it is also called directly by export/snapshot pipes), but computing them here is
584 // what lets the HISTORY_RESYNC signal below advertise a hash that is already final.
585 int x = 0, y = 0, wd = 0, ht = 0;
586 float scale = 1.f;
587 _update_darkroom_roi(dev, pipe, &x, &y, &wd, &ht, &scale);
588 *roi = (dt_iop_roi_t){ x, y, wd, ht, scale };
589 dt_dev_pixelpipe_get_roi_in(pipe, *roi);
591
592 pipe->processing = 0;
594
595 return pipe_resynced;
596}
597
598
611{
612 dt_dev_pixelpipe_t *const pipes[] = { dev->preview_pipe, dev->pipe };
613
614 for(size_t i = 0; i < G_N_ELEMENTS(pipes); i++)
615 dt_atomic_set_int(&pipes[i]->running, TRUE);
616
617 // Infinite loop: run for as long as the worker thread is running.
618 while(!dev->exit && dt_control_running())
619 {
621 {
622 dt_iop_nap(50000); // wait 50 ms until GUI/image sizes are initialized
623 continue;
624 }
625
626 // This is cheap to run, keep it in sync always.
627 for(size_t i = 0; i < G_N_ELEMENTS(pipes); i++)
629 1.0f, DT_MIPMAP_FULL);
630
631 gboolean pipe_needs_update[G_N_ELEMENTS(pipes)] = { FALSE };
632 dt_iop_roi_t pipe_roi[G_N_ELEMENTS(pipes)] = { { 0 } };
633 gboolean history_resynced = FALSE;
634
635 // First, resynchronize all dirty pipelines from history, plan their ROI and finalize their
636 // cumulative global hash, so GUI listeners can resolve stable piece->global_hash values
637 // before any cacheline starts publishing pixels.
638 for(size_t i = 0; i < G_N_ELEMENTS(pipes); i++)
639 history_resynced |= _resync_pipe_with_history(dev, pipes[i], &pipe_roi[i], &pipe_needs_update[i]);
640
641 // NOTE: at this point, we fully know the state of __all__ our GUI pipelines :
642 // - global image input and output size,
643 // - per-module input and output size,
644 // - per-module input and output format (channels, bit depth, mosaiced/raster, CFA pattern)
645 // - pipeline nodes (modules) params are up-to-to date with history,
646 // - global_hash of each module is and stable until next history resync,
647 // - modules whose expected input format is incompatible with previous module output format
648 // will have been disabled from pipeline, but not from history, aka we know our pipelines
649 // can run start to end.
650
651 // GUI widgets that need an image buffer will connect to this signal to grab
652 // the global_hash of the module they are waiting for, even though it's still not ready.
653 if(history_resynced)
655
656 // Second, compute pipelines.
657 // Always service preview first, then the main pipe, so the main pipe can reuse the cache state
658 // just published by preview instead of trying to race it from another thread.
659 for(size_t i = 0; i < G_N_ELEMENTS(pipes) && dt_control_running() && !dev->exit; i++)
660 {
661 if(!pipe_needs_update[i]) continue;
662
663 dt_dev_pixelpipe_t *pipe = pipes[i];
664 const dt_iop_roi_t roi = pipe_roi[i];
665
666 // The resync stage above synchronized history, planned the ROI and advertised the matching
667 // final global hash. We process the exact state it committed, using pipe_roi[i]: that is what
668 // dt_dev_pixelpipe_process() recomputes its hash from, so the cacheline it publishes always
669 // carries the hash already announced to GUI consumers. A change that landed after the resync
670 // (zoom/pan, a fresh history commit) does NOT invalidate this run — it always also raised the
671 // killswitch (`_change_pipe()` sets shutdown together with the changed flag), so the run below
672 // aborts cleanly and the next loop iteration resyncs, re-advertises and reprocesses the new
673 // state. We must NOT skip on a set changed flag here: every darkroom configure-event flags the
674 // preview pipe ZOOMED, so on an image switch (which re-lays-out the view) skipping would starve
675 // the preview and leave navigation/scopes blank.
676 dt_print(DT_DEBUG_PIPE | DT_DEBUG_DEV, "PIPE %s needs update\n", pipe->type == DT_DEV_PIXELPIPE_FULL ? "full" : "preview");
677
679 pipe->processing = 1;
680
681 // We are starting fresh, reset the killswitch signal.
683
691 const gboolean retrying_raster_mask = dt_dev_pixelpipe_has_reentry(pipe);
692
693 // Whether the recompute was triggered because we needed only the output of
694 // a specified module, or we needed the output backbuf of the whole pipeline.
695 // This allows partial pipeline runs, e.g. for histograms and color-pickers.
697
698 // At zoom == fit, both preview and main pipelines have the same size,
699 // so the first one that runs will prevent the next from running
700 // (backbuf fetched directly from pipeline cache).
701 // Therefore we can't rely solely on pipeline type to raise completion signals.
702 const gboolean has_preview_size = dt_dev_pixelpipe_has_preview_output(dev, pipe, &roi);
703
709 const gboolean requested_mask_preview
710 = pipe == dev->pipe
711 && !IS_NULL_PTR(dev->gui_module)
713
714 // Connect GUI feedback for "pipe busy"
717 dev->progress.completed = 0;
718 dev->progress.total = 0;
719
720 dt_times_t thread_start;
721 dt_get_times(&thread_start);
722
723 // The actual processing with runtime log
724 const gint64 process_start_us = g_get_monotonic_time();
725 const int ret = dt_dev_pixelpipe_process(pipe, roi);
726 const gint64 process_runtime_us = g_get_monotonic_time() - process_start_us;
727
728 // Print perf log
729 gchar *msg = g_strdup_printf("[dev_process_%s] pipeline processing thread",
731 dt_show_times(&thread_start, msg);
732 dt_free(msg);
733
734 // Disconnect GUI feedback for "pipe busy"
735 dev->progress.completed = 0;
736 dev->progress.total = 0;
739
740 // Pipeline completed entirely without error
741 const gboolean processed = (!ret && !dt_atomic_get_int(&pipe->shutdown));
742
750 const gboolean published_backbuffer
751 = processed && dt_dev_pixelpipe_is_backbufer_valid(pipe);
752
753 // Pipeline reentry flag is set when we lost the reference to a raster mask.
754 // This typically happens on re-entering darkroom after having gone to lighttable:
755 // the pipeline cache is kept but the references to raster masks are flushed,
756 // so the pipeline recomputation resumes downstream from the last-known cacheline,
757 // which may not refresh raster masks if produced upstream in pipeline.
758 // TODO: cache raster masks too (was attempted before, and failed).
760 {
761 if(retrying_raster_mask)
762 {
763 // Reentry flag was set already before last pipe run, which refreshed
764 // everything we needed. We can resume to normal mode.
765 // In case that wasn't true, it will be caught at the next run.
767 }
768 else
769 {
770 // Reentry flag was set during the last pipe run, which means we
771 // lost at least a raster mask reference, and need to retry again
772 // from the start.
773 // The synchronized graph and its ROIs are still valid. The retry
774 // only needs another processing pass after targeted cache
775 // invalidation, not node destruction and history reconstruction.
777 }
778 }
779
788 if(processed
789 && cache_request == DT_DEV_PIXELPIPE_CACHE_REQUEST_MODULE
790 && !published_backbuffer)
791 {
795 }
796
797 pipe->processing = 0;
799
800 // Update the running average of process time for GUI controls thresholding
801 if(processed)
802 dt_gui_throttle_record_runtime(pipe, process_runtime_us);
803
804 // If everything went well, yell to GUI listeners that they can use the output buffer.
805 if(published_backbuffer)
806 {
807 if(pipe->type == DT_DEV_PIXELPIPE_FULL)
808 {
811 }
812 if(pipe->type == DT_DEV_PIXELPIPE_PREVIEW || has_preview_size)
813 {
816 }
817
829 && has_preview_size
830 && !requested_mask_preview
832 dt_dev_resync_mipmap_cache(dev, pipe, roi);
833 }
834
835 // Allow some breathing room to the OS and GPU
836 dt_iop_nap(10000); // 10 ms
837 }
838
840 dt_iop_nap(10000);
841 else
842 dt_iop_nap(50000);
843 }
844
845 for(size_t i = 0; i < G_N_ELEMENTS(pipes); i++)
846 dt_atomic_set_int(&pipes[i]->running, FALSE);
847}
848
850{
853 return 0;
854}
855
857{
858 dt_job_t *job = dt_control_job_create(&dt_dev_process_job_run, "develop process image");
859 if(IS_NULL_PTR(job)) return NULL;
860 dt_control_job_set_params(job, dev, NULL);
861 return job;
862}
863
870
871static gboolean _dt_dev_mipmap_prefetch_full(dt_develop_t *dev, const int32_t imgid)
872{
875
876 const gboolean ok = (!IS_NULL_PTR(buf.buf)) && buf.width != 0 && buf.height != 0;
877
878 if(dev->gui_attached)
879 {
880 dev->roi.raw_height = buf.height;
881 dev->roi.raw_width = buf.width;
882 dev->roi.raw_inited = TRUE;
883 }
884
886
887 return ok;
888}
889
890static gboolean _dt_dev_refresh_image_storage(dt_develop_t *dev, const int32_t imgid)
891{
892 const dt_image_t *image = dt_image_cache_get(darktable.image_cache, imgid, 'r');
893 if(IS_NULL_PTR(image)) return FALSE;
894 dev->image_storage = *image;
897 return TRUE;
898}
899
913
914// load the raw and get the new image struct, blocking in gui thread
915static inline dt_dev_image_storage_t _dt_dev_load_raw(dt_develop_t *dev, const int32_t imgid)
916{
917 // then load the raw
918 dt_times_t start;
919 dt_get_times(&start);
920
921 // Test we got images. Also that populates the cache for later.
922 // Refresh our private copy in case raw loading updated image metadata
923 const dt_dev_image_storage_t storage_status = dt_dev_ensure_image_storage(dev, imgid);
924 if(storage_status)
925 return storage_status;
926
927 dt_show_times_f(&start, "[dev_pixelpipe]", "to load the image.");
928
929 return storage_status;
930}
931
932// return the zoom scale to fit into the viewport
933float dt_dev_get_zoom_scale(const dt_develop_t *dev, const gboolean preview)
934{
935 const float w = preview ? dev->roi.processed_width : dev->pipe->processed_width;
936 const float h = preview ? dev->roi.processed_height : dev->pipe->processed_height;
937 return fminf(dev->roi.width / w, dev->roi.height / h);
938}
939
941{
942 const dt_dev_image_storage_t ret = _dt_dev_load_raw(dev, imgid);
943 if(ret) return ret;
944
945 // we need a global lock as the dev->iop set must not be changed until read history is terminated
947
948 const gboolean first_run = dt_dev_read_history_ext(dev, imgid);
949
950 if(first_run && dev == darktable.develop)
951 {
952 // Resync our private copy of image image with DB,
953 // mostly for DT_IMAGE_AUTO_PRESETS_APPLIED flag.
955 if(!IS_NULL_PTR(image))
956 {
957 *image = dev->image_storage;
959 }
960
961 dt_dev_write_history_ext(dev, imgid);
962 }
963
965
966 if(first_run && dev == darktable.develop)
967 {
970 }
971
972 return ret;
973}
974
975void dt_dev_configure_real(dt_develop_t *dev, int wd, int ht)
976{
977 // Called only from Darkroom to convert the widget allocation into the
978 // raster ROI contract consumed by the pipeline. Everything stored in
979 // dev->roi below is expressed in real buffer pixels.
980 const dt_iop_roi_t gui_roi = { .x = 0, .y = 0, .width = wd, .height = ht, .scale = 1.0f };
981 dt_iop_roi_t pipe_roi = { 0 };
983 dev->roi.width = pipe_roi.width;
984 dev->roi.height = pipe_roi.height;
985 dev->roi.gui_inited = TRUE;
986
988 "[pixelpipe] Darkroom requested a %i×%i px widget -> %i×%i px raster preview\n",
989 wd, ht, dev->roi.width, dev->roi.height);
990
995}
996
997void dt_dev_check_zoom_pos_bounds(dt_develop_t *dev, float *dev_x, float *dev_y, float *box_w, float *box_h)
998{
999 // for the debug strings lower
1000 //float old_x = *dev_x;
1001 //float old_y = *dev_y;
1002 int proc_w = 0;
1003 int proc_h = 0;
1004 dt_dev_get_processed_size(dev, &proc_w, &proc_h);
1005 const float scale = dt_dev_get_zoom_level(dev);
1006
1007 // find the box size
1008 const float bw = dev->roi.width / (proc_w * scale);
1009 const float bh = dev->roi.height / (proc_h * scale);
1010
1011 // calculate half-dimensions once
1012 const float half_bw = bw * 0.5f;
1013 const float half_bh = bh * 0.5f;
1014
1015 // clamp position using pre-calculated values
1016 *dev_x = (bw > 1.0f || dev->roi.scaling <= 1.0f) ? 0.5f : CLAMPF(*dev_x, half_bw, 1.0f - half_bw);
1017 *dev_y = (bh > 1.0f || dev->roi.scaling <= 1.0f) ? 0.5f : CLAMPF(*dev_y, half_bh, 1.0f - half_bh);
1018 // return box size
1019 if(!IS_NULL_PTR(box_w)) *box_w = bw;
1020 if(!IS_NULL_PTR(box_h)) *box_h = bh;
1021
1022 /*
1023 fprintf(stdout, "BOUNDS: box size: %2.2f x %2.2f\n", bw, bh);
1024 fprintf(stdout, "BOUNDS: half box size: %2.2f x %2.2f\n", half_bw, half_bh);
1025 fprintf(stdout, "BOUNDS: X pos: %2.2f -> %2.2f [%2.2f %2.2f]\n",
1026 old_x, *dev_x, half_bw, 1.0f - half_bw);
1027 fprintf(stdout, "BOUNDS: Y pos: %2.2f -> %2.2f [%2.2f %2.2f]\n",
1028 old_y, *dev_y, half_bh, 1.0f - half_bh);
1029*/
1030}
1031
1032void dt_dev_get_processed_size(const dt_develop_t *dev, int *procw, int *proch)
1033{
1034 if(IS_NULL_PTR(dev)) return;
1035 *procw = dev->roi.processed_width;
1036 *proch = dev->roi.processed_height;
1037 }
1038
1039void dt_dev_coordinates_widget_delta_to_image_delta(dt_develop_t *dev, float *points, size_t num_points)
1040{
1041 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1042
1043 const float scale = dt_dev_get_zoom_level(dev) / darktable.gui->ppd;
1044 if(scale == 0.0f) return;
1045
1046 // Widget deltas are measured in Gtk logical pixels. Convert them to processed-image
1047 // pixels here so dragging thresholds and keyboard pans share the same zoom math.
1048 for(size_t i = 0; i < num_points; ++i)
1049 {
1050 const size_t idx = i * 2;
1051 points[idx + 0] /= scale;
1052 points[idx + 1] /= scale;
1053 }
1054}
1055
1056void dt_dev_coordinates_widget_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
1057{
1058 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1059 const float processed_width = dev->roi.processed_width;
1060 const float processed_height = dev->roi.processed_height;
1061 if(processed_width == 0.0f || processed_height == 0.0f) return;
1062
1063 // Widget events are expressed in GUI logical coordinates, while the pipeline
1064 // zoom lives in raster pixels. Convert back to the same GUI-space zoom used
1065 // by dt_dev_rescale_roi() so event hit-testing and overlay drawing stay aligned.
1066 const float scale = dt_dev_get_zoom_level(dev) / darktable.gui->ppd;
1067 const float roi_x = (float)dev->roi.x;
1068 const float roi_y = (float)dev->roi.y;
1069 const float center_x = 0.5f * (float)dev->roi.orig_width;
1070 const float center_y = 0.5f * (float)dev->roi.orig_height;
1071 const float inv_scaled_width = 1.0f / (processed_width * scale);
1072 const float inv_scaled_height = 1.0f / (processed_height * scale);
1073
1074 for(size_t i = 0; i < num_points; ++i)
1075 {
1076 const size_t idx = i * 2;
1077 const float px = points[idx + 0];
1078 const float py = points[idx + 1];
1079 points[idx + 0] = roi_x + (px - center_x) * inv_scaled_width;
1080 points[idx + 1] = roi_y + (py - center_y) * inv_scaled_height;
1081 }
1082}
1083
1084void dt_dev_coordinates_image_norm_to_widget(dt_develop_t *dev, float *points, size_t num_points)
1085{
1086 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1087 const float processed_width = dev->roi.processed_width;
1088 const float processed_height = dev->roi.processed_height;
1089 if(processed_width == 0.0f || processed_height == 0.0f) return;
1090
1091 // GUI overlays are drawn in logical widget coordinates, so use the same
1092 // GUI-space zoom that the Cairo darkroom transform applies.
1093 const float scale = dt_dev_get_zoom_level(dev) / darktable.gui->ppd;
1094 const float roi_x = (float)dev->roi.x;
1095 const float roi_y = (float)dev->roi.y;
1096 const float scaled_width = processed_width * scale;
1097 const float scaled_height = processed_height * scale;
1098 const float center_x = 0.5f * (float)dev->roi.orig_width;
1099 const float center_y = 0.5f * (float)dev->roi.orig_height;
1100
1101 for(size_t i = 0; i < num_points; ++i)
1102 {
1103 const size_t idx = i * 2;
1104 const float px = points[idx + 0];
1105 const float py = points[idx + 1];
1106 const float dx = (px - roi_x) * scaled_width;
1107 const float dy = (py - roi_y) * scaled_height;
1108 points[idx + 0] = dx + center_x;
1109 points[idx + 1] = dy + center_y;
1110 }
1111}
1112
1113void dt_dev_coordinates_image_norm_to_image_abs(dt_develop_t *dev, float *points, size_t num_points)
1114{
1115 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1116 const float processed_width = dev->roi.processed_width;
1117 const float processed_height = dev->roi.processed_height;
1118 if(processed_width == 0.0f || processed_height == 0.0f) return;
1119
1120 for(size_t i = 0; i < num_points; ++i)
1121 {
1122 const size_t idx = i * 2;
1123 points[idx + 0] *= processed_width;
1124 points[idx + 1] *= processed_height;
1125 }
1126}
1127
1128void dt_dev_coordinates_image_abs_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
1129{
1130 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1131 const float processed_width = dev->roi.processed_width;
1132 const float processed_height = dev->roi.processed_height;
1133 if(processed_width == 0.0f || processed_height == 0.0f) return;
1134
1135 const float inv_width = 1.0f / processed_width;
1136 const float inv_height = 1.0f / processed_height;
1137 for(size_t i = 0; i < num_points; ++i)
1138 {
1139 const size_t idx = i * 2;
1140 points[idx + 0] *= inv_width;
1141 points[idx + 1] *= inv_height;
1142 }
1143}
1144
1145void dt_dev_coordinates_raw_abs_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
1146{
1147 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1148 const float raw_width = dev->roi.raw_width;
1149 const float raw_height = dev->roi.raw_height;
1150 if(raw_width == 0.0f || raw_height == 0.0f) return;
1151
1152 const float inv_width = 1.f / raw_width;
1153 const float inv_height = 1.f / raw_height;
1154 for(size_t i = 0; i < num_points; i++)
1155 {
1156 const size_t idx = i * 2;
1157 points[idx + 0] *= inv_width;
1158 points[idx + 1] *= inv_height;
1159 }
1160}
1161
1162void dt_dev_coordinates_raw_norm_to_raw_abs(dt_develop_t *dev, float *points, size_t num_points)
1163{
1164 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1165 const float raw_width = dev->roi.raw_width;
1166 const float raw_height = dev->roi.raw_height;
1167 if(raw_width == 0.0f || raw_height == 0.0f) return;
1168
1169 for(size_t i = 0; i < num_points; i++)
1170 {
1171 const size_t idx = i * 2;
1172 points[idx + 0] *= raw_width;
1173 points[idx + 1] *= raw_height;
1174 }
1175}
1176
1177void dt_dev_coordinates_image_norm_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
1178{
1179 dt_dev_coordinates_image_norm_to_image_abs(dev, points, num_points);
1180 dt_dev_coordinates_image_abs_to_raw_abs(dev, points, num_points);
1181 dt_dev_coordinates_raw_abs_to_raw_norm(dev, points, num_points);
1182}
1183
1184void dt_dev_coordinates_raw_norm_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
1185{
1186 dt_dev_coordinates_raw_norm_to_raw_abs(dev, points, num_points);
1187 dt_dev_coordinates_raw_abs_to_image_abs(dev, points, num_points);
1188 dt_dev_coordinates_image_abs_to_image_norm(dev, points, num_points);
1189}
1190
1191void dt_dev_coordinates_image_abs_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
1192{
1193 dt_dev_coordinates_image_abs_to_raw_abs(dev, points, num_points);
1194 dt_dev_coordinates_raw_abs_to_raw_norm(dev, points, num_points);
1195}
1196
1197void dt_dev_coordinates_image_norm_to_preview_abs(dt_develop_t *dev, float *points, size_t num_points)
1198{
1199 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1200 const float preview_width = dev->roi.preview_width;
1201 const float preview_height = dev->roi.preview_height;
1202 if(preview_width == 0.0f || preview_height == 0.0f) return;
1203
1204 for(size_t i = 0; i < num_points; i++)
1205 {
1206 const size_t idx = i * 2;
1207 points[idx + 0] *= preview_width;
1208 points[idx + 1] *= preview_height;
1209 }
1210}
1211
1212void dt_dev_coordinates_preview_abs_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
1213{
1214 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1215 const float preview_width = dev->roi.preview_width;
1216 const float preview_height = dev->roi.preview_height;
1217 if(preview_width == 0.0f || preview_height == 0.0f) return;
1218
1219 const float inv_width = 1.f / preview_width;
1220 const float inv_height = 1.f / preview_height;
1221 for(size_t i = 0; i < num_points; i++)
1222 {
1223 const size_t idx = i * 2;
1224 points[idx + 0] *= inv_width;
1225 points[idx + 1] *= inv_height;
1226 }
1227}
1228
1230{
1231 return (dev->image_storage.id == imgid) ? 1 : 0;
1232}
1233
1239
1241{
1242 if(dev->proxy.masks.module && dev->proxy.masks.list_change)
1243 dev->proxy.masks.list_change(dev->proxy.masks.module);
1244}
1246{
1247 if(dev->proxy.masks.module && dev->proxy.masks.list_update)
1248 dev->proxy.masks.list_update(dev->proxy.masks.module);
1249}
1250void dt_dev_masks_list_remove(dt_develop_t *dev, int formid, int parentid)
1251{
1252 if(dev->proxy.masks.module && dev->proxy.masks.list_remove)
1253 dev->proxy.masks.list_remove(dev->proxy.masks.module, formid, parentid);
1254}
1256 const int selectid, const int throw_event)
1257{
1258 if(dev->proxy.masks.module && dev->proxy.masks.selection_change)
1259 dev->proxy.masks.selection_change(dev->proxy.masks.module, module, selectid, throw_event);
1260}
1261
1262void dt_dev_snapshot_request(dt_develop_t *dev, const char *filename)
1263{
1264 dev->proxy.snapshot.filename = filename;
1265 dev->proxy.snapshot.request = TRUE;
1267}
1268
1271{
1272 // we create the new module
1273 dt_iop_module_t *module = (dt_iop_module_t *)calloc(1, sizeof(dt_iop_module_t));
1274 if(dt_iop_load_module(module, base->so, base->dev)) return NULL;
1275 module->instance = base->instance;
1276
1277 // we set the multi-instance priority and the iop order
1278 int pmax = 0;
1279 for(GList *modules = base->dev->iop; modules; modules = g_list_next(modules))
1280 {
1281 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1282 if(mod->instance == base->instance)
1283 {
1284 if(pmax < mod->multi_priority) pmax = mod->multi_priority;
1285 }
1286 }
1287 // create a unique multi-priority
1288 pmax += 1;
1289 dt_iop_update_multi_priority(module, pmax);
1290
1291 // add this new module position into the iop-order-list
1293
1294 // since we do not rename the module we need to check that an old module does not have the same name. Indeed
1295 // the multi_priority
1296 // are always rebased to start from 0, to it may be the case that the same multi_name be generated when
1297 // duplicating a module.
1298 int pname = module->multi_priority;
1299 char mname[128];
1300
1301 do
1302 {
1303 snprintf(mname, sizeof(mname), "%d", pname);
1304 gboolean dup = FALSE;
1305
1306 for(GList *modules = base->dev->iop; modules; modules = g_list_next(modules))
1307 {
1308 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1309 if(mod->instance == base->instance)
1310 {
1311 if(strcmp(mname, mod->multi_name) == 0)
1312 {
1313 dup = TRUE;
1314 break;
1315 }
1316 }
1317 }
1318
1319 if(dup)
1320 pname++;
1321 else
1322 break;
1323 } while(1);
1324
1325 // the multi instance name
1326 g_strlcpy(module->multi_name, mname, sizeof(module->multi_name));
1327 // we insert this module into dev->iop
1328 base->dev->iop = g_list_insert_sorted(base->dev->iop, module, dt_sort_iop_by_order);
1329
1330 // always place the new instance after the base one
1331 if(!dt_ioppr_move_iop_after(base->dev, module, base))
1332 {
1333 fprintf(stderr, "[dt_dev_module_duplicate] can't move new instance after the base one\n");
1334 }
1335
1336 // that's all. rest of insertion is gui work !
1337 return module;
1338}
1339
1340
1341
1343{
1344 // if(dt_gui_widgets_suppressed()) return;
1345 int del = 0;
1346
1347 if(dev->gui_attached)
1348 {
1351
1352 const int history_end = dt_dev_get_history_end_ext(dev);
1353 int removed_before_end = 0;
1354 int history_pos = 0;
1355 GList *elem = dev->history;
1356 while(!IS_NULL_PTR(elem))
1357 {
1358 GList *next = g_list_next(elem);
1359 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(elem->data);
1360
1361 if(module == hist->module)
1362 {
1363 dt_print(DT_DEBUG_HISTORY, "[dt_module_remode] removing obsoleted history item: %s %s %p %p\n",
1364 hist->op_name, hist->multi_name, module, hist->module);
1366 dev->history = g_list_delete_link(dev->history, elem);
1367 if(history_pos < history_end) removed_before_end++;
1368 del = 1;
1369 }
1370 history_pos++;
1371 elem = next;
1372 }
1373
1374 if(removed_before_end > 0)
1375 dt_dev_set_history_end_ext(dev, MAX(0, history_end - removed_before_end));
1376
1380 }
1381
1382 // and we remove it from the list
1383 for(GList *modules = dev->iop; modules; modules = g_list_next(modules))
1384 {
1385 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1386 if(mod == module)
1387 {
1388 dev->iop = g_list_delete_link(dev->iop, modules);
1389 break;
1390 }
1391 }
1392}
1393
1401
1404{
1405 const int nb_instances = GPOINTER_TO_INT(
1406 g_hash_table_lookup(state->instance_counts, GINT_TO_POINTER(module->instance)));
1407
1408 dt_iop_module_t *mod_prev = (dt_iop_module_t *)g_hash_table_lookup(state->prev_visible, module);
1409 dt_iop_module_t *mod_next = (dt_iop_module_t *)g_hash_table_lookup(state->next_visible, module);
1410
1411 const gboolean move_next = mod_next
1412 ? dt_ioppr_check_can_move_after_iop(dev->iop, module, mod_next)
1413 : -1.0;
1414 const gboolean move_prev = mod_prev
1415 ? dt_ioppr_check_can_move_before_iop(dev->iop, module, mod_prev)
1416 : -1.0;
1417
1418 module->multi_show_new = !(module->flags() & IOP_FLAGS_ONE_INSTANCE);
1419 // Never allow deleting the base instance (multi_priority == 0) nor modules limited to one instance.
1420 module->multi_show_close =
1421 (nb_instances > 1 && module->multi_priority > 0 && !(module->flags() & IOP_FLAGS_ONE_INSTANCE));
1422 if(!IS_NULL_PTR(mod_next))
1423 module->multi_show_up = move_next;
1424 else
1425 module->multi_show_up = 0;
1426 if(!IS_NULL_PTR(mod_prev))
1427 module->multi_show_down = move_prev;
1428 else
1429 module->multi_show_down = 0;
1430
1431 // If it's an additional instance supposed to be added by an history item after
1432 // the current history_end cursor, conceptually it doesn't exist yet,
1433 // even though it's dangling there on the pipe. So hide it from GUI.
1434 if(nb_instances > 1
1435 && module->multi_priority > 0
1436 && !g_hash_table_contains(state->modules_in_history, module))
1437 gtk_widget_hide(module->expander);
1438}
1439
1440// FIXME: this function should just disappear, as it mixes concepts from multi-instances from before
1441// pipeline reordering and pipeline reordering.
1442// Multi-instances concept should just be ditched entirely.
1444{
1445 dt_ioppr_check_iop_order(dev, 0, "dt_dev_modules_update_multishow");
1446 const int history_end = dt_dev_get_history_end_ext(dev);
1447
1449 state.instance_counts = g_hash_table_new(g_direct_hash, g_direct_equal);
1450 state.modules_in_history = g_hash_table_new(g_direct_hash, g_direct_equal);
1451 state.prev_visible = g_hash_table_new(g_direct_hash, g_direct_equal);
1452 state.next_visible = g_hash_table_new(g_direct_hash, g_direct_equal);
1453
1454 // Precompute how many instances exist for each base module.
1455 for(GList *modules = dev->iop; modules; modules = g_list_next(modules))
1456 {
1457 const dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1458 gpointer key = GINT_TO_POINTER(mod->instance);
1459 int count = GPOINTER_TO_INT(g_hash_table_lookup(state.instance_counts, key));
1460 g_hash_table_replace(state.instance_counts, key, GINT_TO_POINTER(count + 1));
1461 }
1462
1463 // Precompute which modules exist in history up to history_end.
1464 int history_pos = 0;
1465 for(GList *history = g_list_first(dev->history);
1466 history && history_pos < history_end;
1467 history = g_list_next(history), history_pos++)
1468 {
1469 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)history->data;
1470 if(hist->module) g_hash_table_add(state.modules_in_history, hist->module);
1471 }
1472
1473 // Precompute previous visible module in pipeline order.
1474 dt_iop_module_t *last_visible = NULL;
1475 for(GList *modules = g_list_first(dev->iop); modules; modules = g_list_next(modules))
1476 {
1477 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1479 {
1480 g_hash_table_insert(state.prev_visible, mod, last_visible);
1481 last_visible = mod;
1482 }
1483 }
1484
1485 // Precompute next visible module in GUI order (reverse pipeline).
1486 last_visible = NULL;
1487 for(GList *modules = g_list_last(dev->iop); modules; modules = g_list_previous(modules))
1488 {
1489 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1491 {
1492 g_hash_table_insert(state.next_visible, mod, last_visible);
1493 last_visible = mod;
1494 }
1495 }
1496
1497 for(GList *modules = dev->iop; modules; modules = g_list_next(modules))
1498 {
1499 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1500
1503 }
1504
1505 g_hash_table_destroy(state.instance_counts);
1506 g_hash_table_destroy(state.modules_in_history);
1507 g_hash_table_destroy(state.prev_visible);
1508 g_hash_table_destroy(state.next_visible);
1509}
1510
1511gchar *dt_history_item_get_label(const struct dt_iop_module_t *module)
1512{
1513 gchar *label;
1514 /* create a history button and add to box */
1515 if(!module->multi_name[0] || strcmp(module->multi_name, "0") == 0)
1516 label = g_strdup(module->name());
1517 else
1518 {
1519 // multi_name is free-typed user text (dt_iop_gui_rename_module()), but this label is
1520 // rendered through gtk_label_set_markup_with_mnemonic() so that module->name()'s own
1521 // mnemonic keeps working. Escape it for markup and double its underscores so it prints
1522 // literally instead of breaking markup parsing ("&") or being eaten as a mnemonic ("_").
1523 gchar *escaped_multi_name = g_markup_escape_text(module->multi_name, -1);
1524 gchar **underscore_parts = g_strsplit(escaped_multi_name, "_", -1);
1525 gchar *safe_multi_name = g_strjoinv("__", underscore_parts);
1526 g_strfreev(underscore_parts);
1527 dt_free(escaped_multi_name);
1528
1529 label = g_strdup_printf("%s %s", module->name(), safe_multi_name);
1530 dt_free(safe_multi_name);
1531 }
1532 return label;
1533}
1534
1535gchar *dt_dev_get_multi_name(const struct dt_iop_module_t *module)
1536{
1537 gboolean has_multi_name = g_strcmp0(module->multi_name, "0") != 0 && g_strcmp0(module->multi_name, "") != 0;
1538 gchar *label = has_multi_name ? g_strdup(module->multi_name) : g_strdup("");
1539
1540 return label;
1541}
1542
1544{
1545 gchar *module_multi_name = dt_dev_get_multi_name(module);
1546 gboolean has_multi_name = g_strcmp0(module_multi_name, "") != 0;
1547 // fallback to module name if no multi name
1548 gchar *module_label = has_multi_name ? g_strdup(module_multi_name) : dt_history_item_get_name(module);
1549 gchar *group_name = g_strdup_printf("Mask %s", module_label);
1550 dt_free(module_label);
1551 dt_free(module_multi_name);
1552
1553 return group_name;
1554}
1555
1556
1557gchar *dt_history_item_get_name(const struct dt_iop_module_t *module)
1558{
1559 gchar *label;
1560 /* create a history button and add to box */
1561 if(!module->multi_name[0] || strcmp(module->multi_name, "0") == 0)
1562 label = delete_underscore(module->name());
1563 else
1564 {
1565 gchar *clean_name = delete_underscore(module->name());
1566 label = g_strdup_printf("%s %s", clean_name, module->multi_name);
1567 dt_free(clean_name);
1568 }
1569 dt_capitalize_label(label);
1570 return label;
1571}
1572
1574{
1575 gchar *clean_name = delete_underscore(module->name());
1576 gchar *label;
1577 /* create a history button and add to box */
1578 if(!module->multi_name[0] || strcmp(module->multi_name, "0") == 0)
1579 label = g_markup_escape_text(clean_name, -1);
1580 else
1581 label = g_markup_printf_escaped("%s <span size=\"smaller\">%s</span>", clean_name, module->multi_name);
1582 dt_free(clean_name);
1583 return label;
1584}
1585
1586static int dt_dev_distort_backtransform_locked(const dt_dev_pixelpipe_t *pipe, const double iop_order,
1587 const int transf_direction, float *points, size_t points_count);
1588
1589int dt_dev_coordinates_raw_abs_to_image_abs(dt_develop_t *dev, float *points, size_t points_count)
1590{
1591 return dt_dev_distort_transform_plus(dev->virtual_pipe, 0.0f, DT_DEV_TRANSFORM_DIR_ALL, points, points_count);
1592}
1593
1594int dt_dev_coordinates_image_abs_to_raw_abs(dt_develop_t *dev, float *points, size_t points_count)
1595{
1596 return dt_dev_distort_backtransform_locked(dev->virtual_pipe, 0.0f, DT_DEV_TRANSFORM_DIR_ALL, points, points_count);
1597}
1598
1599// only call directly or indirectly from dt_dev_distort_transform_plus, so that it runs with the history locked
1600int dt_dev_distort_transform_locked(const dt_dev_pixelpipe_t *pipe, const double iop_order,
1601 const int transf_direction, float *points, size_t points_count)
1602{
1603 for(GList *pieces = g_list_first(pipe->nodes); pieces; pieces = g_list_next(pieces))
1604 {
1605 dt_dev_pixelpipe_iop_t *piece = (dt_dev_pixelpipe_iop_t *)(pieces->data);
1606 dt_iop_module_t *module = piece->module;
1607 if(piece->enabled
1608 && ((transf_direction == DT_DEV_TRANSFORM_DIR_ALL)
1609 || (transf_direction == DT_DEV_TRANSFORM_DIR_FORW_INCL && module->iop_order >= iop_order)
1610 || (transf_direction == DT_DEV_TRANSFORM_DIR_FORW_EXCL && module->iop_order > iop_order)
1611 || (transf_direction == DT_DEV_TRANSFORM_DIR_BACK_INCL && module->iop_order <= iop_order)
1612 || (transf_direction == DT_DEV_TRANSFORM_DIR_BACK_EXCL && module->iop_order < iop_order))
1613 && !dt_dev_pixelpipe_activemodule_disables_currentmodule(pipe->dev, module))
1614 {
1615 module->distort_transform(module, pipe, piece, points, points_count);
1616 }
1617 }
1618 return 1;
1619}
1620
1621int dt_dev_distort_transform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction,
1622 float *points, size_t points_count)
1623{
1624 dt_dev_distort_transform_locked(pipe, iop_order, transf_direction, points, points_count);
1625 return 1;
1626}
1627
1628// Internal backtransform loop. Keep this file-local so callers use the public wrappers.
1629static int dt_dev_distort_backtransform_locked(const dt_dev_pixelpipe_t *pipe, const double iop_order,
1630 const int transf_direction, float *points, size_t points_count)
1631{
1632 for(GList *pieces = g_list_last(pipe->nodes); pieces; pieces = g_list_previous(pieces))
1633 {
1634 dt_dev_pixelpipe_iop_t *piece = (dt_dev_pixelpipe_iop_t *)(pieces->data);
1635 dt_iop_module_t *module = piece->module;
1636 if(piece->enabled
1637 && ((transf_direction == DT_DEV_TRANSFORM_DIR_ALL)
1638 || (transf_direction == DT_DEV_TRANSFORM_DIR_FORW_INCL && module->iop_order >= iop_order)
1639 || (transf_direction == DT_DEV_TRANSFORM_DIR_FORW_EXCL && module->iop_order > iop_order)
1640 || (transf_direction == DT_DEV_TRANSFORM_DIR_BACK_INCL && module->iop_order <= iop_order)
1641 || (transf_direction == DT_DEV_TRANSFORM_DIR_BACK_EXCL && module->iop_order < iop_order))
1642 && !dt_dev_pixelpipe_activemodule_disables_currentmodule(pipe->dev, module))
1643 {
1644 module->distort_backtransform(module, pipe, piece, points, points_count);
1645 }
1646 }
1647 return 1;
1648}
1649
1650int dt_dev_distort_backtransform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction,
1651 float *points, size_t points_count)
1652{
1653 const int success = dt_dev_distort_backtransform_locked(pipe, iop_order, transf_direction, points, points_count);
1654 return success;
1655}
1656
1658 struct dt_iop_module_t *module)
1659{
1660 for(const GList *pieces = g_list_last(pipe->nodes); pieces; pieces = g_list_previous(pieces))
1661 {
1662 dt_dev_pixelpipe_iop_t *piece = (dt_dev_pixelpipe_iop_t *)(pieces->data);
1663 if(piece->module == module)
1664 {
1665 return piece;
1666 }
1667 }
1668 return NULL;
1669}
1670
1671// set the module list order
1677
1679{
1681
1682 /* record current history state : before change (needed for undo) */
1683 if(dev->gui_attached && cv->view((dt_view_t *)cv) == DT_VIEW_DARKROOM)
1684 {
1686 }
1687}
1688
1690{
1692
1693 /* record current history state : after change (needed for undo) */
1694 if(dev->gui_attached && cv->view((dt_view_t *)cv) == DT_VIEW_DARKROOM)
1695 {
1698 }
1699}
1700
1702{
1703 if(dev->gui_attached)
1704 {
1706 const gboolean state = dev->mask_lock;
1708 return state;
1709 }
1710 return FALSE;
1711}
1712
1713void dt_masks_set_lock_mode(dt_develop_t *dev, gboolean mode)
1714{
1715 if(dev->gui_attached)
1716 {
1718 dev->mask_lock = mode;
1720 }
1721}
1722
1724{
1725 const int num_items = g_list_length(dev->history);
1726 return CLAMP(dev->history_end, 0, num_items);
1727}
1728
1729void dt_dev_set_history_end_ext(dt_develop_t *dev, const uint32_t index)
1730{
1731 const int num_items = g_list_length(dev->history);
1732 dev->history_end = CLAMP(index, 0, num_items);
1734}
1735
1736void dt_dev_append_changed_tag(const int32_t imgid)
1737{
1738 /* attach changed tag reflecting actual change */
1739 guint tagid = 0;
1740 dt_tag_new("darktable|changed", &tagid);
1741 const gboolean tag_change = dt_tag_attach(tagid, imgid, FALSE, FALSE);
1743}
1744
1746{
1747 // dev->forms is protected by masks_mutex, not history_mutex -- this only reads the
1748 // forms list, so it must not be wrapped in a history_mutex lock by callers (that would
1749 // needlessly exclude the pipeline thread's history reads while this walk runs).
1751
1752 uint64_t hash = 5381;
1753 for(GList *form = g_list_first(dev->forms); form; form = g_list_next(form))
1754 {
1755 dt_masks_form_t *shape = (dt_masks_form_t *)form->data;
1756 hash = dt_masks_form_get_own_hash(hash, shape);
1757 }
1758
1760
1761 // Keep on accumulating "changed" states until something saves the new stack
1762 // and resets that to 0
1763 uint64_t old_hash = dev->forms_hash;
1764 dev->forms_changed |= (old_hash != hash);
1765 dev->forms_hash = hash;
1766}
1767
1769{
1770 if(!dev->roi.gui_inited || !dev->roi.raw_inited) return -1.f;
1771
1772 return fminf(fminf((float)dev->roi.width / (float)dev->roi.processed_width,
1773 (float)dev->roi.height / (float)dev->roi.processed_height),
1774 1.f);
1775}
1776
1778{
1779 if(IS_NULL_PTR(dev)) return 1.0f;
1780 return dev->roi.scaling / darktable.gui->ppd;
1781}
1782
1784{
1785 return dt_dev_get_fit_scale(dev);
1786}
1787
1789{
1790 if(IS_NULL_PTR(dev)) return 1.0f;
1791 return scaling * dev->roi.natural_scale / darktable.gui->ppd;
1792}
1793
1795{
1796 if(IS_NULL_PTR(dev) || IS_NULL_PTR(point)) return;
1797 point[0] = 0.5f * dev->roi.orig_width;
1798 point[1] = 0.5f * dev->roi.orig_height;
1799}
1800
1801void dt_dev_get_image_box_in_widget(const dt_develop_t *dev, const int32_t width, const int32_t height, float *box)
1802{
1803 if(IS_NULL_PTR(dev) || IS_NULL_PTR(box)) return;
1804
1805 const float scale = dev->roi.scaling / darktable.gui->ppd;
1806 const float roi_width = fminf(width, dev->roi.preview_width * scale);
1807 const float roi_height = fminf(height, dev->roi.preview_height * scale);
1808 const float border = dev->roi.border_size;
1809
1810 box[0] = fmaxf(border, 0.5f * (width - roi_width));
1811 box[1] = fmaxf(border, 0.5f * (height - roi_height));
1812 box[2] = fminf(width - 2 * border, roi_width);
1813 box[3] = fminf(height - 2 * border, roi_height);
1814}
1815
1817{
1818 if(IS_NULL_PTR(dev)) return 1.f;
1819 return dev->roi.scaling * dev->roi.natural_scale;
1820}
1821
1823{
1824 dev->roi.natural_scale = -1.f;
1825 dev->roi.scaling = 1.f;
1826 dev->roi.x = 0.5f;
1827 dev->roi.y = 0.5f;
1828}
1829
1830void dt_dev_convert_roi(const dt_develop_t *dev, const dt_iop_roi_t *roi_in, dt_iop_roi_t *roi_out,
1831 const dt_dev_roi_space_t from, const dt_dev_roi_space_t to)
1832{
1833 if(IS_NULL_PTR(dev) || IS_NULL_PTR(roi_in) || IS_NULL_PTR(roi_out)) return;
1834
1835 *roi_out = *roi_in;
1836 if(from == to) return;
1837
1838 const float factor = (from == DT_DEV_ROI_GUI_LOGICAL && to == DT_DEV_ROI_PIPELINE)
1839 ? darktable.gui->ppd
1840 : 1.0f / darktable.gui->ppd;
1841
1842 // x/y/width/height belong to the GUI/pipeline geometry boundary and therefore
1843 // follow the ppd factor. roi->scale stays unchanged because it expresses the
1844 // image-space sampling ratio, which must not depend on GUI density.
1845 roi_out->x = lroundf(roi_in->x * factor);
1846 roi_out->y = lroundf(roi_in->y * factor);
1847 roi_out->width = lroundf(roi_in->width * factor);
1848 roi_out->height = lroundf(roi_in->height * factor);
1849 roi_out->scale = roi_in->scale * factor;
1850}
1851
1852gboolean dt_dev_clip_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
1853{
1854 // DO NOT MODIFIY !! //
1855
1856 const float wd = dev->roi.preview_width;
1857 const float ht = dev->roi.preview_height;
1858 if(wd == 0.f || ht == 0.f) return TRUE;
1859
1860 const float zoom_scale = dt_dev_get_overlay_scale(dev);
1861 const int32_t border = dev->roi.border_size;
1862 const float roi_width = fminf(width, wd * zoom_scale);
1863 const float roi_height = fminf(height, ht * zoom_scale);
1864
1865 const float rec_x = fmaxf(border, (width - roi_width) * 0.5f);
1866 const float rec_y = fmaxf(border, (height - roi_height) * 0.5f);
1867 const float rec_w = fminf(width - 2 * border, roi_width);
1868 const float rec_h = fminf(height - 2 * border, roi_height);
1869
1870 cairo_rectangle(cr, rec_x, rec_y, rec_w, rec_h);
1871 cairo_clip(cr);
1872
1873 return FALSE;
1874}
1875
1876static gboolean _dev_translate_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
1877{
1878 // DO NOT MODIFIY !! //
1879 // used by preview image scalling, guide and modules //
1880 int proc_wd = 0;
1881 int proc_ht = 0;
1882 dt_dev_get_processed_size(dev, &proc_wd, &proc_ht);
1883 if(proc_wd == 0.f || proc_ht == 0.f) return TRUE;
1884
1885 // Get image's origin position and scale
1886 const float zoom_scale = dt_dev_get_zoom_level(dev) / darktable.gui->ppd;
1887 const float tx = 0.5f * width - dev->roi.x * proc_wd * zoom_scale;
1888 const float ty = 0.5f * height - dev->roi.y * proc_ht * zoom_scale;
1889
1890 cairo_translate(cr, tx, ty);
1891
1892 return FALSE;
1893}
1894
1895gboolean dt_dev_rescale_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
1896{
1897 if(_dev_translate_roi(dev, cr, width, height))
1898 return TRUE;
1899 const float scale = dt_dev_get_fit_scale(dev);
1900 cairo_scale(cr, scale, scale);
1901
1902 return FALSE;
1903}
1904
1905gboolean dt_dev_rescale_roi_to_input(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
1906{
1907 if(_dev_translate_roi(dev, cr, width, height))
1908 return TRUE;
1909 const float scale = dt_dev_get_zoom_level(dev) / darktable.gui->ppd;
1910 cairo_scale(cr, scale, scale);
1911
1912 return FALSE;
1913}
1914
1916{
1917 const float natural_scale = dev->roi.natural_scale;
1918
1919 // Limit zoom in to 16x the size of an apparent pixel on screen
1920 const float pixel_actual_size = natural_scale * dev->roi.scaling;
1921 const float pixel_max_size = 16.f;
1922
1923 if(pixel_actual_size >= pixel_max_size)
1924 {
1925 // Restore old scaling (caller should handle this)
1926 dev->roi.scaling = pixel_max_size / natural_scale;
1927 return TRUE;
1928 }
1929
1930 // Limit zoom out to 1/3rd of the fit-to-window size
1931 const float min_scaling = 0.33f;
1932 if(dev->roi.scaling < min_scaling)
1933 {
1934 dev->roi.scaling = min_scaling;
1935 return TRUE;
1936 }
1937 return FALSE;
1938}
1939
1941{
1942 float zoom_level = dt_dev_get_zoom_level(dev);
1943 if(zoom_level <= 0.f) zoom_level = 1.0f;
1944
1945 // Keep mouse hit-tests usable across zoom levels by bounding the selection
1946 // radius once it is expressed in image-space pixels.
1948 DT_PIXEL_APPLY_DPI(4.0f) / zoom_level,
1949 DT_PIXEL_APPLY_DPI(15.0f) / zoom_level);
1950
1952 "[mouse] effect_radius=%0.3f effect_radius_clamped=%0.3f zoom_level=%0.4f ppd=%0.4f\n",
1954 zoom_level, darktable.gui->ppd);
1955}
1956
1957void dt_dev_set_backbuf(dt_backbuf_t *backbuf, const int width, const int height, const size_t bpp,
1958 const int64_t hash, const int64_t history_hash)
1959{
1960 backbuf->height = height;
1961 backbuf->width = width;
1962 dt_dev_backbuf_set_hash(backbuf, hash);
1963 backbuf->bpp = bpp;
1964 dt_dev_backbuf_set_history_hash(backbuf, history_hash);
1965}
1966
1967// clang-format off
1968// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1969// vim: shiftwidth=2 expandtab tabstop=2 cindent
1970// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1971// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_atomic_set_int(dt_atomic_int *var, int value)
int dt_atomic_get_int(dt_atomic_int *var)
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
static const float scaling
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
void dt_image_init(dt_image_t *img)
char * key
int dt_conf_get_bool(const char *name)
void dt_conf_set_float(const char *name, float val)
float dt_conf_get_float(const char *name)
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
void dt_control_queue_redraw_center()
request redraw of center window. This redraws the center view within a gdk critical section to preven...
Definition control.c:877
void dt_control_toast_busy_enter()
Definition control.c:848
void dt_control_log_busy_leave()
Definition control.c:856
int dt_control_running()
Definition control.c:433
void dt_control_log_busy_enter()
Definition control.c:840
void dt_control_toast_busy_leave()
Definition control.c:864
void dt_control_queue_redraw()
request redraw of the workspace. This redraws the whole workspace within a gdk critical section to pr...
Definition control.c:872
void dt_show_times(const dt_times_t *start, const char *prefix)
Definition darktable.c:1638
darktable_t darktable
Definition darktable.c:183
void dt_show_times_f(const dt_times_t *start, const char *prefix, const char *suffix,...)
Definition darktable.c:1652
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1600
#define dt_free_align(ptr)
Definition darktable.h:503
@ DT_DEBUG_PIPE
Definition darktable.h:763
@ DT_DEBUG_HISTORY
Definition darktable.h:762
@ DT_DEBUG_DEV
Definition darktable.h:739
@ DT_DEBUG_MASKS
Definition darktable.h:749
static void dt_free_gpointer(gpointer ptr)
Definition darktable.h:485
#define dt_free(ptr)
Definition darktable.h:478
static void dt_get_times(dt_times_t *t)
Definition darktable.h:983
static gchar * delete_underscore(const char *s)
Definition darktable.h:1210
#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 darktable.h:293
void dt_dev_free_history_item(gpointer data)
Release a reference to a history item (used as GList free callback).
void dt_dev_history_undo_invalidate_module(dt_iop_module_t *module)
Invalidate a module pointer inside undo snapshots.
uint64_t dt_dev_history_compute_hash(dt_develop_t *dev)
Get the integrity checksum of the whole history stack. This should be done ONLY when history is chang...
void dt_dev_history_undo_start_record_locked(dt_develop_t *dev)
Start an undo record with history_mutex already locked.
void dt_dev_history_undo_end_record(dt_develop_t *dev)
Finish an undo record for history changes.
void dt_dev_history_undo_end_record_locked(dt_develop_t *dev)
Finish an undo record with history_mutex already locked.
void dt_dev_history_undo_start_record(dt_develop_t *dev)
Start an undo record for history changes.
void dt_dev_write_history_ext(dt_develop_t *dev, const int32_t imgid)
Write dev->history to DB and XMP for a given image id.
void dt_dev_history_notify_change(dt_develop_t *dev, const int32_t imgid)
Notify the rest of the app that history changes were written.
gboolean dt_dev_read_history_ext(dt_develop_t *dev, const int32_t imgid)
Read history and masks from DB and populate dev->history.
void dt_pixelpipe_get_global_hash(dt_dev_pixelpipe_t *pipe)
gboolean dt_dev_pixelpipe_is_backbufer_valid(dt_dev_pixelpipe_t *pipe)
void dt_dev_pixelpipe_get_roi_out(dt_dev_pixelpipe_t *pipe, const int width_in, const int height_in, int *width, int *height)
void dt_dev_pixelpipe_change(dt_dev_pixelpipe_t *pipe)
void dt_dev_pixelpipe_get_roi_in(dt_dev_pixelpipe_t *pipe, const struct dt_iop_roi_t roi_out)
#define dt_dev_pixelpipe_update_zoom_preview(dev)
#define dt_dev_pixelpipe_update_zoom_main(dev)
void dt_dev_signal_modules_moved(dt_develop_t *dev)
Definition develop.c:1672
dt_job_t * dt_dev_process_job_create(dt_develop_t *dev)
Definition develop.c:856
void dt_dev_masks_list_change(dt_develop_t *dev)
Definition develop.c:1240
static gboolean _dev_translate_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
Definition develop.c:1876
void dt_dev_coordinates_image_abs_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1128
float dt_dev_get_natural_scale(dt_develop_t *dev)
Definition develop.c:1768
int dt_dev_distort_transform_locked(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, float *points, size_t points_count)
Definition develop.c:1600
void dt_dev_get_processed_size(const dt_develop_t *dev, int *procw, int *proch)
Definition develop.c:1032
dt_dev_pixelpipe_iop_t * dt_dev_distort_get_iop_pipe(struct dt_dev_pixelpipe_t *pipe, struct dt_iop_module_t *module)
Definition develop.c:1657
void dt_masks_set_lock_mode(dt_develop_t *dev, gboolean mode)
Definition develop.c:1713
void dt_dev_modulegroups_switch_tab(dt_develop_t *dev, dt_iop_module_t *module)
Definition develop.c:1234
void dt_dev_coordinates_preview_abs_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1212
int dt_dev_get_thumbnail_size(dt_develop_t *dev)
Definition develop.c:356
void _dev_module_update_multishow(dt_develop_t *dev, struct dt_iop_module_t *module, const dt_dev_multishow_state_t *state)
Definition develop.c:1402
void dt_dev_get_image_box_in_widget(const dt_develop_t *dev, const int32_t width, const int32_t height, float *box)
Get the displayed image rectangle in darkroom widget coordinates.
Definition develop.c:1801
int dt_dev_coordinates_raw_abs_to_image_abs(dt_develop_t *dev, float *points, size_t points_count)
Definition develop.c:1589
void dt_dev_coordinates_raw_norm_to_raw_abs(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1162
void dt_dev_get_widget_center(const dt_develop_t *dev, float *point)
Get the center of the darkroom widget in logical coordinates.
Definition develop.c:1794
void dt_dev_module_remove(dt_develop_t *dev, dt_iop_module_t *module)
Definition develop.c:1342
void dt_dev_cleanup(dt_develop_t *dev)
Definition develop.c:225
static void dt_dev_resync_mipmap_cache(dt_develop_t *dev, dt_dev_pixelpipe_t *pipe, dt_iop_roi_t roi)
Definition develop.c:521
int dt_dev_distort_transform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, float *points, size_t points_count)
Definition develop.c:1621
float dt_dev_get_overlay_scale(dt_develop_t *dev)
Get the overlay scale factor in GUI logical coordinates.
Definition develop.c:1783
void dt_dev_append_changed_tag(const int32_t imgid)
Definition develop.c:1736
dt_dev_image_storage_t dt_dev_load_image(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:940
void dt_dev_coordinates_image_norm_to_preview_abs(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1197
float dt_dev_get_zoom_level(const dt_develop_t *dev)
Definition develop.c:1816
void dt_dev_coordinates_image_norm_to_widget(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1084
void dt_dev_masks_selection_change(dt_develop_t *dev, struct dt_iop_module_t *module, const int selectid, const int throw_event)
Definition develop.c:1255
void dt_dev_init(dt_develop_t *dev, int32_t gui_attached)
Definition develop.c:128
int dt_dev_is_current_image(dt_develop_t *dev, int32_t imgid)
Definition develop.c:1229
void dt_dev_undo_start_record(dt_develop_t *dev)
Definition develop.c:1678
void dt_dev_snapshot_request(dt_develop_t *dev, const char *filename)
Definition develop.c:1262
gboolean dt_masks_get_lock_mode(dt_develop_t *dev)
Definition develop.c:1701
void dt_dev_darkroom_pipeline(dt_develop_t *dev)
Run darkroom preview and main pipelines from one background loop.
Definition develop.c:610
gchar * dt_history_item_get_label(const struct dt_iop_module_t *module)
Definition develop.c:1511
void dt_dev_coordinates_raw_norm_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1184
gchar * dt_history_item_get_name_html(const struct dt_iop_module_t *module)
Definition develop.c:1573
void dt_dev_check_zoom_pos_bounds(dt_develop_t *dev, float *dev_x, float *dev_y, float *box_w, float *box_h)
Ensure that the current ROI position is within allowed bounds .
Definition develop.c:997
gboolean dt_dev_check_zoom_scale_bounds(dt_develop_t *dev)
Ensure that the current zoom level is within allowed bounds (for scrolling).
Definition develop.c:1915
void dt_dev_reset_roi(dt_develop_t *dev)
Definition develop.c:1822
dt_iop_module_t * dt_dev_module_duplicate(dt_develop_t *dev, dt_iop_module_t *base)
Definition develop.c:1270
gboolean _resync_pipe_with_history(dt_develop_t *dev, dt_dev_pixelpipe_t *pipe, dt_iop_roi_t *roi, gboolean *needs_update)
Definition develop.c:549
GList * dt_dev_load_modules(dt_develop_t *dev)
Definition develop.c:98
void dt_dev_set_backbuf(dt_backbuf_t *backbuf, const int width, const int height, const size_t bpp, const int64_t hash, const int64_t history_hash)
Definition develop.c:1957
void dt_dev_set_history_end_ext(dt_develop_t *dev, const uint32_t index)
Set the history end index (GUI perspective).
Definition develop.c:1729
gboolean dt_dev_rescale_roi_to_input(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
Scale the ROI to fit the input size within given width/height, centered.
Definition develop.c:1905
static gboolean _darkroom_pipeline_inputs_ready(const dt_develop_t *dev)
Definition develop.c:341
float dt_dev_get_widget_zoom_scale(const dt_develop_t *dev, const float scaling)
Convert a darkroom scaling factor to GUI logical zoom.
Definition develop.c:1788
float dt_dev_get_zoom_scale(const dt_develop_t *dev, const gboolean preview)
Definition develop.c:933
void dt_dev_coordinates_image_norm_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1177
static int dt_dev_distort_backtransform_locked(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, float *points, size_t points_count)
Definition develop.c:1629
gboolean dt_dev_pipelines_share_preview_output(dt_develop_t *dev)
Tell whether the darkroom main and preview pipes currently target the same GUI output.
Definition develop.c:504
void dt_dev_configure_real(dt_develop_t *dev, int wd, int ht)
Definition develop.c:975
void dt_dev_masks_update_hash(dt_develop_t *dev)
Definition develop.c:1745
gboolean dt_dev_clip_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
Clip the view to the ROI. WARNING: this must be done before any translation.
Definition develop.c:1852
int dt_dev_coordinates_image_abs_to_raw_abs(dt_develop_t *dev, float *points, size_t points_count)
Definition develop.c:1594
void dt_dev_undo_end_record(dt_develop_t *dev)
Definition develop.c:1689
gboolean dt_dev_rescale_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
Scale the ROI to fit within given width/height, centered.
Definition develop.c:1895
void dt_dev_coordinates_widget_delta_to_image_delta(dt_develop_t *dev, float *points, size_t num_points)
Convert a widget-space distance to processed-image pixels.
Definition develop.c:1039
void dt_dev_convert_roi(const dt_develop_t *dev, const dt_iop_roi_t *roi_in, dt_iop_roi_t *roi_out, const dt_dev_roi_space_t from, const dt_dev_roi_space_t to)
Convert a full ROI object between pipeline raster coordinates and GUI logical coordinates.
Definition develop.c:1830
float dt_dev_get_fit_scale(dt_develop_t *dev)
Get the scale factor that maps preview-buffer pixels to GUI coordinates.
Definition develop.c:1777
dt_dev_image_storage_t dt_dev_ensure_image_storage(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:900
int32_t dt_dev_get_history_end_ext(dt_develop_t *dev)
Get the current history end index (GUI perspective).
Definition develop.c:1723
void dt_dev_modules_update_multishow(dt_develop_t *dev)
Definition develop.c:1443
void dt_dev_coordinates_image_norm_to_image_abs(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1113
static gboolean _update_darkroom_roi(dt_develop_t *dev, dt_dev_pixelpipe_t *pipe, int *x, int *y, int *wd, int *ht, float *scale)
Definition develop.c:463
gchar * dt_dev_get_masks_group_name(const struct dt_iop_module_t *module)
Definition develop.c:1543
gboolean dt_dev_pixelpipe_has_preview_output(const dt_develop_t *dev, const dt_dev_pixelpipe_t *pipe, const dt_iop_roi_t *roi)
Definition develop.c:414
void dt_dev_coordinates_widget_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Coordinate conversion helpers between widget, normalized image, and absolute image spaces.
Definition develop.c:1056
void dt_dev_update_mouse_effect_radius(dt_develop_t *dev)
Convert absolute output-image coordinates to input image space by calling dt_dev_coordinates_image_ab...
Definition develop.c:1940
static gboolean _dt_dev_refresh_image_storage(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:890
void dt_dev_masks_list_update(dt_develop_t *dev)
Definition develop.c:1245
static gboolean _dt_dev_mipmap_prefetch_full(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:871
static dt_dev_image_storage_t _dt_dev_load_raw(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:915
int dt_dev_distort_backtransform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, float *points, size_t points_count)
Definition develop.c:1650
gchar * dt_dev_get_multi_name(const struct dt_iop_module_t *module)
Definition develop.c:1535
void dt_dev_masks_list_remove(dt_develop_t *dev, int formid, int parentid)
Definition develop.c:1250
static int32_t dt_dev_process_job_run(dt_job_t *job)
Definition develop.c:849
void dt_dev_coordinates_raw_abs_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1145
void dt_dev_coordinates_image_abs_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1191
gchar * dt_history_item_get_name(const struct dt_iop_module_t *module)
Definition develop.c:1557
void dt_dev_start_all_pipelines(dt_develop_t *dev)
Definition develop.c:864
static uint64_t dt_dev_get_history_hash(const dt_develop_t *dev)
Definition develop.h:510
@ DT_DEV_OVEREXPOSED_PURPLEGREEN
Definition develop.h:74
@ DT_DEV_OVEREXPOSED_REDBLUE
Definition develop.h:73
@ DT_DEV_OVEREXPOSED_BLACKWHITE
Definition develop.h:72
@ DT_DEV_PIXELPIPE_DISPLAY_NONE
Definition develop.h:117
@ DT_DEV_TRANSFORM_DIR_FORW_INCL
Definition develop.h:103
@ DT_DEV_TRANSFORM_DIR_ALL
Definition develop.h:102
@ DT_DEV_RAWOVEREXPOSED_MODE_FALSECOLOR
Definition develop.h:90
@ DT_DEV_RAWOVEREXPOSED_MODE_MARK_CFA
Definition develop.h:88
dt_dev_roi_space_t
Definition develop.h:110
@ DT_DEV_ROI_GUI_LOGICAL
Definition develop.h:112
@ DT_DEV_ROI_PIPELINE
Definition develop.h:111
@ DT_CLIPPING_PREVIEW_GAMUT
Definition develop.h:150
@ DT_CLIPPING_PREVIEW_SATURATION
Definition develop.h:153
static void dt_dev_set_history_hash(dt_develop_t *dev, const uint64_t history_hash)
Definition develop.h:515
@ DT_DEV_RAWOVEREXPOSED_RED
Definition develop.h:94
@ DT_DEV_RAWOVEREXPOSED_BLACK
Definition develop.h:97
dt_dev_image_storage_t
Definition develop.h:529
@ DT_DEV_IMAGE_STORAGE_DB_NOT_READ
Definition develop.h:532
@ DT_DEV_IMAGE_STORAGE_OK
Definition develop.h:530
@ DT_DEV_IMAGE_STORAGE_MIPMAP_NOT_FOUND
Definition develop.h:531
static int dt_pthread_rwlock_unlock(dt_pthread_rwlock_t *rwlock)
Definition dtpthread.h:452
static void dt_pthread_rwlock_set_name(dt_pthread_rwlock_t *lock, const char *name)
Definition dtpthread.h:442
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:384
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Definition dtpthread.h:369
static int dt_pthread_rwlock_destroy(dt_pthread_rwlock_t *lock)
Definition dtpthread.h:447
static int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:389
static int dt_pthread_rwlock_init(dt_pthread_rwlock_t *lock, const pthread_rwlockattr_t *attr)
Definition dtpthread.h:427
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:374
static int dt_pthread_rwlock_rdlock(dt_pthread_rwlock_t *rwlock)
Definition dtpthread.h:502
static int dt_pthread_rwlock_wrlock(dt_pthread_rwlock_t *rwlock)
Definition dtpthread.h:534
void dt_iop_buffer_dsc_update_bpp(dt_iop_buffer_dsc_t *dsc)
Definition format.c:28
void dt_capitalize_label(gchar *text)
Definition gtk.c:3382
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
void dt_gui_throttle_cancel(gpointer source)
void dt_gui_throttle_record_runtime(const dt_dev_pixelpipe_t *pipe, const gint64 runtime_us)
void dt_image_cache_read_release(dt_image_cache_t *cache, const dt_image_t *img)
dt_image_t * dt_image_cache_get(dt_image_cache_t *cache, const int32_t imgid, char mode)
void dt_image_cache_write_release(dt_image_cache_t *cache, dt_image_t *img, dt_image_cache_write_mode_t mode)
@ DT_IMAGE_CACHE_SAFE
Definition image_cache.h:49
int bpp
void dt_iop_cleanup_module(dt_iop_module_t *module)
Definition imageop.c:1578
int dt_iop_load_module(dt_iop_module_t *module, dt_iop_module_so_t *module_so, dt_develop_t *dev)
Definition imageop.c:1567
int dt_iop_load_module_by_so(dt_iop_module_t *module, dt_iop_module_so_t *so, dt_develop_t *dev)
Definition imageop.c:471
void dt_iop_nap(int32_t usec)
Definition imageop.c:2973
gboolean dt_iop_gui_module_is_visible(dt_iop_module_t *module)
Definition imageop.c:740
void dt_iop_update_multi_priority(dt_iop_module_t *module, int new_priority)
Definition imageop.c:3122
gint dt_sort_iop_by_order(gconstpointer a, gconstpointer b)
Compare two module instances by iop_order for sorting.
Definition iop_order.c:2008
gboolean dt_ioppr_check_can_move_before_iop(GList *iop_list, dt_iop_module_t *module, dt_iop_module_t *module_next)
Validate whether module can be moved before module_next.
Definition iop_order.c:2021
gboolean dt_ioppr_check_can_move_after_iop(GList *iop_list, dt_iop_module_t *module, dt_iop_module_t *module_prev)
Validate whether module can be moved after module_prev.
Definition iop_order.c:2190
gboolean dt_ioppr_move_iop_after(struct dt_develop_t *dev, dt_iop_module_t *module, dt_iop_module_t *module_prev)
Move a module instance after another module in the pipe.
Definition iop_order.c:2257
void dt_ioppr_insert_module_instance(struct dt_develop_t *dev, dt_iop_module_t *module)
Ensure a module instance has an entry in dev->iop_order_list.
Definition iop_order.c:2355
int dt_ioppr_check_iop_order(dt_develop_t *dev, const int32_t imgid, const char *msg)
Debug helper to validate the current order for a develop context.
Definition iop_order.c:2383
void dt_ioppr_cleanup_profile_info(dt_iop_order_iccprofile_info_t *profile_info)
static const float x
dt_job_t * dt_control_job_create(dt_job_execute_callback execute, const char *msg,...)
Definition jobs.c:135
int32_t dt_control_add_job_res(dt_control_t *control, _dt_job_t *job, int32_t res)
Definition jobs.c:349
void * dt_control_job_get_params(const _dt_job_t *job)
Definition jobs.c:129
void dt_control_job_set_params(_dt_job_t *job, void *params, dt_job_destroy_callback callback)
Definition jobs.c:112
#define DT_CTL_WORKER_DARKROOM
Definition jobs.h:38
uint64_t dt_masks_form_get_own_hash(uint64_t hash, const dt_masks_form_t *form)
void dt_masks_form_unref(dt_masks_form_t *form)
#define CLAMPF(a, mn, mx)
Definition math.h:89
void dt_mipmap_cache_swap_at_size(dt_mipmap_cache_t *cache, const int32_t imgid, const dt_mipmap_size_t mip, const uint8_t *const in, const int32_t width, const int32_t height, dt_colorspaces_color_profile_type_t profile)
dt_mipmap_size_t dt_mipmap_cache_get_fitting_size(const dt_mipmap_cache_t *cache, const int32_t width, const int32_t height, const uint32_t imgid)
#define dt_mipmap_cache_get(A, B, C, D, E, F)
@ DT_MIPMAP_BLOCKING
#define dt_mipmap_cache_release(A, B)
dt_mipmap_size_t
@ DT_MIPMAP_FULL
const float factor
Definition pdf.h:90
@ DT_DEV_PIXELPIPE_PREVIEW
Definition pixelpipe.h:40
@ DT_DEV_PIXELPIPE_FULL
Definition pixelpipe.h:39
void dt_dev_pixelpipe_cache_ref_count_entry(dt_dev_pixelpipe_cache_t *cache, gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Increase/Decrease the reference count on the cache line as to prevent LRU item removal....
gboolean dt_dev_pixelpipe_cache_ref_entry_by_hash(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash, void **data, dt_pixel_cache_entry_t **entry)
Resolve and retain an existing cache entry by hash.
void dt_dev_pixelpipe_cache_rdlock_entry(dt_dev_pixelpipe_cache_t *cache, gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the read lock on the entry.
Pixelpipe cache for storing intermediate results in the pixelpipe.
#define DT_PIXELPIPE_CACHE_HASH_INVALID
void dt_dev_pixelpipe_set_input(dt_dev_pixelpipe_t *pipe, int32_t imgid, int width, int height, float iscale, dt_mipmap_size_t size)
void dt_dev_pixelpipe_reset_reentry(dt_dev_pixelpipe_t *pipe)
gboolean dt_dev_pixelpipe_has_reentry(dt_dev_pixelpipe_t *pipe)
int dt_dev_pixelpipe_init(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev)
gboolean dt_dev_pixelpipe_get_realtime(const dt_dev_pixelpipe_t *pipe)
char * dt_pixelpipe_get_pipe_name(dt_dev_pixelpipe_type_t pipe_type)
int dt_dev_pixelpipe_init_preview(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev)
void dt_dev_pixelpipe_cleanup(dt_dev_pixelpipe_t *pipe)
int dt_dev_pixelpipe_process(dt_dev_pixelpipe_t *pipe, dt_iop_roi_t roi)
static dt_dev_pixelpipe_cache_request_t dt_dev_pixelpipe_get_cache_request(const dt_dev_pixelpipe_t *pipe)
static dt_dev_pixelpipe_change_t dt_dev_pixelpipe_get_changed(const dt_dev_pixelpipe_t *pipe)
static uint64_t dt_dev_pixelpipe_get_hash(const dt_dev_pixelpipe_t *pipe)
static void dt_dev_pixelpipe_or_changed(dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_change_t flags)
static void dt_dev_pixelpipe_set_cache_request(dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_cache_request_t request, const struct dt_iop_module_t *module)
@ DT_DEV_PIPE_REENTRY
@ DT_DEV_PIPE_CACHE_REQUEST
@ DT_DEV_PIPE_SYNCH
@ DT_DEV_PIPE_TOP_CHANGED
@ DT_DEV_PIPE_REMOVE
@ DT_DEV_PIPE_UNCHANGED
static uint64_t dt_dev_pixelpipe_get_history_hash(const dt_dev_pixelpipe_t *pipe)
static void dt_dev_pixelpipe_set_history_hash(dt_dev_pixelpipe_t *pipe, const uint64_t history_hash)
static void dt_dev_backbuf_set_hash(dt_backbuf_t *backbuf, const uint64_t hash)
static void dt_dev_backbuf_set_history_hash(dt_backbuf_t *backbuf, const uint64_t history_hash)
dt_dev_pixelpipe_cache_request_t
@ DT_DEV_PIXELPIPE_CACHE_REQUEST_BACKBUF
@ DT_DEV_PIXELPIPE_CACHE_REQUEST_NONE
@ DT_DEV_PIXELPIPE_CACHE_REQUEST_MODULE
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:366
@ DT_SIGNAL_DEVELOP_HISTORY_CHANGE
This signal is raised when develop history is changed no param, no returned value.
Definition signal.h:204
@ DT_SIGNAL_DEVELOP_PREVIEW_PIPE_FINISHED
This signal is raised when develop preview pipe process is finished no param, no returned value.
Definition signal.h:174
@ DT_SIGNAL_HISTORY_RESYNC
This signal is raised once darkroom history has been resynchronized into all live pipelines....
Definition signal.h:209
@ DT_SIGNAL_DEVELOP_UI_PIPE_FINISHED
This signal is raised when pipe is finished and the gui is attached no param, no returned value.
Definition signal.h:179
@ DT_SIGNAL_DEVELOP_MODULE_MOVED
This signal is raised when order of modules in pipeline is changed.
Definition signal.h:218
@ DT_SIGNAL_DEVELOP_MODULEGROUPS_SET
This signal is raised to request a modulegroups update. 1 : dt_iop_module_t *module,...
Definition signal.h:191
@ DT_SIGNAL_TAG_CHANGED
This signal is raised when a tag is added/deleted/changed
Definition signal.h:130
const float uint32_t state[4]
unsigned __int64 uint64_t
Definition strptime.c:75
struct dt_dev_pixelpipe_cache_t * pixelpipe_cache
Definition darktable.h:818
struct dt_gui_gtk_t * gui
Definition darktable.h:803
struct dt_colorspaces_t * color_profiles
Definition darktable.h:816
struct dt_mipmap_cache_t * mipmap_cache
Definition darktable.h:804
GList * iop
Definition darktable.h:789
struct dt_control_signal_t * signals
Definition darktable.h:802
struct dt_image_cache_t * image_cache
Definition darktable.h:805
struct dt_develop_t * develop
Definition darktable.h:798
struct dt_view_manager_t * view_manager
Definition darktable.h:800
struct dt_control_t * control
Definition darktable.h:801
dt_colorspaces_color_profile_type_t display_type
GHashTable * modules_in_history
Definition develop.c:1397
GHashTable * prev_visible
Definition develop.c:1398
GHashTable * next_visible
Definition develop.c:1399
GHashTable * instance_counts
Definition develop.c:1396
dt_pthread_mutex_t busy_mutex
dt_backbuf_t backbuf
dt_atomic_int shutdown
dt_dev_pixelpipe_type_t type
struct dt_develop_t * dev
void(* list_update)(struct dt_lib_module_t *self)
Definition develop.h:436
int32_t height
Definition develop.h:189
int32_t gui_attached
Definition develop.h:162
GList * iop_order_list
Definition develop.h:291
int undo_history_before_end
Definition develop.h:298
dt_image_t image_storage
Definition develop.h:259
struct dt_develop_t::@19 color_picker
Authoritative darkroom color-picker state.
int32_t orig_height
Definition develop.h:205
struct dt_colorpicker_sample_t * primary_sample
Definition develop.h:397
GList * undo_history_before_snapshot
Definition develop.h:297
gboolean output_inited
Definition develop.h:242
int32_t border_size
Definition develop.h:200
dt_backbuf_t display_histogram
Definition develop.h:343
uint32_t * histogram_pre_levels
Definition develop.h:323
GList * iop
Definition develop.h:285
gboolean request
Definition develop.h:425
int32_t raw_height
Definition develop.h:228
dt_backbuf_t output_histogram
Definition develop.h:342
int32_t preview_height
Definition develop.h:213
int32_t history_end
Definition develop.h:272
struct dt_develop_t::@18 transient_params
GList * undo_history_before_iop_order_list
Definition develop.h:299
gboolean wb_is_D65
Definition develop.h:447
gboolean restrict_histogram
Definition develop.h:402
int32_t processed_width
Definition develop.h:233
void(* list_remove)(struct dt_lib_module_t *self, int formid, int parentid)
Definition develop.h:435
int completed
Definition develop.h:499
struct dt_iop_module_t * gui_module
Definition develop.h:165
dt_pthread_rwlock_t history_mutex
Definition develop.h:263
gboolean live_samples_enabled
Definition develop.h:401
dt_clipping_preview_mode_t mode
Definition develop.h:461
gboolean gui_inited
Definition develop.h:236
struct dt_develop_t::@20::@27 snapshot
int32_t orig_width
Definition develop.h:205
struct dt_lib_module_t *void(* list_change)(struct dt_lib_module_t *self)
Definition develop.h:434
gboolean forms_changed
Definition develop.h:332
float lower
Definition develop.h:459
struct dt_dev_pixelpipe_t * preview_pipe
Definition develop.h:247
gboolean pipelines_started
Definition develop.h:351
GList * history
Definition develop.h:275
int32_t raw_width
Definition develop.h:228
struct dt_dev_pixelpipe_t * virtual_pipe
Definition develop.h:251
GList * allprofile_info
Definition develop.h:320
struct dt_iop_module_t *void * params
Definition develop.h:310
GList * alliop
Definition develop.h:287
dt_aligned_pixel_t wb_coeffs
Definition develop.h:448
dt_dev_overexposed_colorscheme_t colorscheme
Definition develop.h:458
GList * allforms
Definition develop.h:335
dt_backbuf_t raw_histogram
Definition develop.h:341
float scaling
Definition develop.h:193
struct dt_develop_t::@17 roi
struct dt_develop_t::@20::@28 masks
void(* selection_change)(struct dt_lib_module_t *self, struct dt_iop_module_t *module, const int selectid, const int throw_event)
Definition develop.h:438
struct dt_develop_t::@20 proxy
uint32_t * histogram_pre_tonecurve
Definition develop.h:323
uint32_t histogram_pre_levels_max
Definition develop.h:324
int32_t processed_height
Definition develop.h:233
int32_t preview_width
Definition develop.h:213
struct dt_iop_module_t * chroma_adaptation
Definition develop.h:444
gboolean mask_lock
Definition develop.h:503
dt_pthread_mutex_t transient_params_mutex
Definition develop.h:317
dt_pthread_rwlock_t masks_mutex
Definition develop.h:339
uint64_t forms_hash
Definition develop.h:330
struct dt_develop_t::@22 rawoverexposed
const gchar * filename
Definition develop.h:426
uint32_t histogram_pre_tonecurve_max
Definition develop.h:324
float threshold
Definition develop.h:472
struct dt_develop_t::@26 progress
gboolean raw_inited
Definition develop.h:239
struct dt_dev_pixelpipe_t * pipe
Definition develop.h:247
float upper
Definition develop.h:460
int32_t width
Definition develop.h:189
gboolean display_samples
Definition develop.h:400
GList * forms
Definition develop.h:327
int undo_history_depth
Definition develop.h:296
struct dt_develop_t::@21 overexposed
void * blend_params
Definition develop.h:313
float natural_scale
Definition develop.h:224
double ppd
Definition gtk.h:205
float effect_radius
Definition gtk.h:215
float effect_radius_clamped
Definition gtk.h:217
dt_pthread_mutex_t mutex
Definition gtk.h:249
struct dt_gui_gtk_t::@49 mouse
dt_iop_buffer_dsc_t dsc
Definition image.h:337
int32_t id
Definition image.h:319
char multi_name[128]
Definition imageop.h:400
struct dt_develop_t * dev
Definition imageop.h:333
int32_t instance
Definition imageop.h:288
dt_iop_module_so_t * so
Definition imageop.h:396
GtkWidget * expander
Definition imageop.h:382
int request_mask_display
Definition imageop.h:298
Region of interest passed through the pixelpipe.
Definition imageop.h:72
double scale
Definition imageop.h:74
gboolean dt_tag_attach(const guint tagid, const int32_t imgid, const gboolean undo_on, const gboolean group_on)
Definition tags.c:485
gboolean dt_tag_new(const char *name, guint *tagid)
Definition tags.c:179
#define MAX(a, b)
Definition thinplate.c:29
const dt_view_t * dt_view_manager_get_current_view(dt_view_manager_t *vm)
Definition view.c:141
@ DT_VIEW_DARKROOM
Definition view.h:79