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/paths.h" // DT_PATH_MAX
76#include "common/image_notify.h"
78#include "common/act_on.h"
80#include "develop/imageop.h"
81#include "common/collection.h"
82#include "system/dtpthread.h"
83#include "common/xmp_sidecar.h"
84#include "metadata/exif.h"
86#include "common/grouping.h"
87#include "common/film.h"
88#include "history/history.h"
89#include "database/database.h"
96#include "caches/image_cache.h"
99#include "caches/mipmap_cache.h"
100#include "metadata/notify.h"
101#include "metadata/ratings.h"
102#include "metadata/tags.h"
103#include "common/undo.h"
104#include "common/selection.h"
105#include "common/datetime.h"
106#include "common/conf.h"
107#include "control/control.h"
108#include "system/atomic.h"
109#include "develop/lightroom.h"
110#include "develop/develop.h"
111#include "views/view.h"
112#include "win/filepath.h"
113#include <assert.h>
114#include <ctype.h>
115#include <inttypes.h>
116#include <math.h>
117#include <sqlite3.h>
118#include <stdlib.h>
119#include <string.h>
120#include <strings.h>
121#ifndef _WIN32
122#include <glob.h>
123#endif
124#include <glib/gstdio.h>
125#include "common/utility.h"
126
128{
129 int32_t imgid;
130 gboolean before;
131 gboolean after;
133
140
147
154
155/* An image removed from the library holds nothing in memory worth keeping: the whole state
156 * is the rows database/removed_image_repository.c staged, and this is the ticket to them. */
157typedef struct dt_undo_remove_t
158{
159 int32_t imgid;
162
163static void _pop_undo_execute(const int32_t imgid, const gboolean before, const gboolean after);
164static int32_t _image_duplicate_with_version(const int32_t imgid, const int32_t newversion, const gboolean undo,
165 const gboolean reload);
166static void _image_remove(const int32_t imgid, const gboolean undo);
167static 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);
168
169static void _copy_text_sidecar_if_present(const char *src_image_path, const char *dest_image_path);
170static void _move_text_sidecar_if_present(const char *src_image_path, const char *dest_image_path, const gboolean overwrite);
171
172// NULL terminated list of supported non-RAW extensions
173// const char *dt_non_raw_extensions[]
174// = { ".jpeg", ".jpg", ".pfm", ".hdr", ".exr", ".pxn", ".tif", ".tiff", ".png",
175// ".j2c", ".j2k", ".jp2", ".jpc", ".gif", ".jpc", ".jp2", ".bmp", ".dcm",
176// ".jng", ".miff", ".mng", ".pbm", ".pnm", ".ppm", ".pgm", NULL };
177gboolean dt_image_is_raw(const dt_image_t *img)
178{
179 return (img->flags & DT_IMAGE_RAW) != 0;
180}
181
182// LDR / HDR are flag-only predicates now: no filename-extension sniffing. The flags are set by the
183// decoding codec from the real file content (the buffer datatype is not a dynamic-range signal:
184// every raster decodes into a TYPE_FLOAT working buffer regardless of LDR/HDR), so these report what
185// was really loaded, not what the file name suggested. They are kept as a public API so callers
186// don't open-code the bitmask test.
187gboolean dt_image_is_ldr(const dt_image_t *img)
188{
189 return (img->flags & DT_IMAGE_LDR) != 0;
190}
191
192gboolean dt_image_is_hdr(const dt_image_t *img)
193{
194 return (img->flags & DT_IMAGE_HDR) != 0;
195}
196
198{
200}
201
202static void _image_set_monochrome_flag(const int32_t imgid, gboolean monochrome, gboolean undo_on)
203{
204 dt_image_t *img = NULL;
205 gboolean changed = FALSE;
206
207 img = dt_image_cache_get(imgid, 'r');
208 if(img)
209 {
210 const int mask_bw = dt_image_monochrome_flags(img);
212
213 if((!monochrome) && (mask_bw & DT_IMAGE_MONOCHROME_PREVIEW))
214 {
215 // wanting it to be color found preview
216 img = dt_image_cache_get(imgid, 'w');
218 changed = TRUE;
219 }
220 if(monochrome && ((mask_bw == 0) || (mask_bw == DT_IMAGE_MONOCHROME_PREVIEW)))
221 {
222 // wanting monochrome and found color or just preview without workflow activation
223 img = dt_image_cache_get(imgid, 'w');
225 changed = TRUE;
226 }
227 if(changed)
228 {
229 const int mask = dt_image_monochrome_flags(img);
231
232 if(undo_on)
233 {
235 undomono->imgid = imgid;
236 undomono->before = mask_bw;
237 undomono->after = mask;
238 dt_undo_record(dt_undo_get_global(), NULL, DT_UNDO_FLAGS, undomono, _pop_undo, g_free);
239 }
240 }
241 }
242 else
243 fprintf(stderr,"[image] could not dt_image_cache_get imgid %i\n", imgid);
244}
245
246void dt_image_set_monochrome_flag(const int32_t imgid, gboolean monochrome)
247{
248 _image_set_monochrome_flag(imgid, monochrome, TRUE);
249}
250
251static void _pop_undo_execute(const int32_t imgid, const gboolean before, const gboolean after)
252{
253 _image_set_monochrome_flag(imgid, after, FALSE);
254}
255
256static gboolean _image_matrix_has_data(const float *matrix, const int count)
257{
258 float sum = 0.0f;
259 for(int i = 0; i < count; i++)
260 {
261 if(!isfinite(matrix[i])) return FALSE;
262 sum += fabsf(matrix[i]);
263 }
264 return sum > 0.0f;
265}
266
268{
269 // Whether a camera input matrix (and the white balance / color calibration it feeds) applies
270 // depends only on the colorimetry axis, never on the mosaic axis: an already-demosaiced raw
271 // (sRAW / linear DNG, e.g. DxO PureRAW) still carries raw colorimetry and an embedded matrix
272 // and must get matrix correction. A previous `S_RAW && dsc.filters == 0 -> FALSE` short-circuit
273 // wrongly excluded exactly those files, leaving white balance and color calibration disabled
274 // (green renders, issue #729).
275 if(!(img->flags & (DT_IMAGE_RAW | DT_IMAGE_S_RAW))) return FALSE;
276 if(img->flags & DT_IMAGE_MONOCHROME) return FALSE;
277
278 const gboolean has_d65 = _image_matrix_has_data(img->d65_color_matrix, 9);
279 const gboolean has_adobe = _image_matrix_has_data(&img->adobe_XYZ_to_CAM[0][0], 9);
280
281 return (has_d65 || has_adobe) ? TRUE : FALSE;
282}
283
289
294
295/* ------------------------------------------------------------------------------------------
296 * Canonical image-type API. See image.h and src/doc/image-type-detection.md.
297 * Each predicate tests exactly one independent flag fact; no filename sniffing here.
298 * ------------------------------------------------------------------------------------------ */
299
301{
302 return (img->flags & DT_IMAGE_BUFFER_RESOLVED) == 0;
303}
304
305gboolean dt_image_is_sraw(const dt_image_t *img)
306{
307 return (img->flags & DT_IMAGE_S_RAW) != 0;
308}
309
311{
312 return (img->flags & DT_IMAGE_MOSAIC) != 0;
313}
314
316{
317 // both mosaiced raw and already-demosaiced sRAW/linear-DNG carry raw colorimetry
318 return (img->flags & (DT_IMAGE_RAW | DT_IMAGE_S_RAW)) != 0;
319}
320
322{
323 const gboolean resolved = (img->flags & DT_IMAGE_BUFFER_RESOLVED) != 0;
324 const gboolean raw_colorimetry = (img->flags & (DT_IMAGE_RAW | DT_IMAGE_S_RAW)) != 0;
325
326 // Raw colorimetry takes precedence over the LDR/HDR bit-depth flags: a float mosaiced raw
327 // is flagged both RAW and HDR but must still be treated as a mosaiced raw, never as RGB HDR.
328 if(raw_colorimetry)
329 {
330 // Once decoded, the mosaic bit is authoritative and tells a mosaiced raw apart from an
331 // already-demosaiced raw (sRAW / linear DNG).
332 if(resolved)
334
335 // Provisional (not decoded yet, or a database predating DT_IMAGE_BUFFER_RESOLVED):
336 // DT_IMAGE_S_RAW is only ever set by a codec on a real decode (the extension detector
337 // never sets it), so it reliably means an already-demosaiced raw even without the resolved
338 // bit. Otherwise assume the common case of a mosaiced sensor raw; dt_image_buffer_resolve_flags()
339 // corrects the guess on first decode.
340 if((img->flags & DT_IMAGE_S_RAW) && !(img->flags & DT_IMAGE_RAW))
343 }
344
347
349}
350
352{
354}
355
357{
358 switch(klass)
359 {
360 case DT_IMAGE_PIPE_MOSAIC_RAW: return "mosaic-raw";
361 case DT_IMAGE_PIPE_LINEAR_RAW: return "linear-raw";
362 case DT_IMAGE_PIPE_RGB_LDR: return "rgb-ldr";
363 case DT_IMAGE_PIPE_RGB_HDR: return "rgb-hdr";
365 default: return "unknown";
366 }
367}
368
370{
371 if(IS_NULL_PTR(img)) return;
372
373 // The decoded buffer descriptor is the authoritative source for the mosaic axis.
374 if(img->dsc.filters != 0u)
375 img->flags |= DT_IMAGE_MOSAIC;
376 else
377 img->flags &= ~DT_IMAGE_MOSAIC;
378
379 // The LDR/HDR axis is NOT derivable from the buffer datatype: every raster codec decodes into a
380 // TYPE_FLOAT working buffer in RAM regardless of the file's real dynamic range (sRGB JPEG/PNG/WebP
381 // all land as float just like Radiance/PFM/EXR). The decoding codec is the authoritative source
382 // and has already set DT_IMAGE_LDR / DT_IMAGE_HDR from the actual file content, so we must not
383 // override it here from the datatype — doing so re-flagged every display-referred raster as HDR.
384 //
385 // The only normalization we still own is the raw-sensor case: mosaiced / sRAW integer data is
386 // scene-linear sensor data, neither display LDR nor HDR. A float raw (e.g. HDRMerge, legacy float
387 // DNG) is left flagged HDR exactly as the raw codec set it.
389 img->flags &= ~(DT_IMAGE_LDR | DT_IMAGE_HDR);
390
392}
393
395{
396 if(IS_NULL_PTR(img)) return;
397 // Never clobber a descriptor that a codec has already produced.
398 if(img->flags & DT_IMAGE_BUFFER_RESOLVED) return;
399
400 switch(dt_image_pipe_class(img))
401 {
403 img->dsc.channels = 4; img->dsc.datatype = TYPE_UINT8; img->dsc.filters = 0u; img->dsc.cst = IOP_CS_RGB;
404 break;
406 img->dsc.channels = 4; img->dsc.datatype = TYPE_FLOAT; img->dsc.filters = 0u; img->dsc.cst = IOP_CS_RGB;
407 break;
409 // filters is unknown until decode; leave 0 as a placeholder (the class is carried by
410 // flags, not by this provisional filters value).
411 img->dsc.channels = 1; img->dsc.datatype = TYPE_UINT16; img->dsc.filters = 0u; img->dsc.cst = IOP_CS_RAW;
412 break;
414 img->dsc.channels = 4; img->dsc.datatype = TYPE_FLOAT; img->dsc.filters = 0u; img->dsc.cst = IOP_CS_RAW;
415 break;
417 default:
418 return; // nothing reliable to seed
419 }
421}
422
424{
425 switch(type)
426 {
427 case TYPE_FLOAT:
428 return "float";
429 case TYPE_UINT16:
430 return "uint16";
431 case TYPE_UINT8:
432 return "uint8";
433 case TYPE_UNKNOWN:
434 default:
435 return "unknown";
436 }
437}
438
439static const char *_image_colorspace_to_string(const dt_image_colorspace_t colorspace)
440{
441 switch(colorspace)
442 {
444 return "sRGB";
446 return "AdobeRGB";
448 default:
449 return "none";
450 }
451}
452
453void dt_image_print_debug_info(const dt_image_t *img, const char *context)
454{
455 if(IS_NULL_PTR(img)) return;
456
457 const char *ctx = context ? context : "image";
458 const dt_iop_buffer_dsc_t *dsc = &img->dsc;
459 const gboolean is_raw = dt_image_is_raw(img);
460 const gboolean is_ldr = dt_image_is_ldr(img);
461 const gboolean is_hdr = dt_image_is_hdr(img);
462 const gboolean is_mono = dt_image_is_monochrome(img);
463 const gboolean mono_workflow = dt_image_use_monochrome_workflow(img);
464 const int mono_flags = dt_image_monochrome_flags(img);
465 const gboolean mosaic = dsc->filters != 0u;
466 const gboolean xtrans = dsc->filters == 9u;
467 const gboolean bayer = mosaic && !xtrans;
468 const size_t bpp = dsc->bpp;
469 int bit_depth = 0;
470
471 switch(dsc->datatype)
472 {
473 case TYPE_FLOAT:
474 bit_depth = 32;
475 break;
476 case TYPE_UINT16:
477 bit_depth = 16;
478 break;
479 case TYPE_UINT8:
480 bit_depth = 8;
481 break;
482 case TYPE_UNKNOWN:
483 default:
484 if(dsc->channels != 0)
485 bit_depth = (int)(bpp * 8 / dsc->channels);
486 break;
487 }
488
489 const unsigned int flags = (unsigned int)img->flags;
490
492 "[image debug] %s id=%d filename='%s' fullpath='%s'\n",
493 ctx, img->id, img->filename, img->fullpath);
495 "[image debug] %s size=%dx%d crop=%dx%d+%d+%d orientation=%d psize=%dx%d pixel_aspect=%.6f\n",
496 ctx, img->width, img->height, img->crop_width, img->crop_height, img->crop_x, img->crop_y,
497 img->orientation, img->p_width, img->p_height, img->pixel_aspect_ratio);
499 "[image debug] %s flags=0x%08x raw=%d non_raw=%d ldr=%d hdr=%d sraw=%d 4bayer=%d mosaic=%d xtrans=%d "
500 "bayer=%d mono=%d mono_flags=0x%x mono_workflow=%d mono_preview=%d mono_bayer=%d bw=%d bw_flow=%d "
501 "local_copy=%d has_txt=%d has_wav=%d addl_dng=%d auto_presets=%d no_legacy_presets=%d rejected=%d remove=%d "
502 "has_localcopy=%d has_audio=%d is_hdr_field=%d\n",
503 ctx, flags, is_raw, !is_raw, is_ldr, is_hdr,
504 (flags & DT_IMAGE_S_RAW) != 0, (flags & DT_IMAGE_4BAYER) != 0, mosaic, xtrans, bayer, is_mono,
505 mono_flags, mono_workflow, (flags & DT_IMAGE_MONOCHROME_PREVIEW) != 0,
506 (flags & DT_IMAGE_MONOCHROME_BAYER) != 0, img->is_bw, img->is_bw_flow,
510 img->has_localcopy, img->has_audio, img->is_hdr);
512 "[image debug] %s buf: channels=%u datatype=%s bit_depth=%d bpp=%" G_GSIZE_FORMAT " filters=%u cst=%d colorspace=%s "
513 "processed_max=[%.4f %.4f %.4f %.4f]\n",
514 ctx, dsc->channels, _image_buf_type_to_string(dsc->datatype), bit_depth, bpp, dsc->filters, dsc->cst,
516 dsc->processed_maximum[2], dsc->processed_maximum[3]);
518 "[image debug] %s colorspace=enum:%s d65_color_matrix=[%.6f %.6f %.6f %.6f %.6f %.6f %.6f %.6f %.6f]\n",
520 img->d65_color_matrix[2], img->d65_color_matrix[3], img->d65_color_matrix[4], img->d65_color_matrix[5],
521 img->d65_color_matrix[6], img->d65_color_matrix[7], img->d65_color_matrix[8]);
523 "[image debug] %s raw: black=%u separate=[%u %u %u %u] white=%u rawprepare=[%u %u]\n",
528 "[image debug] %s class=%s state=%s needs_rawprepare=%d needs_demosaic=%d is_mosaiced=%d is_sraw=%d has_matrix=%d\n",
530 dt_image_pipe_class_is_provisional(img) ? "provisional" : "resolved",
533}
534
535const char *dt_image_film_roll_name(const char *path)
536{
537 const char *folder = path + strlen(path);
538 const int numparts = CLAMPS(dt_conf_get_int("show_folder_levels"), 1, 5);
539 int count = 0;
540 while(folder > path)
541 {
542
543#ifdef _WIN32
544 // in Windows, both \ and / can be folder separator
545 if(*folder == G_DIR_SEPARATOR || *folder == '/')
546#else
547 if(*folder == G_DIR_SEPARATOR)
548#endif
549
550 if(++count >= numparts)
551 {
552 ++folder;
553 break;
554 }
555 --folder;
556 }
557 return folder;
558}
559
560void dt_image_film_roll_directory(const dt_image_t *img, char *pathname, size_t pathname_len)
561{
563 if(folder)
564 {
565 g_strlcpy(pathname, folder, pathname_len);
567 }
568 pathname[pathname_len - 1] = '\0';
569}
570
571void dt_image_film_roll(const dt_image_t *img, char *pathname, size_t pathname_len)
572{
573 if(img->film_id < 0)
574 {
575 g_strlcpy(pathname, _("orphaned image"), pathname_len);
576 return;
577 }
578
580 if(folder)
581 {
582 g_strlcpy(pathname, dt_image_film_roll_name(folder), pathname_len);
584 }
585 else
586 {
587 g_strlcpy(pathname, _("orphaned image"), pathname_len);
588 }
589 pathname[pathname_len - 1] = '\0';
590}
591
592// dt_image_get_xmp_mode() is on the hot path: called from many threads (image cache
593// write-release, export/save-xmp jobs...) for every single image. Re-parsing the conf string on
594// every call meant every reader raced every writer on the same key -- a torn read could
595// masquerade as an unrecognized/disabled value and silently skip writing a sidecar. The setting
596// itself changes only when the user acts (preferences dialog) or once at startup, so it is read
597// from conf exactly those two times and cached here the rest of the time -- same lifecycle as
598// dt_mipmap_cache_settings_t in darktable.c: "read at startup, re-read when the user changes it,
599// never anywhere else". dt_image_xmp_mode_refresh_from_conf() does the actual conf read+sanitize
600// and is wired to DT_SIGNAL_PREFERENCES_CHANGE in dt_init().
601static dt_atomic_int _xmp_mode_cache = 1; // matches the confgen default (TRUE) until first refresh
602
604{
606}
607
609{
610 // Write sidecars when the setting is absent, consistently with the default exposed in the preferences.
611 gboolean res = TRUE;
612 char *config = dt_conf_get_string("write_sidecar_files");
613 if(!IS_NULL_PTR(config))
614 {
615 res = FALSE;
616
617 // Darktable > 3.6
618 if(!strcmp(config, "after edit") || !strcmp(config, "on import") || !strcmp(config, "always")) res = TRUE;
619
620 // Darktable < 3.8 and Ansel
621 else if(!strcmp(config, "TRUE"))
622 res = TRUE;
623 }
624
625 // sanitize keys, but only if they actually need it -- this now only ever runs at startup and
626 // on user-initiated preference changes, so there is no concurrent reader/writer race left to
627 // worry about; the guard is just to avoid rewriting an already-canonical value.
628 const char *sanitized = res ? "TRUE" : "FALSE";
629 if(IS_NULL_PTR(config) || strcmp(config, sanitized)) dt_conf_set_string("write_sidecar_files", sanitized);
630
631 dt_free(config);
632
634}
635
636gboolean dt_image_safe_remove(const int32_t imgid)
637{
638 // always safe to remove if we do not have .xmp
639 if(!dt_image_get_xmp_mode()) return TRUE;
640
641 // check whether the original file is accessible
642 char pathname[DT_PATH_MAX] = { 0 };
643 gboolean from_cache = TRUE;
644
645 dt_image_full_path(imgid, pathname, sizeof(pathname), &from_cache, __FUNCTION__);
646
647 if(!from_cache)
648 return TRUE;
649
650 else
651 {
652 // finally check if we have a .xmp for the local copy. If no modification done on the local copy it is safe
653 // to remove.
654 g_strlcat(pathname, ".xmp", sizeof(pathname));
655 return !g_file_test(pathname, G_FILE_TEST_EXISTS);
656 }
657}
658
660 size_t pathname_len, gboolean force_cache)
661{
662 if(IS_NULL_PTR(img) || IS_NULL_PTR(pathname)) return DT_IMAGE_PATH_NONE;
663 pathname[0] = '\0';
664
665 // Start with local copies as file I/O will be better if we have a choice
666 if(img->flags & DT_IMAGE_LOCAL_COPY || force_cache)
667 {
668 if(img->local_copy_path[0] && g_file_test(img->local_copy_path, G_FILE_TEST_EXISTS))
669 {
670 g_strlcpy(pathname, img->local_copy_path, pathname_len);
672 }
673
674 if(img->local_copy_legacy_path[0] && g_file_test(img->local_copy_legacy_path, G_FILE_TEST_EXISTS))
675 {
676 g_strlcpy(pathname, img->local_copy_legacy_path, pathname_len);
678 }
679
680 // Local copy flag might be stale (cache cleared, moved file, etc.). If local copy is missing and
681 // we are not explicitly forcing the cache, fall back to the original image.
682 if(!force_cache && img->fullpath[0] && g_file_test(img->fullpath, G_FILE_TEST_EXISTS))
683 {
684 g_strlcpy(pathname, img->fullpath, pathname_len);
686 }
687 }
688 // Forcing the cache should not consider the original image
689 else
690 {
691 if(img->fullpath[0] && g_file_test(img->fullpath, G_FILE_TEST_EXISTS))
692 {
693 g_strlcpy(pathname, img->fullpath, pathname_len);
695 }
696 }
697
698 // In case the local copy flag was not set properly, but the original file failed
699 // last-chance attempt at getting a possibly forgotten local copy to have some input
700 if(img->local_copy_path[0] && g_file_test(img->local_copy_path, G_FILE_TEST_EXISTS))
701 {
702 g_strlcpy(pathname, img->local_copy_path, pathname_len);
704 }
705
706 return DT_IMAGE_PATH_NONE;
707}
708
718void dt_image_full_path(const int32_t imgid, char *pathname, size_t pathname_len, gboolean *from_cache, const char *calling_func)
719{
720 if(imgid < 0) return;
722 const gboolean prefer_cache = (from_cache && *from_cache);
723
724 const dt_image_t *img = dt_image_cache_get(imgid, 'r');
725 if(img)
726 {
727 // Preserve legacy semantics: `from_cache = TRUE` means "try local copy first, then fall back to original",
728 // not "only local copy".
729 if(prefer_cache)
730 {
731 source = dt_image_choose_input_path(img, pathname, pathname_len, TRUE);
732 if(source == DT_IMAGE_PATH_NONE)
733 source = dt_image_choose_input_path(img, pathname, pathname_len, FALSE);
734 }
735 else
736 {
737 source = dt_image_choose_input_path(img, pathname, pathname_len, FALSE);
738 }
740 }
741
742 if(from_cache)
743 *from_cache = (source == DT_IMAGE_PATH_LOCAL_COPY
745
746}
747
748void dt_image_local_copy_paths_from_fullpath(const char *fullpath, int32_t imgid, char *local_copy_path,
749 size_t local_copy_len, char *local_copy_legacy_path,
750 size_t local_copy_legacy_len)
751{
752 if(local_copy_path) local_copy_path[0] = '\0';
753 if(local_copy_legacy_path) local_copy_legacy_path[0] = '\0';
754 if(IS_NULL_PTR(fullpath) || !*fullpath || imgid <= 0 || IS_NULL_PTR(local_copy_path) || IS_NULL_PTR(local_copy_legacy_path)) return;
755
756 char *md5_filename = g_compute_checksum_for_string(G_CHECKSUM_MD5, fullpath, strlen(fullpath));
757 if(IS_NULL_PTR(md5_filename)) return;
758
759 char cachedir[DT_PATH_MAX] = { 0 };
760 dt_loc_get_user_cache_dir(cachedir, sizeof(cachedir));
761
762 const char *c = fullpath + strlen(fullpath);
763 while(*c != '.' && c > fullpath) c--;
764
765 // cache filename old format: <cachedir>/img-<id>-<MD5>.<ext>
766 // for upward compatibility we check for the old name, if found we return it
767 snprintf(local_copy_legacy_path, local_copy_legacy_len, "%s/img-%d-%s%s", cachedir, imgid, md5_filename, c);
768
769 // cache filename format: <cachedir>/img-<MD5>.<ext>
770 snprintf(local_copy_path, local_copy_len, "%s/img-%s%s", cachedir, md5_filename, c);
771
772 dt_free(md5_filename);
773}
774
775void dt_image_path_append_version_no_db(int version, char *pathname, size_t pathname_len)
776{
777 // the "first" instance (version zero) does not get a version suffix
778 if(version > 0)
779 {
780 // add version information:
781 char *filename = g_strdup(pathname);
782
783 char *c = pathname + strlen(pathname);
784 while(*c != '.' && c > pathname) c--;
785 snprintf(c, pathname + pathname_len - c, "_%02d", version);
786 c = pathname + strlen(pathname);
787 char *c2 = filename + strlen(filename);
788 while(*c2 != '.' && c2 > filename) c2--;
789 g_strlcpy(c, c2, pathname + pathname_len - c);
790 dt_free(filename);
791 }
792}
793
794void dt_image_path_append_version(const int32_t imgid, char *pathname, size_t pathname_len)
795{
796 // get duplicate suffix
797 const int version = dt_image_repository_get_version(imgid);
798
799 dt_image_path_append_version_no_db(version, pathname, pathname_len);
800}
801
802void dt_image_print_exif(const dt_image_t *img, char *line, size_t line_len)
803{
804 char *exposure_str = dt_util_format_exposure(img->exif_exposure);
805
806 snprintf(line, line_len, "%s f/%.1f %dmm ISO %d", exposure_str, img->exif_aperture, (int)img->exif_focal_length,
807 (int)img->exif_iso);
808
809 dt_free(exposure_str);
810}
811
813{
814 return (flags & DT_IMAGE_REJECTED)
815 ? -1 // rejected image = -1
816 : (flags & DT_VIEW_RATINGS_MASK); // others = 0 .. 5
817}
818
823
824void dt_image_set_xmp_rating(dt_image_t *img, const int rating)
825{
826 // clean flags stars and rejected
828
829 if(rating == -2) // assuming that value -2 cannot be found
830 {
831 img->flags |= (DT_VIEW_RATINGS_MASK & 0);
832 }
833 else if(rating == -1)
834 {
835 img->flags |= DT_IMAGE_REJECTED;
836 }
837 else
838 {
839 img->flags |= (DT_VIEW_RATINGS_MASK & rating);
840 }
841}
842
843void dt_image_get_location(const int32_t imgid, dt_image_geoloc_t *geoloc)
844{
845 const dt_image_t *img = dt_image_cache_get(imgid, 'r');
846 geoloc->longitude = img->geoloc.longitude;
847 geoloc->latitude = img->geoloc.latitude;
848 geoloc->elevation = img->geoloc.elevation;
850}
851
852static void _set_location(const int32_t imgid, const dt_image_geoloc_t *geoloc)
853{
854 /* fetch image from cache */
855 dt_image_t *image = dt_image_cache_get(imgid, 'w');
856
857 memcpy(&image->geoloc, geoloc, sizeof(dt_image_geoloc_t));
858
860}
861
862static void _set_datetime(const int32_t imgid, const char *datetime)
863{
864 /* fetch image from cache */
865 dt_image_t *image = dt_image_cache_get(imgid, 'w');
866
867 dt_datetime_exif_to_img(image, datetime);
868
870}
871
872static 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)
873{
874 if(type == DT_UNDO_GEOTAG)
875 {
876 int i = 0;
877
878 for(GList *list = (GList *)data; list; list = g_list_next(list))
879 {
880 dt_undo_geotag_t *undogeotag = (dt_undo_geotag_t *)list->data;
881 const dt_image_geoloc_t *geoloc = (action == DT_ACTION_UNDO) ? &undogeotag->before : &undogeotag->after;
882
883 _set_location(undogeotag->imgid, geoloc);
884
885 *imgs = g_list_prepend(*imgs, GINT_TO_POINTER(undogeotag->imgid));
886 i++;
887 }
888 if(i > 1) dt_control_log((action == DT_ACTION_UNDO)
889 ? _("geo-location undone for %d images")
890 : _("geo-location re-applied to %d images"), i);
891 // the handler copies the list; ours stays ours (the raise it replaces took ownership,
892 // which is why this site used to g_list_copy)
894 }
895 else if(type == DT_UNDO_DATETIME)
896 {
897 int i = 0;
898
899 for(GList *list = (GList *)data; list; list = g_list_next(list))
900 {
901 dt_undo_datetime_t *undodatetime = (dt_undo_datetime_t *)list->data;
902
903 _set_datetime(undodatetime->imgid, (action == DT_ACTION_UNDO)
904 ? undodatetime->before : undodatetime->after);
905
906 *imgs = g_list_prepend(*imgs, GINT_TO_POINTER(undodatetime->imgid));
907 i++;
908 }
909 if(i > 1) dt_control_log((action == DT_ACTION_UNDO)
910 ? _("date/time undone for %d images")
911 : _("date/time re-applied to %d images"), i);
912 }
913 else if(type == DT_UNDO_DUPLICATE)
914 {
916
917 if(action == DT_ACTION_UNDO)
918 {
919 // remove image
921 }
922 else
923 {
924 // restore image, note that we record the new imgid created while
925 // restoring the duplicate.
927 *imgs = g_list_prepend(*imgs, GINT_TO_POINTER(undo->new_imgid));
928 }
929 }
930 else if(type == DT_UNDO_REMOVE)
931 {
932 const dt_undo_remove_t *undo = (const dt_undo_remove_t *)data;
933
934 /* Undo puts the rows back, redo takes them out again -- and in both directions the
935 * snapshot stays: it is the undo record's, and the record can be popped either way any
936 * number of times. It is dropped only when the record itself is, by
937 * _remove_undo_data_free() below. */
938 GList *one = g_list_prepend(NULL, GINT_TO_POINTER(undo->imgid));
939
940 if(action == DT_ACTION_UNDO)
941 {
943 {
944 /* DT_IMAGE_REMOVE is written to the row BEFORE the removal runs, so it is part of
945 * what was staged -- and every collection query filters that flag out, which would
946 * bring the image back into the database and into no view. The image is not marked
947 * for deletion any more, so the flag goes. */
949
950 /* The removal emptied the image cache and the mipmap cache of this image, and neither
951 * refills itself: the mipmap cache regenerates only on explicit removal, and the image
952 * cache's history_items -- the "altered" flag deciding raw processing over the unedited
953 * embedded JPEG -- would be whatever the entry held before. Without this the image comes
954 * back looking undeveloped, or as a thumbnail that never renders at all. Reloading also
955 * re-reads the flags cleared just above, so no stale cache entry can write DT_IMAGE_REMOVE
956 * back over the restored row. TRUE: this is a lighttable operation, the filmstrip may
957 * refresh with the grid. */
959
960 /* The collection is the last one: nothing about a restored row reaches the grid until
961 * the query is re-run. */
963 DT_COLLECTION_PROP_UNDEF, g_list_copy(one));
964 /* Restoring the last image of a film roll brings the roll back with it, so the
965 * folder list has to be told; the notifier keeps that a layer-1 statement. */
967 *imgs = g_list_prepend(*imgs, GINT_TO_POINTER(undo->imgid));
968 }
969 else
970 fprintf(stderr, "[dt_image_remove] fails to restore image %" PRId32 "\n", undo->imgid);
971 }
972 else
973 {
974 // not added to *imgs: dt_undo_do_undo() ends by writing the XMP of every image it
975 // names, and this one no longer has a row to write from.
976 _image_remove(undo->imgid, FALSE);
978 DT_COLLECTION_PROP_UNDEF, g_list_copy(one));
980 }
981
982 g_list_free(one);
983 }
984 else if(type == DT_UNDO_FLAGS)
985 {
986 dt_undo_monochrome_t *undomono = (dt_undo_monochrome_t *)data;
987
988 const gboolean before = (action == DT_ACTION_UNDO) ? undomono->after : undomono->before;
989 const gboolean after = (action == DT_ACTION_UNDO) ? undomono->before : undomono->after;
990 _pop_undo_execute(undomono->imgid, before, after);
991 *imgs = g_list_prepend(*imgs, GINT_TO_POINTER(undomono->imgid));
992 }
993}
994
995static void _geotag_undo_data_free(gpointer data)
996{
997 GList *l = (GList *)data;
998 g_list_free_full(l, dt_free_gpointer);
999 l = NULL;
1000}
1001
1002static void _image_set_location(GList *imgs, const dt_image_geoloc_t *geoloc, GList **undo, const gboolean undo_on)
1003{
1004 for(GList *images = imgs; images; images = g_list_next(images))
1005 {
1006 const int32_t imgid = GPOINTER_TO_INT(images->data);
1007
1008 if(undo_on)
1009 {
1010 dt_undo_geotag_t *undogeotag = (dt_undo_geotag_t *)malloc(sizeof(dt_undo_geotag_t));
1011 undogeotag->imgid = imgid;
1012 dt_image_get_location(imgid, &undogeotag->before);
1013
1014 memcpy(&undogeotag->after, geoloc, sizeof(dt_image_geoloc_t));
1015
1016 *undo = g_list_append(*undo, undogeotag);
1017 }
1018
1019 _set_location(imgid, geoloc);
1020 }
1021}
1022
1023void dt_image_set_locations(const GList *imgs, const dt_image_geoloc_t *geoloc, const gboolean undo_on)
1024{
1025 if(imgs)
1026 {
1027 GList *undo = NULL;
1029
1030 _image_set_location((GList *)imgs, geoloc, &undo, undo_on);
1031
1032 if(undo_on)
1033 {
1036 }
1037 }
1038}
1039
1040void dt_image_set_location(const int32_t imgid, const dt_image_geoloc_t *geoloc, const gboolean undo_on, const gboolean group_on)
1041{
1042 GList *imgs = NULL;
1043 if(imgid == UNKNOWN_IMAGE)
1044 imgs = dt_act_on_get_images();
1045 else
1046 imgs = g_list_prepend(imgs, GINT_TO_POINTER(imgid));
1047 if(group_on) dt_grouping_add_grouped_images(&imgs);
1048 dt_image_set_locations(imgs, geoloc, undo_on);
1049 g_list_free(imgs);
1050 imgs = NULL;
1051}
1052
1053static void _image_set_images_locations(const GList *img, const GArray *gloc,
1054 GList **undo, const gboolean undo_on)
1055{
1056 int i = 0;
1057 for(GList *imgs = (GList *)img; imgs; imgs = g_list_next(imgs))
1058 {
1059 const int32_t imgid = GPOINTER_TO_INT(imgs->data);
1060 const dt_image_geoloc_t *geoloc = &g_array_index(gloc, dt_image_geoloc_t, i);
1061 if(undo_on)
1062 {
1063 dt_undo_geotag_t *undogeotag = (dt_undo_geotag_t *)malloc(sizeof(dt_undo_geotag_t));
1064 undogeotag->imgid = imgid;
1065 dt_image_get_location(imgid, &undogeotag->before);
1066
1067 memcpy(&undogeotag->after, geoloc, sizeof(dt_image_geoloc_t));
1068
1069 *undo = g_list_prepend(*undo, undogeotag);
1070 }
1071
1072 _set_location(imgid, geoloc);
1073 i++;
1074 }
1075}
1076
1077void dt_image_set_images_locations(const GList *imgs, const GArray *gloc, const gboolean undo_on)
1078{
1079 if(IS_NULL_PTR(imgs) || IS_NULL_PTR(gloc) || (g_list_length((GList *)imgs) != gloc->len))
1080 return;
1081 GList *undo = NULL;
1083
1084 _image_set_images_locations(imgs, gloc, &undo, undo_on);
1085
1086 if(undo_on)
1087 {
1090 }
1091}
1092
1093void dt_image_history_changed(const int32_t imgid, const gboolean refresh_filmstrip)
1094{
1095 if(imgid <= 0) return;
1096
1097 // Reload the cached image metadata from the DB. The caller has already persisted the new
1098 // history there; this refreshes history_items (the count of history entries) in the shared
1099 // image cache. history_items is the "altered" flag that the thumbnail regeneration uses to
1100 // pick raw processing over the (unedited) embedded JPEG, so a stale count makes edits and
1101 // rotations appear to have no effect on the thumbnail (issues #647, #861).
1102 dt_image_t *image = dt_image_cache_get_reload(imgid, 'w');
1103 if(image)
1105
1106 // Drop the stale rendered thumbnail. The mipmap cache regenerates purely on explicit removal,
1107 // never by comparing history hashes, so this is mandatory after any development change.
1109
1110 // The filmstrip is best-effort: refreshing it spawns an export thread that competes with the
1111 // realtime darkroom main preview. Darkroom write paths pass FALSE; lighttable ops pass TRUE.
1112 // No "is there a GUI?" guard needed: with no handler registered this is a no-op.
1113 dt_thumbnail_notify_image_changed(imgid, refresh_filmstrip);
1114}
1115
1116void dt_image_set_flip(const int32_t imgid, const dt_image_orientation_t orientation)
1117{
1118 // push new orientation to sql via additional history entry:
1119 const int iop_flip_MODVER = 2;
1120 const int num = dt_history_repository_get_next_num(imgid);
1121 dt_history_repository_write_item(imgid, num, "flip", &orientation, sizeof(int32_t), iop_flip_MODVER, 1,
1122 NULL, 0, 0, 0, "");
1123 dt_history_repository_set_end(imgid, num + 1);
1124 dt_control_save_xmp(imgid);
1125
1126 // Refresh the cached metadata and thumbnail. Without this the stale history_items keeps the
1127 // image flagged unedited, so the rotated raw keeps showing its (unrotated) embedded JPEG and
1128 // the rotation appears to do nothing unless "never use embedded JPEG" is forced (issue #647).
1130}
1131
1134{
1135 float top = in[0];
1136 float left = in[1];
1137 float bottom = in[2];
1138 float right = in[3];
1139
1140 /* Mirror, then swap, in the exact order iop/flip.c's distort_transform() applies to points:
1141 * x -> width - x, y -> height - y, then (x, y) -> (y, x). On normalized edge coordinates a
1142 * mirror exchanges the two edges of that axis, and the swap exchanges the two axes. */
1143 if(orientation & ORIENTATION_FLIP_X)
1144 {
1145 const float new_left = 1.0f - right;
1146 right = 1.0f - left;
1147 left = new_left;
1148 }
1149
1150 if(orientation & ORIENTATION_FLIP_Y)
1151 {
1152 const float new_top = 1.0f - bottom;
1153 bottom = 1.0f - top;
1154 top = new_top;
1155 }
1156
1157 if(orientation & ORIENTATION_SWAP_XY)
1158 {
1159 const float swap_top = left;
1160 const float swap_bottom = right;
1161 left = top;
1162 right = bottom;
1163 top = swap_top;
1164 bottom = swap_bottom;
1165 }
1166
1167 out[0] = top;
1168 out[1] = left;
1169 out[2] = bottom;
1170 out[3] = right;
1171}
1172
1174{
1175 box[0] = box[1] = 0.0f;
1176 box[2] = box[3] = 1.0f;
1177
1178 if(IS_NULL_PTR(img) || img->usercrop_status != DT_IMAGE_USERCROP_VALID) return FALSE;
1179
1180 for(int i = 0; i < 4; i++) box[i] = img->usercrop[i];
1181 return TRUE;
1182}
1183
1184gboolean dt_image_resolve_usercrop(const int32_t imgid, dt_boundingbox_t box)
1185{
1186 box[0] = box[1] = 0.0f;
1187 box[2] = box[3] = 1.0f;
1188
1189 if(imgid < 1 || !dt_image_cache_is_ready()) return FALSE;
1190
1191 char filename[DT_PATH_MAX] = { 0 };
1192 const dt_image_t *img = dt_image_cache_get(imgid, 'r');
1193 if(IS_NULL_PTR(img)) return FALSE;
1194
1196 {
1197 // Already answered for this image, in this session.
1198 const gboolean valid = dt_image_get_usercrop(img, box);
1200 return valid;
1201 }
1202
1203 /* DT_IMAGE_HAS_ADDITIONAL_DNG_TAGS is persisted and is set at import for every image that has
1204 * a usable DefaultUserCrop (it also covers DNG opcodes, so it is a superset -- which is what we
1205 * want here: no valid framing can hide behind a cleared bit). Images without it can be settled
1206 * without touching the file at all, which keeps this off the hot path for whole libraries. */
1208 {
1209 // Cheap enough to redo on every call -- deliberately not memoized, so that populating a whole
1210 // library's thumbnails does not take one image-cache write lock per image to record a "no".
1212 return FALSE;
1213 }
1214
1215 const gboolean has_file
1216 = (dt_image_choose_input_path(img, filename, sizeof(filename), FALSE) != DT_IMAGE_PATH_NONE);
1218 if(!has_file) return FALSE;
1219
1220 /* Parse into a throwaway image, holding no lock: the crop lives in runtime-only state, so a
1221 * thumbnail worker starting from a database-loaded cache entry has to read the file itself.
1222 * Two threads racing here compute the same answer from the same file, so the duplicated work
1223 * is wasteful at worst -- which is cheaper than serializing file I/O behind a write lock. */
1224 dt_image_t probe;
1225 dt_image_init(&probe);
1226 probe.id = imgid;
1227 dt_exif_read_usercrop(&probe, filename);
1228
1229 dt_image_t *write = dt_image_cache_get(imgid, 'w');
1230 if(!IS_NULL_PTR(write))
1231 {
1232 write->usercrop_status = probe.usercrop_status;
1233 for(int i = 0; i < 4; i++) write->usercrop[i] = probe.usercrop[i];
1235 }
1236
1237 return dt_image_get_usercrop(&probe, box);
1238}
1239
1241 dt_boundingbox_t box)
1242{
1243 if(!dt_image_get_usercrop(img, box)) return FALSE;
1244
1245 dt_image_orient_boundingbox(box, orientation, box);
1246 return TRUE;
1247}
1248
1255{
1256 // find the flip module -- the pointer stays valid until darktable shuts down
1257 static dt_iop_module_so_t *flip = NULL;
1258 if(IS_NULL_PTR(flip))
1259 {
1260 for(const GList *modules = dt_iop_get_modules_so(); modules; modules = g_list_next(modules))
1261 {
1262 dt_iop_module_so_t *module = (dt_iop_module_so_t *)(modules->data);
1263 if(!strcmp(module->op, "flip"))
1264 {
1265 flip = module;
1266 break;
1267 }
1268 }
1269 }
1270
1272
1273 // db lookup flip params
1274 if(flip && flip->have_introspection && flip->get_p)
1275 {
1276 void *params = NULL;
1277 int32_t params_len = 0;
1278 if(dt_history_repository_get_last_enabled_params(imgid, "flip", &params, &params_len))
1279 {
1280 // use introspection to get the orientation from the binary params blob
1281 orientation = *((dt_image_orientation_t *)flip->get_p(params, "orientation"));
1282 dt_free(params);
1283 }
1284 }
1285
1286 return orientation;
1287}
1288
1290{
1291 if(IS_NULL_PTR(img)) return ORIENTATION_NONE;
1292
1294 return (orientation != ORIENTATION_NULL) ? orientation : dt_image_orientation(img);
1295}
1296
1298{
1300
1301 if(orientation == ORIENTATION_NULL)
1302 {
1303 const dt_image_t *img = dt_image_cache_get(imgid, 'r');
1304 if(IS_NULL_PTR(img)) return ORIENTATION_NONE;
1305 orientation = dt_image_orientation(img);
1307 }
1308
1309 return orientation;
1310}
1311
1321{
1322 if(orientation & ORIENTATION_SWAP_XY)
1323 return orientation ^ ORIENTATION_FLIP_Y;
1324 else
1325 return orientation ^ ORIENTATION_FLIP_X;
1326}
1327
1330{
1331 if(orientation & ORIENTATION_SWAP_XY)
1332 return orientation ^ ORIENTATION_FLIP_X;
1333 else
1334 return orientation ^ ORIENTATION_FLIP_Y;
1335}
1336
1337void dt_image_flip(const int32_t imgid, const dt_image_transform_t transform)
1338{
1339 // this is light table only:
1341 if(dt_dev_get_global()->image_storage.id == imgid && cv->view((dt_view_t *)cv) == DT_VIEW_DARKROOM) return;
1342
1344 hist->imgid = imgid;
1346
1348
1349 switch(transform)
1350 {
1352 // mirror horizontally, then transpose
1353 orientation = _orientation_mirror_horizontally(orientation) ^ ORIENTATION_SWAP_XY;
1354 break;
1355
1357 // mirror vertically, then transpose
1358 orientation = _orientation_mirror_vertically(orientation) ^ ORIENTATION_SWAP_XY;
1359 break;
1360
1362 orientation = _orientation_mirror_horizontally(orientation);
1363 break;
1364
1366 orientation = _orientation_mirror_vertically(orientation);
1367 break;
1368
1370 // NULL, not NONE: hand the image back to whatever its EXIF says, rather than declaring it
1371 // upright. The previous code computed a quarter turn first and then threw it away.
1372 orientation = ORIENTATION_NULL;
1373 break;
1374 }
1375
1376 // dt_image_set_flip() writes the new orientation history entry and notifies the caches/GUI
1377 // (mipmap invalidation + thumbnail refresh) via dt_image_history_changed().
1378 dt_image_set_flip(imgid, orientation);
1379
1383}
1384
1385int32_t dt_image_duplicate(const int32_t imgid)
1386{
1387 return dt_image_duplicate_with_version(imgid, -1);
1388}
1389
1390
1391
1392static int32_t _image_duplicate_with_version(const int32_t imgid, const int32_t newversion, const gboolean undo,
1393 const gboolean reload)
1394{
1395 const int32_t newid = dt_image_repository_duplicate(imgid, newversion);
1396
1397 if(newid != -1)
1398 {
1399 if(undo)
1400 {
1401 dt_undo_duplicate_t *dupundo = (dt_undo_duplicate_t *)malloc(sizeof(dt_undo_duplicate_t));
1402 dupundo->orig_imgid = imgid;
1403 dupundo->version = newversion;
1404 dupundo->new_imgid = newid;
1406 }
1407
1408 // make sure that the duplicate doesn't have some magic darktable| tags
1409 if(dt_tag_detach_by_string("darktable|changed", newid, FALSE, FALSE)
1410 || dt_tag_detach_by_string("darktable|exported", newid, FALSE, FALSE))
1412
1413 const dt_image_t *img = dt_image_cache_get(imgid, 'r');
1414 const int grpid = img->group_id;
1416 dt_grouping_add_to_group(grpid, newid);
1417
1418 // Reloading here exposes newid to the lighttable grid: it becomes eligible for thumbnail
1419 // generation immediately, against whatever history it has *right now*. Callers still about
1420 // to write history onto it (duplicate-with-history, style-on-duplicate) must skip this and
1421 // reload themselves once that is done -- see dt_image_duplicate_no_reload().
1422 if(reload)
1424 }
1425 return newid;
1426}
1427
1428int32_t dt_image_duplicate_with_version(const int32_t imgid, const int32_t newversion)
1429{
1430 return _image_duplicate_with_version(imgid, newversion, TRUE, TRUE);
1431}
1432
1433int32_t dt_image_duplicate_no_reload(const int32_t imgid)
1434{
1435 return _image_duplicate_with_version(imgid, -1, TRUE, FALSE);
1436}
1437
1438static void _remove_undo_data_free(gpointer data)
1439{
1440 dt_undo_remove_t *undo = (dt_undo_remove_t *)data;
1441
1442 /* The record is gone, so the removal it could have undone is now permanent. Nothing to
1443 * drop once the connection is closed, though: the staging tables are `memory.` ones and
1444 * went with it.
1445 *
1446 * No reachable path takes that branch today, and the check stays anyway. dt_undo_cleanup()
1447 * does run after dt_database_close() at shutdown, but it finds an empty list: the GUI's
1448 * teardown calls dt_ctl_switch_mode_to(""), and switching to no view begins by clearing
1449 * DT_UNDO_ALL -- while the database is still open. Without a GUI the order reverses, but
1450 * then nothing can have recorded a removal either, both callers of dt_control_remove_images()
1451 * being GUI ones. So this guards an ordering that nothing enforces, for the cost of one
1452 * call: dropping it would cost an abort on quit in a debug build (the assert inside
1453 * DT_DEBUG_SQLITE3_PREPARE_V2) the day either half of that changes. */
1456
1457 dt_free(undo);
1458}
1459
1460static void _image_remove(const int32_t imgid, const gboolean undo)
1461{
1462 // if a local copy exists, remove it
1463
1464 if(dt_image_local_copy_reset(imgid)) return;
1465
1466 /* Snapshot before anything below runs, dt_grouping_remove_from_group() included: that call
1467 * rewrites the group_id of the images STAYING behind, and putting that back is part of
1468 * undoing this removal. A snapshot that cannot be taken is not recorded -- removing the
1469 * image anyway is better than an undo entry that restores nothing. */
1470 if(undo)
1471 {
1472 dt_undo_remove_t *rm = (dt_undo_remove_t *)g_malloc0(sizeof(dt_undo_remove_t));
1473 rm->imgid = imgid;
1475
1476 if(rm->snap_id >= 0)
1478 else
1479 {
1480 fprintf(stderr, "[dt_image_remove] fails to snapshot image %" PRId32 ", removing it without undo\n", imgid);
1481 dt_free(rm);
1482 }
1483 }
1484
1485 const dt_image_t *img = dt_image_cache_get(imgid, 'r');
1487
1488 // make sure we remove from the cache first, or else the cache will look for imgid in sql
1489 dt_image_cache_remove(imgid);
1490
1493
1494 /* Every buffer, not just the thumbnails: the decoded raw input survives dt_mipmap_cache_remove()
1495 * by design, and an image that has left the library has no business keeping one. It is what the
1496 * pipeline slices through basebuffer when the image comes back on Ctrl+Z, and a stale entry
1497 * there renders as a husk that no later pass replaces. */
1499}
1500
1501void dt_image_remove(const int32_t imgid)
1502{
1503 _image_remove(imgid, FALSE);
1504}
1505
1506void dt_image_remove_undoable(const int32_t imgid)
1507{
1508 _image_remove(imgid, TRUE);
1509}
1510
1511uint32_t dt_image_altered(const int32_t imgid)
1512{
1514}
1515
1516#ifndef _WIN32
1517static int _valid_glob_match(const char *const name, size_t offset)
1518{
1519 // verify that the name matched by glob() is a valid sidecar name by checking whether we have an underscore
1520 // followed by a sequence of digits followed by a period at the given offset in the name
1521 if(strlen(name) < offset || name[offset] != '_')
1522 return FALSE;
1523 size_t i;
1524 for(i = offset+1; name[i] && name[i] != '.'; i++)
1525 {
1526 if(!isdigit(name[i]))
1527 return FALSE;
1528 }
1529 return name[i] == '.';
1530}
1531#endif /* !_WIN32 */
1532
1533GList* dt_image_find_xmps(const char* filename)
1534{
1535 // find all duplicates of an image by looking for all possible sidecars for the file: file.ext.xmp, file_NN.ext.xmp,
1536 // file_NNN.ext.xmp, and file_NNNN.ext.xmp
1537 // because a glob() needs to scan the entire directory, we minimize work for large directories by doing a single
1538 // glob which might generate false matches (if the image name contains an underscore followed by a digit) and
1539 // filter out the false positives afterward
1540#ifndef _WIN32
1541 // start by locating the extension, which we'll be referencing multiple times
1542 const size_t fn_len = strlen(filename);
1543 const char* ext = strrchr(filename,'.'); // find last dot
1544 if(IS_NULL_PTR(ext)) ext = filename;
1545 const size_t ext_offset = ext - filename;
1546
1547 gchar pattern[DT_PATH_MAX] = { 0 };
1548 GList* files = NULL;
1549
1550 // check for file.ext.xmp
1551 static const char xmp[] = ".xmp";
1552 const size_t xmp_len = strlen(xmp);
1553 // concatenate filename and sidecar extension
1554 g_strlcpy(pattern, filename, sizeof(pattern));
1555 g_strlcpy(pattern + fn_len, xmp, sizeof(pattern) - fn_len);
1556 if(dt_util_test_image_file(pattern))
1557 {
1558 // the default sidecar exists, is readable and is a regular file with lenght > 0, so add it to the list
1559 files = g_list_prepend(files, g_strdup(pattern));
1560 }
1561
1562 // now collect all file_N*N.ext.xmp matches
1563 static const char glob_pattern[] = "_[0-9]*[0-9]";
1564 const size_t gp_len = strlen(glob_pattern);
1565 if(fn_len + gp_len + xmp_len < sizeof(pattern)) // enough space to build pattern?
1566 {
1567 // add GLOB.ext.xmp to the root of the basename
1568 g_strlcpy(pattern + ext_offset, glob_pattern, sizeof(pattern) - fn_len);
1569 g_strlcpy(pattern + ext_offset + gp_len, ext, sizeof(pattern) - ext_offset - gp_len);
1570 g_strlcpy(pattern + fn_len + gp_len, xmp, sizeof(pattern) - fn_len - gp_len);
1571 glob_t globbuf;
1572 if(!glob(pattern, 0, NULL, &globbuf))
1573 {
1574 // for each match of the pattern
1575 for(size_t i = 0; i < globbuf.gl_pathc; i++)
1576 {
1577 if(_valid_glob_match(globbuf.gl_pathv[i], ext_offset))
1578 {
1579 // it's not a false positive, so add it to the list of sidecars
1580 files = g_list_prepend(files, g_strdup(globbuf.gl_pathv[i]));
1581 }
1582 }
1583 globfree(&globbuf);
1584 }
1585 }
1586 // we built the list in reverse order for speed, so un-reverse it
1587 return g_list_reverse(files);
1588
1589#else
1590 return win_image_find_duplicates(filename);
1591#endif
1592}
1593
1594// Search for duplicate's sidecar files and import them if found and not in DB yet
1595int dt_image_read_duplicates(const uint32_t id, const char *filename, const gboolean clear_selection)
1596{
1597 int count_xmps_processed = 0;
1598 gchar pattern[DT_PATH_MAX] = { 0 };
1599
1600 GList *files = dt_image_find_xmps(filename);
1601
1602 // we store the xmp filename without version part in pattern to speed up string comparison later
1603 g_snprintf(pattern, sizeof(pattern), "%s.xmp", filename);
1604
1605 for(GList *file_iter = files; file_iter; file_iter = g_list_next(file_iter))
1606 {
1607 gchar *xmpfilename = file_iter->data;
1608 int version = -1;
1609
1610 // we need to get the version number of the sidecar filename
1611 if(!strncmp(xmpfilename, pattern, sizeof(pattern)))
1612 {
1613 // this is an xmp file without version number which corresponds to version 0
1614 version = 0;
1615 }
1616 else
1617 {
1618 // we need to derive the version number from the filename
1619
1620 gchar *c3 = xmpfilename + strlen(xmpfilename)
1621 - 5; // skip over .xmp extension; position c3 at character before the '.'
1622 while(*c3 != '.' && c3 > xmpfilename)
1623 c3--; // skip over filename extension; position c3 is at character '.'
1624 gchar *c4 = c3;
1625 while(*c4 != '_' && c4 > xmpfilename) c4--; // move to beginning of version number
1626 c4++;
1627
1628 gchar *idfield = g_strndup(c4, c3 - c4);
1629
1630 version = atoi(idfield);
1631 dt_free(idfield);
1632 }
1633
1634 int newid = id;
1635 int grpid = -1;
1636
1637 if(count_xmps_processed == 0)
1638 {
1639 // this is the first xmp processed, just update the passed-in id
1641 }
1642 else
1643 {
1644 // create a new duplicate based on the passed-in id. Note that we do not call
1645 // dt_image_duplicate_with_version() as this version also set the group which
1646 // is using DT_IMAGE_CACHE_SAFE and so will write the .XMP. But we must avoid
1647 // this has the xmp for the duplicate is read just below.
1648 newid = dt_image_repository_duplicate(id, version);
1649 const dt_image_t *img = dt_image_cache_get(id, 'r');
1650 if(!IS_NULL_PTR(img))
1651 {
1652 grpid = img->group_id;
1654 }
1655 }
1656 // make sure newid is not selected
1657 if(clear_selection) dt_selection_clear(dt_selection_get_global());
1658
1659 // dt_image_repository_duplicate() above returns -1 when it cannot create the duplicate,
1660 // and dt_image_cache_get() returns NULL for an id it cannot load. Both were dereferenced
1661 // unchecked, which is Sentry 140350855: SIGSEGV on `img->version = version` while
1662 // importing a film roll whose sidecars name versions the duplicate could not be created
1663 // for. Skipping this sidecar leaves the rest of the import to proceed.
1664 dt_image_t *img = (newid > 0) ? dt_image_cache_get(newid, 'w') : NULL;
1665 if(IS_NULL_PTR(img))
1666 {
1667 fprintf(stderr, "[dt_image_read_duplicates] could not load image %i for sidecar '%s', skipping it\n",
1668 newid, xmpfilename);
1669 continue;
1670 }
1671 (void)dt_exif_xmp_read(img, xmpfilename, 0);
1672 img->version = version;
1674
1675 if(grpid != -1)
1676 {
1677 // now it is safe to set the duplicate group-id
1678 dt_grouping_add_to_group(grpid, newid);
1680 }
1681
1682 count_xmps_processed++;
1683 }
1684
1685 g_list_free_full(files, dt_free_gpointer);
1686 files = NULL;
1687 return count_xmps_processed;
1688}
1689
1690static int32_t _image_import_internal(const int32_t film_id, const char *filename,
1691 gboolean lua_locking, gboolean raise_signals)
1692{
1693 char *normalized_filename = dt_util_normalize_path(filename);
1694 if(!normalized_filename || !dt_util_test_image_file(normalized_filename))
1695 {
1696 dt_free(normalized_filename);
1697 return 0;
1698 }
1699 const char *cc = normalized_filename + strlen(normalized_filename);
1700 for(; *cc != '.' && cc > normalized_filename; cc--)
1701 ;
1702 if(!strcasecmp(cc, ".dt") || !strcasecmp(cc, ".dttags") || !strcasecmp(cc, ".xmp"))
1703 {
1704 dt_free(normalized_filename);
1705 return 0;
1706 }
1707 char *ext = g_ascii_strdown(cc + 1, -1);
1709 {
1710 dt_free(normalized_filename);
1711 dt_free(ext);
1712 return 0;
1713 }
1714 // select from images; if found => return
1715 gchar *imgfname = g_path_get_basename(normalized_filename);
1716 int32_t id = dt_image_get_id(film_id, imgfname);
1717 if(id > UNKNOWN_IMAGE)
1718 {
1719 dt_control_log(_("Image %s is already in library and will not be re-imported.\n"), imgfname);
1720 dt_free(imgfname);
1721 dt_free(ext);
1722 dt_free(normalized_filename);
1723 return id;
1724 }
1725
1726 // also need to set the no-legacy bit, to make sure we get the right presets (new ones)
1727 uint32_t flags = 0;
1729 // and we set the type of image flag (from extension for now)
1730 gchar *extension = g_strrstr(imgfname, ".");
1732 // set the bits in flags that indicate if any of the extra files (.txt, .wav) are present
1733 char *extra_file = dt_image_get_audio_path_from_path(normalized_filename);
1734 if(extra_file)
1735 {
1737 dt_free(extra_file);
1738 }
1739 extra_file = dt_image_get_text_path_from_path(normalized_filename);
1740 if(extra_file)
1741 {
1743 dt_free(extra_file);
1744 }
1745
1747 return 0;
1748
1749 id = dt_image_get_id(film_id, imgfname);
1750
1751 // Try to find out if this should be grouped already.
1752 gchar *basename = g_strdup(imgfname);
1753 gchar *cc2 = basename + strlen(basename);
1754 for(; *cc2 != '.' && cc2 > basename; cc2--)
1755 ;
1756 *cc2 = '\0';
1757 gchar *sql_pattern = g_strconcat(basename, ".%", NULL);
1758 int group_id;
1759 // in case we are not a jpg check if we need to change group representative
1760 if(strcmp(ext, "jpg") != 0 && strcmp(ext, "jpeg") != 0)
1761 {
1762 const int32_t other_group = dt_image_repository_find_group_for_pattern(film_id, sql_pattern, TRUE, id);
1763 // if we have a group already
1764 if(other_group != -1)
1765 {
1766 int other_id = other_group;
1767 dt_image_t *other_img = dt_image_cache_get(other_id, 'w');
1768 gchar *other_basename = g_strdup(other_img->filename);
1769 gchar *cc3 = other_basename + strlen(other_img->filename);
1770 for(; *cc3 != '.' && cc3 > other_basename; cc3--)
1771 ;
1772 ++cc3;
1773 gchar *ext_lowercase = g_ascii_strdown(cc3, -1);
1774 // if the group representative is a jpg, change group representative to this new imported image
1775 if(!strcmp(ext_lowercase, "jpg") || !strcmp(ext_lowercase, "jpeg"))
1776 {
1777 other_img->group_id = id;
1779 GList *members = dt_image_repository_get_group_members(other_id, other_id);
1780 for(GList *l = members; l; l = g_list_next(l))
1781 {
1782 other_id = GPOINTER_TO_INT(l->data);
1783 dt_image_t *group_img = dt_image_cache_get(other_id, 'w');
1784 group_img->group_id = id;
1786 }
1787 g_list_free(members);
1788 group_id = id;
1789 }
1790 else
1791 {
1793 group_id = other_id;
1794 }
1795 dt_free(ext_lowercase);
1796 dt_free(other_basename);
1797 }
1798 else
1799 {
1800 group_id = id;
1801 }
1802 }
1803 else
1804 {
1805 const int32_t other_group = dt_image_repository_find_group_for_pattern(film_id, sql_pattern, FALSE, id);
1806 group_id = (other_group != -1) ? other_group : id;
1807 }
1808 dt_image_repository_set_group(id, group_id);
1809
1810 // printf("[image_import] importing `%s' to img id %d\n", imgfname, id);
1811
1812 // lock as shortly as possible:
1813 dt_image_t *img = dt_image_cache_get(id, 'w');
1814 img->group_id = group_id;
1815
1816 // read dttags and exif for database queries!
1817 (void)dt_exif_read(img, normalized_filename);
1818 char dtfilename[DT_PATH_MAX] = { 0 };
1819 g_strlcpy(dtfilename, normalized_filename, sizeof(dtfilename));
1820 // dt_image_path_append_version(id, dtfilename, sizeof(dtfilename));
1821 g_strlcat(dtfilename, ".xmp", sizeof(dtfilename));
1822
1823 const int res = dt_exif_xmp_read(img, dtfilename, 0);
1824
1825 // write through to db, but not to xmp.
1827
1828 // read all sidecar files
1829 const int nb_xmp = dt_image_read_duplicates(id, normalized_filename, raise_signals);
1830
1831 if((res != 0) && (nb_xmp == 0))
1832 {
1833 const gboolean lr_xmp = dt_lightroom_import(id, NULL, TRUE);
1834 if(lr_xmp) dt_control_save_xmp(id);
1835 }
1836
1837 // add a tag with the file extension
1838 guint tagid = 0;
1839 char tagname[512];
1840 snprintf(tagname, sizeof(tagname), "darktable|format|%s", ext);
1841 dt_free(ext);
1842 dt_tag_new(tagname, &tagid);
1843 dt_tag_attach(tagid, id, FALSE, FALSE);
1844
1845 // make sure that there are no stale thumbnails left
1847
1848 //synch database entries to xmp
1849 if(dt_image_get_xmp_mode()) dt_image_synch_all_xmp(normalized_filename);
1850
1851 dt_free(imgfname);
1852 dt_free(basename);
1853 dt_free(sql_pattern);
1854 dt_free(normalized_filename);
1855
1856 if(raise_signals)
1857 {
1859 // the old raise handed the freshly built list to the signal; the handler copies now,
1860 // so this site frees its own
1861 GList *imgs = g_list_prepend(NULL, GINT_TO_POINTER(id));
1863 g_list_free(imgs);
1864 }
1865
1866 // the following line would look logical with new_tags_set being the return value
1867 // from dt_tag_new above, but this could lead to too rapid signals, being able to lock up the
1868 // keywords side pane when trying to use it, which can lock up the whole dt GUI ..
1869 // if(new_tags_set) DT_DEBUG_CONTROL_SIGNAL_RAISE(dt_control_signal_get_global(),DT_SIGNAL_TAG_CHANGED);
1870 return id;
1871}
1872
1873int32_t dt_image_get_id_full_path(const gchar *filename)
1874{
1875 gchar *dir = g_path_get_dirname(filename);
1876 gchar *file = g_path_get_basename(filename);
1877 const int32_t id = dt_image_repository_find_by_folder_and_filename(dir, file);
1878 dt_free(dir);
1879 dt_free(file);
1880
1881 return id;
1882}
1883
1884int32_t dt_image_get_id(int32_t film_id, const gchar *filename)
1885{
1886 return dt_image_repository_find_by_film_and_filename(film_id, filename);
1887}
1888
1889/* Guess what kind of image a file holds from its extension alone.
1890 *
1891 * Lived in imageio/imageio_core.c, which put a six-line function over three predicates
1892 * from common/image_extensions.h and one enum from this file at layer 6 -- above every
1893 * one of its callers (common/image.c, common/exif.cc, caches/image_cache.c,
1894 * database/database.c, gui/import.c). Nothing in imageio/ or above ever called it. */
1896{
1897 const char *ext = g_str_has_prefix(extension, ".") ? extension + 1 : extension;
1898 if(dt_image_ext_is_ambiguous(ext)) return 0;
1899 if(dt_image_ext_is_raw(ext)) return DT_IMAGE_RAW;
1900 if(dt_image_ext_is_hdr(ext)) return DT_IMAGE_HDR;
1901 if(dt_image_ext_is_ldr(ext)) return DT_IMAGE_LDR;
1902 return 0;
1903}
1904
1905int32_t dt_image_import(const int32_t film_id, const char *filename, gboolean raise_signals)
1906{
1907 return _image_import_internal(film_id, filename, TRUE, raise_signals);
1908}
1909
1910int32_t dt_image_import_lua(const int32_t film_id, const char *filename)
1911{
1912 return _image_import_internal(film_id, filename, FALSE, TRUE);
1913}
1914
1916{
1917 memset(img, 0, sizeof(*img));
1918 img->width = img->height = 0;
1919 img->p_width = img->p_height = 0;
1920 img->crop_x = img->crop_y = img->crop_width = img->crop_height = 0;
1922
1924
1925 img->legacy_flip.legacy = 0;
1926 img->legacy_flip.user_flip = 0;
1927
1928 img->dsc = (dt_iop_buffer_dsc_t){ .channels = 0, .datatype = TYPE_UNKNOWN, .bpp = 0, .filters = 0u };
1929 img->film_id = UNKNOWN_IMAGE;
1930 img->group_id = UNKNOWN_IMAGE;
1931 img->group_members = 0;
1932 img->history_items = 0;
1933 img->history_hash = UINT64_MAX;
1934 img->mipmap_hash = 0; // don't default it to img->history_hash to not make it look like they are in sync
1935 img->self_hash = 0;
1936 img->flags = 0;
1937 img->id = UNKNOWN_IMAGE;
1938 img->version = -1;
1939 img->loader = LOADER_UNKNOWN;
1940 img->exif_inited = 0;
1942 dt_datetime_exif_to_img(img, "");
1943 memset(img->exif_maker, 0, sizeof(img->exif_maker));
1944 memset(img->exif_model, 0, sizeof(img->exif_model));
1945 memset(img->exif_lens, 0, sizeof(img->exif_lens));
1946 memset(img->camera_maker, 0, sizeof(img->camera_maker));
1947 memset(img->camera_model, 0, sizeof(img->camera_model));
1948 memset(img->camera_alias, 0, sizeof(img->camera_alias));
1949 memset(img->camera_makermodel, 0, sizeof(img->camera_makermodel));
1950 memset(img->camera_legacy_makermodel, 0, sizeof(img->camera_legacy_makermodel));
1951 memset(img->filename, 0, sizeof(img->filename));
1952 memset(img->fullpath, 0, sizeof(img->fullpath));
1953 memset(img->local_copy_path, 0, sizeof(img->local_copy_path));
1954 memset(img->local_copy_legacy_path, 0, sizeof(img->local_copy_legacy_path));
1955 memset(img->folder, 0, sizeof(img->folder));
1956 memset(img->filmroll, 0, sizeof(img->filmroll));
1957 memset(img->datetime, 0, sizeof(img->datetime));
1958 g_strlcpy(img->filename, "(unknown)", sizeof(img->filename));
1959 img->exif_crop = 1.0;
1960 img->exif_exposure = 0;
1961 img->exif_exposure_bias = NAN;
1962 img->exif_aperture = 0;
1963 img->exif_iso = 0;
1964 img->exif_focal_length = 0;
1965 img->exif_focus_distance = 0;
1966 img->geoloc.latitude = NAN;
1967 img->geoloc.longitude = NAN;
1968 img->geoloc.elevation = NAN;
1969 img->raw_black_level = 0;
1970 for(uint8_t i = 0; i < 4; i++) img->raw_black_level_separate[i] = 0;
1971 img->raw_white_point = 16384; // 2^14
1972 img->d65_color_matrix[0] = NAN;
1973 img->profile = NULL;
1974 img->profile_size = 0;
1975 img->embedded_profile = NULL;
1977 img->fuji_rotation_pos = 0;
1978 img->pixel_aspect_ratio = 1.0f;
1979 img->wb_coeffs[0] = NAN;
1980 img->wb_coeffs[1] = NAN;
1981 img->wb_coeffs[2] = NAN;
1982 img->wb_coeffs[3] = NAN;
1983 img->usercrop[0] = img->usercrop[1] = 0;
1984 img->usercrop[2] = img->usercrop[3] = 1;
1986 img->dng_gain_maps = NULL;
1987 img->cache_entry = 0;
1988 img->color_labels = 0;
1989 img->rating = 0;
1990 img->has_localcopy = FALSE;
1991 img->has_audio = FALSE;
1992 img->is_bw = FALSE;
1993 img->is_bw_flow = FALSE;
1994 img->is_hdr = FALSE;
1995
1996 for(int k=0; k<4; k++)
1997 for(int i=0; i<3; i++)
1998 img->adobe_XYZ_to_CAM[k][i] = NAN;
1999}
2000
2002{
2003 if(!img->camera_maker[0] || !img->camera_model[0] || !img->camera_alias[0])
2004 {
2005 // We need to use the exif values, so let's get rawspeed to munge them
2007 img->camera_maker, sizeof(img->camera_maker),
2008 img->camera_model, sizeof(img->camera_model),
2009 img->camera_alias, sizeof(img->camera_alias));
2010 }
2011
2012 // Now we just create a makermodel by concatenation
2013 g_strlcpy(img->camera_makermodel, img->camera_maker, sizeof(img->camera_makermodel));
2014 const int len = strlen(img->camera_maker);
2015 img->camera_makermodel[len] = ' ';
2016 g_strlcpy(img->camera_makermodel+len+1, img->camera_model, sizeof(img->camera_makermodel)-len-1);
2017}
2018
2019int32_t dt_image_rename(const int32_t imgid, const int32_t filmid, const gchar *newname)
2020{
2021 // TODO: several places where string truncation could occur unnoticed
2022 int32_t result = -1;
2023 gchar oldimg[DT_PATH_MAX] = { 0 };
2024 gchar newimg[DT_PATH_MAX] = { 0 };
2025 gboolean from_cache = FALSE;
2026 dt_image_full_path(imgid, oldimg, sizeof(oldimg), &from_cache, __FUNCTION__);
2027 gchar *newdir = NULL;
2028
2029 newdir = dt_film_repository_get_folder(filmid);
2030
2031 gchar copysrcpath[DT_PATH_MAX] = { 0 };
2032 gchar copydestpath[DT_PATH_MAX] = { 0 };
2033 GFile *old = NULL, *new = NULL;
2034 if(newdir)
2035 {
2036 old = g_file_new_for_path(oldimg);
2037
2038 if(newname)
2039 {
2040 g_snprintf(newimg, sizeof(newimg), "%s%c%s", newdir, G_DIR_SEPARATOR, newname);
2041 new = g_file_new_for_path(newimg);
2042 // 'newname' represents the file's new *basename* -- it must not
2043 // refer to a file outside of 'newdir'.
2044 gchar *newBasename = g_file_get_basename(new);
2045 if(g_strcmp0(newname, newBasename) != 0)
2046 {
2047 g_object_unref(old);
2048 old = NULL;
2049 g_object_unref(new);
2050 new = NULL;
2051 }
2052 dt_free(newBasename);
2053 }
2054 else
2055 {
2056 gchar *imgbname = g_path_get_basename(oldimg);
2057 g_snprintf(newimg, sizeof(newimg), "%s%c%s", newdir, G_DIR_SEPARATOR, imgbname);
2058 new = g_file_new_for_path(newimg);
2059 dt_free(imgbname);
2060 }
2061 dt_free(newdir);
2062 }
2063
2064 if(new)
2065 {
2066 // get current local copy if any
2067 dt_image_t *img = dt_image_cache_get(imgid, 'r');
2068 if(img)
2069 {
2070 dt_image_choose_input_path(img, copysrcpath, sizeof(copysrcpath), TRUE);
2072 }
2073
2074 // move image
2075 GError *moveError = NULL;
2076 gboolean moveStatus = g_file_move(old, new, 0, NULL, NULL, NULL, &moveError);
2077
2078 if(moveStatus)
2079 {
2080 _move_text_sidecar_if_present(oldimg, newimg, FALSE);
2081
2082 // first move xmp files of image and duplicates
2083 GList *dup_list = dt_image_repository_get_duplicate_ids(imgid);
2084 for(GList *l = dup_list; l; l = g_list_next(l))
2085 {
2086 const int32_t id = GPOINTER_TO_INT(l->data);
2087 gchar oldxmp[DT_PATH_MAX] = { 0 }, newxmp[DT_PATH_MAX] = { 0 };
2088 g_strlcpy(oldxmp, oldimg, sizeof(oldxmp));
2089 g_strlcpy(newxmp, newimg, sizeof(newxmp));
2090 dt_image_path_append_version(id, oldxmp, sizeof(oldxmp));
2091 dt_image_path_append_version(id, newxmp, sizeof(newxmp));
2092 g_strlcat(oldxmp, ".xmp", sizeof(oldxmp));
2093 g_strlcat(newxmp, ".xmp", sizeof(newxmp));
2094
2095 GFile *goldxmp = g_file_new_for_path(oldxmp);
2096 GFile *gnewxmp = g_file_new_for_path(newxmp);
2097
2098 g_file_move(goldxmp, gnewxmp, 0, NULL, NULL, NULL, NULL);
2099
2100 g_object_unref(goldxmp);
2101 g_object_unref(gnewxmp);
2102 }
2103
2104 // then update database and cache
2105 // if update was performed in above loop, dt_image_path_append_version()
2106 // would return wrong version!
2107 while(dup_list)
2108 {
2109 const int id = GPOINTER_TO_INT(dup_list->data);
2110 img = dt_image_cache_get(id, 'w');
2111 img->film_id = filmid;
2112 if(newname) g_strlcpy(img->filename, newname, DT_MAX_FILENAME_LEN);
2113 // write through to db and queue xmp write
2115 dup_list = g_list_delete_link(dup_list, dup_list);
2116 }
2117 g_list_free(dup_list);
2118 dup_list = NULL;
2119
2120 // finally, rename local copy if any
2121 if(g_file_test(copysrcpath, G_FILE_TEST_EXISTS))
2122 {
2123 // get new name
2124 img = dt_image_cache_get(imgid, 'r');
2125 if(img)
2126 {
2127 dt_image_choose_input_path(img, copydestpath, sizeof(copydestpath), TRUE);
2129 }
2130
2131 GFile *cold = g_file_new_for_path(copysrcpath);
2132 GFile *cnew = g_file_new_for_path(copydestpath);
2133
2134 g_clear_error(&moveError);
2135 moveStatus = g_file_move(cold, cnew, 0, NULL, NULL, NULL, &moveError);
2136 if(!moveStatus)
2137 {
2138 fprintf(stderr, "[dt_image_rename] error moving local copy `%s' -> `%s'\n", copysrcpath, copydestpath);
2139
2140 if(g_error_matches(moveError, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
2141 {
2142 gchar *oldBasename = g_path_get_basename(copysrcpath);
2143 dt_control_log(_("cannot access local copy `%s'"), oldBasename);
2144 dt_free(oldBasename);
2145 }
2146 else if(g_error_matches(moveError, G_IO_ERROR, G_IO_ERROR_EXISTS)
2147 || g_error_matches(moveError, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY))
2148 {
2149 gchar *newBasename = g_path_get_basename(copydestpath);
2150 dt_control_log(_("cannot write local copy `%s'"), newBasename);
2151 dt_free(newBasename);
2152 }
2153 else
2154 {
2155 gchar *oldBasename = g_path_get_basename(copysrcpath);
2156 gchar *newBasename = g_path_get_basename(copydestpath);
2157 dt_control_log(_("error moving local copy `%s' -> `%s'"), oldBasename, newBasename);
2158 dt_free(oldBasename);
2159 dt_free(newBasename);
2160 }
2161 }
2162
2163 g_object_unref(cold);
2164 g_object_unref(cnew);
2165 }
2166
2167 result = 0;
2168 }
2169 else
2170 {
2171 if(g_error_matches(moveError, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
2172 {
2173 dt_control_log(_("error moving `%s': file not found"), oldimg);
2174 }
2175 // only display error message if newname is set (renaming and
2176 // not moving) as when moving it can be the case where a
2177 // duplicate is being moved, so only the .xmp are present but
2178 // the original file may already have been moved.
2179 else if(newname
2180 && (g_error_matches(moveError, G_IO_ERROR, G_IO_ERROR_EXISTS)
2181 || g_error_matches(moveError, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY)))
2182 {
2183 dt_control_log(_("error moving `%s' -> `%s': file exists"), oldimg, newimg);
2184 }
2185 else if(newname)
2186 {
2187 dt_control_log(_("error moving `%s' -> `%s'"), oldimg, newimg);
2188 }
2189 }
2190
2191 g_clear_error(&moveError);
2192 g_object_unref(old);
2193 g_object_unref(new);
2194 }
2195
2196 return result;
2197}
2198
2199int32_t dt_image_move(const int32_t imgid, const int32_t filmid)
2200{
2201 return dt_image_rename(imgid, filmid, NULL);
2202}
2203
2204int32_t dt_image_copy_rename(const int32_t imgid, const int32_t filmid, const gchar *newname)
2205{
2206 int32_t newid = -1;
2207 gchar srcpath[DT_PATH_MAX] = { 0 };
2208 gchar *newdir = NULL;
2209 gboolean from_cache = FALSE;
2210 gchar *oldFilename = NULL;
2211 gchar *newFilename = NULL;
2212
2213 newdir = dt_film_repository_get_folder(filmid);
2214
2215 GFile *src = NULL, *dest = NULL;
2216 if(newdir)
2217 {
2218 dt_image_full_path(imgid, srcpath, sizeof(srcpath), &from_cache, __FUNCTION__);
2219 oldFilename = g_path_get_basename(srcpath);
2220 gchar *destpath;
2221 if(newname)
2222 {
2223 newFilename = g_strdup(newname);
2224 destpath = g_build_filename(newdir, newname, NULL);
2225 dest = g_file_new_for_path(destpath);
2226 // 'newname' represents the file's new *basename* -- it must not
2227 // refer to a file outside of 'newdir'.
2228 gchar *destBasename = g_file_get_basename(dest);
2229 if(g_strcmp0(newname, destBasename) != 0)
2230 {
2231 g_object_unref(dest);
2232 dest = NULL;
2233 }
2234 dt_free(destBasename);
2235 }
2236 else
2237 {
2238 newFilename = g_path_get_basename(srcpath);
2239 destpath = g_build_filename(newdir, newFilename, NULL);
2240 dest = g_file_new_for_path(destpath);
2241 }
2242 if(dest)
2243 {
2244 src = g_file_new_for_path(srcpath);
2245 }
2246 dt_free(newdir);
2247 dt_free(destpath);
2248 }
2249
2250 if(dest)
2251 {
2252 // copy image to new folder
2253 // if image file already exists, continue
2254 GError *gerror = NULL;
2255 gboolean copyStatus = g_file_copy(src, dest, G_FILE_COPY_NONE, NULL, NULL, NULL, &gerror);
2256
2257 if(copyStatus || g_error_matches(gerror, G_IO_ERROR, G_IO_ERROR_EXISTS))
2258 {
2259 gchar *dest_image_path = g_file_get_path(dest);
2260 if(dest_image_path)
2261 {
2262 _copy_text_sidecar_if_present(srcpath, dest_image_path);
2263 dt_free(dest_image_path);
2264 }
2265
2266 newid = dt_image_repository_copy_to_film(imgid, filmid, newFilename, oldFilename);
2267
2268 if(newid != -1)
2269 {
2270 // also copy over on-disk thumbnails, if any
2271 dt_mipmap_cache_copy_thumbnails(newid, imgid);
2272
2274
2276 NULL);
2277 }
2278 }
2279 else
2280 {
2281 fprintf(stderr, "Failed to copy image %s: %s\n", srcpath, gerror->message);
2282 }
2283 g_object_unref(dest);
2284 g_object_unref(src);
2285 g_clear_error(&gerror);
2286 }
2287 dt_free(oldFilename);
2288 dt_free(newFilename);
2289
2290 return newid;
2291}
2292
2293int32_t dt_image_copy(const int32_t imgid, const int32_t filmid)
2294{
2295 return dt_image_copy_rename(imgid, filmid, NULL);
2296}
2297
2298int dt_image_local_copy_set(const int32_t imgid)
2299{
2300 gchar srcpath[DT_PATH_MAX] = { 0 };
2301 gchar destpath[DT_PATH_MAX] = { 0 };
2303 char local_copy_path[DT_PATH_MAX] = { 0 };
2304 char local_copy_legacy_path[DT_PATH_MAX] = { 0 };
2305 dt_image_t *img = dt_image_cache_get(imgid, 'r');
2306 if(img)
2307 {
2308 g_strlcpy(srcpath, img->fullpath, DT_PATH_MAX);
2309 g_strlcpy(local_copy_path, img->local_copy_path, sizeof(local_copy_path));
2310 g_strlcpy(local_copy_legacy_path, img->local_copy_legacy_path, sizeof(local_copy_legacy_path));
2311 char existing[DT_PATH_MAX] = { 0 };
2312 source = dt_image_choose_input_path(img, existing, sizeof(existing), TRUE);
2314 g_strlcpy(destpath, existing, sizeof(destpath));
2315 else
2316 g_strlcpy(destpath, local_copy_path, sizeof(destpath));
2318 }
2319 else
2320 {
2321 return 1;
2322 }
2323
2324 // check that the src file is readable
2325 if(!g_file_test(srcpath, G_FILE_TEST_IS_REGULAR))
2326 {
2327 dt_control_log(_("cannot create local copy when the original file is not accessible."));
2328 return 1;
2329 }
2330
2332 {
2333 GFile *src = g_file_new_for_path(srcpath);
2334 GFile *dest = g_file_new_for_path(destpath);
2335
2336 // copy image to cache directory
2337 GError *gerror = NULL;
2338
2339 if(!g_file_copy(src, dest, G_FILE_COPY_NONE, NULL, NULL, NULL, &gerror))
2340 {
2341 dt_control_log(_("cannot create local copy."));
2342 g_object_unref(dest);
2343 g_object_unref(src);
2344 return 1;
2345 }
2346
2347 g_object_unref(dest);
2348 g_object_unref(src);
2349 }
2350
2351 _copy_text_sidecar_if_present(srcpath, destpath);
2352
2353 // update cache local copy flags, do this even if the local copy already exists as we need to set the flags
2354 // for duplicate
2355 img = dt_image_cache_get(imgid, 'w');
2356 img->flags |= DT_IMAGE_LOCAL_COPY;
2358
2360 return 0;
2361}
2362
2363static int _nb_other_local_copy_for(const int32_t imgid)
2364{
2366}
2367
2368int dt_image_local_copy_reset(const int32_t imgid)
2369{
2370 gchar destpath[DT_PATH_MAX] = { 0 };
2371 gchar locppath[DT_PATH_MAX] = { 0 };
2372 gchar cachedir[DT_PATH_MAX] = { 0 };
2373
2374 // check that a local copy exists, otherwise there is nothing to do
2375 dt_image_t *imgr = dt_image_cache_get(imgid, 'r');
2376 if(!imgr) return 0;
2377 const gboolean local_copy_exists = (imgr->flags & DT_IMAGE_LOCAL_COPY) == DT_IMAGE_LOCAL_COPY ? TRUE : FALSE;
2379
2380 if(!local_copy_exists)
2381 return 0;
2382
2383 // check that the original file is accessible
2384
2385 gboolean from_cache = FALSE;
2386 dt_image_full_path(imgid, destpath, sizeof(destpath), &from_cache, __FUNCTION__);
2387
2388 from_cache = TRUE;
2389 dt_image_full_path(imgid, locppath, sizeof(locppath), &from_cache, __FUNCTION__);
2390 dt_image_path_append_version(imgid, locppath, sizeof(locppath));
2391 g_strlcat(locppath, ".xmp", sizeof(locppath));
2392
2393 // a local copy exists, but the original is not accessible
2394
2395 if(g_file_test(locppath, G_FILE_TEST_EXISTS) && !g_file_test(destpath, G_FILE_TEST_EXISTS))
2396 {
2397 dt_control_log(_("cannot remove local copy when the original file is not accessible."));
2398 return 1;
2399 }
2400
2401 // get name of local copy
2402
2403 locppath[0] = '\0';
2404 dt_image_t *img = dt_image_cache_get(imgid, 'r');
2405 if(img)
2406 {
2407 dt_image_choose_input_path(img, locppath, sizeof(locppath), TRUE);
2409 }
2410
2411 // remove cached file, but double check that this is really into the cache. We really want to avoid deleting
2412 // a user's original file.
2413
2414 dt_loc_get_user_cache_dir(cachedir, sizeof(cachedir));
2415
2416 if(g_file_test(locppath, G_FILE_TEST_EXISTS) && strstr(locppath, cachedir))
2417 {
2418 GFile *dest = g_file_new_for_path(locppath);
2419
2420 // first sync the xmp with the original picture
2421
2422 dt_control_save_xmp(imgid);
2423
2424 // delete image from cache directory only if there is no other local cache image referencing it
2425 // for example duplicates are all referencing the same base picture.
2426
2427 if(_nb_other_local_copy_for(imgid) == 0)
2428 {
2429 _move_text_sidecar_if_present(locppath, destpath, TRUE);
2430 g_file_delete(dest, NULL, NULL);
2431 }
2432
2433 g_object_unref(dest);
2434
2435 // delete xmp if any
2436 dt_image_path_append_version(imgid, locppath, sizeof(locppath));
2437 g_strlcat(locppath, ".xmp", sizeof(locppath));
2438 dest = g_file_new_for_path(locppath);
2439
2440 if(g_file_test(locppath, G_FILE_TEST_EXISTS)) g_file_delete(dest, NULL, NULL);
2441 g_object_unref(dest);
2442 }
2443
2444 // update cache, remove local copy flags, this is done in all cases here as when we
2445 // reach this point the local-copy flag is present and the file has been either removed
2446 // or is not present.
2447
2448 img = dt_image_cache_get(imgid, 'w');
2449 img->flags &= ~DT_IMAGE_LOCAL_COPY;
2451
2453
2454 return 0;
2455}
2456
2457// *******************************************************
2458// xmp stuff
2459// *******************************************************
2460
2461static gboolean _sidecar_is_up_to_date(const dt_image_t *img)
2462{
2463 if(IS_NULL_PTR(img) || img->id <= 0) return FALSE;
2464 if(img->change_timestamp <= 0) return FALSE;
2465
2466 const int64_t write_timestamp = dt_image_repository_get_write_timestamp(img->id);
2467 if(write_timestamp <= 0) return FALSE;
2468
2470 if(IS_NULL_PTR(gdt)) return FALSE;
2471 const int64_t change_timestamp_unix = g_date_time_to_unix(gdt);
2472 g_date_time_unref(gdt);
2474 "[xmp] imgid %d change_ts=%lld write_ts=%lld\n",
2475 img->id, (long long)change_timestamp_unix, (long long)write_timestamp);
2476 return change_timestamp_unix <= write_timestamp;
2477}
2478
2480 const gboolean force_write)
2481{
2482 if(IS_NULL_PTR(img) || img->id <= 0) return DT_IMAGE_WRITE_SIDECAR_DISABLED;
2483
2484 char imgpath[DT_PATH_MAX] = { 0 };
2485 if(dt_image_choose_input_path(img, imgpath, sizeof(imgpath), FALSE) == DT_IMAGE_PATH_NONE)
2486 {
2487 dt_print(DT_DEBUG_CONTROL, "[xmp] imgid %d no source path available\n", img->id);
2489 }
2490
2491 char filename[DT_PATH_MAX] = { 0 };
2492 g_strlcpy(filename, imgpath, sizeof(filename));
2493 dt_image_path_append_version(img->id, filename, sizeof(filename));
2494 g_strlcat(filename, ".xmp", sizeof(filename));
2495
2496 if(!force_write && g_file_test(filename, G_FILE_TEST_EXISTS))
2497 {
2498 if(_sidecar_is_up_to_date(img))
2499 {
2500 dt_print(DT_DEBUG_CONTROL, "[xmp] imgid %d sidecar up-to-date, skip\n", img->id);
2502 }
2503 }
2504
2505 dt_print(DT_DEBUG_CONTROL, "[xmp] imgid %d writing sidecar %s\n", img->id, filename);
2506 if(dt_exif_xmp_write_with_imgpath(img, filename, imgpath) != 0)
2508
2509 dt_print(DT_DEBUG_CONTROL, "[xmp] imgid %d updating write_timestamp\n", img->id);
2512}
2513
2515{
2516 if(imgid <= 0 || !dt_image_get_xmp_mode()) return DT_IMAGE_WRITE_SIDECAR_DISABLED;
2517
2518 dt_print(DT_DEBUG_CONTROL, "[xmp] imgid %d write start\n", imgid);
2519 dt_image_t *img = dt_image_cache_get(imgid, 'w');
2520 if(IS_NULL_PTR(img))
2521 {
2522 dt_print(DT_DEBUG_CONTROL, "[xmp] imgid %d cache lock failed\n", imgid);
2524 }
2525 dt_print(DT_DEBUG_CONTROL, "[xmp] imgid %d cache lock acquired (write)\n", imgid);
2526
2529 dt_print(DT_DEBUG_CONTROL, "[xmp] imgid %d cache lock released (write minimal)\n", imgid);
2530 return res;
2531}
2532
2534{
2535 if(imgid <= 0) return DT_IMAGE_WRITE_SIDECAR_DISABLED;
2536
2537 dt_print(DT_DEBUG_CONTROL, "[xmp] imgid %d forced write start\n", imgid);
2538 dt_image_t *img = dt_image_cache_get(imgid, 'w');
2539 if(IS_NULL_PTR(img))
2540 {
2541 dt_print(DT_DEBUG_CONTROL, "[xmp] imgid %d cache lock failed\n", imgid);
2543 }
2544 dt_print(DT_DEBUG_CONTROL, "[xmp] imgid %d cache lock acquired (write)\n", imgid);
2545
2548 dt_print(DT_DEBUG_CONTROL, "[xmp] imgid %d cache lock released (write minimal)\n", imgid);
2549 return res;
2550}
2551
2552void dt_image_synch_xmps(const GList *img)
2553{
2554 if(IS_NULL_PTR(img)) return;
2556 {
2558 }
2559}
2560
2562{
2563 if(selected > 0)
2564 {
2565 GList *imgs = g_list_append(NULL, GINT_TO_POINTER(selected));
2567 g_list_free(imgs);
2568 imgs = NULL;
2569 }
2570 else
2571 {
2572 GList *imgs = dt_act_on_get_images();
2573 dt_image_synch_xmps(imgs);
2574 g_list_free(imgs);
2575 imgs = NULL;
2576 }
2577}
2578
2579void dt_image_synch_all_xmp(const gchar *pathname)
2580{
2582 {
2583 const int32_t imgid = dt_image_get_id_full_path(pathname);
2584 if(imgid != UNKNOWN_IMAGE)
2585 {
2586 GList *imgs = g_list_append(NULL, GINT_TO_POINTER(imgid));
2588 g_list_free(imgs);
2589 imgs = NULL;
2590 }
2591 }
2592}
2593
2595{
2596 GList *imgs = NULL;
2597
2599 for(GList *l = candidates; l; l = g_list_next(l))
2600 {
2601 const int32_t imgid = GPOINTER_TO_INT(l->data);
2602 gboolean from_cache = FALSE;
2603 char filename[DT_PATH_MAX] = { 0 };
2604 dt_image_full_path(imgid, filename, sizeof(filename), &from_cache, __FUNCTION__);
2605
2606 if(g_file_test(filename, G_FILE_TEST_EXISTS))
2607 {
2608 imgs = g_list_prepend(imgs, GINT_TO_POINTER(imgid));
2609 }
2610 }
2611 g_list_free(candidates);
2612
2613 const int count = g_list_length(imgs);
2614 if(count > 0)
2615 {
2617 dt_control_log(ngettext("%d local copy has been synchronized",
2618 "%d local copies have been synchronized", count),
2619 count);
2620 }
2621 g_list_free(imgs);
2622 imgs = NULL;
2623}
2624
2625void dt_image_get_datetime(const int32_t imgid, char *datetime)
2626{
2627 if(IS_NULL_PTR(datetime)) return;
2628 datetime[0] = '\0';
2629 const dt_image_t *cimg = dt_image_cache_get(imgid, 'r');
2630 if(IS_NULL_PTR(cimg)) return;
2633}
2634
2635static void _datetime_undo_data_free(gpointer data)
2636{
2637 GList *l = (GList *)data;
2638 g_list_free_full(l, dt_free_gpointer);
2639 l = NULL;
2640}
2641
2646
2647static void _image_set_datetimes(const GList *img, const GArray *dtime,
2648 GList **undo, const gboolean undo_on)
2649{
2650 int i = 0;
2651 for(GList *imgs = (GList *)img; imgs; imgs = g_list_next(imgs))
2652 {
2653 const int32_t imgid = GPOINTER_TO_INT(imgs->data);
2654 // if char *datetime, the returned pointer is not correct => use of _datetime_t
2655 const _datetime_t *datetime = &g_array_index(dtime, _datetime_t, i);
2656 if(undo_on)
2657 {
2658 dt_undo_datetime_t *undodatetime = (dt_undo_datetime_t *)malloc(sizeof(dt_undo_datetime_t));
2659 undodatetime->imgid = imgid;
2660 dt_image_get_datetime(imgid, undodatetime->before);
2661
2662 memcpy(&undodatetime->after, datetime->dt, DT_DATETIME_LENGTH);
2663
2664 *undo = g_list_prepend(*undo, undodatetime);
2665 }
2666
2667 _set_datetime(imgid, datetime->dt);
2668 i++;
2669 }
2670}
2671
2672void dt_image_set_datetimes(const GList *imgs, const GArray *dtime, const gboolean undo_on)
2673{
2674 if(IS_NULL_PTR(imgs) || IS_NULL_PTR(dtime) || (g_list_length((GList *)imgs) != dtime->len))
2675 return;
2676 GList *undo = NULL;
2678
2679 _image_set_datetimes(imgs, dtime, &undo, undo_on);
2680
2681 if(undo_on)
2682 {
2685 }
2686}
2687
2688static void _image_set_datetime(const GList *img, const char *datetime,
2689 GList **undo, const gboolean undo_on)
2690{
2691 for(GList *imgs = (GList *)img; imgs; imgs = g_list_next(imgs))
2692 {
2693 const int32_t imgid = GPOINTER_TO_INT(imgs->data);
2694 if(undo_on)
2695 {
2696 dt_undo_datetime_t *undodatetime = (dt_undo_datetime_t *)malloc(sizeof(dt_undo_datetime_t));
2697 undodatetime->imgid = imgid;
2698 dt_image_get_datetime(imgid, undodatetime->before);
2699
2700 memcpy(&undodatetime->after, datetime, DT_DATETIME_LENGTH);
2701
2702 *undo = g_list_prepend(*undo, undodatetime);
2703 }
2704
2705 _set_datetime(imgid, datetime);
2706 }
2707}
2708
2709void dt_image_set_datetime(const GList *imgs, const char *datetime, const gboolean undo_on)
2710{
2711 if(IS_NULL_PTR(imgs))
2712 return;
2713 GList *undo = NULL;
2715
2716 _image_set_datetime(imgs, datetime, &undo, undo_on);
2717
2718 if(undo_on)
2719 {
2722 }
2723}
2724
2725char *dt_image_get_audio_path_from_path(const char *image_path)
2726{
2727 size_t len = strlen(image_path);
2728 const char *c = image_path + len;
2729 while((c > image_path) && (*c != '.')) c--;
2730 len = c - image_path + 1;
2731
2732 char *result = g_strndup(image_path, len + 3);
2733
2734 result[len] = 'w';
2735 result[len + 1] = 'a';
2736 result[len + 2] = 'v';
2737 if(g_file_test(result, G_FILE_TEST_EXISTS)) return result;
2738
2739 result[len] = 'W';
2740 result[len + 1] = 'A';
2741 result[len + 2] = 'V';
2742 if(g_file_test(result, G_FILE_TEST_EXISTS)) return result;
2743
2744 dt_free(result);
2745 return NULL;
2746}
2747
2748char *dt_image_get_audio_path(const int32_t imgid)
2749{
2750 gboolean from_cache = FALSE;
2751 char image_path[DT_PATH_MAX] = { 0 };
2752 dt_image_full_path(imgid, image_path, sizeof(image_path), &from_cache, __FUNCTION__);
2753
2754 return dt_image_get_audio_path_from_path(image_path);
2755}
2756
2757static char *_text_path_legacy_if_exists(const char *image_path)
2758{
2759 size_t len = strlen(image_path);
2760 const char *c = image_path + len;
2761 while((c > image_path) && (*c != '.')) c--;
2762 len = c - image_path + 1;
2763
2764 char *result = g_strndup(image_path, len + 3);
2765
2766 result[len] = 't';
2767 result[len + 1] = 'x';
2768 result[len + 2] = 't';
2769 if(g_file_test(result, G_FILE_TEST_EXISTS)) return result;
2770
2771 result[len] = 'T';
2772 result[len + 1] = 'X';
2773 result[len + 2] = 'T';
2774 if(g_file_test(result, G_FILE_TEST_EXISTS)) return result;
2775
2776 dt_free(result);
2777 return NULL;
2778}
2779
2780static char *_text_path_legacy_build(const char *image_path)
2781{
2782 size_t len = strlen(image_path);
2783 const char *c = image_path + len;
2784 while((c > image_path) && (*c != '.')) c--;
2785 len = c - image_path + 1;
2786
2787 char *result = g_strndup(image_path, len + 3);
2788 result[len] = 't';
2789 result[len + 1] = 'x';
2790 result[len + 2] = 't';
2791 return result;
2792}
2793
2794char *dt_image_get_text_path_from_path(const char *image_path)
2795{
2796 if(IS_NULL_PTR(image_path)) return NULL;
2797
2798 return _text_path_legacy_if_exists(image_path);
2799}
2800
2801char *dt_image_build_text_path_from_path(const char *image_path)
2802{
2803 if(IS_NULL_PTR(image_path)) return NULL;
2804
2805 return _text_path_legacy_build(image_path);
2806}
2807
2808static void _copy_text_sidecar_if_present(const char *src_image_path, const char *dest_image_path)
2809{
2810 if(IS_NULL_PTR(src_image_path) || IS_NULL_PTR(dest_image_path)) return;
2811
2812 char *src_txt = dt_image_get_text_path_from_path(src_image_path);
2813 if(IS_NULL_PTR(src_txt)) return;
2814
2815 char *dest_txt = _text_path_legacy_build(dest_image_path);
2816 if(dest_txt)
2817 {
2818 GFile *src = g_file_new_for_path(src_txt);
2819 GFile *dest = g_file_new_for_path(dest_txt);
2820 GError *gerror = NULL;
2821 gboolean copyStatus = g_file_copy(src, dest, G_FILE_COPY_NONE, NULL, NULL, NULL, &gerror);
2822
2823 if(!copyStatus && g_error_matches(gerror, G_IO_ERROR, G_IO_ERROR_EXISTS))
2824 copyStatus = TRUE;
2825
2826 if(!copyStatus && gerror)
2827 fprintf(stderr, "[dt_image] failed to copy text sidecar `%s' -> `%s': %s\n",
2828 src_txt, dest_txt, gerror->message);
2829
2830 g_clear_error(&gerror);
2831 g_object_unref(dest);
2832 g_object_unref(src);
2833 }
2834
2835 dt_free(dest_txt);
2836 dt_free(src_txt);
2837}
2838
2839static void _move_text_sidecar_if_present(const char *src_image_path, const char *dest_image_path, const gboolean overwrite)
2840{
2841 if(IS_NULL_PTR(src_image_path) || IS_NULL_PTR(dest_image_path)) return;
2842
2843 char *src_txt = dt_image_get_text_path_from_path(src_image_path);
2844 if(IS_NULL_PTR(src_txt)) return;
2845
2846 char *dest_txt = _text_path_legacy_build(dest_image_path);
2847 if(dest_txt)
2848 {
2849 GFile *src = g_file_new_for_path(src_txt);
2850 GFile *dest = g_file_new_for_path(dest_txt);
2851 GError *gerror = NULL;
2852 const GFileCopyFlags flags = overwrite ? G_FILE_COPY_OVERWRITE : G_FILE_COPY_NONE;
2853 gboolean moveStatus = g_file_move(src, dest, flags, NULL, NULL, NULL, &gerror);
2854
2855 if(!moveStatus && gerror)
2856 fprintf(stderr, "[dt_image] failed to move text sidecar `%s' -> `%s': %s\n",
2857 src_txt, dest_txt, gerror->message);
2858
2859 g_clear_error(&gerror);
2860 g_object_unref(dest);
2861 g_object_unref(src);
2862 }
2863
2864 dt_free(dest_txt);
2865 dt_free(src_txt);
2866}
2867
2868char *dt_image_get_text_path(const int32_t imgid)
2869{
2870 char image_path[DT_PATH_MAX] = { 0 };
2871
2872 gboolean from_cache = FALSE;
2873 dt_image_full_path(imgid, image_path, sizeof(image_path), &from_cache, __FUNCTION__);
2874
2875 if(image_path[0] != '\0' && g_file_test(image_path, G_FILE_TEST_EXISTS))
2876 return dt_image_get_text_path_from_path(image_path);
2877
2878 from_cache = TRUE;
2879 dt_image_full_path(imgid, image_path, sizeof(image_path), &from_cache, __FUNCTION__);
2880
2881 if(from_cache && image_path[0] != '\0' && g_file_test(image_path, G_FILE_TEST_EXISTS))
2882 return dt_image_get_text_path_from_path(image_path);
2883
2884 return NULL;
2885}
2886
2887float dt_image_get_exposure_bias(const struct dt_image_t *image_storage)
2888{
2889 // just check that pointers exist and are initialized
2890 if((image_storage) && (image_storage->exif_exposure_bias))
2891 {
2892 // sanity checks because I don't trust exif tags too much
2893 if(isnan(image_storage->exif_exposure_bias)
2894 || CLAMP(image_storage->exif_exposure_bias, -5.0f, 5.0f) != image_storage->exif_exposure_bias)
2895 return 0.0f; // isnan
2896 else
2897 return CLAMP(image_storage->exif_exposure_bias, -5.0f, 5.0f);
2898 }
2899 else
2900 return 0.0f;
2901}
2902
2903char *dt_image_camera_missing_sample_message(const struct dt_image_t *img, gboolean logmsg)
2904{
2905 const char *T1 = _("<b>WARNING</b>: camera is missing samples!");
2906 const char *T2 = _("You must provide samples in <a href='https://raw.pixls.us/'>https://raw.pixls.us/</a>");
2907 char *T3 = g_strdup_printf(_("for `%s' `%s'\n"
2908 "in as many format/compression/bit depths as possible"),
2909 img->camera_maker, img->camera_model);
2910 const char *T4 = _("or the <b>RAW won't be readable</b> in next version.");
2911
2912 char *NL = logmsg ? "\n\n" : "\n";
2913 char *PREFIX = logmsg ? "<big>" : "";
2914 char *SUFFIX = logmsg ? "</big>" : "";
2915
2916 char *msg = g_strconcat(PREFIX, T1, NL, T2, NL, T3, NL, T4, SUFFIX, NULL);
2917
2918 if(logmsg)
2919 {
2920 char *newmsg = dt_util_str_replace(msg, "<b>", "<span foreground='red'><b>");
2921 dt_free(msg);
2922 msg = dt_util_str_replace(newmsg, "</b>", "</b></span>");
2923 dt_free(newmsg);
2924 }
2925
2926 dt_free(T3);
2927 return msg;
2928}
2929
2931{
2932 if(img->camera_missing_sample)
2933 {
2935 dt_control_log(msg, (char *)NULL);
2936 dt_free(msg);
2937 }
2938}
2939
2940void dt_get_dirname_from_imgid(gchar *dir, const int32_t imgid)
2941{
2942 gchar path[DT_PATH_MAX] = { 0 };
2943 gboolean from_cache = FALSE;
2944 dt_image_full_path(imgid, path, sizeof(path), &from_cache, __FUNCTION__);
2945 gchar *folder = g_path_get_dirname(path);
2946 g_strlcpy(dir, folder, DT_PATH_MAX); // callers all provide a DT_PATH_MAX-sized dir buffer
2947 dt_free(folder);
2948}
2949
2950// clang-format off
2951// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
2952// vim: shiftwidth=2 expandtab tabstop=2 cindent
2953// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
2954// clang-format on
GList * dt_act_on_get_images()
Definition act_on.c:38
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_atomic_set_int(dt_atomic_int *var, int value)
int dt_atomic_get_int(dt_atomic_int *var)
atomic_int dt_atomic_int
Definition atomic.h:68
const char * extension(dt_imageio_module_data_t *data)
Definition avif.c:651
uint32_t bit_depth
Definition avif.c:103
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
static void transform(float *x, float *o, const float *m, const float t_h, const float t_v)
Definition clipping.c:487
void dt_collection_update_query(const dt_collection_t *collection, dt_collection_change_t query_change, dt_collection_properties_t changed_property, GList *list)
Definition collection.c:837
dt_collection_t * dt_collection_get_global(void)
Definition collection.c:138
@ DT_COLLECTION_PROP_UNDEF
Definition collection.h:155
@ DT_COLLECTION_CHANGE_RELOAD
Definition collection.h:164
@ IOP_CS_RAW
@ IOP_CS_RGB
const dt_colormatrix_t dt_aligned_pixel_t out
const float top
const dt_colormatrix_t matrix
gchar * dt_conf_get_string(const char *name)
Read the stored string for name as a private copy.
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
void dt_conf_set_string(const char *name, const char *val)
static dt_atomic_int _xmp_mode_cache
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 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)
int32_t dt_image_import(const int32_t film_id, const char *filename, gboolean raise_signals)
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)
dt_image_flags_t dt_image_flags_from_extension(const char *extension)
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)
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 dt_image_orientation_t _orientation_mirror_horizontally(dt_image_orientation_t orientation)
Toggle the stored bit that mirrors the image along the axis the USER calls horizontal.
int32_t dt_image_duplicate_no_reload(const int32_t imgid)
static const char * _image_buf_type_to_string(const dt_iop_buffer_type_t type)
int dt_image_monochrome_flags(const dt_image_t *img)
uint32_t dt_image_altered(const int32_t imgid)
int32_t dt_image_import_lua(const int32_t film_id, const char *filename)
void dt_image_get_datetime(const int32_t imgid, char *datetime)
const char * dt_image_pipe_class_name(const dt_image_pipe_class_t klass)
static void _geotag_undo_data_free(gpointer data)
char * dt_image_get_text_path_from_path(const char *image_path)
void dt_image_set_location(const int32_t imgid, const dt_image_geoloc_t *geoloc, const gboolean undo_on, const gboolean group_on)
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)
static dt_image_orientation_t _orientation_mirror_vertically(dt_image_orientation_t orientation)
The same, along the axis the user calls vertical.
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 int32_t _image_duplicate_with_version(const int32_t imgid, const int32_t newversion, const gboolean undo, const gboolean reload)
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)
float dt_image_get_exposure_bias(const struct dt_image_t *image_storage)
GList * dt_image_find_xmps(const char *filename)
void dt_image_flip(const int32_t imgid, const dt_image_transform_t transform)
Apply an orientation change to one image, on top of the orientation it already has.
static dt_image_write_sidecar_result_t _write_sidecar_file_from_image_locked(const dt_image_t *img, const gboolean force_write)
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)
static dt_image_orientation_t _image_get_history_orientation(const int32_t imgid)
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)
void dt_image_remove_undoable(const int32_t imgid)
Same as dt_image_remove(), and records the removal on the undo stack.
gboolean dt_image_use_monochrome_workflow(const dt_image_t *img)
int dt_image_local_copy_set(const int32_t imgid)
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)
gboolean dt_image_get_usercrop_oriented(const dt_image_t *img, const dt_image_orientation_t orientation, dt_boundingbox_t box)
dt_image_path_source_t dt_image_choose_input_path(const dt_image_t *img, char *pathname, size_t pathname_len, gboolean force_cache)
dt_image_write_sidecar_result_t dt_image_write_sidecar_file(const int32_t imgid)
static void _remove_undo_data_free(gpointer data)
void dt_image_xmp_mode_refresh_from_conf(void)
dt_image_orientation_t dt_image_get_effective_orientation(const dt_image_t *img)
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)
static void _image_remove(const int32_t imgid, const gboolean undo)
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)
int dt_image_read_duplicates(const uint32_t id, const char *filename, const gboolean clear_selection)
void dt_image_orient_boundingbox(const dt_boundingbox_t in, const dt_image_orientation_t orientation, dt_boundingbox_t out)
gboolean dt_image_resolve_usercrop(const int32_t imgid, dt_boundingbox_t box)
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)
static gboolean _sidecar_is_up_to_date(const dt_image_t *img)
dt_image_write_sidecar_result_t dt_image_write_sidecar_file_forced(const int32_t imgid)
int32_t dt_image_duplicate(const int32_t imgid)
gboolean dt_image_get_usercrop(const dt_image_t *img, dt_boundingbox_t box)
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.
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)
void dt_control_log(const char *msg,...)
Definition control.c:824
void dt_control_queue_redraw_center()
Request a redraw of the centre view.
Definition control.c:924
void dt_control_save_xmp(const int32_t imgid)
void dt_control_save_xmps(const GList *imgids, const gboolean check_history)
void dt_control_save_xmps_forced(const GList *imgids)
struct dt_develop_t * dt_dev_get_global(void)
Definition darktable.c:528
GList * dt_iop_get_modules_so(void)
Definition darktable.c:533
struct dt_view_manager_t * dt_view_manager_get_global(void)
Definition darktable.c:599
gboolean dt_database_is_open(void)
Definition database.c:3771
gboolean dt_datetime_img_to_exif(char *exif, const size_t exif_size, const dt_image_t *img)
Definition datetime.c:255
GDateTime * dt_datetime_gtimespan_to_gdatetime(const GTimeSpan gts)
Definition datetime.c:445
GTimeSpan dt_datetime_now_to_gtimespan()
Definition datetime.c:237
void dt_datetime_exif_to_img(dt_image_t *img, const char *exif)
Definition datetime.c:243
#define DT_DATETIME_LENGTH
Definition datetime.h:38
GHashTable * selected
set of checked row labels, mirrored to conf on every change
GtkWidget * folder
destination folder chooser
const int res
Definition dtpthread.h:351
int dt_exif_read(dt_image_t *img, const char *path)
Definition exif.cc:1994
void dt_exif_read_usercrop(dt_image_t *img, const char *filename)
Definition exif.cc:1031
What a photograph says about itself: the EXIF, IPTC and XMP tags a camera and a cataloguer write,...
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_film_notify_rolls_changed(void)
State that the film-roll list changed.
Definition film.c:247
char * dt_film_repository_get_folder(const int32_t id)
dt_iop_buffer_type_t
Definition format.h:54
@ TYPE_FLOAT
Definition format.h:56
@ TYPE_UNKNOWN
Definition format.h:55
@ TYPE_UINT8
Definition format.h:58
@ TYPE_UINT16
Definition format.h:57
int dt_grouping_remove_from_group(const int32_t image_id)
Definition grouping.c:58
void dt_grouping_add_grouped_images(GList **images)
Definition grouping.c:148
void dt_grouping_add_to_group(const int32_t group_id, const int32_t image_id)
Definition grouping.c:47
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
gboolean dt_history_repository_get_last_enabled_params(const int32_t imgid, const char *operation, void **params, int32_t *params_len)
gboolean dt_history_repository_write_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)
int dt_history_repository_count_items(const int32_t imgid)
gboolean dt_history_repository_set_end(const int32_t imgid, const int32_t history_end)
int32_t dt_history_repository_get_next_num(const int32_t imgid)
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:191
@ DT_IMAGE_COLORSPACE_NONE
Definition image.h:192
@ DT_IMAGE_COLORSPACE_ADOBE_RGB
Definition image.h:194
@ DT_IMAGE_COLORSPACE_SRGB
Definition image.h:193
dt_image_path_source_t
Definition image.h:301
@ DT_IMAGE_PATH_NONE
Definition image.h:302
@ DT_IMAGE_PATH_LOCAL_COPY_LEGACY
Definition image.h:304
@ DT_IMAGE_PATH_ORIGINAL
Definition image.h:305
@ DT_IMAGE_PATH_LOCAL_COPY
Definition image.h:303
#define UNKNOWN_IMAGE
Definition image.h:78
float dt_boundingbox_t[4]
Definition image.h:83
dt_image_orientation_t
Definition image.h:216
@ ORIENTATION_SWAP_XY
Definition image.h:221
@ ORIENTATION_FLIP_Y
Definition image.h:219
@ ORIENTATION_NULL
Definition image.h:217
@ ORIENTATION_NONE
Definition image.h:218
@ ORIENTATION_FLIP_X
Definition image.h:220
dt_image_flags_t
Definition image.h:104
@ DT_IMAGE_NO_LEGACY_PRESETS
Definition image.h:132
@ DT_IMAGE_HAS_ADDITIONAL_DNG_TAGS
Definition image.h:145
@ DT_IMAGE_HAS_WAV
Definition image.h:138
@ DT_IMAGE_AUTO_PRESETS_APPLIED
Definition image.h:130
@ DT_IMAGE_S_RAW
Definition image.h:147
@ DT_IMAGE_REMOVE
Definition image.h:128
@ DT_IMAGE_MOSAIC
Definition image.h:159
@ DT_IMAGE_MONOCHROME_BAYER
Definition image.h:151
@ DT_IMAGE_LOCAL_COPY
Definition image.h:134
@ DT_IMAGE_BUFFER_RESOLVED
Definition image.h:164
@ DT_IMAGE_RAW
Definition image.h:124
@ DT_IMAGE_REJECTED
Definition image.h:113
@ DT_IMAGE_4BAYER
Definition image.h:140
@ DT_IMAGE_MONOCHROME_WORKFLOW
Definition image.h:153
@ DT_IMAGE_HAS_TXT
Definition image.h:136
@ DT_IMAGE_MONOCHROME
Definition image.h:142
@ DT_IMAGE_HDR
Definition image.h:126
@ DT_IMAGE_LDR
Definition image.h:122
@ DT_IMAGE_MONOCHROME_PREVIEW
Definition image.h:149
dt_image_write_sidecar_result_t
Definition image.h:752
@ DT_IMAGE_WRITE_SIDECAR_OK
Definition image.h:753
@ DT_IMAGE_WRITE_SIDECAR_IO_ERROR
Definition image.h:757
@ DT_IMAGE_WRITE_SIDECAR_CACHE_BUSY
Definition image.h:755
@ DT_IMAGE_WRITE_SIDECAR_NO_SOURCE_PATH
Definition image.h:756
@ DT_IMAGE_WRITE_SIDECAR_DISABLED
Definition image.h:754
dt_image_transform_t
An orientation change the user asks for, in the coordinates they are looking at.
Definition image.h:245
@ DT_IMAGE_TRANSFORM_FLIP_VERTICALLY
mirror top <-> bottom, as displayed
Definition image.h:250
@ DT_IMAGE_TRANSFORM_ROTATE_CW
quarter turn clockwise
Definition image.h:246
@ DT_IMAGE_TRANSFORM_RESET
back to ORIENTATION_NULL, i.e. what EXIF says
Definition image.h:248
@ DT_IMAGE_TRANSFORM_FLIP_HORIZONTALLY
mirror left <-> right, as displayed
Definition image.h:249
@ DT_IMAGE_TRANSFORM_ROTATE_CCW
quarter turn counter-clockwise
Definition image.h:247
dt_image_pipe_class_t
Mutually-exclusive classification of an image by the early-pipeline processing it requires....
Definition image.h:182
@ DT_IMAGE_PIPE_MOSAIC_RAW
Definition image.h:184
@ DT_IMAGE_PIPE_LINEAR_RAW
Definition image.h:185
@ DT_IMAGE_PIPE_RGB_LDR
Definition image.h:186
@ DT_IMAGE_PIPE_RGB_HDR
Definition image.h:187
@ DT_IMAGE_PIPE_UNKNOWN
Definition image.h:183
static dt_image_orientation_t dt_image_orientation(const dt_image_t *img)
Definition image.h:692
@ DT_IMAGE_USERCROP_UNKNOWN
Definition image.h:266
@ DT_IMAGE_USERCROP_VALID
Definition image.h:269
@ LOADER_UNKNOWN
Definition image.h:283
void dt_image_cache_write_release(dt_image_t *img, dt_image_cache_write_mode_t mode)
gboolean dt_image_cache_is_ready(void)
Has the image cache been initialised? Callers that run before dt_image_cache_init() or after its clea...
Definition image_cache.c:83
dt_image_t * dt_image_cache_get(const int32_t imgid, char mode)
dt_image_t * dt_image_cache_get_reload(const int32_t imgid, char mode)
void dt_image_cache_remove(const int32_t imgid)
void dt_image_cache_read_release(const dt_image_t *img)
@ DT_IMAGE_CACHE_RELAXED
Definition image_cache.h:50
@ DT_IMAGE_CACHE_MINIMAL
Definition image_cache.h:53
@ DT_IMAGE_CACHE_SAFE
Definition image_cache.h:48
gboolean dt_image_ext_is_ambiguous(const char *ext)
gboolean dt_image_ext_is_supported(const char *ext)
gboolean dt_image_ext_is_ldr(const char *ext)
gboolean dt_image_ext_is_raw(const char *ext)
gboolean dt_image_ext_is_hdr(const char *ext)
void dt_image_notify_imported(const int32_t imgid)
Raise it. Internal to common/image.c.
common/image.c states that an image entered the library; whoever is showing the collection decides wh...
int dt_image_repository_get_version(const int32_t imgid)
The duplicate version number of imgid, or 0.
void dt_image_repository_touch_write_timestamp(const int32_t imgid)
Set write_timestamp of imgid to now.
int dt_image_repository_count_others_with_flag(const int32_t imgid, const int flag)
How many OTHER images share this image's film roll and filename and carry flag.
int64_t dt_image_repository_get_write_timestamp(const int32_t imgid)
main.images.write_timestamp for imgid, or 0.
gboolean dt_image_repository_set_group(const int32_t imgid, const int32_t group_id)
Set imgid's group_id.
int32_t dt_image_repository_find_group_for_pattern(const int32_t film_id, const char *filename_pattern, const gboolean leader_only, const int32_t exclude_imgid)
The group leader of an existing image in film_id matching filename_pattern (a LIKE pattern),...
gboolean dt_image_repository_delete(const int32_t imgid)
Delete imgid from main.images and main.meta_data.
int32_t dt_image_repository_copy_to_film(const int32_t imgid, const int32_t filmid, const char *new_filename, const char *old_filename)
Copy imgid into film roll filmid under new_filename, returning the new id.
GList * dt_image_repository_get_duplicate_ids(const int32_t imgid)
Every image id sharing imgid's film roll and filename, itself included, in row order – an image and i...
GList * dt_image_repository_get_ids_with_flag(const int flag)
Every image id whose flags carry flag, in row order.
gboolean dt_image_repository_clear_flag_among(GList *imgids, const int flag)
Clear flag on every image in imgids – the counterpart of dt_image_repository_set_flag_among(),...
int32_t dt_image_repository_duplicate(const int32_t imgid, const int32_t newversion)
Duplicate imgid as a new version, returning the new image id (-1 on failure).
int32_t dt_image_repository_find_by_film_and_filename(const int32_t film_id, const char *filename)
The image id for filename inside film roll film_id, or -1.
gboolean dt_image_repository_set_version(const int32_t imgid, const int version)
Set both version and max_version of imgid to version.
int32_t dt_image_repository_find_by_folder_and_filename(const char *folder, const char *filename)
The image id for a full path, split into its folder and filename, or -1.
GList * dt_image_repository_get_group_members(const int32_t group_id, const int32_t exclude_imgid)
Image ids in group group_id.
gboolean dt_image_repository_insert_import(const int32_t film_id, const char *filename, const int flags, const int64_t import_timestamp)
Insert a bare row for a file being imported, returning TRUE on success.
Reading and writing one dt_image_t to and from the library database. The SQL half of what used to be ...
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)
gboolean dt_lightroom_import(int32_t imgid, dt_develop_t *dev, gboolean iauto)
Definition lightroom.c:1053
_lib_location_type_t type
Definition location.c:1
@ DT_DEBUG_CONTROL
Definition logging.h:52
@ DT_DEBUG_IMAGEIO
Definition logging.h:68
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define CLAMPS(A, L, H)
Definition math.h:78
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
Definition mem_alloc.h:184
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
void dt_metadata_geotags_changed(const GList *imgs)
void dt_metadata_tags_changed(void)
Raise it. Internal to the module.
Everything this module says to the outside world: messages for the user, and the fact that the tag vo...
void dt_mipmap_cache_remove(const int32_t imgid, const gboolean flush_disk)
void dt_mipmap_cache_remove_all_sizes(const int32_t imgid, const gboolean flush_disk)
void dt_mipmap_cache_copy_thumbnails(const uint32_t dst_imgid, const uint32_t src_imgid)
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
Definition paths.h:57
#define DT_MAX_FILENAME_LEN
Definition paths.h:35
const char * name
Definition pdf.h:90
void dt_iop_buffer_dsc_update_bpp(dt_iop_buffer_dsc_t *dsc)
#define DT_VIEW_RATINGS_MASK
Definition ratings.h:42
void dt_removed_image_repository_clear(const int snap_id, const int32_t imgid)
Drop snapshot snap_id of imgid, making the removal permanent.
gboolean dt_removed_image_repository_restore(const int snap_id, const int32_t imgid)
Copy snapshot snap_id of imgid back into main, film roll included.
int dt_removed_image_repository_create(const int32_t imgid)
Copy every row imgid owns out of main, so the removal about to happen can be undone.
memory.removed_*: the staging area behind "undo remove from library".
struct dt_selection_t * dt_selection_get_global(void)
Definition selection.c:80
void dt_selection_clear(dt_selection_t *selection)
Definition selection.c:271
const float const int flip
char dt[DT_DATETIME_LENGTH]
double latitude
Definition image.h:332
double elevation
Definition image.h:332
double longitude
Definition image.h:332
gboolean has_audio
Definition image.h:476
gboolean is_hdr
Definition image.h:479
float exif_exposure
Definition image.h:367
float pixel_aspect_ratio
Definition image.h:443
int32_t height
Definition image.h:397
gboolean camera_missing_sample
Definition image.h:384
GTimeSpan export_timestamp
Definition image.h:415
uint64_t history_hash
Definition image.h:404
char camera_model[64]
Definition image.h:380
float exif_focus_distance
Definition image.h:372
dt_boundingbox_t usercrop
Definition image.h:460
GTimeSpan import_timestamp
Definition image.h:415
gboolean is_bw
Definition image.h:477
int32_t group_id
Definition image.h:401
char camera_makermodel[128]
Definition image.h:382
float exif_exposure_bias
Definition image.h:368
uint16_t raw_black_level
Definition image.h:437
float exif_iso
Definition image.h:370
GList * dng_gain_maps
Definition image.h:464
dt_image_loader_t loader
Definition image.h:417
char camera_maker[64]
Definition image.h:379
uint32_t raw_white_point
Definition image.h:439
int32_t exif_inited
Definition image.h:365
float exif_aperture
Definition image.h:369
char local_copy_path[DT_PATH_MAX]
Definition image.h:388
int32_t version
Definition image.h:401
int32_t crop_height
Definition image.h:398
uint64_t mipmap_hash
Definition image.h:411
dt_image_geoloc_t geoloc
Definition image.h:434
char camera_legacy_makermodel[128]
Definition image.h:383
dt_image_usercrop_status_t usercrop_status
Definition image.h:461
int32_t flags
Definition image.h:401
dt_image_orientation_t orientation
Definition image.h:366
int32_t width
Definition image.h:397
float exif_focal_length
Definition image.h:371
char exif_maker[64]
Definition image.h:374
GTimeSpan change_timestamp
Definition image.h:415
uint32_t profile_size
Definition image.h:423
int32_t crop_y
Definition image.h:398
uint32_t history_items
Definition image.h:403
int rating
Definition image.h:473
gboolean has_localcopy
Definition image.h:475
char camera_alias[64]
Definition image.h:381
char exif_lens[128]
Definition image.h:376
GTimeSpan print_timestamp
Definition image.h:415
int32_t film_id
Definition image.h:401
char folder[DT_PATH_MAX]
Definition image.h:390
int32_t crop_x
Definition image.h:398
int color_labels
Definition image.h:472
float d65_color_matrix[9]
Definition image.h:421
int32_t p_height
Definition image.h:397
dt_iop_buffer_dsc_t dsc
Definition image.h:419
int32_t p_width
Definition image.h:397
char filmroll[DT_PATH_MAX]
Definition image.h:391
char exif_model[64]
Definition image.h:375
dt_image_colorspace_t colorspace
Definition image.h:429
float adobe_XYZ_to_CAM[4][3]
Definition image.h:449
struct dt_colorspaces_color_profile_t * embedded_profile
Definition image.h:428
uint8_t * profile
Definition image.h:422
char datetime[200]
Definition image.h:392
struct dt_cache_entry_t * cache_entry
Definition image.h:482
int32_t crop_width
Definition image.h:398
uint32_t group_members
Definition image.h:402
char local_copy_legacy_path[DT_PATH_MAX]
Definition image.h:389
dt_image_raw_parameters_t legacy_flip
Definition image.h:431
char filename[DT_MAX_FILENAME_LEN]
Definition image.h:386
int32_t id
Definition image.h:401
uint16_t raw_black_level_separate[4]
Definition image.h:438
uint64_t self_hash
Definition image.h:412
dt_aligned_pixel_t wb_coeffs
Definition image.h:446
float exif_crop
Definition image.h:373
char fullpath[DT_PATH_MAX]
Definition image.h:387
uint32_t fuji_rotation_pos
Definition image.h:442
gboolean is_bw_flow
Definition image.h:478
uint32_t filters
Definition format.h:89
uint16_t raw_black_level
Definition format.h:103
unsigned int channels
Definition format.h:83
struct dt_iop_buffer_dsc_t::@55 rawprepare
dt_iop_buffer_type_t datatype
Definition format.h:85
dt_aligned_pixel_t processed_maximum
Definition format.h:114
uint16_t raw_white_point
Definition format.h:104
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:377
gboolean dt_tag_detach_by_string(const char *name, const int32_t imgid, const gboolean undo_on, const gboolean group_on)
Definition tags.c:500
gboolean dt_tag_new(const char *name, guint *tagid)
Definition tags.c:164
void dt_thumbnail_notify_image_changed(int32_t imgid, gboolean refresh_filmstrip)
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:40
@ DT_UNDO_REMOVE
Definition undo.h:53
@ DT_UNDO_GEOTAG
Definition undo.h:42
@ DT_UNDO_DATETIME
Definition undo.h:51
@ DT_UNDO_LT_HISTORY
Definition undo.h:49
@ DT_UNDO_FLAGS
Definition undo.h:50
@ DT_UNDO_DUPLICATE
Definition undo.h:52
dt_undo_t * dt_undo_get_global(void)
Definition darktable.c:611
void * dt_undo_data_t
Definition undo.h:70
dt_undo_action_t
Definition undo.h:65
@ DT_ACTION_UNDO
Definition undo.h:66
gchar * dt_util_str_replace(const gchar *string, const gchar *pattern, const gchar *substitute)
Definition utility.c:140
gboolean dt_util_test_image_file(const char *filename)
Definition utility.c:322
gchar * dt_util_normalize_path(const gchar *_input)
Definition utility.c:683
char * dt_util_format_exposure(const float exposuretime)
Definition utility.c:868
const dt_view_t * dt_view_manager_get_current_view(dt_view_manager_t *vm)
Definition view.c:139
@ DT_VIEW_DARKROOM
Definition view.h:79
int dt_exif_xmp_write_with_imgpath(const dt_image_t *image, const char *filename, const char *imgpath)
int dt_exif_xmp_read(dt_image_t *img, const char *filename, const int history_only)
The XMP document that carries an image's development: history stack, mask shapes, module order,...