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 <assert.h>
61#include <stddef.h>
62#include <glib/gprintf.h>
63#include <inttypes.h>
64#include <math.h>
65#include <stdint.h>
66#include <stdlib.h>
67#include <string.h>
68#include <strings.h>
69#include <unistd.h>
70
71#include "develop/imageop_gui.h"
73#include "system/atomic.h"
74#include "history/history.h"
75#include "caches/image_cache.h"
76#include "caches/mipmap_cache.h"
77#include "metadata/tags.h"
78#include "common/conf.h"
79#include "common/image.h"
80#include "common/paths.h" // DT_PATH_MAX
81#include "control/control.h"
82#include "control/signal.h"
83#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/application.h"
94#include "libs/colorpicker.h"
95#include "widgets/label.h"
97#include "gui/screen_metrics.h"
98
99#define DT_IOP_ORDER_INFO (dt_get_debug_flags() & DT_DEBUG_IOPORDER)
100
102{
103 GList *res = NULL;
104 dt_iop_module_t *module;
105 dt_iop_module_so_t *module_so;
106 GList *iop = g_list_first(dt_iop_get_modules_so());
107 while(iop)
108 {
109 module_so = (dt_iop_module_so_t *)iop->data;
110 module = (dt_iop_module_t *)calloc(1, sizeof(dt_iop_module_t));
111 if(dt_iop_load_module_by_so(module, module_so, dev))
112 {
113 dt_free(module);
114 continue;
115 }
116 res = g_list_insert_sorted(res, module, dt_sort_iop_by_order);
117 module->global_data = module_so->data;
118 module->so = module_so;
119 iop = g_list_next(iop);
120 }
121
122 /* Give every base module a distinct `instance`.
123 *
124 * `instance` identifies a module FAMILY: a base module and every extra instance
125 * duplicated from it share one value, and dt_dev_module_duplicate() scans dev->iop for
126 * `mod->instance == base->instance` to find the next free multi_priority and multi_name.
127 * With every module left at the calloc'd 0, that scan matches every module of every
128 * operation, so duplicating `retouch` picked up the numbering of an unrelated `exposure`
129 * copy and produced retouch "2" with no "1" -- issue #1265.
130 *
131 * Runtime-only: the database and XMP store multi_priority and multi_name, never this, so
132 * the numbering does not have to be stable across sessions -- only distinct within a dev.
133 *
134 * 214cc1cc8e removed this line while tidying dt_develop_t and left the loop empty; the
135 * counter it uses stayed on the struct, unread, which is why nothing complained. */
136 GList *it = res;
137 while(it)
138 {
139 module = (dt_iop_module_t *)it->data;
140 module->instance = dev->iop_instance++;
141 it = g_list_next(it);
142 }
143 return res;
144}
145
146void dt_dev_init(dt_develop_t *dev, int32_t gui_attached)
147{
148 memset(dev, 0, sizeof(dt_develop_t));
153 dt_pthread_rwlock_set_name(&dev->history_mutex, "history_mutex"); // find_history_mutex_blocker, temporary
156
157 dev->gui_attached = gui_attached;
158 if(gui_attached) dev->viewport = dt_dev_viewport_new();
159
161
162 if(dev->gui_attached)
163 {
164 dev->pipe = (dt_dev_pixelpipe_t *)malloc(sizeof(dt_dev_pixelpipe_t));
165 dev->preview_pipe = (dt_dev_pixelpipe_t *)malloc(sizeof(dt_dev_pixelpipe_t));
166 dt_dev_pixelpipe_init(dev->pipe, dev);
168
169 /* Where the GUI gets sizes and coordinates from (doc/geometry-service.md). GUI devs only:
170 * it answers GUI questions and is GUI-thread state, so a headless dev has nothing to do with
171 * one -- which is also what makes the NULL chain the guard on every entry point. */
173 }
174
175 dt_dev_set_backbuf(&dev->raw_histogram, 0, 0, 0, -1, -1);
176 dt_dev_set_backbuf(&dev->output_histogram, 0, 0, 0, -1, -1);
177 dt_dev_set_backbuf(&dev->display_histogram, 0, 0, 0, -1, -1);
178
179 dev->proxy.wb_is_D65 = TRUE; // don't display error messages until we know for sure it's FALSE
180 dev->proxy.wb_coeffs[0] = 0.f;
181
182 // Overlay toolbar prefs have been found corrupted on disk (heap-looking int garbage, zeroed
183 // floats). An out-of-enum mode/colorscheme indexes the fixed-size color tables of
184 // iop/rawoverexposed.c out of bounds and selects diverging CPU/OpenCL preview paths (the CL
185 // switch defaults to false color), painting the whole image as a CFA checkerboard. An invalid
186 // enum means the whole stored group is untrustworthy: restore the group defaults instead of
187 // clamping, so a corrupted threshold does not survive as a legal-looking value.
188 const int raw_mode = dt_conf_get_int("darkroom/ui/rawoverexposed/mode");
189 const int raw_colorscheme = dt_conf_get_int("darkroom/ui/rawoverexposed/colorscheme");
190 const float raw_threshold = dt_conf_get_float("darkroom/ui/rawoverexposed/threshold");
192 && raw_colorscheme >= DT_DEV_RAWOVEREXPOSED_RED && raw_colorscheme <= DT_DEV_RAWOVEREXPOSED_BLACK
193 && raw_threshold >= 0.f && raw_threshold <= 2.f)
194 {
195 dev->rawoverexposed.mode = raw_mode;
196 dev->rawoverexposed.colorscheme = raw_colorscheme;
197 dev->rawoverexposed.threshold = raw_threshold;
198 }
199 else
200 {
203 dev->rawoverexposed.threshold = 1.f;
204 }
205
206 const int over_mode = dt_conf_get_int("darkroom/ui/overexposed/mode");
207 const int over_colorscheme = dt_conf_get_int("darkroom/ui/overexposed/colorscheme");
208 const float over_lower = dt_conf_get_float("darkroom/ui/overexposed/lower");
209 const float over_upper = dt_conf_get_float("darkroom/ui/overexposed/upper");
210 if(over_mode >= DT_CLIPPING_PREVIEW_GAMUT && over_mode <= DT_CLIPPING_PREVIEW_SATURATION
211 && over_colorscheme >= DT_DEV_OVEREXPOSED_BLACKWHITE && over_colorscheme <= DT_DEV_OVEREXPOSED_PURPLEGREEN
212 && over_lower >= -32.f && over_lower <= -4.f && over_upper >= 0.f && over_upper <= 100.f)
213 {
214 dev->overexposed.mode = over_mode;
215 dev->overexposed.colorscheme = over_colorscheme;
216 dev->overexposed.lower = over_lower;
217 dev->overexposed.upper = over_upper;
218 }
219 else
220 {
223 dev->overexposed.lower = -12.69f;
224 dev->overexposed.upper = 99.99f;
225 }
226
227 if(dev->gui_attached)
228 {
229 dev->color_picker.primary_sample = g_malloc0(sizeof(dt_colorpicker_sample_t));
230 dev->color_picker.display_samples = dt_conf_get_bool("ui_last/colorpicker_display_samples");
232 dev->color_picker.restrict_histogram = dt_conf_get_bool("ui_last/colorpicker_restrict_histogram");
233 }
234
235 dt_dev_reset_roi(dev);
236
237 dev->iop = dt_dev_load_modules(dev);
238}
239
241{
242 if(IS_NULL_PTR(dev)) return;
243 // image_cache does not have to be unref'd, this is done outside develop module.
244
247
249 dev->viewport = NULL;
250
251 dev->proxy.chroma_adaptation = NULL;
252 dev->proxy.wb_coeffs[0] = 0.f;
253 if(dev->pipe)
254 {
256 dt_free(dev->pipe);
257 }
258 if(dev->preview_pipe)
259 {
261 dt_free(dev->preview_pipe);
262 }
264 dev->geometry_chain = NULL;
265
267 while(dev->history)
268 {
270 dev->history = g_list_delete_link(dev->history, dev->history);
271 }
274
275 // free pending "before" snapshots for history undo
276 dev->undo_history_depth = 0;
282
283 // free the transient param channel
286 dev->transient_params.params = NULL;
288 dev->transient_params.blend_params = NULL;
289 dev->transient_params.module = NULL;
292
293 while(dev->iop)
294 {
296 dt_free(dev->iop->data);
297 dev->iop = g_list_delete_link(dev->iop, dev->iop);
298 }
299 while(dev->alliop)
300 {
302 dt_free(dev->alliop->data);
303 dev->alliop = g_list_delete_link(dev->alliop, dev->alliop);
304 }
305 g_list_free_full(dev->iop_order_list, dt_free_gpointer);
306 dev->iop_order_list = NULL;
307
309 {
311 dev->color_picker.primary_sample = NULL;
312 }
313
315
317
318 // Overlay toolbar prefs may only be persisted by a gui_attached develop: transient devs
319 // (export, styles, snapshots, history copies) never expose the toolbar, run on worker
320 // threads, and would keep re-writing these keys on every background job — one corrupted
321 // or racing write is then resurrected as the user's settings at every later startup.
322 if(dev->gui_attached)
323 {
324 dt_conf_set_int("darkroom/ui/rawoverexposed/mode", dev->rawoverexposed.mode);
325 dt_conf_set_int("darkroom/ui/rawoverexposed/colorscheme", dev->rawoverexposed.colorscheme);
326 dt_conf_set_float("darkroom/ui/rawoverexposed/threshold", dev->rawoverexposed.threshold);
327
328 dt_conf_set_int("darkroom/ui/overexposed/mode", dev->overexposed.mode);
329 dt_conf_set_int("darkroom/ui/overexposed/colorscheme", dev->overexposed.colorscheme);
330 dt_conf_set_float("darkroom/ui/overexposed/lower", dev->overexposed.lower);
331 dt_conf_set_float("darkroom/ui/overexposed/upper", dev->overexposed.upper);
332 }
333}
334
335static gboolean _update_darkroom_roi(dt_develop_t *dev, dt_dev_pixelpipe_t *pipe, int *x, int *y, int *wd, int *ht,
336 float *scale);
337
339{
340 if(IS_NULL_PTR(dev)) return FALSE;
341
344
345 return dev->image_storage.id > 0
346 && dt_dev_viewport_box_width(dev) >= 32
347 && dt_dev_viewport_box_height(dev) >= 32
348 && geometry.raw_width >= 32
349 && geometry.raw_height >= 32
350 && geometry.processed_width >= 32
351 && geometry.processed_height >= 32
354}
355
357{
358 if(!dt_dev_geometry_raw_inited(dev) || !dt_dev_viewport_configured(dev)) return 1;
359
361 const int32_t raw_width = geometry.raw_width;
362 const int32_t raw_height = geometry.raw_height;
363
364 /* The chain is rebuilt BEFORE the size is decided now, because it is what decides it. It is
365 * cheap -- small derivations of already-committed parameters, no LUT, no colour transform, no
366 * disk -- which is the whole point of the exercise: the resync below is not. */
367 const double chain_start = dt_get_wtime();
369 const double chain_ms = (dt_get_wtime() - chain_start) * 1000.0;
370
371 /* The developed size, folded from the geometry records. Nothing is rebuilt to obtain it: that
372 * is what this service replaced, and what it cost is recorded in doc/geometry-service.md. */
373 int processed_width = 0;
374 int processed_height = 0;
375 if(!dt_geometry_chain_processed_size(dev->geometry_chain, &processed_width, &processed_height))
376 return 1;
377
378 dt_dev_geometry_set_processed_size(dev, processed_width, processed_height);
379
380 // Derive and publish everything the pipes plan from, as one record.
382
383 /* Shadow mode compares against the pipe's own fold, which only exists when the pipe was
384 * resynced above -- so it is asked for, and paid for, under `-d dev' and nowhere else. It runs
385 * STRICTLY AFTER both publications and never between them: those two are one atomic-looking
386 * update, the first moving the geometry record to the new size and the second moving the ROI
387 * request to match AND flagging the pipes to replan. Anything inserted between them widens the
388 * window in which the darkroom worker latches the OLD request against ALREADY NEW history,
389 * which is the mixed frame of #1157. An observer must not sit inside the update it observes. */
390 dt_geometry_self_check(dev, chain_ms);
391
392
394
395 const dt_dev_roi_request_t request = dt_dev_roi_request_get(dev);
396
398 "[pixelpipe] thumbnail sizes raw %dx%d -> processed %dx%d -> preview %dx%d (scale %.5f)\n",
399 raw_width, raw_height, request.processed_width, request.processed_height,
400 request.preview_width, request.preview_height, request.natural_scale);
401
402 return 0;
403}
404
406 const dt_iop_roi_t *roi)
407{
408 // The NULL checks come FIRST: this is called with dev->preview_pipe, which is allocated only
409 // for a gui_attached dev (dt_dev_init) and is therefore NULL on every export and thumbnail
410 // dev -- iop/denoiseprofile.c passes exactly that.
411 if(IS_NULL_PTR(dev) || IS_NULL_PTR(pipe) || !dev->gui_attached) return FALSE;
412
413 // Compare against what this pipe was planned from, not what the GUI has published since.
415 if(!request.valid) return FALSE;
416
417 int x = 0;
418 int y = 0;
419 int width = 0;
420 int height = 0;
421 float scale = request.natural_scale;
422
423 if(!IS_NULL_PTR(roi))
424 {
425 x = roi->x;
426 y = roi->y;
427 width = roi->width;
428 height = roi->height;
429 scale = roi->scale;
430 }
431 else
432 {
433 // Recompute the current darkroom output geometry so callers that run ahead of process()
434 // still classify the pipe from the image they are about to produce, not the last backbuffer.
436 }
437
438 // A module upstream of the orientation swap (the "flip" module) — e.g. ashift, demosaic,
439 // highlights — produces output whose width/height are swapped relative to the final, post-flip
440 // preview dimensions on portrait images. Accept that swapped match too: otherwise the
441 // "is this the full preview image?" test wrongly fails for every pre-flip module on portrait,
442 // and `roi` here is the module's own (pre-flip) `roi_out`. This is why ashift never captured its
443 // GUI buffer on portrait images, breaking structure detection and manual drawing (#710).
444 //
445 // Tolerate a couple of pixels of slack on the dimensions. `dev->roi.preview_*` is derived from the
446 // composed geometry at scale 1.0, whereas `roi` is produced at `natural_scale`; geometric modules
447 // (ashift, lens) round their transformed bounding box with floorf() independently at each scale,
448 // so the two legitimately disagree by ~1px for the very same full image. The real discriminators
449 // are the origin and scale tests below: a zoomed or panned ROI has a non-zero x/y and a scale
450 // strictly greater than natural_scale, so loosening the size match cannot misclassify those.
451 const int tol = 2;
452 const gboolean dims_match
453 = (abs(width - request.preview_width) <= tol && abs(height - request.preview_height) <= tol)
454 || (abs(width - request.preview_height) <= tol && abs(height - request.preview_width) <= tol);
455 if(!dims_match) return FALSE;
456 return x == 0 && y == 0 && fabsf(scale - request.natural_scale) < 1e-4f;
457}
458
459
460// Return TRUE if ROI changed since previous computation
461static gboolean _update_darkroom_roi(dt_develop_t *dev, dt_dev_pixelpipe_t *pipe, int *x, int *y, int *wd, int *ht,
462 float *scale)
463{
464 // The latched snapshot, not the live record: this runs on the worker, and the frame must be
465 // planned from the same numbers the rest of this iteration uses.
467 if(!request.valid) return 1;
468
469 // Store previous values
470 int x_old = *x;
471 int y_old = *y;
472 int wd_old = *wd;
473 int ht_old = *ht;
474 float old_scale = *scale;
475
476 // roi->scale is the pipeline sampling ratio against the processed image and
477 // therefore excludes the GUI backing-store density.
478 *scale = request.natural_scale;
479 const gboolean preview_pipe = (pipe == dev->preview_pipe);
480 if(!preview_pipe) *scale *= request.scaling;
481
482 // Width, height, x and y are already expressed in raster pixels, so they
483 // must follow the same raster-space sampling ratio as roi->scale. All of it comes from the
484 // latched record: mixing one live read into this arithmetic is exactly how a frame ends up
485 // describing a viewport that never existed.
486 int roi_width = roundf(*scale * request.processed_width);
487 int roi_height = roundf(*scale * request.processed_height);
488 int widget_wd = request.box_width;
489 int widget_ht = request.box_height;
490
491 *wd = roundf(fminf(roi_width, widget_wd));
492 *ht = roundf(fminf(roi_height, widget_ht));
493
494 // dt_dev_viewport_center_x(dev),y are the relative coordinates of the ROI center.
495 // in preview pipe, we always render a full image, so x,y = 0,0
496 // otherwise, x,y here are the top-left corner. Translate:
497 *x = preview_pipe ? 0 : roundf(request.center_x * roi_width - *wd * .5f);
498 *y = preview_pipe ? 0 : roundf(request.center_y * roi_height - *ht * .5f);
499
500/* fprintf (stderr, "_update_darkroom_roi: dev %.2f %.2f type %s xy %d %d dim %d %d"
501 " ppd:%.4f scale:%.4f nat_scale:%.4f * scaling:%.4f\n",
502 dt_dev_viewport_center_x(dev), dt_dev_viewport_center_y(dev), dt_pipe_type_to_str(pipe->type), *x, *y, *wd, *ht, dt_gui_get_global()->ppd, *scale, dt_dev_roi_request_natural_scale(dev), dt_dev_viewport_scaling(dev));
503*/
504 return x_old != *x || y_old != *y || wd_old != *wd || ht_old != *ht || old_scale != *scale;
505}
506
508{
509 if(IS_NULL_PTR(dev) || !dev->gui_attached || IS_NULL_PTR(dev->pipe) || IS_NULL_PTR(dev->preview_pipe) || !dt_dev_roi_request_valid(dev)) return FALSE;
510
511 float preview_scale = 1.0f;
512 float main_scale = 1.0f;
513 int preview_x = 0, preview_y = 0, preview_wd = 0, preview_ht = 0;
514 int main_x = 0, main_y = 0, main_wd = 0, main_ht = 0;
515
516 _update_darkroom_roi(dev, dev->preview_pipe, &preview_x, &preview_y, &preview_wd, &preview_ht, &preview_scale);
517 _update_darkroom_roi(dev, dev->pipe, &main_x, &main_y, &main_wd, &main_ht, &main_scale);
518
519 return preview_x == main_x && preview_y == main_y && preview_wd == main_wd && preview_ht == main_ht
520 && fabsf(preview_scale - main_scale) < 1e-4f;
521}
522
523
525{
526 const int32_t imgid = pipe->dev->image_storage.id;
527
528 // Get the mip size that is at most as big as our pipeline backbuf. One read: these dimensions
529 // are handed to the mipmap cache alongside the payload below, so they must describe it.
531 dt_mipmap_size_t mip = dt_mipmap_cache_get_fitting_size(published.width, published.height, imgid);
532
533 // Flush backup to mipmap_cache. This runs after dt_dev_pixelpipe_process() released the OpenCL device
534 // lock, so we must NOT pass pipe->devid (now stale/unlocked): a device-only payload would otherwise be
535 // materialized from the GPU without owning it. The final display backbuffer is always host-resident,
536 // so preferred_devid = -1 returns it directly; anything else is simply skipped.
537 uint8_t *data = NULL;
538 dt_pixel_cache_entry_t *entry = NULL;
540 && data)
541 {
545 dt_mipmap_cache_swap_at_size(imgid, mip, data, published.width, published.height,
546 settings.display_type);
549 }
550 else if(entry)
551 {
553 }
554}
555
556gboolean _resync_pipe_with_history(dt_develop_t *dev, dt_dev_pixelpipe_t *pipe, dt_iop_roi_t *roi, gboolean *needs_update)
557{
558 // When in realtime mode, preview pipe gets paused at the benefit of main pipeline.
559 // This is a transient state.
560 if(dt_atomic_get_int(&pipe->pause)) return FALSE;
561
562 // We recompute if history hash changed or ROI has changed.
563 // If we know history changed, ensure at least the last step is resynced.
565 const uint64_t dev_hash = dt_dev_get_history_hash(dev);
566 if(pipe_hash != dev_hash)
567 {
569 dt_print(DT_DEBUG_PIPE | DT_DEBUG_DEV, "dev history hash = %" PRIu64 ", pipe history hash %" PRIu64 "\n", dev_hash, pipe_hash);
570 }
571
573 if(!*needs_update) return FALSE;
574
576 pipe->processing = 1;
577
578 // Commit history to pipeline.
579 // This can take 40-80 ms or much more with masks on weak hardware,
580 // so user may have changed the history again during that lapse.
581 gboolean pipe_resynced = FALSE;
583 {
585 pipe_resynced = TRUE;
586 }
587
588 // Plan the ROI for this run and finalize the cumulative global hash now, while the pipe is
589 // settled and not yet publishing pixels. dt_dev_pixelpipe_process() recomputes both at its
590 // entry (it is also called directly by export/snapshot pipes), but computing them here is
591 // what lets the HISTORY_RESYNC signal below advertise a hash that is already final.
592 int x = 0, y = 0, wd = 0, ht = 0;
593 float scale = 1.f;
594 _update_darkroom_roi(dev, pipe, &x, &y, &wd, &ht, &scale);
595 *roi = (dt_iop_roi_t){ x, y, wd, ht, scale };
598
599 pipe->processing = 0;
601
602 return pipe_resynced;
603}
604
605
618{
619 dt_dev_pixelpipe_t *const pipes[] = { dev->preview_pipe, dev->pipe };
620
621 for(size_t i = 0; i < G_N_ELEMENTS(pipes); i++)
622 dt_atomic_set_int(&pipes[i]->running, TRUE);
623
624 // Infinite loop: run for as long as the worker thread is running.
625 while(!dev->exit && dt_control_running())
626 {
628 {
629 dt_iop_nap(50000); // wait 50 ms until GUI/image sizes are initialized
630 continue;
631 }
632
633 // Latch the viewport request ONCE for this iteration, before anything reads it. Everything
634 // downstream -- the ROI planner, the resync, every module callback -- then works from one
635 // snapshot for the whole frame, however many times the GUI thread republishes meanwhile.
636 // Reading it live at each consumer is what let a single frame be planned from two different
637 // viewport states.
638 const dt_dev_roi_request_t latched = dt_dev_roi_request_get(dev);
639 for(size_t i = 0; i < G_N_ELEMENTS(pipes); i++)
640 dt_dev_roi_request_latch(pipes[i], &latched);
641
642 // This is cheap to run, keep it in sync always.
644 const int32_t input_width = geometry.raw_width;
645 const int32_t input_height = geometry.raw_height;
646 for(size_t i = 0; i < G_N_ELEMENTS(pipes); i++)
647 dt_dev_pixelpipe_set_input(pipes[i], dev->image_storage.id, input_width, input_height,
648 1.0f, DT_MIPMAP_FULL);
649
650 gboolean pipe_needs_update[G_N_ELEMENTS(pipes)] = { FALSE };
651 dt_iop_roi_t pipe_roi[G_N_ELEMENTS(pipes)] = { { 0 } };
652 gboolean history_resynced = FALSE;
653
654 // First, resynchronize all dirty pipelines from history, plan their ROI and finalize their
655 // cumulative global hash, so GUI listeners can resolve stable piece->global_hash values
656 // before any cacheline starts publishing pixels.
657 for(size_t i = 0; i < G_N_ELEMENTS(pipes); i++)
658 history_resynced |= _resync_pipe_with_history(dev, pipes[i], &pipe_roi[i], &pipe_needs_update[i]);
659
660 // NOTE: at this point, we fully know the state of __all__ our GUI pipelines :
661 // - global image input and output size,
662 // - per-module input and output size,
663 // - per-module input and output format (channels, bit depth, mosaiced/raster, CFA pattern)
664 // - pipeline nodes (modules) params are up-to-to date with history,
665 // - global_hash of each module is and stable until next history resync,
666 // - modules whose expected input format is incompatible with previous module output format
667 // will have been disabled from pipeline, but not from history, aka we know our pipelines
668 // can run start to end.
669
670 // GUI widgets that need an image buffer will connect to this signal to grab
671 // the global_hash of the module they are waiting for, even though it's still not ready.
672 if(history_resynced)
674
675 // Second, compute pipelines.
676 // Always service preview first, then the main pipe, so the main pipe can reuse the cache state
677 // just published by preview instead of trying to race it from another thread.
678 for(size_t i = 0; i < G_N_ELEMENTS(pipes) && dt_control_running() && !dev->exit; i++)
679 {
680 if(!pipe_needs_update[i]) continue;
681
682 dt_dev_pixelpipe_t *pipe = pipes[i];
683 const dt_iop_roi_t roi = pipe_roi[i];
684
685 // The resync stage above synchronized history, planned the ROI and advertised the matching
686 // final global hash. We process the exact state it committed, using pipe_roi[i]: that is what
687 // dt_dev_pixelpipe_process() recomputes its hash from, so the cacheline it publishes always
688 // carries the hash already announced to GUI consumers. A change that lands after this point
689 // raises the killswitch together with its changed flag (`_change_pipe()`, and
690 // dt_dev_roi_request_publish() does the same on a payload change), so the run below aborts
691 // and the next loop iteration resyncs, re-advertises and reprocesses the new state. We must
692 // NOT skip on a set changed flag here: every darkroom configure-event flags the preview pipe
693 // ZOOMED, so on an image switch (which re-lays-out the view) skipping would starve the
694 // preview and leave navigation/scopes blank.
695 //
696 // A change that landed BETWEEN this iteration's latch and this point is another matter, and
697 // the check below is the second half of the #1157 fix. The resync's while(changed) loop
698 // spans 100-400 ms and consumes whatever flags land inside it, syncing their history -- but
699 // the ROI was then planned from THIS ITERATION'S latch, taken before any of it. Their
700 // killswitch does not save the run either, since the reset a few lines down would swallow
701 // it. Rendering the consumed history into the stale latched geometry is how a crop Apply
702 // produced a border-clamped smear that nothing ever corrected. So: if the published record
703 // has moved past the latch, this plan is dead. Re-flag (the flag may have been consumed) and
704 // skip the run; the next iteration re-latches and replans from the current record. This
705 // cannot starve: the generation only advances when a GUI publish actually changed the
706 // payload, so a quiet viewport lets the very next iteration through.
707 const dt_dev_roi_request_t current_request = dt_dev_roi_request_get(dev);
708 if(current_request.generation != latched.generation)
709 {
711 continue;
712 }
713
714 // Both pipes were resynced before the preview ran, so a history change committed while the
715 // preview was running (a module switched off mid-render, #1519) finds the main pipe planned
716 // from the old history: its killswitch is raised and its changed flag left set. The reset
717 // below would swallow that killswitch and render the stale history to completion, heavy
718 // modules included. The flag survives the skip, so the next iteration resyncs and runs the
719 // current state. Main pipe only: the preview runs first, right after the resync, and the
720 // configure-event ZOOMED flag landing inside that resync must not starve it (see above).
721 if(pipe == dev->pipe && dt_atomic_get_int(&pipe->shutdown)
723 continue;
724
725 dt_print(DT_DEBUG_PIPE | DT_DEBUG_DEV, "PIPE %s needs update\n", pipe->type == DT_DEV_PIXELPIPE_FULL ? "full" : "preview");
726
728 pipe->processing = 1;
729
730 // We are starting fresh, reset the killswitch signal. Safe only because the staleness
731 // check above ran after the resync: any killswitch this discards belonged to a change
732 // whose flag either survives (raised after the resync loop exited) or whose publish
733 // moved the generation (caught above). Do not move this reset earlier.
735
743 const gboolean retrying_raster_mask = dt_dev_pixelpipe_has_reentry(pipe);
744
745 // Whether the recompute was triggered because we needed only the output of
746 // a specified module, or we needed the output backbuf of the whole pipeline.
747 // This allows partial pipeline runs, e.g. for histograms and color-pickers.
749
750 // At zoom == fit, both preview and main pipelines have the same size,
751 // so the first one that runs will prevent the next from running
752 // (backbuf fetched directly from pipeline cache).
753 // Therefore we can't rely solely on pipeline type to raise completion signals.
754 const gboolean has_preview_size = dt_dev_pixelpipe_has_preview_output(dev, pipe, &roi);
755
761 const gboolean requested_mask_preview
762 = pipe == dev->pipe
763 && !IS_NULL_PTR(dev->gui_module)
765
772 dt_colorprofiles_settings_t profile_settings;
773 dt_colorprofiles_get_settings(&profile_settings);
774 const gboolean requested_overlay
775 = pipe == dev->pipe
777 || profile_settings.mode != DT_PROFILE_NORMAL);
778
779 // Connect GUI feedback for "pipe busy"
782 dev->progress.completed = 0;
783 dev->progress.total = 0;
784
785 dt_times_t thread_start;
786 dt_get_times(&thread_start);
787
788 // The actual processing with runtime log
789 const gint64 process_start_us = g_get_monotonic_time();
790 const int ret = dt_dev_pixelpipe_process(pipe, roi);
791 const gint64 process_runtime_us = g_get_monotonic_time() - process_start_us;
792
793 // Print perf log
794 gchar *msg = g_strdup_printf("[dev_process_%s] pipeline processing thread",
796 dt_show_times(&thread_start, msg);
797 dt_free(msg);
798
799 // Disconnect GUI feedback for "pipe busy"
800 dev->progress.completed = 0;
801 dev->progress.total = 0;
804
805 // Pipeline completed entirely without error
806 const gboolean processed = (!ret && !dt_atomic_get_int(&pipe->shutdown));
807
815 const gboolean published_backbuffer
817
818 // Pipeline reentry flag is set when we lost the reference to a raster mask.
819 // This typically happens on re-entering darkroom after having gone to lighttable:
820 // the pipeline cache is kept but the references to raster masks are flushed,
821 // so the pipeline recomputation resumes downstream from the last-known cacheline,
822 // which may not refresh raster masks if produced upstream in pipeline.
823 // TODO: cache raster masks too (was attempted before, and failed).
825 {
826 if(retrying_raster_mask)
827 {
828 // Reentry flag was set already before last pipe run, which refreshed
829 // everything we needed. We can resume to normal mode.
830 // In case that wasn't true, it will be caught at the next run.
832 }
833 else
834 {
835 // Reentry flag was set during the last pipe run, which means we
836 // lost at least a raster mask reference, and need to retry again
837 // from the start.
838 // The synchronized graph and its ROIs are still valid. The retry
839 // only needs another processing pass after targeted cache
840 // invalidation, not node destruction and history reconstruction.
842 }
843 }
844
853 if(processed
854 && cache_request == DT_DEV_PIXELPIPE_CACHE_REQUEST_MODULE
855 && !published_backbuffer)
856 {
860 }
861
862 pipe->processing = 0;
864
865 // Update the running average of process time for GUI controls thresholding
866 if(processed)
867 {
868 // Map the pipe onto the throttle's own two slots; it does not know pipeline types.
872 dt_gui_throttle_record_runtime(slot, process_runtime_us);
873 }
874
875 // If everything went well, yell to GUI listeners that they can use the output buffer.
876 if(published_backbuffer)
877 {
879 {
882 }
883 if(pipe->type == DT_DEV_PIXELPIPE_PREVIEW || has_preview_size)
884 {
887 }
888
905 && has_preview_size
906 && !requested_mask_preview
907 && !requested_overlay
910 }
911
912 /* Breathing room for the OS and the GPU, sized against the work just done rather than
913 * fixed. A realtime stroke paces itself upstream -- the publisher only republishes once
914 * per measured pipe runtime -- so a constant here is not what stops this loop spinning;
915 * it is pure latency added to every frame. Two 10 ms naps were ~15% of a 130 ms frame
916 * when this was written and are 41% of a 29 ms one, which is how a constant ages when
917 * the thing it sits beside gets three times faster. */
919 }
920
921 /* Nap only when this turn found nothing to do. Having serviced a pipe, go straight back and
922 * look again: the publish throttle decides how often there is new work, and sleeping past
923 * its next publish just moves the stroke further behind the pointer. */
924 const gboolean serviced = pipe_needs_update[0] || pipe_needs_update[1];
926 dt_iop_nap(serviced ? 500 : 10000);
927 else
928 dt_iop_nap(serviced ? 1000 : 50000);
929 }
930
931 for(size_t i = 0; i < G_N_ELEMENTS(pipes); i++)
932 dt_atomic_set_int(&pipes[i]->running, FALSE);
933}
934
936{
939 return 0;
940}
941
943{
944 dt_job_t *job = dt_control_job_create(&dt_dev_process_job_run, "develop process image");
945 if(IS_NULL_PTR(job)) return NULL;
946 dt_control_job_set_params(job, dev, NULL);
947 return job;
948}
949
956
957static gboolean _dt_dev_mipmap_prefetch_full(dt_develop_t *dev, const int32_t imgid)
958{
961
962 const gboolean ok = (!IS_NULL_PTR(buf.buf)) && buf.width != 0 && buf.height != 0;
963
964 // dt_dev_geometry_raw_width(dev)/height are the raw image's own pixel dimensions -- an objective fact
965 // about the loaded buffer, not GUI viewport state -- and must be set for every dev, not just
966 // gui_attached ones. dt_dev_coordinates_raw_norm_to_raw_abs() (develop.c) and every drawn-mask
967 // shape's own geometry function (masks/circle.c, ellipse.c, brush.c, gradient.c, polygon.c) read
968 // dt_dev_geometry_raw_width(dev)/height to convert a form's normalized center/points into absolute pixel
969 // coordinates; with raw_width/height left at 0 (their calloc default), that conversion silently
970 // no-ops (dt_dev_coordinates_raw_norm_to_raw_abs() early-returns on raw_width==0), leaving the
971 // shape's normalized (0..1) coordinates masquerading as pixel coordinates -- collapsing every
972 // shape's computed position to somewhere near the image origin regardless of where it was
973 // actually drawn. That made every drawn mask on any non-GUI dev (export, thumbnail generation,
974 // dev_snapshot.c's frozen dev) resolve to the wrong location, so a module needing mask history
975 // (retouch's clone/heal/blur/fill, or any masked blend) either processes empty geometry or
976 // silently produces zero visible effect outside the live darkroom.
977 // Publish what the read actually produced. `ok' is FALSE when the mipmap cache handed back
978 // no buffer or a 0-sized one; claiming raw_inited in that case told every later reader that
979 // 0x0 was a measured fact about the image, and dt_dev_geometry_refresh()'s own guard
980 // (dt_dev_geometry_raw_inited) then let the size fold run on it.
981 dt_dev_geometry_set_raw_size(dev, ok ? buf.width : 0, ok ? buf.height : 0, ok);
982
983 /* The entry could not be allocated just now. A decode that ran out of memory leaves an entry
984 * behind, and dt_imageio_open() says so itself, in the same window. */
986 {
987 char path[DT_PATH_MAX] = { 0 };
988 dt_image_full_path(imgid, path, sizeof(path), NULL, __FUNCTION__);
989 dt_control_alert_once(_("Not enough memory"),
990 _("Not enough memory to load the image at full size.\n"
991 "Close other applications or add more RAM to your system."),
992 path[0] ? path : NULL);
993 }
994
996
997 return ok;
998}
999
1000static gboolean _dt_dev_refresh_image_storage(dt_develop_t *dev, const int32_t imgid)
1001{
1002 const dt_image_t *image = dt_image_cache_get(imgid, 'r');
1003 if(IS_NULL_PTR(image)) return FALSE;
1004 dev->image_storage = *image;
1007 return TRUE;
1008}
1009
1011{
1012 if(IS_NULL_PTR(dev) || imgid <= 0 || !dt_image_cache_is_ready())
1014
1015 if(!_dt_dev_mipmap_prefetch_full(dev, imgid))
1017
1018 if(!_dt_dev_refresh_image_storage(dev, imgid))
1020
1022}
1023
1024// load the raw and get the new image struct, blocking in gui thread
1025static inline dt_dev_image_storage_t _dt_dev_load_raw(dt_develop_t *dev, const int32_t imgid)
1026{
1027 // then load the raw
1028 dt_times_t start;
1029 dt_get_times(&start);
1030
1031 // Test we got images. Also that populates the cache for later.
1032 // Refresh our private copy in case raw loading updated image metadata
1033 const dt_dev_image_storage_t storage_status = dt_dev_ensure_image_storage(dev, imgid);
1034 if(storage_status)
1035 return storage_status;
1036
1037 dt_show_times_f(&start, "[dev_pixelpipe]", "to load the image.");
1038
1039 return storage_status;
1040}
1041
1042// return the zoom scale to fit into the viewport
1043float dt_dev_get_zoom_scale(const dt_develop_t *dev, const gboolean preview)
1044{
1046 const int32_t processed_width = geometry.processed_width;
1047 const int32_t processed_height = geometry.processed_height;
1048
1049 const float w = preview ? processed_width : dev->pipe->processed_width;
1050 const float h = preview ? processed_height : dev->pipe->processed_height;
1051 return fminf(dt_dev_viewport_box_width(dev) / w, dt_dev_viewport_box_height(dev) / h);
1052}
1053
1055{
1056 const dt_dev_image_storage_t ret = _dt_dev_load_raw(dev, imgid);
1057 if(ret) return ret;
1058
1059 // we need a global lock as the dev->iop set must not be changed until read history is terminated
1061
1062 const gboolean first_run = dt_dev_read_history_ext(dev, imgid);
1063
1064 if(first_run && dev == dt_dev_get_global())
1065 {
1066 // Resync our private copy of image image with DB,
1067 // mostly for DT_IMAGE_AUTO_PRESETS_APPLIED flag.
1068 dt_image_t *image = dt_image_cache_get(imgid, 'w');
1069 if(!IS_NULL_PTR(image))
1070 {
1071 *image = dev->image_storage;
1073 }
1074
1075 dt_dev_write_history_ext(dev, imgid);
1076 }
1077
1079
1080 if(first_run && dev == dt_dev_get_global())
1081 {
1083 dt_dev_history_notify_change(dev, imgid);
1084 }
1085
1086 return ret;
1087}
1088
1089void dt_dev_configure_real(dt_develop_t *dev, int wd, int ht)
1090{
1091 // Called only from Darkroom to convert the widget allocation into the
1092 // raster ROI contract consumed by the pipeline. Everything stored in
1093 // dev->roi below is expressed in real buffer pixels.
1094 const dt_iop_roi_t gui_roi = { .x = 0, .y = 0, .width = wd, .height = ht, .scale = 1.0f };
1095 dt_iop_roi_t pipe_roi = { 0 };
1097
1098 /* A configure that changes nothing does nothing. GTK delivers a configure-event to the centre
1099 * whenever its window is allocated again, and a layout pass runs whenever any widget's size
1100 * request changes -- the theme gives every hovered tab and button a heavier font weight, so
1101 * that is every hover. The box is what the pipes plan from; when it comes back the same as
1102 * the one already published there is nothing to re-plan, no thumbnail size to recompute, and
1103 * no reason to redraw the whole window (every panel, the navigation, the scopes). Only the
1104 * first configure and a real size, border or zoom change get past this. */
1105 if(!dt_dev_viewport_set_box(dev, pipe_roi.width, pipe_roi.height))
1106 {
1107 dt_print(DT_DEBUG_DEV, "[pixelpipe] Darkroom requested a %i×%i px widget -> %i×%i px raster preview, unchanged\n",
1109 return;
1110 }
1111
1113 "[pixelpipe] Darkroom requested a %i×%i px widget -> %i×%i px raster preview\n",
1115
1120}
1121
1133{
1134 const dt_dev_viewport_state_t viewport = dt_dev_viewport_get(dev);
1135 float center_x = viewport.center_x;
1136 float center_y = viewport.center_y;
1137 dt_dev_check_zoom_pos_bounds(dev, &center_x, &center_y, NULL, NULL);
1138 return dt_dev_viewport_set_center(dev, center_x, center_y);
1139}
1140
1141void dt_dev_check_zoom_pos_bounds(dt_develop_t *dev, float *dev_x, float *dev_y, float *box_w, float *box_h)
1142{
1143 // for the debug strings lower
1144 //float old_x = *dev_x;
1145 //float old_y = *dev_y;
1146 int proc_w = 0;
1147 int proc_h = 0;
1148 dt_dev_get_processed_size(dev, &proc_w, &proc_h);
1149 const float scale = dt_dev_get_zoom_level(dev);
1150
1151 // find the box size
1152 const float bw = dt_dev_viewport_box_width(dev) / (proc_w * scale);
1153 const float bh = dt_dev_viewport_box_height(dev) / (proc_h * scale);
1154
1155 // calculate half-dimensions once
1156 const float half_bw = bw * 0.5f;
1157 const float half_bh = bh * 0.5f;
1158
1159 // clamp position using pre-calculated values
1160 *dev_x = (bw > 1.0f || dt_dev_viewport_scaling(dev) <= 1.0f) ? 0.5f : CLAMPF(*dev_x, half_bw, 1.0f - half_bw);
1161 *dev_y = (bh > 1.0f || dt_dev_viewport_scaling(dev) <= 1.0f) ? 0.5f : CLAMPF(*dev_y, half_bh, 1.0f - half_bh);
1162 // return box size
1163 if(!IS_NULL_PTR(box_w)) *box_w = bw;
1164 if(!IS_NULL_PTR(box_h)) *box_h = bh;
1165
1166 /*
1167 fprintf(stdout, "BOUNDS: box size: %2.2f x %2.2f\n", bw, bh);
1168 fprintf(stdout, "BOUNDS: half box size: %2.2f x %2.2f\n", half_bw, half_bh);
1169 fprintf(stdout, "BOUNDS: X pos: %2.2f -> %2.2f [%2.2f %2.2f]\n",
1170 old_x, *dev_x, half_bw, 1.0f - half_bw);
1171 fprintf(stdout, "BOUNDS: Y pos: %2.2f -> %2.2f [%2.2f %2.2f]\n",
1172 old_y, *dev_y, half_bh, 1.0f - half_bh);
1173*/
1174}
1175
1176void dt_dev_get_processed_size(const dt_develop_t *dev, int *procw, int *proch)
1177{
1178 // Write the outputs on EVERY path. Callers declare them uninitialized and read them straight
1179 // back -- iop/crop.c's _aspect_apply() does `int iwd, iht; dt_dev_get_processed_size(...);
1180 // if(iwd < iht)` -- so returning without writing hands them whatever was on the stack. Zero
1181 // is not a meaningful size, but it is a deterministic one, and it is what those callers
1182 // already behave sanely for; garbage is what they could not.
1183 if(!IS_NULL_PTR(procw)) *procw = 0;
1184 if(!IS_NULL_PTR(proch)) *proch = 0;
1185
1186 if(IS_NULL_PTR(dev)) return;
1188 const int32_t processed_width = geometry.processed_width;
1189 const int32_t processed_height = geometry.processed_height;
1190 if(!IS_NULL_PTR(procw)) *procw = processed_width;
1191 if(!IS_NULL_PTR(proch)) *proch = processed_height;
1192}
1193
1194void dt_dev_coordinates_widget_delta_to_image_delta(dt_develop_t *dev, float *points, size_t num_points)
1195{
1196 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1197
1198 const float scale = dt_dev_get_zoom_level(dev) / dt_gui_get_global()->ppd;
1199 if(scale == 0.0f) return;
1200
1201 // Widget deltas are measured in Gtk logical pixels. Convert them to processed-image
1202 // pixels here so dragging thresholds and keyboard pans share the same zoom math.
1203 for(size_t i = 0; i < num_points; ++i)
1204 {
1205 const size_t idx = i * 2;
1206 points[idx + 0] /= scale;
1207 points[idx + 1] /= scale;
1208 }
1209}
1210
1211void dt_dev_coordinates_widget_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
1212{
1213 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1215 const float processed_width = geometry.processed_width;
1216 const float processed_height = geometry.processed_height;
1217 if(processed_width == 0.0f || processed_height == 0.0f) return;
1218
1219 // Widget events are expressed in GUI logical coordinates, while the pipeline
1220 // zoom lives in raster pixels. Convert back to the same GUI-space zoom used
1221 // by dt_dev_rescale_roi() so event hit-testing and overlay drawing stay aligned.
1222 const float scale = dt_dev_get_zoom_level(dev) / dt_gui_get_global()->ppd;
1223 const float roi_x = (float)dt_dev_viewport_center_x(dev);
1224 const float roi_y = (float)dt_dev_viewport_center_y(dev);
1225 const float center_x = 0.5f * (float)dt_dev_viewport_widget_width(dev);
1226 const float center_y = 0.5f * (float)dt_dev_viewport_widget_height(dev);
1227 const float inv_scaled_width = 1.0f / (processed_width * scale);
1228 const float inv_scaled_height = 1.0f / (processed_height * scale);
1229
1230 for(size_t i = 0; i < num_points; ++i)
1231 {
1232 const size_t idx = i * 2;
1233 const float px = points[idx + 0];
1234 const float py = points[idx + 1];
1235 points[idx + 0] = roi_x + (px - center_x) * inv_scaled_width;
1236 points[idx + 1] = roi_y + (py - center_y) * inv_scaled_height;
1237 }
1238}
1239
1240void dt_dev_coordinates_image_norm_to_widget(dt_develop_t *dev, float *points, size_t num_points)
1241{
1242 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1244 const float processed_width = geometry.processed_width;
1245 const float processed_height = geometry.processed_height;
1246 if(processed_width == 0.0f || processed_height == 0.0f) return;
1247
1248 // GUI overlays are drawn in logical widget coordinates, so use the same
1249 // GUI-space zoom that the Cairo darkroom transform applies.
1250 const float scale = dt_dev_get_zoom_level(dev) / dt_gui_get_global()->ppd;
1251 const float roi_x = (float)dt_dev_viewport_center_x(dev);
1252 const float roi_y = (float)dt_dev_viewport_center_y(dev);
1253 const float scaled_width = processed_width * scale;
1254 const float scaled_height = processed_height * scale;
1255 const float center_x = 0.5f * (float)dt_dev_viewport_widget_width(dev);
1256 const float center_y = 0.5f * (float)dt_dev_viewport_widget_height(dev);
1257
1258 for(size_t i = 0; i < num_points; ++i)
1259 {
1260 const size_t idx = i * 2;
1261 const float px = points[idx + 0];
1262 const float py = points[idx + 1];
1263 const float dx = (px - roi_x) * scaled_width;
1264 const float dy = (py - roi_y) * scaled_height;
1265 points[idx + 0] = dx + center_x;
1266 points[idx + 1] = dy + center_y;
1267 }
1268}
1269
1270void dt_dev_coordinates_image_norm_to_image_abs(dt_develop_t *dev, float *points, size_t num_points)
1271{
1272 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1274 const float processed_width = geometry.processed_width;
1275 const float processed_height = geometry.processed_height;
1276 if(processed_width == 0.0f || processed_height == 0.0f) return;
1277
1278 for(size_t i = 0; i < num_points; ++i)
1279 {
1280 const size_t idx = i * 2;
1281 points[idx + 0] *= processed_width;
1282 points[idx + 1] *= processed_height;
1283 }
1284}
1285
1286void dt_dev_coordinates_image_abs_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
1287{
1288 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1290 const float processed_width = geometry.processed_width;
1291 const float processed_height = geometry.processed_height;
1292 if(processed_width == 0.0f || processed_height == 0.0f) return;
1293
1294 const float inv_width = 1.0f / processed_width;
1295 const float inv_height = 1.0f / processed_height;
1296 for(size_t i = 0; i < num_points; ++i)
1297 {
1298 const size_t idx = i * 2;
1299 points[idx + 0] *= inv_width;
1300 points[idx + 1] *= inv_height;
1301 }
1302}
1303
1304void dt_dev_coordinates_raw_abs_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
1305{
1306 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1308 const float raw_width = geometry.raw_width;
1309 const float raw_height = geometry.raw_height;
1310 if(raw_width == 0.0f || raw_height == 0.0f) return;
1311
1312 const float inv_width = 1.f / raw_width;
1313 const float inv_height = 1.f / raw_height;
1314 for(size_t i = 0; i < num_points; i++)
1315 {
1316 const size_t idx = i * 2;
1317 points[idx + 0] *= inv_width;
1318 points[idx + 1] *= inv_height;
1319 }
1320}
1321
1322void dt_dev_coordinates_raw_norm_to_raw_abs(dt_develop_t *dev, float *points, size_t num_points)
1323{
1324 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1326 const float raw_width = geometry.raw_width;
1327 const float raw_height = geometry.raw_height;
1328 if(raw_width == 0.0f || raw_height == 0.0f) return;
1329
1330 for(size_t i = 0; i < num_points; i++)
1331 {
1332 const size_t idx = i * 2;
1333 points[idx + 0] *= raw_width;
1334 points[idx + 1] *= raw_height;
1335 }
1336}
1337
1338void dt_dev_coordinates_image_norm_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
1339{
1340 dt_dev_coordinates_image_norm_to_image_abs(dev, points, num_points);
1341 dt_dev_coordinates_image_abs_to_raw_abs(dev, points, num_points);
1342 dt_dev_coordinates_raw_abs_to_raw_norm(dev, points, num_points);
1343}
1344
1345void dt_dev_coordinates_raw_norm_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
1346{
1347 dt_dev_coordinates_raw_norm_to_raw_abs(dev, points, num_points);
1348 dt_dev_coordinates_raw_abs_to_image_abs(dev, points, num_points);
1349 dt_dev_coordinates_image_abs_to_image_norm(dev, points, num_points);
1350}
1351
1352void dt_dev_coordinates_image_abs_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
1353{
1354 dt_dev_coordinates_image_abs_to_raw_abs(dev, points, num_points);
1355 dt_dev_coordinates_raw_abs_to_raw_norm(dev, points, num_points);
1356}
1357
1358void dt_dev_coordinates_image_norm_to_preview_abs(dt_develop_t *dev, float *points, size_t num_points)
1359{
1360 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1361 const float preview_width = dt_dev_roi_request_preview_width(dev);
1362 const float preview_height = dt_dev_roi_request_preview_height(dev);
1363 if(preview_width == 0.0f || preview_height == 0.0f) return;
1364
1365 for(size_t i = 0; i < num_points; i++)
1366 {
1367 const size_t idx = i * 2;
1368 points[idx + 0] *= preview_width;
1369 points[idx + 1] *= preview_height;
1370 }
1371}
1372
1373void dt_dev_coordinates_preview_abs_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
1374{
1375 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1376 const float preview_width = dt_dev_roi_request_preview_width(dev);
1377 const float preview_height = dt_dev_roi_request_preview_height(dev);
1378 if(preview_width == 0.0f || preview_height == 0.0f) return;
1379
1380 const float inv_width = 1.f / preview_width;
1381 const float inv_height = 1.f / preview_height;
1382 for(size_t i = 0; i < num_points; i++)
1383 {
1384 const size_t idx = i * 2;
1385 points[idx + 0] *= inv_width;
1386 points[idx + 1] *= inv_height;
1387 }
1388}
1389
1391{
1392 return (dev->image_storage.id == imgid) ? 1 : 0;
1393}
1394
1400
1402{
1403 return dev && dev->proxy.masks.module && dev->proxy.masks.is_visible
1404 && dev->proxy.masks.is_visible(dev->proxy.masks.module);
1405}
1406
1408{
1409 if(dev->proxy.masks.module && dev->proxy.masks.list_change)
1410 dev->proxy.masks.list_change(dev->proxy.masks.module);
1411}
1413{
1414 if(dev->proxy.masks.module && dev->proxy.masks.list_update)
1415 dev->proxy.masks.list_update(dev->proxy.masks.module);
1416}
1417void dt_dev_masks_list_remove(dt_develop_t *dev, int formid, int parentid)
1418{
1419 if(dev->proxy.masks.module && dev->proxy.masks.list_remove)
1420 dev->proxy.masks.list_remove(dev->proxy.masks.module, formid, parentid);
1421}
1423 const int selectid, const int throw_event)
1424{
1425 if(dev->proxy.masks.module && dev->proxy.masks.selection_change)
1426 dev->proxy.masks.selection_change(dev->proxy.masks.module, module, selectid, throw_event);
1427}
1428
1429void dt_dev_snapshot_request(dt_develop_t *dev, const char *filename)
1430{
1431 dev->proxy.snapshot.filename = filename;
1432 dev->proxy.snapshot.request = TRUE;
1434}
1435
1438{
1439 // we create the new module
1440 dt_iop_module_t *module = (dt_iop_module_t *)calloc(1, sizeof(dt_iop_module_t));
1441 if(dt_iop_load_module(module, base->so, base->dev)) return NULL;
1442 module->instance = base->instance;
1443
1444 // we set the multi-instance priority and the iop order
1445 int pmax = 0;
1446 for(GList *modules = base->dev->iop; modules; modules = g_list_next(modules))
1447 {
1448 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1449 if(mod->instance == base->instance)
1450 {
1451 if(pmax < mod->multi_priority) pmax = mod->multi_priority;
1452 }
1453 }
1454 // create a unique multi-priority
1455 pmax += 1;
1456 dt_iop_update_multi_priority(module, pmax);
1457
1458 // add this new module position into the iop-order-list
1460
1461 // since we do not rename the module we need to check that an old module does not have the same name. Indeed
1462 // the multi_priority
1463 // are always rebased to start from 0, to it may be the case that the same multi_name be generated when
1464 // duplicating a module.
1465 int pname = module->multi_priority;
1466 char mname[128];
1467
1468 do
1469 {
1470 snprintf(mname, sizeof(mname), "%d", pname);
1471 gboolean dup = FALSE;
1472
1473 for(GList *modules = base->dev->iop; modules; modules = g_list_next(modules))
1474 {
1475 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1476 if(mod->instance == base->instance)
1477 {
1478 if(strcmp(mname, mod->multi_name) == 0)
1479 {
1480 dup = TRUE;
1481 break;
1482 }
1483 }
1484 }
1485
1486 if(dup)
1487 pname++;
1488 else
1489 break;
1490 } while(1);
1491
1492 // the multi instance name
1493 g_strlcpy(module->multi_name, mname, sizeof(module->multi_name));
1494 // we insert this module into dev->iop
1495 base->dev->iop = g_list_insert_sorted(base->dev->iop, module, dt_sort_iop_by_order);
1496
1497 // always place the new instance after the base one
1498 if(!dt_ioppr_move_iop_after(base->dev, module, base))
1499 {
1500 fprintf(stderr, "[dt_dev_module_duplicate] can't move new instance after the base one\n");
1501 }
1502
1503 // that's all. rest of insertion is gui work !
1504 return module;
1505}
1506
1507
1508
1510{
1511 // if(dt_gui_widgets_suppressed()) return;
1512 int del = 0;
1513
1514 if(dev->gui_attached)
1515 {
1518
1519 const int history_end = dt_dev_get_history_end_ext(dev);
1520 int removed_before_end = 0;
1521 int history_pos = 0;
1522 GList *elem = dev->history;
1523 while(!IS_NULL_PTR(elem))
1524 {
1525 GList *next = g_list_next(elem);
1526 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(elem->data);
1527
1528 if(module == hist->module)
1529 {
1530 dt_print(DT_DEBUG_HISTORY, "[dt_module_remode] removing obsoleted history item: %s %s %p %p\n",
1531 hist->op_name, hist->multi_name, module, hist->module);
1533 dev->history = g_list_delete_link(dev->history, elem);
1534 if(history_pos < history_end) removed_before_end++;
1535 del = 1;
1536 }
1537 history_pos++;
1538 elem = next;
1539 }
1540
1541 if(removed_before_end > 0)
1542 dt_dev_set_history_end_ext(dev, MAX(0, history_end - removed_before_end));
1543
1547 }
1548
1549 // and we remove it from the list
1550 for(GList *modules = dev->iop; modules; modules = g_list_next(modules))
1551 {
1552 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1553 if(mod == module)
1554 {
1555 dev->iop = g_list_delete_link(dev->iop, modules);
1556 break;
1557 }
1558 }
1559}
1560
1568
1571{
1572 const int nb_instances = GPOINTER_TO_INT(
1573 g_hash_table_lookup(state->instance_counts, GINT_TO_POINTER(module->instance)));
1574
1575 dt_iop_module_t *mod_prev = (dt_iop_module_t *)g_hash_table_lookup(state->prev_visible, module);
1576 dt_iop_module_t *mod_next = (dt_iop_module_t *)g_hash_table_lookup(state->next_visible, module);
1577
1578 const gboolean move_next = mod_next
1579 ? dt_ioppr_check_can_move_after_iop(dev->iop, module, mod_next)
1580 : -1.0;
1581 const gboolean move_prev = mod_prev
1582 ? dt_ioppr_check_can_move_before_iop(dev->iop, module, mod_prev)
1583 : -1.0;
1584
1585 if(IS_NULL_PTR(module->gui)) return; // headless module: no buttons to update
1586
1587 module->gui->multi_show_new = !(module->flags() & IOP_FLAGS_ONE_INSTANCE);
1588 // Never allow deleting the base instance (multi_priority == 0) nor modules limited to one instance.
1589 module->gui->multi_show_close =
1590 (nb_instances > 1 && module->multi_priority > 0 && !(module->flags() & IOP_FLAGS_ONE_INSTANCE));
1591 if(!IS_NULL_PTR(mod_next))
1592 module->gui->multi_show_up = move_next;
1593 else
1594 module->gui->multi_show_up = 0;
1595 if(!IS_NULL_PTR(mod_prev))
1596 module->gui->multi_show_down = move_prev;
1597 else
1598 module->gui->multi_show_down = 0;
1599
1600 // If it's an additional instance supposed to be added by an history item after
1601 // the current history_end cursor, conceptually it doesn't exist yet,
1602 // even though it's dangling there on the pipe. So hide it from GUI.
1603 if(nb_instances > 1
1604 && module->multi_priority > 0
1605 && !g_hash_table_contains(state->modules_in_history, module))
1606 gtk_widget_hide(module->gui->expander);
1607}
1608
1609// FIXME: this function should just disappear, as it mixes concepts from multi-instances from before
1610// pipeline reordering and pipeline reordering.
1611// Multi-instances concept should just be ditched entirely.
1613{
1614 dt_ioppr_check_iop_order(dev, 0, "dt_dev_modules_update_multishow");
1615 const int history_end = dt_dev_get_history_end_ext(dev);
1616
1618 state.instance_counts = g_hash_table_new(g_direct_hash, g_direct_equal);
1619 state.modules_in_history = g_hash_table_new(g_direct_hash, g_direct_equal);
1620 state.prev_visible = g_hash_table_new(g_direct_hash, g_direct_equal);
1621 state.next_visible = g_hash_table_new(g_direct_hash, g_direct_equal);
1622
1623 // Precompute how many instances exist for each base module.
1624 for(GList *modules = dev->iop; modules; modules = g_list_next(modules))
1625 {
1626 const dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1627 gpointer key = GINT_TO_POINTER(mod->instance);
1628 int count = GPOINTER_TO_INT(g_hash_table_lookup(state.instance_counts, key));
1629 g_hash_table_replace(state.instance_counts, key, GINT_TO_POINTER(count + 1));
1630 }
1631
1632 // Precompute which modules exist in history up to history_end.
1633 int history_pos = 0;
1634 for(GList *history = g_list_first(dev->history);
1635 history && history_pos < history_end;
1636 history = g_list_next(history), history_pos++)
1637 {
1638 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)history->data;
1639 if(hist->module) g_hash_table_add(state.modules_in_history, hist->module);
1640 }
1641
1642 // Precompute previous visible module in pipeline order.
1643 dt_iop_module_t *last_visible = NULL;
1644 for(GList *modules = g_list_first(dev->iop); modules; modules = g_list_next(modules))
1645 {
1646 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1648 {
1649 g_hash_table_insert(state.prev_visible, mod, last_visible);
1650 last_visible = mod;
1651 }
1652 }
1653
1654 // Precompute next visible module in GUI order (reverse pipeline).
1655 last_visible = NULL;
1656 for(GList *modules = g_list_last(dev->iop); modules; modules = g_list_previous(modules))
1657 {
1658 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1660 {
1661 g_hash_table_insert(state.next_visible, mod, last_visible);
1662 last_visible = mod;
1663 }
1664 }
1665
1666 for(GList *modules = dev->iop; modules; modules = g_list_next(modules))
1667 {
1668 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1669
1672 }
1673
1674 g_hash_table_destroy(state.instance_counts);
1675 g_hash_table_destroy(state.modules_in_history);
1676 g_hash_table_destroy(state.prev_visible);
1677 g_hash_table_destroy(state.next_visible);
1678}
1679
1680gchar *dt_history_item_get_label(const struct dt_iop_module_t *module)
1681{
1682 gchar *label;
1683 /* create a history button and add to box */
1684 if(!module->multi_name[0] || strcmp(module->multi_name, "0") == 0)
1685 label = g_strdup(module->name());
1686 else
1687 {
1688 // multi_name is free-typed user text (dt_iop_gui_rename_module()), but this label is
1689 // rendered through gtk_label_set_markup_with_mnemonic() so that module->name()'s own
1690 // mnemonic keeps working. Escape it for markup and double its underscores so it prints
1691 // literally instead of breaking markup parsing ("&") or being eaten as a mnemonic ("_").
1692 gchar *escaped_multi_name = g_markup_escape_text(module->multi_name, -1);
1693 gchar **underscore_parts = g_strsplit(escaped_multi_name, "_", -1);
1694 gchar *safe_multi_name = g_strjoinv("__", underscore_parts);
1695 g_strfreev(underscore_parts);
1696 dt_free(escaped_multi_name);
1697
1698 label = g_strdup_printf("%s %s", module->name(), safe_multi_name);
1699 dt_free(safe_multi_name);
1700 }
1701 return label;
1702}
1703
1704gchar *dt_dev_get_multi_name(const struct dt_iop_module_t *module)
1705{
1706 gboolean has_multi_name = g_strcmp0(module->multi_name, "0") != 0 && g_strcmp0(module->multi_name, "") != 0;
1707 gchar *label = has_multi_name ? g_strdup(module->multi_name) : g_strdup("");
1708
1709 return label;
1710}
1711
1712gchar *dt_history_item_get_name(const struct dt_iop_module_t *module)
1713{
1714 gchar *label;
1715 /* create a history button and add to box */
1716 if(!module->multi_name[0] || strcmp(module->multi_name, "0") == 0)
1717 label = delete_underscore(module->name());
1718 else
1719 {
1720 gchar *clean_name = delete_underscore(module->name());
1721 label = g_strdup_printf("%s %s", clean_name, module->multi_name);
1722 dt_free(clean_name);
1723 }
1724 dt_capitalize_label(label);
1725 return label;
1726}
1727
1729{
1730 gchar *clean_name = delete_underscore(module->name());
1731 gchar *label;
1732 /* create a history button and add to box */
1733 if(!module->multi_name[0] || strcmp(module->multi_name, "0") == 0)
1734 label = g_markup_escape_text(clean_name, -1);
1735 else
1736 label = g_markup_printf_escaped("%s <span size=\"smaller\">%s</span>", clean_name, module->multi_name);
1737 dt_free(clean_name);
1738 return label;
1739}
1740
1741static int dt_dev_distort_backtransform_locked(const dt_dev_pixelpipe_t *pipe, const double iop_order,
1742 const int transf_direction, float *points, size_t points_count);
1743
1744/* These two are the whole of the mask GUI's coordinate handling -- every shape's centre, every
1745 * handle, every source position routes through one of them -- and they ask the geometry service
1746 * the simplest question there is: everything, in order, no bound.
1747 */
1748
1758int dt_dev_distort_transform_gui(dt_develop_t *dev, const double iop_order, const int transf_direction,
1759 float *points, size_t points_count)
1760{
1761 if(IS_NULL_PTR(dev)) return 0;
1762 return dt_geometry_transform(dev, iop_order, transf_direction, points, points_count);
1763}
1764
1766int dt_dev_distort_backtransform_gui(dt_develop_t *dev, const double iop_order, const int transf_direction,
1767 float *points, size_t points_count)
1768{
1769 if(IS_NULL_PTR(dev)) return 0;
1770 return dt_geometry_backtransform(dev, iop_order, transf_direction, points, points_count);
1771}
1772
1798{
1799 if(IS_NULL_PTR(dev)) return FALSE;
1800
1801 int w = 0;
1802 int h = 0;
1804 && dt_geometry_chain_authoritative(dev->geometry_chain) && w > 0 && h > 0)
1805 {
1806 if(!IS_NULL_PTR(width)) *width = w;
1807 if(!IS_NULL_PTR(height)) *height = h;
1808 return TRUE;
1809 }
1810
1812 if(geometry.processed_width <= 0 || geometry.processed_height <= 0) return FALSE;
1813 if(!IS_NULL_PTR(width)) *width = geometry.processed_width;
1814 if(!IS_NULL_PTR(height)) *height = geometry.processed_height;
1815 return TRUE;
1816}
1817
1820{
1821 if(IS_NULL_PTR(dev) || IS_NULL_PTR(module)) return FALSE;
1822
1823 const dt_geometry_record_t *const record
1824 = dt_geometry_chain_find(dev->geometry_chain, module->op, module->multi_priority);
1826
1827 if(!IS_NULL_PTR(in)) *in = record->in;
1828 if(!IS_NULL_PTR(out)) *out = record->out;
1829 return TRUE;
1830}
1831
1832int dt_dev_coordinates_raw_abs_to_image_abs(dt_develop_t *dev, float *points, size_t points_count)
1833{
1834 return dt_geometry_transform(dev, 0.0, DT_DEV_TRANSFORM_DIR_ALL, points, points_count);
1835}
1836
1837int dt_dev_coordinates_image_abs_to_raw_abs(dt_develop_t *dev, float *points, size_t points_count)
1838{
1839 return dt_geometry_backtransform(dev, 0.0, DT_DEV_TRANSFORM_DIR_ALL, points, points_count);
1840}
1841
1842// only call directly or indirectly from dt_dev_distort_transform_plus, so that it runs with the history locked
1844 const int transf_direction, float *points, size_t points_count)
1845{
1846 for(GList *pieces = g_list_first(pipe->nodes); pieces; pieces = g_list_next(pieces))
1847 {
1849 dt_iop_module_t *module = piece->module;
1850 if(piece->enabled
1851 && ((transf_direction == DT_DEV_TRANSFORM_DIR_ALL)
1852 || (transf_direction == DT_DEV_TRANSFORM_DIR_FORW_INCL && module->iop_order >= iop_order)
1853 || (transf_direction == DT_DEV_TRANSFORM_DIR_FORW_EXCL && module->iop_order > iop_order)
1854 || (transf_direction == DT_DEV_TRANSFORM_DIR_BACK_INCL && module->iop_order <= iop_order)
1855 || (transf_direction == DT_DEV_TRANSFORM_DIR_BACK_EXCL && module->iop_order < iop_order))
1856 && !dt_dev_pixelpipe_activemodule_disables_currentmodule(pipe->dev, module))
1857 {
1858 module->distort_transform(module, pipe, piece, points, points_count);
1859 }
1860 }
1861 return 1;
1862}
1863
1864int dt_dev_distort_transform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction,
1865 float *points, size_t points_count)
1866{
1867 dt_dev_distort_transform_locked(pipe, iop_order, transf_direction, points, points_count);
1868 return 1;
1869}
1870
1871// Internal backtransform loop. Keep this file-local so callers use the public wrappers.
1872static int dt_dev_distort_backtransform_locked(const dt_dev_pixelpipe_t *pipe, const double iop_order,
1873 const int transf_direction, float *points, size_t points_count)
1874{
1875 for(GList *pieces = g_list_last(pipe->nodes); pieces; pieces = g_list_previous(pieces))
1876 {
1878 dt_iop_module_t *module = piece->module;
1879 if(piece->enabled
1880 && ((transf_direction == DT_DEV_TRANSFORM_DIR_ALL)
1881 || (transf_direction == DT_DEV_TRANSFORM_DIR_FORW_INCL && module->iop_order >= iop_order)
1882 || (transf_direction == DT_DEV_TRANSFORM_DIR_FORW_EXCL && module->iop_order > iop_order)
1883 || (transf_direction == DT_DEV_TRANSFORM_DIR_BACK_INCL && module->iop_order <= iop_order)
1884 || (transf_direction == DT_DEV_TRANSFORM_DIR_BACK_EXCL && module->iop_order < iop_order))
1885 && !dt_dev_pixelpipe_activemodule_disables_currentmodule(pipe->dev, module))
1886 {
1887 module->distort_backtransform(module, pipe, piece, points, points_count);
1888 }
1889 }
1890 return 1;
1891}
1892
1893int dt_dev_distort_backtransform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction,
1894 float *points, size_t points_count)
1895{
1896 const int success = dt_dev_distort_backtransform_locked(pipe, iop_order, transf_direction, points, points_count);
1897 return success;
1898}
1899
1901 struct dt_iop_module_t *module)
1902{
1903 for(const GList *pieces = g_list_last(pipe->nodes); pieces; pieces = g_list_previous(pieces))
1904 {
1906 if(piece->module == module)
1907 {
1908 return piece;
1909 }
1910 }
1911 return NULL;
1912}
1913
1914// set the module list order
1920
1922{
1924
1925 /* record current history state : before change (needed for undo) */
1926 if(dev->gui_attached && cv->view((dt_view_t *)cv) == DT_VIEW_DARKROOM)
1927 {
1929 }
1930}
1931
1933{
1935
1936 /* record current history state : after change (needed for undo) */
1937 if(dev->gui_attached && cv->view((dt_view_t *)cv) == DT_VIEW_DARKROOM)
1938 {
1941 }
1942}
1943
1945{
1946 if(dev->gui_attached)
1947 {
1949 const gboolean state = dev->mask_lock;
1951 return state;
1952 }
1953 return FALSE;
1954}
1955
1956void dt_masks_set_lock_mode(dt_develop_t *dev, gboolean mode)
1957{
1958 if(dev->gui_attached)
1959 {
1961 dev->mask_lock = mode;
1963 }
1964}
1965
1967{
1968 const int num_items = g_list_length(dev->history);
1969 return CLAMP(dev->history_end, 0, num_items);
1970}
1971
1972void dt_dev_set_history_end_ext(dt_develop_t *dev, const uint32_t index)
1973{
1974 const int num_items = g_list_length(dev->history);
1975 dev->history_end = CLAMP(index, 0, num_items);
1977}
1978
1979void dt_dev_append_changed_tag(const int32_t imgid)
1980{
1981 /* attach changed tag reflecting actual change */
1982 guint tagid = 0;
1983 dt_tag_new("darktable|changed", &tagid);
1984 const gboolean tag_change = dt_tag_attach(tagid, imgid, FALSE, FALSE);
1986}
1987
1989{
1990 // dev->forms is protected by masks_mutex, not history_mutex -- this only reads the
1991 // forms list, so it must not be wrapped in a history_mutex lock by callers (that would
1992 // needlessly exclude the pipeline thread's history reads while this walk runs).
1994
1995 uint64_t hash = 5381;
1996 for(GList *form = g_list_first(dev->forms); form; form = g_list_next(form))
1997 {
1998 dt_masks_form_t *shape = (dt_masks_form_t *)form->data;
1999 hash = dt_masks_form_get_own_hash(hash, dev->forms, shape);
2000 }
2001
2003
2004 // Keep on accumulating "changed" states until something saves the new stack
2005 // and resets that to 0
2006 uint64_t old_hash = dev->forms_hash;
2007 dev->forms_changed |= (old_hash != hash);
2008 dev->forms_hash = hash;
2009}
2010
2012{
2013 if(!dt_dev_viewport_configured(dev) || !dt_dev_geometry_raw_inited(dev)) return -1.f;
2014
2015 return fminf(fminf((float)dt_dev_viewport_box_width(dev) / (float)dt_dev_geometry_processed_width(dev),
2017 1.f);
2018}
2019
2021{
2022 if(IS_NULL_PTR(dev)) return 1.0f;
2024}
2025
2027{
2028 return dt_dev_get_fit_scale(dev);
2029}
2030
2032{
2033 if(IS_NULL_PTR(dev)) return 1.0f;
2035}
2036
2038{
2039 if(IS_NULL_PTR(dev) || IS_NULL_PTR(point)) return;
2040 point[0] = 0.5f * dt_dev_viewport_widget_width(dev);
2041 point[1] = 0.5f * dt_dev_viewport_widget_height(dev);
2042}
2043
2044void dt_dev_get_image_box_in_widget(const dt_develop_t *dev, const int32_t width, const int32_t height, float *box)
2045{
2046 if(IS_NULL_PTR(dev) || IS_NULL_PTR(box)) return;
2047
2048 const float scale = dt_dev_viewport_scaling(dev) / dt_gui_get_global()->ppd;
2049 const float roi_width = fminf(width, dt_dev_roi_request_preview_width(dev) * scale);
2050 const float roi_height = fminf(height, dt_dev_roi_request_preview_height(dev) * scale);
2051 const float border = dt_dev_viewport_border_size(dev);
2052
2053 box[0] = fmaxf(border, 0.5f * (width - roi_width));
2054 box[1] = fmaxf(border, 0.5f * (height - roi_height));
2055 box[2] = fminf(width - 2 * border, roi_width);
2056 box[3] = fminf(height - 2 * border, roi_height);
2057}
2058
2060{
2061 if(IS_NULL_PTR(dev)) return 1.f;
2063}
2064
2066{
2067 // Zoom and pan back to "fit". The ROI request is derived from the viewport, so the reset
2068 // republishes it; there is nothing to invalidate by hand, and an invalidation here could not
2069 // have survived that republication anyway.
2071}
2072
2073void dt_dev_convert_roi(const dt_develop_t *dev, const dt_iop_roi_t *roi_in, dt_iop_roi_t *roi_out,
2074 const dt_dev_roi_space_t from, const dt_dev_roi_space_t to)
2075{
2076 if(IS_NULL_PTR(dev) || IS_NULL_PTR(roi_in) || IS_NULL_PTR(roi_out)) return;
2077
2078 *roi_out = *roi_in;
2079 if(from == to) return;
2080
2081 const float factor = (from == DT_DEV_ROI_GUI_LOGICAL && to == DT_DEV_ROI_PIPELINE)
2083 : 1.0f / dt_gui_get_global()->ppd;
2084
2085 // x/y/width/height belong to the GUI/pipeline geometry boundary and therefore
2086 // follow the ppd factor. roi->scale stays unchanged because it expresses the
2087 // image-space sampling ratio, which must not depend on GUI density.
2088 roi_out->x = lroundf(roi_in->x * factor);
2089 roi_out->y = lroundf(roi_in->y * factor);
2090 roi_out->width = lroundf(roi_in->width * factor);
2091 roi_out->height = lroundf(roi_in->height * factor);
2092 roi_out->scale = roi_in->scale * factor;
2093}
2094
2095gboolean dt_dev_clip_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
2096{
2097 // DO NOT MODIFIY !! //
2098
2099 const float wd = dt_dev_roi_request_preview_width(dev);
2100 const float ht = dt_dev_roi_request_preview_height(dev);
2101 if(wd == 0.f || ht == 0.f) return TRUE;
2102
2103 const float zoom_scale = dt_dev_get_overlay_scale(dev);
2104 const int32_t border = dt_dev_viewport_border_size(dev);
2105 const float roi_width = fminf(width, wd * zoom_scale);
2106 const float roi_height = fminf(height, ht * zoom_scale);
2107
2108 const float rec_x = fmaxf(border, (width - roi_width) * 0.5f);
2109 const float rec_y = fmaxf(border, (height - roi_height) * 0.5f);
2110 const float rec_w = fminf(width - 2 * border, roi_width);
2111 const float rec_h = fminf(height - 2 * border, roi_height);
2112
2113 cairo_rectangle(cr, rec_x, rec_y, rec_w, rec_h);
2114 cairo_clip(cr);
2115
2116 return FALSE;
2117}
2118
2119static gboolean _dev_translate_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
2120{
2121 // DO NOT MODIFIY !! //
2122 // used by preview image scalling, guide and modules //
2123 int proc_wd = 0;
2124 int proc_ht = 0;
2125 dt_dev_get_processed_size(dev, &proc_wd, &proc_ht);
2126 if(proc_wd == 0.f || proc_ht == 0.f) return TRUE;
2127
2128 // Get image's origin position and scale
2129 const float zoom_scale = dt_dev_get_zoom_level(dev) / dt_gui_get_global()->ppd;
2130 const float tx = 0.5f * width - dt_dev_viewport_center_x(dev) * proc_wd * zoom_scale;
2131 const float ty = 0.5f * height - dt_dev_viewport_center_y(dev) * proc_ht * zoom_scale;
2132
2133 cairo_translate(cr, tx, ty);
2134
2135 return FALSE;
2136}
2137
2138gboolean dt_dev_rescale_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
2139{
2140 if(_dev_translate_roi(dev, cr, width, height))
2141 return TRUE;
2142 const float scale = dt_dev_get_fit_scale(dev);
2143 cairo_scale(cr, scale, scale);
2144
2145 return FALSE;
2146}
2147
2148gboolean dt_dev_rescale_roi_to_input(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
2149{
2150 if(_dev_translate_roi(dev, cr, width, height))
2151 return TRUE;
2152 const float scale = dt_dev_get_zoom_level(dev) / dt_gui_get_global()->ppd;
2153 cairo_scale(cr, scale, scale);
2154
2155 return FALSE;
2156}
2157
2159{
2160 const float natural_scale = dt_dev_roi_request_natural_scale(dev);
2161 const float scaling = dt_dev_viewport_scaling(dev);
2162
2163 // Limit zoom in to 16x the size of an apparent pixel on screen
2164 const float pixel_actual_size = natural_scale * scaling;
2165 const float pixel_max_size = 16.f;
2166
2167 if(pixel_actual_size >= pixel_max_size)
2168 {
2169 // Restore old scaling (caller should handle this)
2170 dt_dev_viewport_set_scaling(dev, pixel_max_size / natural_scale);
2171 return TRUE;
2172 }
2173
2174 // Limit zoom out to 1/3rd of the fit-to-window size
2175 const float min_scaling = 0.33f;
2176 if(scaling < min_scaling)
2177 {
2178 dt_dev_viewport_set_scaling(dev, min_scaling);
2179 return TRUE;
2180 }
2181 return FALSE;
2182}
2183
2185{
2186 float zoom_level = dt_dev_get_zoom_level(dev);
2187 if(zoom_level <= 0.f) zoom_level = 1.0f;
2188
2189 // Keep mouse hit-tests usable across zoom levels by bounding the selection
2190 // radius once it is expressed in image-space pixels.
2191 const float radius = dt_widget_mouse_radius();
2192 const float clamped = CLAMP(radius, DT_PIXEL_APPLY_DPI(4.0f) / zoom_level,
2193 DT_PIXEL_APPLY_DPI(15.0f) / zoom_level);
2194 dt_widget_set_mouse_radius(radius, clamped);
2195
2197 "[mouse] effect_radius=%0.3f effect_radius_clamped=%0.3f zoom_level=%0.4f ppd=%0.4f\n",
2198 radius, clamped, zoom_level, dt_screen_ppd());
2199}
2200
2202{
2203 if(IS_NULL_PTR(backbuf)) return;
2204 /* Idempotent: a republication resolving to the same cacheline must not stack references, and
2205 * must not drop the one it holds either -- which an unconditional release-then-take would do
2206 * for a moment, long enough for the LRU to take the entry the screen is showing. */
2207 if(backbuf->keepalive == entry) return;
2208
2210 if(!IS_NULL_PTR(entry))
2211 {
2213 backbuf->keepalive = entry;
2214 }
2215}
2216
2218{
2219 if(IS_NULL_PTR(backbuf) || IS_NULL_PTR(backbuf->keepalive)) return;
2220 /* By POINTER. This reference was taken by pointer, and an entry's hash is not stable under
2221 * rekey reuse -- see dt_dev_pixelpipe_cache_unref_entry() for what releasing by hash costs. */
2223 backbuf->keepalive = NULL;
2224}
2225
2226void dt_dev_set_backbuf(dt_backbuf_t *backbuf, const int width, const int height, const size_t bpp,
2227 const int64_t hash, const int64_t history_hash)
2228{
2229 /* One publication: the shape and the cacheline it describes settle together, so no consumer
2230 * can pair a hash with dimensions from a different frame. The atomics are written directly
2231 * rather than through the single-field setters, which bracket the counter themselves. */
2233 backbuf->height = height;
2234 backbuf->width = width;
2235 backbuf->bpp = bpp;
2237 dt_atomic_set_uint64(&backbuf->history_hash, (uint64_t)history_hash);
2239}
2240
2241// clang-format off
2242// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
2243// vim: shiftwidth=2 expandtab tabstop=2 cindent
2244// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
2245// 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)
void dt_atomic_set_uint64(dt_atomic_uint64 *var, uint64_t value)
static const float scaling
static const float x
void dt_colorprofiles_get_settings(dt_colorprofiles_settings_t *const out)
Copy the current settings into caller-provided storage, under one lock.
const dt_colormatrix_t dt_aligned_pixel_t out
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)
Float for name, clamped to its declared bounds.
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
void dt_image_init(dt_image_t *img)
void dt_image_full_path(const int32_t imgid, char *pathname, size_t pathname_len, gboolean *from_cache, const char *calling_func)
Get the full path of an image out of the database.
void dt_control_alert_once(const char *title, const char *message, const char *item)
dt_control_alert() for a failure that repeats as long as its cause lasts – memory running short,...
Definition control.c:888
void dt_control_queue_redraw_center()
Request a redraw of the centre view.
Definition control.c:970
void dt_control_toast_busy_enter()
Definition control.c:941
void dt_control_log_busy_leave()
Definition control.c:949
int dt_control_running()
Definition control.c:442
void dt_control_log_busy_enter()
Definition control.c:933
void dt_control_toast_busy_leave()
Definition control.c:957
void dt_control_queue_redraw()
Request a redraw of the whole workspace.
Definition control.c:965
struct dt_control_t * dt_control_get_global(void)
Definition darktable.c:654
struct dt_develop_t * dt_dev_get_global(void)
Definition darktable.c:531
GList * dt_iop_get_modules_so(void)
Definition darktable.c:536
struct dt_view_manager_t * dt_view_manager_get_global(void)
Definition darktable.c:602
struct dt_gui_gtk_t * dt_gui_get_global(void)
Definition darktable.c:526
int32_t dt_dev_geometry_processed_height(const dt_develop_t *dev)
gboolean dt_dev_geometry_raw_inited(const dt_develop_t *dev)
void dt_dev_geometry_set_processed_size(dt_develop_t *dev, const int32_t width, const int32_t height)
gboolean dt_dev_geometry_get(const dt_develop_t *dev, dt_dev_image_geometry_t *out)
void dt_dev_geometry_init(dt_develop_t *dev)
dt_dev_image_geometry_t dt_dev_geometry_snapshot(const dt_develop_t *dev)
int32_t dt_dev_geometry_processed_width(const dt_develop_t *dev)
void dt_dev_geometry_set_raw_size(dt_develop_t *dev, const int32_t width, const int32_t height, const gboolean valid)
gboolean dt_dev_read_history_ext(dt_develop_t *dev, const int32_t imgid) REQUIRES(dev -> history_mutex)
Read history and masks from DB and populate dev->history.
void dt_dev_history_undo_end_record_locked(dt_develop_t *dev) REQUIRES(dev -> history_mutex)
Finish an undo record with history_mutex already locked.
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) REQUIRES_SHARED(dev -> history_mutex)
Get the integrity checksum of the whole history stack. This should be done ONLY when history is chang...
void dt_dev_history_undo_end_record(dt_develop_t *dev)
Finish an undo record for history changes.
void dt_dev_history_undo_start_record(dt_develop_t *dev)
Start an undo record for history changes.
void dt_dev_history_undo_start_record_locked(dt_develop_t *dev) REQUIRES(dev -> history_mutex)
Start an undo record with history_mutex already locked.
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.
void dt_dev_write_history_ext(dt_develop_t *dev, const int32_t imgid) REQUIRES_SHARED(dev -> history_mutex)
Write dev->history to DB and XMP for a given image id.
void dt_dev_history_drop_pending_commits(struct dt_develop_t *dev)
Discard every history commit still queued for dev, without running it.
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_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)
int32_t dt_dev_roi_request_preview_height(const dt_develop_t *dev)
void dt_dev_roi_request_latch(dt_dev_pixelpipe_t *pipe, const dt_dev_roi_request_t *request)
Publish onto a pipe the request its next run is planned from. Darkroom worker only.
float dt_dev_roi_request_natural_scale(const dt_develop_t *dev)
int32_t dt_dev_roi_request_preview_width(const dt_develop_t *dev)
dt_dev_roi_request_t dt_dev_roi_request_of_pipe(const dt_dev_pixelpipe_t *pipe)
uint64_t dt_dev_roi_request_publish(dt_develop_t *dev)
Recompute the derived members from the viewport and the geometry record, and publish if anything chan...
void dt_dev_roi_request_init(dt_develop_t *dev)
gboolean dt_dev_roi_request_valid(const dt_develop_t *dev)
dt_dev_roi_request_t dt_dev_roi_request_get(const dt_develop_t *dev)
void dt_dev_viewport_free(dt_dev_viewport_t *viewport)
gboolean dt_dev_viewport_set_box(dt_develop_t *dev, const int32_t box_width, const int32_t box_height)
gboolean dt_dev_viewport_set_center(dt_develop_t *dev, const float center_x, const float center_y)
void dt_dev_viewport_reset(dt_develop_t *dev)
float dt_dev_viewport_center_y(const dt_develop_t *dev)
gboolean dt_dev_viewport_set_scaling(dt_develop_t *dev, const float scaling)
float dt_dev_viewport_center_x(const dt_develop_t *dev)
int32_t dt_dev_viewport_border_size(const dt_develop_t *dev)
gboolean dt_dev_viewport_configured(const dt_develop_t *dev)
int32_t dt_dev_viewport_box_height(const dt_develop_t *dev)
dt_dev_viewport_t * dt_dev_viewport_new(void)
float dt_dev_viewport_scaling(const dt_develop_t *dev)
dt_dev_viewport_state_t dt_dev_viewport_get(const dt_develop_t *dev)
int32_t dt_dev_viewport_widget_height(const dt_develop_t *dev)
int32_t dt_dev_viewport_widget_width(const dt_develop_t *dev)
int32_t dt_dev_viewport_box_width(const dt_develop_t *dev)
void dt_dev_signal_modules_moved(dt_develop_t *dev)
Definition develop.c:1915
dt_job_t * dt_dev_process_job_create(dt_develop_t *dev)
Definition develop.c:942
void dt_dev_masks_list_change(dt_develop_t *dev)
Definition develop.c:1407
static gboolean _dev_translate_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
Definition develop.c:2119
void dt_dev_coordinates_image_abs_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1286
float dt_dev_get_natural_scale(dt_develop_t *dev)
Definition develop.c:2011
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:1843
void dt_dev_get_processed_size(const dt_develop_t *dev, int *procw, int *proch)
Definition develop.c:1176
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:1900
void dt_masks_set_lock_mode(dt_develop_t *dev, gboolean mode)
Definition develop.c:1956
void dt_dev_modulegroups_switch_tab(dt_develop_t *dev, dt_iop_module_t *module)
Definition develop.c:1395
void dt_dev_coordinates_preview_abs_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1373
gboolean dt_dev_processed_size_gui(dt_develop_t *dev, int *width, int *height)
One module's own input and output rectangles, at full resolution.
Definition develop.c:1797
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:1569
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:2044
int dt_dev_coordinates_raw_abs_to_image_abs(dt_develop_t *dev, float *points, size_t points_count)
Definition develop.c:1832
void dt_dev_coordinates_raw_norm_to_raw_abs(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1322
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:2037
int dt_dev_distort_backtransform_gui(dt_develop_t *dev, const double iop_order, const int transf_direction, float *points, size_t points_count)
The inverse of dt_dev_distort_transform_gui(), same rules.
Definition develop.c:1766
void dt_dev_backbuf_take_keepalive(dt_backbuf_t *backbuf, struct dt_pixel_cache_entry_t *entry)
Take the keepalive reference on entry, releasing whatever this backbuffer held. Idempotent for the sa...
Definition develop.c:2201
void dt_dev_module_remove(dt_develop_t *dev, dt_iop_module_t *module)
Definition develop.c:1509
void dt_dev_cleanup(dt_develop_t *dev)
Definition develop.c:240
static void dt_dev_resync_mipmap_cache(dt_develop_t *dev, dt_dev_pixelpipe_t *pipe, dt_iop_roi_t roi)
Definition develop.c:524
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:1864
float dt_dev_get_overlay_scale(dt_develop_t *dev)
Get the overlay scale factor in GUI logical coordinates.
Definition develop.c:2026
void dt_dev_append_changed_tag(const int32_t imgid)
Definition develop.c:1979
dt_dev_image_storage_t dt_dev_load_image(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:1054
gboolean dt_dev_masks_manager_is_visible(dt_develop_t *dev)
Definition develop.c:1401
void dt_dev_coordinates_image_norm_to_preview_abs(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1358
float dt_dev_get_zoom_level(const dt_develop_t *dev)
Definition develop.c:2059
void dt_dev_coordinates_image_norm_to_widget(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1240
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:1422
void dt_dev_init(dt_develop_t *dev, int32_t gui_attached)
Definition develop.c:146
int dt_dev_is_current_image(dt_develop_t *dev, int32_t imgid)
Definition develop.c:1390
void dt_dev_undo_start_record(dt_develop_t *dev)
Definition develop.c:1921
gboolean dt_dev_clamp_viewport_center(dt_develop_t *dev)
Clamp the viewport centre against the box currently visible at this zoom.
Definition develop.c:1132
void dt_dev_snapshot_request(dt_develop_t *dev, const char *filename)
Definition develop.c:1429
gboolean dt_masks_get_lock_mode(dt_develop_t *dev)
Definition develop.c:1944
void dt_dev_darkroom_pipeline(dt_develop_t *dev)
Run darkroom preview and main pipelines from one background loop.
Definition develop.c:617
gchar * dt_history_item_get_label(const struct dt_iop_module_t *module)
Definition develop.c:1680
void dt_dev_coordinates_raw_norm_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1345
gchar * dt_history_item_get_name_html(const struct dt_iop_module_t *module)
Definition develop.c:1728
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:1141
void dt_dev_backbuf_release_keepalive(dt_backbuf_t *backbuf)
Release this backbuffer's keepalive reference, if it holds one. Safe to call twice.
Definition develop.c:2217
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:2158
void dt_dev_reset_roi(dt_develop_t *dev)
Definition develop.c:2065
dt_iop_module_t * dt_dev_module_duplicate(dt_develop_t *dev, dt_iop_module_t *base)
Definition develop.c:1437
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:556
GList * dt_dev_load_modules(dt_develop_t *dev)
Definition develop.c:101
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:2226
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:1972
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:2148
static gboolean _darkroom_pipeline_inputs_ready(const dt_develop_t *dev)
Definition develop.c:338
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:2031
float dt_dev_get_zoom_scale(const dt_develop_t *dev, const gboolean preview)
Definition develop.c:1043
void dt_dev_coordinates_image_norm_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1338
gboolean dt_dev_module_geometry_gui(dt_develop_t *dev, dt_iop_module_t *module, dt_iop_roi_t *in, dt_iop_roi_t *out)
One module's own input and output rectangles at full resolution, from the geometry service.
Definition develop.c:1818
int dt_dev_distort_transform_gui(dt_develop_t *dev, const double iop_order, const int transf_direction, float *points, size_t points_count)
The GUI's bounded transform folds, composed by the geometry service.
Definition develop.c:1758
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:1872
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:507
void dt_dev_configure_real(dt_develop_t *dev, int wd, int ht)
Definition develop.c:1089
void dt_dev_masks_update_hash(dt_develop_t *dev)
Definition develop.c:1988
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:2095
int dt_dev_coordinates_image_abs_to_raw_abs(dt_develop_t *dev, float *points, size_t points_count)
Definition develop.c:1837
void dt_dev_undo_end_record(dt_develop_t *dev)
Definition develop.c:1932
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:2138
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:1194
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:2073
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:2020
dt_dev_image_storage_t dt_dev_ensure_image_storage(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:1010
int32_t dt_dev_get_history_end_ext(dt_develop_t *dev)
Get the current history end index (GUI perspective).
Definition develop.c:1966
void dt_dev_modules_update_multishow(dt_develop_t *dev)
Definition develop.c:1612
void dt_dev_coordinates_image_norm_to_image_abs(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1270
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:461
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:405
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:1211
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:2184
static gboolean _dt_dev_refresh_image_storage(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:1000
void dt_dev_masks_list_update(dt_develop_t *dev)
Definition develop.c:1412
static gboolean _dt_dev_mipmap_prefetch_full(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:957
static dt_dev_image_storage_t _dt_dev_load_raw(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:1025
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:1893
gchar * dt_dev_get_multi_name(const struct dt_iop_module_t *module)
Definition develop.c:1704
void dt_dev_masks_list_remove(dt_develop_t *dev, int formid, int parentid)
Definition develop.c:1417
static int32_t dt_dev_process_job_run(dt_job_t *job)
Definition develop.c:935
void dt_dev_coordinates_raw_abs_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1304
void dt_dev_coordinates_image_abs_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1352
gchar * dt_history_item_get_name(const struct dt_iop_module_t *module)
Definition develop.c:1712
void dt_dev_start_all_pipelines(dt_develop_t *dev)
Definition develop.c:950
static uint64_t dt_dev_get_history_hash(const dt_develop_t *dev)
Definition develop.h:494
@ DT_DEV_OVEREXPOSED_PURPLEGREEN
Definition develop.h:79
@ DT_DEV_OVEREXPOSED_REDBLUE
Definition develop.h:78
@ DT_DEV_OVEREXPOSED_BLACKWHITE
Definition develop.h:77
@ DT_DEV_PIXELPIPE_DISPLAY_NONE
Definition develop.h:122
@ DT_DEV_TRANSFORM_DIR_FORW_INCL
Definition develop.h:108
@ DT_DEV_TRANSFORM_DIR_ALL
Definition develop.h:107
@ DT_DEV_RAWOVEREXPOSED_MODE_FALSECOLOR
Definition develop.h:95
@ DT_DEV_RAWOVEREXPOSED_MODE_MARK_CFA
Definition develop.h:93
dt_dev_roi_space_t
Definition develop.h:115
@ DT_DEV_ROI_GUI_LOGICAL
Definition develop.h:117
@ DT_DEV_ROI_PIPELINE
Definition develop.h:116
@ DT_CLIPPING_PREVIEW_GAMUT
Definition develop.h:155
@ DT_CLIPPING_PREVIEW_SATURATION
Definition develop.h:158
static void dt_dev_set_history_hash(dt_develop_t *dev, const uint64_t history_hash)
Definition develop.h:499
@ DT_DEV_RAWOVEREXPOSED_RED
Definition develop.h:99
@ DT_DEV_RAWOVEREXPOSED_BLACK
Definition develop.h:102
dt_dev_image_storage_t
Definition develop.h:513
@ DT_DEV_IMAGE_STORAGE_DB_NOT_READ
Definition develop.h:516
@ DT_DEV_IMAGE_STORAGE_OK
Definition develop.h:514
@ DT_DEV_IMAGE_STORAGE_MIPMAP_NOT_FOUND
Definition develop.h:515
GtkWidget * geometry
its size, under the preview
GtkWidget * preview
what the selected row actually captures
static int dt_pthread_rwlock_wrlock(dt_pthread_rwlock_t *rwlock) ACQUIRE(rwlock) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:299
static void dt_pthread_rwlock_set_name(dt_pthread_rwlock_t *lock, const char *name)
Definition dtpthread.h:207
static int dt_pthread_rwlock_unlock(dt_pthread_rwlock_t *rwlock) RELEASE_GENERIC(rwlock) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:217
const int res
Definition dtpthread.h:351
static int dt_pthread_rwlock_rdlock(dt_pthread_rwlock_t *rwlock) ACQUIRE_SHARED(rwlock) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:267
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:127
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Initialise a mutex. With mutexattr NULL – which is how 54 of the 56 call sites in this tree spell it ...
Definition dtpthread.h:104
static int mutex
Definition dtpthread.h:123
static int dt_pthread_rwlock_destroy(dt_pthread_rwlock_t *lock)
Definition dtpthread.h:212
static int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:132
static int dt_pthread_rwlock_init(dt_pthread_rwlock_t *lock, const pthread_rwlockattr_t *attr)
Definition dtpthread.h:192
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:117
const dt_geometry_record_t * dt_geometry_chain_find(const dt_geometry_chain_t *chain, const char *op, const int instance)
One module instance's record, or NULL. Use it for that module's own in/out dims.
Definition geometry.c:337
dt_geometry_chain_t * dt_geometry_chain_new(void)
Definition geometry.c:119
int dt_geometry_backtransform(dt_develop_t *dev, const double iop_order, const int direction, float *points, const size_t points_count)
Compose backward over the chain, in place.
Definition geometry.c:429
gboolean dt_geometry_chain_authoritative(const dt_geometry_chain_t *chain)
Can this chain answer questions yet?
Definition geometry.c:324
void dt_geometry_self_check(dt_develop_t *dev, const double chain_ms)
What the shadow harness became once there was nothing left to shadow.
Definition geometry.c:473
gboolean dt_geometry_chain_processed_size(const dt_geometry_chain_t *chain, int *width, int *height)
The developed image's full-resolution size, from the chain's own fold.
Definition geometry.c:329
void dt_geometry_chain_free(dt_geometry_chain_t *chain)
Definition geometry.c:124
int dt_geometry_transform(dt_develop_t *dev, const double iop_order, const int direction, float *points, const size_t points_count)
Compose forward over the chain, in place. direction is a DT_DEV_TRANSFORM_DIR_*.
Definition geometry.c:419
void dt_geometry_chain_rebuild(dt_develop_t *dev)
Rebuild the chain from the dev's current modules and history. GUI thread only.
Definition geometry.c:247
Where things are on the image, answered without a pipeline.
void dt_gui_throttle_cancel(gpointer source)
void dt_gui_throttle_record_runtime(const dt_throttle_slot_t slot, const gint64 runtime_us)
dt_throttle_slot_t
@ DT_THROTTLE_SLOT_MAIN
@ DT_THROTTLE_SLOT_PREVIEW
@ DT_THROTTLE_SLOT_OTHER
@ DT_IMAGEIO_CACHE_FULL
Definition image.h:101
void dt_image_cache_write_release(dt_image_t *img, dt_image_cache_write_mode_t mode)
gboolean dt_image_cache_is_ready(void)
Has the image cache been initialised? Callers that run before dt_image_cache_init() or after its clea...
Definition image_cache.c:84
dt_image_t * dt_image_cache_get(const int32_t imgid, char mode)
void dt_image_cache_read_release(const dt_image_t *img)
@ DT_IMAGE_CACHE_SAFE
Definition image_cache.h:48
int bpp
void dt_iop_cleanup_module(dt_iop_module_t *module)
Definition imageop.c:1113
int dt_iop_load_module(dt_iop_module_t *module, dt_iop_module_so_t *module_so, dt_develop_t *dev)
Definition imageop.c:1102
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:510
void dt_iop_nap(int32_t usec)
Definition imageop.c:1669
void dt_iop_update_multi_priority(dt_iop_module_t *module, int new_priority)
Definition imageop.c:1817
gboolean dt_iop_gui_module_is_visible(dt_iop_module_t *module)
gint dt_sort_iop_by_order(gconstpointer a, gconstpointer b)
Compare two module instances by iop_order for sorting.
Definition iop_order.c:1976
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:1989
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:2158
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:2225
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:2323
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:2351
dt_job_t * dt_control_job_create(dt_job_execute_callback execute, const char *msg,...)
Definition jobs.c:138
int32_t dt_control_add_job_res(dt_control_t *control, _dt_job_t *job, int32_t res)
Definition jobs.c:363
void * dt_control_job_get_params(const _dt_job_t *job)
Definition jobs.c:132
void dt_control_job_set_params(_dt_job_t *job, void *params, dt_job_destroy_callback callback)
Definition jobs.c:115
#define DT_CTL_WORKER_DARKROOM
Definition jobs.h:40
gchar * delete_underscore(const char *s)
Definition label.c:75
static const dt_dev_pixelpipe_t * pipe
Definition layers.h:49
static const dt_dev_pixelpipe_t const dt_dev_pixelpipe_iop_t * piece
Definition layers.h:50
hash
Definition layers.h:90
@ DT_DEBUG_PIPE
Definition logging.h:76
@ DT_DEBUG_HISTORY
Definition logging.h:75
@ DT_DEBUG_DEV
Definition logging.h:53
@ DT_DEBUG_MASKS
Definition logging.h:62
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
uint64_t dt_masks_form_get_own_hash(uint64_t hash, GList *masks, const dt_masks_form_t *form)
Definition masks.c:1460
void dt_masks_release_all_forms(dt_develop_t *dev)
#define CLAMPF(a, mn, mx)
Definition math.h:91
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
Definition mem_alloc.h:184
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
char * key
dt_mipmap_size_t dt_mipmap_cache_get_fitting_size(const int32_t width, const int32_t height, const uint32_t imgid)
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
void dt_mipmap_cache_swap_at_size(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_BLOCKING
dt_mipmap_size_t
@ DT_MIPMAP_FULL
#define dt_mipmap_cache_get(B, C, D, E, F)
#define dt_mipmap_cache_release(B)
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
Definition paths.h:57
const float factor
Definition pdf.h:91
void dt_iop_buffer_dsc_update_bpp(dt_iop_buffer_dsc_t *dsc)
@ DT_DEV_PIXELPIPE_PREVIEW
Definition pixelpipe.h:44
@ DT_DEV_PIXELPIPE_FULL
Definition pixelpipe.h:43
void dt_dev_pixelpipe_cache_unref_entry(dt_pixel_cache_entry_t *entry)
Release one reference held on entry, by POINTER.
void dt_dev_pixelpipe_cache_ref_count_entry(gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Take or release one reference on a cache line. A referenced line is never evicted,...
void dt_dev_pixelpipe_cache_rdlock_entry(gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the read lock on the entry.
gboolean dt_dev_pixelpipe_cache_ref_entry_by_hash(const uint64_t hash, void **data, dt_pixel_cache_entry_t **entry)
Resolve and retain an existing cache entry by hash.
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 void dt_dev_backbuf_publish_begin(dt_backbuf_t *backbuf)
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_ZOOMED
@ DT_DEV_PIPE_CACHE_REQUEST
@ DT_DEV_PIPE_TOP_CHANGED
@ DT_DEV_PIPE_UNCHANGED
static uint64_t dt_dev_pixelpipe_get_history_hash(const dt_dev_pixelpipe_t *pipe)
static dt_backbuf_state_t dt_dev_backbuf_snapshot(const dt_backbuf_t *backbuf)
Read the whole record as one publication.
static void dt_dev_backbuf_publish_end(dt_backbuf_t *backbuf)
dt_dev_pixelpipe_cache_request_t
@ DT_DEV_PIXELPIPE_CACHE_REQUEST_BACKBUF
@ DT_DEV_PIXELPIPE_CACHE_REQUEST_NONE
@ DT_DEV_PIXELPIPE_CACHE_REQUEST_MODULE
@ DT_PROFILE_NORMAL
double dt_screen_ppd(void)
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:390
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:619
@ DT_SIGNAL_DEVELOP_HISTORY_CHANGE
This signal is raised when develop history is changed no param, no returned value.
Definition signal.h:208
@ 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:178
@ DT_SIGNAL_HISTORY_RESYNC
This signal is raised once darkroom history has been resynchronized into all live pipelines....
Definition signal.h:213
@ 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:183
@ DT_SIGNAL_DEVELOP_MODULE_MOVED
This signal is raised when order of modules in pipeline is changed.
Definition signal.h:222
@ DT_SIGNAL_DEVELOP_MODULEGROUPS_SET
This signal is raised to request a modulegroups update. 1 : dt_iop_module_t *module,...
Definition signal.h:195
@ DT_SIGNAL_TAG_CHANGED
This signal is raised when a tag is added/deleted/changed
Definition signal.h:134
const float uint32_t state[4]
unsigned __int64 uint64_t
Definition strptime.c:75
One coherent publication, by value.
What the pipeline last published: which cacheline, and what shape its pixels are in.
dt_atomic_uint64 history_hash
dt_atomic_uint64 hash
struct dt_pixel_cache_entry_t * keepalive
Consistent snapshot of the display and soft-proofing settings.
dt_colorspaces_color_profile_type_t display_type
monitor profile identity
dt_colorspaces_color_mode_t mode
NORMAL / SOFTPROOF / GAMUTCHECK.
Objective facts about the image a dev is working on.
GHashTable * modules_in_history
Definition develop.c:1564
GHashTable * prev_visible
Definition develop.c:1565
GHashTable * next_visible
Definition develop.c:1566
GHashTable * instance_counts
Definition develop.c:1563
dt_pthread_mutex_t busy_mutex
dt_atomic_int pause
dt_backbuf_t backbuf
dt_atomic_int shutdown
dt_dev_pixelpipe_type_t type
struct dt_develop_t * dev
The coherent set of numbers a darkroom pipe plans its ROI from.
What the darkroom view asks the pipeline to show: the window onto the image.
void(* list_update)(struct dt_lib_module_t *self)
Definition develop.h:413
int32_t gui_attached
Definition develop.h:167
GList * iop_order_list
Definition develop.h:275
int undo_history_before_end
Definition develop.h:282
dt_image_t image_storage
Definition develop.h:225
struct dt_colorpicker_sample_t * primary_sample
Definition develop.h:374
GList * undo_history_before_snapshot
Definition develop.h:281
dt_backbuf_t display_histogram
Definition develop.h:320
GList * iop
Definition develop.h:269
gboolean request
Definition develop.h:402
dt_backbuf_t output_histogram
Definition develop.h:319
struct dt_develop_t::@12 transient_params
int32_t history_end
Definition develop.h:256
GList * undo_history_before_iop_order_list
Definition develop.h:283
gboolean wb_is_D65
Definition develop.h:427
struct dt_develop_t::@13 color_picker
Authoritative darkroom color-picker state.
struct dt_develop_t::@20 progress
gboolean restrict_histogram
Definition develop.h:379
void(* list_remove)(struct dt_lib_module_t *self, int formid, int parentid)
Definition develop.h:412
int completed
Definition develop.h:479
struct dt_iop_module_t * gui_module
Definition develop.h:170
dt_pthread_rwlock_t history_mutex
Definition develop.h:247
gboolean live_samples_enabled
Definition develop.h:378
dt_clipping_preview_mode_t mode
Definition develop.h:441
struct dt_lib_module_t *void(* list_change)(struct dt_lib_module_t *self)
Definition develop.h:411
gboolean forms_changed
Definition develop.h:309
float lower
Definition develop.h:439
struct dt_dev_pixelpipe_t * preview_pipe
Definition develop.h:214
struct dt_develop_t::@15 overexposed
gboolean pipelines_started
Definition develop.h:328
GList * history
Definition develop.h:259
struct dt_develop_t::@14::@21 snapshot
struct dt_iop_module_t *void * params
Definition develop.h:294
GList * alliop
Definition develop.h:271
dt_aligned_pixel_t wb_coeffs
Definition develop.h:428
dt_dev_overexposed_colorscheme_t colorscheme
Definition develop.h:438
dt_backbuf_t raw_histogram
Definition develop.h:318
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:415
gboolean(* is_visible)(struct dt_lib_module_t *self)
Definition develop.h:418
struct dt_iop_module_t * chroma_adaptation
Definition develop.h:424
gboolean mask_lock
Definition develop.h:483
gboolean enabled
Definition develop.h:370
dt_pthread_mutex_t transient_params_mutex
Definition develop.h:301
dt_pthread_rwlock_t masks_mutex
Definition develop.h:316
struct dt_develop_t::@14::@22 masks
uint64_t forms_hash
Definition develop.h:307
const gchar * filename
Definition develop.h:403
struct dt_develop_t::@14 proxy
struct dt_develop_t::@16 rawoverexposed
float threshold
Definition develop.h:452
struct dt_dev_pixelpipe_t * pipe
Definition develop.h:214
float upper
Definition develop.h:440
struct dt_geometry_chain_t * geometry_chain
Definition develop.h:491
dt_dev_viewport_t * viewport
The darkroom view's window onto the image: widget allocation, borders, zoom, pan.
Definition develop.h:201
gboolean display_samples
Definition develop.h:377
GList * forms
Definition develop.h:304
int undo_history_depth
Definition develop.h:280
void * blend_params
Definition develop.h:297
One module instance's contribution, as data.
Definition geometry.h:99
dt_iop_roi_t in
Definition geometry.h:117
dt_iop_roi_t out
Definition geometry.h:118
dt_iop_buffer_dsc_t dsc
Definition image.h:428
int32_t id
Definition image.h:410
GtkWidget * expander
Definition imageop_gui.h:57
struct dt_iop_module_gui_t * gui
Definition imageop.h:346
char multi_name[128]
Definition imageop.h:387
struct dt_develop_t * dev
Definition imageop.h:311
int32_t instance
Definition imageop.h:266
dt_iop_module_so_t * so
Definition imageop.h:376
int request_mask_display
Definition imageop.h:276
dt_dev_operation_t op
Definition imageop.h:259
Region of interest passed through the pixelpipe.
Definition format.h:49
double scale
Definition format.h:51
int width
Definition format.h:50
int height
Definition format.h:50
dt_cache_entry_t * cache_entry
NULL with a non-NULL buf when a blocking get could not allocate the thumbnail's entry: buf is then a ...
dt_imageio_retval_t status
Why the entry holds no image: a thumbnail drawn as a skull, or a full-size input with no pixels....
gboolean dt_tag_attach(const guint tagid, const int32_t imgid, const gboolean undo_on, const gboolean group_on)
Definition tags.c:377
gboolean dt_tag_new(const char *name, guint *tagid)
Definition tags.c:164
#define MAX(a, b)
Definition thinplate.c:29
void dt_show_times(const dt_times_t *start, const char *prefix)
Definition darktable.c:2147
static void dt_get_times(dt_times_t *t)
Definition times.h:50
static double dt_get_wtime(void)
Definition times.h:43
void dt_show_times_f(const dt_times_t *start, const char *prefix, const char *suffix,...) __attribute__((format(printf
Telling the user something happened.
const dt_view_t * dt_view_manager_get_current_view(dt_view_manager_t *vm)
Definition view.c:144
@ DT_VIEW_DARKROOM
Definition view.h:79
float dt_widget_mouse_radius(void)
void dt_widget_set_mouse_radius(float radius, float clamped)
#define DT_PIXEL_APPLY_DPI(value)
void dt_capitalize_label(gchar *text)