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 "common/history.h"
57#include "common/imageio.h"
58#include "common/imageio_jpeg.h"
60#include "control/conf.h"
61#include "control/jobs.h"
63#include "gui/gtk.h"
64
65#include <assert.h>
66#include <errno.h>
67#include <fcntl.h>
68#include <glib.h>
69#include <glib/gstdio.h>
70#include <inttypes.h>
71#include <limits.h>
72#include <stdio.h>
73#include <stdlib.h>
74#include <string.h>
75#include <strings.h>
76#include <unistd.h>
77
78#if !defined(_WIN32)
79#include <sys/statvfs.h>
80#else
81//statvfs does not exist in Windows, providing implementation
82#include "win/statvfs.h"
83#endif
84
85#define DT_MIPMAP_CACHE_FILE_MAGIC 0xD71337
86#define DT_MIPMAP_CACHE_FILE_VERSION 23
87#define DT_MIPMAP_CACHE_DEFAULT_FILE_NAME "mipmaps"
88
95
96// the embedded Exif data to tag thumbnails as sRGB or AdobeRGB
97static const uint8_t dt_mipmap_cache_exif_data_srgb[] = {
98 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x49, 0x49, 0x2a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x69,
99 0x87, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
100 0x01, 0xa0, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
101};
102static const uint8_t dt_mipmap_cache_exif_data_adobergb[] = {
103 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x49, 0x49, 0x2a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x69,
104 0x87, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
105 0x01, 0xa0, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
106};
111
113{
114 uint32_t width;
115 uint32_t height;
116 float iscale;
117 size_t size;
120
121#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
122 // do not touch!
123 // must be the last element.
124 // must be no less than 16bytes
125 char redzone[16];
126#endif
127
128 /* NB: sizeof must be a multiple of 4*sizeof(float) */
129} __attribute__((packed, aligned(DT_CACHELINE_BYTES)));
130
131#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
132static const size_t dt_mipmap_buffer_dsc_size __attribute__((unused))
133= sizeof(struct dt_mipmap_buffer_dsc) - sizeof(((struct dt_mipmap_buffer_dsc *)0)->redzone);
134#else
135static const size_t dt_mipmap_buffer_dsc_size __attribute__((unused)) = sizeof(struct dt_mipmap_buffer_dsc);
136#endif
137
138static uint8_t * _get_buffer_from_dsc(struct dt_mipmap_buffer_dsc *dsc);
139
140static inline void *dead_image_8(struct dt_mipmap_buffer_dsc *dsc)
141{
142 dsc->width = dsc->height = 8;
143 dsc->iscale = 1.0f;
145 assert(dsc->size > 64 * sizeof(uint32_t));
146 const uint32_t X = 0xffffffffu;
147 const uint32_t o = 0u;
148 const uint32_t image[]
149 = { o, o, o, o, o, o, o, o,
150 o, o, X, X, X, X, o, o,
151 o, X, o, X, X, o, X, o,
152 o, X, X, X, X, X, X, o,
153 o, o, X, o, o, X, o, o,
154 o, o, o, o, o, o, o, o,
155 o, o, X, X, X, X, o, o,
156 o, o, o, o, o, o, o, o };
157 memcpy(_get_buffer_from_dsc(dsc), image, sizeof(uint32_t) * 64);
158 return _get_buffer_from_dsc(dsc);
159}
160
161static inline int32_t get_key(const int32_t imgid, const dt_mipmap_size_t size)
162{
163 // imgid can't be >= 2^28 (~250 million images)
164 return (((int32_t)size) << 28) | (imgid - 1);
165}
166
167static inline uint32_t get_imgid(const uint32_t key)
168{
169 return (key & 0xfffffff) + 1;
170}
171
172static inline dt_mipmap_size_t get_size(const uint32_t key)
173{
174 return (dt_mipmap_size_t)(key >> 28);
175}
176
178{
179 g_snprintf(path, sizeof(char) * PATH_MAX, "%s.d" G_DIR_SEPARATOR_S "%d",
180 cache->cachedir, (int)mip);
181}
182
183void dt_mipmap_get_cache_filename(char path[PATH_MAX], const dt_mipmap_cache_t *cache, dt_mipmap_size_t mip, const int32_t imgid)
184{
185 gchar cache_path[PATH_MAX];
186 dt_mipmap_get_cache_dir(cache_path, cache, mip);
187
188 gchar *file = g_strdup_printf("%u.jpg", imgid);
189 dt_concat_path_file(path, cache_path, file);
190 dt_free(file);
191}
192
193static int dt_mipmap_cache_get_filename(gchar *mipmapfilename, size_t size)
194{
195 int r = -1;
196 char *abspath = NULL;
197
198 // Directory
199 char cachedir[PATH_MAX] = { 0 };
200 dt_loc_get_user_cache_dir(cachedir, sizeof(cachedir));
201
202 // Build the mipmap filename fram hashing the path of the library DB
203 const gchar *dbfilename = dt_database_get_path(darktable.db);
204
205 if(!strcmp(dbfilename, ":memory:"))
206 {
207 mipmapfilename[0] = '\0';
208 r = 0;
209 goto exit;
210 }
211
212 abspath = g_realpath(dbfilename);
213 if(IS_NULL_PTR(abspath)) abspath = g_strdup(dbfilename);
214
215 GChecksum *chk = g_checksum_new(G_CHECKSUM_SHA1);
216 g_checksum_update(chk, (guchar *)abspath, strlen(abspath));
217 const gchar *filename = g_checksum_get_string(chk);
218
219 if(!filename || filename[0] == '\0')
220 snprintf(mipmapfilename, size, "%s/%s", cachedir, DT_MIPMAP_CACHE_DEFAULT_FILE_NAME);
221 else
222 snprintf(mipmapfilename, size, "%s/%s-%s", cachedir, DT_MIPMAP_CACHE_DEFAULT_FILE_NAME, filename);
223
224 g_checksum_free(chk);
225 r = 0;
226
227exit:
228 dt_free(abspath);
229
230 return r;
231}
232
247static void _write_mipmap_to_disk(const int32_t imgid, char *filename, char *ext, gboolean *input_exists,
248 gboolean *is_jpg_input, gboolean *use_embedded_jpg, gboolean *write_to_disk)
249{
250 // Get file name
251 char _filename[PATH_MAX] = { 0 };
252 const dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
253 if(filename || ext || input_exists || is_jpg_input)
254 {
255 const dt_image_path_source_t source = dt_image_choose_input_path(img, _filename, sizeof(_filename), FALSE);
256 if(source == DT_IMAGE_PATH_NONE) _filename[0] = '\0';
257 if(filename) strncpy(filename, _filename, PATH_MAX);
258 if(input_exists) *input_exists = (source != DT_IMAGE_PATH_NONE);
259 }
260
261 // Get the file extension
262 if(ext || is_jpg_input)
263 {
264 char *_ext = _filename + strlen(_filename);
265 while(*_ext != '.' && _ext > _filename) _ext--;
266 if(ext) strncpy(ext, _ext, 5);
267 if(is_jpg_input) *is_jpg_input = !strcasecmp(_ext, ".jpg") || !strcasecmp(_ext, ".jpeg");
268 }
269
270 // embedded JPG mode:
271 // 0 = never use embedded thumbnail
272 // 1 = only on unedited pics,
273 // 2 = always use embedded thumbnail
274 int mode = dt_conf_get_int("lighttable/embedded_jpg");
275 gboolean altered = FALSE;
276 if(mode == 1)
277 {
278 if(img) altered = img->history_items > 0;
279 }
280 const gboolean _use_embedded_jpg
281 = (mode == 2 // always use embedded
282 || (mode == 1 && !altered)); // use embedded only on unaltered images
283 if(use_embedded_jpg) *use_embedded_jpg = _use_embedded_jpg;
284
285 // Save to cache only if the option is enabled by user and the thumbnail is not the embedded thumbnail
286 // The rationale is getting the un-processed JPEG thumbnail is cheap and not worth saving on disk.
287 // This allows fast toggling between JPEG and processed RAW thumbnail from GUI.
288 if(write_to_disk)
289 {
290 *write_to_disk = dt_conf_get_bool("cache_disk_backend");
291 }
292
293 if(img)
295}
296
297
298static void _init_f(dt_mipmap_buffer_t *mipmap_buf, float *buf, uint32_t *width, uint32_t *height, float *iscale,
299 const int32_t imgid);
300static void _init_8(uint8_t *buf, uint32_t *width, uint32_t *height, float *iscale,
302 const dt_mipmap_size_t size, dt_atomic_int *shutdown);
303
337// Gets the address of the `buf->buf` pixel buffer, from the cache entry.
338static uint8_t *_get_buffer_from_dsc(struct dt_mipmap_buffer_dsc *dsc)
339{
340 if(dsc)
341 return (uint8_t *)(dsc + 1);
342 else
343 return NULL;
344}
345
346
348{
349 return (struct dt_mipmap_buffer_dsc *)entry->data;
350}
351
352// update buffer params with dsc
353static void _sync_dsc_to_buf(dt_mipmap_buffer_t *buf, struct dt_mipmap_buffer_dsc *dsc, const int32_t imgid,
354 const dt_mipmap_size_t mip)
355{
356 buf->width = dsc->width;
357 buf->height = dsc->height;
358 buf->iscale = dsc->iscale;
359 buf->color_space = dsc->color_space;
360 buf->imgid = imgid;
361 buf->size = mip;
362 buf->buf = _get_buffer_from_dsc(dsc);
363}
364
365
366// flag a buffer as invalid and set it NULL
367// This doesn't paint skulls
369{
370 buf->width = buf->height = 0;
371 buf->iscale = 0.0f;
372 buf->buf = NULL;
373}
374
375
376static size_t _get_entry_size(const size_t buffer_size)
377{
378 return buffer_size + dt_mipmap_buffer_dsc_size;
379}
380
389 const size_t width, const size_t height,
390 const size_t buffer_size)
391{
392 if(IS_NULL_PTR(entry->data))
393 {
394 entry->data_size = 0;
395 *dsc = NULL;
396 return;
397 }
398
399 // Update the entry datasize
400 entry->data_size = _get_entry_size(buffer_size);
401
402 // Update the dsc parameters
403 *dsc = _get_dsc_from_entry(entry);
404 (*dsc)->width = width;
405 (*dsc)->height = height;
406 (*dsc)->iscale = 1.0f;
407 (*dsc)->color_space = DT_COLORSPACE_NONE;
409 (*dsc)->size = _get_entry_size(buffer_size);
410}
411
412
413// callback for the imageio core to allocate memory.
414// only needed for _FULL buffers, as they change size
415// with the input image. will allocate img->width*img->height*img->bpp bytes.
417{
418 assert(buf);
419 if(IS_NULL_PTR(buf)) return NULL;
420
421 assert(buf->size == DT_MIPMAP_FULL);
422
423 if(buf->size != DT_MIPMAP_FULL)
424 {
425 fprintf(stderr, "trying to alloc a wrong mipmap size for %s: %i (should be: %i)\n", img->filename, buf->size, DT_MIPMAP_FULL);
426 return NULL;
427 }
428
429 dt_cache_entry_t *entry = buf->cache_entry;
430 assert(entry);
431
432 if(IS_NULL_PTR(entry))
433 {
434 fprintf(stderr, "trying to alloc a buffer entry that has no back-reference to cache entry\n");
435 return NULL;
436 }
437
438 /* Free and reset everything. ASan poisoning applies to live cache payloads only: after freeing
439 * the old allocation there is no valid user region left, and poisoning a NULL reset pointer makes
440 * the ASan runtime abort before it can report the real caller context. */
441 if(!IS_NULL_PTR(entry->data))
443 dt_free_align(entry->data);
444 entry->data = NULL;
445
446 // Get a new allocation
447 const int wd = img->width;
448 const int ht = img->height;
449 const size_t bpp = img->dsc.bpp;
450 const size_t buffer_size = wd * ht * bpp;
451 const size_t min_buffer_size = 64 * 4 * sizeof(float);
452 entry->data = dt_alloc_align(_get_entry_size(MAX(buffer_size, min_buffer_size)));
453 if(IS_NULL_PTR(entry->data)) return NULL;
454
455 // Update the references
456 struct dt_mipmap_buffer_dsc *dsc = NULL;
457 dt_mipmap_cache_update_buffer_addresses(entry, &dsc, wd, ht, buffer_size);
458
459 // Unpoison the pixel buffer region
460 ASAN_UNPOISON_MEMORY_REGION(buf->buf, dsc->size - dt_mipmap_buffer_dsc_size);
461
462 // Unpoison the dsc region
463 ASAN_UNPOISON_MEMORY_REGION(dsc, dt_mipmap_buffer_dsc_size);
464
465 // So the padding region between dsc and buf->buf should be poisoned still
466 assert(entry->data == dsc);
467
468 if(dsc)
469 {
470 assert(entry->data_size == dsc->size);
471 }
472
473 // return pointer to start of payload
474 return _get_buffer_from_dsc(dsc);
475}
476
477// callback for the cache backend to initialize payload pointers
478// It's actually not dynamic at all, fixed size only.
480{
481 dt_mipmap_cache_t *cache = (dt_mipmap_cache_t *)data;
482 const dt_mipmap_size_t mip = get_size(entry->key);
483 const int32_t imgid = get_imgid(entry->key);
484
485 assert(mip < DT_MIPMAP_NONE);
486
487 // Free and reset everything
488 dt_free_align(entry->data);
489 entry->data = NULL;
490
491 // Get a new allocation
492 const size_t buffer_size = (mip <= DT_MIPMAP_F) ? cache->buffer_size[mip] : _get_entry_size(sizeof(float) * 4 * 64);
493 entry->data = dt_alloc_align(buffer_size);
494 if(IS_NULL_PTR(entry->data)) return;
495
496 // Update the references
497 struct dt_mipmap_buffer_dsc *dsc = NULL;
498 if(mip <= DT_MIPMAP_F)
499 dt_mipmap_cache_update_buffer_addresses(entry, &dsc, cache->max_width[mip], cache->max_height[mip], buffer_size);
500 else
501 dt_mipmap_cache_update_buffer_addresses(entry, &dsc, 0, 0, buffer_size);
502
503 assert(entry->data == dsc);
504
505 if(IS_NULL_PTR(dsc)) return;
506
507 gboolean write_to_disk;
508 _write_mipmap_to_disk(imgid, NULL, NULL, NULL, NULL, NULL, &write_to_disk);
509
510 if(cache->cachedir[0] && write_to_disk && mip < DT_MIPMAP_F)
511 {
512 // try and load from disk, if successful set flag
513 char filename[PATH_MAX] = {0};
514 dt_mipmap_get_cache_filename(filename, cache, mip, get_imgid(entry->key));
515
516 gboolean io_error = FALSE;
517 gchar *error = NULL;
518 uint8_t *blob = NULL;
521 FILE *f = NULL;
522
523 f = g_fopen(filename, "rb");
524 if(IS_NULL_PTR(f))
525 {
527 "[mipmap_cache] cached file for image %d at mip size %i does not exist\n",
528 imgid, mip);
529 goto finish; // file doesn't exist
530 }
531
532 fseek(f, 0, SEEK_END);
533 long len = ftell(f);
534 if(len <= 0)
535 {
536 error = "empty file";
537 io_error = TRUE;
538 goto finish;
539 }
540
541 blob = (uint8_t *)dt_alloc_align(len);
542 if(IS_NULL_PTR(blob))
543 {
544 error = "out of memory";
545 io_error = TRUE;
546 goto finish;
547 }
548
549 fseek(f, 0, SEEK_SET);
550 const size_t rd = fread(blob, sizeof(uint8_t), len, f);
551 if(rd != len)
552 {
553 error = "corrupted file";
554 io_error = TRUE;
555 goto finish;
556 }
557
558 if(dt_imageio_jpeg_decompress_header(blob, len, &jpg))
559 {
560 error = "couldn't decompress header";
561 io_error = TRUE;
562 goto finish;
563 }
564
566
568 {
569 error = "couldn't decompress JPEG";
570 io_error = TRUE;
571 goto finish;
572 }
573
574 dsc->width = jpg.width;
575 dsc->height = jpg.height;
576 dsc->iscale = 1.0f;
578 dsc->flags = 0;
579
580 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] image %d at mip size %d (%ix%i) loaded from disk cache\n",
581 imgid, mip, jpg.width, jpg.height);
582
583finish:
584 if(f && io_error)
585 {
586 // Delete the file, we will regenerate it
587 g_unlink(filename);
588 fprintf(stderr, "[mipmap_cache] failed to open thumbnail for image %" PRIu32 " from `%s'. Reason: %s\n",
589 imgid, filename, error);
590 }
591
592 dt_free_align(blob);
593 if(f) fclose(f);
594 }
595
596 // cost is just flat one for the buffer, as the buffers might have different sizes,
597 // to make sure quota is meaningful.
598 if(mip >= DT_MIPMAP_F)
599 entry->cost = 1;
600 else
601 entry->cost = cache->buffer_size[mip];
602}
603
604static void dt_mipmap_cache_unlink_ondisk_thumbnail(void *data, int32_t imgid, dt_mipmap_size_t mip)
605{
606 dt_mipmap_cache_t *cache = (dt_mipmap_cache_t *)data;
607
608 // also remove jpg backing (always try to do that, in case user just temporarily switched it off,
609 // to avoid inconsistencies.
610 // if(dt_conf_get_bool("cache_disk_backend"))
611 if(cache->cachedir[0])
612 {
613 char filename[PATH_MAX] = { 0 };
614 dt_mipmap_get_cache_filename(filename, cache, mip, imgid);
615 g_unlink(filename);
616 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] image %i for size %i was deleted from disk cache\n", imgid, mip);
617 }
618}
619
621{
622 dt_mipmap_cache_t *cache = (dt_mipmap_cache_t *)data;
623 const dt_mipmap_size_t mip = get_size(entry->key);
624 if(mip < DT_MIPMAP_F)
625 {
626 const int32_t imgid = get_imgid(entry->key);
627 gboolean write_to_disk;
628 _write_mipmap_to_disk(imgid, NULL, NULL, NULL, NULL, NULL, &write_to_disk);
629
630 struct dt_mipmap_buffer_dsc *dsc = _get_dsc_from_entry(entry);
631 // don't write skulls:
632 if(dsc->width > 8 && dsc->height > 8)
633 {
635 {
637 }
638 if(cache->cachedir[0] && write_to_disk && mip < DT_MIPMAP_F)
639 {
640 // serialize to disk
641 gchar cache_path[PATH_MAX];
642 dt_mipmap_get_cache_dir(cache_path, cache, mip);
643 const int mkd = g_mkdir_with_parents(cache_path, 0750);
644
645 if(!mkd)
646 {
647 char filename[PATH_MAX] = {0};
648 dt_mipmap_get_cache_filename(filename, cache, mip, get_imgid(entry->key));
649 // Don't write existing files as both performance and quality (lossy jpg) suffer
650 // FIXME: actually, yes, we write existing files too. See FIXME above.
651 FILE *f = NULL;
652 if((f = g_fopen(filename, "wb"))) // !g_file_test(filename, G_FILE_TEST_EXISTS)
653 {
654 // first check the disk isn't full
655 struct statvfs vfsbuf;
656 if (!statvfs(filename, &vfsbuf))
657 {
658 const int64_t free_mb = ((vfsbuf.f_frsize * vfsbuf.f_bavail) >> 20);
659 if (free_mb < 100)
660 {
661 fprintf(stderr, "Aborting image write as only %" PRId64 " MB free to write %s\n", free_mb, filename);
662 goto write_error;
663 }
664 }
665 else
666 {
667 fprintf(stderr, "Aborting image write since couldn't determine free space available to write %s\n", filename);
668 goto write_error;
669 }
670
671 const int cache_quality = dt_conf_get_int("database_cache_quality");
672 const uint8_t *exif = NULL;
673 int exif_len = 0;
675 {
678 }
679 else if(dsc->color_space == DT_COLORSPACE_ADOBERGB)
680 {
683 }
684 if(dt_imageio_jpeg_write(filename, _get_buffer_from_dsc(dsc), dsc->width, dsc->height,
685 MIN(100, MAX(10, cache_quality)), exif, exif_len))
686 {
687write_error:
688 g_unlink(filename);
689 }
690 else
691 {
692 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] image %i for size %i was written to cache at %s\n", imgid, mip, filename);
693 }
694 }
695 if(f) fclose(f);
696 }
697 }
698 }
699 }
700 dt_free_align(entry->data);
701 entry->data = NULL;
702}
703
705{
706 dt_mipmap_cache_get_filename(cache->cachedir, sizeof(cache->cachedir));
707
708 // Fixed sizes for the thumbnail mip levels, selected for coverage of most screen sizes
709 // Starting at 4K, we use 3:2 ratio of camera sensor instead of 16:9/16:10 of display,
710 // in order to start caching full-resolution JPEG for 100% zoom in lighttable
711 size_t mipsizes[DT_MIPMAP_F][2] = {
712 { 360, 225 }, // mip0 - 1/2 size previous one
713 { 720, 450 }, // mip1 - 1/2 size previous one
714 { 1440, 900 }, // mip2 - covers HD, WXGA+
715 { 1920, 1200 }, // mip3 - covers 1080p and 1600x1200
716 { 2560, 1600 }, // mip4 - covers 2560x1440
717 { 3840, 2560 }, // mip5 - covers 4K and UHD
718 { 5120, 3414 }, // mip6 - covers 5K
719 { 6144, 4096 }, // mip7 - covers 6K
720 { 7680, 5120 }, // mip8 - covers 8K
721 };
722 // Set mipf for preview pipe to 1440x900
723 cache->max_width[DT_MIPMAP_F] = mipsizes[DT_MIPMAP_2][0];
724 cache->max_height[DT_MIPMAP_F] = mipsizes[DT_MIPMAP_2][1];
725 for(int k = DT_MIPMAP_F-1; k >= 0; k--)
726 {
727 cache->max_width[k] = mipsizes[k][0];
728 cache->max_height[k] = mipsizes[k][1];
729 }
730 // header + buffer
731 for(int k = DT_MIPMAP_F-1; k >= 0; k--)
732 cache->buffer_size[k] = _get_entry_size(cache->max_width[k] * cache->max_height[k] * 4);
733
734 // clear stats:
735 cache->mip_thumbs.stats_requests = 0;
736 cache->mip_thumbs.stats_near_match = 0;
737 cache->mip_thumbs.stats_misses = 0;
738 cache->mip_thumbs.stats_fetches = 0;
739 cache->mip_thumbs.stats_standin = 0;
740 cache->mip_f.stats_requests = 0;
741 cache->mip_f.stats_near_match = 0;
742 cache->mip_f.stats_misses = 0;
743 cache->mip_f.stats_fetches = 0;
744 cache->mip_f.stats_standin = 0;
745 cache->mip_full.stats_requests = 0;
746 cache->mip_full.stats_near_match = 0;
747 cache->mip_full.stats_misses = 0;
748 cache->mip_full.stats_fetches = 0;
749 cache->mip_full.stats_standin = 0;
750
754
758 cache->buffer_size[DT_MIPMAP_FULL] = 0;
759
764 = _get_entry_size(4 * sizeof(float) * cache->max_width[DT_MIPMAP_F] * cache->max_height[DT_MIPMAP_F]);
765}
766
774
776{
777 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] thumbs fill %.2f/%.2f MB (%.2f%%)\n",
778 cache->mip_thumbs.cache.cost / (1024.0 * 1024.0),
779 cache->mip_thumbs.cache.cost_quota / (1024.0 * 1024.0),
780 100.0f * (float)cache->mip_thumbs.cache.cost / (float)cache->mip_thumbs.cache.cost_quota);
781 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] float fill %"PRIu32"/%"PRIu32" slots (%.2f%%)\n",
782 (uint32_t)cache->mip_f.cache.cost, (uint32_t)cache->mip_f.cache.cost_quota,
783 100.0f * (float)cache->mip_f.cache.cost / (float)cache->mip_f.cache.cost_quota);
784 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] full fill %"PRIu32"/%"PRIu32" slots (%.2f%%)\n",
785 (uint32_t)cache->mip_full.cache.cost, (uint32_t)cache->mip_full.cache.cost_quota,
786 100.0f * (float)cache->mip_full.cache.cost / (float)cache->mip_full.cache.cost_quota);
787
788 uint64_t sum = 0;
789 uint64_t sum_fetches = 0;
790 uint64_t sum_standins = 0;
791 sum += cache->mip_thumbs.stats_requests;
792 sum_fetches += cache->mip_thumbs.stats_fetches;
793 sum_standins += cache->mip_thumbs.stats_standin;
794 sum += cache->mip_f.stats_requests;
795 sum_fetches += cache->mip_f.stats_fetches;
796 sum_standins += cache->mip_f.stats_standin;
797 sum += cache->mip_full.stats_requests;
798 sum_fetches += cache->mip_full.stats_fetches;
799 sum_standins += cache->mip_full.stats_standin;
800 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] level | near match | miss | stand-in | fetches | total rq\n");
801 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] thumb | %6.2f%% | %6.2f%% | %6.2f%% | %6.2f%% | %6.2f%%\n",
802 100.0 * cache->mip_thumbs.stats_near_match / (float)cache->mip_thumbs.stats_requests,
803 100.0 * cache->mip_thumbs.stats_misses / (float)cache->mip_thumbs.stats_requests,
804 100.0 * cache->mip_thumbs.stats_standin / (float)sum_standins,
805 100.0 * cache->mip_thumbs.stats_fetches / (float)sum_fetches,
806 100.0 * cache->mip_thumbs.stats_requests / (float)sum);
807 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] float | %6.2f%% | %6.2f%% | %6.2f%% | %6.2f%% | %6.2f%%\n",
808 100.0 * cache->mip_f.stats_near_match / (float)cache->mip_f.stats_requests,
809 100.0 * cache->mip_f.stats_misses / (float)cache->mip_f.stats_requests,
810 100.0 * cache->mip_f.stats_standin / (float)sum_standins,
811 100.0 * cache->mip_f.stats_fetches / (float)sum_fetches,
812 100.0 * cache->mip_f.stats_requests / (float)sum);
813 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] full | %6.2f%% | %6.2f%% | %6.2f%% | %6.2f%% | %6.2f%%\n",
814 100.0 * cache->mip_full.stats_near_match / (float)cache->mip_full.stats_requests,
815 100.0 * cache->mip_full.stats_misses / (float)cache->mip_full.stats_requests,
816 100.0 * cache->mip_full.stats_standin / (float)sum_standins,
817 100.0 * cache->mip_full.stats_fetches / (float)sum_fetches,
818 100.0 * cache->mip_full.stats_requests / (float)sum);
819 dt_print(DT_DEBUG_CACHE, "\n\n");
820}
821
823{
824 switch(mip)
825 {
826 case DT_MIPMAP_FULL:
827 return &cache->mip_full;
828 case DT_MIPMAP_F:
829 return &cache->mip_f;
830 default:
831 return &cache->mip_thumbs;
832 }
833}
834
835// if we get a zero-sized image, paint skulls to signal a missing image
836static void _paint_skulls(dt_mipmap_buffer_t *buf, struct dt_mipmap_buffer_dsc *dsc, const int32_t imgid, const dt_mipmap_size_t mip)
837{
838 if(dsc->width == 0 || dsc->height == 0)
839 {
840 dt_print(DT_DEBUG_CACHE, "[mipmap cache get] got a zero-sized image for img %u mip %d!\n", imgid, mip);
841 if(mip < DT_MIPMAP_F)
842 buf->buf = dead_image_8(dsc);
843 else
844 buf->buf = NULL; // full images with NULL buffer have to be handled, indicates `missing image', but still return locked slot
845 }
846}
847
848static void _validate_buffer(dt_mipmap_buffer_t *buf, struct dt_mipmap_buffer_dsc *dsc, const int32_t imgid,
849 const dt_mipmap_size_t mip)
850{
851 // Unpoison the dsc region
852 ASAN_UNPOISON_MEMORY_REGION(dsc, dt_mipmap_buffer_dsc_size);
853
854 // May update buf->buf:
855 _sync_dsc_to_buf(buf, dsc, imgid, mip);
856
857 // Unpoison the pixel buffer region
858 ASAN_UNPOISON_MEMORY_REGION(buf->buf, dsc->size - dt_mipmap_buffer_dsc_size);
859}
860
861// Grab our own local copy of the image structure
862static gboolean _get_image_copy(const int32_t imgid, dt_image_t *buffered_image)
863{
864 // load the image:
865 // make sure we access the r/w lock as shortly as possible!
866 gboolean no_buffer = TRUE;
867 const dt_image_t *cimg = dt_image_cache_get(darktable.image_cache, imgid, 'r');
868
869 if(cimg)
870 {
871 *buffered_image = *cimg;
872 no_buffer = FALSE;
873 }
874
876
877 return no_buffer;
878}
879
880// Actually do the work: produce an image
882 const int32_t imgid, const dt_mipmap_size_t mip, dt_atomic_int *shutdown)
883{
884 struct dt_mipmap_buffer_dsc *dsc = _get_dsc_from_entry(entry);
885 // Unpoison the descriptor before reading any field in this function.
886 ASAN_UNPOISON_MEMORY_REGION(dsc, dt_mipmap_buffer_dsc_size);
887 // _generate_blocking() can write directly into the cache pixel payload
888 // (notably through _init_8() -> _write_image()), so unpoison it up-front.
889 ASAN_UNPOISON_MEMORY_REGION(_get_buffer_from_dsc(dsc), dsc->size - dt_mipmap_buffer_dsc_size);
890 const uint32_t original_width = dsc->width;
891 const uint32_t original_height = dsc->height;
892 const float original_iscale = dsc->iscale;
893 const dt_colorspaces_color_profile_type_t original_color_space = dsc->color_space;
894
896 {
897 // Already in cache, no I/O needed
898 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,
899 dsc->width, dsc->height);
900 _validate_buffer(buf, dsc, imgid, mip);
901 return;
902 }
903
904 if(mip == DT_MIPMAP_FULL)
905 {
906 dt_image_t buffered_image;
907 if(_get_image_copy(imgid, &buffered_image)) return;
908
909 // Get the input file path, possibly from our local cache of copied images
910 char filename[PATH_MAX] = { 0 };
911 dt_image_path_source_t source = dt_image_choose_input_path(&buffered_image, filename, sizeof(filename), FALSE);
912 if(source == DT_IMAGE_PATH_NONE) return;
913
915 "[mipmap_cache] fetch image %i at mip size %d float32 (%ix%i) from original file I/O\n",
916 imgid, mip, dsc->width, dsc->height);
917
918 // will call dt_mipmap_cache_alloc() internally:
919 dt_imageio_retval_t ret = dt_imageio_open(&buffered_image, filename, buf);
920
921 // We need to update dsc because dt_imageio_open re-alloc entry->data
922 dsc = _get_dsc_from_entry(entry);
923
924 if(ret == DT_IMAGEIO_OK)
925 {
926 // swap back new image data, may contain updated EXIF & colorspace
928 *img = buffered_image;
930 }
931 else
932 {
933 dsc->width = dsc->height = 0;
934 dsc->iscale = 0.f;
935 }
936 }
937 else if(mip == DT_MIPMAP_F)
938 {
940 "[mipmap_cache] compute mip size %d float32 for image %i (%ix%i) from original file \n", mip,
941 imgid, dsc->width, dsc->height);
942 _init_f(buf, (float *)_get_buffer_from_dsc(dsc), &dsc->width, &dsc->height, &dsc->iscale, imgid);
943 }
944 else
945 {
946 // 8-bit thumbs
948 "[mipmap_cache] compute mip size %d uint8 for image %i (%ix%i) from original file \n", mip,
949 imgid, dsc->width, dsc->height);
950 _init_8((uint8_t *)_get_buffer_from_dsc(dsc), &dsc->width, &dsc->height, &dsc->iscale, &dsc->color_space, imgid,
951 mip, shutdown);
952 }
953
954 if(shutdown && dt_atomic_get_int(shutdown))
955 {
956 /* A stale GUI request stopped its thumbnail/export pipe while this cache entry
957 * was still being produced. Keep the entry marked as "generate" so the next
958 * request can restart from a clean state instead of reusing a poisoned empty
959 * thumbnail for the rest of the session. */
960 dsc->width = original_width;
961 dsc->height = original_height;
962 dsc->iscale = original_iscale;
963 dsc->color_space = original_color_space;
965 return;
966 }
967
968 dsc->flags &= ~DT_MIPMAP_BUFFER_DSC_FLAG_GENERATE;
969 _paint_skulls(buf, dsc, imgid, mip);
970 _validate_buffer(buf, dsc, imgid, mip);
971
972 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,
973 buf->width, buf->height, dsc->width, dsc->height, buf->buf);
974}
975
976
979 const char mode, const char *file, int line)
980{
981 dt_mipmap_cache_get_with_caller_and_shutdown(cache, buf, imgid, mip, flags, mode, NULL, file, line);
982}
983
985 const int32_t imgid, const dt_mipmap_size_t mip,
986 const dt_mipmap_get_flags_t flags, const char mode,
987 dt_atomic_int *shutdown, const char *file, int line)
988{
989 assert(mip <= DT_MIPMAP_NONE && mip >= DT_MIPMAP_0);
990
991 const int32_t key = get_key(imgid, mip);
992
993 // Allocation functions will check those if we run them
994 buf->imgid = imgid;
995 buf->size = mip;
996
998 {
999 // simple case: only get and lock if it's there.
1000 dt_cache_entry_t *entry = dt_cache_testget(&_get_cache(cache, mip)->cache, key, mode);
1001 buf->cache_entry = entry;
1002
1003 if(entry)
1004 _validate_buffer(buf, _get_dsc_from_entry(entry), imgid, mip);
1005 else
1006 _invalidate_buffer(buf);
1007 }
1008 else if(flags == DT_MIPMAP_BLOCKING)
1009 {
1010 // simple case: blocking get
1011 dt_cache_entry_t *entry = dt_cache_get_with_caller(&_get_cache(cache, mip)->cache, key, mode, file, line);
1012 buf->cache_entry = entry;
1013 __sync_fetch_and_add(&(_get_cache(cache, mip)->stats_fetches), 1);
1014 _generate_blocking(entry, buf, imgid, mip, shutdown);
1015
1016 // image cache is leaving the write lock in place in case the image has been newly allocated.
1017 // this leads to a slight increase in thread contention, so we opt for dropping the write lock
1018 // and acquiring a read lock immediately after. since this opens a small window for other threads
1019 // to get in between, we need to take some care to re-init cache entries and dsc.
1020 // note that concurrencykit has rw locks that can be demoted from w->r without losing the lock in between.
1021 if(mode == 'r')
1022 {
1023 entry->_lock_demoting = 1;
1024 // drop the write lock
1025 dt_cache_release(&_get_cache(cache, mip)->cache, entry);
1026 // get a read lock
1027 entry = dt_cache_get(&_get_cache(cache, mip)->cache, key, mode);
1028 entry->_lock_demoting = 0;
1029 }
1030
1031 buf->cache_entry = entry;
1032
1033 // The cache can't be locked twice from the same thread,
1034 // because then it would never unlock.
1035#ifdef _DEBUG
1036 const pthread_t writer = dt_pthread_rwlock_get_writer(&(buf->cache_entry->lock));
1037 if(mode == 'w')
1038 {
1039 assert(pthread_equal(writer, pthread_self()));
1040 }
1041 else
1042 {
1043 assert(!pthread_equal(writer, pthread_self()));
1044 }
1045#endif
1046 }
1047 else
1048 {
1049 _invalidate_buffer(buf);
1050 }
1051}
1052
1053void 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)
1054{
1055 dt_mipmap_cache_get_with_caller(cache, buf, imgid, mip, DT_MIPMAP_BLOCKING, 'w', file, line);
1056}
1057
1059 int line)
1060{
1061 if(buf->size == DT_MIPMAP_NONE || IS_NULL_PTR(buf->cache_entry)) return;
1062 assert(buf->imgid > 0);
1063 assert(buf->size >= DT_MIPMAP_0);
1064 assert(buf->size < DT_MIPMAP_NONE);
1065 dt_cache_release_with_caller(&_get_cache(cache, buf->size)->cache, buf->cache_entry, file, line);
1066 buf->size = DT_MIPMAP_NONE;
1067 buf->buf = NULL;
1068}
1069
1070
1071// return index dt_mipmap_size_t having at least width & height requested instead of minimum combined diff
1072// please note that the requested size is in pixels not dots.
1074 const int32_t height, const uint32_t imgid)
1075{
1076 for(int k = DT_MIPMAP_0; k < DT_MIPMAP_F; k++)
1077 {
1078 // We assume a "fit" situation, typically rectangle within square
1079 // so we don't need both dimensions to be greater than requested
1080 if((cache->max_width[k] >= width) || (cache->max_height[k] >= height))
1081 {
1082 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]);
1083 return k;
1084 }
1085 }
1086 return DT_MIPMAP_F - 1;
1087}
1088
1090 const int32_t height, const uint32_t imgid)
1091{
1092 for(int k = DT_MIPMAP_F - 1; k >= DT_MIPMAP_0; k--)
1093 {
1094 if((cache->max_width[k] <= width) && (cache->max_height[k] <= height))
1095 {
1096 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]);
1097 return k;
1098 }
1099 }
1100 return DT_MIPMAP_0;
1101}
1102
1103void 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,
1104 const int32_t width, const int32_t height, dt_colorspaces_color_profile_type_t profile)
1105{
1106 if(mip >= DT_MIPMAP_F || mip < DT_MIPMAP_0) return;
1107
1108 const uint32_t key = get_key(imgid, mip);
1109 dt_cache_entry_t *entry = dt_cache_get_with_caller(&_get_cache(cache, mip)->cache, key, 'w', __FILE__, __LINE__);
1110 if(entry)
1111 {
1112 struct dt_mipmap_buffer_dsc *dsc = _get_dsc_from_entry(entry);
1113 // Unpoison descriptor and pixel payload before reading/writing either.
1114 ASAN_UNPOISON_MEMORY_REGION(dsc, dt_mipmap_buffer_dsc_size);
1115 ASAN_UNPOISON_MEMORY_REGION(_get_buffer_from_dsc(dsc), dsc->size - dt_mipmap_buffer_dsc_size);
1116 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] image %d is synchronized from pipeline at size %i (%ix%i->%ix%i)\n",
1117 imgid, mip, width, height, dsc->width, dsc->height);
1118
1119 // Downscale
1120 dsc->iscale = 1.f;
1121 const int32_t wd = dsc->width;
1122 const int32_t ht = dsc->height;
1123 uint8_t *buf =(uint8_t *)_get_buffer_from_dsc(dsc);
1124 dt_iop_flip_and_zoom_8(in, width, height, buf, wd, ht,
1125 ORIENTATION_NONE, &dsc->width, &dsc->height);
1126
1127 // Color convert
1128 cmsHTRANSFORM transform = NULL;
1129 pthread_rwlock_rdlock(&darktable.color_profiles->xprofile_lock);
1130 gboolean alloc = FALSE;
1131
1132 if(profile == DT_COLORSPACE_DISPLAY)
1133 {
1134 // Convert to whatever display space to save thumbnails into Adobe RGB
1136 }
1137 else
1138 {
1139 alloc = TRUE;
1140 transform = cmsCreateTransform(
1141 dt_colorspaces_get_profile(profile, "", DT_PROFILE_DIRECTION_DISPLAY)->profile, TYPE_BGRA_8,
1143 INTENT_PERCEPTUAL, 0);
1144 }
1145
1146 // Need to save BGRA back to RGBA. The function name is misleading,
1147 // it's still only swapping R <-> B.
1149 if(alloc) cmsDeleteTransform(transform);
1150 pthread_rwlock_unlock(&darktable.color_profiles->xprofile_lock);
1151
1153 dsc->flags &= ~DT_MIPMAP_BUFFER_DSC_FLAG_GENERATE;
1154 dt_cache_release(&_get_cache(cache, mip)->cache, entry);
1155 }
1156}
1157
1158
1159void 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)
1160{
1161 if(mip >= DT_MIPMAP_F || mip < DT_MIPMAP_0) return;
1162
1163 // get rid of all ldr thumbnails:
1164 const uint32_t key = get_key(imgid, mip);
1165 dt_cache_entry_t *entry = dt_cache_testget(&_get_cache(cache, mip)->cache, key, 'w');
1166 if(entry)
1167 {
1168 struct dt_mipmap_buffer_dsc *dsc = _get_dsc_from_entry(entry);
1169 ASAN_UNPOISON_MEMORY_REGION(dsc, dt_mipmap_buffer_dsc_size);
1170 if(flush_disk) dsc->flags |= DT_MIPMAP_BUFFER_DSC_FLAG_INVALIDATE;
1171 dt_cache_release(&_get_cache(cache, mip)->cache, entry);
1172 dt_cache_remove(&_get_cache(cache, mip)->cache, key);
1173 }
1174 if(flush_disk)
1175 {
1176 // directly remove the file on disk cache even if we don't have a memory entry
1177 dt_mipmap_cache_unlink_ondisk_thumbnail(cache, imgid, mip);
1178 }
1179}
1180
1181// get rid of all ldr thumbnails:
1182void dt_mipmap_cache_remove(dt_mipmap_cache_t *cache, const int32_t imgid, const gboolean flush_disk)
1183{
1185 dt_mipmap_cache_remove_at_size(cache, imgid, k, flush_disk);
1186}
1187
1188// write thumbnail to disc if not existing there
1189void dt_mimap_cache_evict(dt_mipmap_cache_t *cache, const int32_t imgid)
1190{
1192 dt_cache_remove(&_get_cache(cache, k)->cache, get_key(imgid, k));
1193}
1194
1195static void _init_f(dt_mipmap_buffer_t *mipmap_buf, float *out, uint32_t *width, uint32_t *height, float *iscale,
1196 const int32_t imgid)
1197{
1198 const uint32_t wd = *width, ht = *height;
1199
1200 /* do not even try to process file if it isn't available */
1201 char filename[PATH_MAX] = { 0 };
1203 {
1204 const dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
1205 if(img)
1206 {
1207 source = dt_image_choose_input_path(img, filename, sizeof(filename), FALSE);
1209 }
1210 }
1211 if(source == DT_IMAGE_PATH_NONE)
1212 {
1213 *width = *height = 0;
1214 *iscale = 0.0f;
1215 return;
1216 }
1217
1220
1221 // lock image after we have the buffer, we might need to lock the image struct for
1222 // writing during raw loading, to write to width/height.
1223 const dt_image_t *image = dt_image_cache_get(darktable.image_cache, imgid, 'r');
1224
1225 dt_iop_roi_t roi_in, roi_out;
1226 roi_in.x = roi_in.y = 0;
1227 roi_in.width = image->width;
1228 roi_in.height = image->height;
1229 roi_in.scale = 1.0f;
1230
1231 roi_out.x = roi_out.y = 0;
1232
1233 // now let's figure out the scaling...
1234
1235 // MIP_F is 4 channels, and we do not demosaic here
1236 roi_out.scale = fminf(((float)wd) / (float)image->width, ((float)ht) / (float)image->height);
1237 roi_out.width = roi_out.scale * roi_in.width;
1238 roi_out.height = roi_out.scale * roi_in.height;
1239
1240 if(IS_NULL_PTR(buf.buf) || buf.width == 0 || buf.height == 0)
1241 {
1243 *width = *height = 0;
1244 *iscale = 0.0f;
1245 return;
1246 }
1247
1248 mipmap_buf->color_space = DT_COLORSPACE_NONE; // TODO: do we need that information in this buffer?
1249
1250 if(image->dsc.filters)
1251 {
1252 if(image->dsc.filters != 9u && image->dsc.datatype == TYPE_FLOAT)
1253 {
1254 dt_iop_clip_and_zoom_mosaic_half_size_f((float *const)out, (const float *const)buf.buf, &roi_out, &roi_in,
1255 roi_out.width, roi_in.width, image->dsc.filters);
1256 }
1257 else if(image->dsc.filters != 9u && image->dsc.datatype == TYPE_UINT16)
1258 {
1259 dt_iop_clip_and_zoom_mosaic_half_size((uint16_t * const)out, (const uint16_t *)buf.buf, &roi_out, &roi_in,
1260 roi_out.width, roi_in.width, image->dsc.filters);
1261 }
1262 else if(image->dsc.filters == 9u && image->dsc.datatype == TYPE_UINT16)
1263 {
1264 dt_iop_clip_and_zoom_mosaic_third_size_xtrans((uint16_t * const)out, (const uint16_t *)buf.buf, &roi_out,
1265 &roi_in, roi_out.width, roi_in.width, image->dsc.xtrans);
1266 }
1267 else if(image->dsc.filters == 9u && image->dsc.datatype == TYPE_FLOAT)
1268 {
1269 dt_iop_clip_and_zoom_mosaic_third_size_xtrans_f(out, (const float *)buf.buf, &roi_out, &roi_in,
1270 roi_out.width, roi_in.width, image->dsc.xtrans);
1271 }
1272 else
1273 {
1275 }
1276 }
1277 else
1278 {
1279 // downsample
1280 dt_iop_clip_and_zoom(out, (const float *)buf.buf, &roi_out, &roi_in, roi_out.width, roi_in.width);
1281 }
1282
1284
1285 *width = roi_out.width;
1286 *height = roi_out.height;
1287 *iscale = (float)image->width / (float)roi_out.width;
1288
1290}
1291
1292
1293// dummy functions for `export' to mipmap buffers:
1299
1301{
1302 return IMAGEIO_RGB | IMAGEIO_INT8;
1303}
1304
1306{
1307 return 8;
1308}
1309
1310static int _write_image(dt_imageio_module_data_t *data, const char *filename, const void *in,
1311 dt_colorspaces_color_profile_type_t over_type, const char *over_filename,
1312 void *exif, int exif_len, int32_t imgid, int num, int total, dt_dev_pixelpipe_t *pipe,
1313 const gboolean export_masks)
1314{
1315 _dummy_data_t *d = (_dummy_data_t *)data;
1316 memcpy(d->buf, in, sizeof(uint32_t) * data->width * data->height);
1317 return 0;
1318}
1319
1320static int _load_jpg(const char *filename, const int32_t imgid, const uint32_t wd, const uint32_t ht,
1321 const dt_mipmap_size_t size, const dt_image_orientation_t orientation, uint8_t *buf,
1323{
1324 int res = 1;
1326 if(!dt_imageio_jpeg_read_header(filename, &jpg))
1327 {
1329 uint8_t *tmp = (uint8_t *)dt_alloc_align(sizeof(uint8_t) * jpg.width * jpg.height * 4);
1330 if(tmp && !dt_imageio_jpeg_read(&jpg, tmp))
1331 {
1332 // scale to fit
1333 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] generate mip size %d for image %d from jpeg\n", size, imgid);
1334 dt_iop_flip_and_zoom_8(tmp, jpg.width, jpg.height, buf, wd, ht, orientation, width, height);
1335 res = 0;
1336 }
1337 dt_free_align(tmp);
1338 }
1339 return res;
1340}
1341
1342static int _find_sidecar_jpg(const char *filename, const char *ext, char *sidecar)
1343{
1344 const size_t filename_len = strlen(filename) - strlen(ext);
1345 const char *exts[4] = { ".jpg", ".JPG", ".jpeg", ".JPEG" };
1346
1347 for(int i = 0; i < 4; i++)
1348 {
1349 // Damage control. Should never happen.
1350 if(filename_len + strlen(exts[i]) >= PATH_MAX)
1351 continue;
1352
1353 // Construct the sidecar filename
1354 const size_t str_copy = g_snprintf(sidecar, PATH_MAX, "%.*s%s", (int)filename_len, filename, exts[i]);
1355
1356 // Check if the filename was too long or if the copy failed.
1357 if (str_copy == 0 || str_copy >= PATH_MAX)
1358 continue;
1359
1360 if(g_file_test(sidecar, G_FILE_TEST_EXISTS))
1361 return 1;
1362 }
1363
1364 return 0;
1365}
1366
1367static void _init_8(uint8_t *buf, uint32_t *width, uint32_t *height, float *iscale,
1369 const dt_mipmap_size_t size, dt_atomic_int *shutdown)
1370{
1371 if(size >= DT_MIPMAP_F || *width < 16 || *height < 16) return;
1372
1373 *iscale = 1.0f;
1374 const uint32_t wd = *width, ht = *height;
1375
1376 char filename[PATH_MAX] = { 0 };
1377 char ext[6] = { 0 };
1378 gboolean input_exists, is_jpg_input, use_embedded_jpg;
1379 const int embedded_jpg_mode = dt_conf_get_int("lighttable/embedded_jpg");
1380 _write_mipmap_to_disk(imgid, filename, ext, &input_exists, &is_jpg_input, &use_embedded_jpg, NULL);
1381
1382 /* do not even try to process file if it isn't available */
1383 if(!input_exists)
1384 {
1385 *width = *height = 0;
1386 *iscale = 0.0f;
1388 return;
1389 }
1390
1391 int res = 1;
1392
1393 // try to generate mip from larger mip
1394 // This expects that invalid mips will be flushed, so the assumption is:
1395 // if mip then it's valid (with regard to current history)
1396 if(res && !use_embedded_jpg && size < DT_MIPMAP_F - 1)
1397 {
1398 for(dt_mipmap_size_t k = size + 1; k < DT_MIPMAP_F; k++)
1399 {
1402 if(IS_NULL_PTR(tmp.buf)) continue;
1403
1404 *color_space = tmp.color_space;
1405 // downsample
1406 dt_iop_flip_and_zoom_8(tmp.buf, tmp.width, tmp.height, buf, wd, ht, ORIENTATION_NONE, width, height);
1407 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] generate mip size %d for image %d from mip size %d (%ix%i->%ix%i)\n",
1408 size, imgid, k, tmp.width, tmp.height, *width, *height);
1409
1411 res = 0;
1412 break;
1413 }
1414 }
1415
1416 // Orientation is only needed when loading embedded JPEGs.
1418 if(use_embedded_jpg)
1419 {
1420 const dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r');
1421 if(img)
1422 {
1423 orientation = (img->orientation != ORIENTATION_NULL) ? img->orientation : ORIENTATION_NONE;
1425 }
1426 }
1427
1428 if(res && use_embedded_jpg)
1429 {
1430 char sidecar_filename[PATH_MAX] = { 0 };
1431
1432 if(is_jpg_input)
1433 {
1434 // Input file is a JPEG
1435 res = _load_jpg(filename, imgid, wd, ht, size, orientation, buf, width, height, color_space);
1436 }
1437 else if(_find_sidecar_jpg(filename, ext, sidecar_filename))
1438 {
1439 // input file is a RAW but we have a companion JPEG file in the same folder:
1440 // use it in priority (it may be higher resolution/quality than embedded JPEG).
1441 res = _load_jpg(sidecar_filename, imgid, wd, ht, size, orientation, buf, width, height, color_space);
1442 }
1443 else
1444 {
1445 // input file is a RAW without companion JPEG:
1446 // try to load the embedded thumbnail. Might not be large enough though.
1447 uint8_t *tmp = NULL;
1448 int32_t thumb_width, thumb_height;
1449 res = dt_imageio_large_thumbnail(filename, &tmp, &thumb_width, &thumb_height, color_space, *width, *height);
1450 if(!res)
1451 {
1452 // We take the thumbnail no matter its size. It might be too small for the requested dimension,
1453 // and end up blurry. But it's less bad than the following scenario:
1454 // 1. user displays collections in grid of 5 columns (small thumbnail -> fetch embedded JPEG for performance, all good),
1455 // 2. user zooms in the grid, to 2 or 3 columns,
1456 // 3. suddently, embedded JPEG is too small so we ditch it for a full pipe recompute,
1457 // 4. but then, color/contrast/appearance unexpectedly changes and user doesn't understand WTF just happened.
1458 // Blurry is less bad than randomly inconsistent, plus user has a GUI way in lighttable to
1459 // change how thumbs are processed at runtime.
1460 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] generate mip size %d for image %d from embedded jpeg\n", size, imgid);
1461 dt_iop_flip_and_zoom_8(tmp, thumb_width, thumb_height, buf, wd, ht, orientation, width, height);
1463 }
1464 }
1465 }
1466
1467 if(res)
1468 {
1469 if(embedded_jpg_mode == 2)
1470 {
1472 "[mipmap_cache] embedded JPEG mode forbids raw processing for image %d at mip %d\n",
1473 imgid, size);
1474 *width = *height = 0;
1475 *iscale = 0.0f;
1477 return;
1478 }
1479
1480 // try the real thing: rawspeed + pixelpipe
1482 _dummy_data_t dat;
1483 format.bpp = _bpp;
1484 format.write_image = _write_image;
1485 format.levels = _levels;
1486 dat.head.max_width = wd;
1487 dat.head.max_height = ht;
1488 dat.buf = buf;
1489 // export with flags: ignore exif (don't load from disk), don't swap byte order, don't do hq processing,
1490 // no upscaling and signal we want thumbnail export
1491 res = dt_imageio_export_with_flags(imgid, "unused", &format, (dt_imageio_module_data_t *)&dat, TRUE, FALSE, FALSE,
1493 NULL, 1, 1, NULL, shutdown);
1494 if(!res)
1495 {
1496 dt_print(DT_DEBUG_CACHE, "[mipmap_cache] generated mip %d for image %d from scratch\n", size, imgid);
1497 // might be smaller, or have a different aspect than what we got as input.
1498 *width = dat.head.width;
1499 *height = dat.head.height;
1500 *iscale = 1.0f;
1502 }
1503 }
1504
1505 // any errors?
1506 if(res)
1507 {
1508 fprintf(stderr, "[mipmap_cache] could not process thumbnail!\n");
1509 *width = *height = 0;
1510 *iscale = 0.0f;
1512 }
1513}
1514
1515void dt_mipmap_cache_copy_thumbnails(const dt_mipmap_cache_t *cache, const uint32_t dst_imgid, const uint32_t src_imgid)
1516{
1517 gboolean write_to_disk_src, write_to_disk_dst;
1518 _write_mipmap_to_disk(src_imgid, NULL, NULL, NULL, NULL, NULL, &write_to_disk_src);
1519 _write_mipmap_to_disk(dst_imgid, NULL, NULL, NULL, NULL, NULL, &write_to_disk_dst);
1520
1521 if(cache->cachedir[0] && write_to_disk_src && write_to_disk_dst)
1522 {
1523 for(dt_mipmap_size_t mip = DT_MIPMAP_0; mip < DT_MIPMAP_F; mip++)
1524 {
1525 // try and load from disk, if successful set flag
1526 char srcpath[PATH_MAX] = {0};
1527 char dstpath[PATH_MAX] = {0};
1528 dt_mipmap_get_cache_filename(srcpath, cache, mip, src_imgid);
1529 dt_mipmap_get_cache_filename(dstpath, cache, mip, dst_imgid);
1530 GFile *src = g_file_new_for_path(srcpath);
1531 GFile *dst = g_file_new_for_path(dstpath);
1532 GError *gerror = NULL;
1533 g_file_copy(src, dst, G_FILE_COPY_NONE, NULL, NULL, NULL, &gerror);
1534 // ignore errors, we tried what we could.
1535 g_object_unref(dst);
1536 g_object_unref(src);
1537 g_clear_error(&gerror);
1538 }
1539 }
1540}
1541
1542// clang-format off
1543// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1544// vim: shiftwidth=2 expandtab tabstop=2 cindent
1545// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1546// 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 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:1677
void dt_concat_path_file(char destination[PATH_MAX], const char path[PATH_MAX], const char *const file)
Definition darktable.c:1889
darktable_t darktable
Definition darktable.c:181
void * dt_alloc_align(size_t size)
Definition darktable.c:446
size_t dt_get_mipmap_mem()
Definition darktable.c:1687
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
#define dt_free_align(ptr)
Definition darktable.h:481
@ DT_DEBUG_CACHE
Definition darktable.h:715
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:524
#define dt_free(ptr)
Definition darktable.h:456
#define dt_pixelpipe_cache_free_align(mem)
Definition darktable.h:453
#define dt_unreachable_codepath()
Definition darktable.h:979
#define PATH_MAX
Definition darktable.h:1062
#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
#define DT_CACHELINE_BYTES
Definition darktable.h:380
const gchar * dt_database_get_path(const struct dt_database_t *db)
Definition database.c:3651
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:961
dt_imageio_retval_t dt_imageio_open(dt_image_t *img, const char *filename, dt_mipmap_buffer_t *buf)
Definition imageio.c:1235
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:208
@ 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 _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)
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_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:788
struct dt_mipmap_cache_t * mipmap_cache
Definition darktable.h:776
const struct dt_database_t * db
Definition darktable.h:779
struct dt_image_cache_t * image_cache
Definition darktable.h:777
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
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
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29