Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
film_jobs.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2010 Henrik Andersson.
4 Copyright (C) 2010, 2012 johannes hanika.
5 Copyright (C) 2012 Richard Wonka.
6 Copyright (C) 2014 Jérémy Rosen.
7 Copyright (C) 2014, 2016 Tobias Ellinghaus.
8 Copyright (C) 2015-2016 Roman Lebedev.
9 Copyright (C) 2016 parafin.
10 Copyright (C) 2017 luzpaz.
11 Copyright (C) 2020 Hubert Kowalski.
12 Copyright (C) 2020-2021 Pascal Obry.
13 Copyright (C) 2021 Aldric Renaudin.
14 Copyright (C) 2021 Bill Ferguson.
15 Copyright (C) 2021 Hanno Schwalm.
16 Copyright (C) 2021 Philippe Weyland.
17 Copyright (C) 2021 Ralf Brown.
18 Copyright (C) 2022 Martin Bařinka.
19 Copyright (C) 2023, 2025 Aurélien PIERRE.
20
21 darktable is free software: you can redistribute it and/or modify
22 it under the terms of the GNU General Public License as published by
23 the Free Software Foundation, either version 3 of the License, or
24 (at your option) any later version.
25
26 darktable is distributed in the hope that it will be useful,
27 but WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 GNU General Public License for more details.
30
31 You should have received a copy of the GNU General Public License
32 along with darktable. If not, see <http://www.gnu.org/licenses/>.
33*/
35#include "common/darktable.h"
36#include "common/collection.h"
37#include "common/film.h"
38#include <stdlib.h>
39
45
46static void _film_import1(dt_job_t *job, dt_film_t *film, GList *images);
47
48static int32_t dt_film_import1_run(dt_job_t *job)
49{
51 _film_import1(job, params->film, NULL); // import the given film, collecting its images
52 dt_pthread_mutex_lock(&params->film->images_mutex);
53 params->film->ref--;
54 dt_pthread_mutex_unlock(&params->film->images_mutex);
55 if(params->film->ref <= 0)
56 {
57 if(dt_film_is_empty(params->film->id))
58 {
59 dt_film_remove(params->film->id);
60 }
61 }
62
63 // notify the user via the window manager
65
66 return 0;
67}
68
69static void dt_film_import1_cleanup(void *p)
70{
71 dt_film_import1_t *params = p;
72
73 dt_film_cleanup(params->film);
74 dt_free(params->film);
75
76 dt_free(params);
77}
78
80{
81 dt_job_t *job = dt_control_job_create(&dt_film_import1_run, "cache load raw images for preview");
82 if(IS_NULL_PTR(job)) return NULL;
83 dt_film_import1_t *params = (dt_film_import1_t *)calloc(1, sizeof(dt_film_import1_t));
84 if(IS_NULL_PTR(params))
85 {
87 return NULL;
88 }
89 dt_control_job_add_progress(job, _("import images"), FALSE);
91 params->film = film;
93 film->ref++;
95 return job;
96}
97
98static int32_t _pathlist_import_run(dt_job_t *job)
99{
101 _film_import1(job, NULL, params->imagelist); // import the specified images, creating filmrolls as needed
102 params->imagelist = NULL; // the import will have freed the image list
103
104 // notify the user via the window manager
106 return 0;
107}
108
109static void _pathlist_import_cleanup(void *p)
110{
111 dt_film_import1_t *params = p;
112 dt_free(params);
113}
114
115dt_job_t *dt_pathlist_import_create(int argc, char *argv[])
116{
117 dt_job_t *job = dt_control_job_create(&_pathlist_import_run, "import commandline images");
118 if(IS_NULL_PTR(job)) return NULL;
119 dt_film_import1_t *params = (dt_film_import1_t *)calloc(1, sizeof(dt_film_import1_t));
120 if(IS_NULL_PTR(params))
121 {
123 return NULL;
124 }
125 dt_control_job_add_progress(job, _("import images"), FALSE);
127 params->film = NULL;
128 // now collect all of the images to be imported
129 params->imagelist = NULL;
130 for(int i = 1; i < argc; i++)
131 {
132 char *path = dt_util_normalize_path(argv[i]);
133 if(!g_file_test(path, G_FILE_TEST_IS_DIR))
134 {
135 // add just the given name to the list of images to import
136 params->imagelist = g_list_prepend(params->imagelist, path);
137 }
138 else
139 {
140 // iterate over the directory, extracting image files
141 GDir *cdir = g_dir_open(path, 0, NULL);
142 if (cdir)
143 {
144 while(TRUE)
145 {
146 const gchar *fname = g_dir_read_name(cdir);
147 if(IS_NULL_PTR(fname)) break; // no more files in directory
148 if(fname[0] == '.') continue; // skip hidden files
149 gchar *fullname = g_build_filename(path, fname, NULL);
150 if(!g_file_test(fullname, G_FILE_TEST_IS_DIR) && dt_supported_image(fname))
151 params->imagelist = g_list_prepend(params->imagelist, fullname);
152 else
153 dt_free(fullname);
154 }
155 }
156 g_dir_close(cdir);
157 dt_free(path);
158 }
159 }
160 params->imagelist = g_list_reverse(params->imagelist);
161 return job;
162}
163
164static GList *_film_recursive_get_files(const gchar *path, gboolean recursive, GList **result)
165{
166 gchar *fullname;
167
168 /* let's try open current dir */
169 GDir *cdir = g_dir_open(path, 0, NULL);
170 if(IS_NULL_PTR(cdir)) return *result;
171
172 /* lets read all files in current dir, recurse
173 into directories if we should import recursive.
174 */
175 do
176 {
177 /* get the current filename */
178 const gchar *filename = g_dir_read_name(cdir);
179
180 /* return if no more files are in current dir */
181 if(IS_NULL_PTR(filename)) break;
182 if(filename[0] == '.') continue;
183
184 /* build full path for filename */
185 fullname = g_build_filename(path, filename, NULL);
186
187 /* recurse into directory if we hit one and we doing a recursive import */
188 if(recursive && g_file_test(fullname, G_FILE_TEST_IS_DIR))
189 {
190 *result = _film_recursive_get_files(fullname, recursive, result);
191 dt_free(fullname);
192 }
193 /* or test if we found a supported image format to import */
194 else if(!g_file_test(fullname, G_FILE_TEST_IS_DIR) && dt_supported_image(filename))
195 *result = g_list_prepend(*result, fullname);
196 else
197 dt_free(fullname);
198
199 } while(TRUE);
200
201 /* cleanup and return results */
202 g_dir_close(cdir);
203
204 return *result;
205}
206
207/* check if we can find a gpx data file to be auto applied
208 to images in the just imported filmroll
209*/
211{
212 if(!IS_NULL_PTR(cfr) && cfr->dir)
213 {
214 g_dir_rewind(cfr->dir);
215 const gchar *dfn = NULL;
216 while((dfn = g_dir_read_name(cfr->dir)) != NULL)
217 {
218 /* check if we have a gpx to be auto applied to filmroll */
219 const size_t len = strlen(dfn);
220 if(strcmp(dfn + len - 4, ".gpx") == 0 || strcmp(dfn + len - 4, ".GPX") == 0)
221 {
222 gchar *gpx_file = g_build_path(G_DIR_SEPARATOR_S, cfr->dirname, dfn, NULL);
223 gchar *tz = dt_conf_get_string("plugins/lighttable/geotagging/tz");
224 dt_control_gpx_apply(gpx_file, cfr->id, tz, NULL);
225 dt_free(gpx_file);
226 dt_free(tz);
227 }
228 }
229 }
230}
231
232/* compare used for sorting the list of files to import
233 only sort on basename of full path eg. the actually filename.
234*/
235static int _film_filename_cmp(gchar *a, gchar *b)
236{
237 gchar *a_basename = g_path_get_basename(a);
238 gchar *b_basename = g_path_get_basename(b);
239 const int ret = g_strcmp0(a_basename, b_basename);
240 dt_free(a_basename);
241 dt_free(b_basename);
242 return ret;
243}
244
245static void _film_import1(dt_job_t *job, dt_film_t *film, GList *images)
246{
247 // first, gather all images to import if not already given
248 if (IS_NULL_PTR(images))
249 {
250 const gboolean recursive = dt_conf_get_bool("ui_last/import_recursive");
251
252 images = _film_recursive_get_files(film->dirname, recursive, &images);
253 if(IS_NULL_PTR(images))
254 {
255 dt_control_log(_("no supported images were found to be imported"));
256 return;
257 }
258 }
259
260 if(IS_NULL_PTR(images))
261 {
262 // no error message, lua probably emptied the list on purpose
263 return;
264 }
265
266 /* we got ourself a list of images, lets sort and start import */
267 images = g_list_sort(images, (GCompareFunc)_film_filename_cmp);
268
269 /* let's start import of images */
270 gchar message[512] = { 0 };
271 double fraction = 0;
272 const guint total = g_list_length(images);
273 g_snprintf(message, sizeof(message) - 1, ngettext("importing %d image", "importing %d images", total), total);
275
276 GList *imgs = NULL;
277 GList *all_imgs = NULL;
278
279 /* loop thru the images and import to current film roll */
280 dt_film_t *cfr = film;
281 int pending = 0;
282 double last_update = dt_get_wtime();
283 int32_t imgid = UNKNOWN_IMAGE;
284 for(GList *image = images; image; image = g_list_next(image))
285 {
286 gchar *cdn = g_path_get_dirname((const gchar *)image->data);
287
288 /* check if we need to initialize a new filmroll */
289 if(IS_NULL_PTR(cfr) || g_strcmp0(cfr->dirname, cdn) != 0)
290 {
292
293 /* cleanup previously imported filmroll*/
294 if(cfr && cfr != film)
295 {
296 if(dt_film_is_empty(cfr->id))
297 {
298 dt_film_remove(cfr->id);
299 }
300 dt_film_cleanup(cfr);
301 dt_free(cfr);
302 }
303
304 /* initialize and create a new film to import to */
305 cfr = malloc(sizeof(dt_film_t));
306 dt_film_init(cfr);
307 dt_film_new(cfr, cdn);
308 }
309
310 dt_free(cdn);
311
312 /* import image */
313 imgid = dt_image_import(cfr->id, (const gchar *)image->data, FALSE);
314 pending++; // we have another image which hasn't been reported yet
315 fraction += 1.0 / total;
316 dt_control_job_set_progress(job, fraction);
317
318 all_imgs = g_list_prepend(all_imgs, GINT_TO_POINTER(imgid));
319 imgs = g_list_append(imgs, GINT_TO_POINTER(imgid));
320 const double curr_time = dt_get_wtime();
321 // if we've imported at least four images without an update, and it's been at least half a second since the last
322 // one, update the interface
323 if(pending >= 4 && curr_time - last_update > 0.5)
324 {
326 g_list_copy(imgs));
327 g_list_free(imgs);
328 imgs = NULL;
329 // restart the update count and timer
330 pending = 0;
331 last_update = curr_time;
332 }
333 }
334
335 g_list_free_full(images, dt_free_gpointer);
336 images = NULL;
337 all_imgs = g_list_reverse(all_imgs);
338
339 // only redraw at the end, to not spam the cpu with exposure events
341
342 dt_collection_load_filmroll(darktable.collection, imgid, g_list_length(all_imgs) == 1);
343
344 //QUESTION: should this come after _apply_filmroll_gpx, since that can change geotags again?
346
348
349 /* cleanup previously imported filmroll*/
350 if(!IS_NULL_PTR(cfr) && cfr != film)
351 {
352 dt_film_cleanup(cfr);
353 dt_free(cfr);
354 }
355}
356
357// clang-format off
358// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
359// vim: shiftwidth=2 expandtab tabstop=2 cindent
360// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
361// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
void dt_collection_load_filmroll(dt_collection_t *collection, const int32_t imgid, gboolean open_single_image)
void dt_collection_update_query(const dt_collection_t *collection, dt_collection_change_t query_change, dt_collection_properties_t changed_property, GList *list)
@ DT_COLLECTION_PROP_UNDEF
Definition collection.h:142
@ DT_COLLECTION_CHANGE_RELOAD
Definition collection.h:151
int32_t dt_image_import(const int32_t film_id, const char *filename, gboolean raise_signals)
int dt_conf_get_bool(const char *name)
gchar * dt_conf_get_string(const char *name)
void dt_control_log(const char *msg,...)
Definition control.c:761
void dt_control_queue_redraw_center()
request redraw of center window. This redraws the center view within a gdk critical section to preven...
Definition control.c:861
void dt_control_gpx_apply(const gchar *filename, int32_t filmid, const gchar *tz, GList *imgs)
darktable_t darktable
Definition darktable.c:181
gboolean dt_supported_image(const gchar *filename)
check if file is a supported image
Definition darktable.c:312
#define UNKNOWN_IMAGE
Definition darktable.h:182
static void dt_free_gpointer(gpointer ptr)
Definition darktable.h:463
#define dt_free(ptr)
Definition darktable.h:456
static double dt_get_wtime(void)
Definition darktable.h:914
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:374
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:364
void dt_film_init(dt_film_t *film)
Definition film.c:70
gboolean dt_film_is_empty(const int id)
Definition film.c:383
int dt_film_new(dt_film_t *film, const char *directory)
Definition film.c:161
void dt_film_remove(const int id)
Definition film.c:398
void dt_film_cleanup(dt_film_t *film)
Definition film.c:80
dt_job_t * dt_pathlist_import_create(int argc, char *argv[])
Definition film_jobs.c:115
static void _film_import1(dt_job_t *job, dt_film_t *film, GList *images)
Definition film_jobs.c:245
static void _pathlist_import_cleanup(void *p)
Definition film_jobs.c:109
static GList * _film_recursive_get_files(const gchar *path, gboolean recursive, GList **result)
Definition film_jobs.c:164
static int _film_filename_cmp(gchar *a, gchar *b)
Definition film_jobs.c:235
static int32_t dt_film_import1_run(dt_job_t *job)
Definition film_jobs.c:48
dt_job_t * dt_film_import1_create(dt_film_t *film)
Definition film_jobs.c:79
static int32_t _pathlist_import_run(dt_job_t *job)
Definition film_jobs.c:98
static void _apply_filmroll_gpx(dt_film_t *cfr)
Definition film_jobs.c:210
static void dt_film_import1_cleanup(void *p)
Definition film_jobs.c:69
void dt_ui_notify_user()
draw user's attention
Definition gtk.c:1744
dt_job_t * dt_control_job_create(dt_job_execute_callback execute, const char *msg,...)
Definition jobs.c:135
void * dt_control_job_get_params(const _dt_job_t *job)
Definition jobs.c:129
void dt_control_job_set_progress(dt_job_t *job, double value)
Definition jobs.c:626
void dt_control_job_add_progress(dt_job_t *job, const char *message, gboolean cancellable)
Definition jobs.c:612
void dt_control_job_set_progress_message(dt_job_t *job, const char *message)
Definition jobs.c:620
void dt_control_job_set_params(_dt_job_t *job, void *params, dt_job_destroy_callback callback)
Definition jobs.c:112
void dt_control_job_dispose(_dt_job_t *job)
Definition jobs.c:153
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:347
@ DT_SIGNAL_GEOTAG_CHANGED
This signal is raised when a geotag is added/deleted/changed
Definition signal.h:136
struct dt_collection_t * collection
Definition darktable.h:781
struct dt_control_signal_t * signals
Definition darktable.h:774
dt_film_t * film
Definition film_jobs.c:42
GList * imagelist
Definition film_jobs.c:43
dt_pthread_mutex_t images_mutex
Definition film.h:47
int32_t id
Definition film.h:45
GDir * dir
Definition film.h:48
int32_t ref
Definition film.h:50
char dirname[512]
Definition film.h:46
gchar * dt_util_normalize_path(const gchar *_input)
Definition utility.c:680