Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
image_repository.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2025 Aurélien PIERRE.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#include <math.h> // NAN, the "no colour matrix" sentinel restored below
20#include <string.h>
21
23
25#include "database/database.h"
26#include "common/datetime.h"
27#include "database/sql_debug.h"
28#include "common/image.h"
29#include "common/logging.h"
30#include "system/dtpthread.h"
31#include "system/macros.h"
32
33#include <inttypes.h>
34#include <sqlite3.h>
35
36/* One statement cache for the whole process, guarded by one mutex, exactly as the image cache
37 * held it before the split: these statements are reused on every image load and every write,
38 * and re-preparing them per call was measurable. The mutex is what makes reuse safe, and it
39 * is why the calls below serialise against each other. */
46static dt_pthread_mutex_t _image_stmt_mutex;
48
57
59{
61 {
62 // clang-format off
65 "SELECT i.id, i.group_id, "
66 " (SELECT COUNT(id) FROM main.images WHERE group_id = i.group_id), "
67 " (SELECT COUNT(imgid) FROM main.history WHERE imgid = i.id), "
68 " COALESCE((SELECT current_hash FROM main.history_hash WHERE imgid = i.id), -1), "
69 " COALESCE((SELECT mipmap_hash FROM main.history_hash WHERE imgid = i.id), -1), "
70 " i.film_id, i.version, i.width, i.height, i.orientation, i.flags, "
71 " i.import_timestamp, i.change_timestamp, i.export_timestamp, i.print_timestamp, "
72 " i.exposure, i.exposure_bias, i.aperture, i.iso, i.focal_length, i.focus_distance, "
73 " i.datetime_taken, i.longitude, i.latitude, i.altitude, "
74 " i.filename, f.folder || '" G_DIR_SEPARATOR_S "' || i.filename, "
75 " i.maker, i.model, i.lens, f.folder, "
76 " COALESCE((SELECT SUM(1 << color) FROM main.color_labels WHERE imgid=i.id), 0), "
77 " i.crop, i.raw_parameters, i.color_matrix, i.colorspace, "
78 " i.raw_black, i.raw_maximum, i.aspect_ratio, i.output_width, i.output_height"
79 " FROM main.images AS i"
80 " LEFT JOIN main.film_rolls AS f ON f.id = i.film_id"
81 " WHERE i.id = ?1",
83 // clang-format on
84 }
85
88 return _image_load_stmt;
89}
90
92{
119
120 const char *filename = (const char *)sqlite3_column_text(stmt, 26);
121 if(filename) g_strlcpy(img->filename, filename, sizeof(img->filename));
122 const char *fullpath = (const char *)sqlite3_column_text(stmt, 27);
123 if(fullpath) g_strlcpy(img->fullpath, fullpath, sizeof(img->fullpath));
124 const char *maker = (const char *)sqlite3_column_text(stmt, 28);
125 if(maker) g_strlcpy(img->exif_maker, maker, sizeof(img->exif_maker));
126 const char *model = (const char *)sqlite3_column_text(stmt, 29);
127 if(model) g_strlcpy(img->exif_model, model, sizeof(img->exif_model));
128 const char *lens = (const char *)sqlite3_column_text(stmt, 30);
129 if(lens) g_strlcpy(img->exif_lens, lens, sizeof(img->exif_lens));
130 const char *folder = (const char *)sqlite3_column_text(stmt, 31);
131 if(folder) g_strlcpy(img->folder, folder, sizeof(img->folder));
132
134
137 {
138 uint32_t tmp = sqlite3_column_int(stmt, 34);
140 }
141 /* Size the copy from the blob, not from the destination: the writer binds exactly
142 * sizeof(d65_color_matrix) (see dt_image_repository_store()), so anything shorter is a row this
143 * schema did not write so anything else is a row no writer of this schema produces and reading nine
144 * floats out of it is undefined. No null test: sqlite returns NULL only for a NULL or a zero-length
145 * value, so a size of exactly 36 already says the pointer is good. sqlite3_column_bytes() comes AFTER
146 * sqlite3_column_blob() on purpose -- that is the order sqlite3.h documents as safe, a size
147 * asked for first being allowed to describe a value the later call converts.
148 *
149 * The else branch is the one that runs in practice, on the NULL column of a row not yet stored.
150 * It matters because dt_image_cache_get_reload() reloads into an already populated struct
151 * WITHOUT going through dt_image_init(): the matrix from a previous load would otherwise
152 * survive a row that no longer carries one. NAN is the documented "no matrix" sentinel the
153 * readers test (imageio_profile.c, iop/colorin.c, pixel/illuminants.h). */
154 const void *color_matrix = sqlite3_column_blob(stmt, 35);
155 if(sqlite3_column_bytes(stmt, 35) == (int)sizeof(img->d65_color_matrix))
157 else
158 img->d65_color_matrix[0] = NAN;
162
163 if(img->fullpath[0])
165 sizeof(img->local_copy_path), img->local_copy_legacy_path,
166 sizeof(img->local_copy_legacy_path));
167
168 img->exif_inited = (img->exif_focus_distance >= 0 && img->orientation >= 0);
169
170 if(img->folder[0])
171 g_strlcpy(img->filmroll, dt_image_film_roll_name(img->folder), sizeof(img->filmroll));
172
174
175 // img->dsc are written by imageio drivers : never (re)set them from DB,
176 // they are not saved anyway. Until the codec decodes the file, seed a provisional
177 // descriptor from the (extension-derived) pipeline class so the image can be reasoned
178 // about and the first pipeline stage has a usable contract even before decoding.
180
181 /* Everything past the columns -- rating, monochrome and HDR predicates, the
182 * extension cross-check, makermodel -- is DERIVED, not stored, and needs symbols from
183 * imageio/ and views/. A row mapper that reached two layers up for them is what kept this
184 * code in common/. Callers run dt_image_derive_fields() next; see image_repository.h. */
185
186}
187
189{
190 if(IS_NULL_PTR(img) || img->id <= 0) return;
191
195 {
196 // clang-format off
199 "INSERT INTO main.history_hash (imgid, current_hash, basic_hash, auto_hash, mipmap_hash)"
200 " VALUES (?1, ?2, NULL, NULL, ?3)"
201 " ON CONFLICT (imgid)"
202 " DO UPDATE SET current_hash = ?2, basic_hash = NULL, auto_hash = NULL, mipmap_hash = ?3",
204 // clang-format on
205 }
214}
215
216
217gboolean dt_image_repository_load(const int32_t imgid, dt_image_t *img)
218{
219 if(IS_NULL_PTR(img)) return FALSE;
220
221 gboolean found = FALSE;
224
230 {
232 found = TRUE;
233 }
234 else
235 {
236 img->id = -1;
237 fprintf(stderr, "[image_repository_load] failed to open image %" PRId32 " from database: %s\n", imgid,
239 }
240
242 return found;
243}
244
246{
247 if(IS_NULL_PTR(cb)) return;
248
250 // clang-format off
253 // Same columns, same order, as the single-image load above -- dt_image_from_stmt() maps
254 // both. Only the source differs: the collection instead of one id.
255 "SELECT i.id, i.group_id, "
256 " (SELECT COUNT(id) FROM main.images WHERE group_id = i.group_id), "
257 " (SELECT COUNT(imgid) FROM main.history WHERE imgid = i.id), "
258 " COALESCE((SELECT current_hash FROM main.history_hash WHERE imgid = i.id), -1), "
259 " COALESCE((SELECT mipmap_hash FROM main.history_hash WHERE imgid = i.id), -1), "
260 " i.film_id, i.version, i.width, i.height, i.orientation, i.flags, "
261 " i.import_timestamp, i.change_timestamp, i.export_timestamp, i.print_timestamp, "
262 " i.exposure, i.exposure_bias, i.aperture, i.iso, i.focal_length, i.focus_distance, "
263 " i.datetime_taken, i.longitude, i.latitude, i.altitude, "
264 " i.filename, f.folder || '" G_DIR_SEPARATOR_S "' || i.filename, "
265 " i.maker, i.model, i.lens, f.folder, "
266 " COALESCE((SELECT SUM(1 << color) FROM main.color_labels WHERE imgid=i.id), 0), "
267 " i.crop, i.raw_parameters, i.color_matrix, i.colorspace, "
268 " i.raw_black, i.raw_maximum, i.aspect_ratio, i.output_width, i.output_height"
269 " FROM main.images AS i"
270 " JOIN memory.collected_images AS c ON i.id = c.imgid"
271 " LEFT JOIN main.film_rolls AS f ON f.id = i.film_id"
272 " ORDER BY c.rowid ASC",
273 -1, &stmt, NULL);
274 // clang-format on
275 if(IS_NULL_PTR(stmt)) return;
276
277 while(sqlite3_step(stmt) == SQLITE_ROW)
278 {
279 dt_image_t info;
280 dt_image_init(&info);
281 dt_image_from_stmt(&info, stmt);
282 // No lock is held here on purpose: see the header. cb() reaches the selection and the
283 // image cache, and either can come back through this function.
284 cb(user_data, &info);
285 }
287}
288
289
291{
292 if(IS_NULL_PTR(img) || img->id <= 0) return;
293
294 union {
296 uint32_t u;
297 } flip;
298
300 // clang-format off
302 "UPDATE main.images"
303 " SET width = ?1, height = ?2, filename = ?3, maker = ?4, model = ?5,"
304 " lens = ?6, exposure = ?7, aperture = ?8, iso = ?9, focal_length = ?10,"
305 " focus_distance = ?11, film_id = ?12, datetime_taken = ?13, flags = ?14,"
306 " crop = ?15, orientation = ?16, raw_parameters = ?17, group_id = ?18,"
307 " longitude = ?19, latitude = ?20, altitude = ?21, color_matrix = ?22,"
308 " colorspace = ?23, raw_black = ?24, raw_maximum = ?25,"
309 " aspect_ratio = ROUND(?26,1), exposure_bias = ?27,"
310 " import_timestamp = ?28, change_timestamp = ?29, export_timestamp = ?30,"
311 " print_timestamp = ?31, output_width = ?32, output_height = ?33"
312 " WHERE id = ?34",
313 -1, &stmt, NULL);
314 // clang-format on
327 if(img->exif_datetime_taken)
332 flip.s = img->legacy_flip;
342 DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 26, 0.); // img->aspect_ratio deprecated
344 if(img->import_timestamp)
346 if(img->change_timestamp)
348 if(img->export_timestamp)
350 if(img->print_timestamp)
352 DT_DEBUG_SQLITE3_BIND_INT(stmt, 32, 0); // img->final_width deprecated
353 DT_DEBUG_SQLITE3_BIND_INT(stmt, 33, 0); // img->final_height deprecated
355 const int rc = sqlite3_step(stmt);
356 if(rc != SQLITE_DONE) fprintf(stderr, "[image_cache_write_release] sqlite3 error %d\n", rc);
358
359 /* Straight to the table. This used to call dt_colorlabels_set_labels() in common/, i.e.
360 * the persistence layer reaching up into the domain to have it issue the queries the
361 * persistence layer is for -- the only edge in that direction here. */
362 for(int color = 0; color < 5; color++)
363 {
364 if(img->color_labels & (1 << color))
366 else
368 }
370}
371
372
373/* ---------------------------------------------------------------------------------------
374 * Grouping
375 * ------------------------------------------------------------------------------------- */
376
377/* Collect the `id` column of a stepped statement into a list, dropping @p exclude_imgid. */
379{
380 GList *ids = NULL;
381 while(sqlite3_step(stmt) == SQLITE_ROW)
382 {
383 const int32_t id = sqlite3_column_int(stmt, 0);
384 if(id != exclude_imgid) ids = g_list_prepend(ids, GINT_TO_POINTER(id));
385 }
387 return g_list_reverse(ids);
388}
389
390GList *dt_image_repository_get_group_members(const int32_t group_id, const int32_t exclude_imgid)
391{
394 "SELECT id FROM main.images WHERE group_id = ?1", -1, &stmt, NULL);
395 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, group_id);
397}
398
399void dt_image_group_member_free(gpointer data)
400{
402 if(IS_NULL_PTR(member)) return;
403 dt_free(member->filename);
405}
406
408{
409 GList *members = NULL;
411 // clang-format off
413 "SELECT id, version, filename"
414 " FROM main.images"
415 " WHERE group_id = ?1", -1, &stmt,
416 NULL);
417 // clang-format on
418 if(IS_NULL_PTR(stmt)) return NULL;
419
420 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, group_id);
421 while(sqlite3_step(stmt) == SQLITE_ROW)
422 {
424 if(IS_NULL_PTR(member)) break;
426 member->version = sqlite3_column_int(stmt, 1);
427 const char *filename = (const char *)sqlite3_column_text(stmt, 2);
428 member->filename = filename ? g_strdup(filename) : NULL;
430 }
432
433 // Row order: the caller walks it once to build a tooltip listing the group, and the order
434 // it reads is the order the rows came in.
435 return g_list_reverse(members);
436}
437
439{
442 "SELECT COUNT(*) FROM main.images WHERE id >= ?1 AND id <= ?2", -1,
443 &stmt, 0);
444 if(IS_NULL_PTR(stmt)) return -1;
445
448 const int count = (sqlite3_step(stmt) == SQLITE_ROW) ? sqlite3_column_int(stmt, 0) : -1;
450
451 return count;
452}
453
456 void *user_data)
457{
458 if(IS_NULL_PTR(cb)) return;
459
462 "SELECT id, filename FROM main.images WHERE id >= ?1 AND id <= ?2", -1,
463 &stmt, 0);
464 if(IS_NULL_PTR(stmt)) return;
465
468 while(sqlite3_step(stmt) == SQLITE_ROW)
469 cb(sqlite3_column_int(stmt, 0), (const char *)sqlite3_column_text(stmt, 1), user_data);
471}
472
473
475 const int32_t exclude_imgid)
476{
478 // clang-format off
480 "UPDATE main.images SET group_id = ?1 WHERE group_id = ?2 AND id != ?3",
481 -1, &stmt, NULL);
482 // clang-format on
488}
489
491{
493
494 if(imgid < 0)
495 {
496 // clang-format off
498 "SELECT flags FROM main.images WHERE id IN "
499 "(SELECT imgid FROM main.selected_images)",
500 -1, &stmt, NULL);
501 // clang-format on
502 }
503 else
504 {
505 // clang-format off
507 "SELECT flags FROM main.images WHERE id = ?1", -1, &stmt, NULL);
508 // clang-format on
510 }
511
512 GList *result = NULL;
513 while(sqlite3_step(stmt) == SQLITE_ROW)
514 {
515 const int stars = (sqlite3_column_int(stmt, 0) & 0x7) - 1;
516 result = g_list_prepend(result, GINT_TO_POINTER(stars));
517 }
519
520 return g_list_reverse(result);
521}
522
523/* ---------------------------------------------------------------------------------------------
524 * Identity lookups
525 * ------------------------------------------------------------------------------------------ */
526
527int32_t dt_image_repository_find_by_film_and_filename(const int32_t film_id, const char *filename)
528{
529 if(IS_NULL_PTR(filename)) return -1;
530
531 int32_t id = -1;
534 "SELECT id FROM main.images WHERE film_id = ?1 AND filename = ?2",
535 -1, &stmt, NULL);
536 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id);
540 return id;
541}
542
543int32_t dt_image_repository_find_by_folder_and_filename(const char *folder, const char *filename)
544{
545 if(IS_NULL_PTR(folder) || IS_NULL_PTR(filename)) return -1;
546
547 int32_t id = -1;
549 // clang-format off
551 "SELECT images.id"
552 " FROM main.images, main.film_rolls"
553 " WHERE film_rolls.folder = ?1"
554 " AND images.film_id = film_rolls.id"
555 " AND images.filename = ?2",
556 -1, &stmt, NULL);
557 // clang-format on
562
563 return id;
564}
565
566
567/* ---------------------------------------------------------------------------------------------
568 * Versions, flags and the write timestamp
569 * ------------------------------------------------------------------------------------------ */
570
571int dt_image_repository_get_version(const int32_t imgid)
572{
573 int version = 0;
575 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get_sqlite3_global(), "SELECT version FROM main.images WHERE id = ?1", -1,
576 &stmt, NULL);
578
579 if(sqlite3_step(stmt) == SQLITE_ROW) version = sqlite3_column_int(stmt, 0);
581 return version;
582}
583
584gboolean dt_image_repository_set_version(const int32_t imgid, const int version)
585{
589 "UPDATE main.images SET version=?1, max_version = ?1 WHERE id = ?2", -1, &stmt, NULL);
590 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, version);
592 const gboolean ok = (sqlite3_step(stmt) == SQLITE_DONE);
594 return ok;
595}
596
597int dt_image_repository_count_others_with_flag(const int32_t imgid, const int flag)
598{
600 int result = 1;
601
602 // clang-format off
604 "SELECT COUNT(*)"
605 " FROM main.images"
606 " WHERE id!=?1 AND flags&?2=?2"
607 " AND film_id=(SELECT film_id"
608 " FROM main.images"
609 " WHERE id=?1)"
610 " AND filename=(SELECT filename"
611 " FROM main.images"
612 " WHERE id=?1);",
613 -1, &stmt, NULL);
614 // clang-format on
619
620 return result;
621}
622
627static char *_id_set(GList *imgids)
628{
629 if(IS_NULL_PTR(imgids)) return NULL;
630
631 GString *set = g_string_new(NULL);
632 for(GList *l = imgids; l; l = g_list_next(l))
633 {
634 if(l != imgids) g_string_append_c(set, ',');
635 g_string_append_printf(set, "%d", GPOINTER_TO_INT(l->data));
636 }
637 return g_string_free(set, FALSE);
638}
639
641{
642 char *set = _id_set(imgids);
643 if(IS_NULL_PTR(set)) return NULL;
644
645 GList *ids = NULL;
647 char *query = g_strdup_printf("SELECT id FROM main.images WHERE id IN (%s) AND flags&?1=?1", set);
650 while(sqlite3_step(stmt) == SQLITE_ROW)
653 dt_free(query);
654 dt_free(set);
655 return g_list_reverse(ids); // row order
656}
657
659{
660 char *set = _id_set(imgids);
661 if(IS_NULL_PTR(set)) return FALSE;
662
664 char *query = g_strdup_printf("UPDATE main.images SET flags = (flags|?1) WHERE id IN (%s)", set);
667 const gboolean ok = (sqlite3_step(stmt) == SQLITE_DONE);
669 dt_free(query);
670 dt_free(set);
671 return ok;
672}
673
675{
676 char *set = _id_set(imgids);
677 if(IS_NULL_PTR(set)) return FALSE;
678
680 char *query = g_strdup_printf("UPDATE main.images SET flags = (flags & ~?1) WHERE id IN (%s)", set);
683 const gboolean ok = (sqlite3_step(stmt) == SQLITE_DONE);
685 dt_free(query);
686 dt_free(set);
687 return ok;
688}
689
691{
692 char *set = _id_set(imgids);
693 if(IS_NULL_PTR(set)) return NULL;
694
695 GList *list = NULL;
697 // clang-format off
698 char *query = g_strdup_printf("SELECT DISTINCT folder || '" G_DIR_SEPARATOR_S "' || filename FROM "
699 "main.images i, main.film_rolls f "
700 "ON i.film_id = f.id WHERE i.id IN (%s)", set);
701 // clang-format on
703 while(sqlite3_step(stmt) == SQLITE_ROW)
704 list = g_list_prepend(list, g_strdup((const gchar *)sqlite3_column_text(stmt, 0)));
706 dt_free(query);
707 dt_free(set);
708 return g_list_reverse(list); // list was built in reverse order, so un-reverse it
709}
710
712{
713 GList *ids = NULL;
715 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get_sqlite3_global(), "SELECT id FROM main.images WHERE flags&?1=?1", -1,
716 &stmt, NULL);
718
719 while(sqlite3_step(stmt) == SQLITE_ROW)
722
723 // Row order, NOT reverse-row: the caller filters this list and prepends into its own, and
724 // that second prepend reproduces the single prepend the original did off the cursor.
725 return g_list_reverse(ids);
726}
727
755
756gboolean dt_image_repository_set_flags_masked(const int32_t imgid, const int mask, const int value)
757{
758 if(imgid <= 0 || mask == 0) return FALSE;
759
763 {
764 // The row supplies the bits outside the mask, so nothing this caller read earlier can be
765 // written back over them. Same shape as dt_image_repository_set_flag_among()'s
766 // `flags|?1` and its clearing twin, with both halves in one statement.
768 "UPDATE main.images SET flags = (flags & ~?1) | (?2 & ?1)"
769 " WHERE id = ?3",
771 }
773 {
775 return FALSE;
776 }
777
781 const gboolean ok = (sqlite3_step(_image_set_flags_masked_stmt) == SQLITE_DONE);
785
786 return ok;
787}
788
790{
791 if(IS_NULL_PTR(cb)) return;
792
794 // clang-format off
796 "SELECT i.id, write_timestamp, version,"
797 " folder || '" G_DIR_SEPARATOR_S "' || filename, flags"
798 " FROM main.images i, main.film_rolls f"
799 " ON i.film_id = f.id"
800 " ORDER BY f.id, filename",
801 -1, &stmt, NULL);
802 // clang-format on
803 if(IS_NULL_PTR(stmt)) return;
804
805 while(sqlite3_step(stmt) == SQLITE_ROW)
806 {
807 // No lock is held here on purpose: cb() writes back through this repository.
808 if(!cb(sqlite3_column_int(stmt, 0),
811 (const char *)sqlite3_column_text(stmt, 3),
813 user_data))
814 break;
815 }
817}
818
820{
821 char *set = _id_set(imgids);
822 if(IS_NULL_PTR(set)) return NULL;
823
824 // One subquery per metadata key, each needing the id set again -- nine occurrences in all.
825 // clang-format off
826 char *query = g_strdup_printf("SELECT COUNT(DISTINCT film_id), "
827 "COUNT(DISTINCT film_id), "
828 "2, " // imgid always different
829 "COUNT(DISTINCT group_id), "
830 "COUNT(DISTINCT filename), "
831 "COUNT(DISTINCT version), "
832 "COUNT(DISTINCT film_id || '/' || filename), " //path
833 "COUNT(DISTINCT flags & 2048), " //local copy
834 "COUNT(DISTINCT import_timestamp), "
835 "COUNT(DISTINCT change_timestamp), "
836 "COUNT(DISTINCT export_timestamp), "
837 "COUNT(DISTINCT print_timestamp), "
838 "COUNT(DISTINCT flags), "
839 "COUNT(DISTINCT model), "
840 "COUNT(DISTINCT maker), "
841 "COUNT(DISTINCT lens), "
842 "COUNT(DISTINCT aperture), "
843 "COUNT(DISTINCT exposure), "
844 "COUNT(DISTINCT IFNULL(exposure_bias, '')), "
845 "COUNT(DISTINCT focal_length), "
846 "COUNT(DISTINCT focus_distance), "
847 "COUNT(DISTINCT iso), "
848 "COUNT(DISTINCT datetime_taken), "
849 "COUNT(DISTINCT width), "
850 "COUNT(DISTINCT height), "
851 "COUNT(DISTINCT IFNULL(output_width, '')), " //exported width
852 "COUNT(DISTINCT IFNULL(output_height, '')), " //exported height
853 "(SELECT COUNT(DISTINCT IFNULL(value,'')) FROM images LEFT JOIN meta_data ON meta_data.id = images.id AND key = 2 WHERE images.id in (%s)), " //title
854 "(SELECT COUNT(DISTINCT IFNULL(value,'')) FROM images LEFT JOIN meta_data ON meta_data.id = images.id AND key = 3 WHERE images.id in (%s)), " //description
855 "(SELECT COUNT(DISTINCT IFNULL(value,'')) FROM images LEFT JOIN meta_data ON meta_data.id = images.id AND key = 0 WHERE images.id in (%s)), " //creator
856 "(SELECT COUNT(DISTINCT IFNULL(value,'')) FROM images LEFT JOIN meta_data ON meta_data.id = images.id AND key = 1 WHERE images.id in (%s)), " //publisher
857 "(SELECT COUNT(DISTINCT IFNULL(value,'')) FROM images LEFT JOIN meta_data ON meta_data.id = images.id AND key = 4 WHERE images.id in (%s)), " //rights
858 "(SELECT COUNT(DISTINCT IFNULL(value,'')) FROM images LEFT JOIN meta_data ON meta_data.id = images.id AND key = 5 WHERE images.id in (%s)), " //notes
859 "(SELECT COUNT(DISTINCT IFNULL(value,'')) FROM images LEFT JOIN meta_data ON meta_data.id = images.id AND key = 6 WHERE images.id in (%s)), " //version name
860 "(SELECT COUNT(DISTINCT IFNULL(value,'')) FROM images LEFT JOIN meta_data ON meta_data.id = images.id AND key = 7 WHERE images.id in (%s)), " //image id
861 "COUNT(DISTINCT IFNULL(latitude, '')), "
862 "COUNT(DISTINCT IFNULL(longitude, '')), "
863 "COUNT(DISTINCT IFNULL(altitude, '')) "
864 "FROM main.images "
865 "WHERE id IN (%s)",
866 set, set, set, set, set, set, set, set, set);
867 // clang-format on
868
871 dt_free(query);
872 dt_free(set);
873 if(IS_NULL_PTR(stmt)) return NULL;
874
875 int *counts = NULL;
877 {
878 const int col_count = sqlite3_column_count(stmt);
879 counts = (int *)calloc(DT_IMAGE_FIELD_COUNT, sizeof(int));
880 if(counts)
881 {
882 for(int i = 0; i < DT_IMAGE_FIELD_COUNT && i < col_count; i++)
884 }
885 }
887
888 return counts;
889}
890
892{
893 if(IS_NULL_PTR(bounds)) return FALSE;
894
895 bounds->min_latitude = INFINITY;
896 bounds->max_latitude = -INFINITY;
897 bounds->min_longitude = INFINITY;
898 bounds->max_longitude = -INFINITY;
899 bounds->count = 0;
900
902 // clang-format off
904 "SELECT MIN(latitude), MAX(latitude),"
905 " MIN(longitude), MAX(longitude), COUNT(*)"
906 " FROM main.images AS i "
907 " JOIN memory.collected_images AS l ON l.imgid = i.id "
908 " WHERE latitude NOT NULL AND longitude NOT NULL",
909 -1, &stmt, NULL);
910 // clang-format on
911 if(IS_NULL_PTR(stmt)) return FALSE;
912
913 const gboolean ok = (sqlite3_step(stmt) == SQLITE_ROW);
914 if(ok)
915 {
920 bounds->count = sqlite3_column_int(stmt, 4);
921 }
923
924 return ok;
925}
926
928 const double lat1, const double lat2,
929 int *count)
930{
931 if(IS_NULL_PTR(count)) return NULL;
932 *count = 0;
933
935 // clang-format off
937 "SELECT * FROM"
938 " (SELECT i.id, i.longitude, i.latitude "
939 " FROM main.images i INNER JOIN memory.collected_images c ON i.id = c.imgid"
940 " WHERE longitude >= ?1 AND longitude <= ?2"
941 " AND latitude <= ?3 AND latitude >= ?4 "
942 " AND longitude NOT NULL AND latitude NOT NULL)"
943 " ORDER BY longitude ASC", // critical to make dbscan work
944 -1, &stmt, NULL);
945 // clang-format on
946 if(IS_NULL_PTR(stmt)) return NULL;
947
952
953 // One pass into a growing array, where the caller used to step the whole cursor once to
954 // count the rows and a second time to fill an exactly-sized buffer.
956 while(sqlite3_step(stmt) == SQLITE_ROW)
957 {
959 .longitude = sqlite3_column_double(stmt, 1),
960 .latitude = sqlite3_column_double(stmt, 2) };
961 g_array_append_val(points, p);
962 }
964
965 *count = (int)points->len;
966 if(points->len == 0)
967 {
968 g_array_free(points, TRUE);
969 return NULL;
970 }
971
972 return (dt_image_geo_point_t *)g_array_free(points, FALSE); // hand over the buffer
973}
974
1003
1026
1027gboolean dt_image_repository_set_write_timestamp(const int32_t imgid, const int64_t timestamp)
1028{
1029 if(imgid <= 0) return FALSE;
1030
1033 "UPDATE main.images SET write_timestamp = ?2 WHERE id = ?1",
1034 -1, &stmt, NULL);
1035 if(IS_NULL_PTR(stmt)) return FALSE;
1036
1039 const gboolean ok = (sqlite3_step(stmt) == SQLITE_DONE);
1041
1042 return ok;
1043}
1044
1045gboolean dt_image_repository_delete(const int32_t imgid)
1046{
1047 gboolean ok = TRUE;
1049
1050 // due to foreign keys added in db version 33,
1051 // all entries from tables having references to the images are deleted as well
1052 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get_sqlite3_global(), "DELETE FROM main.images WHERE id = ?1", -1, &stmt,
1053 NULL);
1055 ok &= (sqlite3_step(stmt) == SQLITE_DONE);
1056 sqlite3_finalize(stmt); // the original overwrote `stmt` here and leaked this statement
1057
1059 "DELETE FROM main.meta_data WHERE id = ?1", -1, &stmt, NULL);
1061 ok &= (sqlite3_step(stmt) == SQLITE_DONE);
1063
1064 return ok;
1065}
1066
1067int32_t dt_image_repository_duplicate(const int32_t imgid, const int32_t newversion)
1068{
1070 int32_t newid = -1;
1071
1072 // clang-format off
1074 "SELECT a.id"
1075 " FROM main.images AS a JOIN main.images AS b"
1076 " WHERE a.film_id = b.film_id AND a.filename = b.filename"
1077 " AND b.id = ?1 AND a.version = ?2"
1078 " ORDER BY a.id DESC",
1079 -1, &stmt, NULL);
1080 // clang-format on
1084 {
1086 }
1088
1089 // requested version is already present in DB, so we just return it
1090 if(newid != -1) return newid;
1091
1092 // clang-format off
1095 "INSERT INTO main.images"
1096 " (id, group_id, film_id, width, height, filename, maker, model, lens, exposure,"
1097 " aperture, iso, focal_length, focus_distance, datetime_taken, flags,"
1098 " output_width, output_height, crop, raw_parameters, raw_denoise_threshold,"
1099 " raw_auto_bright_threshold, raw_black, raw_maximum,"
1100 " license, sha1sum, orientation, histogram, lightmap,"
1101 " longitude, latitude, altitude, color_matrix, colorspace, version, max_version, history_end,"
1102 " aspect_ratio, exposure_bias, import_timestamp)"
1103 " SELECT NULL, group_id, film_id, width, height, filename, maker, model, lens,"
1104 " exposure, aperture, iso, focal_length, focus_distance, datetime_taken,"
1105 " flags, output_width, output_height, crop, raw_parameters, raw_denoise_threshold,"
1106 " raw_auto_bright_threshold, raw_black, raw_maximum,"
1107 " license, sha1sum, orientation, histogram, lightmap,"
1108 " longitude, latitude, altitude, color_matrix, colorspace, NULL, NULL, 0,"
1109 " aspect_ratio, exposure_bias, import_timestamp"
1110 " FROM main.images WHERE id = ?1",
1111 -1, &stmt, NULL);
1112 // clang-format on
1116 // clang-format off
1118 "SELECT a.id, a.film_id, a.filename, b.max_version"
1119 " FROM main.images AS a JOIN main.images AS b"
1120 " WHERE a.film_id = b.film_id AND a.filename = b.filename AND b.id = ?1"
1121 " ORDER BY a.id DESC",
1122 -1, &stmt, NULL);
1123 // clang-format on
1125
1126 int32_t film_id = UNKNOWN_IMAGE;
1127 int32_t max_version = -1;
1128 gchar *filename = NULL;
1130 {
1132 film_id = sqlite3_column_int(stmt, 1);
1133 filename = g_strdup((gchar *)sqlite3_column_text(stmt, 2));
1135 }
1137
1138 if(newid != -1)
1139 {
1140 // clang-format off
1142 "INSERT INTO main.color_labels (imgid, color)"
1143 " SELECT ?1, color FROM main.color_labels WHERE imgid = ?2",
1144 -1, &stmt, NULL);
1145 // clang-format on
1150
1151 // clang-format off
1153 "INSERT INTO main.meta_data (id, key, value)"
1154 " SELECT ?1, key, value FROM main.meta_data WHERE id = ?2",
1155 -1, &stmt, NULL);
1156 // clang-format on
1161
1162 // clang-format off
1164 "INSERT INTO main.tagged_images (imgid, tagid, position)"
1165 " SELECT ?1, tagid, "
1166 " (SELECT (IFNULL(MAX(position),0) & 0xFFFFFFFF00000000)"
1167 " FROM main.tagged_images)"
1168 " + (ROW_NUMBER() OVER (ORDER BY imgid) << 32)"
1169 " FROM main.tagged_images AS ti"
1170 " WHERE imgid = ?2",
1171 -1, &stmt, NULL);
1172 // clang-format on
1177
1178 // clang-format off
1180 "INSERT INTO main.module_order (imgid, iop_list, version)"
1181 " SELECT ?1, iop_list, version FROM main.module_order WHERE imgid = ?2",
1182 -1, &stmt, NULL);
1183 // clang-format on
1188
1189 // set version of new entry and max_version of all involved duplicates (with same film_id and filename)
1190 // this needs to happen before we do anything with the image cache, as version isn't updated through the cache
1191 const int32_t version = (newversion != -1) ? newversion : max_version + 1;
1193
1194 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get_sqlite3_global(), "UPDATE main.images SET version=?1 WHERE id = ?2",
1195 -1, &stmt, NULL);
1196 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, version);
1200
1201 // clang-format off
1203 "UPDATE main.images SET max_version=?1 WHERE film_id = ?2 AND filename = ?3", -1,
1204 &stmt, NULL);
1205 // clang-format on
1207 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, film_id);
1211
1212 dt_free(filename);
1213 }
1214 return newid;
1215}
1216
1217void dt_image_version_free(gpointer data)
1218{
1220 if(IS_NULL_PTR(v)) return;
1221 dt_free(v->version_name);
1222 dt_free(v);
1223}
1224
1225GList *dt_image_repository_get_versions(const int32_t film_id, const char *filename,
1226 const int name_keyid)
1227{
1228 if(IS_NULL_PTR(filename)) return NULL;
1229
1231 // clang-format off
1233 "SELECT i.version, i.id, m.value"
1234 " FROM images AS i"
1235 " LEFT JOIN meta_data AS m ON m.id = i.id AND m.key = ?3"
1236 " WHERE film_id = ?1 AND filename = ?2"
1237 " ORDER BY i.version",
1238 -1, &stmt, NULL);
1239 // clang-format on
1240 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id);
1243
1244 GList *versions = NULL;
1245 while(sqlite3_step(stmt) == SQLITE_ROW)
1246 {
1248 if(v)
1249 {
1250 v->version = sqlite3_column_int(stmt, 0);
1251 v->imgid = sqlite3_column_int(stmt, 1);
1252 const char *name = (const char *)sqlite3_column_text(stmt, 2);
1253 v->version_name = name ? g_strdup(name) : NULL;
1255 }
1256 }
1258
1259 return g_list_reverse(versions); // version order, as the query returns it
1260}
1261
1263{
1264 GList *ids = NULL;
1266 // statement for getting ids of the image to be moved and its duplicates
1267 // clang-format off
1270 "SELECT id"
1271 " FROM main.images"
1272 " WHERE filename IN (SELECT filename FROM main.images WHERE id = ?1)"
1273 " AND film_id IN (SELECT film_id FROM main.images WHERE id = ?1)",
1274 -1, &stmt, NULL);
1275 // clang-format on
1277
1278 while(sqlite3_step(stmt) == SQLITE_ROW)
1281
1282 // Row order: the original prepended off the cursor and then reversed, and its caller walks
1283 // the list without prepending again.
1284 return g_list_reverse(ids);
1285}
1286
1287int32_t dt_image_repository_copy_to_film(const int32_t imgid, const int32_t filmid,
1288 const char *new_filename, const char *old_filename)
1289{
1290 int32_t newid = -1;
1291 gchar *filename = NULL;
1293
1294// update database
1295// clang-format off
1298 "INSERT INTO main.images"
1299 " (id, group_id, film_id, width, height, filename, maker, model, lens, exposure,"
1300 " aperture, iso, focal_length, focus_distance, datetime_taken, flags,"
1301 " output_width, output_height, crop, raw_parameters, raw_denoise_threshold,"
1302 " raw_auto_bright_threshold, raw_black, raw_maximum,"
1303 " license, sha1sum, orientation, histogram, lightmap,"
1304 " longitude, latitude, altitude, color_matrix, colorspace, version, max_version,"
1305 " aspect_ratio, exposure_bias)"
1306 " SELECT NULL, group_id, ?1 as film_id, width, height, ?2 as filename, maker, model, lens,"
1307 " exposure, aperture, iso, focal_length, focus_distance, datetime_taken,"
1308 " flags, width, height, crop, raw_parameters, raw_denoise_threshold,"
1309 " raw_auto_bright_threshold, raw_black, raw_maximum,"
1310 " license, sha1sum, orientation, histogram, lightmap,"
1311 " longitude, latitude, altitude, color_matrix, colorspace, -1, -1,"
1312 " aspect_ratio, exposure_bias"
1313 " FROM main.images"
1314 " WHERE id = ?3",
1315 -1, &stmt, NULL);
1316// clang-format on
1322// clang-format off
1325 "SELECT a.id, a.filename"
1326 " FROM main.images AS a"
1327 " JOIN main.images AS b"
1328 " WHERE a.film_id = ?1 AND a.filename = ?2 AND b.filename = ?3 AND b.id = ?4"
1329 " ORDER BY a.id DESC",
1330 -1, &stmt, NULL);
1331// clang-format on
1336
1338{
1340 filename = g_strdup((gchar *)sqlite3_column_text(stmt, 1));
1341}
1343
1344if(newid != -1)
1345{
1346 // clang-format off
1348 "INSERT INTO main.color_labels (imgid, color)"
1349 " SELECT ?1, color"
1350 " FROM main.color_labels"
1351 " WHERE imgid = ?2",
1352 -1, &stmt, NULL);
1353 // clang-format on
1358 // clang-format off
1360 "INSERT INTO main.meta_data (id, key, value)"
1361 " SELECT ?1, key, value"
1362 " FROM main.meta_data"
1363 " WHERE id = ?2",
1364 -1, &stmt, NULL);
1365 // clang-format on
1370
1371 // clang-format off
1373 "INSERT INTO main.tagged_images (imgid, tagid, position)"
1374 " SELECT ?1, tagid, "
1375 " (SELECT (IFNULL(MAX(position),0) & 0xFFFFFFFF00000000)"
1376 " FROM main.tagged_images)"
1377 " + (ROW_NUMBER() OVER (ORDER BY imgid) << 32)"
1378 " FROM main.tagged_images AS ti"
1379 " WHERE imgid = ?2",
1380 -1, &stmt, NULL);
1381 // clang-format on
1386
1387 // get max_version of image duplicates in destination filmroll
1388 int32_t max_version = -1;
1389 // clang-format off
1392 "SELECT MAX(a.max_version)"
1393 " FROM main.images AS a"
1394 " JOIN main.images AS b"
1395 " WHERE a.film_id = b.film_id AND a.filename = b.filename AND b.id = ?1",
1396 -1, &stmt, NULL);
1397 // clang-format on
1399
1402
1403 // set version of new entry and max_version of all involved duplicates (with same film_id and
1404 // filename)
1405 max_version = (max_version >= 0) ? max_version + 1 : 0;
1406 int32_t version = max_version;
1407
1410 "UPDATE main.images SET version=?1 WHERE id = ?2", -1, &stmt, NULL);
1411 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, version);
1415
1418 "UPDATE main.images SET max_version=?1 WHERE film_id = ?2 AND filename = ?3",
1419 -1, &stmt, NULL);
1425
1426 // image group handling follows
1427 // get group_id of potential image duplicates in destination filmroll
1428 int32_t new_group_id = -1;
1429 // clang-format off
1432 "SELECT DISTINCT a.group_id"
1433 " FROM main.images AS a"
1434 " JOIN main.images AS b"
1435 " WHERE a.film_id = b.film_id AND a.filename = b.filename"
1436 " AND b.id = ?1 AND a.id != ?1",
1437 -1, &stmt, NULL);
1438 // clang-format on
1440
1442
1443 // then check if there are further duplicates belonging to different group(s)
1446
1447 // rationale:
1448 // if no group exists or if the image duplicates belong to multiple groups, then the
1449 // new image builds a group of its own, else it is added to the (one) existing group
1450 if(new_group_id == -1) new_group_id = newid;
1451
1452 // make copied image belong to a group
1454 "UPDATE main.images SET group_id=?1 WHERE id = ?2", -1, &stmt, NULL);
1455
1460
1461 }
1462
1463 dt_free(filename);
1464 return newid;
1465}
1466
1467gboolean dt_image_repository_insert_import(const int32_t film_id, const char *filename,
1468 const int flags, const int64_t import_timestamp)
1469{
1471 //insert a v0 record (which may be updated later if no v0 xmp exists)
1472 // clang-format off
1475 "INSERT INTO main.images (id, film_id, filename, license, sha1sum, flags, version, "
1476 " max_version, history_end, position, import_timestamp)"
1477 " SELECT NULL, ?1, ?2, '', '', ?3, 0, 0, 0, (IFNULL(MAX(position),0) & 0xFFFFFFFF00000000) + (1 << 32), ?4 "
1478 " FROM images",
1479 -1, &stmt, NULL);
1480 // clang-format on
1481
1482 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id);
1485 DT_DEBUG_SQLITE3_BIND_INT64(stmt, 4, import_timestamp);
1486
1487 const int rc = sqlite3_step(stmt);
1488 if(rc != SQLITE_DONE) fprintf(stderr, "sqlite3 error %d\n", rc);
1490 return (rc == SQLITE_DONE);
1491}
1492
1494 const char *filename_pattern,
1495 const gboolean leader_only,
1496 const int32_t exclude_imgid)
1497{
1498 int32_t group_id = -1;
1500
1501 if(leader_only)
1502 {
1503 // clang-format off
1506 "SELECT group_id"
1507 " FROM main.images"
1508 " WHERE film_id = ?1 AND filename LIKE ?2 AND id = group_id", -1, &stmt,
1509 NULL);
1510 // clang-format on
1511 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id);
1513 }
1514 else
1515 {
1516 // clang-format off
1519 "SELECT group_id"
1520 " FROM main.images"
1521 " WHERE film_id = ?1 AND filename LIKE ?2 AND id != ?3", -1, &stmt, NULL);
1522 // clang-format on
1523 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id);
1526 }
1527
1528 if(sqlite3_step(stmt) == SQLITE_ROW) group_id = sqlite3_column_int(stmt, 0);
1530 return group_id;
1531}
1532
1533gboolean dt_image_repository_set_group(const int32_t imgid, const int32_t group_id)
1534{
1538 "UPDATE main.images SET group_id = ?1 WHERE id = ?2",
1539 -1, &stmt, NULL);
1540 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, group_id);
1542 const gboolean ok = (sqlite3_step(stmt) == SQLITE_DONE);
1544 return ok;
1545}
1546
1548{
1549 if(IS_NULL_PTR(row)) return FALSE;
1550 memset(row, 0, sizeof(*row));
1551
1552 gboolean found = FALSE;
1554 // clang-format off
1556 "SELECT filename, flags, raw_parameters, "
1557 " longitude, latitude, altitude, history_end, datetime_taken"
1558 " FROM main.images"
1559 " WHERE id = ?1",
1560 -1, &stmt, NULL);
1561 // clang-format on
1564 {
1565 // copied, not borrowed: the caller used to read it before finalising the statement, and
1566 // owning it removes the constraint rather than moving it across a module boundary
1567 const char *f = (const char *)sqlite3_column_text(stmt, 0);
1568 row->filename = f ? g_strdup(f) : NULL;
1569 row->flags = sqlite3_column_int(stmt, 1);
1570 row->raw_parameters = sqlite3_column_int(stmt, 2);
1572 {
1573 row->longitude = sqlite3_column_double(stmt, 3);
1574 row->has_longitude = TRUE;
1575 }
1577 {
1578 row->latitude = sqlite3_column_double(stmt, 4);
1579 row->has_latitude = TRUE;
1580 }
1582 {
1583 row->altitude = sqlite3_column_double(stmt, 5);
1584 row->has_altitude = TRUE;
1585 }
1586 row->history_end = sqlite3_column_int(stmt, 6);
1587 row->datetime_taken = sqlite3_column_int64(stmt, 7);
1588 found = TRUE;
1589 }
1591 return found;
1592}
1593
1595{
1596 if(IS_NULL_PTR(row)) return;
1597 dt_free(row->filename);
1598 row->filename = NULL;
1599}
1600
1602{
1603 if(IS_NULL_PTR(ts)) return FALSE;
1604 memset(ts, 0, sizeof(*ts));
1605
1606 gboolean found = FALSE;
1608 // clang-format off
1611 "SELECT import_timestamp, change_timestamp, export_timestamp, print_timestamp"
1612 " FROM main.images"
1613 " WHERE id = ?1",
1614 -1, &stmt, NULL);
1616 // clang-format on
1617
1619 {
1621 {
1623 ts->has_import = TRUE;
1624 }
1626 {
1628 ts->has_change = TRUE;
1629 }
1631 {
1633 ts->has_export = TRUE;
1634 }
1636 {
1638 ts->has_print = TRUE;
1639 }
1640 found = TRUE;
1641 }
1643 return found;
1644}
1645
1685
1686// clang-format off
1687// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1688// vim: shiftwidth=2 expandtab tabstop=2 cindent
1689// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1690// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_colorlabel_repository_remove(const int32_t imgid, const int color)
Clear colour color on imgid. Clearing one that is not set does nothing.
void dt_colorlabel_repository_set(const int32_t imgid, const int color)
Set colour color on imgid. Setting one that is already set does nothing.
main.color_labels: which of the five colour labels are set on an image.
const float f
const float v
static const int row
void dt_image_init(dt_image_t *img)
const char * dt_image_film_roll_name(const char *path)
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)
sqlite3 * dt_database_get_sqlite3_global(void)
Definition database.c:3790
gboolean dt_datetime_gtimespan_to_local(char *local, const size_t local_size, const GTimeSpan gts, const gboolean msec, const gboolean tz)
Definition datetime.c:178
GtkWidget * folder
destination folder chooser
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:127
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Initialise a mutex. With mutexattr NULL – which is how 54 of the 56 call sites in this tree spell it ...
Definition dtpthread.h:104
static int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:132
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:117
GdkRGBA color[]
Definition geotagging.c:541
#define UNKNOWN_IMAGE
Definition image.h:78
const char flag
Definition image.h:311
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.
static void _image_write_history_hash(const dt_image_t *img)
static gsize _image_stmt_mutex_inited
static sqlite3_stmt * _image_write_timestamp_update_stmt
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.
int dt_image_repository_count_in_id_range(const int32_t min_imgid, const int32_t max_imgid)
How many images have an id in [min_imgid, max_imgid]. -1 when unreadable.
gboolean dt_image_repository_set_flags_masked(const int32_t imgid, const int mask, const int value)
Write only mask's bits of imgid's flags, taking them from value.
GList * dt_image_repository_get_ids_with_flag_among(GList *imgids, const int flag)
Of the images in imgids, those whose flags carry flag, in row order.
void dt_image_repository_cleanup(void)
Finalise the prepared statements. Called at shutdown, after the last cache release and before the dat...
static char * _id_set(GList *imgids)
void dt_image_repository_xmp_row_cleanup(dt_image_xmp_row_t *row)
Release what dt_image_repository_get_xmp_row() allocated.
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.
static sqlite3_stmt * _image_write_timestamp_select_stmt
gboolean dt_image_repository_set_write_timestamp(const int32_t imgid, const int64_t timestamp)
Set write_timestamp of imgid to timestamp (seconds since the epoch). Bound as a 64-bit integer; the c...
void dt_image_group_member_free(gpointer data)
Free one dt_image_group_member_t. Suits g_list_free_full().
gboolean dt_image_repository_get_collected_geo_bounds(dt_image_geo_bounds_t *bounds)
Bounding box of every collected image carrying coordinates.
void dt_image_repository_foreach_collected(dt_image_repository_collected_cb cb, void *user_data)
Every image of the current collection, in collection order, already mapped.
gboolean dt_image_repository_load(const int32_t imgid, dt_image_t *img)
Fill img from the main.images row for imgid.
static sqlite3_stmt * _image_set_flags_stmt
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),...
GList * dt_image_repository_get_group_member_rows(const int32_t group_id)
Every member of group_id, in row order, named. Free with g_list_free_full(list, dt_image_group_member...
gboolean dt_image_repository_get_timestamps(const int32_t imgid, dt_image_timestamps_t *ts)
Read imgid's four timestamps. FALSE when there is no such image.
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_versions(const int32_t film_id, const char *filename, const int name_keyid)
Every version of the file (film_id, filename), in version order, each with the name stored under meta...
gboolean dt_image_repository_set_flags(const int32_t imgid, const int flags)
Replace imgid's whole flags word.
static sqlite3_stmt * _image_set_flags_masked_stmt
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...
static sqlite3_stmt * _image_load_stmt
GList * dt_image_repository_get_ids_with_flag(const int flag)
Every image id whose flags carry flag, in row order.
void dt_image_repository_foreach_in_id_range(const int32_t min_imgid, const int32_t max_imgid, dt_image_repository_id_filename_cb cb, void *user_data)
Walk the images whose id falls in [min_imgid, max_imgid], in row order.
gboolean dt_image_repository_get_xmp_row(const int32_t imgid, dt_image_xmp_row_t *row)
Read imgid's sidecar fields. FALSE when there is no such image.
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(),...
static dt_pthread_mutex_t _image_stmt_mutex
void dt_image_version_free(gpointer data)
Release one dt_image_version_t. Use with g_list_free_full().
void dt_image_repository_reassign_group(const int32_t from_group_id, const int32_t to_group_id, const int32_t exclude_imgid)
Move every member of from_group_id into to_group_id, except exclude_imgid.
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).
static void dt_image_from_stmt(dt_image_t *img, sqlite3_stmt *stmt)
static void _image_stmt_mutex_ensure(void)
void dt_image_repository_store(const dt_image_t *img)
Write img back to main.images, plus its colour labels and history hash.
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.
static sqlite3_stmt * _image_get_stmt(void)
GList * dt_image_repository_get_ratings(const int32_t imgid)
Star ratings, as main.images.flags encodes them: the low three bits, minus one.
dt_image_geo_point_t * dt_image_repository_get_collected_geo_points(const double lon1, const double lon2, const double lat1, const double lat2, int *count)
Collected images inside the box, ordered by longitude ascending.
void dt_image_repository_foreach_with_path(dt_image_repository_path_row_cb cb, void *user_data)
Walk every image in the library, film roll by film roll, filename within each.
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.
gboolean dt_image_repository_set_flag_among(GList *imgids, const int flag)
Set flag on every image in imgids.
GList * dt_image_repository_get_group_members(const int32_t group_id, const int32_t exclude_imgid)
Image ids in group group_id.
GList * dt_image_repository_get_full_paths(GList *imgids)
"<folder>/<filename>" for every image in imgids, distinct, in row order. Caller owns the strings.
int * dt_image_repository_count_distinct_fields(GList *imgids)
How many DISTINCT values each dt_image_field_t takes over imgids.
static sqlite3_stmt * _image_write_history_hash_stmt
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.
static GList * _collect_ids(sqlite3_stmt *stmt, const int32_t exclude_imgid)
Reading and writing one dt_image_t to and from the library database. The SQL half of what used to be ...
@ DT_IMAGE_FIELD_COUNT
gboolean(* dt_image_repository_path_row_cb)(const int32_t imgid, const int64_t write_timestamp, const int version, const char *image_path, const int flags, void *user_data)
One row of dt_image_repository_foreach_with_path(). image_path is borrowed: it lives until the callba...
void(* dt_image_repository_id_filename_cb)(const int32_t imgid, const char *filename, void *user_data)
One row of dt_image_repository_foreach_in_id_range(). filename is borrowed.
void(* dt_image_repository_collected_cb)(void *user_data, const dt_image_t *info)
One image of the current collection. info is borrowed: it lives until the callback returns.
const char * maker
const char * model
#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 dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
const char * name
Definition pdf.h:90
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
Checked wrappers around the sqlite3_* calls: trace the statement, assert the return code,...
#define DT_DEBUG_SQLITE3_BIND_BLOB(a, b, c, d, e)
Definition sql_debug.h:149
#define DT_DEBUG_SQLITE3_PREPARE_V2(a, b, c, d, e)
Definition sql_debug.h:137
#define DT_DEBUG_SQLITE3_BIND_TEXT(a, b, c, d, e)
Definition sql_debug.h:148
#define DT_DEBUG_SQLITE3_BIND_INT(a, b, c)
Definition sql_debug.h:145
#define DT_DEBUG_SQLITE3_BIND_INT64(a, b, c)
Definition sql_debug.h:146
#define DT_DEBUG_SQLITE3_BIND_DOUBLE(a, b, c)
Definition sql_debug.h:147
const float const int flip
The extent of the collection's geotagged images.
One geotagged image, in degrees as stored.
double latitude
Definition image.h:332
double elevation
Definition image.h:332
double longitude
Definition image.h:332
One member of a group, with what a tooltip needs to name it.
float exif_exposure
Definition image.h:367
int32_t height
Definition image.h:397
GTimeSpan export_timestamp
Definition image.h:415
uint64_t history_hash
Definition image.h:404
float exif_focus_distance
Definition image.h:372
GTimeSpan import_timestamp
Definition image.h:415
int32_t group_id
Definition image.h:401
float exif_exposure_bias
Definition image.h:368
uint16_t raw_black_level
Definition image.h:437
float exif_iso
Definition image.h:370
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
uint64_t mipmap_hash
Definition image.h:411
dt_image_geoloc_t geoloc
Definition image.h:434
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 history_items
Definition image.h:403
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
GTimeSpan exif_datetime_taken
Definition image.h:377
int color_labels
Definition image.h:472
float d65_color_matrix[9]
Definition image.h:421
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
char datetime[200]
Definition image.h:392
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
float exif_crop
Definition image.h:373
char fullpath[DT_PATH_MAX]
Definition image.h:387
The four timestamps, each with whether it is set at all.
One version of a file: which duplicate it is, its id, and the name the user gave it.
The main.images fields an XMP sidecar carries.
#define MAX(a, b)
Definition thinplate.c:29