1 /*
2 This file is part of darktable,
3 Copyright (C) 2009-2021 darktable developers.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include <assert.h>
19 #include <glib/gprintf.h>
20 #include <math.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <strings.h>
25 #include <unistd.h>
26
27 #include "common/atomic.h"
28 #include "common/debug.h"
29 #include "common/history.h"
30 #include "common/image_cache.h"
31 #include "common/imageio.h"
32 #include "common/mipmap_cache.h"
33 #include "common/opencl.h"
34 #include "common/tags.h"
35 #include "control/conf.h"
36 #include "control/control.h"
37 #include "control/jobs.h"
38 #include "develop/blend.h"
39 #include "develop/develop.h"
40 #include "develop/imageop.h"
41 #include "develop/lightroom.h"
42 #include "develop/masks.h"
43 #include "gui/gtk.h"
44 #include "gui/presets.h"
45
46 #define DT_DEV_AVERAGE_DELAY_START 250
47 #define DT_DEV_PREVIEW_AVERAGE_DELAY_START 50
48 #define DT_DEV_AVERAGE_DELAY_COUNT 5
49 #define DT_IOP_ORDER_INFO (darktable.unmuted & DT_DEBUG_IOPORDER)
50
dt_dev_init(dt_develop_t * dev,int32_t gui_attached)51 void dt_dev_init(dt_develop_t *dev, int32_t gui_attached)
52 {
53 memset(dev, 0, sizeof(dt_develop_t));
54 dev->full_preview = FALSE;
55 dev->gui_module = NULL;
56 dev->timestamp = 0;
57 dev->average_delay = DT_DEV_AVERAGE_DELAY_START;
58 dev->preview_average_delay = DT_DEV_PREVIEW_AVERAGE_DELAY_START;
59 dev->preview2_average_delay = DT_DEV_PREVIEW_AVERAGE_DELAY_START;
60 dev->gui_leaving = 0;
61 dev->gui_synch = 0;
62 dt_pthread_mutex_init(&dev->history_mutex, NULL);
63 dev->history_end = 0;
64 dev->history = NULL; // empty list
65
66 dev->gui_attached = gui_attached;
67 dev->width = -1;
68 dev->height = -1;
69
70 dt_image_init(&dev->image_storage);
71 dev->image_status = dev->preview_status = dev->preview2_status = DT_DEV_PIXELPIPE_DIRTY;
72 dev->history_updating = dev->image_force_reload = dev->image_loading = dev->preview_loading = FALSE;
73 dev->preview2_loading = dev->preview_input_changed = dev->preview2_input_changed = FALSE;
74 dev->image_invalid_cnt = 0;
75 dev->pipe = dev->preview_pipe = dev->preview2_pipe = NULL;
76 dt_pthread_mutex_init(&dev->pipe_mutex, NULL);
77 dt_pthread_mutex_init(&dev->preview_pipe_mutex, NULL);
78 dt_pthread_mutex_init(&dev->preview2_pipe_mutex, NULL);
79 dev->histogram_pre_tonecurve = NULL;
80 dev->histogram_pre_levels = NULL;
81 dev->preview_downsampling = dt_dev_get_preview_downsampling();
82 dev->forms = NULL;
83 dev->form_visible = NULL;
84 dev->form_gui = NULL;
85 dev->allforms = NULL;
86
87 if(dev->gui_attached)
88 {
89 dev->pipe = (dt_dev_pixelpipe_t *)malloc(sizeof(dt_dev_pixelpipe_t));
90 dev->preview_pipe = (dt_dev_pixelpipe_t *)malloc(sizeof(dt_dev_pixelpipe_t));
91 dev->preview2_pipe = (dt_dev_pixelpipe_t *)malloc(sizeof(dt_dev_pixelpipe_t));
92 dt_dev_pixelpipe_init(dev->pipe);
93 dt_dev_pixelpipe_init_preview(dev->preview_pipe);
94 dt_dev_pixelpipe_init_preview2(dev->preview2_pipe);
95 dev->histogram_pre_tonecurve = (uint32_t *)calloc(4 * 256, sizeof(uint32_t));
96 dev->histogram_pre_levels = (uint32_t *)calloc(4 * 256, sizeof(uint32_t));
97
98 // FIXME: these are uint32_t, setting to -1 is confusing
99 dev->histogram_pre_tonecurve_max = -1;
100 dev->histogram_pre_levels_max = -1;
101 }
102
103 dev->iop_instance = 0;
104 dev->iop = NULL;
105 dev->alliop = NULL;
106
107 dev->allprofile_info = NULL;
108
109 dev->iop_order_version = 0;
110 dev->iop_order_list = NULL;
111
112 dev->proxy.exposure.module = NULL;
113 dev->proxy.chroma_adaptation = NULL;
114 dev->proxy.wb_is_D65 = TRUE; // don't display error messages until we know for sure it's FALSE
115 dev->proxy.wb_coeffs[0] = 0.f;
116
117 dev->rawoverexposed.enabled = FALSE;
118 dev->rawoverexposed.mode = dt_conf_get_int("darkroom/ui/rawoverexposed/mode");
119 dev->rawoverexposed.colorscheme = dt_conf_get_int("darkroom/ui/rawoverexposed/colorscheme");
120 dev->rawoverexposed.threshold = dt_conf_get_float("darkroom/ui/rawoverexposed/threshold");
121
122 dev->overexposed.enabled = FALSE;
123 dev->overexposed.mode = dt_conf_get_int("darkroom/ui/overexposed/mode");
124 dev->overexposed.colorscheme = dt_conf_get_int("darkroom/ui/overexposed/colorscheme");
125 dev->overexposed.lower = dt_conf_get_float("darkroom/ui/overexposed/lower");
126 dev->overexposed.upper = dt_conf_get_float("darkroom/ui/overexposed/upper");
127
128 dev->overlay_color.enabled = FALSE;
129 dev->overlay_color.color = dt_conf_get_int("darkroom/ui/overlay_color");
130
131 dev->iso_12646.enabled = FALSE;
132
133 dev->second_window.zoom = DT_ZOOM_FIT;
134 dev->second_window.closeup = 0;
135 dev->second_window.zoom_x = dev->second_window.zoom_y = 0;
136 dev->second_window.zoom_scale = 1.0f;
137 }
138
dt_dev_cleanup(dt_develop_t * dev)139 void dt_dev_cleanup(dt_develop_t *dev)
140 {
141 if(!dev) return;
142 // image_cache does not have to be unref'd, this is done outside develop module.
143 dt_pthread_mutex_destroy(&dev->pipe_mutex);
144 dt_pthread_mutex_destroy(&dev->preview_pipe_mutex);
145 dt_pthread_mutex_destroy(&dev->preview2_pipe_mutex);
146 dev->proxy.chroma_adaptation = NULL;
147 dev->proxy.wb_coeffs[0] = 0.f;
148 if(dev->pipe)
149 {
150 dt_dev_pixelpipe_cleanup(dev->pipe);
151 free(dev->pipe);
152 }
153 if(dev->preview_pipe)
154 {
155 dt_dev_pixelpipe_cleanup(dev->preview_pipe);
156 free(dev->preview_pipe);
157 }
158 if(dev->preview2_pipe)
159 {
160 dt_dev_pixelpipe_cleanup(dev->preview2_pipe);
161 free(dev->preview2_pipe);
162 }
163 while(dev->history)
164 {
165 dt_dev_free_history_item(((dt_dev_history_item_t *)dev->history->data));
166 dev->history = g_list_delete_link(dev->history, dev->history);
167 }
168 while(dev->iop)
169 {
170 dt_iop_cleanup_module((dt_iop_module_t *)dev->iop->data);
171 free(dev->iop->data);
172 dev->iop = g_list_delete_link(dev->iop, dev->iop);
173 }
174 while(dev->alliop)
175 {
176 dt_iop_cleanup_module((dt_iop_module_t *)dev->alliop->data);
177 free(dev->alliop->data);
178 dev->alliop = g_list_delete_link(dev->alliop, dev->alliop);
179 }
180 g_list_free_full(dev->iop_order_list, free);
181 while(dev->allprofile_info)
182 {
183 dt_ioppr_cleanup_profile_info((dt_iop_order_iccprofile_info_t *)dev->allprofile_info->data);
184 free(dev->allprofile_info->data);
185 dev->allprofile_info = g_list_delete_link(dev->allprofile_info, dev->allprofile_info);
186 }
187 dt_pthread_mutex_destroy(&dev->history_mutex);
188 free(dev->histogram_pre_tonecurve);
189 free(dev->histogram_pre_levels);
190
191 g_list_free_full(dev->forms, (void (*)(void *))dt_masks_free_form);
192 g_list_free_full(dev->allforms, (void (*)(void *))dt_masks_free_form);
193
194 dt_conf_set_int("darkroom/ui/rawoverexposed/mode", dev->rawoverexposed.mode);
195 dt_conf_set_int("darkroom/ui/rawoverexposed/colorscheme", dev->rawoverexposed.colorscheme);
196 dt_conf_set_float("darkroom/ui/rawoverexposed/threshold", dev->rawoverexposed.threshold);
197
198 dt_conf_set_int("darkroom/ui/overexposed/mode", dev->overexposed.mode);
199 dt_conf_set_int("darkroom/ui/overexposed/colorscheme", dev->overexposed.colorscheme);
200 dt_conf_set_float("darkroom/ui/overexposed/lower", dev->overexposed.lower);
201 dt_conf_set_float("darkroom/ui/overexposed/upper", dev->overexposed.upper);
202
203 dt_conf_set_int("darkroom/ui/overlay_color", dev->overlay_color.color);
204 }
205
dt_dev_get_preview_downsampling()206 float dt_dev_get_preview_downsampling()
207 {
208 gchar *preview_downsample = dt_conf_get_string("preview_downsampling");
209 const float downsample = (g_strcmp0(preview_downsample, "original") == 0) ? 1.0f
210 : (g_strcmp0(preview_downsample, "to 1/2")==0) ? 0.5f
211 : (g_strcmp0(preview_downsample, "to 1/3")==0) ? 1/3.0f
212 : 0.25f;
213 g_free(preview_downsample);
214 return downsample;
215 }
216
dt_dev_process_image(dt_develop_t * dev)217 void dt_dev_process_image(dt_develop_t *dev)
218 {
219 if(!dev->gui_attached || dev->pipe->processing) return;
220 int err
221 = dt_control_add_job_res(darktable.control, dt_dev_process_image_job_create(dev), DT_CTL_WORKER_ZOOM_1);
222 if(err) fprintf(stderr, "[dev_process_image] job queue exceeded!\n");
223 }
224
dt_dev_process_preview(dt_develop_t * dev)225 void dt_dev_process_preview(dt_develop_t *dev)
226 {
227 if(!dev->gui_attached) return;
228 int err = dt_control_add_job_res(darktable.control, dt_dev_process_preview_job_create(dev),
229 DT_CTL_WORKER_ZOOM_FILL);
230 if(err) fprintf(stderr, "[dev_process_preview] job queue exceeded!\n");
231 }
232
dt_dev_process_preview2(dt_develop_t * dev)233 void dt_dev_process_preview2(dt_develop_t *dev)
234 {
235 if(!dev->gui_attached) return;
236 if(!(dev->second_window.widget && GTK_IS_WIDGET(dev->second_window.widget))) return;
237 int err = dt_control_add_job_res(darktable.control, dt_dev_process_preview2_job_create(dev),
238 DT_CTL_WORKER_ZOOM_2);
239 if(err) fprintf(stderr, "[dev_process_preview2] job queue exceeded!\n");
240 }
241
dt_dev_invalidate(dt_develop_t * dev)242 void dt_dev_invalidate(dt_develop_t *dev)
243 {
244 dev->image_status = DT_DEV_PIXELPIPE_DIRTY;
245 dev->timestamp++;
246 if(dev->preview_pipe) dev->preview_pipe->input_timestamp = dev->timestamp;
247 if(dev->preview2_pipe) dev->preview2_pipe->input_timestamp = dev->timestamp;
248 }
249
dt_dev_invalidate_all(dt_develop_t * dev)250 void dt_dev_invalidate_all(dt_develop_t *dev)
251 {
252 dev->image_status = dev->preview_status = dev->preview2_status = DT_DEV_PIXELPIPE_DIRTY;
253 dev->timestamp++;
254 }
255
dt_dev_invalidate_preview(dt_develop_t * dev)256 void dt_dev_invalidate_preview(dt_develop_t *dev)
257 {
258 dev->preview_status = DT_DEV_PIXELPIPE_DIRTY;
259 dev->timestamp++;
260 if(dev->pipe) dev->pipe->input_timestamp = dev->timestamp;
261 if(dev->preview2_pipe) dev->preview2_pipe->input_timestamp = dev->timestamp;
262 }
263
dt_dev_process_preview_job(dt_develop_t * dev)264 void dt_dev_process_preview_job(dt_develop_t *dev)
265 {
266 if(dev->image_loading)
267 {
268 // raw is already loading, no use starting another file access, we wait.
269 return;
270 }
271
272 dt_pthread_mutex_lock(&dev->preview_pipe_mutex);
273
274 if(dev->gui_leaving)
275 {
276 dt_pthread_mutex_unlock(&dev->preview_pipe_mutex);
277 return;
278 }
279
280 dt_control_log_busy_enter();
281 dt_control_toast_busy_enter();
282 dev->preview_pipe->input_timestamp = dev->timestamp;
283 dev->preview_status = DT_DEV_PIXELPIPE_RUNNING;
284
285 // lock if there, issue a background load, if not (best-effort for mip f).
286 dt_mipmap_buffer_t buf;
287 dt_mipmap_cache_get(darktable.mipmap_cache, &buf, dev->image_storage.id, DT_MIPMAP_F, DT_MIPMAP_BEST_EFFORT,
288 'r');
289 if(!buf.buf)
290 {
291 dt_control_log_busy_leave();
292 dt_control_toast_busy_leave();
293 dev->preview_status = DT_DEV_PIXELPIPE_DIRTY;
294 dt_pthread_mutex_unlock(&dev->preview_pipe_mutex);
295 return; // not loaded yet. load will issue a gtk redraw on completion, which in turn will trigger us again
296 // later.
297 }
298 // init pixel pipeline for preview.
299 dt_dev_pixelpipe_set_input(dev->preview_pipe, dev, (float *)buf.buf, buf.width, buf.height, buf.iscale);
300
301 if(dev->preview_loading)
302 {
303 dt_dev_pixelpipe_cleanup_nodes(dev->preview_pipe);
304 dt_dev_pixelpipe_create_nodes(dev->preview_pipe, dev);
305 dt_dev_pixelpipe_flush_caches(dev->preview_pipe);
306 dev->preview_loading = FALSE;
307 }
308
309 // if raw loaded, get new mipf
310 if(dev->preview_input_changed)
311 {
312 dt_dev_pixelpipe_flush_caches(dev->preview_pipe);
313 dev->preview_input_changed = FALSE;
314 }
315
316 // always process the whole downsampled mipf buffer, to allow for fast scrolling and mip4 write-through.
317 restart:
318 if(dev->gui_leaving)
319 {
320 dt_control_log_busy_leave();
321 dt_control_toast_busy_leave();
322 dev->preview_status = DT_DEV_PIXELPIPE_INVALID;
323 dt_pthread_mutex_unlock(&dev->preview_pipe_mutex);
324 dt_mipmap_cache_release(darktable.mipmap_cache, &buf);
325 return;
326 }
327 // adjust pipeline according to changed flag set by {add,pop}_history_item.
328 // this locks dev->history_mutex.
329 dt_times_t start;
330 dt_get_times(&start);
331 dt_dev_pixelpipe_change(dev->preview_pipe, dev);
332 if(dt_dev_pixelpipe_process(
333 dev->preview_pipe, dev, 0, 0, dev->preview_pipe->processed_width * dev->preview_downsampling,
334 dev->preview_pipe->processed_height * dev->preview_downsampling, dev->preview_downsampling))
335 {
336 if(dev->preview_loading || dev->preview_input_changed)
337 {
338 dt_control_log_busy_leave();
339 dt_control_toast_busy_leave();
340 dev->preview_status = DT_DEV_PIXELPIPE_INVALID;
341 dt_pthread_mutex_unlock(&dev->preview_pipe_mutex);
342 dt_mipmap_cache_release(darktable.mipmap_cache, &buf);
343 return;
344 }
345 else
346 goto restart;
347 }
348
349 dev->preview_status = DT_DEV_PIXELPIPE_VALID;
350
351 dt_show_times(&start, "[dev_process_preview] pixel pipeline processing");
352 dt_dev_average_delay_update(&start, &dev->preview_average_delay);
353
354 // if a widget needs to be redraw there's the DT_SIGNAL_*_PIPE_FINISHED signals
355 dt_control_log_busy_leave();
356 dt_control_toast_busy_leave();
357 dt_pthread_mutex_unlock(&dev->preview_pipe_mutex);
358 dt_mipmap_cache_release(darktable.mipmap_cache, &buf);
359
360 DT_DEBUG_CONTROL_SIGNAL_RAISE(darktable.signals, DT_SIGNAL_DEVELOP_PREVIEW_PIPE_FINISHED);
361 }
362
dt_dev_process_preview2_job(dt_develop_t * dev)363 void dt_dev_process_preview2_job(dt_develop_t *dev)
364 {
365 if(dev->image_loading)
366 {
367 // raw is already loading, no use starting another file access, we wait.
368 return;
369 }
370
371 if(!(dev->second_window.widget && GTK_IS_WIDGET(dev->second_window.widget)))
372 {
373 return;
374 }
375
376 dt_pthread_mutex_lock(&dev->preview2_pipe_mutex);
377
378 if(dev->gui_leaving)
379 {
380 dt_pthread_mutex_unlock(&dev->preview2_pipe_mutex);
381 return;
382 }
383
384 dt_control_log_busy_enter();
385 dt_control_toast_busy_enter();
386 dev->preview2_pipe->input_timestamp = dev->timestamp;
387 dev->preview2_status = DT_DEV_PIXELPIPE_RUNNING;
388
389 // lock if there, issue a background load, if not (best-effort for mip f).
390 dt_mipmap_buffer_t buf;
391 dt_mipmap_cache_get(darktable.mipmap_cache, &buf, dev->image_storage.id, DT_MIPMAP_FULL, DT_MIPMAP_BLOCKING, 'r');
392
393 if(!buf.buf)
394 {
395 dt_control_log_busy_leave();
396 dt_control_toast_busy_leave();
397 dev->preview2_status = DT_DEV_PIXELPIPE_DIRTY;
398 dt_pthread_mutex_unlock(&dev->preview2_pipe_mutex);
399 return; // not loaded yet. load will issue a gtk redraw on completion, which in turn will trigger us again
400 // later.
401 }
402 // init pixel pipeline for preview2.
403 dt_dev_pixelpipe_set_input(dev->preview2_pipe, dev, (float *)buf.buf, buf.width, buf.height, 1.0 /*buf.iscale*/);
404
405 if(dev->preview2_loading)
406 {
407 dt_dev_pixelpipe_cleanup_nodes(dev->preview2_pipe);
408 dt_dev_pixelpipe_create_nodes(dev->preview2_pipe, dev);
409 dt_dev_pixelpipe_flush_caches(dev->preview2_pipe);
410 dev->preview2_loading = FALSE;
411 }
412
413 // if raw loaded, get new mipf
414 if(dev->preview2_input_changed)
415 {
416 dt_dev_pixelpipe_flush_caches(dev->preview2_pipe);
417 dev->preview2_input_changed = 0;
418 }
419
420 // always process the whole downsampled mipf buffer, to allow for fast scrolling and mip4 write-through.
421 restart:
422 if(dev->gui_leaving)
423 {
424 dt_control_log_busy_leave();
425 dt_control_toast_busy_leave();
426 dev->preview2_status = DT_DEV_PIXELPIPE_INVALID;
427 dt_pthread_mutex_unlock(&dev->preview2_pipe_mutex);
428 dt_mipmap_cache_release(darktable.mipmap_cache, &buf);
429 return;
430 }
431
432 const dt_dev_pixelpipe_change_t pipe_changed = dev->pipe->changed;
433
434 // adjust pipeline according to changed flag set by {add,pop}_history_item.
435 // this locks dev->history_mutex.
436 dt_times_t start;
437 dt_get_times(&start);
438 dt_dev_pixelpipe_change(dev->preview2_pipe, dev);
439
440 const dt_dev_zoom_t zoom = dt_second_window_get_dev_zoom(dev);
441 const int closeup = dt_second_window_get_dev_closeup(dev);
442 float zoom_x = dt_second_window_get_dev_zoom_x(dev);
443 float zoom_y = dt_second_window_get_dev_zoom_y(dev);
444 // if just changed to an image with a different aspect ratio or
445 // altered image orientation, the prior zoom xy could now be beyond
446 // the image boundary
447 if(dev->preview2_loading || (pipe_changed != DT_DEV_PIPE_UNCHANGED))
448 {
449 dt_second_window_check_zoom_bounds(dev, &zoom_x, &zoom_y, zoom, closeup, NULL, NULL);
450 dt_second_window_set_dev_zoom_x(dev, zoom_x);
451 dt_second_window_set_dev_zoom_y(dev, zoom_y);
452 }
453 const float scale = dt_second_window_get_zoom_scale(dev, zoom, 1.0f, 0) * dev->second_window.ppd;
454 int window_width = dev->second_window.width * dev->second_window.ppd;
455 int window_height = dev->second_window.height * dev->second_window.ppd;
456 if(closeup)
457 {
458 window_width /= 1 << closeup;
459 window_height /= 1 << closeup;
460 }
461
462 const int wd = MIN(window_width, dev->preview2_pipe->processed_width * scale);
463 const int ht = MIN(window_height, dev->preview2_pipe->processed_height * scale);
464 int x = MAX(0, scale * dev->preview2_pipe->processed_width * (.5 + zoom_x) - wd / 2);
465 int y = MAX(0, scale * dev->preview2_pipe->processed_height * (.5 + zoom_y) - ht / 2);
466
467 if(dt_dev_pixelpipe_process(dev->preview2_pipe, dev, x, y, wd, ht, scale))
468 {
469 if(dev->preview2_loading || dev->preview2_input_changed)
470 {
471 dt_control_log_busy_leave();
472 dt_control_toast_busy_leave();
473 dev->preview2_status = DT_DEV_PIXELPIPE_INVALID;
474 dt_pthread_mutex_unlock(&dev->preview2_pipe_mutex);
475 dt_mipmap_cache_release(darktable.mipmap_cache, &buf);
476 return;
477 }
478 else
479 goto restart;
480 }
481
482 dev->preview2_pipe->backbuf_scale = scale;
483 dev->preview2_pipe->backbuf_zoom_x = zoom_x;
484 dev->preview2_pipe->backbuf_zoom_y = zoom_y;
485
486 dev->preview2_status = DT_DEV_PIXELPIPE_VALID;
487
488 dt_show_times(&start, "[dev_process_preview2] pixel pipeline processing");
489 dt_dev_average_delay_update(&start, &dev->preview2_average_delay);
490
491 dt_control_log_busy_leave();
492 dt_control_toast_busy_leave();
493 dt_pthread_mutex_unlock(&dev->preview2_pipe_mutex);
494 dt_mipmap_cache_release(darktable.mipmap_cache, &buf);
495
496 DT_DEBUG_CONTROL_SIGNAL_RAISE(darktable.signals, DT_SIGNAL_DEVELOP_PREVIEW2_PIPE_FINISHED);
497 }
498
dt_dev_process_image_job(dt_develop_t * dev)499 void dt_dev_process_image_job(dt_develop_t *dev)
500 {
501 dt_pthread_mutex_lock(&dev->pipe_mutex);
502
503 if(dev->gui_leaving)
504 {
505 dt_pthread_mutex_unlock(&dev->pipe_mutex);
506 return;
507 }
508
509 dt_control_log_busy_enter();
510 dt_control_toast_busy_enter();
511 // let gui know to draw preview instead of us, if it's there:
512 dev->image_status = DT_DEV_PIXELPIPE_RUNNING;
513
514 dt_mipmap_buffer_t buf;
515 dt_times_t start;
516 dt_get_times(&start);
517 dt_mipmap_cache_get(darktable.mipmap_cache, &buf, dev->image_storage.id, DT_MIPMAP_FULL,
518 DT_MIPMAP_BLOCKING, 'r');
519 dt_show_times_f(&start, "[dev]", "to load the image.");
520
521 // failed to load raw?
522 if(!buf.buf)
523 {
524 dt_control_log_busy_leave();
525 dt_control_toast_busy_leave();
526 dev->image_status = DT_DEV_PIXELPIPE_DIRTY;
527 dt_pthread_mutex_unlock(&dev->pipe_mutex);
528 dev->image_invalid_cnt++;
529 return;
530 }
531
532 dt_dev_pixelpipe_set_input(dev->pipe, dev, (float *)buf.buf, buf.width, buf.height, 1.0);
533
534 if(dev->image_loading)
535 {
536 // init pixel pipeline
537 dt_dev_pixelpipe_cleanup_nodes(dev->pipe);
538 dt_dev_pixelpipe_create_nodes(dev->pipe, dev);
539 if(dev->image_force_reload) dt_dev_pixelpipe_flush_caches(dev->pipe);
540 dev->image_force_reload = FALSE;
541 if(dev->gui_attached)
542 {
543 // during load, a mipf update could have been issued.
544 dev->preview_input_changed = TRUE;
545 dev->preview_status = DT_DEV_PIXELPIPE_DIRTY;
546 dev->preview2_input_changed = TRUE;
547 dev->preview2_status = DT_DEV_PIXELPIPE_DIRTY;
548 dev->gui_synch = 1; // notify gui thread we want to synch (call gui_update in the modules)
549 dev->preview_pipe->changed |= DT_DEV_PIPE_SYNCH;
550 dev->preview2_pipe->changed |= DT_DEV_PIPE_SYNCH;
551 }
552 dev->pipe->changed |= DT_DEV_PIPE_SYNCH;
553 }
554
555 dt_dev_zoom_t zoom;
556 float zoom_x = 0.0f, zoom_y = 0.0f, scale = 0.0f;
557 int window_width, window_height, x, y, closeup;
558 dt_dev_pixelpipe_change_t pipe_changed;
559
560 // adjust pipeline according to changed flag set by {add,pop}_history_item.
561 restart:
562 if(dev->gui_leaving)
563 {
564 dt_mipmap_cache_release(darktable.mipmap_cache, &buf);
565 dt_control_log_busy_leave();
566 dt_control_toast_busy_leave();
567 dev->image_status = DT_DEV_PIXELPIPE_INVALID;
568 dt_pthread_mutex_unlock(&dev->pipe_mutex);
569 return;
570 }
571 dev->pipe->input_timestamp = dev->timestamp;
572 // dt_dev_pixelpipe_change() will clear the changed value
573 pipe_changed = dev->pipe->changed;
574 // this locks dev->history_mutex.
575 dt_dev_pixelpipe_change(dev->pipe, dev);
576 // determine scale according to new dimensions
577 zoom = dt_control_get_dev_zoom();
578 closeup = dt_control_get_dev_closeup();
579 zoom_x = dt_control_get_dev_zoom_x();
580 zoom_y = dt_control_get_dev_zoom_y();
581 // if just changed to an image with a different aspect ratio or
582 // altered image orientation, the prior zoom xy could now be beyond
583 // the image boundary
584 if(dev->image_loading || (pipe_changed != DT_DEV_PIPE_UNCHANGED))
585 {
586 dt_dev_check_zoom_bounds(dev, &zoom_x, &zoom_y, zoom, closeup, NULL, NULL);
587 dt_control_set_dev_zoom_x(zoom_x);
588 dt_control_set_dev_zoom_y(zoom_y);
589 }
590
591 scale = dt_dev_get_zoom_scale(dev, zoom, 1.0f, 0) * darktable.gui->ppd;
592 window_width = dev->width * darktable.gui->ppd;
593 window_height = dev->height * darktable.gui->ppd;
594 if(closeup)
595 {
596 window_width /= 1<<closeup;
597 window_height /= 1<<closeup;
598 }
599 const int wd = MIN(window_width, dev->pipe->processed_width * scale);
600 const int ht = MIN(window_height, dev->pipe->processed_height * scale);
601 x = MAX(0, scale * dev->pipe->processed_width * (.5 + zoom_x) - wd / 2);
602 y = MAX(0, scale * dev->pipe->processed_height * (.5 + zoom_y) - ht / 2);
603
604 dt_get_times(&start);
605 if(dt_dev_pixelpipe_process(dev->pipe, dev, x, y, wd, ht, scale))
606 {
607 // interrupted because image changed?
608 if(dev->image_force_reload)
609 {
610 dt_mipmap_cache_release(darktable.mipmap_cache, &buf);
611 dt_control_log_busy_leave();
612 dt_control_toast_busy_leave();
613 dev->image_status = DT_DEV_PIXELPIPE_INVALID;
614 dt_pthread_mutex_unlock(&dev->pipe_mutex);
615 return;
616 }
617 // or because the pipeline changed?
618 else
619 goto restart;
620 }
621 dt_show_times(&start, "[dev_process_image] pixel pipeline processing");
622 dt_dev_average_delay_update(&start, &dev->average_delay);
623
624 // maybe we got zoomed/panned in the meantime?
625 if(dev->pipe->changed != DT_DEV_PIPE_UNCHANGED) goto restart;
626
627 // cool, we got a new image!
628 dev->pipe->backbuf_scale = scale;
629 dev->pipe->backbuf_zoom_x = zoom_x;
630 dev->pipe->backbuf_zoom_y = zoom_y;
631
632 dev->image_status = DT_DEV_PIXELPIPE_VALID;
633 dev->image_loading = FALSE;
634 dev->image_invalid_cnt = 0;
635 dt_mipmap_cache_release(darktable.mipmap_cache, &buf);
636 // if a widget needs to be redraw there's the DT_SIGNAL_*_PIPE_FINISHED signals
637 dt_control_log_busy_leave();
638 dt_control_toast_busy_leave();
639 dt_pthread_mutex_unlock(&dev->pipe_mutex);
640
641 if(dev->gui_attached && !dev->gui_leaving)
642 DT_DEBUG_CONTROL_SIGNAL_RAISE(darktable.signals, DT_SIGNAL_DEVELOP_UI_PIPE_FINISHED);
643 }
644
645
_dt_dev_load_pipeline_defaults(dt_develop_t * dev)646 static inline void _dt_dev_load_pipeline_defaults(dt_develop_t *dev)
647 {
648 for(const GList *modules = g_list_last(dev->iop); modules; modules = g_list_previous(modules))
649 {
650 dt_iop_module_t *module = (dt_iop_module_t *)(modules->data);
651 dt_iop_reload_defaults(module);
652 }
653 }
654
655 // load the raw and get the new image struct, blocking in gui thread
_dt_dev_load_raw(dt_develop_t * dev,const uint32_t imgid)656 static inline void _dt_dev_load_raw(dt_develop_t *dev, const uint32_t imgid)
657 {
658 // first load the raw, to make sure dt_image_t will contain all and correct data.
659 dt_mipmap_buffer_t buf;
660 dt_times_t start;
661 dt_get_times(&start);
662 dt_mipmap_cache_get(darktable.mipmap_cache, &buf, imgid, DT_MIPMAP_FULL, DT_MIPMAP_BLOCKING, 'r');
663 dt_mipmap_cache_release(darktable.mipmap_cache, &buf);
664 dt_show_times_f(&start, "[dev]", "to load the image.");
665
666 const dt_image_t *image = dt_image_cache_get(darktable.image_cache, imgid, 'r');
667 dev->image_storage = *image;
668 dt_image_cache_read_release(darktable.image_cache, image);
669 }
670
dt_dev_reload_image(dt_develop_t * dev,const uint32_t imgid)671 void dt_dev_reload_image(dt_develop_t *dev, const uint32_t imgid)
672 {
673 _dt_dev_load_raw(dev, imgid);
674 dev->image_force_reload = dev->image_loading = dev->preview_loading = dev->preview2_loading = TRUE;
675
676 dev->pipe->changed |= DT_DEV_PIPE_SYNCH;
677 dt_dev_invalidate(dev); // only invalidate image, preview will follow once it's loaded.
678 }
679
dt_dev_get_zoom_scale(dt_develop_t * dev,dt_dev_zoom_t zoom,int closeup_factor,int preview)680 float dt_dev_get_zoom_scale(dt_develop_t *dev, dt_dev_zoom_t zoom, int closeup_factor, int preview)
681 {
682 float zoom_scale;
683
684 const float w = preview ? dev->preview_pipe->processed_width : dev->pipe->processed_width;
685 const float h = preview ? dev->preview_pipe->processed_height : dev->pipe->processed_height;
686 const float ps = dev->pipe->backbuf_width
687 ? dev->pipe->processed_width / (float)dev->preview_pipe->processed_width
688 : dev->preview_pipe->iscale;
689
690 switch(zoom)
691 {
692 case DT_ZOOM_FIT:
693 zoom_scale = fminf(dev->width / w, dev->height / h);
694 break;
695 case DT_ZOOM_FILL:
696 zoom_scale = fmaxf(dev->width / w, dev->height / h);
697 break;
698 case DT_ZOOM_1:
699 zoom_scale = closeup_factor;
700 if(preview) zoom_scale *= ps;
701 break;
702 default: // DT_ZOOM_FREE
703 zoom_scale = dt_control_get_dev_zoom_scale();
704 if(preview) zoom_scale *= ps;
705 break;
706 }
707 if (preview) zoom_scale /= dev->preview_downsampling;
708
709 return zoom_scale;
710 }
711
dt_dev_load_image(dt_develop_t * dev,const uint32_t imgid)712 void dt_dev_load_image(dt_develop_t *dev, const uint32_t imgid)
713 {
714 dt_lock_image(imgid);
715
716 _dt_dev_load_raw(dev, imgid);
717
718 if(dev->pipe)
719 {
720 dev->pipe->processed_width = 0;
721 dev->pipe->processed_height = 0;
722 }
723 dev->image_loading = dev->first_load = dev->preview_loading = dev->preview2_loading = TRUE;
724
725 dev->image_status = dev->preview_status = dev->preview2_status = DT_DEV_PIXELPIPE_DIRTY;
726
727 // we need a global lock as the dev->iop set must not be changed until read history is terminated
728 dt_pthread_mutex_lock(&darktable.dev_threadsafe);
729 dev->iop = dt_iop_load_modules(dev);
730
731 dt_dev_read_history(dev);
732 dt_pthread_mutex_unlock(&darktable.dev_threadsafe);
733
734 dev->first_load = FALSE;
735
736 // Loading an image means we do some developing and so remove the darktable|problem|history-compress tag
737 dt_history_set_compress_problem(imgid, FALSE);
738
739 dt_unlock_image(imgid);
740 }
741
dt_dev_configure(dt_develop_t * dev,int wd,int ht)742 void dt_dev_configure(dt_develop_t *dev, int wd, int ht)
743 {
744 // fixed border on every side
745 const int32_t tb = dev->border_size;
746 wd -= 2*tb;
747 ht -= 2*tb;
748 if(dev->width != wd || dev->height != ht)
749 {
750 dev->width = wd;
751 dev->height = ht;
752 dev->preview_pipe->changed |= DT_DEV_PIPE_ZOOMED;
753 dev->preview2_pipe->changed |= DT_DEV_PIPE_ZOOMED;
754 dev->pipe->changed |= DT_DEV_PIPE_ZOOMED;
755 dt_dev_invalidate(dev);
756 }
757 }
758
759 // helper used to synch a single history item with db
dt_dev_write_history_item(const int imgid,dt_dev_history_item_t * h,int32_t num)760 int dt_dev_write_history_item(const int imgid, dt_dev_history_item_t *h, int32_t num)
761 {
762 sqlite3_stmt *stmt;
763 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
764 "SELECT num FROM main.history WHERE imgid = ?1 AND num = ?2", -1, &stmt, NULL);
765 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
766 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, num);
767 if(sqlite3_step(stmt) != SQLITE_ROW)
768 {
769 sqlite3_finalize(stmt);
770 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
771 "INSERT INTO main.history (imgid, num) VALUES (?1, ?2)", -1, &stmt, NULL);
772 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
773 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, num);
774 sqlite3_step(stmt);
775 }
776 // printf("[dev write history item] writing %d - %s params %f %f\n", h->module->instance, h->module->op,
777 // *(float *)h->params, *(((float *)h->params)+1));
778 sqlite3_finalize(stmt);
779 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
780 "UPDATE main.history"
781 " SET operation = ?1, op_params = ?2, module = ?3, enabled = ?4, "
782 " blendop_params = ?7, blendop_version = ?8, multi_priority = ?9, multi_name = ?10"
783 " WHERE imgid = ?5 AND num = ?6",
784 -1, &stmt, NULL);
785 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, h->module->op, -1, SQLITE_TRANSIENT);
786 DT_DEBUG_SQLITE3_BIND_BLOB(stmt, 2, h->params, h->module->params_size, SQLITE_TRANSIENT);
787 DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, h->module->version());
788 DT_DEBUG_SQLITE3_BIND_INT(stmt, 4, h->enabled);
789 DT_DEBUG_SQLITE3_BIND_INT(stmt, 5, imgid);
790 DT_DEBUG_SQLITE3_BIND_INT(stmt, 6, num);
791 DT_DEBUG_SQLITE3_BIND_BLOB(stmt, 7, h->blend_params, sizeof(dt_develop_blend_params_t), SQLITE_TRANSIENT);
792 DT_DEBUG_SQLITE3_BIND_INT(stmt, 8, dt_develop_blend_version());
793 DT_DEBUG_SQLITE3_BIND_INT(stmt, 9, h->multi_priority);
794 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 10, h->multi_name, -1, SQLITE_TRANSIENT);
795
796 sqlite3_step(stmt);
797 sqlite3_finalize(stmt);
798
799 // write masks (if any)
800 for(GList *forms = h->forms; forms; forms = g_list_next(forms))
801 {
802 dt_masks_form_t *form = (dt_masks_form_t *)forms->data;
803 if (form)
804 dt_masks_write_masks_history_item(imgid, num, form);
805 }
806
807 return 0;
808 }
809
_dev_add_history_item_ext(dt_develop_t * dev,dt_iop_module_t * module,gboolean enable,gboolean new_item,gboolean no_image,gboolean include_masks)810 static void _dev_add_history_item_ext(dt_develop_t *dev, dt_iop_module_t *module, gboolean enable,
811 gboolean new_item, gboolean no_image, gboolean include_masks)
812 {
813 int kept_module = 0;
814 GList *history = g_list_nth(dev->history, dev->history_end);
815 // look for leaks on top of history in two steps
816 // first remove obsolete items above history_end
817 // but keep the always-on modules
818 while(history)
819 {
820 GList *next = g_list_next(history);
821 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
822 // printf("removing obsoleted history item: %s\n", hist->module->op);
823 if(!hist->module->hide_enable_button && !hist->module->default_enabled)
824 {
825 dt_dev_free_history_item(hist);
826 dev->history = g_list_delete_link(dev->history, history);
827 }
828 else
829 kept_module++;
830 history = next;
831 }
832 // then remove NIL items there
833 while ((dev->history_end>0) && (! g_list_nth(dev->history, dev->history_end - 1)))
834 dev->history_end--;
835
836 dev->history_end += kept_module;
837
838 history = g_list_nth(dev->history, dev->history_end - 1);
839 dt_dev_history_item_t *hist = history ? (dt_dev_history_item_t *)(history->data) : 0;
840 if(!history // no history yet, push new item
841 || new_item // a new item is requested
842 || module != hist->module
843 || module->instance != hist->module->instance // add new item for different op
844 || module->multi_priority != hist->module->multi_priority // or instance
845 || ((dev->focus_hash != hist->focus_hash) // or if focused out and in
846 // but only add item if there is a difference at all for the same module
847 && ((module->params_size != hist->module->params_size)
848 || include_masks
849 || (module->params_size == hist->module->params_size
850 && memcmp(hist->params, module->params, module->params_size)))))
851 {
852 // new operation, push new item
853 // printf("adding new history item %d - %s\n", dev->history_end, module->op);
854 // if(history) printf("because item %d - %s is different operation.\n", dev->history_end-1,
855 // ((dt_dev_history_item_t *)history->data)->module->op);
856 dev->history_end++;
857
858 hist = (dt_dev_history_item_t *)calloc(1, sizeof(dt_dev_history_item_t));
859 if(enable)
860 {
861 module->enabled = TRUE;
862 if(!no_image)
863 {
864 if(module->off)
865 {
866 ++darktable.gui->reset;
867 dt_iop_gui_set_enable_button(module);
868 --darktable.gui->reset;
869 }
870 }
871 }
872 g_strlcpy(hist->op_name, module->op, sizeof(hist->op_name));
873 hist->focus_hash = dev->focus_hash;
874 hist->enabled = module->enabled;
875 hist->module = module;
876 hist->params = malloc(module->params_size);
877 hist->iop_order = module->iop_order;
878 hist->multi_priority = module->multi_priority;
879 g_strlcpy(hist->multi_name, module->multi_name, sizeof(hist->multi_name));
880 /* allocate and set hist blend_params */
881 hist->blend_params = malloc(sizeof(dt_develop_blend_params_t));
882 memcpy(hist->params, module->params, module->params_size);
883 memcpy(hist->blend_params, module->blend_params, sizeof(dt_develop_blend_params_t));
884 if(include_masks)
885 hist->forms = dt_masks_dup_forms_deep(dev->forms, NULL);
886 else
887 hist->forms = NULL;
888
889 dev->history = g_list_append(dev->history, hist);
890 if(!no_image)
891 {
892 dev->pipe->changed |= DT_DEV_PIPE_SYNCH;
893 dev->preview_pipe->changed |= DT_DEV_PIPE_SYNCH; // topology remains, as modules are fixed for now.
894 dev->preview2_pipe->changed |= DT_DEV_PIPE_SYNCH; // topology remains, as modules are fixed for now.
895 }
896 }
897 else
898 {
899 // same operation, change params
900 // printf("changing same history item %d - %s\n", dev->history_end-1, module->op);
901 hist = (dt_dev_history_item_t *)history->data;
902 memcpy(hist->params, module->params, module->params_size);
903
904 if(module->flags() & IOP_FLAGS_SUPPORTS_BLENDING)
905 memcpy(hist->blend_params, module->blend_params, sizeof(dt_develop_blend_params_t));
906
907 // if the user changed stuff and the module is still not enabled, do it:
908 if(!hist->enabled && !module->enabled)
909 {
910 module->enabled = 1;
911 if(!no_image)
912 {
913 if(module->off)
914 {
915 ++darktable.gui->reset;
916 dt_iop_gui_set_enable_button(module);
917 --darktable.gui->reset;
918 }
919 }
920 }
921 hist->iop_order = module->iop_order;
922 hist->multi_priority = module->multi_priority;
923 memcpy(hist->multi_name, module->multi_name, sizeof(module->multi_name));
924 hist->enabled = module->enabled;
925
926 if(include_masks)
927 {
928 g_list_free_full(hist->forms, (void (*)(void *))dt_masks_free_form);
929 hist->forms = dt_masks_dup_forms_deep(dev->forms, NULL);
930 }
931 if(!no_image)
932 {
933 dev->pipe->changed |= DT_DEV_PIPE_TOP_CHANGED;
934 dev->preview_pipe->changed |= DT_DEV_PIPE_TOP_CHANGED;
935 dev->preview2_pipe->changed |= DT_DEV_PIPE_TOP_CHANGED;
936 }
937 }
938 }
939
dt_dev_add_history_item_ext(dt_develop_t * dev,dt_iop_module_t * module,gboolean enable,const int no_image)940 void dt_dev_add_history_item_ext(dt_develop_t *dev, dt_iop_module_t *module, gboolean enable, const int no_image)
941 {
942 _dev_add_history_item_ext(dev, module, enable, FALSE, no_image, FALSE);
943 }
944
_dev_add_history_item(dt_develop_t * dev,dt_iop_module_t * module,gboolean enable,gboolean new_item)945 void _dev_add_history_item(dt_develop_t *dev, dt_iop_module_t *module, gboolean enable, gboolean new_item)
946 {
947 if(!darktable.gui || darktable.gui->reset) return;
948
949 dt_dev_undo_start_record(dev);
950
951 dt_pthread_mutex_lock(&dev->history_mutex);
952
953 if(dev->gui_attached)
954 {
955 _dev_add_history_item_ext(dev, module, enable, new_item, FALSE, FALSE);
956 }
957 #if 0
958 {
959 // debug:
960 printf("remaining %d history items:\n", dev->history_end);
961 int i = 0;
962 for(GList *history = dev->history; history; history = g_list_next(history))
963 {
964 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
965 printf("%d %s\n", i, hist->module->op);
966 i++;
967 }
968 }
969 #endif
970
971 /* attach changed tag reflecting actual change */
972 const int imgid = dev->image_storage.id;
973 guint tagid = 0;
974 dt_tag_new("darktable|changed", &tagid);
975 const gboolean tag_change = dt_tag_attach(tagid, imgid, FALSE, FALSE);
976
977 /* register export timestamp in cache */
978 dt_image_cache_set_change_timestamp(darktable.image_cache, imgid);
979
980 // invalidate buffers and force redraw of darkroom
981 dt_dev_invalidate_all(dev);
982 dt_pthread_mutex_unlock(&dev->history_mutex);
983
984 if(dev->gui_attached)
985 {
986 /* signal that history has changed */
987 dt_dev_undo_end_record(dev);
988
989 if(tag_change) DT_DEBUG_CONTROL_SIGNAL_RAISE(darktable.signals, DT_SIGNAL_TAG_CHANGED);
990
991 /* redraw */
992 dt_control_queue_redraw_center();
993 }
994 }
995
dt_dev_add_history_item(dt_develop_t * dev,dt_iop_module_t * module,gboolean enable)996 void dt_dev_add_history_item(dt_develop_t *dev, dt_iop_module_t *module, gboolean enable)
997 {
998 _dev_add_history_item(dev, module, enable, FALSE);
999 }
1000
dt_dev_add_new_history_item(dt_develop_t * dev,dt_iop_module_t * module,gboolean enable)1001 void dt_dev_add_new_history_item(dt_develop_t *dev, dt_iop_module_t *module, gboolean enable)
1002 {
1003 _dev_add_history_item(dev, module, enable, TRUE);
1004 }
1005
dt_dev_add_masks_history_item_ext(dt_develop_t * dev,dt_iop_module_t * _module,gboolean _enable,gboolean no_image)1006 void dt_dev_add_masks_history_item_ext(dt_develop_t *dev, dt_iop_module_t *_module, gboolean _enable, gboolean no_image)
1007 {
1008 dt_iop_module_t *module = _module;
1009 gboolean enable = _enable;
1010
1011 // no module means that is called from the mask manager, so find the iop
1012 if(module == NULL)
1013 {
1014 for(GList *modules = dev->iop; modules; modules = g_list_next(modules))
1015 {
1016 dt_iop_module_t *mod = (dt_iop_module_t *)(modules->data);
1017 if(strcmp(mod->op, "mask_manager") == 0)
1018 {
1019 module = mod;
1020 break;
1021 }
1022 }
1023 enable = FALSE;
1024 }
1025 if(module)
1026 {
1027 _dev_add_history_item_ext(dev, module, enable, FALSE, no_image, TRUE);
1028 }
1029 else
1030 fprintf(stderr, "[dt_dev_add_masks_history_item_ext] can't find mask manager module\n");
1031 }
1032
dt_dev_add_masks_history_item(dt_develop_t * dev,dt_iop_module_t * module,gboolean enable)1033 void dt_dev_add_masks_history_item(dt_develop_t *dev, dt_iop_module_t *module, gboolean enable)
1034 {
1035 if(!darktable.gui || darktable.gui->reset) return;
1036
1037 dt_dev_undo_start_record(dev);
1038
1039 dt_pthread_mutex_lock(&dev->history_mutex);
1040
1041 if(dev->gui_attached)
1042 {
1043 dt_dev_add_masks_history_item_ext(dev, module, enable, FALSE);
1044 }
1045
1046 // invalidate buffers and force redraw of darkroom
1047 dt_dev_invalidate_all(dev);
1048 dt_pthread_mutex_unlock(&dev->history_mutex);
1049
1050 if(dev->gui_attached)
1051 {
1052 /* signal that history has changed */
1053 dt_dev_undo_end_record(dev);
1054
1055 /* recreate mask list */
1056 dt_dev_masks_list_change(dev);
1057
1058 /* redraw */
1059 dt_control_queue_redraw_center();
1060 }
1061 }
1062
dt_dev_free_history_item(gpointer data)1063 void dt_dev_free_history_item(gpointer data)
1064 {
1065 dt_dev_history_item_t *item = (dt_dev_history_item_t *)data;
1066 free(item->params);
1067 free(item->blend_params);
1068 g_list_free_full(item->forms, (void (*)(void *))dt_masks_free_form);
1069 free(item);
1070 }
1071
dt_dev_reload_history_items(dt_develop_t * dev)1072 void dt_dev_reload_history_items(dt_develop_t *dev)
1073 {
1074 dev->focus_hash = 0;
1075
1076 dt_lock_image(dev->image_storage.id);
1077
1078 dt_ioppr_set_default_iop_order(dev, dev->image_storage.id);
1079 dt_dev_pop_history_items(dev, 0);
1080
1081 // remove unused history items:
1082 GList *history = g_list_nth(dev->history, dev->history_end);
1083 while(history)
1084 {
1085 GList *next = g_list_next(history);
1086 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
1087 dt_dev_free_history_item(hist);
1088 dev->history = g_list_delete_link(dev->history, history);
1089 history = next;
1090 }
1091 dt_dev_read_history(dev);
1092
1093 // we have to add new module instances first
1094 for(GList *modules = dev->iop; modules; modules = g_list_next(modules))
1095 {
1096 dt_iop_module_t *module = (dt_iop_module_t *)(modules->data);
1097 if(module->multi_priority > 0)
1098 {
1099 if(!dt_iop_is_hidden(module) && !module->expander)
1100 {
1101 dt_iop_gui_init(module);
1102
1103 /* add module to right panel */
1104 dt_iop_gui_set_expander(module);
1105 dt_iop_gui_set_expanded(module, TRUE, FALSE);
1106
1107 dt_iop_reload_defaults(module);
1108 dt_iop_gui_update_blending(module);
1109
1110 // the pipe need to be reconstruct
1111 dev->pipe->changed |= DT_DEV_PIPE_REMOVE;
1112 dev->preview_pipe->changed |= DT_DEV_PIPE_REMOVE;
1113 dev->preview2_pipe->changed |= DT_DEV_PIPE_REMOVE;
1114 }
1115 }
1116 else if(!dt_iop_is_hidden(module) && module->expander)
1117 {
1118 // we have to ensure that the name of the widget is correct
1119 GtkWidget *child = dt_gui_container_first_child(GTK_CONTAINER(module->expander));
1120 GtkWidget *header = gtk_bin_get_child(GTK_BIN(child));
1121
1122 GtkWidget *wlabel = dt_gui_container_nth_child(GTK_CONTAINER(header), IOP_MODULE_LABEL);
1123 gchar *label = dt_history_item_get_name_html(module);
1124 gtk_label_set_markup(GTK_LABEL(wlabel), label);
1125 g_free(label);
1126 }
1127 }
1128
1129 dt_dev_pop_history_items(dev, dev->history_end);
1130
1131 dt_ioppr_resync_iop_list(dev);
1132
1133 // set the module list order
1134 dt_dev_reorder_gui_module_list(dev);
1135
1136 // we update show params for multi-instances for each other instances
1137 dt_dev_modules_update_multishow(dev);
1138
1139 dt_unlock_image(dev->image_storage.id);
1140 }
1141
dt_dev_pop_history_items_ext(dt_develop_t * dev,int32_t cnt)1142 void dt_dev_pop_history_items_ext(dt_develop_t *dev, int32_t cnt)
1143 {
1144 dt_ioppr_check_iop_order(dev, 0, "dt_dev_pop_history_items_ext begin");
1145 const int end_prev = dev->history_end;
1146 dev->history_end = cnt;
1147
1148 // reset gui params for all modules
1149 for(GList *modules = dev->iop; modules; modules = g_list_next(modules))
1150 {
1151 dt_iop_module_t *module = (dt_iop_module_t *)(modules->data);
1152 memcpy(module->params, module->default_params, module->params_size);
1153 dt_iop_commit_blend_params(module, module->default_blendop_params);
1154 module->enabled = module->default_enabled;
1155
1156 if(module->multi_priority == 0)
1157 module->iop_order = dt_ioppr_get_iop_order(dev->iop_order_list, module->op, module->multi_priority);
1158 else
1159 {
1160 module->iop_order = INT_MAX;
1161 }
1162 }
1163
1164 // go through history and set gui params
1165 GList *forms = NULL;
1166 GList *history = dev->history;
1167 for(int i = 0; i < cnt && history; i++)
1168 {
1169 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
1170 memcpy(hist->module->params, hist->params, hist->module->params_size);
1171 dt_iop_commit_blend_params(hist->module, hist->blend_params);
1172
1173 hist->module->iop_order = hist->iop_order;
1174 hist->module->enabled = hist->enabled;
1175 g_strlcpy(hist->module->multi_name, hist->multi_name, sizeof(hist->module->multi_name));
1176 if(hist->forms) forms = hist->forms;
1177
1178 history = g_list_next(history);
1179 }
1180
1181 dt_ioppr_resync_modules_order(dev);
1182
1183 dt_ioppr_check_duplicate_iop_order(&dev->iop, dev->history);
1184
1185 dt_ioppr_check_iop_order(dev, 0, "dt_dev_pop_history_items_ext end");
1186
1187 // check if masks have changed
1188 int masks_changed = 0;
1189 if(cnt < end_prev)
1190 history = g_list_nth(dev->history, cnt);
1191 else if(cnt > end_prev)
1192 history = g_list_nth(dev->history, end_prev);
1193 else
1194 history = NULL;
1195 for(int i = MIN(cnt, end_prev); i < MAX(cnt, end_prev) && history && !masks_changed; i++)
1196 {
1197 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
1198
1199 if(hist->forms != NULL)
1200 masks_changed = 1;
1201
1202 history = g_list_next(history);
1203 }
1204 if(masks_changed)
1205 dt_masks_replace_current_forms(dev, forms);
1206 }
1207
dt_dev_pop_history_items(dt_develop_t * dev,int32_t cnt)1208 void dt_dev_pop_history_items(dt_develop_t *dev, int32_t cnt)
1209 {
1210 dt_pthread_mutex_lock(&dev->history_mutex);
1211 ++darktable.gui->reset;
1212 GList *dev_iop = g_list_copy(dev->iop);
1213
1214 dt_dev_pop_history_items_ext(dev, cnt);
1215
1216 darktable.develop->history_updating = TRUE;
1217
1218 // update all gui modules
1219 GList *modules = dev->iop;
1220 while(modules)
1221 {
1222 dt_iop_module_t *module = (dt_iop_module_t *)(modules->data);
1223 dt_iop_gui_update(module);
1224 modules = g_list_next(modules);
1225 }
1226
1227 darktable.develop->history_updating = FALSE;
1228
1229 // check if the order of modules has changed
1230 int dev_iop_changed = (g_list_length(dev_iop) != g_list_length(dev->iop));
1231 if(!dev_iop_changed)
1232 {
1233 modules = dev->iop;
1234 GList *modules_old = dev_iop;
1235 while(modules && modules_old)
1236 {
1237 dt_iop_module_t *module = (dt_iop_module_t *)(modules->data);
1238 dt_iop_module_t *module_old = (dt_iop_module_t *)(modules_old->data);
1239
1240 if(module->iop_order != module_old->iop_order)
1241 {
1242 dev_iop_changed = 1;
1243 break;
1244 }
1245
1246 modules = g_list_next(modules);
1247 modules_old = g_list_next(modules_old);
1248 }
1249 }
1250 g_list_free(dev_iop);
1251
1252 if(!dev_iop_changed)
1253 {
1254 dev->pipe->changed |= DT_DEV_PIPE_SYNCH;
1255 dev->preview_pipe->changed |= DT_DEV_PIPE_SYNCH; // again, fixed topology for now.
1256 dev->preview2_pipe->changed |= DT_DEV_PIPE_SYNCH; // again, fixed topology for now.
1257 }
1258 else
1259 {
1260 dev->pipe->changed |= DT_DEV_PIPE_REMOVE;
1261 dev->preview_pipe->changed |= DT_DEV_PIPE_REMOVE;
1262 dev->preview2_pipe->changed |= DT_DEV_PIPE_REMOVE;
1263 dev->pipe->cache_obsolete = 1;
1264 dev->preview_pipe->cache_obsolete = 1;
1265 dev->preview2_pipe->cache_obsolete = 1;
1266 }
1267
1268 --darktable.gui->reset;
1269 dt_dev_invalidate_all(dev);
1270 dt_pthread_mutex_unlock(&dev->history_mutex);
1271
1272 dt_dev_masks_list_change(dev);
1273
1274 dt_control_queue_redraw_center();
1275 }
1276
_cleanup_history(const int imgid)1277 static void _cleanup_history(const int imgid)
1278 {
1279 sqlite3_stmt *stmt;
1280 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.history WHERE imgid = ?1", -1,
1281 &stmt, NULL);
1282 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
1283 sqlite3_step(stmt);
1284 sqlite3_finalize(stmt);
1285
1286 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.masks_history WHERE imgid = ?1", -1,
1287 &stmt, NULL);
1288 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
1289 sqlite3_step(stmt);
1290 sqlite3_finalize(stmt);
1291 }
1292
dt_dev_write_history_ext(dt_develop_t * dev,const int imgid)1293 void dt_dev_write_history_ext(dt_develop_t *dev, const int imgid)
1294 {
1295 sqlite3_stmt *stmt;
1296 dt_lock_image(imgid);
1297
1298 _cleanup_history(imgid);
1299
1300 // write history entries
1301
1302 GList *history = dev->history;
1303 if (DT_IOP_ORDER_INFO)
1304 fprintf(stderr,"\n^^^^ Writing history image: %i, iop version: %i",imgid,dev->iop_order_version);
1305 for(int i = 0; history; i++)
1306 {
1307 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
1308 (void)dt_dev_write_history_item(imgid, hist, i);
1309 if (DT_IOP_ORDER_INFO)
1310 {
1311 fprintf(stderr,"\n%20s, num %i, order %d, v(%i), multiprio %i",
1312 hist->module->op,i,hist->iop_order,hist->module->version(),hist->multi_priority);
1313 if (hist->enabled) fprintf(stderr,", enabled");
1314 }
1315 history = g_list_next(history);
1316 }
1317 if (DT_IOP_ORDER_INFO)
1318 fprintf(stderr,"\nvvvv\n");
1319
1320 // update history end
1321 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
1322 "UPDATE main.images SET history_end = ?1 WHERE id = ?2", -1,
1323 &stmt, NULL);
1324 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, dev->history_end);
1325 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
1326 sqlite3_step(stmt);
1327 sqlite3_finalize(stmt);
1328
1329 // write the current iop-order-list for this image
1330
1331 dt_ioppr_write_iop_order_list(dev->iop_order_list, imgid);
1332 dt_history_hash_write_from_history(imgid, DT_HISTORY_HASH_CURRENT);
1333
1334 dt_unlock_image(imgid);
1335 }
1336
dt_dev_write_history(dt_develop_t * dev)1337 void dt_dev_write_history(dt_develop_t *dev)
1338 {
1339 dt_dev_write_history_ext(dev, dev->image_storage.id);
1340 }
1341
_dev_get_module_nb_records()1342 static int _dev_get_module_nb_records()
1343 {
1344 sqlite3_stmt *stmt;
1345 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
1346 "SELECT count (*) FROM memory.history",
1347 -1, &stmt, NULL);
1348 sqlite3_step(stmt);
1349 const int cnt = sqlite3_column_int(stmt, 0);
1350 sqlite3_finalize(stmt);
1351 return cnt;
1352 }
1353
_dev_insert_module(dt_develop_t * dev,dt_iop_module_t * module,const int imgid)1354 void _dev_insert_module(dt_develop_t *dev, dt_iop_module_t *module, const int imgid)
1355 {
1356 sqlite3_stmt *stmt;
1357
1358 DT_DEBUG_SQLITE3_PREPARE_V2(
1359 dt_database_get(darktable.db),
1360 "INSERT INTO memory.history VALUES (?1, 0, ?2, ?3, ?4, 1, NULL, 0, 0, '')",
1361 -1, &stmt, NULL);
1362 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
1363 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, module->version());
1364 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 3, module->op, -1, SQLITE_TRANSIENT);
1365 DT_DEBUG_SQLITE3_BIND_BLOB(stmt, 4, module->default_params, module->params_size, SQLITE_TRANSIENT);
1366 sqlite3_step(stmt);
1367 sqlite3_finalize(stmt);
1368
1369 dt_print(DT_DEBUG_PARAMS, "[history] module %s inserted to history\n", module->op);
1370 }
1371
_dev_auto_apply_presets(dt_develop_t * dev)1372 static gboolean _dev_auto_apply_presets(dt_develop_t *dev)
1373 {
1374 // NOTE: the presets/default iops will be *prepended* into the history.
1375
1376 const int imgid = dev->image_storage.id;
1377
1378 if(imgid <= 0) return FALSE;
1379
1380 gboolean run = FALSE;
1381 dt_image_t *image = dt_image_cache_get(darktable.image_cache, imgid, 'w');
1382 if(!(image->flags & DT_IMAGE_AUTO_PRESETS_APPLIED)) run = TRUE;
1383
1384 const gboolean is_raw = dt_image_is_raw(image);
1385 const gboolean is_modern_chroma =
1386 dt_conf_is_equal("plugins/darkroom/chromatic-adaptation", "modern");
1387
1388 // flag was already set? only apply presets once in the lifetime of a history stack.
1389 // (the flag will be cleared when removing it).
1390 if(!run || image->id <= 0)
1391 {
1392 // Next section is to recover old edits where all modules with default parameters were not
1393 // recorded in the db nor in the .XMP.
1394 //
1395 // One crutial point is the white-balance which has automatic default based on the camera
1396 // and depends on the chroma-adaptation. In modern mode the default won't be the same used
1397 // in legacy mode and if the white-balance is not found on the history one will be added by
1398 // default using current defaults. But if we are in modern chromatic adaptation the default
1399 // will not be equivalent to the one used to develop this old edit.
1400
1401 // So if the current mode is the modern chromatic-adaptation, do check the history.
1402
1403 if(is_modern_chroma && is_raw)
1404 {
1405 // loop over all modules and display a message for default-enabled modules that
1406 // are not found on the history.
1407
1408 for(GList *modules = dev->iop; modules; modules = g_list_next(modules))
1409 {
1410 dt_iop_module_t *module = (dt_iop_module_t *)modules->data;
1411
1412 if(module->default_enabled
1413 && !(module->flags() & IOP_FLAGS_NO_HISTORY_STACK)
1414 && !dt_history_check_module_exists(imgid, module->op))
1415 {
1416 fprintf(stderr,
1417 "[_dev_auto_apply_presets] missing mandatory module %s for image %d\n",
1418 module->op, imgid);
1419
1420 // If the module is white-balance and we are dealing with a raw file we need to add
1421 // one now with the default legacy parameters. And we want to do this only for
1422 // old edits.
1423 //
1424 // For new edits the temperature will be added back depending on the chromatic
1425 // adaptation the standard way.
1426
1427 if(!strcmp(module->op, "temperature")
1428 && (image->change_timestamp == -1))
1429 {
1430 // it is important to recover temperature in this case (modern chroma and
1431 // not module present as we need to have the pre 3.0 default parameters used.
1432
1433 dt_conf_set_string("plugins/darkroom/chromatic-adaptation", "legacy");
1434 dt_iop_reload_defaults(module);
1435 _dev_insert_module(dev, module, imgid);
1436 dt_conf_set_string("plugins/darkroom/chromatic-adaptation", "modern");
1437 dt_iop_reload_defaults(module);
1438 }
1439 }
1440 }
1441 }
1442
1443 dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_RELAXED);
1444 return FALSE;
1445 }
1446
1447 gchar *workflow = dt_conf_get_string("plugins/darkroom/workflow");
1448 const gboolean is_scene_referred = strcmp(workflow, "scene-referred") == 0;
1449 const gboolean is_display_referred = strcmp(workflow, "display-referred") == 0;
1450 const gboolean is_workflow_none = strcmp(workflow, "none") == 0;
1451 g_free(workflow);
1452
1453 // Add scene-referred workflow
1454 // Note that we cannot use a preset for FilmicRGB as the default values are
1455 // dynamically computed depending on the actual exposure compensation
1456 // (see reload_default routine in filmicrgb.c)
1457
1458 const gboolean has_matrix = dt_image_is_matrix_correction_supported(image);
1459
1460 const gboolean auto_apply_filmic = is_raw && is_scene_referred;
1461 const gboolean auto_apply_cat = has_matrix && is_modern_chroma;
1462 const gboolean auto_apply_sharpen = dt_conf_get_bool("plugins/darkroom/sharpen/auto_apply");
1463
1464 if(auto_apply_filmic || auto_apply_sharpen || auto_apply_cat)
1465 {
1466 for(GList *modules = dev->iop; modules; modules = g_list_next(modules))
1467 {
1468 dt_iop_module_t *module = (dt_iop_module_t *)modules->data;
1469
1470 if(((auto_apply_filmic && strcmp(module->op, "filmicrgb") == 0)
1471 || (auto_apply_sharpen && strcmp(module->op, "sharpen") == 0)
1472 || (auto_apply_cat && strcmp(module->op, "channelmixerrgb") == 0))
1473 && !dt_history_check_module_exists(imgid, module->op)
1474 && !(module->flags() & IOP_FLAGS_NO_HISTORY_STACK))
1475 {
1476 _dev_insert_module(dev, module, imgid);
1477 }
1478 }
1479 }
1480
1481 // select all presets from one of the following table and add them into memory.history. Note that
1482 // this is appended to possibly already present default modules.
1483 const char *preset_table[2] = { "data.presets", "main.legacy_presets" };
1484 const int legacy = (image->flags & DT_IMAGE_NO_LEGACY_PRESETS) ? 0 : 1;
1485 char query[1024];
1486 snprintf(query, sizeof(query),
1487 "INSERT INTO memory.history"
1488 " SELECT ?1, 0, op_version, operation, op_params,"
1489 " enabled, blendop_params, blendop_version, multi_priority, multi_name"
1490 " FROM %s"
1491 " WHERE ( (autoapply=1"
1492 " AND ((?2 LIKE model AND ?3 LIKE maker) OR (?4 LIKE model AND ?5 LIKE maker))"
1493 " AND ?6 LIKE lens AND ?7 BETWEEN iso_min AND iso_max"
1494 " AND ?8 BETWEEN exposure_min AND exposure_max"
1495 " AND ?9 BETWEEN aperture_min AND aperture_max"
1496 " AND ?10 BETWEEN focal_length_min AND focal_length_max"
1497 " AND (format = 0 OR (format&?11 != 0 AND ~format&?12 != 0)))"
1498 " OR (name = ?13))"
1499 " AND operation NOT IN"
1500 " ('ioporder', 'metadata', 'modulegroups', 'export', 'tagging', 'collect', '%s')"
1501 " ORDER BY writeprotect DESC, LENGTH(model), LENGTH(maker), LENGTH(lens)",
1502 preset_table[legacy],
1503 is_display_referred?"":"basecurve");
1504 // query for all modules at once:
1505 sqlite3_stmt *stmt;
1506 const char *workflow_preset = has_matrix && is_display_referred
1507 ? _("display-referred default")
1508 : (has_matrix && is_scene_referred
1509 ?_("scene-referred default")
1510 :"\t\n");
1511 int iformat = 0;
1512 if(dt_image_is_rawprepare_supported(image)) iformat |= FOR_RAW;
1513 else iformat |= FOR_LDR;
1514 if(dt_image_is_hdr(image)) iformat |= FOR_HDR;
1515
1516 int excluded = 0;
1517 if(dt_image_monochrome_flags(image)) excluded |= FOR_NOT_MONO;
1518 else excluded |= FOR_NOT_COLOR;
1519
1520 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt, NULL);
1521 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
1522 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, image->exif_model, -1, SQLITE_TRANSIENT);
1523 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 3, image->exif_maker, -1, SQLITE_TRANSIENT);
1524 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 4, image->camera_alias, -1, SQLITE_TRANSIENT);
1525 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 5, image->camera_maker, -1, SQLITE_TRANSIENT);
1526 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 6, image->exif_lens, -1, SQLITE_TRANSIENT);
1527 DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 7, fmaxf(0.0f, fminf(FLT_MAX, image->exif_iso)));
1528 DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 8, fmaxf(0.0f, fminf(1000000, image->exif_exposure)));
1529 DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 9, fmaxf(0.0f, fminf(1000000, image->exif_aperture)));
1530 DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 10, fmaxf(0.0f, fminf(1000000, image->exif_focal_length)));
1531 // 0: dontcare, 1: ldr, 2: raw plus monochrome & color
1532 DT_DEBUG_SQLITE3_BIND_INT(stmt, 11, iformat);
1533 DT_DEBUG_SQLITE3_BIND_INT(stmt, 12, excluded);
1534 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 13, workflow_preset, -1, SQLITE_TRANSIENT);
1535 sqlite3_step(stmt);
1536 sqlite3_finalize(stmt);
1537
1538 // now we want to auto-apply the iop-order list if one corresponds and none are
1539 // still applied. Note that we can already have an iop-order list set when
1540 // copying an history or applying a style to a not yet developed image.
1541
1542 if(!dt_ioppr_has_iop_order_list(imgid))
1543 {
1544 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
1545 "SELECT op_params"
1546 " FROM data.presets"
1547 " WHERE autoapply=1"
1548 " AND ((?2 LIKE model AND ?3 LIKE maker) OR (?4 LIKE model AND ?5 LIKE maker))"
1549 " AND ?6 LIKE lens AND ?7 BETWEEN iso_min AND iso_max"
1550 " AND ?8 BETWEEN exposure_min AND exposure_max"
1551 " AND ?9 BETWEEN aperture_min AND aperture_max"
1552 " AND ?10 BETWEEN focal_length_min AND focal_length_max"
1553 " AND (format = 0 OR (format&?11 != 0 AND ~format&?12 != 0))"
1554 " AND operation = 'ioporder'"
1555 " ORDER BY writeprotect DESC, LENGTH(model), LENGTH(maker), LENGTH(lens)",
1556 -1, &stmt, NULL);
1557 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
1558 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, image->exif_model, -1, SQLITE_TRANSIENT);
1559 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 3, image->exif_maker, -1, SQLITE_TRANSIENT);
1560 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 4, image->camera_alias, -1, SQLITE_TRANSIENT);
1561 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 5, image->camera_maker, -1, SQLITE_TRANSIENT);
1562 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 6, image->exif_lens, -1, SQLITE_TRANSIENT);
1563 DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 7, fmaxf(0.0f, fminf(FLT_MAX, image->exif_iso)));
1564 DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 8, fmaxf(0.0f, fminf(1000000, image->exif_exposure)));
1565 DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 9, fmaxf(0.0f, fminf(1000000, image->exif_aperture)));
1566 DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 10, fmaxf(0.0f, fminf(1000000, image->exif_focal_length)));
1567 // 0: dontcare, 1: ldr, 2: raw plus monochrome & color
1568 DT_DEBUG_SQLITE3_BIND_INT(stmt, 11, iformat);
1569 DT_DEBUG_SQLITE3_BIND_INT(stmt, 12, excluded);
1570 if(sqlite3_step(stmt) == SQLITE_ROW)
1571 {
1572 const char *params = (char *)sqlite3_column_blob(stmt, 0);
1573 const int32_t params_len = sqlite3_column_bytes(stmt, 0);
1574 GList *iop_list = dt_ioppr_deserialize_iop_order_list(params, params_len);
1575 dt_ioppr_write_iop_order_list(iop_list, imgid);
1576 g_list_free_full(iop_list, free);
1577 dt_ioppr_set_default_iop_order(dev, imgid);
1578 }
1579 else
1580 {
1581 // we have no auto-apply order, so apply iop order, depending of the workflow
1582 GList *iop_list;
1583 if(is_scene_referred || is_workflow_none)
1584 iop_list = dt_ioppr_get_iop_order_list_version(DT_IOP_ORDER_V30);
1585 else
1586 iop_list = dt_ioppr_get_iop_order_list_version(DT_IOP_ORDER_LEGACY);
1587 dt_ioppr_write_iop_order_list(iop_list, imgid);
1588 g_list_free_full(iop_list, free);
1589 dt_ioppr_set_default_iop_order(dev, imgid);
1590 }
1591 sqlite3_finalize(stmt);
1592 }
1593
1594 image->flags |= DT_IMAGE_AUTO_PRESETS_APPLIED | DT_IMAGE_NO_LEGACY_PRESETS;
1595
1596 // make sure these end up in the image_cache; as the history is not correct right now
1597 // we don't write the sidecar here but later in dt_dev_read_history_ext
1598 dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_RELAXED);
1599
1600 return TRUE;
1601 }
1602
_dev_add_default_modules(dt_develop_t * dev,const int imgid)1603 static void _dev_add_default_modules(dt_develop_t *dev, const int imgid)
1604 {
1605 //start with those modules that cannot be disabled
1606 for(GList *modules = dev->iop; modules; modules = g_list_next(modules))
1607 {
1608 dt_iop_module_t *module = (dt_iop_module_t *)modules->data;
1609
1610 if(!dt_history_check_module_exists(imgid, module->op)
1611 && module->default_enabled
1612 && module->hide_enable_button
1613 && !(module->flags() & IOP_FLAGS_NO_HISTORY_STACK))
1614 {
1615 _dev_insert_module(dev, module, imgid);
1616 }
1617 }
1618 //now modules that can be disabled but are auto-on
1619 for(GList *modules = dev->iop; modules; modules = g_list_next(modules))
1620 {
1621 dt_iop_module_t *module = (dt_iop_module_t *)modules->data;
1622
1623 if(!dt_history_check_module_exists(imgid, module->op)
1624 && module->default_enabled
1625 && !module->hide_enable_button
1626 && !(module->flags() & IOP_FLAGS_NO_HISTORY_STACK))
1627 {
1628 _dev_insert_module(dev, module, imgid);
1629 }
1630 }
1631 }
1632
_dev_merge_history(dt_develop_t * dev,const int imgid)1633 static void _dev_merge_history(dt_develop_t *dev, const int imgid)
1634 {
1635 sqlite3_stmt *stmt;
1636
1637 // count what we found:
1638 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
1639 "SELECT COUNT(*) FROM memory.history", -1,
1640 &stmt, NULL);
1641 if(sqlite3_step(stmt) == SQLITE_ROW)
1642 {
1643 // if there is anything..
1644 const int cnt = sqlite3_column_int(stmt, 0);
1645 sqlite3_finalize(stmt);
1646
1647 // workaround a sqlite3 "feature". The above statement to insert
1648 // items into memory.history is complex and in this case sqlite
1649 // does not give rowid a linear increment. But the following code
1650 // really expect that the rowid in this table starts from 0 and
1651 // increment one by one. So in the following code we rewrite the
1652 // "num" values from 0 to cnt-1.
1653
1654 if(cnt > 0)
1655 {
1656 // get all rowids
1657 GList *rowids = NULL;
1658
1659 // get the rowids in descending order since building the list will reverse the order
1660 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
1661 "SELECT rowid FROM memory.history ORDER BY rowid DESC",
1662 -1, &stmt, NULL);
1663 while(sqlite3_step(stmt) == SQLITE_ROW)
1664 rowids = g_list_prepend(rowids, GINT_TO_POINTER(sqlite3_column_int(stmt, 0)));
1665 sqlite3_finalize(stmt);
1666
1667 // update num accordingly
1668 int v = 0;
1669
1670 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
1671 "UPDATE memory.history SET num=?1 WHERE rowid=?2",
1672 -1, &stmt, NULL);
1673
1674 // let's wrap this into a transaction, it might make it a little faster.
1675 sqlite3_exec(dt_database_get(darktable.db), "BEGIN TRANSACTION", NULL, NULL, NULL);
1676 for(GList *r = rowids; r; r = g_list_next(r))
1677 {
1678 DT_DEBUG_SQLITE3_CLEAR_BINDINGS(stmt);
1679 DT_DEBUG_SQLITE3_RESET(stmt);
1680 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, v);
1681 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, GPOINTER_TO_INT(r->data));
1682
1683 if(sqlite3_step(stmt) != SQLITE_DONE) break;
1684
1685 v++;
1686 }
1687
1688 sqlite3_exec(dt_database_get(darktable.db), "COMMIT", NULL, NULL, NULL);
1689
1690 g_list_free(rowids);
1691
1692 // advance the current history by cnt amount, that is, make space
1693 // for the preset/default iops that will be *prepended* into the
1694 // history.
1695 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
1696 "UPDATE main.history SET num=num+?1 WHERE imgid=?2",
1697 -1, &stmt, NULL);
1698 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, cnt);
1699 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
1700
1701 if(sqlite3_step(stmt) == SQLITE_DONE)
1702 {
1703 sqlite3_finalize(stmt);
1704 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
1705 "UPDATE main.images"
1706 " SET history_end=history_end+?1"
1707 " WHERE id=?2",
1708 -1, &stmt, NULL);
1709 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, cnt);
1710 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
1711
1712 if(sqlite3_step(stmt) == SQLITE_DONE)
1713 {
1714 // and finally prepend the rest with increasing numbers (starting at 0)
1715 sqlite3_finalize(stmt);
1716 DT_DEBUG_SQLITE3_PREPARE_V2(
1717 dt_database_get(darktable.db),
1718 "INSERT INTO main.history"
1719 " SELECT imgid, num, module, operation, op_params, enabled, "
1720 " blendop_params, blendop_version, multi_priority,"
1721 " multi_name"
1722 " FROM memory.history",
1723 -1, &stmt, NULL);
1724 sqlite3_step(stmt);
1725 sqlite3_finalize(stmt);
1726 }
1727 }
1728 }
1729 }
1730 }
1731
_dev_write_history(dt_develop_t * dev,const int imgid)1732 void _dev_write_history(dt_develop_t *dev, const int imgid)
1733 {
1734 _cleanup_history(imgid);
1735 // write history entries
1736 GList *history = dev->history;
1737 for(int i = 0; history; i++)
1738 {
1739 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
1740 (void)dt_dev_write_history_item(imgid, hist, i);
1741 history = g_list_next(history);
1742 }
1743 }
1744
1745 // helper function for debug strings
_print_validity(gboolean state)1746 char * _print_validity(gboolean state)
1747 {
1748 if(state)
1749 return "ok";
1750 else
1751 return "WRONG";
1752 }
1753
dt_dev_read_history_ext(dt_develop_t * dev,const int imgid,gboolean no_image)1754 void dt_dev_read_history_ext(dt_develop_t *dev, const int imgid, gboolean no_image)
1755 {
1756 if(imgid <= 0) return;
1757 if(!dev->iop) return;
1758
1759 dt_lock_image(imgid);
1760
1761 dt_dev_undo_start_record(dev);
1762
1763 int auto_apply_modules = 0;
1764 gboolean first_run = FALSE;
1765 gboolean legacy_params = FALSE;
1766
1767 dt_ioppr_set_default_iop_order(dev, imgid);
1768
1769 if(!no_image)
1770 {
1771 // cleanup
1772 DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "DELETE FROM memory.history", NULL, NULL, NULL);
1773
1774 dt_print(DT_DEBUG_PARAMS, "[history] temporary history deleted\n");
1775
1776 // make sure all modules default params are loaded to init history
1777 _dt_dev_load_pipeline_defaults(dev);
1778
1779 // prepend all default modules to memory.history
1780 _dev_add_default_modules(dev, imgid);
1781 const int default_modules = _dev_get_module_nb_records();
1782
1783 // maybe add auto-presets to memory.history
1784 first_run = _dev_auto_apply_presets(dev);
1785 auto_apply_modules = _dev_get_module_nb_records() - default_modules;
1786
1787 dt_print(DT_DEBUG_PARAMS, "[history] temporary history initialised with default params and presets\n");
1788
1789 // now merge memory.history into main.history
1790 _dev_merge_history(dev, imgid);
1791
1792 dt_print(DT_DEBUG_PARAMS, "[history] temporary history merged with image history\n");
1793
1794 // first time we are loading the image, try to import lightroom .xmp if any
1795 if(dev->image_loading && first_run) dt_lightroom_import(dev->image_storage.id, dev, TRUE);
1796 }
1797
1798 sqlite3_stmt *stmt;
1799
1800 // Get the end of the history - What's that ???
1801
1802 int history_end_current = 0;
1803
1804 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
1805 "SELECT history_end FROM main.images WHERE id = ?1",
1806 -1, &stmt, NULL);
1807 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
1808 if(sqlite3_step(stmt) == SQLITE_ROW) // seriously, this should never fail
1809 if(sqlite3_column_type(stmt, 0) != SQLITE_NULL)
1810 history_end_current = sqlite3_column_int(stmt, 0);
1811 sqlite3_finalize(stmt);
1812
1813 // Load current image history from DB
1814 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
1815 "SELECT imgid, num, module, operation,"
1816 " op_params, enabled, blendop_params,"
1817 " blendop_version, multi_priority, multi_name"
1818 " FROM main.history"
1819 " WHERE imgid = ?1"
1820 " ORDER BY num",
1821 -1, &stmt, NULL);
1822 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
1823
1824 dev->history_end = 0;
1825
1826 // Strip rows from DB lookup. One row == One module in history
1827 while(sqlite3_step(stmt) == SQLITE_ROW)
1828 {
1829 // Unpack the DB blobs
1830 const int id = sqlite3_column_int(stmt, 0);
1831 const int num = sqlite3_column_int(stmt, 1);
1832 const int modversion = sqlite3_column_int(stmt, 2);
1833 const char *module_name = (const char *)sqlite3_column_text(stmt, 3);
1834 const void *module_params = sqlite3_column_blob(stmt, 4);
1835 const int enabled = sqlite3_column_int(stmt, 5);
1836 const void *blendop_params = sqlite3_column_blob(stmt, 6);
1837 const int blendop_version = sqlite3_column_int(stmt, 7);
1838 const int multi_priority = sqlite3_column_int(stmt, 8);
1839 const char *multi_name = (const char *)sqlite3_column_text(stmt, 9);
1840
1841 const int param_length = sqlite3_column_bytes(stmt, 4);
1842 const int bl_length = sqlite3_column_bytes(stmt, 6);
1843
1844 // Sanity checks
1845 const gboolean is_valid_id = (id == imgid);
1846 const gboolean has_module_name = (module_name != NULL);
1847
1848 if(!(has_module_name && is_valid_id))
1849 {
1850 fprintf(stderr, "[dev_read_history] database history for image `%s' seems to be corrupted!\n",
1851 dev->image_storage.filename);
1852 continue;
1853 }
1854
1855 const int iop_order = dt_ioppr_get_iop_order(dev->iop_order_list, module_name, multi_priority);
1856
1857 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)calloc(1, sizeof(dt_dev_history_item_t));
1858 hist->module = NULL;
1859
1860 // Find a .so file that matches our history entry, aka a module to run the params stored in DB
1861 dt_iop_module_t *find_op = NULL;
1862 for(GList *modules = dev->iop; modules; modules = g_list_next(modules))
1863 {
1864 dt_iop_module_t *module = (dt_iop_module_t *)modules->data;
1865 if(!strcmp(module->op, module_name))
1866 {
1867 if(module->multi_priority == multi_priority)
1868 {
1869 hist->module = module;
1870 if(multi_name)
1871 g_strlcpy(module->multi_name, multi_name, sizeof(module->multi_name));
1872 else
1873 memset(module->multi_name, 0, sizeof(module->multi_name));
1874 break;
1875 }
1876 else if(multi_priority > 0)
1877 {
1878 // we just say that we find the name, so we just have to add new instance of this module
1879 find_op = module;
1880 }
1881 }
1882 }
1883 if(!hist->module && find_op)
1884 {
1885 // we have to add a new instance of this module and set index to modindex
1886 dt_iop_module_t *new_module = (dt_iop_module_t *)calloc(1, sizeof(dt_iop_module_t));
1887 if(!dt_iop_load_module(new_module, find_op->so, dev))
1888 {
1889 dt_iop_update_multi_priority(new_module, multi_priority);
1890 new_module->iop_order = iop_order;
1891
1892 g_strlcpy(new_module->multi_name, multi_name, sizeof(new_module->multi_name));
1893
1894 dev->iop = g_list_append(dev->iop, new_module);
1895
1896 new_module->instance = find_op->instance;
1897 hist->module = new_module;
1898 }
1899 }
1900
1901 if(!hist->module)
1902 {
1903 fprintf(
1904 stderr,
1905 "[dev_read_history] the module `%s' requested by image `%s' is not installed on this computer!\n",
1906 module_name, dev->image_storage.filename);
1907 free(hist);
1908 continue;
1909 }
1910
1911 // module has no user params and won't bother us in GUI - exit early, we are done
1912 if(hist->module->flags() & IOP_FLAGS_NO_HISTORY_STACK)
1913 {
1914 free(hist);
1915 continue;
1916 }
1917
1918 // Run a battery of tests
1919 const gboolean is_valid_module_name = (strcmp(module_name, hist->module->op) == 0);
1920 const gboolean is_valid_blendop_version = (blendop_version == dt_develop_blend_version());
1921 const gboolean is_valid_blendop_size = (bl_length == sizeof(dt_develop_blend_params_t));
1922 const gboolean is_valid_module_version = (modversion == hist->module->version());
1923 const gboolean is_valid_params_size = (param_length == hist->module->params_size);
1924
1925 dt_print(DT_DEBUG_PARAMS, "[history] successfully loaded module %s from history\n"
1926 "\t\t\tblendop v. %i:\tversion %s\tparams %s\n"
1927 "\t\t\tparams v. %i:\tversion %s\tparams %s\n",
1928 module_name,
1929 blendop_version, _print_validity(is_valid_blendop_version), _print_validity(is_valid_blendop_size),
1930 modversion, _print_validity(is_valid_module_version), _print_validity(is_valid_params_size));
1931
1932 // Init buffers and values
1933 hist->enabled = enabled;
1934 hist->num = num;
1935 hist->iop_order = iop_order;
1936 hist->multi_priority = multi_priority;
1937 g_strlcpy(hist->op_name, hist->module->op, sizeof(hist->op_name));
1938 g_strlcpy(hist->multi_name, multi_name, sizeof(hist->multi_name));
1939 hist->params = malloc(hist->module->params_size);
1940 hist->blend_params = malloc(sizeof(dt_develop_blend_params_t));
1941
1942 // update module iop_order only on active history entries
1943 if(history_end_current > dev->history_end) hist->module->iop_order = hist->iop_order;
1944
1945 // Copy blending params if valid, else try to convert legacy params
1946 if(blendop_params && is_valid_blendop_version && is_valid_blendop_size)
1947 {
1948 memcpy(hist->blend_params, blendop_params, sizeof(dt_develop_blend_params_t));
1949 }
1950 else if(blendop_params
1951 && dt_develop_blend_legacy_params(hist->module, blendop_params, blendop_version,
1952 hist->blend_params, dt_develop_blend_version(), bl_length) == 0)
1953 {
1954 legacy_params = TRUE;
1955 }
1956 else
1957 {
1958 memcpy(hist->blend_params, hist->module->default_blendop_params, sizeof(dt_develop_blend_params_t));
1959 }
1960
1961 // Copy module params if valid, else try to convert legacy params
1962 if(is_valid_module_version && is_valid_params_size && is_valid_module_name)
1963 {
1964 memcpy(hist->params, module_params, hist->module->params_size);
1965 }
1966 else
1967 {
1968 if(!hist->module->legacy_params
1969 || hist->module->legacy_params(hist->module, module_params, labs(modversion),
1970 hist->params, labs(hist->module->version())))
1971 {
1972 fprintf(stderr, "[dev_read_history] module `%s' version mismatch: history is %d, dt %d.\n",
1973 hist->module->op, modversion, hist->module->version());
1974
1975 const char *fname = dev->image_storage.filename + strlen(dev->image_storage.filename);
1976 while(fname > dev->image_storage.filename && *fname != '/') fname--;
1977
1978 if(fname > dev->image_storage.filename) fname++;
1979 dt_control_log(_("%s: module `%s' version mismatch: %d != %d"), fname, hist->module->op,
1980 hist->module->version(), modversion);
1981 dt_dev_free_history_item(hist);
1982 continue;
1983 }
1984 else
1985 {
1986 if(!strcmp(hist->module->op, "spots") && modversion == 1)
1987 {
1988 // quick and dirty hack to handle spot removal legacy_params
1989 memcpy(hist->blend_params, hist->module->blend_params, sizeof(dt_develop_blend_params_t));
1990 }
1991 legacy_params = TRUE;
1992 }
1993
1994 /*
1995 * Fix for flip iop: previously it was not always needed, but it might be
1996 * in history stack as "orientation (off)", but now we always want it
1997 * by default, so if it is disabled, enable it, and replace params with
1998 * default_params. if user want to, he can disable it.
1999 */
2000 if(!strcmp(hist->module->op, "flip") && hist->enabled == 0 && labs(modversion) == 1)
2001 {
2002 memcpy(hist->params, hist->module->default_params, hist->module->params_size);
2003 hist->enabled = 1;
2004 }
2005 }
2006
2007 // make sure that always-on modules are always on. duh.
2008 if(hist->module->default_enabled == 1 && hist->module->hide_enable_button == 1)
2009 hist->enabled = 1;
2010
2011 dev->history = g_list_append(dev->history, hist);
2012 dev->history_end++;
2013 }
2014 sqlite3_finalize(stmt);
2015
2016 dt_ioppr_resync_modules_order(dev);
2017
2018 // find the new history end
2019 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
2020 "SELECT history_end FROM main.images WHERE id = ?1",
2021 -1, &stmt, NULL);
2022 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
2023 if(sqlite3_step(stmt) == SQLITE_ROW) // seriously, this should never fail
2024 if(sqlite3_column_type(stmt, 0) != SQLITE_NULL)
2025 dev->history_end = sqlite3_column_int(stmt, 0);
2026 sqlite3_finalize(stmt);
2027
2028 dt_ioppr_check_iop_order(dev, imgid, "dt_dev_read_history_no_image end");
2029
2030 dt_masks_read_masks_history(dev, imgid);
2031
2032 // FIXME : this probably needs to capture dev thread lock
2033 if(dev->gui_attached && !no_image)
2034 {
2035 dev->pipe->changed |= DT_DEV_PIPE_SYNCH;
2036 dev->preview_pipe->changed |= DT_DEV_PIPE_SYNCH; // again, fixed topology for now.
2037 dev->preview2_pipe->changed |= DT_DEV_PIPE_SYNCH; // again, fixed topology for now.
2038 dt_dev_invalidate_all(dev);
2039
2040 /* signal history changed */
2041 dt_dev_undo_end_record(dev);
2042 }
2043 dt_dev_masks_list_change(dev);
2044
2045 // make sure module_dev is in sync with history
2046 _dev_write_history(dev, imgid);
2047 dt_ioppr_write_iop_order_list(dev->iop_order_list, imgid);
2048 dt_history_hash_t flags = DT_HISTORY_HASH_CURRENT;
2049 if(first_run)
2050 {
2051 const dt_history_hash_t hash_status = dt_history_hash_get_status(imgid);
2052 // if altered doesn't mask it
2053 if(!(hash_status & DT_HISTORY_HASH_CURRENT))
2054 {
2055 flags = flags | (auto_apply_modules ? DT_HISTORY_HASH_AUTO : DT_HISTORY_HASH_BASIC);
2056 }
2057 dt_history_hash_write_from_history(imgid, flags);
2058 // As we have a proper history right now and this is first_run we write the xmp now
2059 dt_image_t *image = dt_image_cache_get(darktable.image_cache, imgid, 'w');
2060 dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_SAFE);
2061 }
2062 else if(legacy_params)
2063 {
2064 const dt_history_hash_t hash_status = dt_history_hash_get_status(imgid);
2065 if(hash_status & (DT_HISTORY_HASH_BASIC | DT_HISTORY_HASH_AUTO))
2066 {
2067 // if image not altered keep the current status
2068 flags = flags | hash_status;
2069 }
2070 dt_history_hash_write_from_history(imgid, flags);
2071 }
2072 else
2073 {
2074 dt_history_hash_write_from_history(imgid, flags);
2075 }
2076
2077 dt_unlock_image(imgid);
2078 }
2079
dt_dev_read_history(dt_develop_t * dev)2080 void dt_dev_read_history(dt_develop_t *dev)
2081 {
2082 dt_dev_read_history_ext(dev, dev->image_storage.id, FALSE);
2083 }
2084
dt_dev_reprocess_all(dt_develop_t * dev)2085 void dt_dev_reprocess_all(dt_develop_t *dev)
2086 {
2087 if(darktable.gui->reset) return;
2088 if(dev && dev->gui_attached)
2089 {
2090 dev->pipe->changed |= DT_DEV_PIPE_SYNCH;
2091 dev->preview_pipe->changed |= DT_DEV_PIPE_SYNCH;
2092 dev->preview2_pipe->changed |= DT_DEV_PIPE_SYNCH;
2093 dev->pipe->cache_obsolete = 1;
2094 dev->preview_pipe->cache_obsolete = 1;
2095 dev->preview2_pipe->cache_obsolete = 1;
2096
2097 // invalidate buffers and force redraw of darkroom
2098 dt_dev_invalidate_all(dev);
2099
2100 /* redraw */
2101 dt_control_queue_redraw_center();
2102 }
2103 }
2104
dt_dev_reprocess_center(dt_develop_t * dev)2105 void dt_dev_reprocess_center(dt_develop_t *dev)
2106 {
2107 if(darktable.gui->reset) return;
2108 if(dev && dev->gui_attached)
2109 {
2110 dev->pipe->changed |= DT_DEV_PIPE_SYNCH;
2111 dev->pipe->cache_obsolete = 1;
2112
2113 // invalidate buffers and force redraw of darkroom
2114 dt_dev_invalidate_all(dev);
2115
2116 /* redraw */
2117 dt_control_queue_redraw_center();
2118 }
2119 }
2120
dt_dev_reprocess_preview(dt_develop_t * dev)2121 void dt_dev_reprocess_preview(dt_develop_t *dev)
2122 {
2123 if(darktable.gui->reset || !dev || !dev->gui_attached) return;
2124
2125 dev->preview_pipe->changed |= DT_DEV_PIPE_SYNCH;
2126 dev->preview_pipe->cache_obsolete = 1;
2127
2128 dt_dev_invalidate_preview(dev);
2129 dt_control_queue_redraw_center();
2130 }
2131
dt_dev_check_zoom_bounds(dt_develop_t * dev,float * zoom_x,float * zoom_y,dt_dev_zoom_t zoom,int closeup,float * boxww,float * boxhh)2132 void dt_dev_check_zoom_bounds(dt_develop_t *dev, float *zoom_x, float *zoom_y, dt_dev_zoom_t zoom,
2133 int closeup, float *boxww, float *boxhh)
2134 {
2135 int procw = 0, proch = 0;
2136 dt_dev_get_processed_size(dev, &procw, &proch);
2137 float boxw = 1.0f, boxh = 1.0f; // viewport in normalised space
2138 // if(zoom == DT_ZOOM_1)
2139 // {
2140 // const float imgw = (closeup ? 2 : 1)*procw;
2141 // const float imgh = (closeup ? 2 : 1)*proch;
2142 // const float devw = MIN(imgw, dev->width);
2143 // const float devh = MIN(imgh, dev->height);
2144 // boxw = fminf(1.0, devw/imgw);
2145 // boxh = fminf(1.0, devh/imgh);
2146 // }
2147 if(zoom == DT_ZOOM_FIT)
2148 {
2149 *zoom_x = *zoom_y = 0.0f;
2150 boxw = boxh = 1.0f;
2151 }
2152 else
2153 {
2154 const float scale = dt_dev_get_zoom_scale(dev, zoom, 1<<closeup, 0);
2155 const float imgw = procw;
2156 const float imgh = proch;
2157 const float devw = dev->width;
2158 const float devh = dev->height;
2159 boxw = devw / (imgw * scale);
2160 boxh = devh / (imgh * scale);
2161 }
2162
2163 if(*zoom_x < boxw / 2 - .5) *zoom_x = boxw / 2 - .5;
2164 if(*zoom_x > .5 - boxw / 2) *zoom_x = .5 - boxw / 2;
2165 if(*zoom_y < boxh / 2 - .5) *zoom_y = boxh / 2 - .5;
2166 if(*zoom_y > .5 - boxh / 2) *zoom_y = .5 - boxh / 2;
2167 if(boxw > 1.0) *zoom_x = 0.0f;
2168 if(boxh > 1.0) *zoom_y = 0.0f;
2169
2170 if(boxww) *boxww = boxw;
2171 if(boxhh) *boxhh = boxh;
2172 }
2173
dt_dev_get_processed_size(const dt_develop_t * dev,int * procw,int * proch)2174 void dt_dev_get_processed_size(const dt_develop_t *dev, int *procw, int *proch)
2175 {
2176 if(!dev) return;
2177
2178 // if pipe is processed, lets return its size
2179 if(dev->pipe && dev->pipe->processed_width)
2180 {
2181 *procw = dev->pipe->processed_width;
2182 *proch = dev->pipe->processed_height;
2183 return;
2184 }
2185
2186 // fallback on preview pipe
2187 if(dev->preview_pipe && dev->preview_pipe->processed_width)
2188 {
2189 const float scale = (dev->preview_pipe->iscale / dev->preview_downsampling);
2190 *procw = scale * dev->preview_pipe->processed_width;
2191 *proch = scale * dev->preview_pipe->processed_height;
2192 return;
2193 }
2194
2195 // no processed pipes, lets return 0 size
2196 *procw = *proch = 0;
2197 return;
2198 }
2199
dt_dev_get_pointer_zoom_pos(dt_develop_t * dev,const float px,const float py,float * zoom_x,float * zoom_y)2200 void dt_dev_get_pointer_zoom_pos(dt_develop_t *dev, const float px, const float py, float *zoom_x,
2201 float *zoom_y)
2202 {
2203 dt_dev_zoom_t zoom;
2204 int closeup = 0, procw = 0, proch = 0;
2205 float zoom2_x = 0.0f, zoom2_y = 0.0f;
2206 zoom = dt_control_get_dev_zoom();
2207 closeup = dt_control_get_dev_closeup();
2208 zoom2_x = dt_control_get_dev_zoom_x();
2209 zoom2_y = dt_control_get_dev_zoom_y();
2210 dt_dev_get_processed_size(dev, &procw, &proch);
2211 const float scale = dt_dev_get_zoom_scale(dev, zoom, 1<<closeup, 0);
2212 // offset from center now (current zoom_{x,y} points there)
2213 const float mouse_off_x = px - .5 * dev->width, mouse_off_y = py - .5 * dev->height;
2214 zoom2_x += mouse_off_x / (procw * scale);
2215 zoom2_y += mouse_off_y / (proch * scale);
2216 *zoom_x = zoom2_x;
2217 *zoom_y = zoom2_y;
2218 }
2219
dt_dev_get_history_item_label(dt_dev_history_item_t * hist,char * label,const int cnt)2220 void dt_dev_get_history_item_label(dt_dev_history_item_t *hist, char *label, const int cnt)
2221 {
2222 gchar *module_label = dt_history_item_get_name(hist->module);
2223 g_snprintf(label, cnt, "%s (%s)", module_label, hist->enabled ? _("on") : _("off"));
2224 g_free(module_label);
2225 }
2226
dt_dev_is_current_image(dt_develop_t * dev,uint32_t imgid)2227 int dt_dev_is_current_image(dt_develop_t *dev, uint32_t imgid)
2228 {
2229 return (dev->image_storage.id == imgid) ? 1 : 0;
2230 }
2231
find_last_exposure_instance(dt_develop_t * dev)2232 static dt_dev_proxy_exposure_t *find_last_exposure_instance(dt_develop_t *dev)
2233 {
2234 if(!dev->proxy.exposure.module) return NULL;
2235
2236 dt_dev_proxy_exposure_t *instance = &dev->proxy.exposure;
2237
2238 return instance;
2239 };
2240
dt_dev_exposure_hooks_available(dt_develop_t * dev)2241 gboolean dt_dev_exposure_hooks_available(dt_develop_t *dev)
2242 {
2243 dt_dev_proxy_exposure_t *instance = find_last_exposure_instance(dev);
2244
2245 /* check if exposure iop module has registered its hooks */
2246 if(instance && instance->module && instance->set_black && instance->get_black && instance->set_exposure
2247 && instance->get_exposure)
2248 return TRUE;
2249
2250 return FALSE;
2251 }
2252
dt_dev_exposure_reset_defaults(dt_develop_t * dev)2253 void dt_dev_exposure_reset_defaults(dt_develop_t *dev)
2254 {
2255 dt_dev_proxy_exposure_t *instance = find_last_exposure_instance(dev);
2256
2257 if(!(instance && instance->module)) return;
2258
2259 dt_iop_module_t *exposure = instance->module;
2260 memcpy(exposure->params, exposure->default_params, exposure->params_size);
2261 exposure->gui_update(exposure);
2262 dt_dev_add_history_item(exposure->dev, exposure, TRUE);
2263 }
2264
dt_dev_exposure_set_exposure(dt_develop_t * dev,const float exposure)2265 void dt_dev_exposure_set_exposure(dt_develop_t *dev, const float exposure)
2266 {
2267 dt_dev_proxy_exposure_t *instance = find_last_exposure_instance(dev);
2268
2269 if(instance && instance->module && instance->set_exposure) instance->set_exposure(instance->module, exposure);
2270 }
2271
dt_dev_exposure_get_exposure(dt_develop_t * dev)2272 float dt_dev_exposure_get_exposure(dt_develop_t *dev)
2273 {
2274 dt_dev_proxy_exposure_t *instance = find_last_exposure_instance(dev);
2275
2276 if(instance && instance->module && instance->get_exposure) return instance->get_exposure(instance->module);
2277
2278 return 0.0;
2279 }
2280
dt_dev_exposure_set_black(dt_develop_t * dev,const float black)2281 void dt_dev_exposure_set_black(dt_develop_t *dev, const float black)
2282 {
2283 dt_dev_proxy_exposure_t *instance = find_last_exposure_instance(dev);
2284
2285 if(instance && instance->module && instance->set_black) instance->set_black(instance->module, black);
2286 }
2287
dt_dev_exposure_get_black(dt_develop_t * dev)2288 float dt_dev_exposure_get_black(dt_develop_t *dev)
2289 {
2290 dt_dev_proxy_exposure_t *instance = find_last_exposure_instance(dev);
2291
2292 if(instance && instance->module && instance->get_black) return instance->get_black(instance->module);
2293
2294 return 0.0;
2295 }
2296
dt_dev_modulegroups_set(dt_develop_t * dev,uint32_t group)2297 void dt_dev_modulegroups_set(dt_develop_t *dev, uint32_t group)
2298 {
2299 if(dev->proxy.modulegroups.module && dev->proxy.modulegroups.set && dev->first_load == FALSE)
2300 dev->proxy.modulegroups.set(dev->proxy.modulegroups.module, group);
2301 }
2302
dt_dev_modulegroups_get_activated(dt_develop_t * dev)2303 uint32_t dt_dev_modulegroups_get_activated(dt_develop_t *dev)
2304 {
2305 if(dev->proxy.modulegroups.module && dev->proxy.modulegroups.get_activated)
2306 return dev->proxy.modulegroups.get_activated(dev->proxy.modulegroups.module);
2307
2308 return 0;
2309 }
2310
dt_dev_modulegroups_get(dt_develop_t * dev)2311 uint32_t dt_dev_modulegroups_get(dt_develop_t *dev)
2312 {
2313 if(dev->proxy.modulegroups.module && dev->proxy.modulegroups.get)
2314 return dev->proxy.modulegroups.get(dev->proxy.modulegroups.module);
2315
2316 return 0;
2317 }
2318
dt_dev_modulegroups_test(dt_develop_t * dev,uint32_t group,dt_iop_module_t * module)2319 gboolean dt_dev_modulegroups_test(dt_develop_t *dev, uint32_t group, dt_iop_module_t *module)
2320 {
2321 if(dev->proxy.modulegroups.module && dev->proxy.modulegroups.test)
2322 return dev->proxy.modulegroups.test(dev->proxy.modulegroups.module, group, module);
2323 return FALSE;
2324 }
2325
dt_dev_modulegroups_switch(dt_develop_t * dev,dt_iop_module_t * module)2326 void dt_dev_modulegroups_switch(dt_develop_t *dev, dt_iop_module_t *module)
2327 {
2328 if(dev->proxy.modulegroups.module && dev->proxy.modulegroups.switch_group && dev->first_load == FALSE)
2329 dev->proxy.modulegroups.switch_group(dev->proxy.modulegroups.module, module);
2330 }
2331
dt_dev_modulegroups_update_visibility(dt_develop_t * dev)2332 void dt_dev_modulegroups_update_visibility(dt_develop_t *dev)
2333 {
2334 if(dev->proxy.modulegroups.module && dev->proxy.modulegroups.switch_group && dev->first_load == FALSE)
2335 dev->proxy.modulegroups.update_visibility(dev->proxy.modulegroups.module);
2336 }
2337
dt_dev_modulegroups_search_text_focus(dt_develop_t * dev)2338 void dt_dev_modulegroups_search_text_focus(dt_develop_t *dev)
2339 {
2340 if(dev->proxy.modulegroups.module && dev->proxy.modulegroups.search_text_focus && dev->first_load == 0)
2341 dev->proxy.modulegroups.search_text_focus(dev->proxy.modulegroups.module);
2342 }
2343
dt_dev_modulegroups_is_visible(dt_develop_t * dev,gchar * module)2344 gboolean dt_dev_modulegroups_is_visible(dt_develop_t *dev, gchar *module)
2345 {
2346 if(dev->proxy.modulegroups.module && dev->proxy.modulegroups.test_visible)
2347 return dev->proxy.modulegroups.test_visible(dev->proxy.modulegroups.module, module);
2348 return FALSE;
2349 }
2350
dt_dev_masks_list_change(dt_develop_t * dev)2351 void dt_dev_masks_list_change(dt_develop_t *dev)
2352 {
2353 if(dev->proxy.masks.module && dev->proxy.masks.list_change)
2354 dev->proxy.masks.list_change(dev->proxy.masks.module);
2355 }
dt_dev_masks_list_update(dt_develop_t * dev)2356 void dt_dev_masks_list_update(dt_develop_t *dev)
2357 {
2358 if(dev->proxy.masks.module && dev->proxy.masks.list_update)
2359 dev->proxy.masks.list_update(dev->proxy.masks.module);
2360 }
dt_dev_masks_list_remove(dt_develop_t * dev,int formid,int parentid)2361 void dt_dev_masks_list_remove(dt_develop_t *dev, int formid, int parentid)
2362 {
2363 if(dev->proxy.masks.module && dev->proxy.masks.list_remove)
2364 dev->proxy.masks.list_remove(dev->proxy.masks.module, formid, parentid);
2365 }
dt_dev_masks_selection_change(dt_develop_t * dev,int selectid,int throw_event)2366 void dt_dev_masks_selection_change(dt_develop_t *dev, int selectid, int throw_event)
2367 {
2368 if(dev->proxy.masks.module && dev->proxy.masks.selection_change)
2369 dev->proxy.masks.selection_change(dev->proxy.masks.module, selectid, throw_event);
2370 }
2371
dt_dev_snapshot_request(dt_develop_t * dev,const char * filename)2372 void dt_dev_snapshot_request(dt_develop_t *dev, const char *filename)
2373 {
2374 dev->proxy.snapshot.filename = filename;
2375 dev->proxy.snapshot.request = TRUE;
2376 dt_control_queue_redraw_center();
2377 }
2378
dt_dev_invalidate_from_gui(dt_develop_t * dev)2379 void dt_dev_invalidate_from_gui(dt_develop_t *dev)
2380 {
2381 dt_dev_pop_history_items(darktable.develop, darktable.develop->history_end);
2382 }
2383
dt_dev_average_delay_update(const dt_times_t * start,uint32_t * average_delay)2384 void dt_dev_average_delay_update(const dt_times_t *start, uint32_t *average_delay)
2385 {
2386 dt_times_t end;
2387 dt_get_times(&end);
2388
2389 *average_delay += ((end.clock - start->clock) * 1000 / DT_DEV_AVERAGE_DELAY_COUNT
2390 - *average_delay / DT_DEV_AVERAGE_DELAY_COUNT);
2391 }
2392
2393
2394 /** duplicate a existent module */
dt_dev_module_duplicate(dt_develop_t * dev,dt_iop_module_t * base)2395 dt_iop_module_t *dt_dev_module_duplicate(dt_develop_t *dev, dt_iop_module_t *base)
2396 {
2397 // we create the new module
2398 dt_iop_module_t *module = (dt_iop_module_t *)calloc(1, sizeof(dt_iop_module_t));
2399 if(dt_iop_load_module(module, base->so, base->dev)) return NULL;
2400 module->instance = base->instance;
2401
2402 // we set the multi-instance priority and the iop order
2403 int pmax = 0;
2404 for(GList *modules = base->dev->iop; modules; modules = g_list_next(modules))
2405 {
2406 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
2407 if(mod->instance == base->instance)
2408 {
2409 if(pmax < mod->multi_priority) pmax = mod->multi_priority;
2410 }
2411 }
2412 // create a unique multi-priority
2413 pmax += 1;
2414 dt_iop_update_multi_priority(module, pmax);
2415
2416 // add this new module position into the iop-order-list
2417 dt_ioppr_insert_module_instance(dev, module);
2418
2419 // since we do not rename the module we need to check that an old module does not have the same name. Indeed
2420 // the multi_priority
2421 // are always rebased to start from 0, to it may be the case that the same multi_name be generated when
2422 // duplicating a module.
2423 int pname = module->multi_priority;
2424 char mname[128];
2425
2426 do
2427 {
2428 snprintf(mname, sizeof(mname), "%d", pname);
2429 gboolean dup = FALSE;
2430
2431 for(GList *modules = base->dev->iop; modules; modules = g_list_next(modules))
2432 {
2433 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
2434 if(mod->instance == base->instance)
2435 {
2436 if(strcmp(mname, mod->multi_name) == 0)
2437 {
2438 dup = TRUE;
2439 break;
2440 }
2441 }
2442 }
2443
2444 if(dup)
2445 pname++;
2446 else
2447 break;
2448 } while(1);
2449
2450 // the multi instance name
2451 g_strlcpy(module->multi_name, mname, sizeof(module->multi_name));
2452 // we insert this module into dev->iop
2453 base->dev->iop = g_list_insert_sorted(base->dev->iop, module, dt_sort_iop_by_order);
2454
2455 // always place the new instance after the base one
2456 if(!dt_ioppr_move_iop_after(base->dev, module, base))
2457 {
2458 fprintf(stderr, "[dt_dev_module_duplicate] can't move new instance after the base one\n");
2459 }
2460
2461 // that's all. rest of insertion is gui work !
2462 return module;
2463 }
2464
dt_dev_invalidate_history_module(GList * list,dt_iop_module_t * module)2465 void dt_dev_invalidate_history_module(GList *list, dt_iop_module_t *module)
2466 {
2467 for(; list; list = g_list_next(list))
2468 {
2469 dt_dev_history_item_t *hitem = (dt_dev_history_item_t *)list->data;
2470 if (hitem->module == module)
2471 {
2472 hitem->module = NULL;
2473 }
2474 }
2475 }
2476
dt_dev_module_remove(dt_develop_t * dev,dt_iop_module_t * module)2477 void dt_dev_module_remove(dt_develop_t *dev, dt_iop_module_t *module)
2478 {
2479 // if(darktable.gui->reset) return;
2480 dt_pthread_mutex_lock(&dev->history_mutex);
2481 int del = 0;
2482
2483 if(dev->gui_attached)
2484 {
2485 dt_dev_undo_start_record(dev);
2486
2487 GList *elem = dev->history;
2488 while(elem != NULL)
2489 {
2490 GList *next = g_list_next(elem);
2491 dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(elem->data);
2492
2493 if(module == hist->module)
2494 {
2495 // printf("removing obsoleted history item: %s %s %p %p\n", hist->module->op, hist->module->multi_name,
2496 // module, hist->module);
2497 dt_dev_free_history_item(hist);
2498 dev->history = g_list_delete_link(dev->history, elem);
2499 dev->history_end--;
2500 del = 1;
2501 }
2502 elem = next;
2503 }
2504 }
2505
2506 dt_pthread_mutex_unlock(&dev->history_mutex);
2507
2508 // and we remove it from the list
2509 for(GList *modules = dev->iop; modules; modules = g_list_next(modules))
2510 {
2511 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
2512 if(mod == module)
2513 {
2514 dev->iop = g_list_remove_link(dev->iop, modules);
2515 break;
2516 }
2517 }
2518
2519 if(dev->gui_attached && del)
2520 {
2521 /* signal that history has changed */
2522 dt_dev_undo_end_record(dev);
2523
2524 DT_DEBUG_CONTROL_SIGNAL_RAISE(darktable.signals, DT_SIGNAL_DEVELOP_MODULE_REMOVE, module);
2525 /* redraw */
2526 dt_control_queue_redraw_center();
2527 }
2528 }
2529
_dev_module_update_multishow(dt_develop_t * dev,struct dt_iop_module_t * module)2530 void _dev_module_update_multishow(dt_develop_t *dev, struct dt_iop_module_t *module)
2531 {
2532 // We count the number of other instances
2533 int nb_instances = 0;
2534 for(GList *modules = dev->iop; modules; modules = g_list_next(modules))
2535 {
2536 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
2537
2538 if(mod->instance == module->instance) nb_instances++;
2539 }
2540
2541 dt_iop_module_t *mod_prev = dt_iop_gui_get_previous_visible_module(module);
2542 dt_iop_module_t *mod_next = dt_iop_gui_get_next_visible_module(module);
2543
2544 const gboolean move_next = (mod_next && mod_next->iop_order != INT_MAX) ? dt_ioppr_check_can_move_after_iop(dev->iop, module, mod_next) : -1.0;
2545 const gboolean move_prev = (mod_prev && mod_prev->iop_order != INT_MAX) ? dt_ioppr_check_can_move_before_iop(dev->iop, module, mod_prev) : -1.0;
2546
2547 module->multi_show_new = !(module->flags() & IOP_FLAGS_ONE_INSTANCE);
2548 module->multi_show_close = (nb_instances > 1);
2549 if(mod_next)
2550 module->multi_show_up = move_next;
2551 else
2552 module->multi_show_up = 0;
2553 if(mod_prev)
2554 module->multi_show_down = move_prev;
2555 else
2556 module->multi_show_down = 0;
2557 }
2558
dt_dev_modules_update_multishow(dt_develop_t * dev)2559 void dt_dev_modules_update_multishow(dt_develop_t *dev)
2560 {
2561 dt_ioppr_check_iop_order(dev, 0, "dt_dev_modules_update_multishow");
2562
2563 for(GList *modules = dev->iop; modules; modules = g_list_next(modules))
2564 {
2565 dt_iop_module_t *mod = (dt_iop_module_t *)modules->data;
2566
2567 // only for visible modules
2568 GtkWidget *expander = mod->expander;
2569 if(expander && gtk_widget_is_visible(expander))
2570 {
2571 _dev_module_update_multishow(dev, mod);
2572 }
2573 }
2574 }
2575
dt_history_item_get_name(const struct dt_iop_module_t * module)2576 gchar *dt_history_item_get_name(const struct dt_iop_module_t *module)
2577 {
2578 gchar *label;
2579 /* create a history button and add to box */
2580 if(!module->multi_name[0] || strcmp(module->multi_name, "0") == 0)
2581 label = g_strdup_printf("%s", module->name());
2582 else
2583 label = g_strdup_printf("%s %s", module->name(), module->multi_name);
2584 return label;
2585 }
2586
dt_history_item_get_name_html(const struct dt_iop_module_t * module)2587 gchar *dt_history_item_get_name_html(const struct dt_iop_module_t *module)
2588 {
2589 gchar *label;
2590 /* create a history button and add to box */
2591 if(!module->multi_name[0] || strcmp(module->multi_name, "0") == 0)
2592 label = g_strdup_printf("%s", module->name());
2593 else
2594 label = g_markup_printf_escaped("%s <span size=\"smaller\">%s</span>", module->name(), module->multi_name);
2595 return label;
2596 }
2597
dt_dev_distort_transform(dt_develop_t * dev,float * points,size_t points_count)2598 int dt_dev_distort_transform(dt_develop_t *dev, float *points, size_t points_count)
2599 {
2600 return dt_dev_distort_transform_plus(dev, dev->preview_pipe, 0.0f, DT_DEV_TRANSFORM_DIR_ALL, points, points_count);
2601 }
dt_dev_distort_backtransform(dt_develop_t * dev,float * points,size_t points_count)2602 int dt_dev_distort_backtransform(dt_develop_t *dev, float *points, size_t points_count)
2603 {
2604 return dt_dev_distort_backtransform_plus(dev, dev->preview_pipe, 0.0f, DT_DEV_TRANSFORM_DIR_ALL, points, points_count);
2605 }
2606
2607 // only call directly or indirectly from dt_dev_distort_transform_plus, so that it runs with the history locked
dt_dev_distort_transform_locked(dt_develop_t * dev,dt_dev_pixelpipe_t * pipe,const double iop_order,const int transf_direction,float * points,size_t points_count)2608 int dt_dev_distort_transform_locked(dt_develop_t *dev, dt_dev_pixelpipe_t *pipe, const double iop_order,
2609 const int transf_direction, float *points, size_t points_count)
2610 {
2611 GList *modules = pipe->iop;
2612 GList *pieces = pipe->nodes;
2613 while(modules)
2614 {
2615 if(!pieces)
2616 {
2617 return 0;
2618 }
2619 dt_iop_module_t *module = (dt_iop_module_t *)(modules->data);
2620 dt_dev_pixelpipe_iop_t *piece = (dt_dev_pixelpipe_iop_t *)(pieces->data);
2621 if(piece->enabled
2622 && ((transf_direction == DT_DEV_TRANSFORM_DIR_ALL)
2623 || (transf_direction == DT_DEV_TRANSFORM_DIR_FORW_INCL && module->iop_order >= iop_order)
2624 || (transf_direction == DT_DEV_TRANSFORM_DIR_FORW_EXCL && module->iop_order > iop_order)
2625 || (transf_direction == DT_DEV_TRANSFORM_DIR_BACK_INCL && module->iop_order <= iop_order)
2626 || (transf_direction == DT_DEV_TRANSFORM_DIR_BACK_EXCL && module->iop_order < iop_order))
2627 && !(dev->gui_module && dev->gui_module != module
2628 && (dev->gui_module->operation_tags_filter() & module->operation_tags())))
2629 {
2630 module->distort_transform(module, piece, points, points_count);
2631 }
2632 modules = g_list_next(modules);
2633 pieces = g_list_next(pieces);
2634 }
2635 return 1;
2636 }
2637
dt_dev_distort_transform_plus(dt_develop_t * dev,dt_dev_pixelpipe_t * pipe,const double iop_order,const int transf_direction,float * points,size_t points_count)2638 int dt_dev_distort_transform_plus(dt_develop_t *dev, dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction,
2639 float *points, size_t points_count)
2640 {
2641 dt_pthread_mutex_lock(&dev->history_mutex);
2642 const int success = dt_dev_distort_transform_locked(dev,pipe,iop_order,transf_direction,points,points_count);
2643
2644 if (success
2645 && (dev->preview_downsampling != 1.0f)
2646 && (transf_direction == DT_DEV_TRANSFORM_DIR_ALL
2647 || transf_direction == DT_DEV_TRANSFORM_DIR_FORW_EXCL
2648 || transf_direction == DT_DEV_TRANSFORM_DIR_FORW_INCL))
2649 for(size_t idx=0; idx < 2 * points_count; idx++)
2650 points[idx] *= dev->preview_downsampling;
2651
2652 dt_pthread_mutex_unlock(&dev->history_mutex);
2653 return 1;
2654 }
2655
2656 // only call directly or indirectly from dt_dev_distort_transform_plus, so that it runs with the history locked
dt_dev_distort_backtransform_locked(dt_develop_t * dev,dt_dev_pixelpipe_t * pipe,const double iop_order,const int transf_direction,float * points,size_t points_count)2657 int dt_dev_distort_backtransform_locked(dt_develop_t *dev, dt_dev_pixelpipe_t *pipe, const double iop_order,
2658 const int transf_direction, float *points, size_t points_count)
2659 {
2660 GList *modules = g_list_last(pipe->iop);
2661 GList *pieces = g_list_last(pipe->nodes);
2662 while(modules)
2663 {
2664 if(!pieces)
2665 {
2666 return 0;
2667 }
2668 dt_iop_module_t *module = (dt_iop_module_t *)(modules->data);
2669 dt_dev_pixelpipe_iop_t *piece = (dt_dev_pixelpipe_iop_t *)(pieces->data);
2670 if(piece->enabled
2671 && ((transf_direction == DT_DEV_TRANSFORM_DIR_ALL)
2672 || (transf_direction == DT_DEV_TRANSFORM_DIR_FORW_INCL && module->iop_order >= iop_order)
2673 || (transf_direction == DT_DEV_TRANSFORM_DIR_FORW_EXCL && module->iop_order > iop_order)
2674 || (transf_direction == DT_DEV_TRANSFORM_DIR_BACK_INCL && module->iop_order <= iop_order)
2675 || (transf_direction == DT_DEV_TRANSFORM_DIR_BACK_EXCL && module->iop_order < iop_order))
2676 && !(dev->gui_module && dev->gui_module != module
2677 && (dev->gui_module->operation_tags_filter() & module->operation_tags())))
2678 {
2679 module->distort_backtransform(module, piece, points, points_count);
2680 }
2681 modules = g_list_previous(modules);
2682 pieces = g_list_previous(pieces);
2683 }
2684 return 1;
2685 }
2686
dt_dev_distort_backtransform_plus(dt_develop_t * dev,dt_dev_pixelpipe_t * pipe,const double iop_order,const int transf_direction,float * points,size_t points_count)2687 int dt_dev_distort_backtransform_plus(dt_develop_t *dev, dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction,
2688 float *points, size_t points_count)
2689 {
2690 dt_pthread_mutex_lock(&dev->history_mutex);
2691 if ((dev->preview_downsampling != 1.0f) && (transf_direction == DT_DEV_TRANSFORM_DIR_ALL
2692 || transf_direction == DT_DEV_TRANSFORM_DIR_FORW_EXCL
2693 || transf_direction == DT_DEV_TRANSFORM_DIR_FORW_INCL))
2694 for(size_t idx=0; idx < 2 * points_count; idx++)
2695 points[idx] /= dev->preview_downsampling;
2696
2697 const int success = dt_dev_distort_backtransform_locked(dev, pipe, iop_order, transf_direction, points, points_count);
2698 dt_pthread_mutex_unlock(&dev->history_mutex);
2699 return success;
2700 }
2701
dt_dev_distort_get_iop_pipe(dt_develop_t * dev,struct dt_dev_pixelpipe_t * pipe,struct dt_iop_module_t * module)2702 dt_dev_pixelpipe_iop_t *dt_dev_distort_get_iop_pipe(dt_develop_t *dev, struct dt_dev_pixelpipe_t *pipe,
2703 struct dt_iop_module_t *module)
2704 {
2705 for(const GList *pieces = g_list_last(pipe->nodes); pieces; pieces = g_list_previous(pieces))
2706 {
2707 dt_dev_pixelpipe_iop_t *piece = (dt_dev_pixelpipe_iop_t *)(pieces->data);
2708 if(piece->module == module)
2709 {
2710 return piece;
2711 }
2712 }
2713 return NULL;
2714 }
2715
dt_dev_hash(dt_develop_t * dev)2716 uint64_t dt_dev_hash(dt_develop_t *dev)
2717 {
2718 return dt_dev_hash_plus(dev, dev->preview_pipe, 0.0f, DT_DEV_TRANSFORM_DIR_ALL);
2719 }
2720
dt_dev_hash_plus(dt_develop_t * dev,struct dt_dev_pixelpipe_t * pipe,const double iop_order,const int transf_direction)2721 uint64_t dt_dev_hash_plus(dt_develop_t *dev, struct dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction)
2722 {
2723 uint64_t hash = 5381;
2724 dt_pthread_mutex_lock(&dev->history_mutex);
2725 GList *modules = g_list_last(pipe->iop);
2726 GList *pieces = g_list_last(pipe->nodes);
2727 while(modules)
2728 {
2729 if(!pieces)
2730 {
2731 dt_pthread_mutex_unlock(&dev->history_mutex);
2732 return 0;
2733 }
2734 dt_iop_module_t *module = (dt_iop_module_t *)(modules->data);
2735 dt_dev_pixelpipe_iop_t *piece = (dt_dev_pixelpipe_iop_t *)(pieces->data);
2736 if(piece->enabled && ((transf_direction == DT_DEV_TRANSFORM_DIR_ALL)
2737 || (transf_direction == DT_DEV_TRANSFORM_DIR_FORW_INCL && module->iop_order >= iop_order)
2738 || (transf_direction == DT_DEV_TRANSFORM_DIR_FORW_EXCL && module->iop_order > iop_order)
2739 || (transf_direction == DT_DEV_TRANSFORM_DIR_BACK_INCL && module->iop_order <= iop_order)
2740 || (transf_direction == DT_DEV_TRANSFORM_DIR_BACK_EXCL && module->iop_order < iop_order)))
2741 {
2742 hash = ((hash << 5) + hash) ^ piece->hash;
2743 }
2744 modules = g_list_previous(modules);
2745 pieces = g_list_previous(pieces);
2746 }
2747 dt_pthread_mutex_unlock(&dev->history_mutex);
2748 return hash;
2749 }
2750
dt_dev_wait_hash(dt_develop_t * dev,struct dt_dev_pixelpipe_t * pipe,const double iop_order,const int transf_direction,dt_pthread_mutex_t * lock,const volatile uint64_t * const hash)2751 int dt_dev_wait_hash(dt_develop_t *dev, struct dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, dt_pthread_mutex_t *lock,
2752 const volatile uint64_t *const hash)
2753 {
2754 const int usec = 5000;
2755 int nloop;
2756
2757 #ifdef HAVE_OPENCL
2758 if(pipe->devid >= 0)
2759 nloop = darktable.opencl->opencl_synchronization_timeout;
2760 else
2761 nloop = dt_conf_get_int("pixelpipe_synchronization_timeout");
2762 #else
2763 nloop = dt_conf_get_int("pixelpipe_synchronization_timeout");
2764 #endif
2765
2766 if(nloop <= 0) return TRUE; // non-positive values omit pixelpipe synchronization
2767
2768 for(int n = 0; n < nloop; n++)
2769 {
2770 if(dt_atomic_get_int(&pipe->shutdown))
2771 return TRUE; // stop waiting if pipe shuts down
2772
2773 uint64_t probehash;
2774
2775 if(lock)
2776 {
2777 dt_pthread_mutex_lock(lock);
2778 probehash = *hash;
2779 dt_pthread_mutex_unlock(lock);
2780 }
2781 else
2782 probehash = *hash;
2783
2784 if(probehash == dt_dev_hash_plus(dev, pipe, iop_order, transf_direction))
2785 return TRUE;
2786
2787 dt_iop_nap(usec);
2788 }
2789
2790 return FALSE;
2791 }
2792
dt_dev_sync_pixelpipe_hash(dt_develop_t * dev,struct dt_dev_pixelpipe_t * pipe,const double iop_order,const int transf_direction,dt_pthread_mutex_t * lock,const volatile uint64_t * const hash)2793 int dt_dev_sync_pixelpipe_hash(dt_develop_t *dev, struct dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, dt_pthread_mutex_t *lock,
2794 const volatile uint64_t *const hash)
2795 {
2796 // first wait for matching hash values
2797 if(dt_dev_wait_hash(dev, pipe, iop_order, transf_direction, lock, hash))
2798 return TRUE;
2799
2800 // timed out. let's see if history stack has changed
2801 if(pipe->changed & (DT_DEV_PIPE_TOP_CHANGED | DT_DEV_PIPE_REMOVE | DT_DEV_PIPE_SYNCH))
2802 {
2803 // history stack has changed. let's trigger reprocessing
2804 dt_control_queue_redraw_center();
2805 // pretend that everything is fine
2806 return TRUE;
2807 }
2808
2809 // no way to get pixelpipes in sync
2810 return FALSE;
2811 }
2812
dt_dev_hash_distort(dt_develop_t * dev)2813 uint64_t dt_dev_hash_distort(dt_develop_t *dev)
2814 {
2815 return dt_dev_hash_distort_plus(dev, dev->preview_pipe, 0.0f, DT_DEV_TRANSFORM_DIR_ALL);
2816 }
2817
dt_dev_hash_distort_plus(dt_develop_t * dev,struct dt_dev_pixelpipe_t * pipe,const double iop_order,const int transf_direction)2818 uint64_t dt_dev_hash_distort_plus(dt_develop_t *dev, struct dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction)
2819 {
2820 uint64_t hash = 5381;
2821 dt_pthread_mutex_lock(&dev->history_mutex);
2822 GList *modules = g_list_last(pipe->iop);
2823 GList *pieces = g_list_last(pipe->nodes);
2824 while(modules)
2825 {
2826 if(!pieces)
2827 {
2828 dt_pthread_mutex_unlock(&dev->history_mutex);
2829 return 0;
2830 }
2831 dt_iop_module_t *module = (dt_iop_module_t *)(modules->data);
2832 dt_dev_pixelpipe_iop_t *piece = (dt_dev_pixelpipe_iop_t *)(pieces->data);
2833 if(piece->enabled && module->operation_tags() & IOP_TAG_DISTORT
2834 && ((transf_direction == DT_DEV_TRANSFORM_DIR_ALL)
2835 || (transf_direction == DT_DEV_TRANSFORM_DIR_FORW_INCL && module->iop_order >= iop_order)
2836 || (transf_direction == DT_DEV_TRANSFORM_DIR_FORW_EXCL && module->iop_order > iop_order)
2837 || (transf_direction == DT_DEV_TRANSFORM_DIR_BACK_INCL && module->iop_order <= iop_order)
2838 || (transf_direction == DT_DEV_TRANSFORM_DIR_BACK_EXCL && module->iop_order < iop_order)))
2839 {
2840 hash = ((hash << 5) + hash) ^ piece->hash;
2841 }
2842 modules = g_list_previous(modules);
2843 pieces = g_list_previous(pieces);
2844 }
2845 dt_pthread_mutex_unlock(&dev->history_mutex);
2846 return hash;
2847 }
2848
dt_dev_wait_hash_distort(dt_develop_t * dev,struct dt_dev_pixelpipe_t * pipe,const double iop_order,const int transf_direction,dt_pthread_mutex_t * lock,const volatile uint64_t * const hash)2849 int dt_dev_wait_hash_distort(dt_develop_t *dev, struct dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, dt_pthread_mutex_t *lock,
2850 const volatile uint64_t *const hash)
2851 {
2852 const int usec = 5000;
2853 int nloop = 0;
2854
2855 #ifdef HAVE_OPENCL
2856 if(pipe->devid >= 0)
2857 nloop = darktable.opencl->opencl_synchronization_timeout;
2858 else
2859 nloop = dt_conf_get_int("pixelpipe_synchronization_timeout");
2860 #else
2861 nloop = dt_conf_get_int("pixelpipe_synchronization_timeout");
2862 #endif
2863
2864 if(nloop <= 0) return TRUE; // non-positive values omit pixelpipe synchronization
2865
2866 for(int n = 0; n < nloop; n++)
2867 {
2868 if(dt_atomic_get_int(&pipe->shutdown))
2869 return TRUE; // stop waiting if pipe shuts down
2870
2871 uint64_t probehash = 0;
2872
2873 if(lock)
2874 {
2875 dt_pthread_mutex_lock(lock);
2876 probehash = *hash;
2877 dt_pthread_mutex_unlock(lock);
2878 }
2879 else
2880 probehash = *hash;
2881
2882 if(probehash == dt_dev_hash_distort_plus(dev, pipe, iop_order, transf_direction))
2883 return TRUE;
2884
2885 dt_iop_nap(usec);
2886 }
2887
2888 return FALSE;
2889 }
2890
dt_dev_sync_pixelpipe_hash_distort(dt_develop_t * dev,struct dt_dev_pixelpipe_t * pipe,const double iop_order,const int transf_direction,dt_pthread_mutex_t * lock,const volatile uint64_t * const hash)2891 int dt_dev_sync_pixelpipe_hash_distort(dt_develop_t *dev, struct dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, dt_pthread_mutex_t *lock,
2892 const volatile uint64_t *const hash)
2893 {
2894 // first wait for matching hash values
2895 if(dt_dev_wait_hash_distort(dev, pipe, iop_order, transf_direction, lock, hash))
2896 return TRUE;
2897
2898 // timed out. let's see if history stack has changed
2899 if(pipe->changed & (DT_DEV_PIPE_TOP_CHANGED | DT_DEV_PIPE_REMOVE | DT_DEV_PIPE_SYNCH))
2900 {
2901 // history stack has changed. let's trigger reprocessing
2902 dt_control_queue_redraw_center();
2903 // pretend that everything is fine
2904 return TRUE;
2905 }
2906
2907 // no way to get pixelpipes in sync
2908 return FALSE;
2909 }
2910
2911 // set the module list order
dt_dev_reorder_gui_module_list(dt_develop_t * dev)2912 void dt_dev_reorder_gui_module_list(dt_develop_t *dev)
2913 {
2914 int pos_module = 0;
2915 for(const GList *modules = g_list_last(dev->iop); modules; modules = g_list_previous(modules))
2916 {
2917 dt_iop_module_t *module = (dt_iop_module_t *)(modules->data);
2918
2919 GtkWidget *expander = module->expander;
2920 if(expander)
2921 {
2922 gtk_box_reorder_child(dt_ui_get_container(darktable.gui->ui, DT_UI_CONTAINER_PANEL_RIGHT_CENTER), expander,
2923 pos_module++);
2924 }
2925 }
2926 }
2927
2928 //-----------------------------------------------------------
2929 // second darkroom window
2930 //-----------------------------------------------------------
2931
dt_second_window_get_dev_zoom(dt_develop_t * dev)2932 dt_dev_zoom_t dt_second_window_get_dev_zoom(dt_develop_t *dev)
2933 {
2934 return dev->second_window.zoom;
2935 }
2936
dt_second_window_set_dev_zoom(dt_develop_t * dev,const dt_dev_zoom_t value)2937 void dt_second_window_set_dev_zoom(dt_develop_t *dev, const dt_dev_zoom_t value)
2938 {
2939 dev->second_window.zoom = value;
2940 }
2941
dt_second_window_get_dev_closeup(dt_develop_t * dev)2942 int dt_second_window_get_dev_closeup(dt_develop_t *dev)
2943 {
2944 return dev->second_window.closeup;
2945 }
2946
dt_second_window_set_dev_closeup(dt_develop_t * dev,const int value)2947 void dt_second_window_set_dev_closeup(dt_develop_t *dev, const int value)
2948 {
2949 dev->second_window.closeup = value;
2950 }
2951
dt_second_window_get_dev_zoom_x(dt_develop_t * dev)2952 float dt_second_window_get_dev_zoom_x(dt_develop_t *dev)
2953 {
2954 return dev->second_window.zoom_x;
2955 }
2956
dt_second_window_set_dev_zoom_x(dt_develop_t * dev,const float value)2957 void dt_second_window_set_dev_zoom_x(dt_develop_t *dev, const float value)
2958 {
2959 dev->second_window.zoom_x = value;
2960 }
2961
dt_second_window_get_dev_zoom_y(dt_develop_t * dev)2962 float dt_second_window_get_dev_zoom_y(dt_develop_t *dev)
2963 {
2964 return dev->second_window.zoom_y;
2965 }
2966
dt_second_window_set_dev_zoom_y(dt_develop_t * dev,const float value)2967 void dt_second_window_set_dev_zoom_y(dt_develop_t *dev, const float value)
2968 {
2969 dev->second_window.zoom_y = value;
2970 }
2971
dt_second_window_get_free_zoom_scale(dt_develop_t * dev)2972 float dt_second_window_get_free_zoom_scale(dt_develop_t *dev)
2973 {
2974 return dev->second_window.zoom_scale;
2975 }
2976
dt_second_window_get_zoom_scale(dt_develop_t * dev,const dt_dev_zoom_t zoom,const int closeup_factor,const int preview)2977 float dt_second_window_get_zoom_scale(dt_develop_t *dev, const dt_dev_zoom_t zoom, const int closeup_factor,
2978 const int preview)
2979 {
2980 float zoom_scale = 0.0f;
2981
2982 const float w = preview ? dev->preview_pipe->processed_width : dev->preview2_pipe->processed_width;
2983 const float h = preview ? dev->preview_pipe->processed_height : dev->preview2_pipe->processed_height;
2984 const float ps = dev->preview2_pipe->backbuf_width
2985 ? dev->preview2_pipe->processed_width / (float)dev->preview_pipe->processed_width
2986 : dev->preview_pipe->iscale;
2987
2988 switch(zoom)
2989 {
2990 case DT_ZOOM_FIT:
2991 zoom_scale = fminf(dev->second_window.width / w, dev->second_window.height / h);
2992 break;
2993 case DT_ZOOM_FILL:
2994 zoom_scale = fmaxf(dev->second_window.width / w, dev->second_window.height / h);
2995 break;
2996 case DT_ZOOM_1:
2997 zoom_scale = closeup_factor;
2998 if(preview) zoom_scale *= ps;
2999 break;
3000 default: // DT_ZOOM_FREE
3001 zoom_scale = dt_second_window_get_free_zoom_scale(dev);
3002 if(preview) zoom_scale *= ps;
3003 break;
3004 }
3005 if (preview) zoom_scale /= dev->preview_downsampling;
3006 return zoom_scale;
3007 }
3008
dt_second_window_set_zoom_scale(dt_develop_t * dev,const float value)3009 void dt_second_window_set_zoom_scale(dt_develop_t *dev, const float value)
3010 {
3011 dev->second_window.zoom_scale = value;
3012 }
3013
dt_second_window_get_processed_size(const dt_develop_t * dev,int * procw,int * proch)3014 void dt_second_window_get_processed_size(const dt_develop_t *dev, int *procw, int *proch)
3015 {
3016 if(!dev) return;
3017
3018 // if preview2 is processed, lets return its size
3019 if(dev->preview2_pipe && dev->preview2_pipe->processed_width)
3020 {
3021 *procw = dev->preview2_pipe->processed_width;
3022 *proch = dev->preview2_pipe->processed_height;
3023 return;
3024 }
3025
3026 // fallback on preview pipe
3027 if(dev->preview_pipe && dev->preview_pipe->processed_width)
3028 {
3029 const float scale = (dev->preview_pipe->iscale / dev->preview_downsampling);
3030 *procw = scale * dev->preview_pipe->processed_width;
3031 *proch = scale * dev->preview_pipe->processed_height;
3032 return;
3033 }
3034
3035 // no processed pipes, lets return 0 size
3036 *procw = *proch = 0;
3037 return;
3038 }
3039
dt_second_window_check_zoom_bounds(dt_develop_t * dev,float * zoom_x,float * zoom_y,const dt_dev_zoom_t zoom,const int closeup,float * boxww,float * boxhh)3040 void dt_second_window_check_zoom_bounds(dt_develop_t *dev, float *zoom_x, float *zoom_y, const dt_dev_zoom_t zoom,
3041 const int closeup, float *boxww, float *boxhh)
3042 {
3043 int procw = 0, proch = 0;
3044 dt_second_window_get_processed_size(dev, &procw, &proch);
3045 float boxw = 1.0f, boxh = 1.0f; // viewport in normalised space
3046 // if(zoom == DT_ZOOM_1)
3047 // {
3048 // const float imgw = (closeup ? 2 : 1)*procw;
3049 // const float imgh = (closeup ? 2 : 1)*proch;
3050 // const float devw = MIN(imgw, dev->width);
3051 // const float devh = MIN(imgh, dev->height);
3052 // boxw = fminf(1.0, devw/imgw);
3053 // boxh = fminf(1.0, devh/imgh);
3054 // }
3055 if(zoom == DT_ZOOM_FIT)
3056 {
3057 *zoom_x = *zoom_y = 0.0f;
3058 boxw = boxh = 1.0f;
3059 }
3060 else
3061 {
3062 const float scale = dt_second_window_get_zoom_scale(dev, zoom, 1 << closeup, 0);
3063 const float imgw = procw;
3064 const float imgh = proch;
3065 const float devw = dev->second_window.width;
3066 const float devh = dev->second_window.height;
3067 boxw = devw / (imgw * scale);
3068 boxh = devh / (imgh * scale);
3069 }
3070
3071 if(*zoom_x < boxw / 2 - .5) *zoom_x = boxw / 2 - .5;
3072 if(*zoom_x > .5 - boxw / 2) *zoom_x = .5 - boxw / 2;
3073 if(*zoom_y < boxh / 2 - .5) *zoom_y = boxh / 2 - .5;
3074 if(*zoom_y > .5 - boxh / 2) *zoom_y = .5 - boxh / 2;
3075 if(boxw > 1.0) *zoom_x = 0.0f;
3076 if(boxh > 1.0) *zoom_y = 0.0f;
3077
3078 if(boxww) *boxww = boxw;
3079 if(boxhh) *boxhh = boxh;
3080 }
3081
dt_dev_undo_start_record(dt_develop_t * dev)3082 void dt_dev_undo_start_record(dt_develop_t *dev)
3083 {
3084 const dt_view_t *cv = dt_view_manager_get_current_view(darktable.view_manager);
3085
3086 /* record current history state : before change (needed for undo) */
3087 if(dev->gui_attached && cv->view((dt_view_t *)cv) == DT_VIEW_DARKROOM)
3088 {
3089 DT_DEBUG_CONTROL_SIGNAL_RAISE
3090 (darktable.signals, DT_SIGNAL_DEVELOP_HISTORY_WILL_CHANGE,
3091 dt_history_duplicate(dev->history),
3092 dev->history_end,
3093 dt_ioppr_iop_order_copy_deep(dev->iop_order_list));
3094 }
3095 }
3096
dt_dev_undo_end_record(dt_develop_t * dev)3097 void dt_dev_undo_end_record(dt_develop_t *dev)
3098 {
3099 const dt_view_t *cv = dt_view_manager_get_current_view(darktable.view_manager);
3100
3101 /* record current history state : after change (needed for undo) */
3102 if(dev->gui_attached && cv->view((dt_view_t *)cv) == DT_VIEW_DARKROOM)
3103 {
3104 DT_DEBUG_CONTROL_SIGNAL_RAISE(darktable.signals, DT_SIGNAL_DEVELOP_HISTORY_CHANGE);
3105 }
3106 }
3107
3108 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
3109 // vim: shiftwidth=2 expandtab tabstop=2 cindent
3110 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
3111