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 <string.h>
20
22
24#include "database/database.h"
25#include "common/datetime.h"
26#include "database/sql_debug.h"
27#include "common/image.h"
28#include "common/logging.h"
29#include "system/dtpthread.h"
30#include "system/macros.h"
31
32#include <inttypes.h>
33#include <sqlite3.h>
34
35/* One statement cache for the whole process, guarded by one mutex, exactly as the image cache
36 * held it before the split: these statements are reused on every image load and every write,
37 * and re-preparing them per call was measurable. The mutex is what makes reuse safe, and it
38 * is why the calls below serialise against each other. */
44static dt_pthread_mutex_t _image_stmt_mutex;
46
55
57{
59 {
60 // clang-format off
63 "SELECT i.id, i.group_id, "
64 " (SELECT COUNT(id) FROM main.images WHERE group_id = i.group_id), "
65 " (SELECT COUNT(imgid) FROM main.history WHERE imgid = i.id), "
66 " COALESCE((SELECT current_hash FROM main.history_hash WHERE imgid = i.id), -1), "
67 " COALESCE((SELECT mipmap_hash FROM main.history_hash WHERE imgid = i.id), -1), "
68 " i.film_id, i.version, i.width, i.height, i.orientation, i.flags, "
69 " i.import_timestamp, i.change_timestamp, i.export_timestamp, i.print_timestamp, "
70 " i.exposure, i.exposure_bias, i.aperture, i.iso, i.focal_length, i.focus_distance, "
71 " i.datetime_taken, i.longitude, i.latitude, i.altitude, "
72 " i.filename, f.folder || '" G_DIR_SEPARATOR_S "' || i.filename, "
73 " i.maker, i.model, i.lens, f.folder, "
74 " COALESCE((SELECT SUM(1 << color) FROM main.color_labels WHERE imgid=i.id), 0), "
75 " i.crop, i.raw_parameters, i.color_matrix, i.colorspace, "
76 " i.raw_black, i.raw_maximum, i.aspect_ratio, i.output_width, i.output_height"
77 " FROM main.images AS i"
78 " LEFT JOIN main.film_rolls AS f ON f.id = i.film_id"
79 " WHERE i.id = ?1",
81 // clang-format on
82 }
83
86 return _image_load_stmt;
87}
88
90{
117
118 const char *filename = (const char *)sqlite3_column_text(stmt, 26);
119 if(filename) g_strlcpy(img->filename, filename, sizeof(img->filename));
120 const char *fullpath = (const char *)sqlite3_column_text(stmt, 27);
121 if(fullpath) g_strlcpy(img->fullpath, fullpath, sizeof(img->fullpath));
122 const char *maker = (const char *)sqlite3_column_text(stmt, 28);
123 if(maker) g_strlcpy(img->exif_maker, maker, sizeof(img->exif_maker));
124 const char *model = (const char *)sqlite3_column_text(stmt, 29);
125 if(model) g_strlcpy(img->exif_model, model, sizeof(img->exif_model));
126 const char *lens = (const char *)sqlite3_column_text(stmt, 30);
127 if(lens) g_strlcpy(img->exif_lens, lens, sizeof(img->exif_lens));
128 const char *folder = (const char *)sqlite3_column_text(stmt, 31);
129 if(folder) g_strlcpy(img->folder, folder, sizeof(img->folder));
130
132
135 {
136 uint32_t tmp = sqlite3_column_int(stmt, 34);
138 }
139 const void *color_matrix = sqlite3_column_blob(stmt, 35);
144
145 if(img->fullpath[0])
147 sizeof(img->local_copy_path), img->local_copy_legacy_path,
148 sizeof(img->local_copy_legacy_path));
149
150 img->exif_inited = (img->exif_focus_distance >= 0 && img->orientation >= 0);
151
152 if(img->folder[0])
153 g_strlcpy(img->filmroll, dt_image_film_roll_name(img->folder), sizeof(img->filmroll));
154
156
157 // img->dsc are written by imageio drivers : never (re)set them from DB,
158 // they are not saved anyway. Until the codec decodes the file, seed a provisional
159 // descriptor from the (extension-derived) pipeline class so the image can be reasoned
160 // about and the first pipeline stage has a usable contract even before decoding.
162
163 /* Everything past the columns -- rating, monochrome and HDR predicates, the
164 * extension cross-check, makermodel -- is DERIVED, not stored, and needs symbols from
165 * imageio/ and views/. A row mapper that reached two layers up for them is what kept this
166 * code in common/. Callers run dt_image_derive_fields() next; see image_repository.h. */
167
168}
169
171{
172 if(IS_NULL_PTR(img) || img->id <= 0) return;
173
177 {
178 // clang-format off
181 "INSERT INTO main.history_hash (imgid, current_hash, basic_hash, auto_hash, mipmap_hash)"
182 " VALUES (?1, ?2, NULL, NULL, ?3)"
183 " ON CONFLICT (imgid)"
184 " DO UPDATE SET current_hash = ?2, basic_hash = NULL, auto_hash = NULL, mipmap_hash = ?3",
186 // clang-format on
187 }
196}
197
198
199gboolean dt_image_repository_load(const int32_t imgid, dt_image_t *img)
200{
201 if(IS_NULL_PTR(img)) return FALSE;
202
203 gboolean found = FALSE;
206
212 {
214 found = TRUE;
215 }
216 else
217 {
218 img->id = -1;
219 fprintf(stderr, "[image_repository_load] failed to open image %" PRId32 " from database: %s\n", imgid,
221 }
222
224 return found;
225}
226
228{
229 if(IS_NULL_PTR(cb)) return;
230
232 // clang-format off
235 // Same columns, same order, as the single-image load above -- dt_image_from_stmt() maps
236 // both. Only the source differs: the collection instead of one id.
237 "SELECT i.id, i.group_id, "
238 " (SELECT COUNT(id) FROM main.images WHERE group_id = i.group_id), "
239 " (SELECT COUNT(imgid) FROM main.history WHERE imgid = i.id), "
240 " COALESCE((SELECT current_hash FROM main.history_hash WHERE imgid = i.id), -1), "
241 " COALESCE((SELECT mipmap_hash FROM main.history_hash WHERE imgid = i.id), -1), "
242 " i.film_id, i.version, i.width, i.height, i.orientation, i.flags, "
243 " i.import_timestamp, i.change_timestamp, i.export_timestamp, i.print_timestamp, "
244 " i.exposure, i.exposure_bias, i.aperture, i.iso, i.focal_length, i.focus_distance, "
245 " i.datetime_taken, i.longitude, i.latitude, i.altitude, "
246 " i.filename, f.folder || '" G_DIR_SEPARATOR_S "' || i.filename, "
247 " i.maker, i.model, i.lens, f.folder, "
248 " COALESCE((SELECT SUM(1 << color) FROM main.color_labels WHERE imgid=i.id), 0), "
249 " i.crop, i.raw_parameters, i.color_matrix, i.colorspace, "
250 " i.raw_black, i.raw_maximum, i.aspect_ratio, i.output_width, i.output_height"
251 " FROM main.images AS i"
252 " JOIN memory.collected_images AS c ON i.id = c.imgid"
253 " LEFT JOIN main.film_rolls AS f ON f.id = i.film_id"
254 " ORDER BY c.rowid ASC",
255 -1, &stmt, NULL);
256 // clang-format on
257 if(IS_NULL_PTR(stmt)) return;
258
259 while(sqlite3_step(stmt) == SQLITE_ROW)
260 {
261 dt_image_t info;
262 dt_image_init(&info);
263 dt_image_from_stmt(&info, stmt);
264 // No lock is held here on purpose: see the header. cb() reaches the selection and the
265 // image cache, and either can come back through this function.
266 cb(user_data, &info);
267 }
269}
270
271
273{
274 if(IS_NULL_PTR(img) || img->id <= 0) return;
275
276 union {
278 uint32_t u;
279 } flip;
280
282 // clang-format off
284 "UPDATE main.images"
285 " SET width = ?1, height = ?2, filename = ?3, maker = ?4, model = ?5,"
286 " lens = ?6, exposure = ?7, aperture = ?8, iso = ?9, focal_length = ?10,"
287 " focus_distance = ?11, film_id = ?12, datetime_taken = ?13, flags = ?14,"
288 " crop = ?15, orientation = ?16, raw_parameters = ?17, group_id = ?18,"
289 " longitude = ?19, latitude = ?20, altitude = ?21, color_matrix = ?22,"
290 " colorspace = ?23, raw_black = ?24, raw_maximum = ?25,"
291 " aspect_ratio = ROUND(?26,1), exposure_bias = ?27,"
292 " import_timestamp = ?28, change_timestamp = ?29, export_timestamp = ?30,"
293 " print_timestamp = ?31, output_width = ?32, output_height = ?33"
294 " WHERE id = ?34",
295 -1, &stmt, NULL);
296 // clang-format on
309 if(img->exif_datetime_taken)
314 flip.s = img->legacy_flip;
324 DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 26, 0.); // img->aspect_ratio deprecated
326 if(img->import_timestamp)
328 if(img->change_timestamp)
330 if(img->export_timestamp)
332 if(img->print_timestamp)
334 DT_DEBUG_SQLITE3_BIND_INT(stmt, 32, 0); // img->final_width deprecated
335 DT_DEBUG_SQLITE3_BIND_INT(stmt, 33, 0); // img->final_height deprecated
337 const int rc = sqlite3_step(stmt);
338 if(rc != SQLITE_DONE) fprintf(stderr, "[image_cache_write_release] sqlite3 error %d\n", rc);
340
341 /* Straight to the table. This used to call dt_colorlabels_set_labels() in common/, i.e.
342 * the persistence layer reaching up into the domain to have it issue the queries the
343 * persistence layer is for -- the only edge in that direction here. */
344 for(int color = 0; color < 5; color++)
345 {
346 if(img->color_labels & (1 << color))
348 else
350 }
352}
353
354
355/* ---------------------------------------------------------------------------------------
356 * Grouping
357 * ------------------------------------------------------------------------------------- */
358
359/* Collect the `id` column of a stepped statement into a list, dropping @p exclude_imgid. */
361{
362 GList *ids = NULL;
363 while(sqlite3_step(stmt) == SQLITE_ROW)
364 {
365 const int32_t id = sqlite3_column_int(stmt, 0);
366 if(id != exclude_imgid) ids = g_list_prepend(ids, GINT_TO_POINTER(id));
367 }
369 return g_list_reverse(ids);
370}
371
372GList *dt_image_repository_get_group_members(const int32_t group_id, const int32_t exclude_imgid)
373{
376 "SELECT id FROM main.images WHERE group_id = ?1", -1, &stmt, NULL);
377 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, group_id);
379}
380
381void dt_image_group_member_free(gpointer data)
382{
384 if(IS_NULL_PTR(member)) return;
385 dt_free(member->filename);
387}
388
390{
391 GList *members = NULL;
393 // clang-format off
395 "SELECT id, version, filename"
396 " FROM main.images"
397 " WHERE group_id = ?1", -1, &stmt,
398 NULL);
399 // clang-format on
400 if(IS_NULL_PTR(stmt)) return NULL;
401
402 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, group_id);
403 while(sqlite3_step(stmt) == SQLITE_ROW)
404 {
406 if(IS_NULL_PTR(member)) break;
408 member->version = sqlite3_column_int(stmt, 1);
409 const char *filename = (const char *)sqlite3_column_text(stmt, 2);
410 member->filename = filename ? g_strdup(filename) : NULL;
412 }
414
415 // Row order: the caller walks it once to build a tooltip listing the group, and the order
416 // it reads is the order the rows came in.
417 return g_list_reverse(members);
418}
419
421{
424 "SELECT COUNT(*) FROM main.images WHERE id >= ?1 AND id <= ?2", -1,
425 &stmt, 0);
426 if(IS_NULL_PTR(stmt)) return -1;
427
430 const int count = (sqlite3_step(stmt) == SQLITE_ROW) ? sqlite3_column_int(stmt, 0) : -1;
432
433 return count;
434}
435
438 void *user_data)
439{
440 if(IS_NULL_PTR(cb)) return;
441
444 "SELECT id, filename FROM main.images WHERE id >= ?1 AND id <= ?2", -1,
445 &stmt, 0);
446 if(IS_NULL_PTR(stmt)) return;
447
450 while(sqlite3_step(stmt) == SQLITE_ROW)
451 cb(sqlite3_column_int(stmt, 0), (const char *)sqlite3_column_text(stmt, 1), user_data);
453}
454
455
457 const int32_t exclude_imgid)
458{
460 // clang-format off
462 "UPDATE main.images SET group_id = ?1 WHERE group_id = ?2 AND id != ?3",
463 -1, &stmt, NULL);
464 // clang-format on
470}
471
473{
475
476 if(imgid < 0)
477 {
478 // clang-format off
480 "SELECT flags FROM main.images WHERE id IN "
481 "(SELECT imgid FROM main.selected_images)",
482 -1, &stmt, NULL);
483 // clang-format on
484 }
485 else
486 {
487 // clang-format off
489 "SELECT flags FROM main.images WHERE id = ?1", -1, &stmt, NULL);
490 // clang-format on
492 }
493
494 GList *result = NULL;
495 while(sqlite3_step(stmt) == SQLITE_ROW)
496 {
497 const int stars = (sqlite3_column_int(stmt, 0) & 0x7) - 1;
498 result = g_list_prepend(result, GINT_TO_POINTER(stars));
499 }
501
502 return g_list_reverse(result);
503}
504
505/* ---------------------------------------------------------------------------------------------
506 * Identity lookups
507 * ------------------------------------------------------------------------------------------ */
508
509int32_t dt_image_repository_find_by_film_and_filename(const int32_t film_id, const char *filename)
510{
511 if(IS_NULL_PTR(filename)) return -1;
512
513 int32_t id = -1;
516 "SELECT id FROM main.images WHERE film_id = ?1 AND filename = ?2",
517 -1, &stmt, NULL);
518 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id);
522 return id;
523}
524
525int32_t dt_image_repository_find_by_folder_and_filename(const char *folder, const char *filename)
526{
527 if(IS_NULL_PTR(folder) || IS_NULL_PTR(filename)) return -1;
528
529 int32_t id = -1;
531 // clang-format off
533 "SELECT images.id"
534 " FROM main.images, main.film_rolls"
535 " WHERE film_rolls.folder = ?1"
536 " AND images.film_id = film_rolls.id"
537 " AND images.filename = ?2",
538 -1, &stmt, NULL);
539 // clang-format on
544
545 return id;
546}
547
548
549/* ---------------------------------------------------------------------------------------------
550 * Versions, flags and the write timestamp
551 * ------------------------------------------------------------------------------------------ */
552
553int dt_image_repository_get_version(const int32_t imgid)
554{
555 int version = 0;
557 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get_sqlite3_global(), "SELECT version FROM main.images WHERE id = ?1", -1,
558 &stmt, NULL);
560
561 if(sqlite3_step(stmt) == SQLITE_ROW) version = sqlite3_column_int(stmt, 0);
563 return version;
564}
565
566gboolean dt_image_repository_set_version(const int32_t imgid, const int version)
567{
571 "UPDATE main.images SET version=?1, max_version = ?1 WHERE id = ?2", -1, &stmt, NULL);
572 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, version);
574 const gboolean ok = (sqlite3_step(stmt) == SQLITE_DONE);
576 return ok;
577}
578
579int dt_image_repository_count_others_with_flag(const int32_t imgid, const int flag)
580{
582 int result = 1;
583
584 // clang-format off
586 "SELECT COUNT(*)"
587 " FROM main.images"
588 " WHERE id!=?1 AND flags&?2=?2"
589 " AND film_id=(SELECT film_id"
590 " FROM main.images"
591 " WHERE id=?1)"
592 " AND filename=(SELECT filename"
593 " FROM main.images"
594 " WHERE id=?1);",
595 -1, &stmt, NULL);
596 // clang-format on
601
602 return result;
603}
604
609static char *_id_set(GList *imgids)
610{
611 if(IS_NULL_PTR(imgids)) return NULL;
612
613 GString *set = g_string_new(NULL);
614 for(GList *l = imgids; l; l = g_list_next(l))
615 {
616 if(l != imgids) g_string_append_c(set, ',');
617 g_string_append_printf(set, "%d", GPOINTER_TO_INT(l->data));
618 }
619 return g_string_free(set, FALSE);
620}
621
623{
624 char *set = _id_set(imgids);
625 if(IS_NULL_PTR(set)) return NULL;
626
627 GList *ids = NULL;
629 char *query = g_strdup_printf("SELECT id FROM main.images WHERE id IN (%s) AND flags&?1=?1", set);
632 while(sqlite3_step(stmt) == SQLITE_ROW)
635 dt_free(query);
636 dt_free(set);
637 return g_list_reverse(ids); // row order
638}
639
641{
642 char *set = _id_set(imgids);
643 if(IS_NULL_PTR(set)) return FALSE;
644
646 char *query = g_strdup_printf("UPDATE main.images SET flags = (flags|?1) WHERE id IN (%s)", set);
649 const gboolean ok = (sqlite3_step(stmt) == SQLITE_DONE);
651 dt_free(query);
652 dt_free(set);
653 return ok;
654}
655
657{
658 char *set = _id_set(imgids);
659 if(IS_NULL_PTR(set)) return NULL;
660
661 GList *list = NULL;
663 // clang-format off
664 char *query = g_strdup_printf("SELECT DISTINCT folder || '" G_DIR_SEPARATOR_S "' || filename FROM "
665 "main.images i, main.film_rolls f "
666 "ON i.film_id = f.id WHERE i.id IN (%s)", set);
667 // clang-format on
669 while(sqlite3_step(stmt) == SQLITE_ROW)
670 list = g_list_prepend(list, g_strdup((const gchar *)sqlite3_column_text(stmt, 0)));
672 dt_free(query);
673 dt_free(set);
674 return g_list_reverse(list); // list was built in reverse order, so un-reverse it
675}
676
678{
679 GList *ids = NULL;
681 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get_sqlite3_global(), "SELECT id FROM main.images WHERE flags&?1=?1", -1,
682 &stmt, NULL);
684
685 while(sqlite3_step(stmt) == SQLITE_ROW)
688
689 // Row order, NOT reverse-row: the caller filters this list and prepends into its own, and
690 // that second prepend reproduces the single prepend the original did off the cursor.
691 return g_list_reverse(ids);
692}
693
721
723{
724 if(IS_NULL_PTR(cb)) return;
725
727 // clang-format off
729 "SELECT i.id, write_timestamp, version,"
730 " folder || '" G_DIR_SEPARATOR_S "' || filename, flags"
731 " FROM main.images i, main.film_rolls f"
732 " ON i.film_id = f.id"
733 " ORDER BY f.id, filename",
734 -1, &stmt, NULL);
735 // clang-format on
736 if(IS_NULL_PTR(stmt)) return;
737
738 while(sqlite3_step(stmt) == SQLITE_ROW)
739 {
740 // No lock is held here on purpose: cb() writes back through this repository.
744 (const char *)sqlite3_column_text(stmt, 3),
746 user_data);
747 }
749}
750
752{
753 char *set = _id_set(imgids);
754 if(IS_NULL_PTR(set)) return NULL;
755
756 // One subquery per metadata key, each needing the id set again -- nine occurrences in all.
757 // clang-format off
758 char *query = g_strdup_printf("SELECT COUNT(DISTINCT film_id), "
759 "COUNT(DISTINCT film_id), "
760 "2, " // imgid always different
761 "COUNT(DISTINCT group_id), "
762 "COUNT(DISTINCT filename), "
763 "COUNT(DISTINCT version), "
764 "COUNT(DISTINCT film_id || '/' || filename), " //path
765 "COUNT(DISTINCT flags & 2048), " //local copy
766 "COUNT(DISTINCT import_timestamp), "
767 "COUNT(DISTINCT change_timestamp), "
768 "COUNT(DISTINCT export_timestamp), "
769 "COUNT(DISTINCT print_timestamp), "
770 "COUNT(DISTINCT flags), "
771 "COUNT(DISTINCT model), "
772 "COUNT(DISTINCT maker), "
773 "COUNT(DISTINCT lens), "
774 "COUNT(DISTINCT aperture), "
775 "COUNT(DISTINCT exposure), "
776 "COUNT(DISTINCT IFNULL(exposure_bias, '')), "
777 "COUNT(DISTINCT focal_length), "
778 "COUNT(DISTINCT focus_distance), "
779 "COUNT(DISTINCT iso), "
780 "COUNT(DISTINCT datetime_taken), "
781 "COUNT(DISTINCT width), "
782 "COUNT(DISTINCT height), "
783 "COUNT(DISTINCT IFNULL(output_width, '')), " //exported width
784 "COUNT(DISTINCT IFNULL(output_height, '')), " //exported height
785 "(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
786 "(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
787 "(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
788 "(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
789 "(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
790 "(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
791 "(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
792 "(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
793 "COUNT(DISTINCT IFNULL(latitude, '')), "
794 "COUNT(DISTINCT IFNULL(longitude, '')), "
795 "COUNT(DISTINCT IFNULL(altitude, '')) "
796 "FROM main.images "
797 "WHERE id IN (%s)",
798 set, set, set, set, set, set, set, set, set);
799 // clang-format on
800
803 dt_free(query);
804 dt_free(set);
805 if(IS_NULL_PTR(stmt)) return NULL;
806
807 int *counts = NULL;
809 {
810 const int col_count = sqlite3_column_count(stmt);
811 counts = (int *)calloc(DT_IMAGE_FIELD_COUNT, sizeof(int));
812 if(counts)
813 {
814 for(int i = 0; i < DT_IMAGE_FIELD_COUNT && i < col_count; i++)
816 }
817 }
819
820 return counts;
821}
822
824{
825 if(IS_NULL_PTR(bounds)) return FALSE;
826
827 bounds->min_latitude = INFINITY;
828 bounds->max_latitude = -INFINITY;
829 bounds->min_longitude = INFINITY;
830 bounds->max_longitude = -INFINITY;
831 bounds->count = 0;
832
834 // clang-format off
836 "SELECT MIN(latitude), MAX(latitude),"
837 " MIN(longitude), MAX(longitude), COUNT(*)"
838 " FROM main.images AS i "
839 " JOIN memory.collected_images AS l ON l.imgid = i.id "
840 " WHERE latitude NOT NULL AND longitude NOT NULL",
841 -1, &stmt, NULL);
842 // clang-format on
843 if(IS_NULL_PTR(stmt)) return FALSE;
844
845 const gboolean ok = (sqlite3_step(stmt) == SQLITE_ROW);
846 if(ok)
847 {
852 bounds->count = sqlite3_column_int(stmt, 4);
853 }
855
856 return ok;
857}
858
860 const double lat1, const double lat2,
861 int *count)
862{
863 if(IS_NULL_PTR(count)) return NULL;
864 *count = 0;
865
867 // clang-format off
869 "SELECT * FROM"
870 " (SELECT i.id, i.longitude, i.latitude "
871 " FROM main.images i INNER JOIN memory.collected_images c ON i.id = c.imgid"
872 " WHERE longitude >= ?1 AND longitude <= ?2"
873 " AND latitude <= ?3 AND latitude >= ?4 "
874 " AND longitude NOT NULL AND latitude NOT NULL)"
875 " ORDER BY longitude ASC", // critical to make dbscan work
876 -1, &stmt, NULL);
877 // clang-format on
878 if(IS_NULL_PTR(stmt)) return NULL;
879
884
885 // One pass into a growing array, where the caller used to step the whole cursor once to
886 // count the rows and a second time to fill an exactly-sized buffer.
888 while(sqlite3_step(stmt) == SQLITE_ROW)
889 {
891 .longitude = sqlite3_column_double(stmt, 1),
892 .latitude = sqlite3_column_double(stmt, 2) };
893 g_array_append_val(points, p);
894 }
896
897 *count = (int)points->len;
898 if(points->len == 0)
899 {
900 g_array_free(points, TRUE);
901 return NULL;
902 }
903
904 return (dt_image_geo_point_t *)g_array_free(points, FALSE); // hand over the buffer
905}
906
935
958
959gboolean dt_image_repository_set_write_timestamp(const int32_t imgid, const int64_t timestamp)
960{
961 if(imgid <= 0) return FALSE;
962
965 "UPDATE main.images SET write_timestamp = ?2 WHERE id = ?1",
966 -1, &stmt, NULL);
967 if(IS_NULL_PTR(stmt)) return FALSE;
968
971 const gboolean ok = (sqlite3_step(stmt) == SQLITE_DONE);
973
974 return ok;
975}
976
977gboolean dt_image_repository_delete(const int32_t imgid)
978{
979 gboolean ok = TRUE;
981
982 // due to foreign keys added in db version 33,
983 // all entries from tables having references to the images are deleted as well
984 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get_sqlite3_global(), "DELETE FROM main.images WHERE id = ?1", -1, &stmt,
985 NULL);
987 ok &= (sqlite3_step(stmt) == SQLITE_DONE);
988 sqlite3_finalize(stmt); // the original overwrote `stmt` here and leaked this statement
989
991 "DELETE FROM main.meta_data WHERE id = ?1", -1, &stmt, NULL);
993 ok &= (sqlite3_step(stmt) == SQLITE_DONE);
995
996 return ok;
997}
998
999int32_t dt_image_repository_duplicate(const int32_t imgid, const int32_t newversion)
1000{
1002 int32_t newid = -1;
1003
1004 // clang-format off
1006 "SELECT a.id"
1007 " FROM main.images AS a JOIN main.images AS b"
1008 " WHERE a.film_id = b.film_id AND a.filename = b.filename"
1009 " AND b.id = ?1 AND a.version = ?2"
1010 " ORDER BY a.id DESC",
1011 -1, &stmt, NULL);
1012 // clang-format on
1016 {
1018 }
1020
1021 // requested version is already present in DB, so we just return it
1022 if(newid != -1) return newid;
1023
1024 // clang-format off
1027 "INSERT INTO main.images"
1028 " (id, group_id, film_id, width, height, filename, maker, model, lens, exposure,"
1029 " aperture, iso, focal_length, focus_distance, datetime_taken, flags,"
1030 " output_width, output_height, crop, raw_parameters, raw_denoise_threshold,"
1031 " raw_auto_bright_threshold, raw_black, raw_maximum,"
1032 " license, sha1sum, orientation, histogram, lightmap,"
1033 " longitude, latitude, altitude, color_matrix, colorspace, version, max_version, history_end,"
1034 " aspect_ratio, exposure_bias, import_timestamp)"
1035 " SELECT NULL, group_id, film_id, width, height, filename, maker, model, lens,"
1036 " exposure, aperture, iso, focal_length, focus_distance, datetime_taken,"
1037 " flags, output_width, output_height, crop, raw_parameters, raw_denoise_threshold,"
1038 " raw_auto_bright_threshold, raw_black, raw_maximum,"
1039 " license, sha1sum, orientation, histogram, lightmap,"
1040 " longitude, latitude, altitude, color_matrix, colorspace, NULL, NULL, 0,"
1041 " aspect_ratio, exposure_bias, import_timestamp"
1042 " FROM main.images WHERE id = ?1",
1043 -1, &stmt, NULL);
1044 // clang-format on
1048 // clang-format off
1050 "SELECT a.id, a.film_id, a.filename, b.max_version"
1051 " FROM main.images AS a JOIN main.images AS b"
1052 " WHERE a.film_id = b.film_id AND a.filename = b.filename AND b.id = ?1"
1053 " ORDER BY a.id DESC",
1054 -1, &stmt, NULL);
1055 // clang-format on
1057
1058 int32_t film_id = UNKNOWN_IMAGE;
1059 int32_t max_version = -1;
1060 gchar *filename = NULL;
1062 {
1064 film_id = sqlite3_column_int(stmt, 1);
1065 filename = g_strdup((gchar *)sqlite3_column_text(stmt, 2));
1067 }
1069
1070 if(newid != -1)
1071 {
1072 // clang-format off
1074 "INSERT INTO main.color_labels (imgid, color)"
1075 " SELECT ?1, color FROM main.color_labels WHERE imgid = ?2",
1076 -1, &stmt, NULL);
1077 // clang-format on
1082
1083 // clang-format off
1085 "INSERT INTO main.meta_data (id, key, value)"
1086 " SELECT ?1, key, value FROM main.meta_data WHERE id = ?2",
1087 -1, &stmt, NULL);
1088 // clang-format on
1093
1094 // clang-format off
1096 "INSERT INTO main.tagged_images (imgid, tagid, position)"
1097 " SELECT ?1, tagid, "
1098 " (SELECT (IFNULL(MAX(position),0) & 0xFFFFFFFF00000000)"
1099 " FROM main.tagged_images)"
1100 " + (ROW_NUMBER() OVER (ORDER BY imgid) << 32)"
1101 " FROM main.tagged_images AS ti"
1102 " WHERE imgid = ?2",
1103 -1, &stmt, NULL);
1104 // clang-format on
1109
1110 // clang-format off
1112 "INSERT INTO main.module_order (imgid, iop_list, version)"
1113 " SELECT ?1, iop_list, version FROM main.module_order WHERE imgid = ?2",
1114 -1, &stmt, NULL);
1115 // clang-format on
1120
1121 // set version of new entry and max_version of all involved duplicates (with same film_id and filename)
1122 // this needs to happen before we do anything with the image cache, as version isn't updated through the cache
1123 const int32_t version = (newversion != -1) ? newversion : max_version + 1;
1125
1126 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get_sqlite3_global(), "UPDATE main.images SET version=?1 WHERE id = ?2",
1127 -1, &stmt, NULL);
1128 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, version);
1132
1133 // clang-format off
1135 "UPDATE main.images SET max_version=?1 WHERE film_id = ?2 AND filename = ?3", -1,
1136 &stmt, NULL);
1137 // clang-format on
1139 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, film_id);
1143
1144 dt_free(filename);
1145 }
1146 return newid;
1147}
1148
1149void dt_image_version_free(gpointer data)
1150{
1152 if(IS_NULL_PTR(v)) return;
1153 dt_free(v->version_name);
1154 dt_free(v);
1155}
1156
1157GList *dt_image_repository_get_versions(const int32_t film_id, const char *filename,
1158 const int name_keyid)
1159{
1160 if(IS_NULL_PTR(filename)) return NULL;
1161
1163 // clang-format off
1165 "SELECT i.version, i.id, m.value"
1166 " FROM images AS i"
1167 " LEFT JOIN meta_data AS m ON m.id = i.id AND m.key = ?3"
1168 " WHERE film_id = ?1 AND filename = ?2"
1169 " ORDER BY i.version",
1170 -1, &stmt, NULL);
1171 // clang-format on
1172 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id);
1175
1176 GList *versions = NULL;
1177 while(sqlite3_step(stmt) == SQLITE_ROW)
1178 {
1180 if(v)
1181 {
1182 v->version = sqlite3_column_int(stmt, 0);
1183 v->imgid = sqlite3_column_int(stmt, 1);
1184 const char *name = (const char *)sqlite3_column_text(stmt, 2);
1185 v->version_name = name ? g_strdup(name) : NULL;
1187 }
1188 }
1190
1191 return g_list_reverse(versions); // version order, as the query returns it
1192}
1193
1195{
1196 GList *ids = NULL;
1198 // statement for getting ids of the image to be moved and its duplicates
1199 // clang-format off
1202 "SELECT id"
1203 " FROM main.images"
1204 " WHERE filename IN (SELECT filename FROM main.images WHERE id = ?1)"
1205 " AND film_id IN (SELECT film_id FROM main.images WHERE id = ?1)",
1206 -1, &stmt, NULL);
1207 // clang-format on
1209
1210 while(sqlite3_step(stmt) == SQLITE_ROW)
1213
1214 // Row order: the original prepended off the cursor and then reversed, and its caller walks
1215 // the list without prepending again.
1216 return g_list_reverse(ids);
1217}
1218
1219int32_t dt_image_repository_copy_to_film(const int32_t imgid, const int32_t filmid,
1220 const char *new_filename, const char *old_filename)
1221{
1222 int32_t newid = -1;
1223 gchar *filename = NULL;
1225
1226// update database
1227// clang-format off
1230 "INSERT INTO main.images"
1231 " (id, group_id, film_id, width, height, filename, maker, model, lens, exposure,"
1232 " aperture, iso, focal_length, focus_distance, datetime_taken, flags,"
1233 " output_width, output_height, crop, raw_parameters, raw_denoise_threshold,"
1234 " raw_auto_bright_threshold, raw_black, raw_maximum,"
1235 " license, sha1sum, orientation, histogram, lightmap,"
1236 " longitude, latitude, altitude, color_matrix, colorspace, version, max_version,"
1237 " aspect_ratio, exposure_bias)"
1238 " SELECT NULL, group_id, ?1 as film_id, width, height, ?2 as filename, maker, model, lens,"
1239 " exposure, aperture, iso, focal_length, focus_distance, datetime_taken,"
1240 " flags, width, height, crop, raw_parameters, raw_denoise_threshold,"
1241 " raw_auto_bright_threshold, raw_black, raw_maximum,"
1242 " license, sha1sum, orientation, histogram, lightmap,"
1243 " longitude, latitude, altitude, color_matrix, colorspace, -1, -1,"
1244 " aspect_ratio, exposure_bias"
1245 " FROM main.images"
1246 " WHERE id = ?3",
1247 -1, &stmt, NULL);
1248// clang-format on
1254// clang-format off
1257 "SELECT a.id, a.filename"
1258 " FROM main.images AS a"
1259 " JOIN main.images AS b"
1260 " WHERE a.film_id = ?1 AND a.filename = ?2 AND b.filename = ?3 AND b.id = ?4"
1261 " ORDER BY a.id DESC",
1262 -1, &stmt, NULL);
1263// clang-format on
1268
1270{
1272 filename = g_strdup((gchar *)sqlite3_column_text(stmt, 1));
1273}
1275
1276if(newid != -1)
1277{
1278 // clang-format off
1280 "INSERT INTO main.color_labels (imgid, color)"
1281 " SELECT ?1, color"
1282 " FROM main.color_labels"
1283 " WHERE imgid = ?2",
1284 -1, &stmt, NULL);
1285 // clang-format on
1290 // clang-format off
1292 "INSERT INTO main.meta_data (id, key, value)"
1293 " SELECT ?1, key, value"
1294 " FROM main.meta_data"
1295 " WHERE id = ?2",
1296 -1, &stmt, NULL);
1297 // clang-format on
1302
1303 // clang-format off
1305 "INSERT INTO main.tagged_images (imgid, tagid, position)"
1306 " SELECT ?1, tagid, "
1307 " (SELECT (IFNULL(MAX(position),0) & 0xFFFFFFFF00000000)"
1308 " FROM main.tagged_images)"
1309 " + (ROW_NUMBER() OVER (ORDER BY imgid) << 32)"
1310 " FROM main.tagged_images AS ti"
1311 " WHERE imgid = ?2",
1312 -1, &stmt, NULL);
1313 // clang-format on
1318
1319 // get max_version of image duplicates in destination filmroll
1320 int32_t max_version = -1;
1321 // clang-format off
1324 "SELECT MAX(a.max_version)"
1325 " FROM main.images AS a"
1326 " JOIN main.images AS b"
1327 " WHERE a.film_id = b.film_id AND a.filename = b.filename AND b.id = ?1",
1328 -1, &stmt, NULL);
1329 // clang-format on
1331
1334
1335 // set version of new entry and max_version of all involved duplicates (with same film_id and
1336 // filename)
1337 max_version = (max_version >= 0) ? max_version + 1 : 0;
1338 int32_t version = max_version;
1339
1342 "UPDATE main.images SET version=?1 WHERE id = ?2", -1, &stmt, NULL);
1343 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, version);
1347
1350 "UPDATE main.images SET max_version=?1 WHERE film_id = ?2 AND filename = ?3",
1351 -1, &stmt, NULL);
1357
1358 // image group handling follows
1359 // get group_id of potential image duplicates in destination filmroll
1360 int32_t new_group_id = -1;
1361 // clang-format off
1364 "SELECT DISTINCT a.group_id"
1365 " FROM main.images AS a"
1366 " JOIN main.images AS b"
1367 " WHERE a.film_id = b.film_id AND a.filename = b.filename"
1368 " AND b.id = ?1 AND a.id != ?1",
1369 -1, &stmt, NULL);
1370 // clang-format on
1372
1374
1375 // then check if there are further duplicates belonging to different group(s)
1378
1379 // rationale:
1380 // if no group exists or if the image duplicates belong to multiple groups, then the
1381 // new image builds a group of its own, else it is added to the (one) existing group
1382 if(new_group_id == -1) new_group_id = newid;
1383
1384 // make copied image belong to a group
1386 "UPDATE main.images SET group_id=?1 WHERE id = ?2", -1, &stmt, NULL);
1387
1392
1393 }
1394
1395 dt_free(filename);
1396 return newid;
1397}
1398
1399gboolean dt_image_repository_insert_import(const int32_t film_id, const char *filename,
1400 const int flags, const int64_t import_timestamp)
1401{
1403 //insert a v0 record (which may be updated later if no v0 xmp exists)
1404 // clang-format off
1407 "INSERT INTO main.images (id, film_id, filename, license, sha1sum, flags, version, "
1408 " max_version, history_end, position, import_timestamp)"
1409 " SELECT NULL, ?1, ?2, '', '', ?3, 0, 0, 0, (IFNULL(MAX(position),0) & 0xFFFFFFFF00000000) + (1 << 32), ?4 "
1410 " FROM images",
1411 -1, &stmt, NULL);
1412 // clang-format on
1413
1414 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id);
1417 DT_DEBUG_SQLITE3_BIND_INT64(stmt, 4, import_timestamp);
1418
1419 const int rc = sqlite3_step(stmt);
1420 if(rc != SQLITE_DONE) fprintf(stderr, "sqlite3 error %d\n", rc);
1422 return (rc == SQLITE_DONE);
1423}
1424
1426 const char *filename_pattern,
1427 const gboolean leader_only,
1428 const int32_t exclude_imgid)
1429{
1430 int32_t group_id = -1;
1432
1433 if(leader_only)
1434 {
1435 // clang-format off
1438 "SELECT group_id"
1439 " FROM main.images"
1440 " WHERE film_id = ?1 AND filename LIKE ?2 AND id = group_id", -1, &stmt,
1441 NULL);
1442 // clang-format on
1443 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id);
1445 }
1446 else
1447 {
1448 // clang-format off
1451 "SELECT group_id"
1452 " FROM main.images"
1453 " WHERE film_id = ?1 AND filename LIKE ?2 AND id != ?3", -1, &stmt, NULL);
1454 // clang-format on
1455 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id);
1458 }
1459
1460 if(sqlite3_step(stmt) == SQLITE_ROW) group_id = sqlite3_column_int(stmt, 0);
1462 return group_id;
1463}
1464
1465gboolean dt_image_repository_set_group(const int32_t imgid, const int32_t group_id)
1466{
1470 "UPDATE main.images SET group_id = ?1 WHERE id = ?2",
1471 -1, &stmt, NULL);
1472 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, group_id);
1474 const gboolean ok = (sqlite3_step(stmt) == SQLITE_DONE);
1476 return ok;
1477}
1478
1480{
1481 if(IS_NULL_PTR(row)) return FALSE;
1482 memset(row, 0, sizeof(*row));
1483
1484 gboolean found = FALSE;
1486 // clang-format off
1488 "SELECT filename, flags, raw_parameters, "
1489 " longitude, latitude, altitude, history_end, datetime_taken"
1490 " FROM main.images"
1491 " WHERE id = ?1",
1492 -1, &stmt, NULL);
1493 // clang-format on
1496 {
1497 // copied, not borrowed: the caller used to read it before finalising the statement, and
1498 // owning it removes the constraint rather than moving it across a module boundary
1499 const char *f = (const char *)sqlite3_column_text(stmt, 0);
1500 row->filename = f ? g_strdup(f) : NULL;
1501 row->flags = sqlite3_column_int(stmt, 1);
1502 row->raw_parameters = sqlite3_column_int(stmt, 2);
1504 {
1505 row->longitude = sqlite3_column_double(stmt, 3);
1506 row->has_longitude = TRUE;
1507 }
1509 {
1510 row->latitude = sqlite3_column_double(stmt, 4);
1511 row->has_latitude = TRUE;
1512 }
1514 {
1515 row->altitude = sqlite3_column_double(stmt, 5);
1516 row->has_altitude = TRUE;
1517 }
1518 row->history_end = sqlite3_column_int(stmt, 6);
1519 row->datetime_taken = sqlite3_column_int64(stmt, 7);
1520 found = TRUE;
1521 }
1523 return found;
1524}
1525
1527{
1528 if(IS_NULL_PTR(row)) return;
1529 dt_free(row->filename);
1530 row->filename = NULL;
1531}
1532
1534{
1535 if(IS_NULL_PTR(ts)) return FALSE;
1536 memset(ts, 0, sizeof(*ts));
1537
1538 gboolean found = FALSE;
1540 // clang-format off
1543 "SELECT import_timestamp, change_timestamp, export_timestamp, print_timestamp"
1544 " FROM main.images"
1545 " WHERE id = ?1",
1546 -1, &stmt, NULL);
1548 // clang-format on
1549
1551 {
1553 {
1555 ts->has_import = TRUE;
1556 }
1558 {
1560 ts->has_change = TRUE;
1561 }
1563 {
1565 ts->has_export = TRUE;
1566 }
1568 {
1570 ts->has_print = TRUE;
1571 }
1572 found = TRUE;
1573 }
1575 return found;
1576}
1577
1612
1613// clang-format off
1614// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1615// vim: shiftwidth=2 expandtab tabstop=2 cindent
1616// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1617// 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:3753
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
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:385
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Definition dtpthread.h:370
static int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:390
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:375
GdkRGBA color[]
Definition geotagging.c:539
#define UNKNOWN_IMAGE
Definition image.h:77
const char flag
Definition image.h:285
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.
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.
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.
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
void(* 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:65
#define dt_free(ptr)
Definition mem_alloc.h:97
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
const char * name
Definition pdf.h:90
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:308
double elevation
Definition image.h:308
double longitude
Definition image.h:308
One member of a group, with what a tooltip needs to name it.
float exif_exposure
Definition image.h:343
int32_t height
Definition image.h:373
GTimeSpan export_timestamp
Definition image.h:391
uint64_t history_hash
Definition image.h:380
float exif_focus_distance
Definition image.h:348
GTimeSpan import_timestamp
Definition image.h:391
int32_t group_id
Definition image.h:377
float exif_exposure_bias
Definition image.h:344
uint16_t raw_black_level
Definition image.h:413
float exif_iso
Definition image.h:346
uint32_t raw_white_point
Definition image.h:415
int32_t exif_inited
Definition image.h:341
float exif_aperture
Definition image.h:345
int32_t version
Definition image.h:377
uint64_t mipmap_hash
Definition image.h:387
char fullpath[PATH_MAX]
Definition image.h:363
dt_image_geoloc_t geoloc
Definition image.h:410
int32_t flags
Definition image.h:377
dt_image_orientation_t orientation
Definition image.h:342
int32_t width
Definition image.h:373
float exif_focal_length
Definition image.h:347
char exif_maker[64]
Definition image.h:350
GTimeSpan change_timestamp
Definition image.h:391
uint32_t history_items
Definition image.h:379
char exif_lens[128]
Definition image.h:352
char local_copy_path[PATH_MAX]
Definition image.h:364
GTimeSpan print_timestamp
Definition image.h:391
int32_t film_id
Definition image.h:377
GTimeSpan exif_datetime_taken
Definition image.h:353
int color_labels
Definition image.h:443
float d65_color_matrix[9]
Definition image.h:397
char exif_model[64]
Definition image.h:351
dt_image_colorspace_t colorspace
Definition image.h:405
char datetime[200]
Definition image.h:368
uint32_t group_members
Definition image.h:378
dt_image_raw_parameters_t legacy_flip
Definition image.h:407
char filename[DT_MAX_FILENAME_LEN]
Definition image.h:362
char folder[PATH_MAX]
Definition image.h:366
int32_t id
Definition image.h:377
char filmroll[PATH_MAX]
Definition image.h:367
char local_copy_legacy_path[PATH_MAX]
Definition image.h:365
float exif_crop
Definition image.h:349
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