Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
mipmap_cache.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2011-2012, 2015 Edouard Gomez.
4 Copyright (C) 2011-2012 Henrik Andersson.
5 Copyright (C) 2011-2017 johannes hanika.
6 Copyright (C) 2011-2012 José Carlos García Sogo.
7 Copyright (C) 2012 Christian Tellefsen.
8 Copyright (C) 2012, 2014 Jérémy Rosen.
9 Copyright (C) 2012 Richard Wonka.
10 Copyright (C) 2012-2017, 2019-2020 Tobias Ellinghaus.
11 Copyright (C) 2012, 2014 Ulrich Pegelow.
12 Copyright (C) 2013-2014, 2019-2021 Pascal Obry.
13 Copyright (C) 2013 Simon Spannagel.
14 Copyright (C) 2014, 2016 Dan Torop.
15 Copyright (C) 2014-2015 parafin.
16 Copyright (C) 2014-2016 Pedro Côrte-Real.
17 Copyright (C) 2014-2016 Roman Lebedev.
18 Copyright (C) 2015 Matthias Gehre.
19 Copyright (C) 2016-2017 Peter Budai.
20 Copyright (C) 2017 luzpaz.
21 Copyright (C) 2019-2021 Aldric Renaudin.
22 Copyright (C) 2019, 2022-2023, 2025-2026 Aurélien PIERRE.
23 Copyright (C) 2019-2020 Heiko Bauke.
24 Copyright (C) 2019 jakubfi.
25 Copyright (C) 2019-2020 Philippe Weyland.
26 Copyright (C) 2020-2022 Hanno Schwalm.
27 Copyright (C) 2020-2021 Hubert Kowalski.
28 Copyright (C) 2021 Ralf Brown.
29 Copyright (C) 2022 Martin Bařinka.
30 Copyright (C) 2022 Miloš Komarčević.
31 Copyright (C) 2023 Ricky Moon.
32 Copyright (C) 2024 Alynx Zhou.
33 Copyright (C) 2025 Guillaume Stutin.
34
35 darktable is free software: you can redistribute it and/or modify
36 it under the terms of the GNU General Public License as published by
37 the Free Software Foundation, either version 3 of the License, or
38 (at your option) any later version.
39
40 darktable is distributed in the hope that it will be useful,
41 but WITHOUT ANY WARRANTY; without even the implied warranty of
42 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
43 GNU General Public License for more details.
44
45 You should have received a copy of the GNU General Public License
46 along with darktable. If not, see <http://www.gnu.org/licenses/>.
47*/
48
49#include "common/mipmap_cache.h"
50#include "common/darktable.h"
51#include "common/debug.h"
52#include "common/exif.h"
54#include "common/grealpath.h"
55#include "common/image_cache.h"
56#include "develop/supervisor.h"
57#include "common/history.h"
58#include "common/imageio.h"
59#include "common/imageio_jpeg.h"
61#include "control/conf.h"
62#include "control/jobs.h"
64#include "gui/gtk.h"
65
66#include <assert.h>
67#include <errno.h>
68#include <fcntl.h>
69#include <glib.h>
70#include <glib/gstdio.h>
71#include <inttypes.h>
72#include <limits.h>
73#include <stdio.h>
74#include <stdlib.h>
75#include <string.h>
76#include <strings.h>
77#include <unistd.h>
78
79#if !defined(_WIN32)
80#include <sys/statvfs.h>
81#else
82//statvfs does not exist in Windows, providing implementation
83#include "win/statvfs.h"
84#endif
85
86#define DT_MIPMAP_CACHE_FILE_MAGIC 0xD71337
87#define DT_MIPMAP_CACHE_FILE_VERSION 23
88#define DT_MIPMAP_CACHE_DEFAULT_FILE_NAME "mipmaps"
89
96
97// the embedded Exif data to tag thumbnails as sRGB or AdobeRGB
98static const uint8_t dt_mipmap_cache_exif_data_srgb[] = {
99 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x49, 0x49, 0x2a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x69,
100 0x87, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
101 0x01, 0xa0, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
102};
103static const uint8_t dt_mipmap_cache_exif_data_adobergb[] = {
104 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x49, 0x49, 0x2a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x69,
105 0x87, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
106 0x01, 0xa0, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
107};
112
114{
115 uint32_t width;
116 uint32_t height;
117 float iscale;
118 size_t size;
121
122#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
123 // do not touch!
124 // must be the last element.
125 // must be no less than 16bytes
126 char redzone[16];
127#endif
128
129 /* NB: sizeof must be a multiple of 4*sizeof(float) */
130} __attribute__((packed, aligned(DT_CACHELINE_BYTES)));
131
132#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
133static const size_t dt_mipmap_buffer_dsc_size __attribute__((unused))
134= sizeof(struct dt_mipmap_buffer_dsc) - sizeof(((struct dt_mipmap_buffer_dsc *)0)->redzone);
135#else
136static const size_t dt_mipmap_buffer_dsc_size __attribute__((unused)) = sizeof(struct dt_mipmap_buffer_dsc);
137#endif
138
139static uint8_t * _get_buffer_from_dsc(struct dt_mipmap_buffer_dsc *dsc);
140
141static inline void *dead_image_8(struct dt_mipmap_buffer_dsc *dsc)
142{
143 dsc->width = dsc->height = 8;
144 dsc->iscale = 1.0f;
146 assert(dsc->size > 64 * sizeof(uint32_t));
147 const uint32_t X = 0xffffffffu;
148 const uint32_t o = 0u;
149 const uint32_t image[]
150 = { o, o, o, o, o, o, o, o,
151 o, o, X, X, X, X, o, o,
152 o, X, o, X, X, o, X, o,
153 o, X, X, X, X, X, X, o,
154 o, o, X, o, o, X, o, o,
155 o, o, o, o, o, o, o, o,
156 o, o, X, X, X, X, o, o,
157 o, o, o, o, o, o, o, o };
158 memcpy(_get_buffer_from_dsc(dsc), image, sizeof(uint32_t) * 64);
159 return _get_buffer_from_dsc(dsc);
160}
161
162static inline int32_t get_key(const int32_t imgid, const dt_mipmap_size_t size)
163{
164 // imgid can't be >= 2^28 (~250 million images)
165 return (((int32_t)size) << 28) | (imgid - 1);
166}
167
168static inline uint32_t get_imgid(const uint32_t key)
169{
170 return (key & 0xfffffff) + 1;
171}
172
173static inline dt_mipmap_size_t get_size(const uint32_t key)
174{
175 return (dt_mipmap_size_t)(key >> 28);
176}
177
179{
180 g_snprintf(path, sizeof(char) * PATH_MAX, "%s.d" G_DIR_SEPARATOR_S "%d",
181 cache->cachedir, (int)mip);
182}
183
184void dt_mipmap_get_cache_filename(char path[PATH_MAX], const dt_mipmap_cache_t *cache, dt_mipmap_size_t mip, const int32_t imgid)
185{
186 gchar cache_path[PATH_MAX];
187 dt_mipmap_get_cache_dir(cache_path, cache, mip);
188
189 gchar *file = g_strdup_printf("%u.jpg", imgid);
190 dt_concat_path_file(path, cache_path, file);
191 dt_free(file);
192}
193
194static int dt_mipmap_cache_get_filename(gchar *mipmapfilename, size_t size)
195{
196 int r = -1;
197 char *abspath = NULL;
198
199 // Directory
200 char cachedir[PATH_MAX] = { 0 };
201 dt_loc_get_user_cache_dir(cachedir, sizeof(cachedir));
202
203 // Build the mipmap filename fram hashing the path of the library DB
204 const gchar *dbfilename = dt_database_get_path(darktable.db);
205
206 if(!strcmp(dbfilename, ":memory:"))
207 {
208 mipmapfilename[0] = '\0';
209 r = 0;
210 goto exit;
211 }
212
213 abspath = g_realpath(dbfilename);
214 if(IS_NULL_PTR(abspath)) abspath = g_strdup(dbfilename);
215
216 GChecksum *chk = g_checksum_new(G_CHECKSUM_SHA1);
217 g_checksum_update(chk, (guchar *)abspath, strlen(abspath));
218 const gchar *filename = g_checksum_get_string(chk);
219
220 if(!filename || filename[0] == '\0')
221 snprintf(mipmapfilename, size, "%s/%s", cachedir, DT_MIPMAP_CACHE_DEFAULT_FILE_NAME);
222 else
223 snprintf(mipmapfilename, size, "%s/%s-%s", cachedir, DT_MIPMAP_CACHE_DEFAULT_FILE_NAME, filename);
224
225 g_checksum_free(chk);
226 r = 0;
227
228exit:
229 dt_free(abspath);
230
231 return r;
232}
233
248static void _write_mipmap_to_disk(const int32_t imgid, char *filename, char *ext, gboolean *input_exists,
249 gboolean *is_jpg_input, gboolean *use_embedded_jpg, gboolean *write_to_disk)
250{
251 // Get file name
252 char _filename[PATH_MAX] = { 0 };
253 const dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
254 if(filename || ext || input_exists || is_jpg_input)
255 {
256 const dt_image_path_source_t source = dt_image_choose_input_path(img, _filename, sizeof(_filename), FALSE);
257 if(source == DT_IMAGE_PATH_NONE) _filename[0] = '\0';
258 if(filename) strncpy(filename, _filename, PATH_MAX);
259 if(input_exists) *input_exists = (source != DT_IMAGE_PATH_NONE);
260 }
261
262 // Get the file extension
263 if(ext || is_jpg_input)
264 {
265 char *_ext = _filename + strlen(_filename);
266 while(*_ext != '.' && _ext > _filename) _ext--;
267 if(ext) strncpy(ext, _ext, 5);
268 if(is_jpg_input) *is_jpg_input = !strcasecmp(_ext, ".jpg") || !strcasecmp(_ext, ".jpeg");
269 }
270
271 // embedded JPG mode:
272 // 0 = never use embedded thumbnail
273 // 1 = only on unedited pics,
274 // 2 = always use embedded thumbnail
275 int mode = dt_conf_get_int("lighttable/embedded_jpg");
276 gboolean altered = FALSE;
277 if(mode == 1)
278 {
279 if(img) altered = img->history_items > 0;
280 }
281 const gboolean _use_embedded_jpg
282 = (mode == 2 // always use embedded
283 || (mode == 1 && !altered)); // use embedded only on unaltered images
284 if(use_embedded_jpg) *use_embedded_jpg = _use_embedded_jpg;
285
286 // Save to cache only if the option is enabled by user and the thumbnail is not the embedded thumbnail
287 // The rationale is getting the un-processed JPEG thumbnail is cheap and not worth saving on disk.
288 // This allows fast toggling between JPEG and processed RAW thumbnail from GUI.
289 if(write_to_disk)
290 {
291 *write_to_disk = dt_conf_get_bool("cache_disk_backend");
292 }
293
294 if(img)
296}
297
298
299static void _init_f(dt_mipmap_buffer_t *mipmap_buf, float *buf, uint32_t *width, uint32_t *height, float *iscale,
300 const int32_t imgid);
301static void _init_8(uint8_t *buf, uint32_t *width, uint32_t *height, float *iscale,
303 const dt_mipmap_size_t size, dt_atomic_int *shutdown);
304
338// Gets the address of the `buf->buf` pixel buffer, from the cache entry.
339static uint8_t *_get_buffer_from_dsc(struct dt_mipmap_buffer_dsc *dsc)
340{
341 if(dsc)
342 return (uint8_t *)(dsc + 1);
343 else
344 return NULL;
345}
346
347
349{
350 return (struct dt_mipmap_buffer_dsc *)entry->data;
351}
352
353// update buffer params with dsc
354static void _sync_dsc_to_buf(dt_mipmap_buffer_t *buf, struct dt_mipmap_buffer_dsc *dsc, const int32_t imgid,
355 const dt_mipmap_size_t mip)
356{
357 buf->width = dsc->width;
358 buf->height = dsc->height;
359 buf->iscale = dsc->iscale;
360 buf->color_space = dsc->color_space;
361 buf->imgid = imgid;
362 buf->size = mip;
363 buf->buf = _get_buffer_from_dsc(dsc);
364}
365
366
367// flag a buffer as invalid and set it NULL
368// This doesn't paint skulls
370{
371 buf->width = buf->height = 0;
372 buf->iscale = 0.0f;
373 buf->buf = NULL;
374}
375
376
377static size_t _get_entry_size(const size_t buffer_size)
378{
379 return buffer_size + dt_mipmap_buffer_dsc_size;
380}
381
390 const size_t width, const size_t height,
391 const size_t buffer_size)
392{
393 if(IS_NULL_PTR(entry->data))
394 {
395 entry->data_size = 0;
396 *dsc = NULL;
397 return;
398 }
399
400 // Update the entry datasize
401 entry->data_size = _get_entry_size(buffer_size);
402
403 // Update the dsc parameters
404 *dsc = _get_dsc_from_entry(entry);
405 (*dsc)->width = width;
406 (*dsc)->height = height;
407 (*dsc)->iscale = 1.0f;
408 (*dsc)->color_space = DT_COLORSPACE_NONE;
410 (*dsc)->size = _get_entry_size(buffer_size);
411}
412
413
414// callback for the imageio core to allocate memory.
415// only needed for _FULL buffers, as they change size
416// with the input image. will allocate img->width*img->height*img->bpp bytes.
418{
419 if(IS_NULL_PTR(buf)) return NULL;
420
421 if(buf->size != DT_MIPMAP_FULL)
422 {
423 fprintf(stderr, "trying to alloc a wrong mipmap size for %s: %i (should be: %i)\n", img->filename, buf->size, DT_MIPMAP_FULL);
424 return NULL;
425 }
426
427 dt_cache_entry_t *entry = buf->cache_entry;
428
429 if(IS_NULL_PTR(entry))
430 {
431 fprintf(stderr, "trying to alloc a buffer entry that has no back-reference to cache entry\n");
432 return NULL;
433 }
434
435 /* Free and reset everything. ASan poisoning applies to live cache payloads only: after freeing
436 * the old allocation there is no valid user region left, and poisoning a NULL reset pointer makes
437 * the ASan runtime abort before it can report the real caller context. */
438 if(!IS_NULL_PTR(entry->data))
440 dt_free_align(entry->data);
441 entry->data = NULL;
442
443 // Get a new allocation
444 const int wd = img->width;
445 const int ht = img->height;
446 const size_t bpp = img->dsc.bpp;
447 const size_t buffer_size = wd * ht * bpp;
448 const size_t min_buffer_size = 64 * 4 * sizeof(float);
449 entry->data = dt_alloc_align(_get_entry_size(MAX(buffer_size, min_buffer_size)));
450 if(IS_NULL_PTR(entry->data)) return NULL;
451
452 // Update the references
453 struct dt_mipmap_buffer_dsc *dsc = NULL;
454 dt_mipmap_cache_update_buffer_addresses(entry, &dsc, wd, ht, buffer_size);
455
456 // Unpoison the pixel buffer region
457 ASAN_UNPOISON_MEMORY_REGION(buf->buf, dsc->size - dt_mipmap_buffer_dsc_size);
458
459 // Unpoison the dsc region
460 ASAN_UNPOISON_MEMORY_REGION(dsc, dt_mipmap_buffer_dsc_size);
461
462 // So the padding region between dsc and buf->buf should be poisoned still
463 assert(entry->data == dsc);
464
465 if(dsc)
466 {
467 assert(entry->data_size == dsc->size);
468 }
469
470 // return pointer to start of payload
471 return _get_buffer_from_dsc(dsc);
472}
473
474// callback for the cache backend to initialize payload pointers
475// It's actually not dynamic at all, fixed size only.
477{
478 dt_mipmap_cache_t *cache = (dt_mipmap_cache_t *)data;
479 const dt_mipmap_size_t mip = get_size(entry->key);
480 const int32_t imgid = get_imgid(entry->key);
481
482 assert(mip < DT_MIPMAP_NONE);
483
484 // Free and reset everything
485 dt_free_align(entry->data);
486 entry->data = NULL;
487
488 // Get a new allocation
489 const size_t buffer_size = (mip <= DT_MIPMAP_F) ? cache->buffer_size[mip] : _get_entry_size(sizeof(float) * 4 * 64);
490 entry->data = dt_alloc_align(buffer_size);
491 if(IS_NULL_PTR(entry->data)) return;
492
493 // Update the references
494 struct dt_mipmap_buffer_dsc *dsc = NULL;
495 if(mip <= DT_MIPMAP_F)
496 dt_mipmap_cache_update_buffer_addresses(entry, &dsc, cache->max_width[mip], cache->max_height[mip], buffer_size);
497 else
498 dt_mipmap_cache_update_buffer_addresses(entry, &dsc, 0, 0, buffer_size);
499
500 if(IS_NULL_PTR(dsc)) return;
501
502 // Register the mipmap object, and the image cache object it links to. The image
503 // is usually already cached (so its allocate callback never fired), hence we
504 // register it here from the live dt_image_t; dt_supervisor_image() promotes the
505 // first sighting to a `create` so the link resolves.
507 {
508 const dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
509 if(img) dt_supervisor_image(DT_SV_UPDATE, imgid, img);
512 }
513
514 gboolean write_to_disk;
515 _write_mipmap_to_disk(imgid, NULL, NULL, NULL, NULL, NULL, &write_to_disk);
516
517 if(cache->cachedir[0] && write_to_disk && mip < DT_MIPMAP_F)
518 {
519 // try and load from disk, if successful set flag
520 char filename[PATH_MAX] = {0};
521 dt_mipmap_get_cache_filename(filename, cache, mip, get_imgid(entry->key));
522
523 gboolean io_error = FALSE;
524 gchar *error = NULL;
525 uint8_t *blob = NULL;
528 FILE *f = NULL;
529
530 f = g_fopen(filename, "rb");
531 if(IS_NULL_PTR(f))
532 {
534 "[mipmap_cache] cached file for image %d at mip size %i does not exist\n",
535 imgid, mip);
536 goto finish; // file doesn't exist
537 }
538
539 fseek(f, 0, SEEK_END);
540 long len = ftell(f);
541 if(len <= 0)
542 {
543 error = "empty file";
544 io_error = TRUE;
545 goto finish;
546 }
547
548 blob = (uint8_t *)dt_alloc_align(len);
549 if(IS_NULL_PTR(blob))
550 {
551 error = "out of memory";
552 io_error = TRUE;
553 goto finish;
554 }
555
556 fseek(f, 0, SEEK_SET);
557 const size_t rd = fread(blob, sizeof(uint8_t), len, f);
558 if(rd != len)
559 {
560 error = "corrupted file";
561 io_error = TRUE;
562 goto finish;
563 }
564
565 if(dt_imageio_jpeg_decompress_header(blob, len, &jpg))
566 {
567 error = "couldn't decompress header";
568 io_error = TRUE;
569 goto finish;
570 }
571
573
575 {
576 error = "couldn't decompress JPEG";
577 io_error = TRUE;
578 goto finish;
579 }
580
581 dsc->width = jpg.width;
582 dsc->height = jpg.height;
583 dsc->iscale = 1.0f;
585 dsc->flags = 0;
586
587 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] image %d at mip size %d (%ix%i) loaded from disk cache\n",
588 imgid, mip, jpg.width, jpg.height);
589
590finish:
591 if(f && io_error)
592 {
593 // Delete the file, we will regenerate it
594 g_unlink(filename);
595 fprintf(stderr, "[mipmap_cache] failed to open thumbnail for image %" PRIu32 " from `%s'. Reason: %s\n",
596 imgid, filename, error);
597 }
598
599 dt_free_align(blob);
600 if(f) fclose(f);
601 }
602
603 // cost is just flat one for the buffer, as the buffers might have different sizes,
604 // to make sure quota is meaningful.
605 if(mip >= DT_MIPMAP_F)
606 entry->cost = 1;
607 else
608 entry->cost = cache->buffer_size[mip];
609}
610
611static void dt_mipmap_cache_unlink_ondisk_thumbnail(void *data, int32_t imgid, dt_mipmap_size_t mip)
612{
613 dt_mipmap_cache_t *cache = (dt_mipmap_cache_t *)data;
614
615 // also remove jpg backing (always try to do that, in case user just temporarily switched it off,
616 // to avoid inconsistencies.
617 // if(dt_conf_get_bool("cache_disk_backend"))
618 if(cache->cachedir[0])
619 {
620 char filename[PATH_MAX] = { 0 };
621 dt_mipmap_get_cache_filename(filename, cache, mip, imgid);
622 g_unlink(filename);
623 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] image %i for size %i was deleted from disk cache\n", imgid, mip);
624 }
625}
626
628{
629 dt_mipmap_cache_t *cache = (dt_mipmap_cache_t *)data;
630 const dt_mipmap_size_t mip = get_size(entry->key);
631
634 if(mip < DT_MIPMAP_F)
635 {
636 const int32_t imgid = get_imgid(entry->key);
637 gboolean write_to_disk;
638 _write_mipmap_to_disk(imgid, NULL, NULL, NULL, NULL, NULL, &write_to_disk);
639
640 struct dt_mipmap_buffer_dsc *dsc = _get_dsc_from_entry(entry);
641 // don't write skulls:
642 if(dsc->width > 8 && dsc->height > 8)
643 {
645 {
647 }
648 if(cache->cachedir[0] && write_to_disk && mip < DT_MIPMAP_F)
649 {
650 // serialize to disk
651 gchar cache_path[PATH_MAX];
652 dt_mipmap_get_cache_dir(cache_path, cache, mip);
653 const int mkd = g_mkdir_with_parents(cache_path, 0750);
654
655 if(!mkd)
656 {
657 char filename[PATH_MAX] = {0};
658 dt_mipmap_get_cache_filename(filename, cache, mip, get_imgid(entry->key));
659 // Don't write existing files as both performance and quality (lossy jpg) suffer
660 // FIXME: actually, yes, we write existing files too. See FIXME above.
661 FILE *f = NULL;
662 if((f = g_fopen(filename, "wb"))) // !g_file_test(filename, G_FILE_TEST_EXISTS)
663 {
664 // first check the disk isn't full
665 struct statvfs vfsbuf;
666 if (!statvfs(filename, &vfsbuf))
667 {
668 const int64_t free_mb = ((vfsbuf.f_frsize * vfsbuf.f_bavail) >> 20);
669 if (free_mb < 100)
670 {
671 fprintf(stderr, "Aborting image write as only %" PRId64 " MB free to write %s\n", free_mb, filename);
672 goto write_error;
673 }
674 }
675 else
676 {
677 fprintf(stderr, "Aborting image write since couldn't determine free space available to write %s\n", filename);
678 goto write_error;
679 }
680
681 const int cache_quality = dt_conf_get_int("database_cache_quality");
682 const uint8_t *exif = NULL;
683 int exif_len = 0;
685 {
688 }
689 else if(dsc->color_space == DT_COLORSPACE_ADOBERGB)
690 {
693 }
694 if(dt_imageio_jpeg_write(filename, _get_buffer_from_dsc(dsc), dsc->width, dsc->height,
695 MIN(100, MAX(10, cache_quality)), exif, exif_len))
696 {
697write_error:
698 g_unlink(filename);
699 }
700 else
701 {
702 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] image %i for size %i was written to cache at %s\n", imgid, mip, filename);
703 }
704 }
705 if(f) fclose(f);
706 }
707 }
708 }
709 }
710 dt_free_align(entry->data);
711 entry->data = NULL;
712}
713
715{
716 dt_mipmap_cache_get_filename(cache->cachedir, sizeof(cache->cachedir));
717
718 // Fixed sizes for the thumbnail mip levels, selected for coverage of most screen sizes
719 // Starting at 4K, we use 3:2 ratio of camera sensor instead of 16:9/16:10 of display,
720 // in order to start caching full-resolution JPEG for 100% zoom in lighttable
721 size_t mipsizes[DT_MIPMAP_F][2] = {
722 { 360, 225 }, // mip0 - 1/2 size previous one
723 { 720, 450 }, // mip1 - 1/2 size previous one
724 { 1440, 900 }, // mip2 - covers HD, WXGA+
725 { 1920, 1200 }, // mip3 - covers 1080p and 1600x1200
726 { 2560, 1600 }, // mip4 - covers 2560x1440
727 { 3840, 2560 }, // mip5 - covers 4K and UHD
728 { 5120, 3414 }, // mip6 - covers 5K
729 { 6144, 4096 }, // mip7 - covers 6K
730 { 7680, 5120 }, // mip8 - covers 8K
731 };
732 // Set mipf for preview pipe to 1440x900
733 cache->max_width[DT_MIPMAP_F] = mipsizes[DT_MIPMAP_2][0];
734 cache->max_height[DT_MIPMAP_F] = mipsizes[DT_MIPMAP_2][1];
735 for(int k = DT_MIPMAP_F-1; k >= 0; k--)
736 {
737 cache->max_width[k] = mipsizes[k][0];
738 cache->max_height[k] = mipsizes[k][1];
739 }
740 // header + buffer
741 for(int k = DT_MIPMAP_F-1; k >= 0; k--)
742 cache->buffer_size[k] = _get_entry_size(cache->max_width[k] * cache->max_height[k] * 4);
743
744 // clear stats:
745 cache->mip_thumbs.stats_requests = 0;
746 cache->mip_thumbs.stats_near_match = 0;
747 cache->mip_thumbs.stats_misses = 0;
748 cache->mip_thumbs.stats_fetches = 0;
749 cache->mip_thumbs.stats_standin = 0;
750 cache->mip_f.stats_requests = 0;
751 cache->mip_f.stats_near_match = 0;
752 cache->mip_f.stats_misses = 0;
753 cache->mip_f.stats_fetches = 0;
754 cache->mip_f.stats_standin = 0;
755 cache->mip_full.stats_requests = 0;
756 cache->mip_full.stats_near_match = 0;
757 cache->mip_full.stats_misses = 0;
758 cache->mip_full.stats_fetches = 0;
759 cache->mip_full.stats_standin = 0;
760
764
768 cache->buffer_size[DT_MIPMAP_FULL] = 0;
769
774 = _get_entry_size(4 * sizeof(float) * cache->max_width[DT_MIPMAP_F] * cache->max_height[DT_MIPMAP_F]);
775}
776
784
786{
787 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] thumbs fill %.2f/%.2f MB (%.2f%%)\n",
788 cache->mip_thumbs.cache.cost / (1024.0 * 1024.0),
789 cache->mip_thumbs.cache.cost_quota / (1024.0 * 1024.0),
790 100.0f * (float)cache->mip_thumbs.cache.cost / (float)cache->mip_thumbs.cache.cost_quota);
791 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] float fill %"PRIu32"/%"PRIu32" slots (%.2f%%)\n",
792 (uint32_t)cache->mip_f.cache.cost, (uint32_t)cache->mip_f.cache.cost_quota,
793 100.0f * (float)cache->mip_f.cache.cost / (float)cache->mip_f.cache.cost_quota);
794 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] full fill %"PRIu32"/%"PRIu32" slots (%.2f%%)\n",
795 (uint32_t)cache->mip_full.cache.cost, (uint32_t)cache->mip_full.cache.cost_quota,
796 100.0f * (float)cache->mip_full.cache.cost / (float)cache->mip_full.cache.cost_quota);
797
798 uint64_t sum = 0;
799 uint64_t sum_fetches = 0;
800 uint64_t sum_standins = 0;
801 sum += cache->mip_thumbs.stats_requests;
802 sum_fetches += cache->mip_thumbs.stats_fetches;
803 sum_standins += cache->mip_thumbs.stats_standin;
804 sum += cache->mip_f.stats_requests;
805 sum_fetches += cache->mip_f.stats_fetches;
806 sum_standins += cache->mip_f.stats_standin;
807 sum += cache->mip_full.stats_requests;
808 sum_fetches += cache->mip_full.stats_fetches;
809 sum_standins += cache->mip_full.stats_standin;
810 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] level | near match | miss | stand-in | fetches | total rq\n");
811 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] thumb | %6.2f%% | %6.2f%% | %6.2f%% | %6.2f%% | %6.2f%%\n",
812 100.0 * cache->mip_thumbs.stats_near_match / (float)cache->mip_thumbs.stats_requests,
813 100.0 * cache->mip_thumbs.stats_misses / (float)cache->mip_thumbs.stats_requests,
814 100.0 * cache->mip_thumbs.stats_standin / (float)sum_standins,
815 100.0 * cache->mip_thumbs.stats_fetches / (float)sum_fetches,
816 100.0 * cache->mip_thumbs.stats_requests / (float)sum);
817 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] float | %6.2f%% | %6.2f%% | %6.2f%% | %6.2f%% | %6.2f%%\n",
818 100.0 * cache->mip_f.stats_near_match / (float)cache->mip_f.stats_requests,
819 100.0 * cache->mip_f.stats_misses / (float)cache->mip_f.stats_requests,
820 100.0 * cache->mip_f.stats_standin / (float)sum_standins,
821 100.0 * cache->mip_f.stats_fetches / (float)sum_fetches,
822 100.0 * cache->mip_f.stats_requests / (float)sum);
823 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] full | %6.2f%% | %6.2f%% | %6.2f%% | %6.2f%% | %6.2f%%\n",
824 100.0 * cache->mip_full.stats_near_match / (float)cache->mip_full.stats_requests,
825 100.0 * cache->mip_full.stats_misses / (float)cache->mip_full.stats_requests,
826 100.0 * cache->mip_full.stats_standin / (float)sum_standins,
827 100.0 * cache->mip_full.stats_fetches / (float)sum_fetches,
828 100.0 * cache->mip_full.stats_requests / (float)sum);
829 dt_print(DT_DEBUG_CACHE, "\n\n");
830}
831
832// Collect one sub-cache's live entries (imgid + mip + real bytes) under its lock.
834{
835 dt_pthread_mutex_lock(&c->lock);
836 GHashTableIter it;
837 gpointer key, value;
838 g_hash_table_iter_init(&it, c->hashtable);
839 while(g_hash_table_iter_next(&it, &key, &value))
840 {
841 const dt_cache_entry_t *e = (const dt_cache_entry_t *)value;
842 if(!e) continue;
844 s.imgid = get_imgid(e->key);
845 s.mip = (int)get_size(e->key);
846 s.size = e->data_size; // real allocation (cost is byte-based for thumbs, slot-based for f/full)
847 g_array_append_val(out, s);
848 }
849 dt_pthread_mutex_unlock(&c->lock);
850}
851
852void dt_mipmap_cache_get_usage(dt_mipmap_cache_t *cache, size_t *current, size_t *max)
853{
854 if(current) *current = 0;
855 if(max) *max = 0;
856 if(IS_NULL_PTR(cache)) return;
857
858 // Real bytes currently held = sum of entry allocations across sub-caches.
860 size_t used = 0;
861 for(guint i = 0; i < entries->len; i++)
862 used += g_array_index(entries, dt_mipmap_cache_stats_entry_t, i).size;
863 g_array_free(entries, TRUE);
864
865 if(current) *current = used;
866 if(max) *max = darktable.dtresources.mipmap_memory; // user-specified mipmap RAM budget
867}
868
870{
871 GArray *out = g_array_new(FALSE, FALSE, sizeof(dt_mipmap_cache_stats_entry_t));
872 if(IS_NULL_PTR(cache)) return out;
876 return out;
877}
878
880{
881 switch(mip)
882 {
883 case DT_MIPMAP_FULL:
884 return &cache->mip_full;
885 case DT_MIPMAP_F:
886 return &cache->mip_f;
887 default:
888 return &cache->mip_thumbs;
889 }
890}
891
892// if we get a zero-sized image, paint skulls to signal a missing image
893static void _paint_skulls(dt_mipmap_buffer_t *buf, struct dt_mipmap_buffer_dsc *dsc, const int32_t imgid, const dt_mipmap_size_t mip)
894{
895 if(dsc->width == 0 || dsc->height == 0)
896 {
897 dt_print(DT_DEBUG_CACHE, "[mipmap cache get] got a zero-sized image for img %u mip %d!\n", imgid, mip);
898 if(mip < DT_MIPMAP_F)
899 buf->buf = dead_image_8(dsc);
900 else
901 buf->buf = NULL; // full images with NULL buffer have to be handled, indicates `missing image', but still return locked slot
902 }
903}
904
905static void _validate_buffer(dt_mipmap_buffer_t *buf, struct dt_mipmap_buffer_dsc *dsc, const int32_t imgid,
906 const dt_mipmap_size_t mip)
907{
908 // Unpoison the dsc region
909 ASAN_UNPOISON_MEMORY_REGION(dsc, dt_mipmap_buffer_dsc_size);
910
911 // May update buf->buf:
912 _sync_dsc_to_buf(buf, dsc, imgid, mip);
913
914 // Unpoison the pixel buffer region
915 ASAN_UNPOISON_MEMORY_REGION(buf->buf, dsc->size - dt_mipmap_buffer_dsc_size);
916}
917
918// Grab our own local copy of the image structure
919static gboolean _get_image_copy(const int32_t imgid, dt_image_t *buffered_image)
920{
921 // load the image:
922 // make sure we access the r/w lock as shortly as possible!
923 gboolean no_buffer = TRUE;
924 const dt_image_t *cimg = dt_image_cache_get(darktable.image_cache, imgid, 'r');
925
926 if(cimg)
927 {
928 *buffered_image = *cimg;
929 no_buffer = FALSE;
930 }
931
933
934 return no_buffer;
935}
936
937// Actually do the work: produce an image
939 const int32_t imgid, const dt_mipmap_size_t mip, dt_atomic_int *shutdown)
940{
941 struct dt_mipmap_buffer_dsc *dsc = _get_dsc_from_entry(entry);
942 // Unpoison the descriptor before reading any field in this function.
943 ASAN_UNPOISON_MEMORY_REGION(dsc, dt_mipmap_buffer_dsc_size);
944 // _generate_blocking() can write directly into the cache pixel payload
945 // (notably through _init_8() -> _write_image()), so unpoison it up-front.
946 ASAN_UNPOISON_MEMORY_REGION(_get_buffer_from_dsc(dsc), dsc->size - dt_mipmap_buffer_dsc_size);
947 const uint32_t original_width = dsc->width;
948 const uint32_t original_height = dsc->height;
949 const float original_iscale = dsc->iscale;
950 const dt_colorspaces_color_profile_type_t original_color_space = dsc->color_space;
951
953 {
954 // Already in cache, no I/O needed
955 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] image %d at mip size %i (%ix%i) will skip disk I/O, found in RAM cache.\n", imgid, mip,
956 dsc->width, dsc->height);
957 _validate_buffer(buf, dsc, imgid, mip);
958 return;
959 }
960
961 if(mip == DT_MIPMAP_FULL)
962 {
963 dt_image_t buffered_image;
964 if(_get_image_copy(imgid, &buffered_image)) return;
965
966 // Get the input file path, possibly from our local cache of copied images
967 char filename[PATH_MAX] = { 0 };
968 dt_image_path_source_t source = dt_image_choose_input_path(&buffered_image, filename, sizeof(filename), FALSE);
969 if(source == DT_IMAGE_PATH_NONE) return;
970
972 "[mipmap_cache] fetch image %i at mip size %d float32 (%ix%i) from original file I/O\n",
973 imgid, mip, dsc->width, dsc->height);
974
975 // will call dt_mipmap_cache_alloc() internally:
976 dt_imageio_retval_t ret = dt_imageio_open(&buffered_image, filename, buf);
977
978 // We need to update dsc because dt_imageio_open re-alloc entry->data
979 dsc = _get_dsc_from_entry(entry);
980
981 if(ret == DT_IMAGEIO_OK)
982 {
983 // swap back new image data, may contain updated EXIF & colorspace
985 // dt_image_cache_get() returns NULL if the image vanished from the DB/cache while this
986 // full-res I/O was in flight (e.g. removed from the library concurrently) -- nothing left
987 // to swap the freshly-read data back into.
988 if(!IS_NULL_PTR(img))
989 {
990 *img = buffered_image;
992 }
993 }
994 else
995 {
996 dsc->width = dsc->height = 0;
997 dsc->iscale = 0.f;
998 }
999 }
1000 else if(mip == DT_MIPMAP_F)
1001 {
1003 "[mipmap_cache] compute mip size %d float32 for image %i (%ix%i) from original file \n", mip,
1004 imgid, dsc->width, dsc->height);
1005 _init_f(buf, (float *)_get_buffer_from_dsc(dsc), &dsc->width, &dsc->height, &dsc->iscale, imgid);
1006 }
1007 else
1008 {
1009 // 8-bit thumbs
1011 "[mipmap_cache] compute mip size %d uint8 for image %i (%ix%i) from original file \n", mip,
1012 imgid, dsc->width, dsc->height);
1013 _init_8((uint8_t *)_get_buffer_from_dsc(dsc), &dsc->width, &dsc->height, &dsc->iscale, &dsc->color_space, imgid,
1014 mip, shutdown);
1015 }
1016
1017 if(shutdown && dt_atomic_get_int(shutdown))
1018 {
1019 /* A stale GUI request stopped its thumbnail/export pipe while this cache entry
1020 * was still being produced. Keep the entry marked as "generate" so the next
1021 * request can restart from a clean state instead of reusing a poisoned empty
1022 * thumbnail for the rest of the session. */
1023 dsc->width = original_width;
1024 dsc->height = original_height;
1025 dsc->iscale = original_iscale;
1026 dsc->color_space = original_color_space;
1027 _invalidate_buffer(buf);
1028 return;
1029 }
1030
1031 dsc->flags &= ~DT_MIPMAP_BUFFER_DSC_FLAG_GENERATE;
1032 _paint_skulls(buf, dsc, imgid, mip);
1033 _validate_buffer(buf, dsc, imgid, mip);
1034
1035 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] image %d at mip size %d got a new cache entry (%ix%i / %ix%i) at %p\n", imgid, mip,
1036 buf->width, buf->height, dsc->width, dsc->height, buf->buf);
1037}
1038
1039
1042 const char mode, const char *file, int line)
1043{
1044 dt_mipmap_cache_get_with_caller_and_shutdown(cache, buf, imgid, mip, flags, mode, NULL, file, line);
1045}
1046
1048 const int32_t imgid, const dt_mipmap_size_t mip,
1049 const dt_mipmap_get_flags_t flags, const char mode,
1050 dt_atomic_int *shutdown, const char *file, int line)
1051{
1052 assert(mip <= DT_MIPMAP_NONE && mip >= DT_MIPMAP_0);
1053
1054 const int32_t key = get_key(imgid, mip);
1055
1056 // Allocation functions will check those if we run them
1057 buf->imgid = imgid;
1058 buf->size = mip;
1059
1061 {
1062 // simple case: only get and lock if it's there.
1063 dt_cache_entry_t *entry = dt_cache_testget(&_get_cache(cache, mip)->cache, key, mode);
1064 buf->cache_entry = entry;
1065
1066 if(entry)
1067 _validate_buffer(buf, _get_dsc_from_entry(entry), imgid, mip);
1068 else
1069 _invalidate_buffer(buf);
1070 }
1071 else if(flags == DT_MIPMAP_BLOCKING)
1072 {
1073 // simple case: blocking get
1074 dt_cache_entry_t *entry = dt_cache_get_with_caller(&_get_cache(cache, mip)->cache, key, mode, file, line);
1075 buf->cache_entry = entry;
1076 __sync_fetch_and_add(&(_get_cache(cache, mip)->stats_fetches), 1);
1077 _generate_blocking(entry, buf, imgid, mip, shutdown);
1078
1079 // image cache is leaving the write lock in place in case the image has been newly allocated.
1080 // this leads to a slight increase in thread contention, so we opt for dropping the write lock
1081 // and acquiring a read lock immediately after. since this opens a small window for other threads
1082 // to get in between, we need to take some care to re-init cache entries and dsc.
1083 // note that concurrencykit has rw locks that can be demoted from w->r without losing the lock in between.
1084 if(mode == 'r')
1085 {
1086 entry->_lock_demoting = 1;
1087 // drop the write lock
1088 dt_cache_release(&_get_cache(cache, mip)->cache, entry);
1089 // get a read lock
1090 entry = dt_cache_get(&_get_cache(cache, mip)->cache, key, mode);
1091 entry->_lock_demoting = 0;
1092 }
1093
1094 buf->cache_entry = entry;
1095
1096 // The cache can't be locked twice from the same thread,
1097 // because then it would never unlock.
1098#ifdef _DEBUG
1099 const pthread_t writer = dt_pthread_rwlock_get_writer(&(buf->cache_entry->lock));
1100 if(mode == 'w')
1101 {
1102 assert(pthread_equal(writer, pthread_self()));
1103 }
1104 else
1105 {
1106 assert(!pthread_equal(writer, pthread_self()));
1107 }
1108#endif
1109 }
1110 else
1111 {
1112 _invalidate_buffer(buf);
1113 }
1114}
1115
1116void dt_mipmap_cache_write_get_with_caller(dt_mipmap_cache_t *cache, dt_mipmap_buffer_t *buf, const int32_t imgid, const int mip, const char *file, int line)
1117{
1118 dt_mipmap_cache_get_with_caller(cache, buf, imgid, mip, DT_MIPMAP_BLOCKING, 'w', file, line);
1119}
1120
1122 int line)
1123{
1124 if(buf->size == DT_MIPMAP_NONE || IS_NULL_PTR(buf->cache_entry)) return;
1125 assert(buf->imgid > 0);
1126 assert(buf->size >= DT_MIPMAP_0);
1127 assert(buf->size < DT_MIPMAP_NONE);
1128 dt_cache_release_with_caller(&_get_cache(cache, buf->size)->cache, buf->cache_entry, file, line);
1129 buf->size = DT_MIPMAP_NONE;
1130 buf->buf = NULL;
1131}
1132
1133
1134// return index dt_mipmap_size_t having at least width & height requested instead of minimum combined diff
1135// please note that the requested size is in pixels not dots.
1137 const int32_t height, const uint32_t imgid)
1138{
1139 for(int k = DT_MIPMAP_0; k < DT_MIPMAP_F; k++)
1140 {
1141 // We assume a "fit" situation, typically rectangle within square
1142 // so we don't need both dimensions to be greater than requested
1143 if((cache->max_width[k] >= width) || (cache->max_height[k] >= height))
1144 {
1145 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] image %d will load a mip size %i (%" G_GSIZE_FORMAT "x%" G_GSIZE_FORMAT ")\n", imgid, k, cache->max_width[k], cache->max_height[k]);
1146 return k;
1147 }
1148 }
1149 return DT_MIPMAP_F - 1;
1150}
1151
1153 const int32_t height, const uint32_t imgid)
1154{
1155 for(int k = DT_MIPMAP_F - 1; k >= DT_MIPMAP_0; k--)
1156 {
1157 if((cache->max_width[k] <= width) && (cache->max_height[k] <= height))
1158 {
1159 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] image %d will fit a mip size %i (%" G_GSIZE_FORMAT "x%" G_GSIZE_FORMAT ")\n", imgid, k, cache->max_width[k], cache->max_height[k]);
1160 return k;
1161 }
1162 }
1163 return DT_MIPMAP_0;
1164}
1165
1166void dt_mipmap_cache_swap_at_size(dt_mipmap_cache_t *cache, const int32_t imgid, const dt_mipmap_size_t mip, const uint8_t *const in,
1167 const int32_t width, const int32_t height, dt_colorspaces_color_profile_type_t profile)
1168{
1169 if(mip >= DT_MIPMAP_F || mip < DT_MIPMAP_0) return;
1170
1171 const uint32_t key = get_key(imgid, mip);
1172 dt_cache_entry_t *entry = dt_cache_get_with_caller(&_get_cache(cache, mip)->cache, key, 'w', __FILE__, __LINE__);
1173 if(entry)
1174 {
1175 struct dt_mipmap_buffer_dsc *dsc = _get_dsc_from_entry(entry);
1176 // Unpoison descriptor and pixel payload before reading/writing either.
1177 ASAN_UNPOISON_MEMORY_REGION(dsc, dt_mipmap_buffer_dsc_size);
1178 ASAN_UNPOISON_MEMORY_REGION(_get_buffer_from_dsc(dsc), dsc->size - dt_mipmap_buffer_dsc_size);
1179 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] image %d is synchronized from pipeline at size %i (%ix%i->%ix%i)\n",
1180 imgid, mip, width, height, dsc->width, dsc->height);
1181
1182 // Downscale
1183 dsc->iscale = 1.f;
1184 const int32_t wd = dsc->width;
1185 const int32_t ht = dsc->height;
1186 uint8_t *buf =(uint8_t *)_get_buffer_from_dsc(dsc);
1187 dt_iop_flip_and_zoom_8(in, width, height, buf, wd, ht,
1188 ORIENTATION_NONE, &dsc->width, &dsc->height);
1189
1190 // Color convert
1191 cmsHTRANSFORM transform = NULL;
1192 pthread_rwlock_rdlock(&darktable.color_profiles->xprofile_lock);
1193 gboolean alloc = FALSE;
1194
1195 if(profile == DT_COLORSPACE_DISPLAY)
1196 {
1197 // Convert to whatever display space to save thumbnails into Adobe RGB
1199 }
1200 else
1201 {
1202 const dt_colorspaces_color_profile_t *const profile_in
1204 const dt_colorspaces_color_profile_t *const profile_out
1206 if(profile_in && profile_out)
1207 {
1208 alloc = TRUE;
1209 transform = cmsCreateTransform(profile_in->profile, TYPE_BGRA_8, profile_out->profile, TYPE_RGBA_8,
1210 INTENT_PERCEPTUAL, 0);
1211 }
1212 }
1213
1214 // Need to save BGRA back to RGBA. The function name is misleading,
1215 // it's still only swapping R <-> B.
1217 if(alloc) cmsDeleteTransform(transform);
1218 pthread_rwlock_unlock(&darktable.color_profiles->xprofile_lock);
1219
1221 dsc->flags &= ~DT_MIPMAP_BUFFER_DSC_FLAG_GENERATE;
1222 dt_cache_release(&_get_cache(cache, mip)->cache, entry);
1223 }
1224}
1225
1226
1227void dt_mipmap_cache_remove_at_size(dt_mipmap_cache_t *cache, const int32_t imgid, const dt_mipmap_size_t mip, const gboolean flush_disk)
1228{
1229 if(mip >= DT_MIPMAP_F || mip < DT_MIPMAP_0) return;
1230
1231 // get rid of all ldr thumbnails:
1232 const uint32_t key = get_key(imgid, mip);
1233 dt_cache_entry_t *entry = dt_cache_testget(&_get_cache(cache, mip)->cache, key, 'w');
1234 if(entry)
1235 {
1236 struct dt_mipmap_buffer_dsc *dsc = _get_dsc_from_entry(entry);
1237 ASAN_UNPOISON_MEMORY_REGION(dsc, dt_mipmap_buffer_dsc_size);
1238 if(flush_disk) dsc->flags |= DT_MIPMAP_BUFFER_DSC_FLAG_INVALIDATE;
1239 dt_cache_release(&_get_cache(cache, mip)->cache, entry);
1240 dt_cache_remove(&_get_cache(cache, mip)->cache, key);
1241 }
1242 if(flush_disk)
1243 {
1244 // directly remove the file on disk cache even if we don't have a memory entry
1245 dt_mipmap_cache_unlink_ondisk_thumbnail(cache, imgid, mip);
1246 }
1247}
1248
1249// get rid of all ldr thumbnails:
1250void dt_mipmap_cache_remove(dt_mipmap_cache_t *cache, const int32_t imgid, const gboolean flush_disk)
1251{
1253 dt_mipmap_cache_remove_at_size(cache, imgid, k, flush_disk);
1254}
1255
1256// write thumbnail to disc if not existing there
1257void dt_mimap_cache_evict(dt_mipmap_cache_t *cache, const int32_t imgid)
1258{
1260 dt_cache_remove(&_get_cache(cache, k)->cache, get_key(imgid, k));
1261}
1262
1263static void _init_f(dt_mipmap_buffer_t *mipmap_buf, float *out, uint32_t *width, uint32_t *height, float *iscale,
1264 const int32_t imgid)
1265{
1266 const uint32_t wd = *width, ht = *height;
1267
1268 /* do not even try to process file if it isn't available */
1269 char filename[PATH_MAX] = { 0 };
1271 {
1272 const dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
1273 if(img)
1274 {
1275 source = dt_image_choose_input_path(img, filename, sizeof(filename), FALSE);
1277 }
1278 }
1279 if(source == DT_IMAGE_PATH_NONE)
1280 {
1281 *width = *height = 0;
1282 *iscale = 0.0f;
1283 return;
1284 }
1285
1288
1289 // lock image after we have the buffer, we might need to lock the image struct for
1290 // writing during raw loading, to write to width/height.
1291 const dt_image_t *image = dt_image_cache_get(darktable.image_cache, imgid, 'r');
1292
1293 dt_iop_roi_t roi_in, roi_out;
1294 roi_in.x = roi_in.y = 0;
1295 roi_in.width = image->width;
1296 roi_in.height = image->height;
1297 roi_in.scale = 1.0f;
1298
1299 roi_out.x = roi_out.y = 0;
1300
1301 // now let's figure out the scaling...
1302
1303 // MIP_F is 4 channels, and we do not demosaic here
1304 roi_out.scale = fminf(((float)wd) / (float)image->width, ((float)ht) / (float)image->height);
1305 roi_out.width = roi_out.scale * roi_in.width;
1306 roi_out.height = roi_out.scale * roi_in.height;
1307
1308 if(IS_NULL_PTR(buf.buf) || buf.width == 0 || buf.height == 0)
1309 {
1311 *width = *height = 0;
1312 *iscale = 0.0f;
1313 return;
1314 }
1315
1316 mipmap_buf->color_space = DT_COLORSPACE_NONE; // TODO: do we need that information in this buffer?
1317
1318 if(image->dsc.filters)
1319 {
1320 if(image->dsc.filters != 9u && image->dsc.datatype == TYPE_FLOAT)
1321 {
1322 dt_iop_clip_and_zoom_mosaic_half_size_f((float *const)out, (const float *const)buf.buf, &roi_out, &roi_in,
1323 roi_out.width, roi_in.width, image->dsc.filters);
1324 }
1325 else if(image->dsc.filters != 9u && image->dsc.datatype == TYPE_UINT16)
1326 {
1327 dt_iop_clip_and_zoom_mosaic_half_size((uint16_t * const)out, (const uint16_t *)buf.buf, &roi_out, &roi_in,
1328 roi_out.width, roi_in.width, image->dsc.filters);
1329 }
1330 else if(image->dsc.filters == 9u && image->dsc.datatype == TYPE_UINT16)
1331 {
1332 dt_iop_clip_and_zoom_mosaic_third_size_xtrans((uint16_t * const)out, (const uint16_t *)buf.buf, &roi_out,
1333 &roi_in, roi_out.width, roi_in.width, image->dsc.xtrans);
1334 }
1335 else if(image->dsc.filters == 9u && image->dsc.datatype == TYPE_FLOAT)
1336 {
1337 dt_iop_clip_and_zoom_mosaic_third_size_xtrans_f(out, (const float *)buf.buf, &roi_out, &roi_in,
1338 roi_out.width, roi_in.width, image->dsc.xtrans);
1339 }
1340 else
1341 {
1343 }
1344 }
1345 else
1346 {
1347 // downsample
1348 dt_iop_clip_and_zoom(out, (const float *)buf.buf, &roi_out, &roi_in, roi_out.width, roi_in.width);
1349 }
1350
1352
1353 *width = roi_out.width;
1354 *height = roi_out.height;
1355 *iscale = (float)image->width / (float)roi_out.width;
1356
1358}
1359
1360
1361// dummy functions for `export' to mipmap buffers:
1367
1369{
1370 return IMAGEIO_RGB | IMAGEIO_INT8;
1371}
1372
1374{
1375 return 8;
1376}
1377
1378static int _write_image(dt_imageio_module_data_t *data, const char *filename, const void *in,
1379 dt_colorspaces_color_profile_type_t over_type, const char *over_filename,
1380 void *exif, int exif_len, int32_t imgid, int num, int total, dt_dev_pixelpipe_t *pipe,
1381 const gboolean export_masks)
1382{
1383 _dummy_data_t *d = (_dummy_data_t *)data;
1384 memcpy(d->buf, in, sizeof(uint32_t) * data->width * data->height);
1385 return 0;
1386}
1387
1388static int _load_jpg(const char *filename, const int32_t imgid, const uint32_t wd, const uint32_t ht,
1389 const dt_mipmap_size_t size, const dt_image_orientation_t orientation, uint8_t *buf,
1391{
1392 int res = 1;
1394 if(!dt_imageio_jpeg_read_header(filename, &jpg))
1395 {
1397 uint8_t *tmp = (uint8_t *)dt_alloc_align(sizeof(uint8_t) * jpg.width * jpg.height * 4);
1398 if(tmp && !dt_imageio_jpeg_read(&jpg, tmp))
1399 {
1400 // scale to fit
1401 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] generate mip size %d for image %d from jpeg\n", size, imgid);
1402 dt_iop_flip_and_zoom_8(tmp, jpg.width, jpg.height, buf, wd, ht, orientation, width, height);
1403 res = 0;
1404 }
1405 dt_free_align(tmp);
1406 }
1407 return res;
1408}
1409
1410static int _find_sidecar_jpg(const char *filename, const char *ext, char *sidecar)
1411{
1412 const size_t filename_len = strlen(filename) - strlen(ext);
1413 const char *exts[4] = { ".jpg", ".JPG", ".jpeg", ".JPEG" };
1414
1415 for(int i = 0; i < 4; i++)
1416 {
1417 // Damage control. Should never happen.
1418 if(filename_len + strlen(exts[i]) >= PATH_MAX)
1419 continue;
1420
1421 // Construct the sidecar filename
1422 const size_t str_copy = g_snprintf(sidecar, PATH_MAX, "%.*s%s", (int)filename_len, filename, exts[i]);
1423
1424 // Check if the filename was too long or if the copy failed.
1425 if (str_copy == 0 || str_copy >= PATH_MAX)
1426 continue;
1427
1428 if(g_file_test(sidecar, G_FILE_TEST_EXISTS))
1429 return 1;
1430 }
1431
1432 return 0;
1433}
1434
1435static void _init_8(uint8_t *buf, uint32_t *width, uint32_t *height, float *iscale,
1437 const dt_mipmap_size_t size, dt_atomic_int *shutdown)
1438{
1439 if(size >= DT_MIPMAP_F || *width < 16 || *height < 16) return;
1440
1441 *iscale = 1.0f;
1442 const uint32_t wd = *width, ht = *height;
1443
1444 char filename[PATH_MAX] = { 0 };
1445 char ext[6] = { 0 };
1446 gboolean input_exists, is_jpg_input, use_embedded_jpg;
1447 const int embedded_jpg_mode = dt_conf_get_int("lighttable/embedded_jpg");
1448 _write_mipmap_to_disk(imgid, filename, ext, &input_exists, &is_jpg_input, &use_embedded_jpg, NULL);
1449
1450 /* do not even try to process file if it isn't available */
1451 if(!input_exists)
1452 {
1453 *width = *height = 0;
1454 *iscale = 0.0f;
1456 return;
1457 }
1458
1459 int res = 1;
1460
1461 // try to generate mip from larger mip
1462 // This expects that invalid mips will be flushed, so the assumption is:
1463 // if mip then it's valid (with regard to current history)
1464 if(res && !use_embedded_jpg && size < DT_MIPMAP_F - 1)
1465 {
1466 for(dt_mipmap_size_t k = size + 1; k < DT_MIPMAP_F; k++)
1467 {
1470 if(IS_NULL_PTR(tmp.buf)) continue;
1471
1472 *color_space = tmp.color_space;
1473 // downsample
1474 dt_iop_flip_and_zoom_8(tmp.buf, tmp.width, tmp.height, buf, wd, ht, ORIENTATION_NONE, width, height);
1475 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] generate mip size %d for image %d from mip size %d (%ix%i->%ix%i)\n",
1476 size, imgid, k, tmp.width, tmp.height, *width, *height);
1477
1479 res = 0;
1480 break;
1481 }
1482 }
1483
1484 // Orientation is only needed when loading embedded JPEGs.
1486 if(use_embedded_jpg)
1487 {
1488 const dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
1489 if(img)
1490 {
1491 orientation = (img->orientation != ORIENTATION_NULL) ? img->orientation : ORIENTATION_NONE;
1493 }
1494 }
1495
1496 if(res && use_embedded_jpg)
1497 {
1498 char sidecar_filename[PATH_MAX] = { 0 };
1499
1500 if(is_jpg_input)
1501 {
1502 // Input file is a JPEG
1503 res = _load_jpg(filename, imgid, wd, ht, size, orientation, buf, width, height, color_space);
1504 }
1505 else if(_find_sidecar_jpg(filename, ext, sidecar_filename))
1506 {
1507 // input file is a RAW but we have a companion JPEG file in the same folder:
1508 // use it in priority (it may be higher resolution/quality than embedded JPEG).
1509 res = _load_jpg(sidecar_filename, imgid, wd, ht, size, orientation, buf, width, height, color_space);
1510 }
1511 else
1512 {
1513 // input file is a RAW without companion JPEG:
1514 // try to load the embedded thumbnail. Might not be large enough though.
1515 uint8_t *tmp = NULL;
1516 int32_t thumb_width, thumb_height;
1517 res = dt_imageio_large_thumbnail(filename, &tmp, &thumb_width, &thumb_height, color_space, *width, *height);
1518 if(!res)
1519 {
1520 // We take the thumbnail no matter its size. It might be too small for the requested dimension,
1521 // and end up blurry. But it's less bad than the following scenario:
1522 // 1. user displays collections in grid of 5 columns (small thumbnail -> fetch embedded JPEG for performance, all good),
1523 // 2. user zooms in the grid, to 2 or 3 columns,
1524 // 3. suddently, embedded JPEG is too small so we ditch it for a full pipe recompute,
1525 // 4. but then, color/contrast/appearance unexpectedly changes and user doesn't understand WTF just happened.
1526 // Blurry is less bad than randomly inconsistent, plus user has a GUI way in lighttable to
1527 // change how thumbs are processed at runtime.
1528 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] generate mip size %d for image %d from embedded jpeg\n", size, imgid);
1529 dt_iop_flip_and_zoom_8(tmp, thumb_width, thumb_height, buf, wd, ht, orientation, width, height);
1531 }
1532 }
1533 }
1534
1535 if(res)
1536 {
1537 if(embedded_jpg_mode == 2)
1538 {
1540 "[mipmap_cache] embedded JPEG mode forbids raw processing for image %d at mip %d\n",
1541 imgid, size);
1542 *width = *height = 0;
1543 *iscale = 0.0f;
1545 return;
1546 }
1547
1548 // try the real thing: rawspeed + pixelpipe
1550 _dummy_data_t dat;
1551 format.bpp = _bpp;
1552 format.write_image = _write_image;
1553 format.levels = _levels;
1554 dat.head.max_width = wd;
1555 dat.head.max_height = ht;
1556 dat.buf = buf;
1557 // export with flags: ignore exif (don't load from disk), don't swap byte order, don't do hq processing,
1558 // no upscaling and signal we want thumbnail export
1559 res = dt_imageio_export_with_flags(imgid, "unused", &format, (dt_imageio_module_data_t *)&dat, TRUE, FALSE, FALSE,
1561 NULL, 1, 1, NULL, shutdown);
1562 if(!res)
1563 {
1564 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] generated mip %d for image %d from scratch\n", size, imgid);
1565 // might be smaller, or have a different aspect than what we got as input.
1566 *width = dat.head.width;
1567 *height = dat.head.height;
1568 *iscale = 1.0f;
1570 }
1571 }
1572
1573 // any errors?
1574 if(res)
1575 {
1576 fprintf(stderr, "[mipmap_cache] could not process thumbnail!\n");
1577 *width = *height = 0;
1578 *iscale = 0.0f;
1580 }
1581}
1582
1583void dt_mipmap_cache_copy_thumbnails(const dt_mipmap_cache_t *cache, const uint32_t dst_imgid, const uint32_t src_imgid)
1584{
1585 gboolean write_to_disk_src, write_to_disk_dst;
1586 _write_mipmap_to_disk(src_imgid, NULL, NULL, NULL, NULL, NULL, &write_to_disk_src);
1587 _write_mipmap_to_disk(dst_imgid, NULL, NULL, NULL, NULL, NULL, &write_to_disk_dst);
1588
1589 if(cache->cachedir[0] && write_to_disk_src && write_to_disk_dst)
1590 {
1591 for(dt_mipmap_size_t mip = DT_MIPMAP_0; mip < DT_MIPMAP_F; mip++)
1592 {
1593 // try and load from disk, if successful set flag
1594 char srcpath[PATH_MAX] = {0};
1595 char dstpath[PATH_MAX] = {0};
1596 dt_mipmap_get_cache_filename(srcpath, cache, mip, src_imgid);
1597 dt_mipmap_get_cache_filename(dstpath, cache, mip, dst_imgid);
1598 GFile *src = g_file_new_for_path(srcpath);
1599 GFile *dst = g_file_new_for_path(dstpath);
1600 GError *gerror = NULL;
1601 g_file_copy(src, dst, G_FILE_COPY_NONE, NULL, NULL, NULL, &gerror);
1602 // ignore errors, we tried what we could.
1603 g_object_unref(dst);
1604 g_object_unref(src);
1605 g_clear_error(&gerror);
1606 }
1607 }
1608}
1609
1610// clang-format off
1611// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1612// vim: shiftwidth=2 expandtab tabstop=2 cindent
1613// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1614// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
int dt_atomic_get_int(dt_atomic_int *var)
atomic_int dt_atomic_int
Definition atomic.h:66
static void transform(float *x, float *o, const float *m, const float t_h, const float t_v)
Definition clipping.c:482
const dt_colorspaces_color_profile_t * dt_colorspaces_get_profile(dt_colorspaces_color_profile_type_t type, const char *filename, dt_colorspaces_profile_direction_t direction)
void dt_colorspaces_transform_rgba8_to_bgra8(const cmsHTRANSFORM transform, const uint8_t *image_in, uint8_t *image_out, const int width, const int height)
@ DT_INTENT_LAST
Definition colorspaces.h:68
dt_colorspaces_color_profile_type_t
Definition colorspaces.h:81
@ DT_COLORSPACE_ADOBERGB
Definition colorspaces.h:85
@ DT_COLORSPACE_DISPLAY
Definition colorspaces.h:91
@ DT_COLORSPACE_SRGB
Definition colorspaces.h:84
@ DT_COLORSPACE_NONE
Definition colorspaces.h:82
@ DT_PROFILE_DIRECTION_DISPLAY
const dt_aligned_pixel_t f
const float max
const dt_colormatrix_t dt_aligned_pixel_t out
#define dt_cache_release(A, B)
#define dt_cache_get(A, B, C)
static void dt_cache_set_cleanup_callback(dt_cache_t *cache, dt_cache_cleanup_t cleanup_cb, void *cleanup_data)
static void dt_cache_set_allocate_callback(dt_cache_t *cache, dt_cache_allocate_t allocate_cb, void *allocate_data)
dt_image_path_source_t dt_image_choose_input_path(const dt_image_t *img, char *pathname, size_t pathname_len, gboolean force_cache)
char * key
#define ASAN_POISON_MEMORY_REGION(addr, size)
#define ASAN_UNPOISON_MEMORY_REGION(addr, size)
int dt_conf_get_bool(const char *name)
int dt_conf_get_int(const char *name)
int dt_worker_threads()
Definition darktable.c:1735
void dt_concat_path_file(char destination[PATH_MAX], const char path[PATH_MAX], const char *const file)
Definition darktable.c:1947
darktable_t darktable
Definition darktable.c:183
void * dt_alloc_align(size_t size)
Definition darktable.c:484
size_t dt_get_mipmap_mem()
Definition darktable.c:1745
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1600
#define dt_free_align(ptr)
Definition darktable.h:503
@ DT_DEBUG_CACHE
Definition darktable.h:737
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Enable aggressive floating-point arithmetic optimizations, in denormals handling. Set through user pr...
Definition darktable.h:546
#define dt_free(ptr)
Definition darktable.h:478
#define dt_pixelpipe_cache_free_align(mem)
Definition darktable.h:475
static const dt_aligned_pixel_simd_t value
Definition darktable.h:599
#define dt_unreachable_codepath()
Definition darktable.h:1041
#define PATH_MAX
Definition darktable.h:1189
#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:293
#define DT_CACHELINE_BYTES
Definition darktable.h:392
const gchar * dt_database_get_path(const struct dt_database_t *db)
Definition database.c:3658
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:384
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:374
void dt_loc_get_user_cache_dir(char *cachedir, size_t bufsize)
@ TYPE_FLOAT
Definition format.h:46
@ TYPE_UINT16
Definition format.h:47
static gchar * g_realpath(const char *path)
Definition grealpath.h:46
dt_image_path_source_t
Definition image.h:242
@ DT_IMAGE_PATH_NONE
Definition image.h:243
dt_image_orientation_t
Definition image.h:203
@ ORIENTATION_NULL
Definition image.h:204
@ ORIENTATION_NONE
Definition image.h:205
dt_imageio_retval_t
Definition image.h:78
@ DT_IMAGEIO_OK
Definition image.h:79
void dt_image_cache_read_release(dt_image_cache_t *cache, const dt_image_t *img)
dt_image_t * dt_image_cache_get(dt_image_cache_t *cache, const int32_t imgid, char mode)
void dt_image_cache_write_release(dt_image_cache_t *cache, dt_image_t *img, dt_image_cache_write_mode_t mode)
@ DT_IMAGE_CACHE_RELAXED
Definition image_cache.h:51
int bpp
int dt_imageio_export_with_flags(const int32_t imgid, const char *filename, dt_imageio_module_format_t *format, dt_imageio_module_data_t *format_params, const gboolean ignore_exif, const gboolean display_byteorder, const gboolean high_quality, gboolean is_scaling, const gboolean thumbnail_export, const char *filter, const gboolean copy_metadata, const gboolean export_masks, dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, dt_iop_color_intent_t icc_intent, dt_imageio_module_storage_t *storage, dt_imageio_module_data_t *storage_params, int num, int total, dt_export_metadata_t *metadata, dt_atomic_int *shutdown)
Definition imageio.c:995
dt_imageio_retval_t dt_imageio_open(dt_image_t *img, const char *filename, dt_mipmap_buffer_t *buf)
Definition imageio.c:1279
int dt_imageio_large_thumbnail(const char *filename, uint8_t **buffer, int32_t *th_width, int32_t *th_height, dt_colorspaces_color_profile_type_t *color_space, const int width, const int height)
Load the thumbnail embedded into a RAW file having at least the size MAX(width, height) x MAX(width,...
Definition imageio.c:209
@ IMAGEIO_RGB
Definition imageio.h:70
@ IMAGEIO_INT8
Definition imageio.h:62
int dt_imageio_jpeg_read(dt_imageio_jpeg_t *jpg, uint8_t *out)
int dt_imageio_jpeg_read_header(const char *filename, dt_imageio_jpeg_t *jpg)
dt_colorspaces_color_profile_type_t dt_imageio_jpeg_read_color_space(dt_imageio_jpeg_t *jpg)
int dt_imageio_jpeg_decompress_header(const void *in, size_t length, dt_imageio_jpeg_t *jpg)
int dt_imageio_jpeg_decompress(dt_imageio_jpeg_t *jpg, uint8_t *out)
int dt_imageio_jpeg_write(const char *filename, const uint8_t *in, const int width, const int height, const int quality, const void *exif, int exif_len)
void dt_iop_clip_and_zoom_mosaic_half_size_f(float *const out, const float *const in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const int32_t out_stride, const int32_t in_stride, const uint32_t filters)
void dt_iop_clip_and_zoom(float *out, const float *const in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const int32_t out_stride, const int32_t in_stride)
void dt_iop_flip_and_zoom_8(const uint8_t *in, int32_t iw, int32_t ih, uint8_t *out, int32_t ow, int32_t oh, const dt_image_orientation_t orientation, uint32_t *width, uint32_t *height)
void dt_iop_clip_and_zoom_mosaic_half_size(uint16_t *const out, const uint16_t *const in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const int32_t out_stride, const int32_t in_stride, const uint32_t filters)
void dt_iop_clip_and_zoom_mosaic_third_size_xtrans(uint16_t *const out, const uint16_t *const in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const int32_t out_stride, const int32_t in_stride, const uint8_t(*const xtrans)[6])
void dt_iop_clip_and_zoom_mosaic_third_size_xtrans_f(float *const out, const float *const in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const int32_t out_stride, const int32_t in_stride, const uint8_t(*const xtrans)[6])
#define DT_CTL_WORKER_RESERVED
Definition jobs.h:37
float *const restrict const size_t k
void dt_mipmap_cache_get_with_caller_and_shutdown(dt_mipmap_cache_t *cache, dt_mipmap_buffer_t *buf, const int32_t imgid, const dt_mipmap_size_t mip, const dt_mipmap_get_flags_t flags, const char mode, dt_atomic_int *shutdown, const char *file, int line)
void * dt_mipmap_cache_alloc(dt_mipmap_buffer_t *buf, const dt_image_t *img)
void dt_mipmap_cache_remove_at_size(dt_mipmap_cache_t *cache, const int32_t imgid, const dt_mipmap_size_t mip, const gboolean flush_disk)
static void _sync_dsc_to_buf(dt_mipmap_buffer_t *buf, struct dt_mipmap_buffer_dsc *dsc, const int32_t imgid, const dt_mipmap_size_t mip)
void dt_mipmap_cache_swap_at_size(dt_mipmap_cache_t *cache, const int32_t imgid, const dt_mipmap_size_t mip, const uint8_t *const in, const int32_t width, const int32_t height, dt_colorspaces_color_profile_type_t profile)
static void _paint_skulls(dt_mipmap_buffer_t *buf, struct dt_mipmap_buffer_dsc *dsc, const int32_t imgid, const dt_mipmap_size_t mip)
static const uint8_t dt_mipmap_cache_exif_data_srgb[]
static int _write_image(dt_imageio_module_data_t *data, const char *filename, const void *in, dt_colorspaces_color_profile_type_t over_type, const char *over_filename, void *exif, int exif_len, int32_t imgid, int num, int total, dt_dev_pixelpipe_t *pipe, const gboolean export_masks)
static int _bpp(dt_imageio_module_data_t *data)
static void _init_f(dt_mipmap_buffer_t *mipmap_buf, float *buf, uint32_t *width, uint32_t *height, float *iscale, const int32_t imgid)
static const int dt_mipmap_cache_exif_data_adobergb_length
static void _init_8(uint8_t *buf, uint32_t *width, uint32_t *height, float *iscale, dt_colorspaces_color_profile_type_t *color_space, const int32_t imgid, const dt_mipmap_size_t size, dt_atomic_int *shutdown)
uint32_t width
Definition mipmap_cache.c:0
void dt_mipmap_cache_update_buffer_addresses(dt_cache_entry_t *entry, struct dt_mipmap_buffer_dsc **dsc, const size_t width, const size_t height, const size_t buffer_size)
Resync all references to all references.
static int _find_sidecar_jpg(const char *filename, const char *ext, char *sidecar)
void dt_mipmap_get_cache_filename(char path[PATH_MAX], const dt_mipmap_cache_t *cache, dt_mipmap_size_t mip, const int32_t imgid)
static void _mipmap_subcache_collect(dt_cache_t *c, GArray *out)
static void _invalidate_buffer(dt_mipmap_buffer_t *buf)
static const int dt_mipmap_cache_exif_data_srgb_length
static dt_mipmap_cache_one_t * _get_cache(dt_mipmap_cache_t *cache, const dt_mipmap_size_t mip)
void dt_mipmap_cache_init(dt_mipmap_cache_t *cache)
static int dt_mipmap_cache_get_filename(gchar *mipmapfilename, size_t size)
void dt_mipmap_cache_write_get_with_caller(dt_mipmap_cache_t *cache, dt_mipmap_buffer_t *buf, const int32_t imgid, const int mip, const char *file, int line)
static uint8_t * _get_buffer_from_dsc(struct dt_mipmap_buffer_dsc *dsc)
static void _write_mipmap_to_disk(const int32_t imgid, char *filename, char *ext, gboolean *input_exists, gboolean *is_jpg_input, gboolean *use_embedded_jpg, gboolean *write_to_disk)
Check if an image should be written to disk, if the thumbnail should be computed from embedded JPEG,...
static const uint8_t dt_mipmap_cache_exif_data_adobergb[]
void dt_mipmap_cache_get_with_caller(dt_mipmap_cache_t *cache, dt_mipmap_buffer_t *buf, const int32_t imgid, const dt_mipmap_size_t mip, const dt_mipmap_get_flags_t flags, const char mode, const char *file, int line)
void dt_mipmap_cache_remove(dt_mipmap_cache_t *cache, const int32_t imgid, const gboolean flush_disk)
static void * dead_image_8(struct dt_mipmap_buffer_dsc *dsc)
void dt_mipmap_cache_cleanup(dt_mipmap_cache_t *cache)
uint32_t height
Definition mipmap_cache.c:1
static void _generate_blocking(dt_cache_entry_t *entry, dt_mipmap_buffer_t *buf, const int32_t imgid, const dt_mipmap_size_t mip, dt_atomic_int *shutdown)
static gboolean _get_image_copy(const int32_t imgid, dt_image_t *buffered_image)
GArray * dt_mipmap_cache_get_entries_stats(dt_mipmap_cache_t *cache)
float iscale
Definition mipmap_cache.c:2
void dt_mimap_cache_evict(dt_mipmap_cache_t *cache, const int32_t imgid)
static void _validate_buffer(dt_mipmap_buffer_t *buf, struct dt_mipmap_buffer_dsc *dsc, const int32_t imgid, const dt_mipmap_size_t mip)
static size_t _get_entry_size(const size_t buffer_size)
static uint32_t get_imgid(const uint32_t key)
size_t size
Definition mipmap_cache.c:3
void dt_mipmap_get_cache_dir(char path[PATH_MAX], const dt_mipmap_cache_t *cache, dt_mipmap_size_t mip)
void dt_mipmap_cache_print(dt_mipmap_cache_t *cache)
void dt_mipmap_cache_deallocate_dynamic(void *data, dt_cache_entry_t *entry)
void dt_mipmap_cache_get_usage(dt_mipmap_cache_t *cache, size_t *current, size_t *max)
void dt_mipmap_cache_allocate_dynamic(void *data, dt_cache_entry_t *entry)
static int _load_jpg(const char *filename, const int32_t imgid, const uint32_t wd, const uint32_t ht, const dt_mipmap_size_t size, const dt_image_orientation_t orientation, uint8_t *buf, uint32_t *width, uint32_t *height, dt_colorspaces_color_profile_type_t *color_space)
static int32_t get_key(const int32_t imgid, const dt_mipmap_size_t size)
struct dt_mipmap_buffer_dsc * _get_dsc_from_entry(dt_cache_entry_t *entry)
#define DT_MIPMAP_CACHE_DEFAULT_FILE_NAME
dt_mipmap_size_t dt_mipmap_cache_get_fitting_size(const dt_mipmap_cache_t *cache, const int32_t width, const int32_t height, const uint32_t imgid)
dt_mipmap_size_t dt_mipmap_cache_get_matching_size(const dt_mipmap_cache_t *cache, const int32_t width, const int32_t height, const uint32_t imgid)
dt_colorspaces_color_profile_type_t color_space
Definition mipmap_cache.c:5
static dt_mipmap_size_t get_size(const uint32_t key)
dt_mipmap_buffer_dsc_flags
@ DT_MIPMAP_BUFFER_DSC_FLAG_NONE
@ DT_MIPMAP_BUFFER_DSC_FLAG_INVALIDATE
@ DT_MIPMAP_BUFFER_DSC_FLAG_GENERATE
static void dt_mipmap_cache_unlink_ondisk_thumbnail(void *data, int32_t imgid, dt_mipmap_size_t mip)
void dt_mipmap_cache_copy_thumbnails(const dt_mipmap_cache_t *cache, const uint32_t dst_imgid, const uint32_t src_imgid)
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
void dt_mipmap_cache_release_with_caller(dt_mipmap_cache_t *cache, dt_mipmap_buffer_t *buf, const char *file, int line)
static int _levels(dt_imageio_module_data_t *data)
#define dt_mipmap_cache_get(A, B, C, D, E, F)
dt_mipmap_get_flags_t
@ DT_MIPMAP_BLOCKING
@ DT_MIPMAP_TESTLOCK
#define dt_mipmap_cache_release(A, B)
dt_mipmap_size_t
@ DT_MIPMAP_F
@ DT_MIPMAP_0
@ DT_MIPMAP_2
@ DT_MIPMAP_NONE
@ DT_MIPMAP_FULL
void dt_cache_release_with_caller(dt_cache_t *cache, dt_cache_entry_t *entry, const char *file, int line)
dt_cache_entry_t * dt_cache_testget(dt_cache_t *cache, const uint32_t key, char mode)
void dt_cache_init(dt_cache_t *cache, size_t entry_size, size_t cost_quota)
dt_cache_entry_t * dt_cache_get_with_caller(dt_cache_t *cache, const uint32_t key, char mode, const char *file, int line)
int dt_cache_remove(dt_cache_t *cache, const uint32_t key)
void dt_cache_cleanup(dt_cache_t *cache)
const float r
unsigned __int64 uint64_t
Definition strptime.c:75
dt_imageio_module_data_t head
struct dt_colorspaces_t * color_profiles
Definition darktable.h:816
struct dt_mipmap_cache_t * mipmap_cache
Definition darktable.h:804
struct dt_sys_resources_t dtresources
Definition darktable.h:862
const struct dt_database_t * db
Definition darktable.h:807
struct dt_image_cache_t * image_cache
Definition darktable.h:805
uint32_t key
size_t data_size
dt_pthread_rwlock_t lock
int _lock_demoting
void * data
size_t cost
size_t cost
size_t cost_quota
cmsHTRANSFORM transform_display_to_adobe_rgb
pthread_rwlock_t xprofile_lock
int32_t height
Definition image.h:315
dt_image_orientation_t orientation
Definition image.h:284
int32_t width
Definition image.h:315
uint32_t history_items
Definition image.h:321
dt_iop_buffer_dsc_t dsc
Definition image.h:337
char filename[DT_MAX_FILENAME_LEN]
Definition image.h:304
uint32_t filters
Definition format.h:60
uint8_t xtrans[6][6]
Definition format.h:70
dt_iop_buffer_type_t datatype
Definition format.h:56
Region of interest passed through the pixelpipe.
Definition imageop.h:72
double scale
Definition imageop.h:74
dt_colorspaces_color_profile_type_t color_space
dt_mipmap_buffer_dsc_flags flags
dt_colorspaces_color_profile_type_t color_space
dt_cache_entry_t * cache_entry
dt_mipmap_size_t size
int mip
size_t size
int32_t imgid
dt_mipmap_cache_one_t mip_full
size_t max_height[DT_MIPMAP_NONE]
dt_mipmap_cache_one_t mip_f
char cachedir[PATH_MAX]
dt_mipmap_cache_one_t mip_thumbs
size_t max_width[DT_MIPMAP_NONE]
size_t buffer_size[DT_MIPMAP_NONE]
fsblkcnt_t f_bavail
Definition statvfs.h:55
unsigned long f_frsize
Definition statvfs.h:52
void dt_supervisor_mipmap(const dt_sv_op_t op, const int32_t imgid, const int mip)
GHashTable * entries
Definition supervisor.c:116
void dt_supervisor_image(const dt_sv_op_t op, const int32_t imgid, const dt_image_t *img)
@ DT_SV_UPDATE
Definition supervisor.h:78
@ DT_SV_CREATE
Definition supervisor.h:77
@ DT_SV_DELETE
Definition supervisor.h:80
static gboolean dt_supervisor_active(void)
Definition supervisor.h:94
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29