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