Blender V4.3
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
9#include <iostream>
10#include <string>
11
12#include "DNA_ID.h"
13#include "DNA_brush_types.h"
15#include "DNA_light_types.h"
16#include "DNA_material_types.h"
17#include "DNA_node_types.h"
18#include "DNA_object_types.h"
19#include "DNA_scene_types.h"
20#include "DNA_screen_types.h"
21#include "DNA_texture_types.h"
22#include "DNA_world_types.h"
23
24#include "BKE_icons.h"
25
26#include "BLI_ghash.h"
27#include "BLI_string.h"
28#include "BLI_string_ref.hh"
29#ifndef NDEBUG
30# include "BLI_threads.h"
31#endif
32
33#include "BLO_read_write.hh"
34
35#include "GPU_texture.hh"
36
37#include "IMB_imbuf.hh"
38#include "IMB_thumbs.hh"
39
40#include "atomic_ops.h"
41
42#include "BKE_preview_image.hh"
43
44/* Not mutex-protected! */
45static GHash *gCachedPreviews = nullptr;
46
47namespace blender::bke {
48
53
56{
57 if (other.deferred_loading_data) {
58 this->deferred_loading_data = std::make_unique<PreviewDeferredLoadingData>(
59 *other.deferred_loading_data);
60 }
61}
63
64} // namespace blender::bke
65
66static PreviewImage *previewimg_deferred_create(const char *filepath, ThumbSource source)
67{
69 prv->runtime->deferred_loading_data =
70 std::make_unique<blender::bke::PreviewDeferredLoadingData>();
71 prv->runtime->deferred_loading_data->filepath = filepath;
72 prv->runtime->deferred_loading_data->source = source;
73 return prv;
74}
75
77{
78 PreviewImage *prv = static_cast<PreviewImage *>(MEM_callocN(sizeof(PreviewImage), __func__));
79
80 for (int i = 0; i < NUM_ICON_SIZES; i++) {
81 prv->flag[i] |= PRV_CHANGED;
82 prv->changed_timestamp[i] = 0;
83 }
84
85 prv->runtime = MEM_new<blender::bke::PreviewImageRuntime>(__func__);
86 return prv;
87}
88
90{
91 if (prv && (*prv)) {
92 for (int i = 0; i < NUM_ICON_SIZES; i++) {
93 if ((*prv)->rect[i]) {
94 MEM_freeN((*prv)->rect[i]);
95 }
96 if ((*prv)->runtime->gputexture[i]) {
97 GPU_texture_free((*prv)->runtime->gputexture[i]);
98 }
99 }
100
101 MEM_delete((*prv)->runtime);
102 MEM_freeN(*prv);
103 *prv = nullptr;
104 }
105}
106
108{
109 if (!gCachedPreviews) {
111 }
112}
113
121
123{
124 PreviewImage *prv = (PreviewImage *)link;
125 if (!prv) {
126 return;
127 }
129}
130
132{
133 MEM_SAFE_FREE(prv->rect[size]);
134 if (prv->runtime->gputexture[size]) {
135 GPU_texture_free(prv->runtime->gputexture[size]);
136 }
137 prv->h[size] = prv->w[size] = 0;
138 prv->flag[size] |= PRV_CHANGED;
139 prv->flag[size] &= ~PRV_USER_EDITED;
140 prv->changed_timestamp[size] = 0;
141}
142
144{
145 for (int i = 0; i < NUM_ICON_SIZES; i++) {
147 }
148}
149
151{
152 if (!prv) {
153 return nullptr;
154 }
155
156 PreviewImage *prv_img = static_cast<PreviewImage *>(MEM_mallocN(sizeof(PreviewImage), __func__));
157 *prv_img = blender::dna::shallow_copy(*prv);
158 prv_img->runtime = MEM_new<blender::bke::PreviewImageRuntime>(__func__, *prv->runtime);
159
160 for (int i = 0; i < NUM_ICON_SIZES; i++) {
161 if (prv->rect[i]) {
162 prv_img->rect[i] = (uint *)MEM_dupallocN(prv->rect[i]);
163 }
164 prv_img->runtime->gputexture[i] = nullptr;
165 }
166
167 return prv_img;
168}
169
170void BKE_previewimg_id_copy(ID *new_id, const ID *old_id)
171{
172 PreviewImage **old_prv_p = BKE_previewimg_id_get_p(old_id);
173 PreviewImage **new_prv_p = BKE_previewimg_id_get_p(new_id);
174
175 if (old_prv_p && *old_prv_p) {
176 BLI_assert(new_prv_p != nullptr && ELEM(*new_prv_p, nullptr, *old_prv_p));
177 // const int new_icon_id = get_next_free_id();
178
179 // if (new_icon_id == 0) {
180 // return; /* Failure. */
181 // }
182 *new_prv_p = BKE_previewimg_copy(*old_prv_p);
183 new_id->icon_id = (*new_prv_p)->runtime->icon_id = 0;
184 }
185}
186
188{
189 switch (GS(id->name)) {
190#define ID_PRV_CASE(id_code, id_struct) \
191 case id_code: { \
192 return &((id_struct *)id)->preview; \
193 } \
194 ((void)0)
207#undef ID_PRV_CASE
208 default:
209 break;
210 }
211
212 return nullptr;
213}
214
216{
218 return prv_p ? *prv_p : nullptr;
219}
220
222{
224 if (prv_p) {
225 BKE_previewimg_free(prv_p);
226 }
227}
228
230{
232 if (!prv_p) {
233 return nullptr;
234 }
235
236 if (*prv_p == nullptr) {
237 *prv_p = BKE_previewimg_create();
238 }
239 return *prv_p;
240}
241
242void BKE_previewimg_id_custom_set(ID *id, const char *filepath)
243{
245
246 /* Thumbnail previews must use the deferred pipeline. But we force them to be immediately
247 * generated here still. */
248
249 if (*prv) {
251 }
253
254 /* Can't lazy-render the preview on access. ID previews are saved to files and we want them to be
255 * there in time. Not only if something happened to have accessed it meanwhile. */
256 for (int i = 0; i < NUM_ICON_SIZES; i++) {
257 BKE_previewimg_ensure(*prv, i);
258 /* Prevent auto-updates. */
259 (*prv)->flag[i] |= PRV_USER_EDITED;
260 }
261}
262
264{
265 return ELEM(GS(id->name), ID_OB, ID_MA, ID_TE, ID_LA, ID_WO, ID_IM, ID_BR, ID_GR);
266}
267
269{
270 if (!prv) {
271 return;
272 }
273
274 if (prv->runtime->tag & PRV_TAG_DEFFERED_RENDERING) {
275 /* We cannot delete the preview while it is being loaded in another thread... */
276 prv->runtime->tag |= PRV_TAG_DEFFERED_DELETE;
277 return;
278 }
279 if (prv->runtime->icon_id) {
280 BKE_icon_delete(prv->runtime->icon_id);
281 }
283}
284
290
292{
294
295 PreviewImage *prv = nullptr;
296 void **key_p, **prv_p;
297
298 if (!BLI_ghash_ensure_p_ex(gCachedPreviews, name, &key_p, &prv_p)) {
299 *key_p = BLI_strdup(name);
300 *prv_p = BKE_previewimg_create();
301 }
302 prv = *(PreviewImage **)prv_p;
303 BLI_assert(prv);
304
305 return prv;
306}
307
309 const char *filepath,
310 const int source,
311 bool force_update)
312{
314
315 PreviewImage *prv = nullptr;
316 void **prv_p;
317
318 prv_p = BLI_ghash_lookup_p(gCachedPreviews, name);
319
320 if (prv_p) {
321 prv = static_cast<PreviewImage *>(*prv_p);
322 BLI_assert(prv);
323 BLI_assert(prv->runtime->deferred_loading_data);
324 }
325
326 if (prv && force_update) {
327 if (prv->runtime->deferred_loading_data &&
328 (prv->runtime->deferred_loading_data->source == source) &&
329 (prv->runtime->deferred_loading_data->filepath == filepath))
330 {
331 /* If same filepath, no need to re-allocate preview, just clear it up. */
333 }
334 else {
336 }
337 }
338
339 if (!prv) {
340 prv = previewimg_deferred_create(filepath, ThumbSource(source));
341 force_update = true;
342 }
343
344 if (force_update) {
345 if (prv_p) {
346 *prv_p = prv;
347 }
348 else {
350 }
351 }
352
353 return prv;
354}
355
364
365void BKE_previewimg_ensure(PreviewImage *prv, const int size)
366{
367 if (!prv->runtime->deferred_loading_data) {
368 return;
369 }
370
371 const bool do_icon = ((size == ICON_SIZE_ICON) && !prv->rect[ICON_SIZE_ICON]);
372 const bool do_preview = ((size == ICON_SIZE_PREVIEW) && !prv->rect[ICON_SIZE_PREVIEW]);
373
374 if (!(do_icon || do_preview)) {
375 /* Nothing to do. */
376 return;
377 }
378
379 const blender::bke::PreviewDeferredLoadingData &prv_deferred =
380 *prv->runtime->deferred_loading_data;
381 int icon_w, icon_h;
382
383 ImBuf *thumb = IMB_thumb_manage(prv_deferred.filepath.c_str(), THB_LARGE, prv_deferred.source);
384 if (!thumb) {
385 return;
386 }
387
388 /* #PreviewImage assumes pre-multiplied alpha. */
390
391 if (do_preview) {
392 prv->w[ICON_SIZE_PREVIEW] = thumb->x;
393 prv->h[ICON_SIZE_PREVIEW] = thumb->y;
396 }
397 if (do_icon) {
398 if (thumb->x > thumb->y) {
400 icon_h = (thumb->y * icon_w) / thumb->x + 1;
401 }
402 else if (thumb->x < thumb->y) {
404 icon_w = (thumb->x * icon_h) / thumb->y + 1;
405 }
406 else {
407 icon_w = icon_h = ICON_RENDER_DEFAULT_HEIGHT;
408 }
409
410 IMB_scale(thumb, icon_w, icon_h, IMBScaleFilter::Box, false);
411 prv->w[ICON_SIZE_ICON] = icon_w;
412 prv->h[ICON_SIZE_ICON] = icon_h;
415 }
416 IMB_freeImBuf(thumb);
417}
418
420{
421 if (!prv->runtime->deferred_loading_data) {
422 return nullptr;
423 }
424
425 return prv->runtime->deferred_loading_data->filepath.c_str();
426}
427
429{
430 if (!prv->runtime->deferred_loading_data) {
431 return std::nullopt;
432 }
433
434 return prv->runtime->deferred_loading_data->source;
435}
436
438{
439 const uint w = prv->w[size];
440 const uint h = prv->h[size];
441 const uint *rect = prv->rect[size];
442
443 ImBuf *ima = nullptr;
444
445 if (w > 0 && h > 0 && rect) {
446 /* first allocate imbuf for copying preview into it */
447 ima = IMB_allocImBuf(w, h, 32, IB_rect);
448 memcpy(ima->byte_buffer.data, rect, w * h * sizeof(uint8_t) * 4);
449 }
450
451 return ima;
452}
453
454void BKE_previewimg_finish(PreviewImage *prv, const int size)
455{
456 /* Previews may be calculated on a thread. */
458}
459
460bool BKE_previewimg_is_finished(const PreviewImage *prv, const int size)
461{
462 return (prv->flag[size] & PRV_RENDERING) == 0;
463}
464
466{
467 /* Note we write previews also for undo steps. It takes up some memory,
468 * but not doing so would causes all previews to be re-rendered after
469 * undo which is too expensive. */
470
471 if (prv == nullptr) {
472 return;
473 }
474
475 PreviewImage prv_copy = blender::dna::shallow_copy(*prv);
476 prv_copy.runtime = nullptr;
477 BLO_write_struct_at_address(writer, PreviewImage, prv, &prv_copy);
478 if (prv_copy.rect[0]) {
479 BLO_write_uint32_array(writer, prv_copy.w[0] * prv_copy.h[0], prv_copy.rect[0]);
480 }
481 if (prv_copy.rect[1]) {
482 BLO_write_uint32_array(writer, prv_copy.w[1] * prv_copy.h[1], prv_copy.rect[1]);
483 }
484}
485
487{
488 if (prv == nullptr) {
489 return;
490 }
491
492 prv->runtime = MEM_new<blender::bke::PreviewImageRuntime>(__func__);
493
494 for (int i = 0; i < NUM_ICON_SIZES; i++) {
495 if (prv->rect[i]) {
496 BLO_read_uint32_array(reader, prv->w[i] * prv->h[i], &prv->rect[i]);
497 }
498
499 /* PRV_RENDERING is a runtime only flag currently, but don't mess with it on undo! It gets
500 * special handling in #memfile_undosys_restart_unfinished_id_previews() then. */
501 if (!BLO_read_data_is_undo(reader)) {
502 prv->flag[i] &= ~PRV_RENDERING;
503 }
504 }
505}
bool BKE_icon_delete(int icon_id)
Definition icons.cc:469
#define ICON_RENDER_DEFAULT_HEIGHT
Definition BKE_icons.h:159
void BKE_previewimg_deferred_release(PreviewImage *prv)
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)
void BKE_preview_images_free()
void BKE_previewimg_free(PreviewImage **prv)
PreviewImage * BKE_previewimg_copy(const PreviewImage *prv)
ImBuf * BKE_previewimg_to_imbuf(PreviewImage *prv, int size)
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:50
void ** BLI_ghash_lookup_p(GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c: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.c:802
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:731
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition BLI_ghash.c:707
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.c: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.c:768
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.c:40
unsigned int uint
int BLI_thread_is_main(void)
Definition threads.cc:179
#define ELEM(...)
void BLO_write_uint32_array(BlendWriter *writer, uint num, const uint32_t *data_ptr)
bool BLO_read_data_is_undo(BlendDataReader *reader)
Definition readfile.cc:5135
void BLO_read_uint32_array(BlendDataReader *reader, int array_size, uint32_t **ptr_p)
Definition readfile.cc:4957
#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:593
@ PRV_TAG_DEFFERED_RENDERING
Definition DNA_ID.h:591
@ PRV_RENDERING
Definition DNA_ID.h:585
@ PRV_CHANGED
Definition DNA_ID.h:581
@ PRV_USER_EDITED
Definition DNA_ID.h:583
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(GPUTexture *texture)
void IMB_premultiply_alpha(ImBuf *ibuf)
Definition filter.cc:579
bool IMB_scale(ImBuf *ibuf, unsigned int newx, unsigned int newy, IMBScaleFilter filter, bool threaded=true)
Definition scaling.cc:779
@ IB_rect
@ THB_LARGE
Definition IMB_thumbs.hh:21
ThumbSource
Definition IMB_thumbs.hh:25
@ THB_SOURCE_IMAGE
Definition IMB_thumbs.hh:26
ImBuf * IMB_thumb_manage(const char *file_or_lib_path, ThumbSize size, ThumbSource source)
Definition thumbs.cc:533
#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
struct ImBuf * IMB_allocImBuf(unsigned int, unsigned int, unsigned char, unsigned int)
void IMB_freeImBuf(ImBuf *)
#define GS(x)
Definition iris.cc:202
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
static GHash * gCachedPreviews
static PreviewImage * previewimg_deferred_create(const char *filepath, ThumbSource source)
#define ID_PRV_CASE(id_code, id_struct)
unsigned char uint8_t
Definition stdint.h:78
Definition DNA_ID.h:413
int icon_id
Definition DNA_ID.h:436
ImBufByteBuffer byte_buffer
PreviewImageRuntimeHandle * runtime
Definition DNA_ID.h:609
unsigned int h[2]
Definition DNA_ID.h:604
short changed_timestamp[2]
Definition DNA_ID.h:606
short flag[2]
Definition DNA_ID.h:605
unsigned int * rect[2]
Definition DNA_ID.h:607
unsigned int w[2]
Definition DNA_ID.h:603
std::unique_ptr< PreviewDeferredLoadingData > deferred_loading_data