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 "control/control.h"
80#include "control/signal.h"
81#include "control/jobs.h"
82#include "develop/blend.h"
83#include "develop/develop.h"
84#include "develop/imageop.h"
85#include "develop/lightroom.h"
86#include "develop/masks.h"
88#include "gui/application.h"
91#include "libs/colorpicker.h"
92#include "widgets/label.h"
94#include "gui/screen_metrics.h"
95
96#define DT_IOP_ORDER_INFO (dt_get_debug_flags() & DT_DEBUG_IOPORDER)
97
99{
100 GList *res = NULL;
101 dt_iop_module_t *module;
102 dt_iop_module_so_t *module_so;
103 GList *iop = g_list_first(dt_iop_get_modules_so());
104 while(iop)
105 {
106 module_so = (dt_iop_module_so_t *)iop->data;
107 module = (dt_iop_module_t *)calloc(1, sizeof(dt_iop_module_t));
108 if(dt_iop_load_module_by_so(module, module_so, dev))
109 {
110 dt_free(module);
111 continue;
112 }
113 res = g_list_insert_sorted(res, module, dt_sort_iop_by_order);
114 module->global_data = module_so->data;
115 module->so = module_so;
116 iop = g_list_next(iop);
117 }
118
119 /* Give every base module a distinct `instance`.
120 *
121 * `instance` identifies a module FAMILY: a base module and every extra instance
122 * duplicated from it share one value, and dt_dev_module_duplicate() scans dev->iop for
123 * `mod->instance == base->instance` to find the next free multi_priority and multi_name.
124 * With every module left at the calloc'd 0, that scan matches every module of every
125 * operation, so duplicating `retouch` picked up the numbering of an unrelated `exposure`
126 * copy and produced retouch "2" with no "1" -- issue #1265.
127 *
128 * Runtime-only: the database and XMP store multi_priority and multi_name, never this, so
129 * the numbering does not have to be stable across sessions -- only distinct within a dev.
130 *
131 * 214cc1cc8e removed this line while tidying dt_develop_t and left the loop empty; the
132 * counter it uses stayed on the struct, unread, which is why nothing complained. */
133 GList *it = res;
134 while(it)
135 {
136 module = (dt_iop_module_t *)it->data;
137 module->instance = dev->iop_instance++;
138 it = g_list_next(it);
139 }
140 return res;
141}
142
143void dt_dev_init(dt_develop_t *dev, int32_t gui_attached)
144{
145 memset(dev, 0, sizeof(dt_develop_t));
150 dt_pthread_rwlock_set_name(&dev->history_mutex, "history_mutex"); // find_history_mutex_blocker, temporary
153
154 dev->gui_attached = gui_attached;
155 if(gui_attached) dev->viewport = dt_dev_viewport_new();
156
158
159 if(dev->gui_attached)
160 {
161 dev->pipe = (dt_dev_pixelpipe_t *)malloc(sizeof(dt_dev_pixelpipe_t));
162 dev->preview_pipe = (dt_dev_pixelpipe_t *)malloc(sizeof(dt_dev_pixelpipe_t));
163 dt_dev_pixelpipe_init(dev->pipe, dev);
165
166 /* Where the GUI gets sizes and coordinates from (doc/geometry-service.md). GUI devs only:
167 * it answers GUI questions and is GUI-thread state, so a headless dev has nothing to do with
168 * one -- which is also what makes the NULL chain the guard on every entry point. */
170 }
171
172 dt_dev_set_backbuf(&dev->raw_histogram, 0, 0, 0, -1, -1);
173 dt_dev_set_backbuf(&dev->output_histogram, 0, 0, 0, -1, -1);
174 dt_dev_set_backbuf(&dev->display_histogram, 0, 0, 0, -1, -1);
175
176 dev->proxy.wb_is_D65 = TRUE; // don't display error messages until we know for sure it's FALSE
177 dev->proxy.wb_coeffs[0] = 0.f;
178
179 // Overlay toolbar prefs have been found corrupted on disk (heap-looking int garbage, zeroed
180 // floats). An out-of-enum mode/colorscheme indexes the fixed-size color tables of
181 // iop/rawoverexposed.c out of bounds and selects diverging CPU/OpenCL preview paths (the CL
182 // switch defaults to false color), painting the whole image as a CFA checkerboard. An invalid
183 // enum means the whole stored group is untrustworthy: restore the group defaults instead of
184 // clamping, so a corrupted threshold does not survive as a legal-looking value.
185 const int raw_mode = dt_conf_get_int("darkroom/ui/rawoverexposed/mode");
186 const int raw_colorscheme = dt_conf_get_int("darkroom/ui/rawoverexposed/colorscheme");
187 const float raw_threshold = dt_conf_get_float("darkroom/ui/rawoverexposed/threshold");
189 && raw_colorscheme >= DT_DEV_RAWOVEREXPOSED_RED && raw_colorscheme <= DT_DEV_RAWOVEREXPOSED_BLACK
190 && raw_threshold >= 0.f && raw_threshold <= 2.f)
191 {
192 dev->rawoverexposed.mode = raw_mode;
193 dev->rawoverexposed.colorscheme = raw_colorscheme;
194 dev->rawoverexposed.threshold = raw_threshold;
195 }
196 else
197 {
200 dev->rawoverexposed.threshold = 1.f;
201 }
202
203 const int over_mode = dt_conf_get_int("darkroom/ui/overexposed/mode");
204 const int over_colorscheme = dt_conf_get_int("darkroom/ui/overexposed/colorscheme");
205 const float over_lower = dt_conf_get_float("darkroom/ui/overexposed/lower");
206 const float over_upper = dt_conf_get_float("darkroom/ui/overexposed/upper");
207 if(over_mode >= DT_CLIPPING_PREVIEW_GAMUT && over_mode <= DT_CLIPPING_PREVIEW_SATURATION
208 && over_colorscheme >= DT_DEV_OVEREXPOSED_BLACKWHITE && over_colorscheme <= DT_DEV_OVEREXPOSED_PURPLEGREEN
209 && over_lower >= -32.f && over_lower <= -4.f && over_upper >= 0.f && over_upper <= 100.f)
210 {
211 dev->overexposed.mode = over_mode;
212 dev->overexposed.colorscheme = over_colorscheme;
213 dev->overexposed.lower = over_lower;
214 dev->overexposed.upper = over_upper;
215 }
216 else
217 {
220 dev->overexposed.lower = -12.69f;
221 dev->overexposed.upper = 99.99f;
222 }
223
224 if(dev->gui_attached)
225 {
226 dev->color_picker.primary_sample = g_malloc0(sizeof(dt_colorpicker_sample_t));
227 dev->color_picker.display_samples = dt_conf_get_bool("ui_last/colorpicker_display_samples");
229 dev->color_picker.restrict_histogram = dt_conf_get_bool("ui_last/colorpicker_restrict_histogram");
230 }
231
232 dt_dev_reset_roi(dev);
233
234 dev->iop = dt_dev_load_modules(dev);
235}
236
238{
239 if(IS_NULL_PTR(dev)) return;
240 // image_cache does not have to be unref'd, this is done outside develop module.
241
244
246 dev->viewport = NULL;
247
248 dev->proxy.chroma_adaptation = NULL;
249 dev->proxy.wb_coeffs[0] = 0.f;
250 if(dev->pipe)
251 {
253 dt_free(dev->pipe);
254 }
255 if(dev->preview_pipe)
256 {
258 dt_free(dev->preview_pipe);
259 }
261 dev->geometry_chain = NULL;
262
264 while(dev->history)
265 {
267 dev->history = g_list_delete_link(dev->history, dev->history);
268 }
271
272 // free pending "before" snapshots for history undo
273 dev->undo_history_depth = 0;
279
280 // free the transient param channel
283 dev->transient_params.params = NULL;
285 dev->transient_params.blend_params = NULL;
286 dev->transient_params.module = NULL;
289
290 while(dev->iop)
291 {
293 dt_free(dev->iop->data);
294 dev->iop = g_list_delete_link(dev->iop, dev->iop);
295 }
296 while(dev->alliop)
297 {
299 dt_free(dev->alliop->data);
300 dev->alliop = g_list_delete_link(dev->alliop, dev->alliop);
301 }
302 g_list_free_full(dev->iop_order_list, dt_free_gpointer);
303 dev->iop_order_list = NULL;
304
306 {
308 dev->color_picker.primary_sample = NULL;
309 }
310
312
314
315 // Overlay toolbar prefs may only be persisted by a gui_attached develop: transient devs
316 // (export, styles, snapshots, history copies) never expose the toolbar, run on worker
317 // threads, and would keep re-writing these keys on every background job — one corrupted
318 // or racing write is then resurrected as the user's settings at every later startup.
319 if(dev->gui_attached)
320 {
321 dt_conf_set_int("darkroom/ui/rawoverexposed/mode", dev->rawoverexposed.mode);
322 dt_conf_set_int("darkroom/ui/rawoverexposed/colorscheme", dev->rawoverexposed.colorscheme);
323 dt_conf_set_float("darkroom/ui/rawoverexposed/threshold", dev->rawoverexposed.threshold);
324
325 dt_conf_set_int("darkroom/ui/overexposed/mode", dev->overexposed.mode);
326 dt_conf_set_int("darkroom/ui/overexposed/colorscheme", dev->overexposed.colorscheme);
327 dt_conf_set_float("darkroom/ui/overexposed/lower", dev->overexposed.lower);
328 dt_conf_set_float("darkroom/ui/overexposed/upper", dev->overexposed.upper);
329 }
330}
331
332static gboolean _update_darkroom_roi(dt_develop_t *dev, dt_dev_pixelpipe_t *pipe, int *x, int *y, int *wd, int *ht,
333 float *scale);
334
336{
337 if(IS_NULL_PTR(dev)) return FALSE;
338
341
342 return dev->image_storage.id > 0
343 && dt_dev_viewport_box_width(dev) >= 32
344 && dt_dev_viewport_box_height(dev) >= 32
345 && geometry.raw_width >= 32
346 && geometry.raw_height >= 32
347 && geometry.processed_width >= 32
348 && geometry.processed_height >= 32
351}
352
354{
355 if(!dt_dev_geometry_raw_inited(dev) || !dt_dev_viewport_configured(dev)) return 1;
356
358 const int32_t raw_width = geometry.raw_width;
359 const int32_t raw_height = geometry.raw_height;
360
361 /* The chain is rebuilt BEFORE the size is decided now, because it is what decides it. It is
362 * cheap -- small derivations of already-committed parameters, no LUT, no colour transform, no
363 * disk -- which is the whole point of the exercise: the resync below is not. */
364 const double chain_start = dt_get_wtime();
366 const double chain_ms = (dt_get_wtime() - chain_start) * 1000.0;
367
368 /* The developed size, folded from the geometry records. Nothing is rebuilt to obtain it: that
369 * is what this service replaced, and what it cost is recorded in doc/geometry-service.md. */
370 int processed_width = 0;
371 int processed_height = 0;
372 if(!dt_geometry_chain_processed_size(dev->geometry_chain, &processed_width, &processed_height))
373 return 1;
374
375 dt_dev_geometry_set_processed_size(dev, processed_width, processed_height);
376
377 // Derive and publish everything the pipes plan from, as one record.
379
380 /* Shadow mode compares against the pipe's own fold, which only exists when the pipe was
381 * resynced above -- so it is asked for, and paid for, under `-d dev' and nowhere else. It runs
382 * STRICTLY AFTER both publications and never between them: those two are one atomic-looking
383 * update, the first moving the geometry record to the new size and the second moving the ROI
384 * request to match AND flagging the pipes to replan. Anything inserted between them widens the
385 * window in which the darkroom worker latches the OLD request against ALREADY NEW history,
386 * which is the mixed frame of #1157. An observer must not sit inside the update it observes. */
387 dt_geometry_self_check(dev, chain_ms);
388
389
391
392 const dt_dev_roi_request_t request = dt_dev_roi_request_get(dev);
393
395 "[pixelpipe] thumbnail sizes raw %dx%d -> processed %dx%d -> preview %dx%d (scale %.5f)\n",
396 raw_width, raw_height, request.processed_width, request.processed_height,
397 request.preview_width, request.preview_height, request.natural_scale);
398
399 return 0;
400}
401
403 const dt_iop_roi_t *roi)
404{
405 // The NULL checks come FIRST: this is called with dev->preview_pipe, which is allocated only
406 // for a gui_attached dev (dt_dev_init) and is therefore NULL on every export and thumbnail
407 // dev -- iop/denoiseprofile.c passes exactly that.
408 if(IS_NULL_PTR(dev) || IS_NULL_PTR(pipe) || !dev->gui_attached) return FALSE;
409
410 // Compare against what this pipe was planned from, not what the GUI has published since.
412 if(!request.valid) return FALSE;
413
414 int x = 0;
415 int y = 0;
416 int width = 0;
417 int height = 0;
418 float scale = request.natural_scale;
419
420 if(!IS_NULL_PTR(roi))
421 {
422 x = roi->x;
423 y = roi->y;
424 width = roi->width;
425 height = roi->height;
426 scale = roi->scale;
427 }
428 else
429 {
430 // Recompute the current darkroom output geometry so callers that run ahead of process()
431 // still classify the pipe from the image they are about to produce, not the last backbuffer.
432 _update_darkroom_roi((dt_develop_t *)dev, (dt_dev_pixelpipe_t *)pipe, &x, &y, &width, &height, &scale);
433 }
434
435 // A module upstream of the orientation swap (the "flip" module) — e.g. ashift, demosaic,
436 // highlights — produces output whose width/height are swapped relative to the final, post-flip
437 // preview dimensions on portrait images. Accept that swapped match too: otherwise the
438 // "is this the full preview image?" test wrongly fails for every pre-flip module on portrait,
439 // and `roi` here is the module's own (pre-flip) `roi_out`. This is why ashift never captured its
440 // GUI buffer on portrait images, breaking structure detection and manual drawing (#710).
441 //
442 // Tolerate a couple of pixels of slack on the dimensions. `dev->roi.preview_*` is derived from the
443 // composed geometry at scale 1.0, whereas `roi` is produced at `natural_scale`; geometric modules
444 // (ashift, lens) round their transformed bounding box with floorf() independently at each scale,
445 // so the two legitimately disagree by ~1px for the very same full image. The real discriminators
446 // are the origin and scale tests below: a zoomed or panned ROI has a non-zero x/y and a scale
447 // strictly greater than natural_scale, so loosening the size match cannot misclassify those.
448 const int tol = 2;
449 const gboolean dims_match
450 = (abs(width - request.preview_width) <= tol && abs(height - request.preview_height) <= tol)
451 || (abs(width - request.preview_height) <= tol && abs(height - request.preview_width) <= tol);
452 if(!dims_match) return FALSE;
453 return x == 0 && y == 0 && fabsf(scale - request.natural_scale) < 1e-4f;
454}
455
456
457// Return TRUE if ROI changed since previous computation
458static gboolean _update_darkroom_roi(dt_develop_t *dev, dt_dev_pixelpipe_t *pipe, int *x, int *y, int *wd, int *ht,
459 float *scale)
460{
461 // The latched snapshot, not the live record: this runs on the worker, and the frame must be
462 // planned from the same numbers the rest of this iteration uses.
464 if(!request.valid) return 1;
465
466 // Store previous values
467 int x_old = *x;
468 int y_old = *y;
469 int wd_old = *wd;
470 int ht_old = *ht;
471 float old_scale = *scale;
472
473 // roi->scale is the pipeline sampling ratio against the processed image and
474 // therefore excludes the GUI backing-store density.
475 *scale = request.natural_scale;
476 const gboolean preview_pipe = (pipe == dev->preview_pipe);
477 if(!preview_pipe) *scale *= request.scaling;
478
479 // Width, height, x and y are already expressed in raster pixels, so they
480 // must follow the same raster-space sampling ratio as roi->scale. All of it comes from the
481 // latched record: mixing one live read into this arithmetic is exactly how a frame ends up
482 // describing a viewport that never existed.
483 int roi_width = roundf(*scale * request.processed_width);
484 int roi_height = roundf(*scale * request.processed_height);
485 int widget_wd = request.box_width;
486 int widget_ht = request.box_height;
487
488 *wd = roundf(fminf(roi_width, widget_wd));
489 *ht = roundf(fminf(roi_height, widget_ht));
490
491 // dt_dev_viewport_center_x(dev),y are the relative coordinates of the ROI center.
492 // in preview pipe, we always render a full image, so x,y = 0,0
493 // otherwise, x,y here are the top-left corner. Translate:
494 *x = preview_pipe ? 0 : roundf(request.center_x * roi_width - *wd * .5f);
495 *y = preview_pipe ? 0 : roundf(request.center_y * roi_height - *ht * .5f);
496
497/* fprintf (stderr, "_update_darkroom_roi: dev %.2f %.2f type %s xy %d %d dim %d %d"
498 " ppd:%.4f scale:%.4f nat_scale:%.4f * scaling:%.4f\n",
499 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));
500*/
501 return x_old != *x || y_old != *y || wd_old != *wd || ht_old != *ht || old_scale != *scale;
502}
503
505{
506 if(IS_NULL_PTR(dev) || !dev->gui_attached || IS_NULL_PTR(dev->pipe) || IS_NULL_PTR(dev->preview_pipe) || !dt_dev_roi_request_valid(dev)) return FALSE;
507
508 float preview_scale = 1.0f;
509 float main_scale = 1.0f;
510 int preview_x = 0, preview_y = 0, preview_wd = 0, preview_ht = 0;
511 int main_x = 0, main_y = 0, main_wd = 0, main_ht = 0;
512
513 _update_darkroom_roi(dev, dev->preview_pipe, &preview_x, &preview_y, &preview_wd, &preview_ht, &preview_scale);
514 _update_darkroom_roi(dev, dev->pipe, &main_x, &main_y, &main_wd, &main_ht, &main_scale);
515
516 return preview_x == main_x && preview_y == main_y && preview_wd == main_wd && preview_ht == main_ht
517 && fabsf(preview_scale - main_scale) < 1e-4f;
518}
519
520
522{
523 const int32_t imgid = pipe->dev->image_storage.id;
524
525 // Get the mip size that is at most as big as our pipeline backbuf. One read: these dimensions
526 // are handed to the mipmap cache alongside the payload below, so they must describe it.
527 const dt_backbuf_state_t published = dt_dev_backbuf_snapshot(&pipe->backbuf);
528 dt_mipmap_size_t mip = dt_mipmap_cache_get_fitting_size(published.width, published.height, imgid);
529
530 // Flush backup to mipmap_cache. This runs after dt_dev_pixelpipe_process() released the OpenCL device
531 // lock, so we must NOT pass pipe->devid (now stale/unlocked): a device-only payload would otherwise be
532 // materialized from the GPU without owning it. The final display backbuffer is always host-resident,
533 // so preferred_devid = -1 returns it directly; anything else is simply skipped.
534 uint8_t *data = NULL;
535 dt_pixel_cache_entry_t *entry = NULL;
537 && data)
538 {
542 dt_mipmap_cache_swap_at_size(imgid, mip, data, published.width, published.height,
543 settings.display_type);
546 }
547 else if(entry)
548 {
550 }
551}
552
553gboolean _resync_pipe_with_history(dt_develop_t *dev, dt_dev_pixelpipe_t *pipe, dt_iop_roi_t *roi, gboolean *needs_update)
554{
555 // When in realtime mode, preview pipe gets paused at the benefit of main pipeline.
556 // This is a transient state.
557 if(pipe->pause) return FALSE;
558
559 // We recompute if history hash changed or ROI has changed.
560 // If we know history changed, ensure at least the last step is resynced.
561 const uint64_t pipe_hash = dt_dev_pixelpipe_get_history_hash(pipe);
562 const uint64_t dev_hash = dt_dev_get_history_hash(dev);
563 if(pipe_hash != dev_hash)
564 {
566 dt_print(DT_DEBUG_PIPE | DT_DEBUG_DEV, "dev history hash = %" PRIu64 ", pipe history hash %" PRIu64 "\n", dev_hash, pipe_hash);
567 }
568
569 *needs_update = (dt_dev_pixelpipe_get_changed(pipe) != DT_DEV_PIPE_UNCHANGED);
570 if(!*needs_update) return FALSE;
571
573 pipe->processing = 1;
574
575 // Commit history to pipeline.
576 // This can take 40-80 ms or much more with masks on weak hardware,
577 // so user may have changed the history again during that lapse.
578 gboolean pipe_resynced = FALSE;
580 {
582 pipe_resynced = TRUE;
583 }
584
585 // Plan the ROI for this run and finalize the cumulative global hash now, while the pipe is
586 // settled and not yet publishing pixels. dt_dev_pixelpipe_process() recomputes both at its
587 // entry (it is also called directly by export/snapshot pipes), but computing them here is
588 // what lets the HISTORY_RESYNC signal below advertise a hash that is already final.
589 int x = 0, y = 0, wd = 0, ht = 0;
590 float scale = 1.f;
591 _update_darkroom_roi(dev, pipe, &x, &y, &wd, &ht, &scale);
592 *roi = (dt_iop_roi_t){ x, y, wd, ht, scale };
593 dt_dev_pixelpipe_get_roi_in(pipe, *roi);
595
596 pipe->processing = 0;
598
599 return pipe_resynced;
600}
601
602
615{
616 dt_dev_pixelpipe_t *const pipes[] = { dev->preview_pipe, dev->pipe };
617
618 for(size_t i = 0; i < G_N_ELEMENTS(pipes); i++)
619 dt_atomic_set_int(&pipes[i]->running, TRUE);
620
621 // Infinite loop: run for as long as the worker thread is running.
622 while(!dev->exit && dt_control_running())
623 {
625 {
626 dt_iop_nap(50000); // wait 50 ms until GUI/image sizes are initialized
627 continue;
628 }
629
630 // Latch the viewport request ONCE for this iteration, before anything reads it. Everything
631 // downstream -- the ROI planner, the resync, every module callback -- then works from one
632 // snapshot for the whole frame, however many times the GUI thread republishes meanwhile.
633 // Reading it live at each consumer is what let a single frame be planned from two different
634 // viewport states.
635 const dt_dev_roi_request_t latched = dt_dev_roi_request_get(dev);
636 for(size_t i = 0; i < G_N_ELEMENTS(pipes); i++)
637 dt_dev_roi_request_latch(pipes[i], &latched);
638
639 // This is cheap to run, keep it in sync always.
641 const int32_t input_width = geometry.raw_width;
642 const int32_t input_height = geometry.raw_height;
643 for(size_t i = 0; i < G_N_ELEMENTS(pipes); i++)
644 dt_dev_pixelpipe_set_input(pipes[i], dev->image_storage.id, input_width, input_height,
645 1.0f, DT_MIPMAP_FULL);
646
647 gboolean pipe_needs_update[G_N_ELEMENTS(pipes)] = { FALSE };
648 dt_iop_roi_t pipe_roi[G_N_ELEMENTS(pipes)] = { { 0 } };
649 gboolean history_resynced = FALSE;
650
651 // First, resynchronize all dirty pipelines from history, plan their ROI and finalize their
652 // cumulative global hash, so GUI listeners can resolve stable piece->global_hash values
653 // before any cacheline starts publishing pixels.
654 for(size_t i = 0; i < G_N_ELEMENTS(pipes); i++)
655 history_resynced |= _resync_pipe_with_history(dev, pipes[i], &pipe_roi[i], &pipe_needs_update[i]);
656
657 // NOTE: at this point, we fully know the state of __all__ our GUI pipelines :
658 // - global image input and output size,
659 // - per-module input and output size,
660 // - per-module input and output format (channels, bit depth, mosaiced/raster, CFA pattern)
661 // - pipeline nodes (modules) params are up-to-to date with history,
662 // - global_hash of each module is and stable until next history resync,
663 // - modules whose expected input format is incompatible with previous module output format
664 // will have been disabled from pipeline, but not from history, aka we know our pipelines
665 // can run start to end.
666
667 // GUI widgets that need an image buffer will connect to this signal to grab
668 // the global_hash of the module they are waiting for, even though it's still not ready.
669 if(history_resynced)
671
672 // Second, compute pipelines.
673 // Always service preview first, then the main pipe, so the main pipe can reuse the cache state
674 // just published by preview instead of trying to race it from another thread.
675 for(size_t i = 0; i < G_N_ELEMENTS(pipes) && dt_control_running() && !dev->exit; i++)
676 {
677 if(!pipe_needs_update[i]) continue;
678
679 dt_dev_pixelpipe_t *pipe = pipes[i];
680 const dt_iop_roi_t roi = pipe_roi[i];
681
682 // The resync stage above synchronized history, planned the ROI and advertised the matching
683 // final global hash. We process the exact state it committed, using pipe_roi[i]: that is what
684 // dt_dev_pixelpipe_process() recomputes its hash from, so the cacheline it publishes always
685 // carries the hash already announced to GUI consumers. A change that lands after this point
686 // raises the killswitch together with its changed flag (`_change_pipe()`, and
687 // dt_dev_roi_request_publish() does the same on a payload change), so the run below aborts
688 // and the next loop iteration resyncs, re-advertises and reprocesses the new state. We must
689 // NOT skip on a set changed flag here: every darkroom configure-event flags the preview pipe
690 // ZOOMED, so on an image switch (which re-lays-out the view) skipping would starve the
691 // preview and leave navigation/scopes blank.
692 //
693 // A change that landed BETWEEN this iteration's latch and this point is another matter, and
694 // the check below is the second half of the #1157 fix. The resync's while(changed) loop
695 // spans 100-400 ms and consumes whatever flags land inside it, syncing their history -- but
696 // the ROI was then planned from THIS ITERATION'S latch, taken before any of it. Their
697 // killswitch does not save the run either, since the reset a few lines down would swallow
698 // it. Rendering the consumed history into the stale latched geometry is how a crop Apply
699 // produced a border-clamped smear that nothing ever corrected. So: if the published record
700 // has moved past the latch, this plan is dead. Re-flag (the flag may have been consumed) and
701 // skip the run; the next iteration re-latches and replans from the current record. This
702 // cannot starve: the generation only advances when a GUI publish actually changed the
703 // payload, so a quiet viewport lets the very next iteration through.
704 const dt_dev_roi_request_t current_request = dt_dev_roi_request_get(dev);
705 if(current_request.generation != latched.generation)
706 {
708 continue;
709 }
710
711 dt_print(DT_DEBUG_PIPE | DT_DEBUG_DEV, "PIPE %s needs update\n", pipe->type == DT_DEV_PIXELPIPE_FULL ? "full" : "preview");
712
714 pipe->processing = 1;
715
716 // We are starting fresh, reset the killswitch signal. Safe only because the staleness
717 // check above ran after the resync: any killswitch this discards belonged to a change
718 // whose flag either survives (raised after the resync loop exited) or whose publish
719 // moved the generation (caught above). Do not move this reset earlier.
721
729 const gboolean retrying_raster_mask = dt_dev_pixelpipe_has_reentry(pipe);
730
731 // Whether the recompute was triggered because we needed only the output of
732 // a specified module, or we needed the output backbuf of the whole pipeline.
733 // This allows partial pipeline runs, e.g. for histograms and color-pickers.
735
736 // At zoom == fit, both preview and main pipelines have the same size,
737 // so the first one that runs will prevent the next from running
738 // (backbuf fetched directly from pipeline cache).
739 // Therefore we can't rely solely on pipeline type to raise completion signals.
740 const gboolean has_preview_size = dt_dev_pixelpipe_has_preview_output(dev, pipe, &roi);
741
747 const gboolean requested_mask_preview
748 = pipe == dev->pipe
749 && !IS_NULL_PTR(dev->gui_module)
751
752 // Connect GUI feedback for "pipe busy"
755 dev->progress.completed = 0;
756 dev->progress.total = 0;
757
758 dt_times_t thread_start;
759 dt_get_times(&thread_start);
760
761 // The actual processing with runtime log
762 const gint64 process_start_us = g_get_monotonic_time();
763 const int ret = dt_dev_pixelpipe_process(pipe, roi);
764 const gint64 process_runtime_us = g_get_monotonic_time() - process_start_us;
765
766 // Print perf log
767 gchar *msg = g_strdup_printf("[dev_process_%s] pipeline processing thread",
769 dt_show_times(&thread_start, msg);
770 dt_free(msg);
771
772 // Disconnect GUI feedback for "pipe busy"
773 dev->progress.completed = 0;
774 dev->progress.total = 0;
777
778 // Pipeline completed entirely without error
779 const gboolean processed = (!ret && !dt_atomic_get_int(&pipe->shutdown));
780
788 const gboolean published_backbuffer
789 = processed && dt_dev_pixelpipe_is_backbufer_valid(pipe);
790
791 // Pipeline reentry flag is set when we lost the reference to a raster mask.
792 // This typically happens on re-entering darkroom after having gone to lighttable:
793 // the pipeline cache is kept but the references to raster masks are flushed,
794 // so the pipeline recomputation resumes downstream from the last-known cacheline,
795 // which may not refresh raster masks if produced upstream in pipeline.
796 // TODO: cache raster masks too (was attempted before, and failed).
798 {
799 if(retrying_raster_mask)
800 {
801 // Reentry flag was set already before last pipe run, which refreshed
802 // everything we needed. We can resume to normal mode.
803 // In case that wasn't true, it will be caught at the next run.
805 }
806 else
807 {
808 // Reentry flag was set during the last pipe run, which means we
809 // lost at least a raster mask reference, and need to retry again
810 // from the start.
811 // The synchronized graph and its ROIs are still valid. The retry
812 // only needs another processing pass after targeted cache
813 // invalidation, not node destruction and history reconstruction.
815 }
816 }
817
826 if(processed
827 && cache_request == DT_DEV_PIXELPIPE_CACHE_REQUEST_MODULE
828 && !published_backbuffer)
829 {
833 }
834
835 pipe->processing = 0;
837
838 // Update the running average of process time for GUI controls thresholding
839 if(processed)
840 {
841 // Map the pipe onto the throttle's own two slots; it does not know pipeline types.
845 dt_gui_throttle_record_runtime(slot, process_runtime_us);
846 }
847
848 // If everything went well, yell to GUI listeners that they can use the output buffer.
849 if(published_backbuffer)
850 {
851 if(pipe->type == DT_DEV_PIXELPIPE_FULL)
852 {
855 }
856 if(pipe->type == DT_DEV_PIXELPIPE_PREVIEW || has_preview_size)
857 {
860 }
861
873 && has_preview_size
874 && !requested_mask_preview
876 dt_dev_resync_mipmap_cache(dev, pipe, roi);
877 }
878
879 // Allow some breathing room to the OS and GPU
880 dt_iop_nap(10000); // 10 ms
881 }
882
884 dt_iop_nap(10000);
885 else
886 dt_iop_nap(50000);
887 }
888
889 for(size_t i = 0; i < G_N_ELEMENTS(pipes); i++)
890 dt_atomic_set_int(&pipes[i]->running, FALSE);
891}
892
894{
897 return 0;
898}
899
901{
902 dt_job_t *job = dt_control_job_create(&dt_dev_process_job_run, "develop process image");
903 if(IS_NULL_PTR(job)) return NULL;
904 dt_control_job_set_params(job, dev, NULL);
905 return job;
906}
907
914
915static gboolean _dt_dev_mipmap_prefetch_full(dt_develop_t *dev, const int32_t imgid)
916{
919
920 const gboolean ok = (!IS_NULL_PTR(buf.buf)) && buf.width != 0 && buf.height != 0;
921
922 // dt_dev_geometry_raw_width(dev)/height are the raw image's own pixel dimensions -- an objective fact
923 // about the loaded buffer, not GUI viewport state -- and must be set for every dev, not just
924 // gui_attached ones. dt_dev_coordinates_raw_norm_to_raw_abs() (develop.c) and every drawn-mask
925 // shape's own geometry function (masks/circle.c, ellipse.c, brush.c, gradient.c, polygon.c) read
926 // dt_dev_geometry_raw_width(dev)/height to convert a form's normalized center/points into absolute pixel
927 // coordinates; with raw_width/height left at 0 (their calloc default), that conversion silently
928 // no-ops (dt_dev_coordinates_raw_norm_to_raw_abs() early-returns on raw_width==0), leaving the
929 // shape's normalized (0..1) coordinates masquerading as pixel coordinates -- collapsing every
930 // shape's computed position to somewhere near the image origin regardless of where it was
931 // actually drawn. That made every drawn mask on any non-GUI dev (export, thumbnail generation,
932 // dev_snapshot.c's frozen dev) resolve to the wrong location, so a module needing mask history
933 // (retouch's clone/heal/blur/fill, or any masked blend) either processes empty geometry or
934 // silently produces zero visible effect outside the live darkroom.
935 // Publish what the read actually produced. `ok' is FALSE when the mipmap cache handed back
936 // no buffer or a 0-sized one; claiming raw_inited in that case told every later reader that
937 // 0x0 was a measured fact about the image, and dt_dev_geometry_refresh()'s own guard
938 // (dt_dev_geometry_raw_inited) then let the size fold run on it.
939 dt_dev_geometry_set_raw_size(dev, ok ? buf.width : 0, ok ? buf.height : 0, ok);
940
942
943 return ok;
944}
945
946static gboolean _dt_dev_refresh_image_storage(dt_develop_t *dev, const int32_t imgid)
947{
948 const dt_image_t *image = dt_image_cache_get(imgid, 'r');
949 if(IS_NULL_PTR(image)) return FALSE;
950 dev->image_storage = *image;
953 return TRUE;
954}
955
969
970// load the raw and get the new image struct, blocking in gui thread
971static inline dt_dev_image_storage_t _dt_dev_load_raw(dt_develop_t *dev, const int32_t imgid)
972{
973 // then load the raw
974 dt_times_t start;
975 dt_get_times(&start);
976
977 // Test we got images. Also that populates the cache for later.
978 // Refresh our private copy in case raw loading updated image metadata
979 const dt_dev_image_storage_t storage_status = dt_dev_ensure_image_storage(dev, imgid);
980 if(storage_status)
981 return storage_status;
982
983 dt_show_times_f(&start, "[dev_pixelpipe]", "to load the image.");
984
985 return storage_status;
986}
987
988// return the zoom scale to fit into the viewport
989float dt_dev_get_zoom_scale(const dt_develop_t *dev, const gboolean preview)
990{
992 const int32_t processed_width = geometry.processed_width;
993 const int32_t processed_height = geometry.processed_height;
994
995 const float w = preview ? processed_width : dev->pipe->processed_width;
996 const float h = preview ? processed_height : dev->pipe->processed_height;
997 return fminf(dt_dev_viewport_box_width(dev) / w, dt_dev_viewport_box_height(dev) / h);
998}
999
1001{
1002 const dt_dev_image_storage_t ret = _dt_dev_load_raw(dev, imgid);
1003 if(ret) return ret;
1004
1005 // we need a global lock as the dev->iop set must not be changed until read history is terminated
1007
1008 const gboolean first_run = dt_dev_read_history_ext(dev, imgid);
1009
1010 if(first_run && dev == dt_dev_get_global())
1011 {
1012 // Resync our private copy of image image with DB,
1013 // mostly for DT_IMAGE_AUTO_PRESETS_APPLIED flag.
1014 dt_image_t *image = dt_image_cache_get(imgid, 'w');
1015 if(!IS_NULL_PTR(image))
1016 {
1017 *image = dev->image_storage;
1019 }
1020
1021 dt_dev_write_history_ext(dev, imgid);
1022 }
1023
1025
1026 if(first_run && dev == dt_dev_get_global())
1027 {
1029 dt_dev_history_notify_change(dev, imgid);
1030 }
1031
1032 return ret;
1033}
1034
1035void dt_dev_configure_real(dt_develop_t *dev, int wd, int ht)
1036{
1037 // Called only from Darkroom to convert the widget allocation into the
1038 // raster ROI contract consumed by the pipeline. Everything stored in
1039 // dev->roi below is expressed in real buffer pixels.
1040 const dt_iop_roi_t gui_roi = { .x = 0, .y = 0, .width = wd, .height = ht, .scale = 1.0f };
1041 dt_iop_roi_t pipe_roi = { 0 };
1043 dt_dev_viewport_set_box(dev, pipe_roi.width, pipe_roi.height);
1044
1046 "[pixelpipe] Darkroom requested a %i×%i px widget -> %i×%i px raster preview\n",
1048
1053}
1054
1066{
1067 const dt_dev_viewport_state_t viewport = dt_dev_viewport_get(dev);
1068 float center_x = viewport.center_x;
1069 float center_y = viewport.center_y;
1070 dt_dev_check_zoom_pos_bounds(dev, &center_x, &center_y, NULL, NULL);
1071 return dt_dev_viewport_set_center(dev, center_x, center_y);
1072}
1073
1074void dt_dev_check_zoom_pos_bounds(dt_develop_t *dev, float *dev_x, float *dev_y, float *box_w, float *box_h)
1075{
1076 // for the debug strings lower
1077 //float old_x = *dev_x;
1078 //float old_y = *dev_y;
1079 int proc_w = 0;
1080 int proc_h = 0;
1081 dt_dev_get_processed_size(dev, &proc_w, &proc_h);
1082 const float scale = dt_dev_get_zoom_level(dev);
1083
1084 // find the box size
1085 const float bw = dt_dev_viewport_box_width(dev) / (proc_w * scale);
1086 const float bh = dt_dev_viewport_box_height(dev) / (proc_h * scale);
1087
1088 // calculate half-dimensions once
1089 const float half_bw = bw * 0.5f;
1090 const float half_bh = bh * 0.5f;
1091
1092 // clamp position using pre-calculated values
1093 *dev_x = (bw > 1.0f || dt_dev_viewport_scaling(dev) <= 1.0f) ? 0.5f : CLAMPF(*dev_x, half_bw, 1.0f - half_bw);
1094 *dev_y = (bh > 1.0f || dt_dev_viewport_scaling(dev) <= 1.0f) ? 0.5f : CLAMPF(*dev_y, half_bh, 1.0f - half_bh);
1095 // return box size
1096 if(!IS_NULL_PTR(box_w)) *box_w = bw;
1097 if(!IS_NULL_PTR(box_h)) *box_h = bh;
1098
1099 /*
1100 fprintf(stdout, "BOUNDS: box size: %2.2f x %2.2f\n", bw, bh);
1101 fprintf(stdout, "BOUNDS: half box size: %2.2f x %2.2f\n", half_bw, half_bh);
1102 fprintf(stdout, "BOUNDS: X pos: %2.2f -> %2.2f [%2.2f %2.2f]\n",
1103 old_x, *dev_x, half_bw, 1.0f - half_bw);
1104 fprintf(stdout, "BOUNDS: Y pos: %2.2f -> %2.2f [%2.2f %2.2f]\n",
1105 old_y, *dev_y, half_bh, 1.0f - half_bh);
1106*/
1107}
1108
1109void dt_dev_get_processed_size(const dt_develop_t *dev, int *procw, int *proch)
1110{
1111 // Write the outputs on EVERY path. Callers declare them uninitialized and read them straight
1112 // back -- iop/crop.c's _aspect_apply() does `int iwd, iht; dt_dev_get_processed_size(...);
1113 // if(iwd < iht)` -- so returning without writing hands them whatever was on the stack. Zero
1114 // is not a meaningful size, but it is a deterministic one, and it is what those callers
1115 // already behave sanely for; garbage is what they could not.
1116 if(!IS_NULL_PTR(procw)) *procw = 0;
1117 if(!IS_NULL_PTR(proch)) *proch = 0;
1118
1119 if(IS_NULL_PTR(dev)) return;
1121 const int32_t processed_width = geometry.processed_width;
1122 const int32_t processed_height = geometry.processed_height;
1123 if(!IS_NULL_PTR(procw)) *procw = processed_width;
1124 if(!IS_NULL_PTR(proch)) *proch = processed_height;
1125}
1126
1127void dt_dev_coordinates_widget_delta_to_image_delta(dt_develop_t *dev, float *points, size_t num_points)
1128{
1129 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1130
1131 const float scale = dt_dev_get_zoom_level(dev) / dt_gui_get_global()->ppd;
1132 if(scale == 0.0f) return;
1133
1134 // Widget deltas are measured in Gtk logical pixels. Convert them to processed-image
1135 // pixels here so dragging thresholds and keyboard pans share the same zoom math.
1136 for(size_t i = 0; i < num_points; ++i)
1137 {
1138 const size_t idx = i * 2;
1139 points[idx + 0] /= scale;
1140 points[idx + 1] /= scale;
1141 }
1142}
1143
1144void dt_dev_coordinates_widget_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
1145{
1146 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1148 const float processed_width = geometry.processed_width;
1149 const float processed_height = geometry.processed_height;
1150 if(processed_width == 0.0f || processed_height == 0.0f) return;
1151
1152 // Widget events are expressed in GUI logical coordinates, while the pipeline
1153 // zoom lives in raster pixels. Convert back to the same GUI-space zoom used
1154 // by dt_dev_rescale_roi() so event hit-testing and overlay drawing stay aligned.
1155 const float scale = dt_dev_get_zoom_level(dev) / dt_gui_get_global()->ppd;
1156 const float roi_x = (float)dt_dev_viewport_center_x(dev);
1157 const float roi_y = (float)dt_dev_viewport_center_y(dev);
1158 const float center_x = 0.5f * (float)dt_dev_viewport_widget_width(dev);
1159 const float center_y = 0.5f * (float)dt_dev_viewport_widget_height(dev);
1160 const float inv_scaled_width = 1.0f / (processed_width * scale);
1161 const float inv_scaled_height = 1.0f / (processed_height * scale);
1162
1163 for(size_t i = 0; i < num_points; ++i)
1164 {
1165 const size_t idx = i * 2;
1166 const float px = points[idx + 0];
1167 const float py = points[idx + 1];
1168 points[idx + 0] = roi_x + (px - center_x) * inv_scaled_width;
1169 points[idx + 1] = roi_y + (py - center_y) * inv_scaled_height;
1170 }
1171}
1172
1173void dt_dev_coordinates_image_norm_to_widget(dt_develop_t *dev, float *points, size_t num_points)
1174{
1175 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1177 const float processed_width = geometry.processed_width;
1178 const float processed_height = geometry.processed_height;
1179 if(processed_width == 0.0f || processed_height == 0.0f) return;
1180
1181 // GUI overlays are drawn in logical widget coordinates, so use the same
1182 // GUI-space zoom that the Cairo darkroom transform applies.
1183 const float scale = dt_dev_get_zoom_level(dev) / dt_gui_get_global()->ppd;
1184 const float roi_x = (float)dt_dev_viewport_center_x(dev);
1185 const float roi_y = (float)dt_dev_viewport_center_y(dev);
1186 const float scaled_width = processed_width * scale;
1187 const float scaled_height = processed_height * scale;
1188 const float center_x = 0.5f * (float)dt_dev_viewport_widget_width(dev);
1189 const float center_y = 0.5f * (float)dt_dev_viewport_widget_height(dev);
1190
1191 for(size_t i = 0; i < num_points; ++i)
1192 {
1193 const size_t idx = i * 2;
1194 const float px = points[idx + 0];
1195 const float py = points[idx + 1];
1196 const float dx = (px - roi_x) * scaled_width;
1197 const float dy = (py - roi_y) * scaled_height;
1198 points[idx + 0] = dx + center_x;
1199 points[idx + 1] = dy + center_y;
1200 }
1201}
1202
1203void dt_dev_coordinates_image_norm_to_image_abs(dt_develop_t *dev, float *points, size_t num_points)
1204{
1205 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1207 const float processed_width = geometry.processed_width;
1208 const float processed_height = geometry.processed_height;
1209 if(processed_width == 0.0f || processed_height == 0.0f) return;
1210
1211 for(size_t i = 0; i < num_points; ++i)
1212 {
1213 const size_t idx = i * 2;
1214 points[idx + 0] *= processed_width;
1215 points[idx + 1] *= processed_height;
1216 }
1217}
1218
1219void dt_dev_coordinates_image_abs_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
1220{
1221 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1223 const float processed_width = geometry.processed_width;
1224 const float processed_height = geometry.processed_height;
1225 if(processed_width == 0.0f || processed_height == 0.0f) return;
1226
1227 const float inv_width = 1.0f / processed_width;
1228 const float inv_height = 1.0f / processed_height;
1229 for(size_t i = 0; i < num_points; ++i)
1230 {
1231 const size_t idx = i * 2;
1232 points[idx + 0] *= inv_width;
1233 points[idx + 1] *= inv_height;
1234 }
1235}
1236
1237void dt_dev_coordinates_raw_abs_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
1238{
1239 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1241 const float raw_width = geometry.raw_width;
1242 const float raw_height = geometry.raw_height;
1243 if(raw_width == 0.0f || raw_height == 0.0f) return;
1244
1245 const float inv_width = 1.f / raw_width;
1246 const float inv_height = 1.f / raw_height;
1247 for(size_t i = 0; i < num_points; i++)
1248 {
1249 const size_t idx = i * 2;
1250 points[idx + 0] *= inv_width;
1251 points[idx + 1] *= inv_height;
1252 }
1253}
1254
1255void dt_dev_coordinates_raw_norm_to_raw_abs(dt_develop_t *dev, float *points, size_t num_points)
1256{
1257 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1259 const float raw_width = geometry.raw_width;
1260 const float raw_height = geometry.raw_height;
1261 if(raw_width == 0.0f || raw_height == 0.0f) return;
1262
1263 for(size_t i = 0; i < num_points; i++)
1264 {
1265 const size_t idx = i * 2;
1266 points[idx + 0] *= raw_width;
1267 points[idx + 1] *= raw_height;
1268 }
1269}
1270
1271void dt_dev_coordinates_image_norm_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
1272{
1273 dt_dev_coordinates_image_norm_to_image_abs(dev, points, num_points);
1274 dt_dev_coordinates_image_abs_to_raw_abs(dev, points, num_points);
1275 dt_dev_coordinates_raw_abs_to_raw_norm(dev, points, num_points);
1276}
1277
1278void dt_dev_coordinates_raw_norm_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
1279{
1280 dt_dev_coordinates_raw_norm_to_raw_abs(dev, points, num_points);
1281 dt_dev_coordinates_raw_abs_to_image_abs(dev, points, num_points);
1282 dt_dev_coordinates_image_abs_to_image_norm(dev, points, num_points);
1283}
1284
1285void dt_dev_coordinates_image_abs_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
1286{
1287 dt_dev_coordinates_image_abs_to_raw_abs(dev, points, num_points);
1288 dt_dev_coordinates_raw_abs_to_raw_norm(dev, points, num_points);
1289}
1290
1291void dt_dev_coordinates_image_norm_to_preview_abs(dt_develop_t *dev, float *points, size_t num_points)
1292{
1293 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1294 const float preview_width = dt_dev_roi_request_preview_width(dev);
1295 const float preview_height = dt_dev_roi_request_preview_height(dev);
1296 if(preview_width == 0.0f || preview_height == 0.0f) return;
1297
1298 for(size_t i = 0; i < num_points; i++)
1299 {
1300 const size_t idx = i * 2;
1301 points[idx + 0] *= preview_width;
1302 points[idx + 1] *= preview_height;
1303 }
1304}
1305
1306void dt_dev_coordinates_preview_abs_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
1307{
1308 if(IS_NULL_PTR(dev) || IS_NULL_PTR(points) || num_points == 0) return;
1309 const float preview_width = dt_dev_roi_request_preview_width(dev);
1310 const float preview_height = dt_dev_roi_request_preview_height(dev);
1311 if(preview_width == 0.0f || preview_height == 0.0f) return;
1312
1313 const float inv_width = 1.f / preview_width;
1314 const float inv_height = 1.f / preview_height;
1315 for(size_t i = 0; i < num_points; i++)
1316 {
1317 const size_t idx = i * 2;
1318 points[idx + 0] *= inv_width;
1319 points[idx + 1] *= inv_height;
1320 }
1321}
1322
1324{
1325 return (dev->image_storage.id == imgid) ? 1 : 0;
1326}
1327
1333
1335{
1336 return dev && dev->proxy.masks.module && dev->proxy.masks.is_visible
1337 && dev->proxy.masks.is_visible(dev->proxy.masks.module);
1338}
1339
1341{
1342 if(dev->proxy.masks.module && dev->proxy.masks.list_change)
1343 dev->proxy.masks.list_change(dev->proxy.masks.module);
1344}
1346{
1347 if(dev->proxy.masks.module && dev->proxy.masks.list_update)
1348 dev->proxy.masks.list_update(dev->proxy.masks.module);
1349}
1350void dt_dev_masks_list_remove(dt_develop_t *dev, int formid, int parentid)
1351{
1352 if(dev->proxy.masks.module && dev->proxy.masks.list_remove)
1353 dev->proxy.masks.list_remove(dev->proxy.masks.module, formid, parentid);
1354}
1356 const int selectid, const int throw_event)
1357{
1358 if(dev->proxy.masks.module && dev->proxy.masks.selection_change)
1359 dev->proxy.masks.selection_change(dev->proxy.masks.module, module, selectid, throw_event);
1360}
1361
1362void dt_dev_snapshot_request(dt_develop_t *dev, const char *filename)
1363{
1364 dev->proxy.snapshot.filename = filename;
1365 dev->proxy.snapshot.request = TRUE;
1367}
1368
1371{
1372 // we create the new module
1373 dt_iop_module_t *module = (dt_iop_module_t *)calloc(1, sizeof(dt_iop_module_t));
1374 if(dt_iop_load_module(module, base->so, base->dev)) return NULL;
1375 module->instance = base->instance;
1376
1377 // we set the multi-instance priority and the iop order
1378 int pmax = 0;
1379 for(GList *modules = base->dev->iop; modules; modules = g_list_next(modules))
1380 {
1381 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1382 if(mod->instance == base->instance)
1383 {
1384 if(pmax < mod->multi_priority) pmax = mod->multi_priority;
1385 }
1386 }
1387 // create a unique multi-priority
1388 pmax += 1;
1389 dt_iop_update_multi_priority(module, pmax);
1390
1391 // add this new module position into the iop-order-list
1393
1394 // since we do not rename the module we need to check that an old module does not have the same name. Indeed
1395 // the multi_priority
1396 // are always rebased to start from 0, to it may be the case that the same multi_name be generated when
1397 // duplicating a module.
1398 int pname = module->multi_priority;
1399 char mname[128];
1400
1401 do
1402 {
1403 snprintf(mname, sizeof(mname), "%d", pname);
1404 gboolean dup = FALSE;
1405
1406 for(GList *modules = base->dev->iop; modules; modules = g_list_next(modules))
1407 {
1408 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1409 if(mod->instance == base->instance)
1410 {
1411 if(strcmp(mname, mod->multi_name) == 0)
1412 {
1413 dup = TRUE;
1414 break;
1415 }
1416 }
1417 }
1418
1419 if(dup)
1420 pname++;
1421 else
1422 break;
1423 } while(1);
1424
1425 // the multi instance name
1426 g_strlcpy(module->multi_name, mname, sizeof(module->multi_name));
1427 // we insert this module into dev->iop
1428 base->dev->iop = g_list_insert_sorted(base->dev->iop, module, dt_sort_iop_by_order);
1429
1430 // always place the new instance after the base one
1431 if(!dt_ioppr_move_iop_after(base->dev, module, base))
1432 {
1433 fprintf(stderr, "[dt_dev_module_duplicate] can't move new instance after the base one\n");
1434 }
1435
1436 // that's all. rest of insertion is gui work !
1437 return module;
1438}
1439
1440
1441
1443{
1444 // if(dt_gui_widgets_suppressed()) return;
1445 int del = 0;
1446
1447 if(dev->gui_attached)
1448 {
1451
1452 const int history_end = dt_dev_get_history_end_ext(dev);
1453 int removed_before_end = 0;
1454 int history_pos = 0;
1455 GList *elem = dev->history;
1456 while(!IS_NULL_PTR(elem))
1457 {
1458 GList *next = g_list_next(elem);
1459 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(elem->data);
1460
1461 if(module == hist->module)
1462 {
1463 dt_print(DT_DEBUG_HISTORY, "[dt_module_remode] removing obsoleted history item: %s %s %p %p\n",
1464 hist->op_name, hist->multi_name, module, hist->module);
1466 dev->history = g_list_delete_link(dev->history, elem);
1467 if(history_pos < history_end) removed_before_end++;
1468 del = 1;
1469 }
1470 history_pos++;
1471 elem = next;
1472 }
1473
1474 if(removed_before_end > 0)
1475 dt_dev_set_history_end_ext(dev, MAX(0, history_end - removed_before_end));
1476
1480 }
1481
1482 // and we remove it from the list
1483 for(GList *modules = dev->iop; modules; modules = g_list_next(modules))
1484 {
1485 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1486 if(mod == module)
1487 {
1488 dev->iop = g_list_delete_link(dev->iop, modules);
1489 break;
1490 }
1491 }
1492}
1493
1501
1504{
1505 const int nb_instances = GPOINTER_TO_INT(
1506 g_hash_table_lookup(state->instance_counts, GINT_TO_POINTER(module->instance)));
1507
1508 dt_iop_module_t *mod_prev = (dt_iop_module_t *)g_hash_table_lookup(state->prev_visible, module);
1509 dt_iop_module_t *mod_next = (dt_iop_module_t *)g_hash_table_lookup(state->next_visible, module);
1510
1511 const gboolean move_next = mod_next
1512 ? dt_ioppr_check_can_move_after_iop(dev->iop, module, mod_next)
1513 : -1.0;
1514 const gboolean move_prev = mod_prev
1515 ? dt_ioppr_check_can_move_before_iop(dev->iop, module, mod_prev)
1516 : -1.0;
1517
1518 if(IS_NULL_PTR(module->gui)) return; // headless module: no buttons to update
1519
1520 module->gui->multi_show_new = !(module->flags() & IOP_FLAGS_ONE_INSTANCE);
1521 // Never allow deleting the base instance (multi_priority == 0) nor modules limited to one instance.
1522 module->gui->multi_show_close =
1523 (nb_instances > 1 && module->multi_priority > 0 && !(module->flags() & IOP_FLAGS_ONE_INSTANCE));
1524 if(!IS_NULL_PTR(mod_next))
1525 module->gui->multi_show_up = move_next;
1526 else
1527 module->gui->multi_show_up = 0;
1528 if(!IS_NULL_PTR(mod_prev))
1529 module->gui->multi_show_down = move_prev;
1530 else
1531 module->gui->multi_show_down = 0;
1532
1533 // If it's an additional instance supposed to be added by an history item after
1534 // the current history_end cursor, conceptually it doesn't exist yet,
1535 // even though it's dangling there on the pipe. So hide it from GUI.
1536 if(nb_instances > 1
1537 && module->multi_priority > 0
1538 && !g_hash_table_contains(state->modules_in_history, module))
1539 gtk_widget_hide(module->gui->expander);
1540}
1541
1542// FIXME: this function should just disappear, as it mixes concepts from multi-instances from before
1543// pipeline reordering and pipeline reordering.
1544// Multi-instances concept should just be ditched entirely.
1546{
1547 dt_ioppr_check_iop_order(dev, 0, "dt_dev_modules_update_multishow");
1548 const int history_end = dt_dev_get_history_end_ext(dev);
1549
1551 state.instance_counts = g_hash_table_new(g_direct_hash, g_direct_equal);
1552 state.modules_in_history = g_hash_table_new(g_direct_hash, g_direct_equal);
1553 state.prev_visible = g_hash_table_new(g_direct_hash, g_direct_equal);
1554 state.next_visible = g_hash_table_new(g_direct_hash, g_direct_equal);
1555
1556 // Precompute how many instances exist for each base module.
1557 for(GList *modules = dev->iop; modules; modules = g_list_next(modules))
1558 {
1559 const dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1560 gpointer key = GINT_TO_POINTER(mod->instance);
1561 int count = GPOINTER_TO_INT(g_hash_table_lookup(state.instance_counts, key));
1562 g_hash_table_replace(state.instance_counts, key, GINT_TO_POINTER(count + 1));
1563 }
1564
1565 // Precompute which modules exist in history up to history_end.
1566 int history_pos = 0;
1567 for(GList *history = g_list_first(dev->history);
1568 history && history_pos < history_end;
1569 history = g_list_next(history), history_pos++)
1570 {
1571 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)history->data;
1572 if(hist->module) g_hash_table_add(state.modules_in_history, hist->module);
1573 }
1574
1575 // Precompute previous visible module in pipeline order.
1576 dt_iop_module_t *last_visible = NULL;
1577 for(GList *modules = g_list_first(dev->iop); modules; modules = g_list_next(modules))
1578 {
1579 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1581 {
1582 g_hash_table_insert(state.prev_visible, mod, last_visible);
1583 last_visible = mod;
1584 }
1585 }
1586
1587 // Precompute next visible module in GUI order (reverse pipeline).
1588 last_visible = NULL;
1589 for(GList *modules = g_list_last(dev->iop); modules; modules = g_list_previous(modules))
1590 {
1591 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1593 {
1594 g_hash_table_insert(state.next_visible, mod, last_visible);
1595 last_visible = mod;
1596 }
1597 }
1598
1599 for(GList *modules = dev->iop; modules; modules = g_list_next(modules))
1600 {
1601 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
1602
1605 }
1606
1607 g_hash_table_destroy(state.instance_counts);
1608 g_hash_table_destroy(state.modules_in_history);
1609 g_hash_table_destroy(state.prev_visible);
1610 g_hash_table_destroy(state.next_visible);
1611}
1612
1613gchar *dt_history_item_get_label(const struct dt_iop_module_t *module)
1614{
1615 gchar *label;
1616 /* create a history button and add to box */
1617 if(!module->multi_name[0] || strcmp(module->multi_name, "0") == 0)
1618 label = g_strdup(module->name());
1619 else
1620 {
1621 // multi_name is free-typed user text (dt_iop_gui_rename_module()), but this label is
1622 // rendered through gtk_label_set_markup_with_mnemonic() so that module->name()'s own
1623 // mnemonic keeps working. Escape it for markup and double its underscores so it prints
1624 // literally instead of breaking markup parsing ("&") or being eaten as a mnemonic ("_").
1625 gchar *escaped_multi_name = g_markup_escape_text(module->multi_name, -1);
1626 gchar **underscore_parts = g_strsplit(escaped_multi_name, "_", -1);
1627 gchar *safe_multi_name = g_strjoinv("__", underscore_parts);
1628 g_strfreev(underscore_parts);
1629 dt_free(escaped_multi_name);
1630
1631 label = g_strdup_printf("%s %s", module->name(), safe_multi_name);
1632 dt_free(safe_multi_name);
1633 }
1634 return label;
1635}
1636
1637gchar *dt_dev_get_multi_name(const struct dt_iop_module_t *module)
1638{
1639 gboolean has_multi_name = g_strcmp0(module->multi_name, "0") != 0 && g_strcmp0(module->multi_name, "") != 0;
1640 gchar *label = has_multi_name ? g_strdup(module->multi_name) : g_strdup("");
1641
1642 return label;
1643}
1644
1645gchar *dt_history_item_get_name(const struct dt_iop_module_t *module)
1646{
1647 gchar *label;
1648 /* create a history button and add to box */
1649 if(!module->multi_name[0] || strcmp(module->multi_name, "0") == 0)
1650 label = delete_underscore(module->name());
1651 else
1652 {
1653 gchar *clean_name = delete_underscore(module->name());
1654 label = g_strdup_printf("%s %s", clean_name, module->multi_name);
1655 dt_free(clean_name);
1656 }
1657 dt_capitalize_label(label);
1658 return label;
1659}
1660
1662{
1663 gchar *clean_name = delete_underscore(module->name());
1664 gchar *label;
1665 /* create a history button and add to box */
1666 if(!module->multi_name[0] || strcmp(module->multi_name, "0") == 0)
1667 label = g_markup_escape_text(clean_name, -1);
1668 else
1669 label = g_markup_printf_escaped("%s <span size=\"smaller\">%s</span>", clean_name, module->multi_name);
1670 dt_free(clean_name);
1671 return label;
1672}
1673
1674static int dt_dev_distort_backtransform_locked(const dt_dev_pixelpipe_t *pipe, const double iop_order,
1675 const int transf_direction, float *points, size_t points_count);
1676
1677/* These two are the whole of the mask GUI's coordinate handling -- every shape's centre, every
1678 * handle, every source position routes through one of them -- and they ask the geometry service
1679 * the simplest question there is: everything, in order, no bound.
1680 */
1681
1691int dt_dev_distort_transform_gui(dt_develop_t *dev, const double iop_order, const int transf_direction,
1692 float *points, size_t points_count)
1693{
1694 if(IS_NULL_PTR(dev)) return 0;
1695 return dt_geometry_transform(dev, iop_order, transf_direction, points, points_count);
1696}
1697
1699int dt_dev_distort_backtransform_gui(dt_develop_t *dev, const double iop_order, const int transf_direction,
1700 float *points, size_t points_count)
1701{
1702 if(IS_NULL_PTR(dev)) return 0;
1703 return dt_geometry_backtransform(dev, iop_order, transf_direction, points, points_count);
1704}
1705
1731{
1732 if(IS_NULL_PTR(dev)) return FALSE;
1733
1734 int w = 0;
1735 int h = 0;
1737 && dt_geometry_chain_authoritative(dev->geometry_chain) && w > 0 && h > 0)
1738 {
1739 if(!IS_NULL_PTR(width)) *width = w;
1740 if(!IS_NULL_PTR(height)) *height = h;
1741 return TRUE;
1742 }
1743
1745 if(geometry.processed_width <= 0 || geometry.processed_height <= 0) return FALSE;
1746 if(!IS_NULL_PTR(width)) *width = geometry.processed_width;
1747 if(!IS_NULL_PTR(height)) *height = geometry.processed_height;
1748 return TRUE;
1749}
1750
1753{
1754 if(IS_NULL_PTR(dev) || IS_NULL_PTR(module)) return FALSE;
1755
1756 const dt_geometry_record_t *const record
1757 = dt_geometry_chain_find(dev->geometry_chain, module->op, module->multi_priority);
1759
1760 if(!IS_NULL_PTR(in)) *in = record->in;
1761 if(!IS_NULL_PTR(out)) *out = record->out;
1762 return TRUE;
1763}
1764
1765int dt_dev_coordinates_raw_abs_to_image_abs(dt_develop_t *dev, float *points, size_t points_count)
1766{
1767 return dt_geometry_transform(dev, 0.0, DT_DEV_TRANSFORM_DIR_ALL, points, points_count);
1768}
1769
1770int dt_dev_coordinates_image_abs_to_raw_abs(dt_develop_t *dev, float *points, size_t points_count)
1771{
1772 return dt_geometry_backtransform(dev, 0.0, DT_DEV_TRANSFORM_DIR_ALL, points, points_count);
1773}
1774
1775// only call directly or indirectly from dt_dev_distort_transform_plus, so that it runs with the history locked
1776int dt_dev_distort_transform_locked(const dt_dev_pixelpipe_t *pipe, const double iop_order,
1777 const int transf_direction, float *points, size_t points_count)
1778{
1779 for(GList *pieces = g_list_first(pipe->nodes); pieces; pieces = g_list_next(pieces))
1780 {
1781 dt_dev_pixelpipe_iop_t *piece = (dt_dev_pixelpipe_iop_t *)(pieces->data);
1782 dt_iop_module_t *module = piece->module;
1783 if(piece->enabled
1784 && ((transf_direction == DT_DEV_TRANSFORM_DIR_ALL)
1785 || (transf_direction == DT_DEV_TRANSFORM_DIR_FORW_INCL && module->iop_order >= iop_order)
1786 || (transf_direction == DT_DEV_TRANSFORM_DIR_FORW_EXCL && module->iop_order > iop_order)
1787 || (transf_direction == DT_DEV_TRANSFORM_DIR_BACK_INCL && module->iop_order <= iop_order)
1788 || (transf_direction == DT_DEV_TRANSFORM_DIR_BACK_EXCL && module->iop_order < iop_order))
1789 && !dt_dev_pixelpipe_activemodule_disables_currentmodule(pipe->dev, module))
1790 {
1791 module->distort_transform(module, pipe, piece, points, points_count);
1792 }
1793 }
1794 return 1;
1795}
1796
1797int dt_dev_distort_transform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction,
1798 float *points, size_t points_count)
1799{
1800 dt_dev_distort_transform_locked(pipe, iop_order, transf_direction, points, points_count);
1801 return 1;
1802}
1803
1804// Internal backtransform loop. Keep this file-local so callers use the public wrappers.
1805static int dt_dev_distort_backtransform_locked(const dt_dev_pixelpipe_t *pipe, const double iop_order,
1806 const int transf_direction, float *points, size_t points_count)
1807{
1808 for(GList *pieces = g_list_last(pipe->nodes); pieces; pieces = g_list_previous(pieces))
1809 {
1810 dt_dev_pixelpipe_iop_t *piece = (dt_dev_pixelpipe_iop_t *)(pieces->data);
1811 dt_iop_module_t *module = piece->module;
1812 if(piece->enabled
1813 && ((transf_direction == DT_DEV_TRANSFORM_DIR_ALL)
1814 || (transf_direction == DT_DEV_TRANSFORM_DIR_FORW_INCL && module->iop_order >= iop_order)
1815 || (transf_direction == DT_DEV_TRANSFORM_DIR_FORW_EXCL && module->iop_order > iop_order)
1816 || (transf_direction == DT_DEV_TRANSFORM_DIR_BACK_INCL && module->iop_order <= iop_order)
1817 || (transf_direction == DT_DEV_TRANSFORM_DIR_BACK_EXCL && module->iop_order < iop_order))
1818 && !dt_dev_pixelpipe_activemodule_disables_currentmodule(pipe->dev, module))
1819 {
1820 module->distort_backtransform(module, pipe, piece, points, points_count);
1821 }
1822 }
1823 return 1;
1824}
1825
1826int dt_dev_distort_backtransform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction,
1827 float *points, size_t points_count)
1828{
1829 const int success = dt_dev_distort_backtransform_locked(pipe, iop_order, transf_direction, points, points_count);
1830 return success;
1831}
1832
1834 struct dt_iop_module_t *module)
1835{
1836 for(const GList *pieces = g_list_last(pipe->nodes); pieces; pieces = g_list_previous(pieces))
1837 {
1838 dt_dev_pixelpipe_iop_t *piece = (dt_dev_pixelpipe_iop_t *)(pieces->data);
1839 if(piece->module == module)
1840 {
1841 return piece;
1842 }
1843 }
1844 return NULL;
1845}
1846
1847// set the module list order
1853
1855{
1857
1858 /* record current history state : before change (needed for undo) */
1859 if(dev->gui_attached && cv->view((dt_view_t *)cv) == DT_VIEW_DARKROOM)
1860 {
1862 }
1863}
1864
1866{
1868
1869 /* record current history state : after change (needed for undo) */
1870 if(dev->gui_attached && cv->view((dt_view_t *)cv) == DT_VIEW_DARKROOM)
1871 {
1874 }
1875}
1876
1878{
1879 if(dev->gui_attached)
1880 {
1882 const gboolean state = dev->mask_lock;
1884 return state;
1885 }
1886 return FALSE;
1887}
1888
1889void dt_masks_set_lock_mode(dt_develop_t *dev, gboolean mode)
1890{
1891 if(dev->gui_attached)
1892 {
1894 dev->mask_lock = mode;
1896 }
1897}
1898
1900{
1901 const int num_items = g_list_length(dev->history);
1902 return CLAMP(dev->history_end, 0, num_items);
1903}
1904
1905void dt_dev_set_history_end_ext(dt_develop_t *dev, const uint32_t index)
1906{
1907 const int num_items = g_list_length(dev->history);
1908 dev->history_end = CLAMP(index, 0, num_items);
1910}
1911
1912void dt_dev_append_changed_tag(const int32_t imgid)
1913{
1914 /* attach changed tag reflecting actual change */
1915 guint tagid = 0;
1916 dt_tag_new("darktable|changed", &tagid);
1917 const gboolean tag_change = dt_tag_attach(tagid, imgid, FALSE, FALSE);
1919}
1920
1922{
1923 // dev->forms is protected by masks_mutex, not history_mutex -- this only reads the
1924 // forms list, so it must not be wrapped in a history_mutex lock by callers (that would
1925 // needlessly exclude the pipeline thread's history reads while this walk runs).
1927
1928 uint64_t hash = 5381;
1929 for(GList *form = g_list_first(dev->forms); form; form = g_list_next(form))
1930 {
1931 dt_masks_form_t *shape = (dt_masks_form_t *)form->data;
1932 hash = dt_masks_form_get_own_hash(hash, dev->forms, shape);
1933 }
1934
1936
1937 // Keep on accumulating "changed" states until something saves the new stack
1938 // and resets that to 0
1939 uint64_t old_hash = dev->forms_hash;
1940 dev->forms_changed |= (old_hash != hash);
1941 dev->forms_hash = hash;
1942}
1943
1945{
1946 if(!dt_dev_viewport_configured(dev) || !dt_dev_geometry_raw_inited(dev)) return -1.f;
1947
1948 return fminf(fminf((float)dt_dev_viewport_box_width(dev) / (float)dt_dev_geometry_processed_width(dev),
1950 1.f);
1951}
1952
1954{
1955 if(IS_NULL_PTR(dev)) return 1.0f;
1957}
1958
1960{
1961 return dt_dev_get_fit_scale(dev);
1962}
1963
1965{
1966 if(IS_NULL_PTR(dev)) return 1.0f;
1968}
1969
1971{
1972 if(IS_NULL_PTR(dev) || IS_NULL_PTR(point)) return;
1973 point[0] = 0.5f * dt_dev_viewport_widget_width(dev);
1974 point[1] = 0.5f * dt_dev_viewport_widget_height(dev);
1975}
1976
1977void dt_dev_get_image_box_in_widget(const dt_develop_t *dev, const int32_t width, const int32_t height, float *box)
1978{
1979 if(IS_NULL_PTR(dev) || IS_NULL_PTR(box)) return;
1980
1981 const float scale = dt_dev_viewport_scaling(dev) / dt_gui_get_global()->ppd;
1982 const float roi_width = fminf(width, dt_dev_roi_request_preview_width(dev) * scale);
1983 const float roi_height = fminf(height, dt_dev_roi_request_preview_height(dev) * scale);
1984 const float border = dt_dev_viewport_border_size(dev);
1985
1986 box[0] = fmaxf(border, 0.5f * (width - roi_width));
1987 box[1] = fmaxf(border, 0.5f * (height - roi_height));
1988 box[2] = fminf(width - 2 * border, roi_width);
1989 box[3] = fminf(height - 2 * border, roi_height);
1990}
1991
1993{
1994 if(IS_NULL_PTR(dev)) return 1.f;
1996}
1997
1999{
2000 // Zoom and pan back to "fit". The ROI request is derived from the viewport, so the reset
2001 // republishes it; there is nothing to invalidate by hand, and an invalidation here could not
2002 // have survived that republication anyway.
2004}
2005
2006void dt_dev_convert_roi(const dt_develop_t *dev, const dt_iop_roi_t *roi_in, dt_iop_roi_t *roi_out,
2007 const dt_dev_roi_space_t from, const dt_dev_roi_space_t to)
2008{
2009 if(IS_NULL_PTR(dev) || IS_NULL_PTR(roi_in) || IS_NULL_PTR(roi_out)) return;
2010
2011 *roi_out = *roi_in;
2012 if(from == to) return;
2013
2014 const float factor = (from == DT_DEV_ROI_GUI_LOGICAL && to == DT_DEV_ROI_PIPELINE)
2016 : 1.0f / dt_gui_get_global()->ppd;
2017
2018 // x/y/width/height belong to the GUI/pipeline geometry boundary and therefore
2019 // follow the ppd factor. roi->scale stays unchanged because it expresses the
2020 // image-space sampling ratio, which must not depend on GUI density.
2021 roi_out->x = lroundf(roi_in->x * factor);
2022 roi_out->y = lroundf(roi_in->y * factor);
2023 roi_out->width = lroundf(roi_in->width * factor);
2024 roi_out->height = lroundf(roi_in->height * factor);
2025 roi_out->scale = roi_in->scale * factor;
2026}
2027
2028gboolean dt_dev_clip_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
2029{
2030 // DO NOT MODIFIY !! //
2031
2032 const float wd = dt_dev_roi_request_preview_width(dev);
2033 const float ht = dt_dev_roi_request_preview_height(dev);
2034 if(wd == 0.f || ht == 0.f) return TRUE;
2035
2036 const float zoom_scale = dt_dev_get_overlay_scale(dev);
2037 const int32_t border = dt_dev_viewport_border_size(dev);
2038 const float roi_width = fminf(width, wd * zoom_scale);
2039 const float roi_height = fminf(height, ht * zoom_scale);
2040
2041 const float rec_x = fmaxf(border, (width - roi_width) * 0.5f);
2042 const float rec_y = fmaxf(border, (height - roi_height) * 0.5f);
2043 const float rec_w = fminf(width - 2 * border, roi_width);
2044 const float rec_h = fminf(height - 2 * border, roi_height);
2045
2046 cairo_rectangle(cr, rec_x, rec_y, rec_w, rec_h);
2047 cairo_clip(cr);
2048
2049 return FALSE;
2050}
2051
2052static gboolean _dev_translate_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
2053{
2054 // DO NOT MODIFIY !! //
2055 // used by preview image scalling, guide and modules //
2056 int proc_wd = 0;
2057 int proc_ht = 0;
2058 dt_dev_get_processed_size(dev, &proc_wd, &proc_ht);
2059 if(proc_wd == 0.f || proc_ht == 0.f) return TRUE;
2060
2061 // Get image's origin position and scale
2062 const float zoom_scale = dt_dev_get_zoom_level(dev) / dt_gui_get_global()->ppd;
2063 const float tx = 0.5f * width - dt_dev_viewport_center_x(dev) * proc_wd * zoom_scale;
2064 const float ty = 0.5f * height - dt_dev_viewport_center_y(dev) * proc_ht * zoom_scale;
2065
2066 cairo_translate(cr, tx, ty);
2067
2068 return FALSE;
2069}
2070
2071gboolean dt_dev_rescale_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
2072{
2073 if(_dev_translate_roi(dev, cr, width, height))
2074 return TRUE;
2075 const float scale = dt_dev_get_fit_scale(dev);
2076 cairo_scale(cr, scale, scale);
2077
2078 return FALSE;
2079}
2080
2081gboolean dt_dev_rescale_roi_to_input(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
2082{
2083 if(_dev_translate_roi(dev, cr, width, height))
2084 return TRUE;
2085 const float scale = dt_dev_get_zoom_level(dev) / dt_gui_get_global()->ppd;
2086 cairo_scale(cr, scale, scale);
2087
2088 return FALSE;
2089}
2090
2092{
2093 const float natural_scale = dt_dev_roi_request_natural_scale(dev);
2094 const float scaling = dt_dev_viewport_scaling(dev);
2095
2096 // Limit zoom in to 16x the size of an apparent pixel on screen
2097 const float pixel_actual_size = natural_scale * scaling;
2098 const float pixel_max_size = 16.f;
2099
2100 if(pixel_actual_size >= pixel_max_size)
2101 {
2102 // Restore old scaling (caller should handle this)
2103 dt_dev_viewport_set_scaling(dev, pixel_max_size / natural_scale);
2104 return TRUE;
2105 }
2106
2107 // Limit zoom out to 1/3rd of the fit-to-window size
2108 const float min_scaling = 0.33f;
2109 if(scaling < min_scaling)
2110 {
2111 dt_dev_viewport_set_scaling(dev, min_scaling);
2112 return TRUE;
2113 }
2114 return FALSE;
2115}
2116
2118{
2119 float zoom_level = dt_dev_get_zoom_level(dev);
2120 if(zoom_level <= 0.f) zoom_level = 1.0f;
2121
2122 // Keep mouse hit-tests usable across zoom levels by bounding the selection
2123 // radius once it is expressed in image-space pixels.
2124 const float radius = dt_widget_mouse_radius();
2125 const float clamped = CLAMP(radius, DT_PIXEL_APPLY_DPI(4.0f) / zoom_level,
2126 DT_PIXEL_APPLY_DPI(15.0f) / zoom_level);
2127 dt_widget_set_mouse_radius(radius, clamped);
2128
2130 "[mouse] effect_radius=%0.3f effect_radius_clamped=%0.3f zoom_level=%0.4f ppd=%0.4f\n",
2131 radius, clamped, zoom_level, dt_screen_ppd());
2132}
2133
2134void dt_dev_set_backbuf(dt_backbuf_t *backbuf, const int width, const int height, const size_t bpp,
2135 const int64_t hash, const int64_t history_hash)
2136{
2137 /* One publication: the shape and the cacheline it describes settle together, so no consumer
2138 * can pair a hash with dimensions from a different frame. The atomics are written directly
2139 * rather than through the single-field setters, which bracket the counter themselves. */
2141 backbuf->height = height;
2142 backbuf->width = width;
2143 backbuf->bpp = bpp;
2144 dt_atomic_set_uint64(&backbuf->hash, (uint64_t)hash);
2145 dt_atomic_set_uint64(&backbuf->history_hash, (uint64_t)history_hash);
2147}
2148
2149// clang-format off
2150// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
2151// vim: shiftwidth=2 expandtab tabstop=2 cindent
2152// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
2153// 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_control_queue_redraw_center()
Request a redraw of the centre view.
Definition control.c:924
void dt_control_toast_busy_enter()
Definition control.c:895
void dt_control_log_busy_leave()
Definition control.c:903
int dt_control_running()
Definition control.c:442
void dt_control_log_busy_enter()
Definition control.c:887
void dt_control_toast_busy_leave()
Definition control.c:911
void dt_control_queue_redraw()
Request a redraw of the whole workspace.
Definition control.c:919
struct dt_control_t * dt_control_get_global(void)
Definition darktable.c:651
struct dt_develop_t * dt_dev_get_global(void)
Definition darktable.c:528
GList * dt_iop_get_modules_so(void)
Definition darktable.c:533
struct dt_view_manager_t * dt_view_manager_get_global(void)
Definition darktable.c:599
struct dt_gui_gtk_t * dt_gui_get_global(void)
Definition darktable.c:523
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:1848
dt_job_t * dt_dev_process_job_create(dt_develop_t *dev)
Definition develop.c:900
void dt_dev_masks_list_change(dt_develop_t *dev)
Definition develop.c:1340
static gboolean _dev_translate_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
Definition develop.c:2052
void dt_dev_coordinates_image_abs_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1219
float dt_dev_get_natural_scale(dt_develop_t *dev)
Definition develop.c:1944
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:1776
void dt_dev_get_processed_size(const dt_develop_t *dev, int *procw, int *proch)
Definition develop.c:1109
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:1833
void dt_masks_set_lock_mode(dt_develop_t *dev, gboolean mode)
Definition develop.c:1889
void dt_dev_modulegroups_switch_tab(dt_develop_t *dev, dt_iop_module_t *module)
Definition develop.c:1328
void dt_dev_coordinates_preview_abs_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1306
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:1730
int dt_dev_get_thumbnail_size(dt_develop_t *dev)
Definition develop.c:353
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:1502
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:1977
int dt_dev_coordinates_raw_abs_to_image_abs(dt_develop_t *dev, float *points, size_t points_count)
Definition develop.c:1765
void dt_dev_coordinates_raw_norm_to_raw_abs(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1255
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:1970
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:1699
void dt_dev_module_remove(dt_develop_t *dev, dt_iop_module_t *module)
Definition develop.c:1442
void dt_dev_cleanup(dt_develop_t *dev)
Definition develop.c:237
static void dt_dev_resync_mipmap_cache(dt_develop_t *dev, dt_dev_pixelpipe_t *pipe, dt_iop_roi_t roi)
Definition develop.c:521
int dt_dev_distort_transform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, float *points, size_t points_count)
Definition develop.c:1797
float dt_dev_get_overlay_scale(dt_develop_t *dev)
Get the overlay scale factor in GUI logical coordinates.
Definition develop.c:1959
void dt_dev_append_changed_tag(const int32_t imgid)
Definition develop.c:1912
dt_dev_image_storage_t dt_dev_load_image(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:1000
gboolean dt_dev_masks_manager_is_visible(dt_develop_t *dev)
Definition develop.c:1334
void dt_dev_coordinates_image_norm_to_preview_abs(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1291
float dt_dev_get_zoom_level(const dt_develop_t *dev)
Definition develop.c:1992
void dt_dev_coordinates_image_norm_to_widget(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1173
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:1355
void dt_dev_init(dt_develop_t *dev, int32_t gui_attached)
Definition develop.c:143
int dt_dev_is_current_image(dt_develop_t *dev, int32_t imgid)
Definition develop.c:1323
void dt_dev_undo_start_record(dt_develop_t *dev)
Definition develop.c:1854
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:1065
void dt_dev_snapshot_request(dt_develop_t *dev, const char *filename)
Definition develop.c:1362
gboolean dt_masks_get_lock_mode(dt_develop_t *dev)
Definition develop.c:1877
void dt_dev_darkroom_pipeline(dt_develop_t *dev)
Run darkroom preview and main pipelines from one background loop.
Definition develop.c:614
gchar * dt_history_item_get_label(const struct dt_iop_module_t *module)
Definition develop.c:1613
void dt_dev_coordinates_raw_norm_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1278
gchar * dt_history_item_get_name_html(const struct dt_iop_module_t *module)
Definition develop.c:1661
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:1074
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:2091
void dt_dev_reset_roi(dt_develop_t *dev)
Definition develop.c:1998
dt_iop_module_t * dt_dev_module_duplicate(dt_develop_t *dev, dt_iop_module_t *base)
Definition develop.c:1370
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:553
GList * dt_dev_load_modules(dt_develop_t *dev)
Definition develop.c:98
void dt_dev_set_backbuf(dt_backbuf_t *backbuf, const int width, const int height, const size_t bpp, const int64_t hash, const int64_t history_hash)
Definition develop.c:2134
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:1905
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:2081
static gboolean _darkroom_pipeline_inputs_ready(const dt_develop_t *dev)
Definition develop.c:335
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:1964
float dt_dev_get_zoom_scale(const dt_develop_t *dev, const gboolean preview)
Definition develop.c:989
void dt_dev_coordinates_image_norm_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1271
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:1751
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:1691
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:1805
gboolean dt_dev_pipelines_share_preview_output(dt_develop_t *dev)
Tell whether the darkroom main and preview pipes currently target the same GUI output.
Definition develop.c:504
void dt_dev_configure_real(dt_develop_t *dev, int wd, int ht)
Definition develop.c:1035
void dt_dev_masks_update_hash(dt_develop_t *dev)
Definition develop.c:1921
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:2028
int dt_dev_coordinates_image_abs_to_raw_abs(dt_develop_t *dev, float *points, size_t points_count)
Definition develop.c:1770
void dt_dev_undo_end_record(dt_develop_t *dev)
Definition develop.c:1865
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:2071
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:1127
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:2006
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:1953
dt_dev_image_storage_t dt_dev_ensure_image_storage(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:956
int32_t dt_dev_get_history_end_ext(dt_develop_t *dev)
Get the current history end index (GUI perspective).
Definition develop.c:1899
void dt_dev_modules_update_multishow(dt_develop_t *dev)
Definition develop.c:1545
void dt_dev_coordinates_image_norm_to_image_abs(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1203
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:458
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:402
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:1144
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:2117
static gboolean _dt_dev_refresh_image_storage(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:946
void dt_dev_masks_list_update(dt_develop_t *dev)
Definition develop.c:1345
static gboolean _dt_dev_mipmap_prefetch_full(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:915
static dt_dev_image_storage_t _dt_dev_load_raw(dt_develop_t *dev, const int32_t imgid)
Definition develop.c:971
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:1826
gchar * dt_dev_get_multi_name(const struct dt_iop_module_t *module)
Definition develop.c:1637
void dt_dev_masks_list_remove(dt_develop_t *dev, int formid, int parentid)
Definition develop.c:1350
static int32_t dt_dev_process_job_run(dt_job_t *job)
Definition develop.c:893
void dt_dev_coordinates_raw_abs_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1237
void dt_dev_coordinates_image_abs_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1285
gchar * dt_history_item_get_name(const struct dt_iop_module_t *module)
Definition develop.c:1645
void dt_dev_start_all_pipelines(dt_develop_t *dev)
Definition develop.c:908
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
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:83
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:1653
void dt_iop_update_multi_priority(dt_iop_module_t *module, int new_priority)
Definition imageop.c:1801
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:137
int32_t dt_control_add_job_res(dt_control_t *control, _dt_job_t *job, int32_t res)
Definition jobs.c:351
void * dt_control_job_get_params(const _dt_job_t *job)
Definition jobs.c:131
void dt_control_job_set_params(_dt_job_t *job, void *params, dt_job_destroy_callback callback)
Definition jobs.c:114
#define DT_CTL_WORKER_DARKROOM
Definition jobs.h:39
gchar * delete_underscore(const char *s)
Definition label.c:75
@ 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:1439
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)
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_ref_count_entry(gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Increase/Decrease the reference count on the cache line as to prevent LRU item removal....
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
double dt_screen_ppd(void)
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:386
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:616
@ DT_SIGNAL_DEVELOP_HISTORY_CHANGE
This signal is raised when develop history is changed no param, no returned value.
Definition signal.h:207
@ 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:177
@ DT_SIGNAL_HISTORY_RESYNC
This signal is raised once darkroom history has been resynchronized into all live pipelines....
Definition signal.h:212
@ 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:182
@ DT_SIGNAL_DEVELOP_MODULE_MOVED
This signal is raised when order of modules in pipeline is changed.
Definition signal.h:221
@ DT_SIGNAL_DEVELOP_MODULEGROUPS_SET
This signal is raised to request a modulegroups update. 1 : dt_iop_module_t *module,...
Definition signal.h:194
@ DT_SIGNAL_TAG_CHANGED
This signal is raised when a tag is added/deleted/changed
Definition signal.h:133
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
Consistent snapshot of the display and soft-proofing settings.
dt_colorspaces_color_profile_type_t display_type
monitor profile identity
Objective facts about the image a dev is working on.
GHashTable * modules_in_history
Definition develop.c:1497
GHashTable * prev_visible
Definition develop.c:1498
GHashTable * next_visible
Definition develop.c:1499
GHashTable * instance_counts
Definition develop.c:1496
dt_pthread_mutex_t busy_mutex
dt_backbuf_t backbuf
dt_atomic_int shutdown
dt_dev_pixelpipe_type_t type
struct dt_develop_t * dev
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
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:419
int32_t id
Definition image.h:401
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
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:2138
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
const dt_view_t * dt_view_manager_get_current_view(dt_view_manager_t *vm)
Definition view.c:139
@ 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)