Blender V5.0
preview_image.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2006-2007 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <string>
10
11#include "DNA_ID.h"
12#include "DNA_brush_types.h"
14#include "DNA_light_types.h"
15#include "DNA_material_types.h"
16#include "DNA_node_types.h"
17#include "DNA_object_types.h"
18#include "DNA_scene_types.h"
19#include "DNA_screen_types.h"
20#include "DNA_texture_types.h"
21#include "DNA_world_types.h"
22
23#include "BKE_icons.h"
24
25#include "BLI_ghash.h"
26#include "BLI_string.h"
27#ifndef NDEBUG
28# include "BLI_threads.h"
29#endif
30
31#include "BLO_read_write.hh"
32
33#include "GPU_texture.hh"
34
35#include "IMB_imbuf.hh"
36#include "IMB_thumbs.hh"
37
38#include "atomic_ops.h"
39
40#include "BKE_preview_image.hh"
41
42/* Not mutex-protected! */
43static GHash *gCachedPreviews = nullptr;
44
45namespace blender::bke {
46
51
54{
55 if (other.deferred_loading_data) {
56 this->deferred_loading_data = std::make_unique<PreviewDeferredLoadingData>(
58 }
59}
61
62} // namespace blender::bke
63
64static PreviewImage *previewimg_deferred_create(const char *filepath, ThumbSource source)
65{
67 prv->runtime->deferred_loading_data =
68 std::make_unique<blender::bke::PreviewDeferredLoadingData>();
69 prv->runtime->deferred_loading_data->filepath = filepath;
70 prv->runtime->deferred_loading_data->source = source;
71 return prv;
72}
73
75{
77
78 for (int i = 0; i < NUM_ICON_SIZES; i++) {
79 prv->flag[i] |= PRV_CHANGED;
80 prv->changed_timestamp[i] = 0;
81 }
82
83 prv->runtime = MEM_new<blender::bke::PreviewImageRuntime>(__func__);
84 return prv;
85}
86
88{
89 if (prv && (*prv)) {
90 for (int i = 0; i < NUM_ICON_SIZES; i++) {
91 if ((*prv)->rect[i]) {
92 MEM_freeN((*prv)->rect[i]);
93 }
94 if ((*prv)->runtime->gputexture[i]) {
95 GPU_texture_free((*prv)->runtime->gputexture[i]);
96 }
97 }
98
99 MEM_delete((*prv)->runtime);
100 MEM_freeN(*prv);
101 *prv = nullptr;
102 }
103}
104
106{
107 if (!gCachedPreviews) {
109 }
110}
111
119
121{
122 PreviewImage *prv = (PreviewImage *)link;
123 if (!prv) {
124 return;
125 }
127}
128
130{
131 MEM_SAFE_FREE(prv->rect[size]);
132 if (prv->runtime->gputexture[size]) {
133 GPU_texture_free(prv->runtime->gputexture[size]);
134 }
135 prv->h[size] = prv->w[size] = 0;
136 prv->flag[size] |= PRV_CHANGED;
137 prv->flag[size] &= ~PRV_USER_EDITED;
138 prv->changed_timestamp[size] = 0;
139}
140
142{
143 for (int i = 0; i < NUM_ICON_SIZES; i++) {
145 }
146}
147
149{
150 if (!prv) {
151 return nullptr;
152 }
153
154 PreviewImage *prv_img = MEM_mallocN<PreviewImage>(__func__);
155 *prv_img = blender::dna::shallow_copy(*prv);
156 prv_img->runtime = MEM_new<blender::bke::PreviewImageRuntime>(__func__, *prv->runtime);
157
158 for (int i = 0; i < NUM_ICON_SIZES; i++) {
159 if (prv->rect[i]) {
160 prv_img->rect[i] = (uint *)MEM_dupallocN(prv->rect[i]);
161 }
162 prv_img->runtime->gputexture[i] = nullptr;
163 }
164
165 return prv_img;
166}
167
168void BKE_previewimg_id_copy(ID *new_id, const ID *old_id)
169{
170 PreviewImage **old_prv_p = BKE_previewimg_id_get_p(old_id);
171 PreviewImage **new_prv_p = BKE_previewimg_id_get_p(new_id);
172
173 if (old_prv_p && *old_prv_p) {
174 BLI_assert(new_prv_p != nullptr && ELEM(*new_prv_p, nullptr, *old_prv_p));
175 // const int new_icon_id = get_next_free_id();
176
177 // if (new_icon_id == 0) {
178 // return; /* Failure. */
179 // }
180 *new_prv_p = BKE_previewimg_copy(*old_prv_p);
181 new_id->icon_id = (*new_prv_p)->runtime->icon_id = 0;
182 }
183}
184
186{
187 switch (GS(id->name)) {
188#define ID_PRV_CASE(id_code, id_struct) \
189 case id_code: { \
190 return &((id_struct *)id)->preview; \
191 } \
192 ((void)0)
205#undef ID_PRV_CASE
206 default:
207 break;
208 }
209
210 return nullptr;
211}
212
214{
216 return prv_p ? *prv_p : nullptr;
217}
218
220{
222 if (prv_p) {
223 BKE_previewimg_free(prv_p);
224 }
225}
226
228{
230 if (!prv_p) {
231 return nullptr;
232 }
233
234 if (*prv_p == nullptr) {
235 *prv_p = BKE_previewimg_create();
236 }
237 return *prv_p;
238}
239
240void BKE_previewimg_id_custom_set(ID *id, const char *filepath)
241{
243
244 /* Thumbnail previews must use the deferred pipeline. But we force them to be immediately
245 * generated here still. */
246
247 if (*prv) {
249 }
251
252 /* Can't lazy-render the preview on access. ID previews are saved to files and we want them to be
253 * there in time. Not only if something happened to have accessed it meanwhile. */
254 for (int i = 0; i < NUM_ICON_SIZES; i++) {
256 /* Prevent auto-updates. */
257 (*prv)->flag[i] |= PRV_USER_EDITED;
258 }
259}
260
262{
263 return ELEM(GS(id->name), ID_OB, ID_MA, ID_TE, ID_LA, ID_WO, ID_IM, ID_BR, ID_GR, ID_SCE);
264}
265
267{
268 if (!prv) {
269 return;
270 }
271
272 if (prv->runtime->tag & PRV_TAG_DEFFERED_RENDERING) {
273 /* We cannot delete the preview while it is being loaded in another thread... */
274 prv->runtime->tag |= PRV_TAG_DEFFERED_DELETE;
275 return;
276 }
277 if (prv->runtime->icon_id) {
278 BKE_icon_delete(prv->runtime->icon_id);
279 }
281}
282
288
290{
292
293 PreviewImage *prv = nullptr;
294 void **key_p, **prv_p;
295
296 if (!BLI_ghash_ensure_p_ex(gCachedPreviews, name, &key_p, &prv_p)) {
297 *key_p = BLI_strdup(name);
298 *prv_p = BKE_previewimg_create();
299 }
300 prv = *(PreviewImage **)prv_p;
301 BLI_assert(prv);
302
303 return prv;
304}
305
307 const char *filepath,
308 const int source,
309 bool force_update)
310{
312
313 PreviewImage *prv = nullptr;
314 void **prv_p;
315
317
318 if (prv_p) {
319 prv = static_cast<PreviewImage *>(*prv_p);
320 BLI_assert(prv);
321 BLI_assert(prv->runtime->deferred_loading_data);
322 }
323
324 if (prv && force_update) {
325 if (prv->runtime->deferred_loading_data &&
326 (prv->runtime->deferred_loading_data->source == source) &&
327 (prv->runtime->deferred_loading_data->filepath == filepath))
328 {
329 /* If same filepath, no need to re-allocate preview, just clear it up. */
331 }
332 else {
334 }
335 }
336
337 if (!prv) {
338 prv = previewimg_deferred_create(filepath, ThumbSource(source));
339 force_update = true;
340 }
341
342 if (force_update) {
343 if (prv_p) {
344 *prv_p = prv;
345 }
346 else {
348 }
349 }
350
351 return prv;
352}
353
355{
357 if (!gCachedPreviews) {
358 /* Static cache was already freed including all contained previews. Can happen on shutdown. */
359 return;
360 }
361
363
365}
366
368{
369 if (!prv->runtime->deferred_loading_data) {
370 return;
371 }
372
373 const bool do_icon = ((size == ICON_SIZE_ICON) && !prv->rect[ICON_SIZE_ICON]);
374 const bool do_preview = ((size == ICON_SIZE_PREVIEW) && !prv->rect[ICON_SIZE_PREVIEW]);
375
376 if (!(do_icon || do_preview)) {
377 /* Nothing to do. */
378 return;
379 }
380
381 const blender::bke::PreviewDeferredLoadingData &prv_deferred =
382 *prv->runtime->deferred_loading_data;
383 int icon_w, icon_h;
384
385 ImBuf *thumb = IMB_thumb_manage(prv_deferred.filepath.c_str(), THB_LARGE, prv_deferred.source);
386 if (!thumb) {
387 return;
388 }
389
390 /* #PreviewImage assumes pre-multiplied alpha. */
392
393 if (do_preview) {
394 prv->w[ICON_SIZE_PREVIEW] = thumb->x;
395 prv->h[ICON_SIZE_PREVIEW] = thumb->y;
398 }
399 if (do_icon) {
400 if (thumb->x > thumb->y) {
402 icon_h = (thumb->y * icon_w) / thumb->x + 1;
403 }
404 else if (thumb->x < thumb->y) {
406 icon_w = (thumb->x * icon_h) / thumb->y + 1;
407 }
408 else {
409 icon_w = icon_h = ICON_RENDER_DEFAULT_HEIGHT;
410 }
411
412 IMB_scale(thumb, icon_w, icon_h, IMBScaleFilter::Box, false);
413 prv->w[ICON_SIZE_ICON] = icon_w;
414 prv->h[ICON_SIZE_ICON] = icon_h;
417 }
418 IMB_freeImBuf(thumb);
419}
420
422{
423 if (!prv->runtime->deferred_loading_data) {
424 return nullptr;
425 }
426
427 return prv->runtime->deferred_loading_data->filepath.c_str();
428}
429
431{
432 if (!prv->runtime->deferred_loading_data) {
433 return std::nullopt;
434 }
435
436 return prv->runtime->deferred_loading_data->source;
437}
438
440{
441 const uint w = prv->w[size];
442 const uint h = prv->h[size];
443 const uint *rect = prv->rect[size];
444
445 ImBuf *ima = nullptr;
446
447 if (w > 0 && h > 0 && rect) {
448 /* first allocate imbuf for copying preview into it */
449 ima = IMB_allocImBuf(w, h, 32, IB_byte_data);
450 memcpy(ima->byte_buffer.data, rect, w * h * sizeof(uint8_t) * 4);
451 }
452
453 return ima;
454}
455
457{
458 /* Previews may be calculated on a thread. */
460}
461
463{
464 return (prv->flag[size] & PRV_RENDERING) == 0;
465}
466
468{
469 return (prv->runtime->tag & PRV_TAG_DEFFERED_INVALID) != 0;
470}
471
473{
474 /* Note we write previews also for undo steps. It takes up some memory,
475 * but not doing so would causes all previews to be re-rendered after
476 * undo which is too expensive. */
477
478 if (prv == nullptr) {
479 return;
480 }
481
482 PreviewImage prv_copy = blender::dna::shallow_copy(*prv);
483 prv_copy.runtime = nullptr;
484 BLO_write_struct_at_address(writer, PreviewImage, prv, &prv_copy);
485 if (prv_copy.rect[0]) {
486 BLO_write_uint32_array(writer, prv_copy.w[0] * prv_copy.h[0], prv_copy.rect[0]);
487 }
488 if (prv_copy.rect[1]) {
489 BLO_write_uint32_array(writer, prv_copy.w[1] * prv_copy.h[1], prv_copy.rect[1]);
490 }
491}
492
494{
495 if (prv == nullptr) {
496 return;
497 }
498
499 prv->runtime = MEM_new<blender::bke::PreviewImageRuntime>(__func__);
500
501 for (int i = 0; i < NUM_ICON_SIZES; i++) {
502 if (prv->rect[i]) {
503 BLO_read_uint32_array(reader, prv->w[i] * prv->h[i], &prv->rect[i]);
504 }
505
506 /* PRV_RENDERING is a runtime only flag currently, but don't mess with it on undo! It gets
507 * special handling in #memfile_undosys_restart_unfinished_id_previews() then. */
508 if (!BLO_read_data_is_undo(reader)) {
509 prv->flag[i] &= ~PRV_RENDERING;
510 }
511 }
512}
bool BKE_icon_delete(int icon_id)
Definition icons.cc:455
#define ICON_RENDER_DEFAULT_HEIGHT
Definition BKE_icons.h:149
void BKE_previewimg_deferred_release(PreviewImage *prv)
ImBuf * BKE_previewimg_to_imbuf(const PreviewImage *prv, int size)
PreviewImage * BKE_previewimg_id_get(const ID *id)
void BKE_previewimg_blend_write(BlendWriter *writer, const PreviewImage *prv)
void BKE_previewimg_ensure(PreviewImage *prv, int size)
bool BKE_previewimg_is_finished(const PreviewImage *prv, int size)
PreviewImage * BKE_previewimg_cached_thumbnail_read(const char *name, const char *filepath, int source, bool force_update)
std::optional< int > BKE_previewimg_deferred_thumb_source_get(const PreviewImage *prv)
void BKE_previewimg_cached_release(const char *name)
PreviewImage * BKE_previewimg_create()
bool BKE_previewimg_id_supports_jobs(const ID *id)
void BKE_preview_images_init()
PreviewImage * BKE_previewimg_id_ensure(ID *id)
void BKE_previewimg_freefunc(void *link)
PreviewImage ** BKE_previewimg_id_get_p(const ID *id)
void BKE_previewimg_clear_single(PreviewImage *prv, enum eIconSizes size)
const char * BKE_previewimg_deferred_filepath_get(const PreviewImage *prv)
bool BKE_previewimg_is_invalid(const PreviewImage *prv)
void BKE_preview_images_free()
void BKE_previewimg_free(PreviewImage **prv)
PreviewImage * BKE_previewimg_copy(const PreviewImage *prv)
PreviewImage * BKE_previewimg_cached_get(const char *name)
void BKE_previewimg_blend_read(BlendDataReader *reader, PreviewImage *prv)
void BKE_previewimg_id_custom_set(ID *id, const char *filepath)
void BKE_previewimg_id_copy(ID *new_id, const ID *old_id)
PreviewImage * BKE_previewimg_cached_ensure(const char *name)
void BKE_previewimg_finish(PreviewImage *prv, int size)
void BKE_previewimg_clear(PreviewImage *prv)
void BKE_previewimg_id_free(ID *id)
#define BLI_assert(a)
Definition BLI_assert.h:46
void ** BLI_ghash_lookup_p(GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:745
GHash * BLI_ghash_str_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void * BLI_ghash_popkey(GHash *gh, const void *key, GHashKeyFreeFP keyfreefp) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:802
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:731
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition BLI_ghash.cc:707
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.cc:860
bool BLI_ghash_ensure_p_ex(GHash *gh, const void *key, void ***r_key, void ***r_val) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:768
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
unsigned int uint
int BLI_thread_is_main(void)
Definition threads.cc:179
#define ELEM(...)
void BLO_read_uint32_array(BlendDataReader *reader, int64_t array_size, uint32_t **ptr_p)
Definition readfile.cc:5802
bool BLO_read_data_is_undo(BlendDataReader *reader)
Definition readfile.cc:5963
void BLO_write_uint32_array(BlendWriter *writer, int64_t num, const uint32_t *data_ptr)
#define BLO_write_struct_at_address(writer, struct_name, address, data_ptr)
ID and Library types, which are fundamental for SDNA.
@ PRV_TAG_DEFFERED_DELETE
Definition DNA_ID.h:630
@ PRV_TAG_DEFFERED_RENDERING
Definition DNA_ID.h:628
@ PRV_TAG_DEFFERED_INVALID
Definition DNA_ID.h:632
@ PRV_RENDERING
Definition DNA_ID.h:622
@ PRV_CHANGED
Definition DNA_ID.h:618
@ PRV_USER_EDITED
Definition DNA_ID.h:620
eIconSizes
@ ICON_SIZE_PREVIEW
@ ICON_SIZE_ICON
@ NUM_ICON_SIZES
@ ID_TE
@ ID_IM
@ ID_NT
@ ID_LA
@ ID_SCE
@ ID_BR
@ ID_WO
@ ID_MA
@ ID_AC
@ ID_SCR
@ ID_GR
@ ID_OB
Object groups, one object can be in many groups at once.
Object is a sort of wrapper for general info.
void GPU_texture_free(blender::gpu::Texture *texture)
void IMB_premultiply_alpha(ImBuf *ibuf)
Definition filter.cc:381
void IMB_freeImBuf(ImBuf *ibuf)
ImBuf * IMB_allocImBuf(unsigned int x, unsigned int y, unsigned char planes, unsigned int flags)
bool IMB_scale(ImBuf *ibuf, unsigned int newx, unsigned int newy, IMBScaleFilter filter, bool threaded=true)
Definition scaling.cc:465
@ IB_byte_data
@ THB_LARGE
Definition IMB_thumbs.hh:23
ThumbSource
Definition IMB_thumbs.hh:27
@ THB_SOURCE_IMAGE
Definition IMB_thumbs.hh:28
ImBuf * IMB_thumb_manage(const char *file_or_lib_path, ThumbSize size, ThumbSource source)
Definition thumbs.cc:538
#define MEM_SAFE_FREE(v)
Provides wrapper around system-specific atomic primitives, and some extensions (faked-atomic operatio...
ATOMIC_INLINE int16_t atomic_fetch_and_and_int16(int16_t *p, int16_t b)
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
#define GS(x)
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static GHash * gCachedPreviews
static PreviewImage * previewimg_deferred_create(const char *filepath, ThumbSource source)
#define ID_PRV_CASE(id_code, id_struct)
const char * name
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
int icon_id
Definition DNA_ID.h:444
ImBufByteBuffer byte_buffer
PreviewImageRuntimeHandle * runtime
Definition DNA_ID.h:648
unsigned int h[2]
Definition DNA_ID.h:643
short changed_timestamp[2]
Definition DNA_ID.h:645
short flag[2]
Definition DNA_ID.h:644
unsigned int * rect[2]
Definition DNA_ID.h:646
unsigned int w[2]
Definition DNA_ID.h:642
std::unique_ptr< PreviewDeferredLoadingData > deferred_loading_data
i
Definition text_draw.cc:230