Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
common/image.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2009-2014, 2016 johannes hanika.
4 Copyright (C) 2010, 2015 Bruce Guenter.
5 Copyright (C) 2010-2012 Henrik Andersson.
6 Copyright (C) 2010-2012 Jérémy Rosen.
7 Copyright (C) 2010 Stuart Henderson.
8 Copyright (C) 2010-2018, 2020 Tobias Ellinghaus.
9 Copyright (C) 2010 Zeus V Panchenko.
10 Copyright (C) 2011 Brian Teague.
11 Copyright (C) 2011, 2013 José Carlos García Sogo.
12 Copyright (C) 2011 Robert Bieber.
13 Copyright (C) 2012 Alexander Clausen.
14 Copyright (C) 2012 Christian Tellefsen.
15 Copyright (C) 2012-2013 Dennis Gnad.
16 Copyright (C) 2012 James C. McPherson.
17 Copyright (C) 2012 marcel.
18 Copyright (C) 2012 Richard Wonka.
19 Copyright (C) 2012-2014 Ulrich Pegelow.
20 Copyright (C) 2013-2016, 2018-2022 Pascal Obry.
21 Copyright (C) 2013-2016 Roman Lebedev.
22 Copyright (C) 2014 Pascal de Bruijn.
23 Copyright (C) 2014-2016 Pedro Côrte-Real.
24 Copyright (C) 2016 Mark Oteiza.
25 Copyright (C) 2016-2018 Peter Budai.
26 Copyright (C) 2017, 2019 luzpaz.
27 Copyright (C) 2017 Žilvinas Žaltiena.
28 Copyright (C) 2018-2019 Edgardo Hoszowski.
29 Copyright (C) 2018 Kelvie Wong.
30 Copyright (C) 2018 Mario Lueder.
31 Copyright (C) 2019-2022 Aldric Renaudin.
32 Copyright (C) 2019 August Schwerdfeger.
33 Copyright (C) 2019 Bill Ferguson.
34 Copyright (C) 2019 fvollmer.
35 Copyright (C) 2019-2022 Hanno Schwalm.
36 Copyright (C) 2019 jakubfi.
37 Copyright (C) 2019-2022 Philippe Weyland.
38 Copyright (C) 2020-2026 Aurélien PIERRE.
39 Copyright (C) 2020 Chris Elston.
40 Copyright (C) 2020 Dan Torop.
41 Copyright (C) 2020 GrahamByrnes.
42 Copyright (C) 2020 hatsunearu.
43 Copyright (C) 2020 Hubert Kowalski.
44 Copyright (C) 2020 JP Verrue.
45 Copyright (C) 2020 Marco.
46 Copyright (C) 2020 U-DESKTOP-HQME86J\marco.
47 Copyright (C) 2021 Daniel Vogelbacher.
48 Copyright (C) 2021-2022 HansBull.
49 Copyright (C) 2021 Marco Carrarini.
50 Copyright (C) 2021-2022 Miloš Komarčević.
51 Copyright (C) 2021 Ralf Brown.
52 Copyright (C) 2021 wpferguson.
53 Copyright (C) 2022 Martin Bařinka.
54 Copyright (C) 2022 Nicolas Auffray.
55 Copyright (C) 2022 paolodepetrillo.
56 Copyright (C) 2022 Victor Forsiuk.
57 Copyright (C) 2023 Ricky Moon.
58 Copyright (C) 2024-2025 Guillaume Stutin.
59
60 darktable is free software: you can redistribute it and/or modify
61 it under the terms of the GNU General Public License as published by
62 the Free Software Foundation, either version 3 of the License, or
63 (at your option) any later version.
64
65 darktable is distributed in the hope that it will be useful,
66 but WITHOUT ANY WARRANTY; without even the implied warranty of
67 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
68 GNU General Public License for more details.
69
70 You should have received a copy of the GNU General Public License
71 along with darktable. If not, see <http://www.gnu.org/licenses/>.
72*/
73
74#include "common/image.h"
75#include "common/collection.h"
76#include "common/darktable.h"
77#include "common/debug.h"
78#include "common/dtpthread.h"
79#include "common/exif.h"
81#include "common/grouping.h"
82#include "common/history.h"
85#include "common/image_cache.h"
86#include "common/imageio.h"
89#include "common/mipmap_cache.h"
90#include "common/ratings.h"
91#include "common/tags.h"
92#include "common/undo.h"
93#include "common/selection.h"
94#include "common/datetime.h"
95#include "control/conf.h"
96#include "control/control.h"
97#include "control/jobs.h"
98#include "develop/lightroom.h"
99#include "develop/develop.h"
100#include "win/filepath.h"
101#include <assert.h>
102#include <ctype.h>
103#include <math.h>
104#include <sqlite3.h>
105#include <stdlib.h>
106#include <string.h>
107#include <strings.h>
108#ifndef _WIN32
109#include <glob.h>
110#endif
111#include <glib/gstdio.h>
112
113static sqlite3_stmt *_image_altered_stmt = NULL;
114static dt_pthread_mutex_t _image_stmt_mutex;
116
117static inline void _image_stmt_mutex_ensure(void)
118{
119 if(g_once_init_enter(&_image_stmt_mutex_inited))
120 {
122 g_once_init_leave(&_image_stmt_mutex_inited, 1);
123 }
124}
125
127{
128 int32_t imgid;
129 gboolean before;
130 gboolean after;
132
139
146
153
154static void _pop_undo_execute(const int32_t imgid, const gboolean before, const gboolean after);
155static int32_t _image_duplicate_with_version(const int32_t imgid, const int32_t newversion, const gboolean undo,
156 const gboolean reload);
157static void _pop_undo(gpointer user_data, const dt_undo_type_t type, dt_undo_data_t data, const dt_undo_action_t action, GList **imgs);
158
159
160static void _copy_text_sidecar_if_present(const char *src_image_path, const char *dest_image_path);
161static void _move_text_sidecar_if_present(const char *src_image_path, const char *dest_image_path, const gboolean overwrite);
162
163// NULL terminated list of supported non-RAW extensions
164// const char *dt_non_raw_extensions[]
165// = { ".jpeg", ".jpg", ".pfm", ".hdr", ".exr", ".pxn", ".tif", ".tiff", ".png",
166// ".j2c", ".j2k", ".jp2", ".jpc", ".gif", ".jpc", ".jp2", ".bmp", ".dcm",
167// ".jng", ".miff", ".mng", ".pbm", ".pnm", ".ppm", ".pgm", NULL };
168gboolean dt_image_is_raw(const dt_image_t *img)
169{
170 return (img->flags & DT_IMAGE_RAW) != 0;
171}
172
173// LDR / HDR are flag-only predicates now: no filename-extension sniffing. The flags are set by the
174// decoding codec from the real file content (the buffer datatype is not a dynamic-range signal:
175// every raster decodes into a TYPE_FLOAT working buffer regardless of LDR/HDR), so these report what
176// was really loaded, not what the file name suggested. They are kept as a public API so callers
177// don't open-code the bitmask test.
178gboolean dt_image_is_ldr(const dt_image_t *img)
179{
180 return (img->flags & DT_IMAGE_LDR) != 0;
181}
182
183gboolean dt_image_is_hdr(const dt_image_t *img)
184{
185 return (img->flags & DT_IMAGE_HDR) != 0;
186}
187
189{
191}
192
193static void _image_set_monochrome_flag(const int32_t imgid, gboolean monochrome, gboolean undo_on)
194{
195 dt_image_t *img = NULL;
196 gboolean changed = FALSE;
197
198 img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
199 if(img)
200 {
201 const int mask_bw = dt_image_monochrome_flags(img);
203
204 if((!monochrome) && (mask_bw & DT_IMAGE_MONOCHROME_PREVIEW))
205 {
206 // wanting it to be color found preview
207 img = dt_image_cache_get(darktable.image_cache, imgid, 'w');
209 changed = TRUE;
210 }
211 if(monochrome && ((mask_bw == 0) || (mask_bw == DT_IMAGE_MONOCHROME_PREVIEW)))
212 {
213 // wanting monochrome and found color or just preview without workflow activation
214 img = dt_image_cache_get(darktable.image_cache, imgid, 'w');
216 changed = TRUE;
217 }
218 if(changed)
219 {
220 const int mask = dt_image_monochrome_flags(img);
222
223 if(undo_on)
224 {
226 undomono->imgid = imgid;
227 undomono->before = mask_bw;
228 undomono->after = mask;
229 dt_undo_record(darktable.undo, NULL, DT_UNDO_FLAGS, undomono, _pop_undo, g_free);
230 }
231 }
232 }
233 else
234 fprintf(stderr,"[image] could not dt_image_cache_get imgid %i\n", imgid);
235}
236
237void dt_image_set_monochrome_flag(const int32_t imgid, gboolean monochrome)
238{
239 _image_set_monochrome_flag(imgid, monochrome, TRUE);
240}
241
242static void _pop_undo_execute(const int32_t imgid, const gboolean before, const gboolean after)
243{
244 _image_set_monochrome_flag(imgid, after, FALSE);
245}
246
247static gboolean _image_matrix_has_data(const float *matrix, const int count)
248{
249 float sum = 0.0f;
250 for(int i = 0; i < count; i++)
251 {
252 if(!isfinite(matrix[i])) return FALSE;
253 sum += fabsf(matrix[i]);
254 }
255 return sum > 0.0f;
256}
257
259{
260 // Whether a camera input matrix (and the white balance / color calibration it feeds) applies
261 // depends only on the colorimetry axis, never on the mosaic axis: an already-demosaiced raw
262 // (sRAW / linear DNG, e.g. DxO PureRAW) still carries raw colorimetry and an embedded matrix
263 // and must get matrix correction. A previous `S_RAW && dsc.filters == 0 -> FALSE` short-circuit
264 // wrongly excluded exactly those files, leaving white balance and color calibration disabled
265 // (green renders, issue #729).
266 if(!(img->flags & (DT_IMAGE_RAW | DT_IMAGE_S_RAW))) return FALSE;
267 if(img->flags & DT_IMAGE_MONOCHROME) return FALSE;
268
269 const gboolean has_d65 = _image_matrix_has_data(img->d65_color_matrix, 9);
270 const gboolean has_adobe = _image_matrix_has_data(&img->adobe_XYZ_to_CAM[0][0], 9);
271
272 return (has_d65 || has_adobe) ? TRUE : FALSE;
273}
274
280
285
286/* ------------------------------------------------------------------------------------------
287 * Canonical image-type API. See image.h and src/doc/image-type-detection.md.
288 * Each predicate tests exactly one independent flag fact; no filename sniffing here.
289 * ------------------------------------------------------------------------------------------ */
290
292{
293 return (img->flags & DT_IMAGE_BUFFER_RESOLVED) == 0;
294}
295
296gboolean dt_image_is_sraw(const dt_image_t *img)
297{
298 return (img->flags & DT_IMAGE_S_RAW) != 0;
299}
300
302{
303 return (img->flags & DT_IMAGE_MOSAIC) != 0;
304}
305
307{
308 // both mosaiced raw and already-demosaiced sRAW/linear-DNG carry raw colorimetry
309 return (img->flags & (DT_IMAGE_RAW | DT_IMAGE_S_RAW)) != 0;
310}
311
313{
314 const gboolean resolved = (img->flags & DT_IMAGE_BUFFER_RESOLVED) != 0;
315 const gboolean raw_colorimetry = (img->flags & (DT_IMAGE_RAW | DT_IMAGE_S_RAW)) != 0;
316
317 // Raw colorimetry takes precedence over the LDR/HDR bit-depth flags: a float mosaiced raw
318 // is flagged both RAW and HDR but must still be treated as a mosaiced raw, never as RGB HDR.
319 if(raw_colorimetry)
320 {
321 // Once decoded, the mosaic bit is authoritative and tells a mosaiced raw apart from an
322 // already-demosaiced raw (sRAW / linear DNG).
323 if(resolved)
325
326 // Provisional (not decoded yet, or a database predating DT_IMAGE_BUFFER_RESOLVED):
327 // DT_IMAGE_S_RAW is only ever set by a codec on a real decode (the extension detector
328 // never sets it), so it reliably means an already-demosaiced raw even without the resolved
329 // bit. Otherwise assume the common case of a mosaiced sensor raw; dt_image_buffer_resolve_flags()
330 // corrects the guess on first decode.
331 if((img->flags & DT_IMAGE_S_RAW) && !(img->flags & DT_IMAGE_RAW))
334 }
335
338
340}
341
343{
345}
346
348{
349 switch(klass)
350 {
351 case DT_IMAGE_PIPE_MOSAIC_RAW: return "mosaic-raw";
352 case DT_IMAGE_PIPE_LINEAR_RAW: return "linear-raw";
353 case DT_IMAGE_PIPE_RGB_LDR: return "rgb-ldr";
354 case DT_IMAGE_PIPE_RGB_HDR: return "rgb-hdr";
356 default: return "unknown";
357 }
358}
359
361{
362 if(IS_NULL_PTR(img)) return;
363
364 // The decoded buffer descriptor is the authoritative source for the mosaic axis.
365 if(img->dsc.filters != 0u)
366 img->flags |= DT_IMAGE_MOSAIC;
367 else
368 img->flags &= ~DT_IMAGE_MOSAIC;
369
370 // The LDR/HDR axis is NOT derivable from the buffer datatype: every raster codec decodes into a
371 // TYPE_FLOAT working buffer in RAM regardless of the file's real dynamic range (sRGB JPEG/PNG/WebP
372 // all land as float just like Radiance/PFM/EXR). The decoding codec is the authoritative source
373 // and has already set DT_IMAGE_LDR / DT_IMAGE_HDR from the actual file content, so we must not
374 // override it here from the datatype — doing so re-flagged every display-referred raster as HDR.
375 //
376 // The only normalization we still own is the raw-sensor case: mosaiced / sRAW integer data is
377 // scene-linear sensor data, neither display LDR nor HDR. A float raw (e.g. HDRMerge, legacy float
378 // DNG) is left flagged HDR exactly as the raw codec set it.
380 img->flags &= ~(DT_IMAGE_LDR | DT_IMAGE_HDR);
381
383}
384
386{
387 if(IS_NULL_PTR(img)) return;
388 // Never clobber a descriptor that a codec has already produced.
389 if(img->flags & DT_IMAGE_BUFFER_RESOLVED) return;
390
391 switch(dt_image_pipe_class(img))
392 {
394 img->dsc.channels = 4; img->dsc.datatype = TYPE_UINT8; img->dsc.filters = 0u; img->dsc.cst = IOP_CS_RGB;
395 break;
397 img->dsc.channels = 4; img->dsc.datatype = TYPE_FLOAT; img->dsc.filters = 0u; img->dsc.cst = IOP_CS_RGB;
398 break;
400 // filters is unknown until decode; leave 0 as a placeholder (the class is carried by
401 // flags, not by this provisional filters value).
402 img->dsc.channels = 1; img->dsc.datatype = TYPE_UINT16; img->dsc.filters = 0u; img->dsc.cst = IOP_CS_RAW;
403 break;
405 img->dsc.channels = 4; img->dsc.datatype = TYPE_FLOAT; img->dsc.filters = 0u; img->dsc.cst = IOP_CS_RAW;
406 break;
408 default:
409 return; // nothing reliable to seed
410 }
412}
413
415{
416 switch(type)
417 {
418 case TYPE_FLOAT:
419 return "float";
420 case TYPE_UINT16:
421 return "uint16";
422 case TYPE_UINT8:
423 return "uint8";
424 case TYPE_UNKNOWN:
425 default:
426 return "unknown";
427 }
428}
429
430static const char *_image_colorspace_to_string(const dt_image_colorspace_t colorspace)
431{
432 switch(colorspace)
433 {
435 return "sRGB";
437 return "AdobeRGB";
439 default:
440 return "none";
441 }
442}
443
444void dt_image_print_debug_info(const dt_image_t *img, const char *context)
445{
446 if(IS_NULL_PTR(img)) return;
447
448 const char *ctx = context ? context : "image";
449 const dt_iop_buffer_dsc_t *dsc = &img->dsc;
450 const gboolean is_raw = dt_image_is_raw(img);
451 const gboolean is_ldr = dt_image_is_ldr(img);
452 const gboolean is_hdr = dt_image_is_hdr(img);
453 const gboolean is_mono = dt_image_is_monochrome(img);
454 const gboolean mono_workflow = dt_image_use_monochrome_workflow(img);
455 const int mono_flags = dt_image_monochrome_flags(img);
456 const gboolean mosaic = dsc->filters != 0u;
457 const gboolean xtrans = dsc->filters == 9u;
458 const gboolean bayer = mosaic && !xtrans;
459 const size_t bpp = dsc->bpp;
460 int bit_depth = 0;
461
462 switch(dsc->datatype)
463 {
464 case TYPE_FLOAT:
465 bit_depth = 32;
466 break;
467 case TYPE_UINT16:
468 bit_depth = 16;
469 break;
470 case TYPE_UINT8:
471 bit_depth = 8;
472 break;
473 case TYPE_UNKNOWN:
474 default:
475 if(dsc->channels != 0)
476 bit_depth = (int)(bpp * 8 / dsc->channels);
477 break;
478 }
479
480 const unsigned int flags = (unsigned int)img->flags;
481
483 "[image debug] %s id=%d filename='%s' fullpath='%s'\n",
484 ctx, img->id, img->filename, img->fullpath);
486 "[image debug] %s size=%dx%d crop=%dx%d+%d+%d orientation=%d psize=%dx%d pixel_aspect=%.6f\n",
487 ctx, img->width, img->height, img->crop_width, img->crop_height, img->crop_x, img->crop_y,
488 img->orientation, img->p_width, img->p_height, img->pixel_aspect_ratio);
490 "[image debug] %s flags=0x%08x raw=%d non_raw=%d ldr=%d hdr=%d sraw=%d 4bayer=%d mosaic=%d xtrans=%d "
491 "bayer=%d mono=%d mono_flags=0x%x mono_workflow=%d mono_preview=%d mono_bayer=%d bw=%d bw_flow=%d "
492 "local_copy=%d has_txt=%d has_wav=%d addl_dng=%d auto_presets=%d no_legacy_presets=%d rejected=%d remove=%d "
493 "has_localcopy=%d has_audio=%d is_hdr_field=%d\n",
494 ctx, flags, is_raw, !is_raw, is_ldr, is_hdr,
495 (flags & DT_IMAGE_S_RAW) != 0, (flags & DT_IMAGE_4BAYER) != 0, mosaic, xtrans, bayer, is_mono,
496 mono_flags, mono_workflow, (flags & DT_IMAGE_MONOCHROME_PREVIEW) != 0,
497 (flags & DT_IMAGE_MONOCHROME_BAYER) != 0, img->is_bw, img->is_bw_flow,
501 img->has_localcopy, img->has_audio, img->is_hdr);
503 "[image debug] %s buf: channels=%u datatype=%s bit_depth=%d bpp=%" G_GSIZE_FORMAT " filters=%u cst=%d colorspace=%s "
504 "processed_max=[%.4f %.4f %.4f %.4f]\n",
505 ctx, dsc->channels, _image_buf_type_to_string(dsc->datatype), bit_depth, bpp, dsc->filters, dsc->cst,
507 dsc->processed_maximum[2], dsc->processed_maximum[3]);
509 "[image debug] %s colorspace=enum:%s d65_color_matrix=[%.6f %.6f %.6f %.6f %.6f %.6f %.6f %.6f %.6f]\n",
511 img->d65_color_matrix[2], img->d65_color_matrix[3], img->d65_color_matrix[4], img->d65_color_matrix[5],
512 img->d65_color_matrix[6], img->d65_color_matrix[7], img->d65_color_matrix[8]);
514 "[image debug] %s raw: black=%u separate=[%u %u %u %u] white=%u rawprepare=[%u %u]\n",
519 "[image debug] %s class=%s state=%s needs_rawprepare=%d needs_demosaic=%d is_mosaiced=%d is_sraw=%d has_matrix=%d\n",
521 dt_image_pipe_class_is_provisional(img) ? "provisional" : "resolved",
524}
525
526const char *dt_image_film_roll_name(const char *path)
527{
528 const char *folder = path + strlen(path);
529 const int numparts = CLAMPS(dt_conf_get_int("show_folder_levels"), 1, 5);
530 int count = 0;
531 while(folder > path)
532 {
533
534#ifdef _WIN32
535 // in Windows, both \ and / can be folder separator
536 if(*folder == G_DIR_SEPARATOR || *folder == '/')
537#else
538 if(*folder == G_DIR_SEPARATOR)
539#endif
540
541 if(++count >= numparts)
542 {
543 ++folder;
544 break;
545 }
546 --folder;
547 }
548 return folder;
549}
550
551void dt_image_film_roll_directory(const dt_image_t *img, char *pathname, size_t pathname_len)
552{
553 sqlite3_stmt *stmt;
554 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT folder FROM main.film_rolls WHERE id = ?1",
555 -1, &stmt, NULL);
556 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, img->film_id);
557 if(sqlite3_step(stmt) == SQLITE_ROW)
558 {
559 const char *f = (char *)sqlite3_column_text(stmt, 0);
560 g_strlcpy(pathname, f, pathname_len);
561 }
562 sqlite3_finalize(stmt);
563 pathname[pathname_len - 1] = '\0';
564}
565
566
567void dt_image_film_roll(const dt_image_t *img, char *pathname, size_t pathname_len)
568{
569 if(img->film_id < 0)
570 {
571 g_strlcpy(pathname, _("orphaned image"), pathname_len);
572 return;
573 }
574
575 sqlite3_stmt *stmt;
576 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT folder FROM main.film_rolls WHERE id = ?1",
577 -1, &stmt, NULL);
578 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, img->film_id);
579 if(sqlite3_step(stmt) == SQLITE_ROW)
580 {
581 const char *f = (char *)sqlite3_column_text(stmt, 0);
582 const char *c = dt_image_film_roll_name(f);
583 g_strlcpy(pathname, c, pathname_len);
584 }
585 else
586 {
587 g_strlcpy(pathname, _("orphaned image"), pathname_len);
588 }
589 sqlite3_finalize(stmt);
590 pathname[pathname_len - 1] = '\0';
591}
592
594{
595 // Write sidecars when the setting is absent, consistently with the default exposed in the preferences.
596 gboolean res = TRUE;
597 const char *config = dt_conf_get_string_const("write_sidecar_files");
598 if(!IS_NULL_PTR(config))
599 {
600 res = FALSE;
601
602 // Darktable > 3.6
603 if(!strcmp(config, "after edit") || !strcmp(config, "on import") || !strcmp(config, "always")) res = TRUE;
604
605 // Darktable < 3.8 and Ansel
606 else if(!strcmp(config, "TRUE"))
607 res = TRUE;
608 }
609
610 // sanitize keys:
611 dt_conf_set_string("write_sidecar_files", (res) ? "TRUE" : "FALSE");
612
613 return res;
614}
615
616gboolean dt_image_safe_remove(const int32_t imgid)
617{
618 // always safe to remove if we do not have .xmp
619 if(!dt_image_get_xmp_mode()) return TRUE;
620
621 // check whether the original file is accessible
622 char pathname[PATH_MAX] = { 0 };
623 gboolean from_cache = TRUE;
624
625 dt_image_full_path(imgid, pathname, sizeof(pathname), &from_cache, __FUNCTION__);
626
627 if(!from_cache)
628 return TRUE;
629
630 else
631 {
632 // finally check if we have a .xmp for the local copy. If no modification done on the local copy it is safe
633 // to remove.
634 g_strlcat(pathname, ".xmp", sizeof(pathname));
635 return !g_file_test(pathname, G_FILE_TEST_EXISTS);
636 }
637}
638
640 size_t pathname_len, gboolean force_cache)
641{
642 if(IS_NULL_PTR(img) || IS_NULL_PTR(pathname)) return DT_IMAGE_PATH_NONE;
643 pathname[0] = '\0';
644
645 // Start with local copies as file I/O will be better if we have a choice
646 if(img->flags & DT_IMAGE_LOCAL_COPY || force_cache)
647 {
648 if(img->local_copy_path[0] && g_file_test(img->local_copy_path, G_FILE_TEST_EXISTS))
649 {
650 g_strlcpy(pathname, img->local_copy_path, pathname_len);
652 }
653
654 if(img->local_copy_legacy_path[0] && g_file_test(img->local_copy_legacy_path, G_FILE_TEST_EXISTS))
655 {
656 g_strlcpy(pathname, img->local_copy_legacy_path, pathname_len);
658 }
659
660 // Local copy flag might be stale (cache cleared, moved file, etc.). If local copy is missing and
661 // we are not explicitly forcing the cache, fall back to the original image.
662 if(!force_cache && img->fullpath[0] && g_file_test(img->fullpath, G_FILE_TEST_EXISTS))
663 {
664 g_strlcpy(pathname, img->fullpath, pathname_len);
666 }
667 }
668 // Forcing the cache should not consider the original image
669 else
670 {
671 if(img->fullpath[0] && g_file_test(img->fullpath, G_FILE_TEST_EXISTS))
672 {
673 g_strlcpy(pathname, img->fullpath, pathname_len);
675 }
676 }
677
678 // In case the local copy flag was not set properly, but the original file failed
679 // last-chance attempt at getting a possibly forgotten local copy to have some input
680 if(img->local_copy_path[0] && g_file_test(img->local_copy_path, G_FILE_TEST_EXISTS))
681 {
682 g_strlcpy(pathname, img->local_copy_path, pathname_len);
684 }
685
686 return DT_IMAGE_PATH_NONE;
687}
688
698void dt_image_full_path(const int32_t imgid, char *pathname, size_t pathname_len, gboolean *from_cache, const char *calling_func)
699{
700 if(imgid < 0) return;
702 const gboolean prefer_cache = (from_cache && *from_cache);
703
704 const dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
705 if(img)
706 {
707 // Preserve legacy semantics: `from_cache = TRUE` means "try local copy first, then fall back to original",
708 // not "only local copy".
709 if(prefer_cache)
710 {
711 source = dt_image_choose_input_path(img, pathname, pathname_len, TRUE);
712 if(source == DT_IMAGE_PATH_NONE)
713 source = dt_image_choose_input_path(img, pathname, pathname_len, FALSE);
714 }
715 else
716 {
717 source = dt_image_choose_input_path(img, pathname, pathname_len, FALSE);
718 }
720 }
721
722 if(from_cache)
723 *from_cache = (source == DT_IMAGE_PATH_LOCAL_COPY
725
726}
727
728void dt_image_local_copy_paths_from_fullpath(const char *fullpath, int32_t imgid, char *local_copy_path,
729 size_t local_copy_len, char *local_copy_legacy_path,
730 size_t local_copy_legacy_len)
731{
732 if(local_copy_path) local_copy_path[0] = '\0';
733 if(local_copy_legacy_path) local_copy_legacy_path[0] = '\0';
734 if(IS_NULL_PTR(fullpath) || !*fullpath || imgid <= 0 || IS_NULL_PTR(local_copy_path) || IS_NULL_PTR(local_copy_legacy_path)) return;
735
736 char *md5_filename = g_compute_checksum_for_string(G_CHECKSUM_MD5, fullpath, strlen(fullpath));
737 if(IS_NULL_PTR(md5_filename)) return;
738
739 char cachedir[PATH_MAX] = { 0 };
740 dt_loc_get_user_cache_dir(cachedir, sizeof(cachedir));
741
742 const char *c = fullpath + strlen(fullpath);
743 while(*c != '.' && c > fullpath) c--;
744
745 // cache filename old format: <cachedir>/img-<id>-<MD5>.<ext>
746 // for upward compatibility we check for the old name, if found we return it
747 snprintf(local_copy_legacy_path, local_copy_legacy_len, "%s/img-%d-%s%s", cachedir, imgid, md5_filename, c);
748
749 // cache filename format: <cachedir>/img-<MD5>.<ext>
750 snprintf(local_copy_path, local_copy_len, "%s/img-%s%s", cachedir, md5_filename, c);
751
752 dt_free(md5_filename);
753}
754
755void dt_image_path_append_version_no_db(int version, char *pathname, size_t pathname_len)
756{
757 // the "first" instance (version zero) does not get a version suffix
758 if(version > 0)
759 {
760 // add version information:
761 char *filename = g_strdup(pathname);
762
763 char *c = pathname + strlen(pathname);
764 while(*c != '.' && c > pathname) c--;
765 snprintf(c, pathname + pathname_len - c, "_%02d", version);
766 c = pathname + strlen(pathname);
767 char *c2 = filename + strlen(filename);
768 while(*c2 != '.' && c2 > filename) c2--;
769 g_strlcpy(c, c2, pathname + pathname_len - c);
770 dt_free(filename);
771 }
772}
773
774void dt_image_path_append_version(const int32_t imgid, char *pathname, size_t pathname_len)
775{
776 // get duplicate suffix
777 int version = 0;
778 sqlite3_stmt *stmt;
779 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT version FROM main.images WHERE id = ?1", -1,
780 &stmt, NULL);
781 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
782
783 if(sqlite3_step(stmt) == SQLITE_ROW) version = sqlite3_column_int(stmt, 0);
784 sqlite3_finalize(stmt);
785
786 dt_image_path_append_version_no_db(version, pathname, pathname_len);
787}
788
789void dt_image_print_exif(const dt_image_t *img, char *line, size_t line_len)
790{
791 char *exposure_str = dt_util_format_exposure(img->exif_exposure);
792
793 snprintf(line, line_len, "%s f/%.1f %dmm ISO %d", exposure_str, img->exif_aperture, (int)img->exif_focal_length,
794 (int)img->exif_iso);
795
796 dt_free(exposure_str);
797}
798
800{
801 return (flags & DT_IMAGE_REJECTED)
802 ? -1 // rejected image = -1
803 : (flags & DT_VIEW_RATINGS_MASK); // others = 0 .. 5
804}
805
810
811void dt_image_set_xmp_rating(dt_image_t *img, const int rating)
812{
813 // clean flags stars and rejected
815
816 if(rating == -2) // assuming that value -2 cannot be found
817 {
818 img->flags |= (DT_VIEW_RATINGS_MASK & 0);
819 }
820 else if(rating == -1)
821 {
822 img->flags |= DT_IMAGE_REJECTED;
823 }
824 else
825 {
826 img->flags |= (DT_VIEW_RATINGS_MASK & rating);
827 }
828}
829
830void dt_image_get_location(const int32_t imgid, dt_image_geoloc_t *geoloc)
831{
832 const dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
833 geoloc->longitude = img->geoloc.longitude;
834 geoloc->latitude = img->geoloc.latitude;
835 geoloc->elevation = img->geoloc.elevation;
837}
838
839static void _set_location(const int32_t imgid, const dt_image_geoloc_t *geoloc)
840{
841 /* fetch image from cache */
843
844 memcpy(&image->geoloc, geoloc, sizeof(dt_image_geoloc_t));
845
847}
848
849static void _set_datetime(const int32_t imgid, const char *datetime)
850{
851 /* fetch image from cache */
853
854 dt_datetime_exif_to_img(image, datetime);
855
857}
858
859static void _pop_undo(gpointer user_data, const dt_undo_type_t type, dt_undo_data_t data, const dt_undo_action_t action, GList **imgs)
860{
861 if(type == DT_UNDO_GEOTAG)
862 {
863 int i = 0;
864
865 for(GList *list = (GList *)data; list; list = g_list_next(list))
866 {
867 dt_undo_geotag_t *undogeotag = (dt_undo_geotag_t *)list->data;
868 const dt_image_geoloc_t *geoloc = (action == DT_ACTION_UNDO) ? &undogeotag->before : &undogeotag->after;
869
870 _set_location(undogeotag->imgid, geoloc);
871
872 *imgs = g_list_prepend(*imgs, GINT_TO_POINTER(undogeotag->imgid));
873 i++;
874 }
875 if(i > 1) dt_control_log((action == DT_ACTION_UNDO)
876 ? _("geo-location undone for %d images")
877 : _("geo-location re-applied to %d images"), i);
879 }
880 else if(type == DT_UNDO_DATETIME)
881 {
882 int i = 0;
883
884 for(GList *list = (GList *)data; list; list = g_list_next(list))
885 {
886 dt_undo_datetime_t *undodatetime = (dt_undo_datetime_t *)list->data;
887
888 _set_datetime(undodatetime->imgid, (action == DT_ACTION_UNDO)
889 ? undodatetime->before : undodatetime->after);
890
891 *imgs = g_list_prepend(*imgs, GINT_TO_POINTER(undodatetime->imgid));
892 i++;
893 }
894 if(i > 1) dt_control_log((action == DT_ACTION_UNDO)
895 ? _("date/time undone for %d images")
896 : _("date/time re-applied to %d images"), i);
897 }
898 else if(type == DT_UNDO_DUPLICATE)
899 {
901
902 if(action == DT_ACTION_UNDO)
903 {
904 // remove image
906 }
907 else
908 {
909 // restore image, note that we record the new imgid created while
910 // restoring the duplicate.
912 *imgs = g_list_prepend(*imgs, GINT_TO_POINTER(undo->new_imgid));
913 }
914 }
915 else if(type == DT_UNDO_FLAGS)
916 {
917 dt_undo_monochrome_t *undomono = (dt_undo_monochrome_t *)data;
918
919 const gboolean before = (action == DT_ACTION_UNDO) ? undomono->after : undomono->before;
920 const gboolean after = (action == DT_ACTION_UNDO) ? undomono->before : undomono->after;
921 _pop_undo_execute(undomono->imgid, before, after);
922 *imgs = g_list_prepend(*imgs, GINT_TO_POINTER(undomono->imgid));
923 }
924}
925
926static void _geotag_undo_data_free(gpointer data)
927{
928 GList *l = (GList *)data;
929 g_list_free_full(l, dt_free_gpointer);
930 l = NULL;
931}
932
933static void _image_set_location(GList *imgs, const dt_image_geoloc_t *geoloc, GList **undo, const gboolean undo_on)
934{
935 for(GList *images = imgs; images; images = g_list_next(images))
936 {
937 const int32_t imgid = GPOINTER_TO_INT(images->data);
938
939 if(undo_on)
940 {
941 dt_undo_geotag_t *undogeotag = (dt_undo_geotag_t *)malloc(sizeof(dt_undo_geotag_t));
942 undogeotag->imgid = imgid;
943 dt_image_get_location(imgid, &undogeotag->before);
944
945 memcpy(&undogeotag->after, geoloc, sizeof(dt_image_geoloc_t));
946
947 *undo = g_list_append(*undo, undogeotag);
948 }
949
950 _set_location(imgid, geoloc);
951 }
952}
953
954void dt_image_set_locations(const GList *imgs, const dt_image_geoloc_t *geoloc, const gboolean undo_on)
955{
956 if(imgs)
957 {
958 GList *undo = NULL;
960
961 _image_set_location((GList *)imgs, geoloc, &undo, undo_on);
962
963 if(undo_on)
964 {
967 }
968 }
969}
970
971void dt_image_set_location(const int32_t imgid, const dt_image_geoloc_t *geoloc, const gboolean undo_on, const gboolean group_on)
972{
973 GList *imgs = NULL;
974 if(imgid == UNKNOWN_IMAGE)
975 imgs = dt_act_on_get_images();
976 else
977 imgs = g_list_prepend(imgs, GINT_TO_POINTER(imgid));
978 if(group_on) dt_grouping_add_grouped_images(&imgs);
979 dt_image_set_locations(imgs, geoloc, undo_on);
980 g_list_free(imgs);
981 imgs = NULL;
982}
983
984static void _image_set_images_locations(const GList *img, const GArray *gloc,
985 GList **undo, const gboolean undo_on)
986{
987 int i = 0;
988 for(GList *imgs = (GList *)img; imgs; imgs = g_list_next(imgs))
989 {
990 const int32_t imgid = GPOINTER_TO_INT(imgs->data);
991 const dt_image_geoloc_t *geoloc = &g_array_index(gloc, dt_image_geoloc_t, i);
992 if(undo_on)
993 {
994 dt_undo_geotag_t *undogeotag = (dt_undo_geotag_t *)malloc(sizeof(dt_undo_geotag_t));
995 undogeotag->imgid = imgid;
996 dt_image_get_location(imgid, &undogeotag->before);
997
998 memcpy(&undogeotag->after, geoloc, sizeof(dt_image_geoloc_t));
999
1000 *undo = g_list_prepend(*undo, undogeotag);
1001 }
1002
1003 _set_location(imgid, geoloc);
1004 i++;
1005 }
1006}
1007
1008void dt_image_set_images_locations(const GList *imgs, const GArray *gloc, const gboolean undo_on)
1009{
1010 if(IS_NULL_PTR(imgs) || IS_NULL_PTR(gloc) || (g_list_length((GList *)imgs) != gloc->len))
1011 return;
1012 GList *undo = NULL;
1014
1015 _image_set_images_locations(imgs, gloc, &undo, undo_on);
1016
1017 if(undo_on)
1018 {
1021 }
1022}
1023
1024void dt_image_history_changed(const int32_t imgid, const gboolean refresh_filmstrip)
1025{
1026 if(imgid <= 0) return;
1027
1028 // Reload the cached image metadata from the DB. The caller has already persisted the new
1029 // history there; this refreshes history_items (the count of history entries) in the shared
1030 // image cache. history_items is the "altered" flag that the thumbnail regeneration uses to
1031 // pick raw processing over the (unedited) embedded JPEG, so a stale count makes edits and
1032 // rotations appear to have no effect on the thumbnail (issues #647, #861).
1034 if(image)
1036
1037 // Drop the stale rendered thumbnail. The mipmap cache regenerates purely on explicit removal,
1038 // never by comparing history hashes, so this is mandatory after any development change.
1040
1041 if(!darktable.gui) return;
1042
1044
1045 // The filmstrip is best-effort: refreshing it spawns an export thread that competes with the
1046 // realtime darkroom main preview. Darkroom write paths pass FALSE; lighttable ops pass TRUE.
1047 if(refresh_filmstrip)
1049}
1050
1051void dt_image_set_flip(const int32_t imgid, const dt_image_orientation_t orientation)
1052{
1053 // push new orientation to sql via additional history entry:
1054 const int iop_flip_MODVER = 2;
1055 const int num = dt_history_db_get_next_history_num(imgid);
1056 dt_history_db_write_history_item(imgid, num, "flip", &orientation, sizeof(int32_t), iop_flip_MODVER, 1,
1057 NULL, 0, 0, 0, "");
1058 dt_history_set_end(imgid, num + 1);
1059 dt_control_save_xmp(imgid);
1060
1061 // Refresh the cached metadata and thumbnail. Without this the stale history_items keeps the
1062 // image flagged unedited, so the rotated raw keeps showing its (unrotated) embedded JPEG and
1063 // the rotation appears to do nothing unless "never use embedded JPEG" is forced (issue #647).
1065}
1066
1068{
1069 // find the flip module -- the pointer stays valid until darktable shuts down
1070 static dt_iop_module_so_t *flip = NULL;
1071 if(IS_NULL_PTR(flip))
1072 {
1073 for(const GList *modules = darktable.iop; modules; modules = g_list_next(modules))
1074 {
1075 dt_iop_module_so_t *module = (dt_iop_module_so_t *)(modules->data);
1076 if(!strcmp(module->op, "flip"))
1077 {
1078 flip = module;
1079 break;
1080 }
1081 }
1082 }
1083
1085
1086 // db lookup flip params
1087 if(flip && flip->have_introspection && flip->get_p)
1088 {
1089 sqlite3_stmt *stmt;
1090 // clang-format off
1093 "SELECT op_params, enabled"
1094 " FROM main.history"
1095 " WHERE imgid=?1 AND operation='flip'"
1096 " ORDER BY num DESC LIMIT 1", -1,
1097 &stmt, NULL);
1098 // clang-format on
1099 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
1100 if(sqlite3_step(stmt) == SQLITE_ROW && sqlite3_column_int(stmt, 1) != 0)
1101 {
1102 // use introspection to get the orientation from the binary params blob
1103 const void *params = sqlite3_column_blob(stmt, 0);
1104 orientation = *((dt_image_orientation_t *)flip->get_p(params, "orientation"));
1105 }
1106 sqlite3_finalize(stmt);
1107 }
1108
1109 if(orientation == ORIENTATION_NULL)
1110 {
1111 const dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
1112 orientation = dt_image_orientation(img);
1114 }
1115
1116 return orientation;
1117}
1118
1119void dt_image_flip(const int32_t imgid, const int32_t cw)
1120{
1121 // this is light table only:
1123 if(darktable.develop->image_storage.id == imgid && cv->view((dt_view_t *)cv) == DT_VIEW_DARKROOM) return;
1124
1126 hist->imgid = imgid;
1128
1130
1131 if(cw == 1)
1132 {
1133 if(orientation & ORIENTATION_SWAP_XY)
1134 orientation ^= ORIENTATION_FLIP_Y;
1135 else
1136 orientation ^= ORIENTATION_FLIP_X;
1137 }
1138 else
1139 {
1140 if(orientation & ORIENTATION_SWAP_XY)
1141 orientation ^= ORIENTATION_FLIP_X;
1142 else
1143 orientation ^= ORIENTATION_FLIP_Y;
1144 }
1145 orientation ^= ORIENTATION_SWAP_XY;
1146
1147 if(cw == 2) orientation = ORIENTATION_NULL;
1148
1149 // dt_image_set_flip() writes the new orientation history entry and notifies the caches/GUI
1150 // (mipmap invalidation + thumbnail refresh) via dt_image_history_changed().
1151 dt_image_set_flip(imgid, orientation);
1152
1156}
1157
1158
1159int32_t dt_image_duplicate(const int32_t imgid)
1160{
1161 return dt_image_duplicate_with_version(imgid, -1);
1162}
1163
1164static int32_t _image_duplicate_with_version_ext(const int32_t imgid, const int32_t newversion)
1165{
1166 sqlite3_stmt *stmt;
1167 int32_t newid = -1;
1168
1169 // clang-format off
1171 "SELECT a.id"
1172 " FROM main.images AS a JOIN main.images AS b"
1173 " WHERE a.film_id = b.film_id AND a.filename = b.filename"
1174 " AND b.id = ?1 AND a.version = ?2"
1175 " ORDER BY a.id DESC",
1176 -1, &stmt, NULL);
1177 // clang-format on
1178 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
1179 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, newversion);
1180 if(sqlite3_step(stmt) == SQLITE_ROW)
1181 {
1182 newid = sqlite3_column_int(stmt, 0);
1183 }
1184 sqlite3_finalize(stmt);
1185
1186 // requested version is already present in DB, so we just return it
1187 if(newid != -1) return newid;
1188
1189 // clang-format off
1192 "INSERT INTO main.images"
1193 " (id, group_id, film_id, width, height, filename, maker, model, lens, exposure,"
1194 " aperture, iso, focal_length, focus_distance, datetime_taken, flags,"
1195 " output_width, output_height, crop, raw_parameters, raw_denoise_threshold,"
1196 " raw_auto_bright_threshold, raw_black, raw_maximum,"
1197 " license, sha1sum, orientation, histogram, lightmap,"
1198 " longitude, latitude, altitude, color_matrix, colorspace, version, max_version, history_end,"
1199 " aspect_ratio, exposure_bias, import_timestamp)"
1200 " SELECT NULL, group_id, film_id, width, height, filename, maker, model, lens,"
1201 " exposure, aperture, iso, focal_length, focus_distance, datetime_taken,"
1202 " flags, output_width, output_height, crop, raw_parameters, raw_denoise_threshold,"
1203 " raw_auto_bright_threshold, raw_black, raw_maximum,"
1204 " license, sha1sum, orientation, histogram, lightmap,"
1205 " longitude, latitude, altitude, color_matrix, colorspace, NULL, NULL, 0,"
1206 " aspect_ratio, exposure_bias, import_timestamp"
1207 " FROM main.images WHERE id = ?1",
1208 -1, &stmt, NULL);
1209 // clang-format on
1210 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
1211 sqlite3_step(stmt);
1212 sqlite3_finalize(stmt);
1213 // clang-format off
1215 "SELECT a.id, a.film_id, a.filename, b.max_version"
1216 " FROM main.images AS a JOIN main.images AS b"
1217 " WHERE a.film_id = b.film_id AND a.filename = b.filename AND b.id = ?1"
1218 " ORDER BY a.id DESC",
1219 -1, &stmt, NULL);
1220 // clang-format on
1221 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
1222
1223 int32_t film_id = UNKNOWN_IMAGE;
1224 int32_t max_version = -1;
1225 gchar *filename = NULL;
1226 if(sqlite3_step(stmt) == SQLITE_ROW)
1227 {
1228 newid = sqlite3_column_int(stmt, 0);
1229 film_id = sqlite3_column_int(stmt, 1);
1230 filename = g_strdup((gchar *)sqlite3_column_text(stmt, 2));
1231 max_version = sqlite3_column_int(stmt, 3);
1232 }
1233 sqlite3_finalize(stmt);
1234
1235 if(newid != -1)
1236 {
1237 // clang-format off
1239 "INSERT INTO main.color_labels (imgid, color)"
1240 " SELECT ?1, color FROM main.color_labels WHERE imgid = ?2",
1241 -1, &stmt, NULL);
1242 // clang-format on
1243 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newid);
1244 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
1245 sqlite3_step(stmt);
1246 sqlite3_finalize(stmt);
1247
1248 // clang-format off
1250 "INSERT INTO main.meta_data (id, key, value)"
1251 " SELECT ?1, key, value FROM main.meta_data WHERE id = ?2",
1252 -1, &stmt, NULL);
1253 // clang-format on
1254 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newid);
1255 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
1256 sqlite3_step(stmt);
1257 sqlite3_finalize(stmt);
1258
1259 // clang-format off
1261 "INSERT INTO main.tagged_images (imgid, tagid, position)"
1262 " SELECT ?1, tagid, "
1263 " (SELECT (IFNULL(MAX(position),0) & 0xFFFFFFFF00000000)"
1264 " FROM main.tagged_images)"
1265 " + (ROW_NUMBER() OVER (ORDER BY imgid) << 32)"
1266 " FROM main.tagged_images AS ti"
1267 " WHERE imgid = ?2",
1268 -1, &stmt, NULL);
1269 // clang-format on
1270 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newid);
1271 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
1272 sqlite3_step(stmt);
1273 sqlite3_finalize(stmt);
1274
1275 // clang-format off
1277 "INSERT INTO main.module_order (imgid, iop_list, version)"
1278 " SELECT ?1, iop_list, version FROM main.module_order WHERE imgid = ?2",
1279 -1, &stmt, NULL);
1280 // clang-format on
1281 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newid);
1282 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
1283 sqlite3_step(stmt);
1284 sqlite3_finalize(stmt);
1285
1286 // set version of new entry and max_version of all involved duplicates (with same film_id and filename)
1287 // this needs to happen before we do anything with the image cache, as version isn't updated through the cache
1288 const int32_t version = (newversion != -1) ? newversion : max_version + 1;
1289 max_version = (newversion != -1) ? MAX(max_version, newversion) : max_version + 1;
1290
1291 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "UPDATE main.images SET version=?1 WHERE id = ?2",
1292 -1, &stmt, NULL);
1293 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, version);
1294 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, newid);
1295 sqlite3_step(stmt);
1296 sqlite3_finalize(stmt);
1297
1298 // clang-format off
1300 "UPDATE main.images SET max_version=?1 WHERE film_id = ?2 AND filename = ?3", -1,
1301 &stmt, NULL);
1302 // clang-format on
1303 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, max_version);
1304 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, film_id);
1305 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 3, filename, -1, SQLITE_TRANSIENT);
1306 sqlite3_step(stmt);
1307 sqlite3_finalize(stmt);
1308
1309 dt_free(filename);
1310 }
1311 return newid;
1312}
1313
1314static int32_t _image_duplicate_with_version(const int32_t imgid, const int32_t newversion, const gboolean undo,
1315 const gboolean reload)
1316{
1317 const int32_t newid = _image_duplicate_with_version_ext(imgid, newversion);
1318
1319 if(newid != -1)
1320 {
1321 if(undo)
1322 {
1323 dt_undo_duplicate_t *dupundo = (dt_undo_duplicate_t *)malloc(sizeof(dt_undo_duplicate_t));
1324 dupundo->orig_imgid = imgid;
1325 dupundo->version = newversion;
1326 dupundo->new_imgid = newid;
1328 }
1329
1330 // make sure that the duplicate doesn't have some magic darktable| tags
1331 if(dt_tag_detach_by_string("darktable|changed", newid, FALSE, FALSE)
1332 || dt_tag_detach_by_string("darktable|exported", newid, FALSE, FALSE))
1334
1335 const dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
1336 const int grpid = img->group_id;
1338 dt_grouping_add_to_group(grpid, newid);
1339
1340 // Reloading here exposes newid to the lighttable grid: it becomes eligible for thumbnail
1341 // generation immediately, against whatever history it has *right now*. Callers still about
1342 // to write history onto it (duplicate-with-history, style-on-duplicate) must skip this and
1343 // reload themselves once that is done -- see dt_image_duplicate_no_reload().
1344 if(reload)
1346 }
1347 return newid;
1348}
1349
1350int32_t dt_image_duplicate_with_version(const int32_t imgid, const int32_t newversion)
1351{
1352 return _image_duplicate_with_version(imgid, newversion, TRUE, TRUE);
1353}
1354
1355int32_t dt_image_duplicate_no_reload(const int32_t imgid)
1356{
1357 return _image_duplicate_with_version(imgid, -1, TRUE, FALSE);
1358}
1359
1360void dt_image_remove(const int32_t imgid)
1361{
1362 // if a local copy exists, remove it
1363
1364 if(dt_image_local_copy_reset(imgid)) return;
1365
1366 sqlite3_stmt *stmt;
1367 const dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
1369
1370 // make sure we remove from the cache first, or else the cache will look for imgid in sql
1372
1374 // due to foreign keys added in db version 33,
1375 // all entries from tables having references to the images are deleted as well
1376 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.images WHERE id = ?1", -1, &stmt,
1377 NULL);
1378 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
1379 sqlite3_step(stmt);
1381 "DELETE FROM main.meta_data WHERE id = ?1", -1, &stmt, NULL);
1382 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
1383 sqlite3_step(stmt);
1384 sqlite3_finalize(stmt);
1385
1386 // also clear all thumbnails in mipmap_cache.
1388}
1389
1390uint32_t dt_image_altered(const int32_t imgid)
1391{
1392 uint32_t found_it = 0;
1393
1397 {
1399 "SELECT COUNT(imgid) FROM main.history WHERE imgid = ?1", -1,
1400 &_image_altered_stmt, NULL);
1401 }
1402 sqlite3_stmt *stmt = _image_altered_stmt;
1403 sqlite3_reset(stmt);
1404 sqlite3_clear_bindings(stmt);
1405 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
1406 if(sqlite3_step(stmt) == SQLITE_ROW)
1407 found_it = sqlite3_column_int(stmt, 0);
1408
1410
1411 return found_it;
1412}
1413
1425
1426#ifndef _WIN32
1427static int _valid_glob_match(const char *const name, size_t offset)
1428{
1429 // verify that the name matched by glob() is a valid sidecar name by checking whether we have an underscore
1430 // followed by a sequence of digits followed by a period at the given offset in the name
1431 if(strlen(name) < offset || name[offset] != '_')
1432 return FALSE;
1433 size_t i;
1434 for(i = offset+1; name[i] && name[i] != '.'; i++)
1435 {
1436 if(!isdigit(name[i]))
1437 return FALSE;
1438 }
1439 return name[i] == '.';
1440}
1441#endif /* !_WIN32 */
1442
1443GList* dt_image_find_xmps(const char* filename)
1444{
1445 // find all duplicates of an image by looking for all possible sidecars for the file: file.ext.xmp, file_NN.ext.xmp,
1446 // file_NNN.ext.xmp, and file_NNNN.ext.xmp
1447 // because a glob() needs to scan the entire directory, we minimize work for large directories by doing a single
1448 // glob which might generate false matches (if the image name contains an underscore followed by a digit) and
1449 // filter out the false positives afterward
1450#ifndef _WIN32
1451 // start by locating the extension, which we'll be referencing multiple times
1452 const size_t fn_len = strlen(filename);
1453 const char* ext = strrchr(filename,'.'); // find last dot
1454 if(IS_NULL_PTR(ext)) ext = filename;
1455 const size_t ext_offset = ext - filename;
1456
1457 gchar pattern[PATH_MAX] = { 0 };
1458 GList* files = NULL;
1459
1460 // check for file.ext.xmp
1461 static const char xmp[] = ".xmp";
1462 const size_t xmp_len = strlen(xmp);
1463 // concatenate filename and sidecar extension
1464 g_strlcpy(pattern, filename, sizeof(pattern));
1465 g_strlcpy(pattern + fn_len, xmp, sizeof(pattern) - fn_len);
1466 if(dt_util_test_image_file(pattern))
1467 {
1468 // the default sidecar exists, is readable and is a regular file with lenght > 0, so add it to the list
1469 files = g_list_prepend(files, g_strdup(pattern));
1470 }
1471
1472 // now collect all file_N*N.ext.xmp matches
1473 static const char glob_pattern[] = "_[0-9]*[0-9]";
1474 const size_t gp_len = strlen(glob_pattern);
1475 if(fn_len + gp_len + xmp_len < sizeof(pattern)) // enough space to build pattern?
1476 {
1477 // add GLOB.ext.xmp to the root of the basename
1478 g_strlcpy(pattern + ext_offset, glob_pattern, sizeof(pattern) - fn_len);
1479 g_strlcpy(pattern + ext_offset + gp_len, ext, sizeof(pattern) - ext_offset - gp_len);
1480 g_strlcpy(pattern + fn_len + gp_len, xmp, sizeof(pattern) - fn_len - gp_len);
1481 glob_t globbuf;
1482 if(!glob(pattern, 0, NULL, &globbuf))
1483 {
1484 // for each match of the pattern
1485 for(size_t i = 0; i < globbuf.gl_pathc; i++)
1486 {
1487 if(_valid_glob_match(globbuf.gl_pathv[i], ext_offset))
1488 {
1489 // it's not a false positive, so add it to the list of sidecars
1490 files = g_list_prepend(files, g_strdup(globbuf.gl_pathv[i]));
1491 }
1492 }
1493 globfree(&globbuf);
1494 }
1495 }
1496 // we built the list in reverse order for speed, so un-reverse it
1497 return g_list_reverse(files);
1498
1499#else
1500 return win_image_find_duplicates(filename);
1501#endif
1502}
1503
1504// Search for duplicate's sidecar files and import them if found and not in DB yet
1505int dt_image_read_duplicates(const uint32_t id, const char *filename, const gboolean clear_selection)
1506{
1507 int count_xmps_processed = 0;
1508 gchar pattern[PATH_MAX] = { 0 };
1509
1510 GList *files = dt_image_find_xmps(filename);
1511
1512 // we store the xmp filename without version part in pattern to speed up string comparison later
1513 g_snprintf(pattern, sizeof(pattern), "%s.xmp", filename);
1514
1515 for(GList *file_iter = files; file_iter; file_iter = g_list_next(file_iter))
1516 {
1517 gchar *xmpfilename = file_iter->data;
1518 int version = -1;
1519
1520 // we need to get the version number of the sidecar filename
1521 if(!strncmp(xmpfilename, pattern, sizeof(pattern)))
1522 {
1523 // this is an xmp file without version number which corresponds to version 0
1524 version = 0;
1525 }
1526 else
1527 {
1528 // we need to derive the version number from the filename
1529
1530 gchar *c3 = xmpfilename + strlen(xmpfilename)
1531 - 5; // skip over .xmp extension; position c3 at character before the '.'
1532 while(*c3 != '.' && c3 > xmpfilename)
1533 c3--; // skip over filename extension; position c3 is at character '.'
1534 gchar *c4 = c3;
1535 while(*c4 != '_' && c4 > xmpfilename) c4--; // move to beginning of version number
1536 c4++;
1537
1538 gchar *idfield = g_strndup(c4, c3 - c4);
1539
1540 version = atoi(idfield);
1541 dt_free(idfield);
1542 }
1543
1544 int newid = id;
1545 int grpid = -1;
1546
1547 if(count_xmps_processed == 0)
1548 {
1549 // this is the first xmp processed, just update the passed-in id
1550 sqlite3_stmt *stmt;
1553 "UPDATE main.images SET version=?1, max_version = ?1 WHERE id = ?2", -1, &stmt, NULL);
1554 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, version);
1555 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, id);
1556 sqlite3_step(stmt);
1557 sqlite3_finalize(stmt);
1558 }
1559 else
1560 {
1561 // create a new duplicate based on the passed-in id. Note that we do not call
1562 // dt_image_duplicate_with_version() as this version also set the group which
1563 // is using DT_IMAGE_CACHE_SAFE and so will write the .XMP. But we must avoid
1564 // this has the xmp for the duplicate is read just below.
1565 newid = _image_duplicate_with_version_ext(id, version);
1566 const dt_image_t *img = dt_image_cache_get(darktable.image_cache, id, 'r');
1567 grpid = img->group_id;
1569 }
1570 // make sure newid is not selected
1571 if(clear_selection) dt_selection_clear(darktable.selection);
1572
1574 (void)dt_exif_xmp_read(img, xmpfilename, 0);
1575 img->version = version;
1577
1578 if(grpid != -1)
1579 {
1580 // now it is safe to set the duplicate group-id
1581 dt_grouping_add_to_group(grpid, newid);
1583 }
1584
1585 count_xmps_processed++;
1586 }
1587
1588 g_list_free_full(files, dt_free_gpointer);
1589 files = NULL;
1590 return count_xmps_processed;
1591}
1592
1593static int32_t _image_import_internal(const int32_t film_id, const char *filename,
1594 gboolean lua_locking, gboolean raise_signals)
1595{
1596 char *normalized_filename = dt_util_normalize_path(filename);
1597 if(!normalized_filename || !dt_util_test_image_file(normalized_filename))
1598 {
1599 dt_free(normalized_filename);
1600 return 0;
1601 }
1602 const char *cc = normalized_filename + strlen(normalized_filename);
1603 for(; *cc != '.' && cc > normalized_filename; cc--)
1604 ;
1605 if(!strcasecmp(cc, ".dt") || !strcasecmp(cc, ".dttags") || !strcasecmp(cc, ".xmp"))
1606 {
1607 dt_free(normalized_filename);
1608 return 0;
1609 }
1610 char *ext = g_ascii_strdown(cc + 1, -1);
1611 int supported = 0;
1612 for(const char **i = dt_supported_extensions; !IS_NULL_PTR(*i); i++)
1613 if(!strcmp(ext, *i))
1614 {
1615 supported = 1;
1616 break;
1617 }
1618 if(!supported)
1619 {
1620 dt_free(normalized_filename);
1621 dt_free(ext);
1622 return 0;
1623 }
1624 int rc;
1625 sqlite3_stmt *stmt;
1626 // select from images; if found => return
1627 gchar *imgfname = g_path_get_basename(normalized_filename);
1628 int32_t id = dt_image_get_id(film_id, imgfname);
1629 if(id > UNKNOWN_IMAGE)
1630 {
1631 dt_control_log(_("Image %s is already in library and will not be re-imported.\n"), imgfname);
1632 dt_free(imgfname);
1633 dt_free(ext);
1634 dt_free(normalized_filename);
1635 return id;
1636 }
1637
1638 // also need to set the no-legacy bit, to make sure we get the right presets (new ones)
1639 uint32_t flags = 0;
1641 // and we set the type of image flag (from extension for now)
1642 gchar *extension = g_strrstr(imgfname, ".");
1644 // set the bits in flags that indicate if any of the extra files (.txt, .wav) are present
1645 char *extra_file = dt_image_get_audio_path_from_path(normalized_filename);
1646 if(extra_file)
1647 {
1649 dt_free(extra_file);
1650 }
1651 extra_file = dt_image_get_text_path_from_path(normalized_filename);
1652 if(extra_file)
1653 {
1655 dt_free(extra_file);
1656 }
1657
1658 //insert a v0 record (which may be updated later if no v0 xmp exists)
1659 // clang-format off
1662 "INSERT INTO main.images (id, film_id, filename, license, sha1sum, flags, version, "
1663 " max_version, history_end, position, import_timestamp)"
1664 " SELECT NULL, ?1, ?2, '', '', ?3, 0, 0, 0, (IFNULL(MAX(position),0) & 0xFFFFFFFF00000000) + (1 << 32), ?4 "
1665 " FROM images",
1666 -1, &stmt, NULL);
1667 // clang-format on
1668
1669 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id);
1670 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, imgfname, -1, SQLITE_TRANSIENT);
1673
1674 rc = sqlite3_step(stmt);
1675 if(rc != SQLITE_DONE)
1676 {
1677 fprintf(stderr, "sqlite3 error %d\n", rc);
1678 sqlite3_finalize(stmt);
1679 return 0;
1680 }
1681 sqlite3_finalize(stmt);
1682
1683 id = dt_image_get_id(film_id, imgfname);
1684
1685 // Try to find out if this should be grouped already.
1686 gchar *basename = g_strdup(imgfname);
1687 gchar *cc2 = basename + strlen(basename);
1688 for(; *cc2 != '.' && cc2 > basename; cc2--)
1689 ;
1690 *cc2 = '\0';
1691 gchar *sql_pattern = g_strconcat(basename, ".%", NULL);
1692 int group_id;
1693 // in case we are not a jpg check if we need to change group representative
1694 if(strcmp(ext, "jpg") != 0 && strcmp(ext, "jpeg") != 0)
1695 {
1696 sqlite3_stmt *stmt2;
1697 // clang-format off
1700 "SELECT group_id"
1701 " FROM main.images"
1702 " WHERE film_id = ?1 AND filename LIKE ?2 AND id = group_id", -1, &stmt2,
1703 NULL);
1704 // clang-format on
1705 DT_DEBUG_SQLITE3_BIND_INT(stmt2, 1, film_id);
1706 DT_DEBUG_SQLITE3_BIND_TEXT(stmt2, 2, sql_pattern, -1, SQLITE_TRANSIENT);
1707 // if we have a group already
1708 if(sqlite3_step(stmt2) == SQLITE_ROW)
1709 {
1710 int other_id = sqlite3_column_int(stmt2, 0);
1711 dt_image_t *other_img = dt_image_cache_get(darktable.image_cache, other_id, 'w');
1712 gchar *other_basename = g_strdup(other_img->filename);
1713 gchar *cc3 = other_basename + strlen(other_img->filename);
1714 for(; *cc3 != '.' && cc3 > other_basename; cc3--)
1715 ;
1716 ++cc3;
1717 gchar *ext_lowercase = g_ascii_strdown(cc3, -1);
1718 // if the group representative is a jpg, change group representative to this new imported image
1719 if(!strcmp(ext_lowercase, "jpg") || !strcmp(ext_lowercase, "jpeg"))
1720 {
1721 other_img->group_id = id;
1723 sqlite3_stmt *stmt3;
1726 "SELECT id FROM main.images WHERE group_id = ?1 AND id != ?1", -1, &stmt3, NULL);
1727 DT_DEBUG_SQLITE3_BIND_INT(stmt3, 1, other_id);
1728 while(sqlite3_step(stmt3) == SQLITE_ROW)
1729 {
1730 other_id = sqlite3_column_int(stmt3, 0);
1731 dt_image_t *group_img = dt_image_cache_get(darktable.image_cache, other_id, 'w');
1732 group_img->group_id = id;
1734 }
1735 group_id = id;
1736 sqlite3_finalize(stmt3);
1737 }
1738 else
1739 {
1741 group_id = other_id;
1742 }
1743 dt_free(ext_lowercase);
1744 dt_free(other_basename);
1745 }
1746 else
1747 {
1748 group_id = id;
1749 }
1750 sqlite3_finalize(stmt2);
1751 }
1752 else
1753 {
1754 sqlite3_stmt *stmt2;
1755 // clang-format off
1758 "SELECT group_id"
1759 " FROM main.images"
1760 " WHERE film_id = ?1 AND filename LIKE ?2 AND id != ?3", -1, &stmt2, NULL);
1761 // clang-format on
1762 DT_DEBUG_SQLITE3_BIND_INT(stmt2, 1, film_id);
1763 DT_DEBUG_SQLITE3_BIND_TEXT(stmt2, 2, sql_pattern, -1, SQLITE_TRANSIENT);
1764 DT_DEBUG_SQLITE3_BIND_INT(stmt2, 3, id);
1765 if(sqlite3_step(stmt2) == SQLITE_ROW)
1766 group_id = sqlite3_column_int(stmt2, 0);
1767 else
1768 group_id = id;
1769 sqlite3_finalize(stmt2);
1770 }
1773 "UPDATE main.images SET group_id = ?1 WHERE id = ?2",
1774 -1, &stmt, NULL);
1775 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, group_id);
1776 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, id);
1777 sqlite3_step(stmt);
1778 sqlite3_finalize(stmt);
1779
1780 // printf("[image_import] importing `%s' to img id %d\n", imgfname, id);
1781
1782 // lock as shortly as possible:
1784 img->group_id = group_id;
1785
1786 // read dttags and exif for database queries!
1787 (void)dt_exif_read(img, normalized_filename);
1788 char dtfilename[PATH_MAX] = { 0 };
1789 g_strlcpy(dtfilename, normalized_filename, sizeof(dtfilename));
1790 // dt_image_path_append_version(id, dtfilename, sizeof(dtfilename));
1791 g_strlcat(dtfilename, ".xmp", sizeof(dtfilename));
1792
1793 const int res = dt_exif_xmp_read(img, dtfilename, 0);
1794
1795 // write through to db, but not to xmp.
1797
1798 // read all sidecar files
1799 const int nb_xmp = dt_image_read_duplicates(id, normalized_filename, raise_signals);
1800
1801 if((res != 0) && (nb_xmp == 0))
1802 {
1803 const gboolean lr_xmp = dt_lightroom_import(id, NULL, TRUE);
1804 if(lr_xmp) dt_control_save_xmp(id);
1805 }
1806
1807 // add a tag with the file extension
1808 guint tagid = 0;
1809 char tagname[512];
1810 snprintf(tagname, sizeof(tagname), "darktable|format|%s", ext);
1811 dt_free(ext);
1812 dt_tag_new(tagname, &tagid);
1813 dt_tag_attach(tagid, id, FALSE, FALSE);
1814
1815 // make sure that there are no stale thumbnails left
1817
1818 //synch database entries to xmp
1819 if(dt_image_get_xmp_mode()) dt_image_synch_all_xmp(normalized_filename);
1820
1821 dt_free(imgfname);
1822 dt_free(basename);
1823 dt_free(sql_pattern);
1824 dt_free(normalized_filename);
1825
1826 if(raise_signals)
1827 {
1829 GList *imgs = g_list_prepend(NULL, GINT_TO_POINTER(id));
1831 }
1832
1833 // the following line would look logical with new_tags_set being the return value
1834 // from dt_tag_new above, but this could lead to too rapid signals, being able to lock up the
1835 // keywords side pane when trying to use it, which can lock up the whole dt GUI ..
1836 // if(new_tags_set) DT_DEBUG_CONTROL_SIGNAL_RAISE(darktable.signals,DT_SIGNAL_TAG_CHANGED);
1837 return id;
1838}
1839
1840int32_t dt_image_get_id_full_path(const gchar *filename)
1841{
1842 int32_t id = -1;
1843 gchar *dir = g_path_get_dirname(filename);
1844 gchar *file = g_path_get_basename(filename);
1845 sqlite3_stmt *stmt;
1846 // clang-format off
1848 "SELECT images.id"
1849 " FROM main.images, main.film_rolls"
1850 " WHERE film_rolls.folder = ?1"
1851 " AND images.film_id = film_rolls.id"
1852 " AND images.filename = ?2",
1853 -1, &stmt, NULL);
1854 // clang-format on
1855 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, dir, -1, SQLITE_STATIC);
1856 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, file, -1, SQLITE_STATIC);
1857 if(sqlite3_step(stmt) == SQLITE_ROW) id=sqlite3_column_int(stmt, 0);
1858 sqlite3_finalize(stmt);
1859 dt_free(dir);
1860 dt_free(file);
1861
1862 return id;
1863}
1864
1865int32_t dt_image_get_id(int32_t film_id, const gchar *filename)
1866{
1867 int32_t id = -1;
1868 sqlite3_stmt *stmt;
1870 "SELECT id FROM main.images WHERE film_id = ?1 AND filename = ?2",
1871 -1, &stmt, NULL);
1872 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id);
1873 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, filename, -1, SQLITE_TRANSIENT);
1874 if(sqlite3_step(stmt) == SQLITE_ROW) id=sqlite3_column_int(stmt, 0);
1875 sqlite3_finalize(stmt);
1876 return id;
1877}
1878
1879int32_t dt_image_import(const int32_t film_id, const char *filename, gboolean raise_signals)
1880{
1881 return _image_import_internal(film_id, filename, TRUE, raise_signals);
1882}
1883
1884int32_t dt_image_import_lua(const int32_t film_id, const char *filename)
1885{
1886 return _image_import_internal(film_id, filename, FALSE, TRUE);
1887}
1888
1890{
1891 memset(img, 0, sizeof(*img));
1892 img->width = img->height = 0;
1893 img->p_width = img->p_height = 0;
1894 img->crop_x = img->crop_y = img->crop_width = img->crop_height = 0;
1896
1898
1899 img->legacy_flip.legacy = 0;
1900 img->legacy_flip.user_flip = 0;
1901
1902 img->dsc = (dt_iop_buffer_dsc_t){ .channels = 0, .datatype = TYPE_UNKNOWN, .bpp = 0, .filters = 0u };
1903 img->film_id = UNKNOWN_IMAGE;
1904 img->group_id = UNKNOWN_IMAGE;
1905 img->group_members = 0;
1906 img->history_items = 0;
1907 img->history_hash = UINT64_MAX;
1908 img->mipmap_hash = 0; // don't default it to img->history_hash to not make it look like they are in sync
1909 img->self_hash = 0;
1910 img->flags = 0;
1911 img->id = UNKNOWN_IMAGE;
1912 img->version = -1;
1913 img->loader = LOADER_UNKNOWN;
1914 img->exif_inited = 0;
1916 dt_datetime_exif_to_img(img, "");
1917 memset(img->exif_maker, 0, sizeof(img->exif_maker));
1918 memset(img->exif_model, 0, sizeof(img->exif_model));
1919 memset(img->exif_lens, 0, sizeof(img->exif_lens));
1920 memset(img->camera_maker, 0, sizeof(img->camera_maker));
1921 memset(img->camera_model, 0, sizeof(img->camera_model));
1922 memset(img->camera_alias, 0, sizeof(img->camera_alias));
1923 memset(img->camera_makermodel, 0, sizeof(img->camera_makermodel));
1924 memset(img->camera_legacy_makermodel, 0, sizeof(img->camera_legacy_makermodel));
1925 memset(img->filename, 0, sizeof(img->filename));
1926 memset(img->fullpath, 0, sizeof(img->fullpath));
1927 memset(img->local_copy_path, 0, sizeof(img->local_copy_path));
1928 memset(img->local_copy_legacy_path, 0, sizeof(img->local_copy_legacy_path));
1929 memset(img->folder, 0, sizeof(img->folder));
1930 memset(img->filmroll, 0, sizeof(img->filmroll));
1931 memset(img->datetime, 0, sizeof(img->datetime));
1932 g_strlcpy(img->filename, "(unknown)", sizeof(img->filename));
1933 img->exif_crop = 1.0;
1934 img->exif_exposure = 0;
1935 img->exif_exposure_bias = NAN;
1936 img->exif_aperture = 0;
1937 img->exif_iso = 0;
1938 img->exif_focal_length = 0;
1939 img->exif_focus_distance = 0;
1940 img->geoloc.latitude = NAN;
1941 img->geoloc.longitude = NAN;
1942 img->geoloc.elevation = NAN;
1943 img->raw_black_level = 0;
1944 for(uint8_t i = 0; i < 4; i++) img->raw_black_level_separate[i] = 0;
1945 img->raw_white_point = 16384; // 2^14
1946 img->d65_color_matrix[0] = NAN;
1947 img->profile = NULL;
1948 img->profile_size = 0;
1950 img->fuji_rotation_pos = 0;
1951 img->pixel_aspect_ratio = 1.0f;
1952 img->wb_coeffs[0] = NAN;
1953 img->wb_coeffs[1] = NAN;
1954 img->wb_coeffs[2] = NAN;
1955 img->wb_coeffs[3] = NAN;
1956 img->usercrop[0] = img->usercrop[1] = 0;
1957 img->usercrop[2] = img->usercrop[3] = 1;
1958 img->dng_gain_maps = NULL;
1959 img->cache_entry = 0;
1960 img->color_labels = 0;
1961 img->rating = 0;
1962 img->has_localcopy = FALSE;
1963 img->has_audio = FALSE;
1964 img->is_bw = FALSE;
1965 img->is_bw_flow = FALSE;
1966 img->is_hdr = FALSE;
1967
1968 for(int k=0; k<4; k++)
1969 for(int i=0; i<3; i++)
1970 img->adobe_XYZ_to_CAM[k][i] = NAN;
1971}
1972
1974{
1975 if(!img->camera_maker[0] || !img->camera_model[0] || !img->camera_alias[0])
1976 {
1977 // We need to use the exif values, so let's get rawspeed to munge them
1979 img->camera_maker, sizeof(img->camera_maker),
1980 img->camera_model, sizeof(img->camera_model),
1981 img->camera_alias, sizeof(img->camera_alias));
1982 }
1983
1984 // Now we just create a makermodel by concatenation
1985 g_strlcpy(img->camera_makermodel, img->camera_maker, sizeof(img->camera_makermodel));
1986 const int len = strlen(img->camera_maker);
1987 img->camera_makermodel[len] = ' ';
1988 g_strlcpy(img->camera_makermodel+len+1, img->camera_model, sizeof(img->camera_makermodel)-len-1);
1989}
1990
1991int32_t dt_image_rename(const int32_t imgid, const int32_t filmid, const gchar *newname)
1992{
1993 // TODO: several places where string truncation could occur unnoticed
1994 int32_t result = -1;
1995 gchar oldimg[PATH_MAX] = { 0 };
1996 gchar newimg[PATH_MAX] = { 0 };
1997 gboolean from_cache = FALSE;
1998 dt_image_full_path(imgid, oldimg, sizeof(oldimg), &from_cache, __FUNCTION__);
1999 gchar *newdir = NULL;
2000
2001 sqlite3_stmt *film_stmt;
2002 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT folder FROM main.film_rolls WHERE id = ?1",
2003 -1, &film_stmt, NULL);
2004 DT_DEBUG_SQLITE3_BIND_INT(film_stmt, 1, filmid);
2005 if(sqlite3_step(film_stmt) == SQLITE_ROW) newdir = g_strdup((gchar *)sqlite3_column_text(film_stmt, 0));
2006 sqlite3_finalize(film_stmt);
2007
2008 gchar copysrcpath[PATH_MAX] = { 0 };
2009 gchar copydestpath[PATH_MAX] = { 0 };
2010 GFile *old = NULL, *new = NULL;
2011 if(newdir)
2012 {
2013 old = g_file_new_for_path(oldimg);
2014
2015 if(newname)
2016 {
2017 g_snprintf(newimg, sizeof(newimg), "%s%c%s", newdir, G_DIR_SEPARATOR, newname);
2018 new = g_file_new_for_path(newimg);
2019 // 'newname' represents the file's new *basename* -- it must not
2020 // refer to a file outside of 'newdir'.
2021 gchar *newBasename = g_file_get_basename(new);
2022 if(g_strcmp0(newname, newBasename) != 0)
2023 {
2024 g_object_unref(old);
2025 old = NULL;
2026 g_object_unref(new);
2027 new = NULL;
2028 }
2029 dt_free(newBasename);
2030 }
2031 else
2032 {
2033 gchar *imgbname = g_path_get_basename(oldimg);
2034 g_snprintf(newimg, sizeof(newimg), "%s%c%s", newdir, G_DIR_SEPARATOR, imgbname);
2035 new = g_file_new_for_path(newimg);
2036 dt_free(imgbname);
2037 }
2038 dt_free(newdir);
2039 }
2040
2041 if(new)
2042 {
2043 // get current local copy if any
2045 if(img)
2046 {
2047 dt_image_choose_input_path(img, copysrcpath, sizeof(copysrcpath), TRUE);
2049 }
2050
2051 // move image
2052 GError *moveError = NULL;
2053 gboolean moveStatus = g_file_move(old, new, 0, NULL, NULL, NULL, &moveError);
2054
2055 if(moveStatus)
2056 {
2057 _move_text_sidecar_if_present(oldimg, newimg, FALSE);
2058
2059 // statement for getting ids of the image to be moved and its duplicates
2060 sqlite3_stmt *duplicates_stmt;
2061 // clang-format off
2064 "SELECT id"
2065 " FROM main.images"
2066 " WHERE filename IN (SELECT filename FROM main.images WHERE id = ?1)"
2067 " AND film_id IN (SELECT film_id FROM main.images WHERE id = ?1)",
2068 -1, &duplicates_stmt, NULL);
2069 // clang-format on
2070
2071 // first move xmp files of image and duplicates
2072 GList *dup_list = NULL;
2073 DT_DEBUG_SQLITE3_BIND_INT(duplicates_stmt, 1, imgid);
2074 while(sqlite3_step(duplicates_stmt) == SQLITE_ROW)
2075 {
2076 const int32_t id = sqlite3_column_int(duplicates_stmt, 0);
2077 dup_list = g_list_prepend(dup_list, GINT_TO_POINTER(id));
2078 gchar oldxmp[PATH_MAX] = { 0 }, newxmp[PATH_MAX] = { 0 };
2079 g_strlcpy(oldxmp, oldimg, sizeof(oldxmp));
2080 g_strlcpy(newxmp, newimg, sizeof(newxmp));
2081 dt_image_path_append_version(id, oldxmp, sizeof(oldxmp));
2082 dt_image_path_append_version(id, newxmp, sizeof(newxmp));
2083 g_strlcat(oldxmp, ".xmp", sizeof(oldxmp));
2084 g_strlcat(newxmp, ".xmp", sizeof(newxmp));
2085
2086 GFile *goldxmp = g_file_new_for_path(oldxmp);
2087 GFile *gnewxmp = g_file_new_for_path(newxmp);
2088
2089 g_file_move(goldxmp, gnewxmp, 0, NULL, NULL, NULL, NULL);
2090
2091 g_object_unref(goldxmp);
2092 g_object_unref(gnewxmp);
2093 }
2094 sqlite3_finalize(duplicates_stmt);
2095
2096 dup_list = g_list_reverse(dup_list); // list was built in reverse order, so un-reverse it
2097
2098 // then update database and cache
2099 // if update was performed in above loop, dt_image_path_append_version()
2100 // would return wrong version!
2101 while(dup_list)
2102 {
2103 const int id = GPOINTER_TO_INT(dup_list->data);
2105 img->film_id = filmid;
2106 if(newname) g_strlcpy(img->filename, newname, DT_MAX_FILENAME_LEN);
2107 // write through to db and queue xmp write
2109 dup_list = g_list_delete_link(dup_list, dup_list);
2110 }
2111 g_list_free(dup_list);
2112 dup_list = NULL;
2113
2114 // finally, rename local copy if any
2115 if(g_file_test(copysrcpath, G_FILE_TEST_EXISTS))
2116 {
2117 // get new name
2118 img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
2119 if(img)
2120 {
2121 dt_image_choose_input_path(img, copydestpath, sizeof(copydestpath), TRUE);
2123 }
2124
2125 GFile *cold = g_file_new_for_path(copysrcpath);
2126 GFile *cnew = g_file_new_for_path(copydestpath);
2127
2128 g_clear_error(&moveError);
2129 moveStatus = g_file_move(cold, cnew, 0, NULL, NULL, NULL, &moveError);
2130 if(!moveStatus)
2131 {
2132 fprintf(stderr, "[dt_image_rename] error moving local copy `%s' -> `%s'\n", copysrcpath, copydestpath);
2133
2134 if(g_error_matches(moveError, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
2135 {
2136 gchar *oldBasename = g_path_get_basename(copysrcpath);
2137 dt_control_log(_("cannot access local copy `%s'"), oldBasename);
2138 dt_free(oldBasename);
2139 }
2140 else if(g_error_matches(moveError, G_IO_ERROR, G_IO_ERROR_EXISTS)
2141 || g_error_matches(moveError, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY))
2142 {
2143 gchar *newBasename = g_path_get_basename(copydestpath);
2144 dt_control_log(_("cannot write local copy `%s'"), newBasename);
2145 dt_free(newBasename);
2146 }
2147 else
2148 {
2149 gchar *oldBasename = g_path_get_basename(copysrcpath);
2150 gchar *newBasename = g_path_get_basename(copydestpath);
2151 dt_control_log(_("error moving local copy `%s' -> `%s'"), oldBasename, newBasename);
2152 dt_free(oldBasename);
2153 dt_free(newBasename);
2154 }
2155 }
2156
2157 g_object_unref(cold);
2158 g_object_unref(cnew);
2159 }
2160
2161 result = 0;
2162 }
2163 else
2164 {
2165 if(g_error_matches(moveError, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
2166 {
2167 dt_control_log(_("error moving `%s': file not found"), oldimg);
2168 }
2169 // only display error message if newname is set (renaming and
2170 // not moving) as when moving it can be the case where a
2171 // duplicate is being moved, so only the .xmp are present but
2172 // the original file may already have been moved.
2173 else if(newname
2174 && (g_error_matches(moveError, G_IO_ERROR, G_IO_ERROR_EXISTS)
2175 || g_error_matches(moveError, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY)))
2176 {
2177 dt_control_log(_("error moving `%s' -> `%s': file exists"), oldimg, newimg);
2178 }
2179 else if(newname)
2180 {
2181 dt_control_log(_("error moving `%s' -> `%s'"), oldimg, newimg);
2182 }
2183 }
2184
2185 g_clear_error(&moveError);
2186 g_object_unref(old);
2187 g_object_unref(new);
2188 }
2189
2190 return result;
2191}
2192
2193int32_t dt_image_move(const int32_t imgid, const int32_t filmid)
2194{
2195 return dt_image_rename(imgid, filmid, NULL);
2196}
2197
2198int32_t dt_image_copy_rename(const int32_t imgid, const int32_t filmid, const gchar *newname)
2199{
2200 int32_t newid = -1;
2201 sqlite3_stmt *stmt;
2202 gchar srcpath[PATH_MAX] = { 0 };
2203 gchar *newdir = NULL;
2204 gchar *filename = NULL;
2205 gboolean from_cache = FALSE;
2206 gchar *oldFilename = NULL;
2207 gchar *newFilename = NULL;
2208
2209 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT folder FROM main.film_rolls WHERE id = ?1",
2210 -1, &stmt, NULL);
2211 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, filmid);
2212 if(sqlite3_step(stmt) == SQLITE_ROW) newdir = g_strdup((gchar *)sqlite3_column_text(stmt, 0));
2213 sqlite3_finalize(stmt);
2214
2215 GFile *src = NULL, *dest = NULL;
2216 if(newdir)
2217 {
2218 dt_image_full_path(imgid, srcpath, sizeof(srcpath), &from_cache, __FUNCTION__);
2219 oldFilename = g_path_get_basename(srcpath);
2220 gchar *destpath;
2221 if(newname)
2222 {
2223 newFilename = g_strdup(newname);
2224 destpath = g_build_filename(newdir, newname, NULL);
2225 dest = g_file_new_for_path(destpath);
2226 // 'newname' represents the file's new *basename* -- it must not
2227 // refer to a file outside of 'newdir'.
2228 gchar *destBasename = g_file_get_basename(dest);
2229 if(g_strcmp0(newname, destBasename) != 0)
2230 {
2231 g_object_unref(dest);
2232 dest = NULL;
2233 }
2234 dt_free(destBasename);
2235 }
2236 else
2237 {
2238 newFilename = g_path_get_basename(srcpath);
2239 destpath = g_build_filename(newdir, newFilename, NULL);
2240 dest = g_file_new_for_path(destpath);
2241 }
2242 if(dest)
2243 {
2244 src = g_file_new_for_path(srcpath);
2245 }
2246 dt_free(newdir);
2247 dt_free(destpath);
2248 }
2249
2250 if(dest)
2251 {
2252 // copy image to new folder
2253 // if image file already exists, continue
2254 GError *gerror = NULL;
2255 gboolean copyStatus = g_file_copy(src, dest, G_FILE_COPY_NONE, NULL, NULL, NULL, &gerror);
2256
2257 if(copyStatus || g_error_matches(gerror, G_IO_ERROR, G_IO_ERROR_EXISTS))
2258 {
2259 gchar *dest_image_path = g_file_get_path(dest);
2260 if(dest_image_path)
2261 {
2262 _copy_text_sidecar_if_present(srcpath, dest_image_path);
2263 dt_free(dest_image_path);
2264 }
2265
2266 // update database
2267 // clang-format off
2270 "INSERT INTO main.images"
2271 " (id, group_id, film_id, width, height, filename, maker, model, lens, exposure,"
2272 " aperture, iso, focal_length, focus_distance, datetime_taken, flags,"
2273 " output_width, output_height, crop, raw_parameters, raw_denoise_threshold,"
2274 " raw_auto_bright_threshold, raw_black, raw_maximum,"
2275 " license, sha1sum, orientation, histogram, lightmap,"
2276 " longitude, latitude, altitude, color_matrix, colorspace, version, max_version,"
2277 " aspect_ratio, exposure_bias)"
2278 " SELECT NULL, group_id, ?1 as film_id, width, height, ?2 as filename, maker, model, lens,"
2279 " exposure, aperture, iso, focal_length, focus_distance, datetime_taken,"
2280 " flags, width, height, crop, raw_parameters, raw_denoise_threshold,"
2281 " raw_auto_bright_threshold, raw_black, raw_maximum,"
2282 " license, sha1sum, orientation, histogram, lightmap,"
2283 " longitude, latitude, altitude, color_matrix, colorspace, -1, -1,"
2284 " aspect_ratio, exposure_bias"
2285 " FROM main.images"
2286 " WHERE id = ?3",
2287 -1, &stmt, NULL);
2288 // clang-format on
2289 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, filmid);
2290 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, newFilename, -1, SQLITE_TRANSIENT);
2291 DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, imgid);
2292 sqlite3_step(stmt);
2293 sqlite3_finalize(stmt);
2294 // clang-format off
2297 "SELECT a.id, a.filename"
2298 " FROM main.images AS a"
2299 " JOIN main.images AS b"
2300 " WHERE a.film_id = ?1 AND a.filename = ?2 AND b.filename = ?3 AND b.id = ?4"
2301 " ORDER BY a.id DESC",
2302 -1, &stmt, NULL);
2303 // clang-format on
2304 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, filmid);
2305 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, newFilename, -1, SQLITE_TRANSIENT);
2306 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 3, oldFilename, -1, SQLITE_TRANSIENT);
2307 DT_DEBUG_SQLITE3_BIND_INT(stmt, 4, imgid);
2308
2309 if(sqlite3_step(stmt) == SQLITE_ROW)
2310 {
2311 newid = sqlite3_column_int(stmt, 0);
2312 filename = g_strdup((gchar *)sqlite3_column_text(stmt, 1));
2313 }
2314 sqlite3_finalize(stmt);
2315
2316 if(newid != -1)
2317 {
2318 // also copy over on-disk thumbnails, if any
2320 // clang-format off
2322 "INSERT INTO main.color_labels (imgid, color)"
2323 " SELECT ?1, color"
2324 " FROM main.color_labels"
2325 " WHERE imgid = ?2",
2326 -1, &stmt, NULL);
2327 // clang-format on
2328 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newid);
2329 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
2330 sqlite3_step(stmt);
2331 sqlite3_finalize(stmt);
2332 // clang-format off
2334 "INSERT INTO main.meta_data (id, key, value)"
2335 " SELECT ?1, key, value"
2336 " FROM main.meta_data"
2337 " WHERE id = ?2",
2338 -1, &stmt, NULL);
2339 // clang-format on
2340 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newid);
2341 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
2342 sqlite3_step(stmt);
2343 sqlite3_finalize(stmt);
2344
2345 // clang-format off
2347 "INSERT INTO main.tagged_images (imgid, tagid, position)"
2348 " SELECT ?1, tagid, "
2349 " (SELECT (IFNULL(MAX(position),0) & 0xFFFFFFFF00000000)"
2350 " FROM main.tagged_images)"
2351 " + (ROW_NUMBER() OVER (ORDER BY imgid) << 32)"
2352 " FROM main.tagged_images AS ti"
2353 " WHERE imgid = ?2",
2354 -1, &stmt, NULL);
2355 // clang-format on
2356 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newid);
2357 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
2358 sqlite3_step(stmt);
2359 sqlite3_finalize(stmt);
2360
2361 // get max_version of image duplicates in destination filmroll
2362 int32_t max_version = -1;
2363 // clang-format off
2366 "SELECT MAX(a.max_version)"
2367 " FROM main.images AS a"
2368 " JOIN main.images AS b"
2369 " WHERE a.film_id = b.film_id AND a.filename = b.filename AND b.id = ?1",
2370 -1, &stmt, NULL);
2371 // clang-format on
2372 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newid);
2373
2374 if(sqlite3_step(stmt) == SQLITE_ROW) max_version = sqlite3_column_int(stmt, 0);
2375 sqlite3_finalize(stmt);
2376
2377 // set version of new entry and max_version of all involved duplicates (with same film_id and
2378 // filename)
2379 max_version = (max_version >= 0) ? max_version + 1 : 0;
2380 int32_t version = max_version;
2381
2384 "UPDATE main.images SET version=?1 WHERE id = ?2", -1, &stmt, NULL);
2385 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, version);
2386 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, newid);
2387 sqlite3_step(stmt);
2388 sqlite3_finalize(stmt);
2389
2392 "UPDATE main.images SET max_version=?1 WHERE film_id = ?2 AND filename = ?3",
2393 -1, &stmt, NULL);
2394 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, max_version);
2395 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, filmid);
2396 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 3, filename, -1, SQLITE_TRANSIENT);
2397 sqlite3_step(stmt);
2398 sqlite3_finalize(stmt);
2399
2400 // image group handling follows
2401 // get group_id of potential image duplicates in destination filmroll
2402 int32_t new_group_id = -1;
2403 // clang-format off
2406 "SELECT DISTINCT a.group_id"
2407 " FROM main.images AS a"
2408 " JOIN main.images AS b"
2409 " WHERE a.film_id = b.film_id AND a.filename = b.filename"
2410 " AND b.id = ?1 AND a.id != ?1",
2411 -1, &stmt, NULL);
2412 // clang-format on
2413 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newid);
2414
2415 if(sqlite3_step(stmt) == SQLITE_ROW) new_group_id = sqlite3_column_int(stmt, 0);
2416
2417 // then check if there are further duplicates belonging to different group(s)
2418 if(sqlite3_step(stmt) == SQLITE_ROW) new_group_id = -1;
2419 sqlite3_finalize(stmt);
2420
2421 // rationale:
2422 // if no group exists or if the image duplicates belong to multiple groups, then the
2423 // new image builds a group of its own, else it is added to the (one) existing group
2424 if(new_group_id == -1) new_group_id = newid;
2425
2426 // make copied image belong to a group
2428 "UPDATE main.images SET group_id=?1 WHERE id = ?2", -1, &stmt, NULL);
2429
2430 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, new_group_id);
2431 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, newid);
2432 sqlite3_step(stmt);
2433 sqlite3_finalize(stmt);
2434
2436
2438 NULL);
2439 }
2440
2441 dt_free(filename);
2442 }
2443 else
2444 {
2445 fprintf(stderr, "Failed to copy image %s: %s\n", srcpath, gerror->message);
2446 }
2447 g_object_unref(dest);
2448 g_object_unref(src);
2449 g_clear_error(&gerror);
2450 }
2451 dt_free(oldFilename);
2452 dt_free(newFilename);
2453
2454 return newid;
2455}
2456
2457int32_t dt_image_copy(const int32_t imgid, const int32_t filmid)
2458{
2459 return dt_image_copy_rename(imgid, filmid, NULL);
2460}
2461
2462int dt_image_local_copy_set(const int32_t imgid)
2463{
2464 gchar srcpath[PATH_MAX] = { 0 };
2465 gchar destpath[PATH_MAX] = { 0 };
2467 char local_copy_path[PATH_MAX] = { 0 };
2468 char local_copy_legacy_path[PATH_MAX] = { 0 };
2470 if(img)
2471 {
2472 g_strlcpy(srcpath, img->fullpath, PATH_MAX);
2473 g_strlcpy(local_copy_path, img->local_copy_path, sizeof(local_copy_path));
2474 g_strlcpy(local_copy_legacy_path, img->local_copy_legacy_path, sizeof(local_copy_legacy_path));
2475 char existing[PATH_MAX] = { 0 };
2476 source = dt_image_choose_input_path(img, existing, sizeof(existing), TRUE);
2478 g_strlcpy(destpath, existing, sizeof(destpath));
2479 else
2480 g_strlcpy(destpath, local_copy_path, sizeof(destpath));
2482 }
2483 else
2484 {
2485 return 1;
2486 }
2487
2488 // check that the src file is readable
2489 if(!g_file_test(srcpath, G_FILE_TEST_IS_REGULAR))
2490 {
2491 dt_control_log(_("cannot create local copy when the original file is not accessible."));
2492 return 1;
2493 }
2494
2496 {
2497 GFile *src = g_file_new_for_path(srcpath);
2498 GFile *dest = g_file_new_for_path(destpath);
2499
2500 // copy image to cache directory
2501 GError *gerror = NULL;
2502
2503 if(!g_file_copy(src, dest, G_FILE_COPY_NONE, NULL, NULL, NULL, &gerror))
2504 {
2505 dt_control_log(_("cannot create local copy."));
2506 g_object_unref(dest);
2507 g_object_unref(src);
2508 return 1;
2509 }
2510
2511 g_object_unref(dest);
2512 g_object_unref(src);
2513 }
2514
2515 _copy_text_sidecar_if_present(srcpath, destpath);
2516
2517 // update cache local copy flags, do this even if the local copy already exists as we need to set the flags
2518 // for duplicate
2519 img = dt_image_cache_get(darktable.image_cache, imgid, 'w');
2520 img->flags |= DT_IMAGE_LOCAL_COPY;
2522
2524 return 0;
2525}
2526
2527static int _nb_other_local_copy_for(const int32_t imgid)
2528{
2529 sqlite3_stmt *stmt;
2530 int result = 1;
2531
2532 // clang-format off
2534 "SELECT COUNT(*)"
2535 " FROM main.images"
2536 " WHERE id!=?1 AND flags&?2=?2"
2537 " AND film_id=(SELECT film_id"
2538 " FROM main.images"
2539 " WHERE id=?1)"
2540 " AND filename=(SELECT filename"
2541 " FROM main.images"
2542 " WHERE id=?1);",
2543 -1, &stmt, NULL);
2544 // clang-format on
2545 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
2547 if(sqlite3_step(stmt) == SQLITE_ROW) result = sqlite3_column_int(stmt, 0);
2548 sqlite3_finalize(stmt);
2549
2550 return result;
2551}
2552
2553int dt_image_local_copy_reset(const int32_t imgid)
2554{
2555 gchar destpath[PATH_MAX] = { 0 };
2556 gchar locppath[PATH_MAX] = { 0 };
2557 gchar cachedir[PATH_MAX] = { 0 };
2558
2559 // check that a local copy exists, otherwise there is nothing to do
2561 const gboolean local_copy_exists = (imgr->flags & DT_IMAGE_LOCAL_COPY) == DT_IMAGE_LOCAL_COPY ? TRUE : FALSE;
2563
2564 if(!local_copy_exists)
2565 return 0;
2566
2567 // check that the original file is accessible
2568
2569 gboolean from_cache = FALSE;
2570 dt_image_full_path(imgid, destpath, sizeof(destpath), &from_cache, __FUNCTION__);
2571
2572 from_cache = TRUE;
2573 dt_image_full_path(imgid, locppath, sizeof(locppath), &from_cache, __FUNCTION__);
2574 dt_image_path_append_version(imgid, locppath, sizeof(locppath));
2575 g_strlcat(locppath, ".xmp", sizeof(locppath));
2576
2577 // a local copy exists, but the original is not accessible
2578
2579 if(g_file_test(locppath, G_FILE_TEST_EXISTS) && !g_file_test(destpath, G_FILE_TEST_EXISTS))
2580 {
2581 dt_control_log(_("cannot remove local copy when the original file is not accessible."));
2582 return 1;
2583 }
2584
2585 // get name of local copy
2586
2587 locppath[0] = '\0';
2589 if(img)
2590 {
2591 dt_image_choose_input_path(img, locppath, sizeof(locppath), TRUE);
2593 }
2594
2595 // remove cached file, but double check that this is really into the cache. We really want to avoid deleting
2596 // a user's original file.
2597
2598 dt_loc_get_user_cache_dir(cachedir, sizeof(cachedir));
2599
2600 if(g_file_test(locppath, G_FILE_TEST_EXISTS) && strstr(locppath, cachedir))
2601 {
2602 GFile *dest = g_file_new_for_path(locppath);
2603
2604 // first sync the xmp with the original picture
2605
2606 dt_control_save_xmp(imgid);
2607
2608 // delete image from cache directory only if there is no other local cache image referencing it
2609 // for example duplicates are all referencing the same base picture.
2610
2611 if(_nb_other_local_copy_for(imgid) == 0)
2612 {
2613 _move_text_sidecar_if_present(locppath, destpath, TRUE);
2614 g_file_delete(dest, NULL, NULL);
2615 }
2616
2617 g_object_unref(dest);
2618
2619 // delete xmp if any
2620 dt_image_path_append_version(imgid, locppath, sizeof(locppath));
2621 g_strlcat(locppath, ".xmp", sizeof(locppath));
2622 dest = g_file_new_for_path(locppath);
2623
2624 if(g_file_test(locppath, G_FILE_TEST_EXISTS)) g_file_delete(dest, NULL, NULL);
2625 g_object_unref(dest);
2626 }
2627
2628 // update cache, remove local copy flags, this is done in all cases here as when we
2629 // reach this point the local-copy flag is present and the file has been either removed
2630 // or is not present.
2631
2632 img = dt_image_cache_get(darktable.image_cache, imgid, 'w');
2633 img->flags &= ~DT_IMAGE_LOCAL_COPY;
2635
2637
2638 return 0;
2639}
2640
2641// *******************************************************
2642// xmp stuff
2643// *******************************************************
2644
2645static sqlite3_stmt *_write_timestamp_select_stmt = NULL;
2646static sqlite3_stmt *_write_timestamp_update_stmt = NULL;
2647static dt_pthread_mutex_t _write_timestamp_stmt_mutex;
2649
2651{
2652 if(g_once_init_enter(&_write_timestamp_stmt_mutex_inited))
2653 {
2655 g_once_init_leave(&_write_timestamp_stmt_mutex_inited, 1);
2656 }
2657}
2658
2659static int64_t _write_timestamp_get(const int32_t imgid)
2660{
2661 if(imgid <= 0) return 0;
2662
2666 {
2668 "SELECT write_timestamp FROM main.images WHERE id = ?1",
2669 -1, &_write_timestamp_select_stmt, NULL);
2670 }
2672 {
2674 return 0;
2675 }
2676
2677 int64_t write_timestamp = 0;
2679 if(sqlite3_step(_write_timestamp_select_stmt) == SQLITE_ROW)
2680 write_timestamp = sqlite3_column_int64(_write_timestamp_select_stmt, 0);
2681 sqlite3_reset(_write_timestamp_select_stmt);
2682 sqlite3_clear_bindings(_write_timestamp_select_stmt);
2684
2685 return write_timestamp;
2686}
2687
2688static void _write_timestamp_set_now(const int32_t imgid)
2689{
2690 if(imgid <= 0) return;
2691
2695 {
2698 "UPDATE main.images SET write_timestamp = STRFTIME('%s', 'now') WHERE id = ?1",
2699 -1, &_write_timestamp_update_stmt, NULL);
2700 }
2702 {
2704 sqlite3_step(_write_timestamp_update_stmt);
2705 sqlite3_reset(_write_timestamp_update_stmt);
2706 sqlite3_clear_bindings(_write_timestamp_update_stmt);
2707 }
2709}
2710
2711static gboolean _sidecar_is_up_to_date(const dt_image_t *img)
2712{
2713 if(IS_NULL_PTR(img) || img->id <= 0) return FALSE;
2714 if(img->change_timestamp <= 0) return FALSE;
2715
2716 const int64_t write_timestamp = _write_timestamp_get(img->id);
2717 if(write_timestamp <= 0) return FALSE;
2718
2720 if(IS_NULL_PTR(gdt)) return FALSE;
2721 const int64_t change_timestamp_unix = g_date_time_to_unix(gdt);
2722 g_date_time_unref(gdt);
2724 "[xmp] imgid %d change_ts=%lld write_ts=%lld\n",
2725 img->id, (long long)change_timestamp_unix, (long long)write_timestamp);
2726 return change_timestamp_unix <= write_timestamp;
2727}
2728
2730{
2731 if(IS_NULL_PTR(img) || img->id <= 0) return 1;
2732
2733 char imgpath[PATH_MAX] = { 0 };
2734 if(dt_image_choose_input_path(img, imgpath, sizeof(imgpath), FALSE) == DT_IMAGE_PATH_NONE)
2735 {
2736 dt_print(DT_DEBUG_CONTROL, "[xmp] imgid %d no source path available\n", img->id);
2737 return 1;
2738 }
2739
2740 char filename[PATH_MAX] = { 0 };
2741 g_strlcpy(filename, imgpath, sizeof(filename));
2742 dt_image_path_append_version(img->id, filename, sizeof(filename));
2743 g_strlcat(filename, ".xmp", sizeof(filename));
2744
2745 if(g_file_test(filename, G_FILE_TEST_EXISTS))
2746 {
2747 if(_sidecar_is_up_to_date(img))
2748 {
2749 dt_print(DT_DEBUG_CONTROL, "[xmp] imgid %d sidecar up-to-date, skip\n", img->id);
2750 return 0;
2751 }
2752 }
2753
2754 dt_print(DT_DEBUG_CONTROL, "[xmp] imgid %d writing sidecar %s\n", img->id, filename);
2755 if(dt_exif_xmp_write_with_imgpath(img, filename, imgpath) != 0)
2756 return 1;
2757
2758 dt_print(DT_DEBUG_CONTROL, "[xmp] imgid %d updating write_timestamp\n", img->id);
2760 return 0;
2761}
2762
2763int dt_image_write_sidecar_file(const int32_t imgid)
2764{
2765 if(imgid <= 0 || !dt_image_get_xmp_mode()) return 1;
2766
2767 dt_print(DT_DEBUG_CONTROL, "[xmp] imgid %d write start\n", imgid);
2769 if(IS_NULL_PTR(img))
2770 {
2771 dt_print(DT_DEBUG_CONTROL, "[xmp] imgid %d cache lock failed\n", imgid);
2772 return 1;
2773 }
2774 dt_print(DT_DEBUG_CONTROL, "[xmp] imgid %d cache lock acquired (write)\n", imgid);
2775
2776 const int res = _write_sidecar_file_from_image_locked(img);
2778 dt_print(DT_DEBUG_CONTROL, "[xmp] imgid %d cache lock released (write minimal)\n", imgid);
2779 return res;
2780}
2781
2782void dt_image_synch_xmps(const GList *img)
2783{
2784 if(IS_NULL_PTR(img)) return;
2786 {
2788 }
2789}
2790
2791void dt_image_synch_xmp(const int selected)
2792{
2793 if(selected > 0)
2794 {
2795 GList *imgs = g_list_append(NULL, GINT_TO_POINTER(selected));
2797 g_list_free(imgs);
2798 imgs = NULL;
2799 }
2800 else
2801 {
2802 GList *imgs = dt_act_on_get_images();
2803 dt_image_synch_xmps(imgs);
2804 g_list_free(imgs);
2805 imgs = NULL;
2806 }
2807}
2808
2809void dt_image_synch_all_xmp(const gchar *pathname)
2810{
2812 {
2813 const int32_t imgid = dt_image_get_id_full_path(pathname);
2814 if(imgid != UNKNOWN_IMAGE)
2815 {
2816 GList *imgs = g_list_append(NULL, GINT_TO_POINTER(imgid));
2818 g_list_free(imgs);
2819 imgs = NULL;
2820 }
2821 }
2822}
2823
2825{
2826 // nothing to do if not creating .xmp
2827 if(!dt_image_get_xmp_mode()) return;
2828 sqlite3_stmt *stmt;
2829 GList *imgs = NULL;
2830
2831 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT id FROM main.images WHERE flags&?1=?1", -1,
2832 &stmt, NULL);
2834
2835 while(sqlite3_step(stmt) == SQLITE_ROW)
2836 {
2837 const int32_t imgid = sqlite3_column_int(stmt, 0);
2838 gboolean from_cache = FALSE;
2839 char filename[PATH_MAX] = { 0 };
2840 dt_image_full_path(imgid, filename, sizeof(filename), &from_cache, __FUNCTION__);
2841
2842 if(g_file_test(filename, G_FILE_TEST_EXISTS))
2843 {
2844 imgs = g_list_prepend(imgs, GINT_TO_POINTER(imgid));
2845 }
2846 }
2847 sqlite3_finalize(stmt);
2848
2849 const int count = g_list_length(imgs);
2850 if(count > 0)
2851 {
2853 dt_control_log(ngettext("%d local copy has been synchronized",
2854 "%d local copies have been synchronized", count),
2855 count);
2856 }
2857 g_list_free(imgs);
2858 imgs = NULL;
2859}
2860
2861void dt_image_get_datetime(const int32_t imgid, char *datetime)
2862{
2863 if(IS_NULL_PTR(datetime)) return;
2864 datetime[0] = '\0';
2865 const dt_image_t *cimg = dt_image_cache_get(darktable.image_cache, imgid, 'r');
2866 if(IS_NULL_PTR(cimg)) return;
2869}
2870
2871static void _datetime_undo_data_free(gpointer data)
2872{
2873 GList *l = (GList *)data;
2874 g_list_free_full(l, dt_free_gpointer);
2875 l = NULL;
2876}
2877
2882
2883static void _image_set_datetimes(const GList *img, const GArray *dtime,
2884 GList **undo, const gboolean undo_on)
2885{
2886 int i = 0;
2887 for(GList *imgs = (GList *)img; imgs; imgs = g_list_next(imgs))
2888 {
2889 const int32_t imgid = GPOINTER_TO_INT(imgs->data);
2890 // if char *datetime, the returned pointer is not correct => use of _datetime_t
2891 const _datetime_t *datetime = &g_array_index(dtime, _datetime_t, i);
2892 if(undo_on)
2893 {
2894 dt_undo_datetime_t *undodatetime = (dt_undo_datetime_t *)malloc(sizeof(dt_undo_datetime_t));
2895 undodatetime->imgid = imgid;
2896 dt_image_get_datetime(imgid, undodatetime->before);
2897
2898 memcpy(&undodatetime->after, datetime->dt, DT_DATETIME_LENGTH);
2899
2900 *undo = g_list_prepend(*undo, undodatetime);
2901 }
2902
2903 _set_datetime(imgid, datetime->dt);
2904 i++;
2905 }
2906}
2907
2908void dt_image_set_datetimes(const GList *imgs, const GArray *dtime, const gboolean undo_on)
2909{
2910 if(IS_NULL_PTR(imgs) || IS_NULL_PTR(dtime) || (g_list_length((GList *)imgs) != dtime->len))
2911 return;
2912 GList *undo = NULL;
2914
2915 _image_set_datetimes(imgs, dtime, &undo, undo_on);
2916
2917 if(undo_on)
2918 {
2921 }
2922}
2923
2924static void _image_set_datetime(const GList *img, const char *datetime,
2925 GList **undo, const gboolean undo_on)
2926{
2927 for(GList *imgs = (GList *)img; imgs; imgs = g_list_next(imgs))
2928 {
2929 const int32_t imgid = GPOINTER_TO_INT(imgs->data);
2930 if(undo_on)
2931 {
2932 dt_undo_datetime_t *undodatetime = (dt_undo_datetime_t *)malloc(sizeof(dt_undo_datetime_t));
2933 undodatetime->imgid = imgid;
2934 dt_image_get_datetime(imgid, undodatetime->before);
2935
2936 memcpy(&undodatetime->after, datetime, DT_DATETIME_LENGTH);
2937
2938 *undo = g_list_prepend(*undo, undodatetime);
2939 }
2940
2941 _set_datetime(imgid, datetime);
2942 }
2943}
2944
2945void dt_image_set_datetime(const GList *imgs, const char *datetime, const gboolean undo_on)
2946{
2947 if(IS_NULL_PTR(imgs))
2948 return;
2949 GList *undo = NULL;
2951
2952 _image_set_datetime(imgs, datetime, &undo, undo_on);
2953
2954 if(undo_on)
2955 {
2958 }
2959}
2960
2961char *dt_image_get_audio_path_from_path(const char *image_path)
2962{
2963 size_t len = strlen(image_path);
2964 const char *c = image_path + len;
2965 while((c > image_path) && (*c != '.')) c--;
2966 len = c - image_path + 1;
2967
2968 char *result = g_strndup(image_path, len + 3);
2969
2970 result[len] = 'w';
2971 result[len + 1] = 'a';
2972 result[len + 2] = 'v';
2973 if(g_file_test(result, G_FILE_TEST_EXISTS)) return result;
2974
2975 result[len] = 'W';
2976 result[len + 1] = 'A';
2977 result[len + 2] = 'V';
2978 if(g_file_test(result, G_FILE_TEST_EXISTS)) return result;
2979
2980 dt_free(result);
2981 return NULL;
2982}
2983
2984char *dt_image_get_audio_path(const int32_t imgid)
2985{
2986 gboolean from_cache = FALSE;
2987 char image_path[PATH_MAX] = { 0 };
2988 dt_image_full_path(imgid, image_path, sizeof(image_path), &from_cache, __FUNCTION__);
2989
2990 return dt_image_get_audio_path_from_path(image_path);
2991}
2992
2993static char *_text_path_legacy_if_exists(const char *image_path)
2994{
2995 size_t len = strlen(image_path);
2996 const char *c = image_path + len;
2997 while((c > image_path) && (*c != '.')) c--;
2998 len = c - image_path + 1;
2999
3000 char *result = g_strndup(image_path, len + 3);
3001
3002 result[len] = 't';
3003 result[len + 1] = 'x';
3004 result[len + 2] = 't';
3005 if(g_file_test(result, G_FILE_TEST_EXISTS)) return result;
3006
3007 result[len] = 'T';
3008 result[len + 1] = 'X';
3009 result[len + 2] = 'T';
3010 if(g_file_test(result, G_FILE_TEST_EXISTS)) return result;
3011
3012 dt_free(result);
3013 return NULL;
3014}
3015
3016static char *_text_path_legacy_build(const char *image_path)
3017{
3018 size_t len = strlen(image_path);
3019 const char *c = image_path + len;
3020 while((c > image_path) && (*c != '.')) c--;
3021 len = c - image_path + 1;
3022
3023 char *result = g_strndup(image_path, len + 3);
3024 result[len] = 't';
3025 result[len + 1] = 'x';
3026 result[len + 2] = 't';
3027 return result;
3028}
3029
3030char *dt_image_get_text_path_from_path(const char *image_path)
3031{
3032 if(IS_NULL_PTR(image_path)) return NULL;
3033
3034 return _text_path_legacy_if_exists(image_path);
3035}
3036
3037char *dt_image_build_text_path_from_path(const char *image_path)
3038{
3039 if(IS_NULL_PTR(image_path)) return NULL;
3040
3041 return _text_path_legacy_build(image_path);
3042}
3043
3044static void _copy_text_sidecar_if_present(const char *src_image_path, const char *dest_image_path)
3045{
3046 if(IS_NULL_PTR(src_image_path) || IS_NULL_PTR(dest_image_path)) return;
3047
3048 char *src_txt = dt_image_get_text_path_from_path(src_image_path);
3049 if(IS_NULL_PTR(src_txt)) return;
3050
3051 char *dest_txt = _text_path_legacy_build(dest_image_path);
3052 if(dest_txt)
3053 {
3054 GFile *src = g_file_new_for_path(src_txt);
3055 GFile *dest = g_file_new_for_path(dest_txt);
3056 GError *gerror = NULL;
3057 gboolean copyStatus = g_file_copy(src, dest, G_FILE_COPY_NONE, NULL, NULL, NULL, &gerror);
3058
3059 if(!copyStatus && g_error_matches(gerror, G_IO_ERROR, G_IO_ERROR_EXISTS))
3060 copyStatus = TRUE;
3061
3062 if(!copyStatus && gerror)
3063 fprintf(stderr, "[dt_image] failed to copy text sidecar `%s' -> `%s': %s\n",
3064 src_txt, dest_txt, gerror->message);
3065
3066 g_clear_error(&gerror);
3067 g_object_unref(dest);
3068 g_object_unref(src);
3069 }
3070
3071 dt_free(dest_txt);
3072 dt_free(src_txt);
3073}
3074
3075static void _move_text_sidecar_if_present(const char *src_image_path, const char *dest_image_path, const gboolean overwrite)
3076{
3077 if(IS_NULL_PTR(src_image_path) || IS_NULL_PTR(dest_image_path)) return;
3078
3079 char *src_txt = dt_image_get_text_path_from_path(src_image_path);
3080 if(IS_NULL_PTR(src_txt)) return;
3081
3082 char *dest_txt = _text_path_legacy_build(dest_image_path);
3083 if(dest_txt)
3084 {
3085 GFile *src = g_file_new_for_path(src_txt);
3086 GFile *dest = g_file_new_for_path(dest_txt);
3087 GError *gerror = NULL;
3088 const GFileCopyFlags flags = overwrite ? G_FILE_COPY_OVERWRITE : G_FILE_COPY_NONE;
3089 gboolean moveStatus = g_file_move(src, dest, flags, NULL, NULL, NULL, &gerror);
3090
3091 if(!moveStatus && gerror)
3092 fprintf(stderr, "[dt_image] failed to move text sidecar `%s' -> `%s': %s\n",
3093 src_txt, dest_txt, gerror->message);
3094
3095 g_clear_error(&gerror);
3096 g_object_unref(dest);
3097 g_object_unref(src);
3098 }
3099
3100 dt_free(dest_txt);
3101 dt_free(src_txt);
3102}
3103
3104char *dt_image_get_text_path(const int32_t imgid)
3105{
3106 char image_path[PATH_MAX] = { 0 };
3107
3108 gboolean from_cache = FALSE;
3109 dt_image_full_path(imgid, image_path, sizeof(image_path), &from_cache, __FUNCTION__);
3110
3111 if(image_path[0] != '\0' && g_file_test(image_path, G_FILE_TEST_EXISTS))
3112 return dt_image_get_text_path_from_path(image_path);
3113
3114 from_cache = TRUE;
3115 dt_image_full_path(imgid, image_path, sizeof(image_path), &from_cache, __FUNCTION__);
3116
3117 if(from_cache && image_path[0] != '\0' && g_file_test(image_path, G_FILE_TEST_EXISTS))
3118 return dt_image_get_text_path_from_path(image_path);
3119
3120 return NULL;
3121}
3122
3123float dt_image_get_exposure_bias(const struct dt_image_t *image_storage)
3124{
3125 // just check that pointers exist and are initialized
3126 if((image_storage) && (image_storage->exif_exposure_bias))
3127 {
3128 // sanity checks because I don't trust exif tags too much
3129 if(isnan(image_storage->exif_exposure_bias)
3130 || CLAMP(image_storage->exif_exposure_bias, -5.0f, 5.0f) != image_storage->exif_exposure_bias)
3131 return 0.0f; // isnan
3132 else
3133 return CLAMP(image_storage->exif_exposure_bias, -5.0f, 5.0f);
3134 }
3135 else
3136 return 0.0f;
3137}
3138
3139char *dt_image_camera_missing_sample_message(const struct dt_image_t *img, gboolean logmsg)
3140{
3141 const char *T1 = _("<b>WARNING</b>: camera is missing samples!");
3142 const char *T2 = _("You must provide samples in <a href='https://raw.pixls.us/'>https://raw.pixls.us/</a>");
3143 char *T3 = g_strdup_printf(_("for `%s' `%s'\n"
3144 "in as many format/compression/bit depths as possible"),
3145 img->camera_maker, img->camera_model);
3146 const char *T4 = _("or the <b>RAW won't be readable</b> in next version.");
3147
3148 char *NL = logmsg ? "\n\n" : "\n";
3149 char *PREFIX = logmsg ? "<big>" : "";
3150 char *SUFFIX = logmsg ? "</big>" : "";
3151
3152 char *msg = g_strconcat(PREFIX, T1, NL, T2, NL, T3, NL, T4, SUFFIX, NULL);
3153
3154 if(logmsg)
3155 {
3156 char *newmsg = dt_util_str_replace(msg, "<b>", "<span foreground='red'><b>");
3157 dt_free(msg);
3158 msg = dt_util_str_replace(newmsg, "</b>", "</b></span>");
3159 dt_free(newmsg);
3160 }
3161
3162 dt_free(T3);
3163 return msg;
3164}
3165
3167{
3168 if(img->camera_missing_sample)
3169 {
3171 dt_control_log(msg, (char *)NULL);
3172 dt_free(msg);
3173 }
3174}
3175
3176void dt_get_dirname_from_imgid(gchar *dir, const int32_t imgid)
3177{
3178 gchar path[PATH_MAX] = { 0 };
3179 gboolean from_cache = FALSE;
3180 dt_image_full_path(imgid, path, sizeof(path), &from_cache, __FUNCTION__);
3181 g_strlcpy(dir, g_path_get_dirname(path), sizeof(path));
3182}
3183
3184// clang-format off
3185// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
3186// vim: shiftwidth=2 expandtab tabstop=2 cindent
3187// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
3188// clang-format on
GList * dt_act_on_get_images()
Definition act_on.c:39
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
const char * extension(dt_imageio_module_data_t *data)
Definition avif.c:645
uint32_t bit_depth
Definition avif.c:97
void dt_collection_update_query(const dt_collection_t *collection, dt_collection_change_t query_change, dt_collection_properties_t changed_property, GList *list)
@ DT_COLLECTION_PROP_UNDEF
Definition collection.h:142
@ DT_COLLECTION_CHANGE_RELOAD
Definition collection.h:151
@ IOP_CS_RAW
@ IOP_CS_RGB
const dt_aligned_pixel_t f
const dt_colormatrix_t matrix
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
gboolean dt_history_set_end(const int32_t imgid, const int32_t history_end)
gboolean dt_history_db_write_history_item(const int32_t imgid, const int num, const char *operation, const void *op_params, const int op_params_size, const int module_version, const gboolean enabled, const void *blendop_params, const int blendop_params_size, const int blendop_version, const int multi_priority, const char *multi_name)
int32_t dt_history_db_get_next_history_num(const int32_t imgid)
gboolean dt_image_is_matrix_correction_supported(const dt_image_t *img)
int32_t dt_image_copy_rename(const int32_t imgid, const int32_t filmid, const gchar *newname)
int32_t dt_image_move(const int32_t imgid, const int32_t filmid)
void dt_image_synch_xmps(const GList *img)
static sqlite3_stmt * _image_altered_stmt
static void _pop_undo(gpointer user_data, const dt_undo_type_t type, dt_undo_data_t data, const dt_undo_action_t action, GList **imgs)
char * dt_image_get_audio_path(const int32_t imgid)
gboolean dt_image_is_raw(const dt_image_t *img)
dt_image_pipe_class_t dt_image_pipe_class(const dt_image_t *img)
int dt_image_get_xmp_rating(const dt_image_t *img)
static int32_t _image_duplicate_with_version_ext(const int32_t imgid, const int32_t newversion)
int32_t dt_image_import(const int32_t film_id, const char *filename, gboolean raise_signals)
static gsize _image_stmt_mutex_inited
void dt_image_check_camera_missing_sample(const struct dt_image_t *img)
dt_image_orientation_t dt_image_get_orientation(const int32_t imgid)
static void _image_set_monochrome_flag(const int32_t imgid, gboolean monochrome, gboolean undo_on)
void dt_image_path_append_version(const int32_t imgid, char *pathname, size_t pathname_len)
static void _set_location(const int32_t imgid, const dt_image_geoloc_t *geoloc)
int dt_image_write_sidecar_file(const int32_t imgid)
static gboolean _image_matrix_has_data(const float *matrix, const int count)
int32_t dt_image_get_id(int32_t film_id, const gchar *filename)
int32_t dt_image_copy(const int32_t imgid, const int32_t filmid)
void dt_image_refresh_makermodel(dt_image_t *img)
int32_t dt_image_duplicate_no_reload(const int32_t imgid)
static const char * _image_buf_type_to_string(const dt_iop_buffer_type_t type)
int dt_image_monochrome_flags(const dt_image_t *img)
uint32_t dt_image_altered(const int32_t imgid)
int32_t dt_image_import_lua(const int32_t film_id, const char *filename)
void dt_image_get_datetime(const int32_t imgid, char *datetime)
const char * dt_image_pipe_class_name(const dt_image_pipe_class_t klass)
static void _geotag_undo_data_free(gpointer data)
char * dt_image_get_text_path_from_path(const char *image_path)
void dt_image_set_location(const int32_t imgid, const dt_image_geoloc_t *geoloc, const gboolean undo_on, const gboolean group_on)
static dt_pthread_mutex_t _write_timestamp_stmt_mutex
void dt_image_film_roll(const dt_image_t *img, char *pathname, size_t pathname_len)
void dt_image_print_exif(const dt_image_t *img, char *line, size_t line_len)
void dt_image_synch_all_xmp(const gchar *pathname)
void dt_image_get_location(const int32_t imgid, dt_image_geoloc_t *geoloc)
static void _datetime_undo_data_free(gpointer data)
gboolean dt_image_is_hdr(const dt_image_t *img)
int32_t dt_image_duplicate_with_version(const int32_t imgid, const int32_t newversion)
gboolean dt_image_is_monochrome(const dt_image_t *img)
void dt_image_print_debug_info(const dt_image_t *img, const char *context)
void dt_image_init(dt_image_t *img)
void dt_image_buffer_resolve_flags(dt_image_t *img)
static void _move_text_sidecar_if_present(const char *src_image_path, const char *dest_image_path, const gboolean overwrite)
void dt_image_set_datetimes(const GList *imgs, const GArray *dtime, const gboolean undo_on)
static void _write_timestamp_set_now(const int32_t imgid)
static int32_t _image_duplicate_with_version(const int32_t imgid, const int32_t newversion, const gboolean undo, const gboolean reload)
static int _write_sidecar_file_from_image_locked(const dt_image_t *img)
static void _image_set_datetimes(const GList *img, const GArray *dtime, GList **undo, const gboolean undo_on)
const char * dt_image_film_roll_name(const char *path)
static int64_t _write_timestamp_get(const int32_t imgid)
float dt_image_get_exposure_bias(const struct dt_image_t *image_storage)
GList * dt_image_find_xmps(const char *filename)
void dt_image_set_datetime(const GList *imgs, const char *datetime, const gboolean undo_on)
static void _pop_undo_execute(const int32_t imgid, const gboolean before, const gboolean after)
gboolean dt_image_is_ldr(const dt_image_t *img)
static int _nb_other_local_copy_for(const int32_t imgid)
gboolean dt_image_pipe_class_is_provisional(const dt_image_t *img)
void dt_image_path_append_version_no_db(int version, char *pathname, size_t pathname_len)
gboolean dt_image_needs_rawprepare(const dt_image_t *img)
void dt_image_set_monochrome_flag(const int32_t imgid, gboolean monochrome)
static void _image_set_datetime(const GList *img, const char *datetime, GList **undo, const gboolean undo_on)
static void _set_datetime(const int32_t imgid, const char *datetime)
void dt_image_set_images_locations(const GList *imgs, const GArray *gloc, const gboolean undo_on)
char * dt_image_get_audio_path_from_path(const char *image_path)
gboolean dt_image_use_monochrome_workflow(const dt_image_t *img)
int dt_image_local_copy_set(const int32_t imgid)
void dt_image_cleanup(void)
static void _write_timestamp_stmt_ensure(void)
static void _image_set_location(GList *imgs, const dt_image_geoloc_t *geoloc, GList **undo, const gboolean undo_on)
void dt_image_film_roll_directory(const dt_image_t *img, char *pathname, size_t pathname_len)
dt_image_path_source_t dt_image_choose_input_path(const dt_image_t *img, char *pathname, size_t pathname_len, gboolean force_cache)
static dt_pthread_mutex_t _image_stmt_mutex
void dt_image_remove(const int32_t imgid)
gboolean dt_image_is_sraw(const dt_image_t *img)
void dt_image_set_flip(const int32_t imgid, const dt_image_orientation_t orientation)
gboolean dt_image_is_mosaiced(const dt_image_t *img)
char * dt_image_camera_missing_sample_message(const struct dt_image_t *img, gboolean logmsg)
int dt_image_get_xmp_rating_from_flags(const int flags)
void dt_image_set_locations(const GList *imgs, const dt_image_geoloc_t *geoloc, const gboolean undo_on)
static void _copy_text_sidecar_if_present(const char *src_image_path, const char *dest_image_path)
static sqlite3_stmt * _write_timestamp_select_stmt
static void _image_stmt_mutex_ensure(void)
int dt_image_read_duplicates(const uint32_t id, const char *filename, const gboolean clear_selection)
static gsize _write_timestamp_stmt_mutex_inited
static void _image_set_images_locations(const GList *img, const GArray *gloc, GList **undo, const gboolean undo_on)
int dt_image_local_copy_reset(const int32_t imgid)
static int32_t _image_import_internal(const int32_t film_id, const char *filename, gboolean lua_locking, gboolean raise_signals)
gboolean dt_image_get_xmp_mode()
static char * _text_path_legacy_if_exists(const char *image_path)
char * dt_image_get_text_path(const int32_t imgid)
int32_t dt_image_get_id_full_path(const gchar *filename)
gboolean dt_image_safe_remove(const int32_t imgid)
void dt_image_set_xmp_rating(dt_image_t *img, const int rating)
char * dt_image_build_text_path_from_path(const char *image_path)
static const char * _image_colorspace_to_string(const dt_image_colorspace_t colorspace)
static char * _text_path_legacy_build(const char *image_path)
void dt_image_flip(const int32_t imgid, const int32_t cw)
static sqlite3_stmt * _write_timestamp_update_stmt
static gboolean _sidecar_is_up_to_date(const dt_image_t *img)
int32_t dt_image_duplicate(const int32_t imgid)
void dt_image_local_copy_synch()
int32_t dt_image_rename(const int32_t imgid, const int32_t filmid, const gchar *newname)
void dt_image_synch_xmp(const int selected)
void dt_get_dirname_from_imgid(gchar *dir, const int32_t imgid)
void dt_image_full_path(const int32_t imgid, char *pathname, size_t pathname_len, gboolean *from_cache, const char *calling_func)
Get the full path of an image out of the database.
static int _valid_glob_match(const char *const name, size_t offset)
void dt_image_history_changed(const int32_t imgid, const gboolean refresh_filmstrip)
gboolean dt_image_needs_demosaic(const dt_image_t *img)
void dt_image_set_provisional_dsc(dt_image_t *img)
void dt_image_local_copy_paths_from_fullpath(const char *fullpath, int32_t imgid, char *local_copy_path, size_t local_copy_len, char *local_copy_legacy_path, size_t local_copy_legacy_len)
int type
char * name
int dt_conf_get_int(const char *name)
void dt_conf_set_string(const char *name, const char *val)
const char * dt_conf_get_string_const(const char *name)
void dt_control_log(const char *msg,...)
Definition control.c:777
void dt_control_queue_redraw_center()
request redraw of center window. This redraws the center view within a gdk critical section to preven...
Definition control.c:877
void dt_control_save_xmp(const int32_t imgid)
void dt_control_save_xmps(const GList *imgids, const gboolean check_history)
darktable_t darktable
Definition darktable.c:183
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1600
#define UNKNOWN_IMAGE
Definition darktable.h:194
@ DT_DEBUG_CONTROL
Definition darktable.h:738
@ DT_DEBUG_IMAGEIO
Definition darktable.h:755
static void dt_free_gpointer(gpointer ptr)
Definition darktable.h:485
#define dt_free(ptr)
Definition darktable.h:478
#define DT_MAX_FILENAME_LEN
Definition darktable.h:1179
#define PATH_MAX
Definition darktable.h:1189
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:293
sqlite3 * dt_database_get(const dt_database_t *db)
Definition database.c:3653
gboolean dt_datetime_img_to_exif(char *exif, const size_t exif_size, const dt_image_t *img)
Definition datetime.c:241
GDateTime * dt_datetime_gtimespan_to_gdatetime(const GTimeSpan gts)
Definition datetime.c:431
GTimeSpan dt_datetime_now_to_gtimespan()
Definition datetime.c:223
void dt_datetime_exif_to_img(dt_image_t *img, const char *exif)
Definition datetime.c:229
#define DT_DATETIME_LENGTH
Definition datetime.h:37
#define DT_DEBUG_SQLITE3_PREPARE_V2(a, b, c, d, e)
Definition debug.h:107
#define DT_DEBUG_SQLITE3_BIND_TEXT(a, b, c, d, e)
Definition debug.h:118
#define DT_DEBUG_SQLITE3_BIND_INT(a, b, c)
Definition debug.h:115
#define DT_DEBUG_SQLITE3_BIND_INT64(a, b, c)
Definition debug.h:116
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:384
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Definition dtpthread.h:369
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:374
int supported(struct dt_imageio_module_storage_t *storage, struct dt_imageio_module_format_t *format)
Definition example.c:274
int dt_exif_xmp_write_with_imgpath(const dt_image_t *image, const char *filename, const char *imgpath)
Definition exif.cc:4506
int dt_exif_read(dt_image_t *img, const char *path)
Definition exif.cc:1752
int dt_exif_xmp_read(dt_image_t *img, const char *filename, const int history_only)
Definition exif.cc:3109
void dt_loc_get_user_cache_dir(char *cachedir, size_t bufsize)
GList * win_image_find_duplicates(const char *filename)
Definition filepath.c:30
void dt_iop_buffer_dsc_update_bpp(dt_iop_buffer_dsc_t *dsc)
Definition format.c:28
dt_iop_buffer_type_t
Definition format.h:44
@ TYPE_FLOAT
Definition format.h:46
@ TYPE_UNKNOWN
Definition format.h:45
@ TYPE_UINT8
Definition format.h:48
@ TYPE_UINT16
Definition format.h:47
int dt_grouping_remove_from_group(const int32_t image_id)
Definition grouping.c:59
void dt_grouping_add_grouped_images(GList **images)
Definition grouping.c:171
void dt_grouping_add_to_group(const int32_t group_id, const int32_t image_id)
Definition grouping.c:48
gboolean dt_history_copy_and_paste_on_image(const int32_t imgid, const int32_t dest_imgid, GList *ops, const gboolean copy_full, const dt_history_merge_strategy_t mode, const gboolean copy_iop_order, dt_hm_batch_state_t *batch)
@ DT_HISTORY_MERGE_REPLACE
void dt_history_snapshot_undo_create(const int32_t imgid, int *snap_id, int *history_end)
void dt_history_snapshot_undo_lt_history_data_free(gpointer data)
dt_undo_lt_history_t * dt_history_snapshot_item_init(void)
void dt_history_snapshot_undo_pop(gpointer user_data, dt_undo_type_t type, dt_undo_data_t data, dt_undo_action_t action, GList **imgs)
dt_image_colorspace_t
Definition image.h:178
@ DT_IMAGE_COLORSPACE_NONE
Definition image.h:179
@ DT_IMAGE_COLORSPACE_ADOBE_RGB
Definition image.h:181
@ DT_IMAGE_COLORSPACE_SRGB
Definition image.h:180
dt_image_path_source_t
Definition image.h:242
@ DT_IMAGE_PATH_NONE
Definition image.h:243
@ DT_IMAGE_PATH_LOCAL_COPY_LEGACY
Definition image.h:245
@ DT_IMAGE_PATH_ORIGINAL
Definition image.h:246
@ DT_IMAGE_PATH_LOCAL_COPY
Definition image.h:244
dt_image_orientation_t
Definition image.h:203
@ ORIENTATION_SWAP_XY
Definition image.h:208
@ ORIENTATION_FLIP_Y
Definition image.h:206
@ ORIENTATION_NULL
Definition image.h:204
@ ORIENTATION_FLIP_X
Definition image.h:207
@ DT_IMAGE_NO_LEGACY_PRESETS
Definition image.h:119
@ DT_IMAGE_HAS_ADDITIONAL_DNG_TAGS
Definition image.h:132
@ DT_IMAGE_HAS_WAV
Definition image.h:125
@ DT_IMAGE_AUTO_PRESETS_APPLIED
Definition image.h:117
@ DT_IMAGE_S_RAW
Definition image.h:134
@ DT_IMAGE_REMOVE
Definition image.h:115
@ DT_IMAGE_MOSAIC
Definition image.h:146
@ DT_IMAGE_MONOCHROME_BAYER
Definition image.h:138
@ DT_IMAGE_LOCAL_COPY
Definition image.h:121
@ DT_IMAGE_BUFFER_RESOLVED
Definition image.h:151
@ DT_IMAGE_RAW
Definition image.h:111
@ DT_IMAGE_REJECTED
Definition image.h:100
@ DT_IMAGE_4BAYER
Definition image.h:127
@ DT_IMAGE_MONOCHROME_WORKFLOW
Definition image.h:140
@ DT_IMAGE_HAS_TXT
Definition image.h:123
@ DT_IMAGE_MONOCHROME
Definition image.h:129
@ DT_IMAGE_HDR
Definition image.h:113
@ DT_IMAGE_LDR
Definition image.h:109
@ DT_IMAGE_MONOCHROME_PREVIEW
Definition image.h:136
dt_image_pipe_class_t
Mutually-exclusive classification of an image by the early-pipeline processing it requires....
Definition image.h:169
@ DT_IMAGE_PIPE_MOSAIC_RAW
Definition image.h:171
@ DT_IMAGE_PIPE_LINEAR_RAW
Definition image.h:172
@ DT_IMAGE_PIPE_RGB_LDR
Definition image.h:173
@ DT_IMAGE_PIPE_RGB_HDR
Definition image.h:174
@ DT_IMAGE_PIPE_UNKNOWN
Definition image.h:170
static dt_image_orientation_t dt_image_orientation(const dt_image_t *img)
Definition image.h:532
@ LOADER_UNKNOWN
Definition image.h:222
void dt_image_cache_read_release(dt_image_cache_t *cache, const dt_image_t *img)
dt_image_t * dt_image_cache_get_reload(dt_image_cache_t *cache, const int32_t imgid, char mode)
dt_image_t * dt_image_cache_get(dt_image_cache_t *cache, const int32_t imgid, char mode)
void dt_image_cache_write_release(dt_image_cache_t *cache, dt_image_t *img, dt_image_cache_write_mode_t mode)
void dt_image_cache_remove(dt_image_cache_t *cache, const int32_t imgid)
@ DT_IMAGE_CACHE_RELAXED
Definition image_cache.h:51
@ DT_IMAGE_CACHE_MINIMAL
Definition image_cache.h:54
@ DT_IMAGE_CACHE_SAFE
Definition image_cache.h:49
int bpp
gboolean dt_imageio_lookup_makermodel(const char *maker, const char *model, char *mk, int mk_len, char *md, int md_len, char *al, int al_len)
Definition imageio.c:1356
dt_image_flags_t dt_imageio_get_type_from_extension(const char *extension)
Map Exiv2 preview MIME types to decoder format identifiers.
Definition imageio.c:181
gboolean dt_lightroom_import(int32_t imgid, dt_develop_t *dev, gboolean iauto)
Definition lightroom.c:1052
float *const restrict const size_t k
#define CLAMPS(A, L, H)
Definition math.h:76
void dt_mipmap_cache_remove(dt_mipmap_cache_t *cache, const int32_t imgid, const gboolean flush_disk)
void dt_mipmap_cache_copy_thumbnails(const dt_mipmap_cache_t *cache, const uint32_t dst_imgid, const uint32_t src_imgid)
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
#define DT_VIEW_RATINGS_MASK
Definition ratings.h:41
void dt_selection_clear(dt_selection_t *selection)
Definition selection.c:266
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:366
@ DT_SIGNAL_GEOTAG_CHANGED
This signal is raised when a geotag is added/deleted/changed
Definition signal.h:136
@ DT_SIGNAL_TAG_CHANGED
This signal is raised when a tag is added/deleted/changed
Definition signal.h:130
@ DT_SIGNAL_IMAGE_IMPORT
This signal is raised when a new image is imported (not cloned) 1 uint32_t : the new image id no retu...
Definition signal.h:248
const float const int flip
char dt[DT_DATETIME_LENGTH]
struct dt_undo_t * undo
Definition darktable.h:815
struct dt_gui_gtk_t * gui
Definition darktable.h:803
struct dt_collection_t * collection
Definition darktable.h:809
struct dt_mipmap_cache_t * mipmap_cache
Definition darktable.h:804
struct dt_selection_t * selection
Definition darktable.h:810
GList * iop
Definition darktable.h:789
const struct dt_database_t * db
Definition darktable.h:807
struct dt_control_signal_t * signals
Definition darktable.h:802
struct dt_image_cache_t * image_cache
Definition darktable.h:805
struct dt_develop_t * develop
Definition darktable.h:798
struct dt_view_manager_t * view_manager
Definition darktable.h:800
dt_image_t image_storage
Definition develop.h:259
dt_ui_t * ui
Definition gtk.h:165
double latitude
Definition image.h:275
double elevation
Definition image.h:275
double longitude
Definition image.h:275
gboolean has_audio
Definition image.h:375
gboolean is_hdr
Definition image.h:378
float exif_exposure
Definition image.h:285
float pixel_aspect_ratio
Definition image.h:356
int32_t height
Definition image.h:315
gboolean camera_missing_sample
Definition image.h:302
GTimeSpan export_timestamp
Definition image.h:333
uint64_t history_hash
Definition image.h:322
char camera_model[64]
Definition image.h:298
float exif_focus_distance
Definition image.h:290
dt_boundingbox_t usercrop
Definition image.h:365
GTimeSpan import_timestamp
Definition image.h:333
gboolean is_bw
Definition image.h:376
int32_t group_id
Definition image.h:319
char camera_makermodel[128]
Definition image.h:300
float exif_exposure_bias
Definition image.h:286
uint16_t raw_black_level
Definition image.h:350
float exif_iso
Definition image.h:288
GList * dng_gain_maps
Definition image.h:368
dt_image_loader_t loader
Definition image.h:335
char camera_maker[64]
Definition image.h:297
uint32_t raw_white_point
Definition image.h:352
int32_t exif_inited
Definition image.h:283
float exif_aperture
Definition image.h:287
int32_t version
Definition image.h:319
int32_t crop_height
Definition image.h:316
uint64_t mipmap_hash
Definition image.h:329
char fullpath[PATH_MAX]
Definition image.h:305
dt_image_geoloc_t geoloc
Definition image.h:347
char camera_legacy_makermodel[128]
Definition image.h:301
int32_t flags
Definition image.h:319
dt_image_orientation_t orientation
Definition image.h:284
int32_t width
Definition image.h:315
float exif_focal_length
Definition image.h:289
char exif_maker[64]
Definition image.h:292
GTimeSpan change_timestamp
Definition image.h:333
uint32_t profile_size
Definition image.h:341
int32_t crop_y
Definition image.h:316
uint32_t history_items
Definition image.h:321
int rating
Definition image.h:372
gboolean has_localcopy
Definition image.h:374
char camera_alias[64]
Definition image.h:299
char exif_lens[128]
Definition image.h:294
char local_copy_path[PATH_MAX]
Definition image.h:306
GTimeSpan print_timestamp
Definition image.h:333
int32_t film_id
Definition image.h:319
int32_t crop_x
Definition image.h:316
int color_labels
Definition image.h:371
float d65_color_matrix[9]
Definition image.h:339
int32_t p_height
Definition image.h:315
dt_iop_buffer_dsc_t dsc
Definition image.h:337
int32_t p_width
Definition image.h:315
char exif_model[64]
Definition image.h:293
dt_image_colorspace_t colorspace
Definition image.h:342
float adobe_XYZ_to_CAM[4][3]
Definition image.h:362
uint8_t * profile
Definition image.h:340
char datetime[200]
Definition image.h:310
struct dt_cache_entry_t * cache_entry
Definition image.h:381
int32_t crop_width
Definition image.h:316
uint32_t group_members
Definition image.h:320
dt_image_raw_parameters_t legacy_flip
Definition image.h:344
char filename[DT_MAX_FILENAME_LEN]
Definition image.h:304
char folder[PATH_MAX]
Definition image.h:308
int32_t id
Definition image.h:319
uint16_t raw_black_level_separate[4]
Definition image.h:351
char filmroll[PATH_MAX]
Definition image.h:309
uint64_t self_hash
Definition image.h:330
char local_copy_legacy_path[PATH_MAX]
Definition image.h:307
dt_aligned_pixel_t wb_coeffs
Definition image.h:359
float exif_crop
Definition image.h:291
uint32_t fuji_rotation_pos
Definition image.h:355
gboolean is_bw_flow
Definition image.h:377
uint32_t filters
Definition format.h:60
uint16_t raw_black_level
Definition format.h:74
unsigned int channels
Definition format.h:54
struct dt_iop_buffer_dsc_t::@32 rawprepare
dt_iop_buffer_type_t datatype
Definition format.h:56
dt_aligned_pixel_t processed_maximum
Definition format.h:85
uint16_t raw_white_point
Definition format.h:75
dt_thumbtable_t * thumbtable_lighttable
dt_thumbtable_t * thumbtable_filmstrip
char after[DT_DATETIME_LENGTH]
char before[DT_DATETIME_LENGTH]
dt_image_geoloc_t after
dt_image_geoloc_t before
gboolean dt_tag_attach(const guint tagid, const int32_t imgid, const gboolean undo_on, const gboolean group_on)
Definition tags.c:485
gboolean dt_tag_detach_by_string(const char *name, const int32_t imgid, const gboolean undo_on, const gboolean group_on)
Definition tags.c:608
gboolean dt_tag_new(const char *name, guint *tagid)
Definition tags.c:179
#define MAX(a, b)
Definition thinplate.c:29
#define dt_thumbtable_refresh_thumbnail(table, imgid, reinit)
Definition thumbtable.h:292
void dt_undo_end_group(dt_undo_t *self)
Definition undo.c:149
void dt_undo_start_group(dt_undo_t *self, dt_undo_type_t type)
Definition undo.c:134
void dt_undo_record(dt_undo_t *self, gpointer user_data, dt_undo_type_t type, dt_undo_data_t data, void(*undo)(gpointer user_data, dt_undo_type_t type, dt_undo_data_t item, dt_undo_action_t action, GList **imgs), void(*free_data)(gpointer data))
Definition undo.c:163
dt_undo_type_t
Definition undo.h:39
@ DT_UNDO_GEOTAG
Definition undo.h:41
@ DT_UNDO_DATETIME
Definition undo.h:50
@ DT_UNDO_LT_HISTORY
Definition undo.h:48
@ DT_UNDO_FLAGS
Definition undo.h:49
@ DT_UNDO_DUPLICATE
Definition undo.h:51
void * dt_undo_data_t
Definition undo.h:67
dt_undo_action_t
Definition undo.h:62
@ DT_ACTION_UNDO
Definition undo.h:63
gchar * dt_util_str_replace(const gchar *string, const gchar *pattern, const gchar *substitute)
Definition utility.c:136
gboolean dt_util_test_image_file(const char *filename)
Definition utility.c:318
gchar * dt_util_normalize_path(const gchar *_input)
Definition utility.c:680
char * dt_util_format_exposure(const float exposuretime)
Definition utility.c:865
const dt_view_t * dt_view_manager_get_current_view(dt_view_manager_t *vm)
Definition view.c:141
@ DT_VIEW_DARKROOM
Definition view.h:79