Blender V4.3
icons.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 <cmath>
10#include <cstdlib>
11#include <cstring>
12#include <memory>
13#include <mutex>
14
15#include "CLG_log.h"
16
17#include "MEM_guardedalloc.h"
18
19#include "DNA_ID.h"
21
22#include "BLI_fileops.h"
23#include "BLI_ghash.h"
25#include "BLI_threads.h"
26#include "BLI_utildefines.h"
27
28#include "BKE_global.hh" /* only for G.background test */
29#include "BKE_icons.h"
30#include "BKE_preview_image.hh"
31#include "BKE_studiolight.h"
32
33#include "BLI_sys_types.h" /* for intptr_t support */
34
35#include "IMB_imbuf.hh"
36#include "IMB_imbuf_types.hh"
37
42enum {
44};
45
46/* GLOBALS */
47
48static CLG_LogRef LOG = {"bke.icons"};
49
50/* Protected by gIconMutex. */
51static GHash *gIcons = nullptr;
52
53/* Protected by gIconMutex. */
54static int gNextIconId = 1;
55
56/* Protected by gIconMutex. */
57static int gFirstIconId = 1;
58
59static std::mutex gIconMutex;
60
61/* Queue of icons for deferred deletion. */
66/* Protected by gIconMutex. */
68
69static void icon_free(void *val)
70{
71 Icon *icon = (Icon *)val;
72 if (!icon) {
73 return;
74 }
75
76 if (icon->obj_type == ICON_DATA_GEOM) {
77 Icon_Geom *obj = (Icon_Geom *)icon->obj;
78 if (obj->mem) {
79 /* coords & colors are part of this memory. */
80 MEM_freeN((void *)obj->mem);
81 }
82 else {
83 MEM_freeN((void *)obj->coords);
84 MEM_freeN((void *)obj->colors);
85 }
86 MEM_freeN(icon->obj);
87 }
88
89 if (icon->drawinfo_free) {
90 icon->drawinfo_free(icon->drawinfo);
91 }
92 else if (icon->drawinfo) {
93 MEM_freeN(icon->drawinfo);
94 }
95 MEM_freeN(icon);
96}
97
98static void icon_free_data(int icon_id, Icon *icon)
99{
100 switch (icon->obj_type) {
101 case ICON_DATA_ID:
102 ((ID *)(icon->obj))->icon_id = 0;
103 break;
104 case ICON_DATA_IMBUF: {
105 ImBuf *imbuf = (ImBuf *)icon->obj;
106 if (imbuf) {
107 IMB_freeImBuf(imbuf);
108 }
109 break;
110 }
112 ((PreviewImage *)(icon->obj))->runtime->icon_id = 0;
113 break;
115 ((bGPDlayer *)(icon->obj))->runtime.icon_id = 0;
116 break;
117 case ICON_DATA_GEOM:
118 ((Icon_Geom *)(icon->obj))->icon_id = 0;
119 break;
121 StudioLight *sl = (StudioLight *)icon->obj;
122 if (sl != nullptr) {
124 }
125 break;
126 }
127 default:
129 }
130}
131
132static Icon *icon_ghash_lookup(int icon_id)
133{
134 std::scoped_lock lock(gIconMutex);
135 return (Icon *)BLI_ghash_lookup(gIcons, POINTER_FROM_INT(icon_id));
136}
137
138/* create an id for a new icon and make sure that ids from deleted icons get reused
139 * after the integer number range is used up */
141{
142 std::scoped_lock lock(gIconMutex);
143 int startId = gFirstIconId;
144
145 /* if we haven't used up the int number range, we just return the next int */
146 if (gNextIconId >= gFirstIconId) {
147 int next_id = gNextIconId++;
148 return next_id;
149 }
150
151 /* Now we try to find the smallest icon id not stored in the gIcons hash.
152 * Don't use icon_ghash_lookup here, it would lock recursively (dead-lock). */
153 while (BLI_ghash_lookup(gIcons, POINTER_FROM_INT(startId)) && startId >= gFirstIconId) {
154 startId++;
155 }
156
157 /* if we found a suitable one that isn't used yet, return it */
158 if (startId >= gFirstIconId) {
159 return startId;
160 }
161
162 /* fail */
163 return 0;
164}
165
166void BKE_icons_init(int first_dyn_id)
167{
169
170 gNextIconId = first_dyn_id;
171 gFirstIconId = first_dyn_id;
172
173 if (!gIcons) {
174 gIcons = BLI_ghash_int_new(__func__);
176 }
177}
178
180{
182
183 if (gIcons) {
185 gIcons = nullptr;
186 }
187
189}
190
192{
193 std::scoped_lock lock(gIconMutex);
194
195 for (DeferredIconDeleteNode *node =
197 node != nullptr;
198 node = node->next)
199 {
200 BLI_ghash_remove(gIcons, POINTER_FROM_INT(node->icon_id), nullptr, icon_free);
201 }
203}
204
205void BKE_icon_changed(const int icon_id)
206{
207 Icon *icon = nullptr;
208
209 if (!icon_id || G.background) {
210 return;
211 }
212
213 icon = icon_ghash_lookup(icon_id);
214 if (!icon) {
215 return;
216 }
217
218 /* We *only* expect ID-tied icons here, not non-ID icon/preview! */
219 BLI_assert(icon->id_type != 0);
221
222 /* Do not enforce creation of previews for valid ID types using BKE_previewimg_id_ensure()
223 * here, we only want to ensure *existing* preview images are properly tagged as
224 * changed/invalid, that's all. */
225 PreviewImage **p_prv = BKE_previewimg_id_get_p((ID *)icon->obj);
226
227 /* If we have previews, they all are now invalid changed. */
228 if (p_prv && *p_prv) {
229 for (int i = 0; i < NUM_ICON_SIZES; i++) {
230 (*p_prv)->flag[i] |= PRV_CHANGED;
231 (*p_prv)->changed_timestamp[i]++;
232 }
233 }
234}
235
236static Icon *icon_create(int icon_id, int obj_type, void *obj)
237{
238 Icon *new_icon = (Icon *)MEM_mallocN(sizeof(Icon), __func__);
239
240 new_icon->obj_type = obj_type;
241 new_icon->obj = obj;
242 new_icon->id_type = 0;
243 new_icon->flag = 0;
244
245 /* next two lines make sure image gets created */
246 new_icon->drawinfo = nullptr;
247 new_icon->drawinfo_free = nullptr;
248
249 {
250 std::scoped_lock lock(gIconMutex);
251 BLI_ghash_insert(gIcons, POINTER_FROM_INT(icon_id), new_icon);
252 }
253
254 return new_icon;
255}
256
258{
260
261 Icon *icon = icon_create(id->icon_id, ICON_DATA_ID, id);
262 icon->id_type = GS(id->name);
263 icon->flag = ICON_FLAG_MANAGED;
264
265 return id->icon_id;
266}
267
269{
270 /* Never handle icons in non-main thread! */
272
273 if (!id || G.background) {
274 return 0;
275 }
276
277 if (id->icon_id) {
278 return id->icon_id;
279 }
280
281 id->icon_id = get_next_free_id();
282
283 if (!id->icon_id) {
284 CLOG_ERROR(&LOG, "not enough IDs");
285 return 0;
286 }
287
288 /* Ensure we synchronize ID icon_id with its previewimage if it has one. */
290 if (p_prv && *p_prv) {
291 BLI_assert(ELEM((*p_prv)->runtime->icon_id, 0, id->icon_id));
292 (*p_prv)->runtime->icon_id = id->icon_id;
293 }
294
296}
297
299{
301
302 /* NOTE: The color previews for GP Layers don't really need
303 * to be "rendered" to image per-se (as it will just be a plain
304 * colored rectangle), we need to define icon data here so that
305 * we can store a pointer to the layer data in icon->obj.
306 */
308 icon->flag = ICON_FLAG_MANAGED;
309
310 return gpl->runtime.icon_id;
311}
312
314{
315 /* Never handle icons in non-main thread! */
317
318 if (!gpl || G.background) {
319 return 0;
320 }
321
322 if (gpl->runtime.icon_id) {
323 return gpl->runtime.icon_id;
324 }
325
327
328 if (!gpl->runtime.icon_id) {
329 CLOG_ERROR(&LOG, "not enough IDs");
330 return 0;
331 }
332
334}
335
337{
338 if (!preview || G.background) {
339 return 0;
340 }
341
342 if (id) {
343 BLI_assert(BKE_previewimg_id_ensure(id) == preview);
344 }
345
346 if (preview->runtime->icon_id) {
347 BLI_assert(!id || !id->icon_id || id->icon_id == preview->runtime->icon_id);
348 return preview->runtime->icon_id;
349 }
350
351 if (id && id->icon_id) {
352 preview->runtime->icon_id = id->icon_id;
353 return preview->runtime->icon_id;
354 }
355
356 preview->runtime->icon_id = get_next_free_id();
357
358 if (!preview->runtime->icon_id) {
359 CLOG_ERROR(&LOG, "not enough IDs");
360 return 0;
361 }
362
363 /* Ensure we synchronize ID icon_id with its previewimage if available,
364 * and generate suitable 'ID' icon. */
365 if (id) {
366 id->icon_id = preview->runtime->icon_id;
368 }
369
370 Icon *icon = icon_create(preview->runtime->icon_id, ICON_DATA_PREVIEW, preview);
371 icon->flag = ICON_FLAG_MANAGED;
372
373 return preview->runtime->icon_id;
374}
375
377{
378 int icon_id = get_next_free_id();
379
380 Icon *icon = icon_create(icon_id, ICON_DATA_IMBUF, ibuf);
381 icon->flag = ICON_FLAG_MANAGED;
382
383 return icon_id;
384}
385
387{
388 Icon *icon = icon_ghash_lookup(icon_id);
389 if (!icon) {
390 CLOG_ERROR(&LOG, "no icon for icon ID: %d", icon_id);
391 return nullptr;
392 }
393 if (icon->obj_type != ICON_DATA_IMBUF) {
394 CLOG_ERROR(&LOG, "icon ID does not refer to an imbuf icon: %d", icon_id);
395 return nullptr;
396 }
397
398 return (ImBuf *)icon->obj;
399}
400
401Icon *BKE_icon_get(const int icon_id)
402{
404
405 Icon *icon = nullptr;
406
407 icon = icon_ghash_lookup(icon_id);
408
409 if (!icon) {
410 CLOG_ERROR(&LOG, "no icon for icon ID: %d", icon_id);
411 return nullptr;
412 }
413
414 return icon;
415}
416
417bool BKE_icon_is_preview(const int icon_id)
418{
419 const Icon *icon = BKE_icon_get(icon_id);
420 return icon != nullptr && icon->obj_type == ICON_DATA_PREVIEW;
421}
422
423bool BKE_icon_is_image(const int icon_id)
424{
425 const Icon *icon = BKE_icon_get(icon_id);
426 return icon != nullptr && icon->obj_type == ICON_DATA_IMBUF;
427}
428
429void BKE_icon_set(const int icon_id, Icon *icon)
430{
431 void **val_p;
432
433 std::scoped_lock lock(gIconMutex);
434 if (BLI_ghash_ensure_p(gIcons, POINTER_FROM_INT(icon_id), &val_p)) {
435 CLOG_ERROR(&LOG, "icon already set: %d", icon_id);
436 return;
437 }
438
439 *val_p = icon;
440}
441
442static void icon_add_to_deferred_delete_queue(int icon_id)
443{
445 sizeof(DeferredIconDeleteNode), __func__);
446 node->icon_id = icon_id;
447 /* Doesn't need lock. */
449}
450
452{
453 const int icon_id = id->icon_id;
454 if (!icon_id) {
455 return; /* no icon defined for library object */
456 }
457 id->icon_id = 0;
458
459 if (!BLI_thread_is_main()) {
461 return;
462 }
463
465 std::scoped_lock lock(gIconMutex);
467}
468
469bool BKE_icon_delete(const int icon_id)
470{
471 if (icon_id == 0) {
472 /* no icon defined for library object */
473 return false;
474 }
475
476 std::scoped_lock lock(gIconMutex);
477 if (Icon *icon = (Icon *)BLI_ghash_popkey(gIcons, POINTER_FROM_INT(icon_id), nullptr)) {
478 icon_free_data(icon_id, icon);
479 icon_free(icon);
480 return true;
481 }
482
483 return false;
484}
485
486bool BKE_icon_delete_unmanaged(const int icon_id)
487{
488 if (icon_id == 0) {
489 /* no icon defined for library object */
490 return false;
491 }
492
493 std::scoped_lock lock(gIconMutex);
494
495 Icon *icon = (Icon *)BLI_ghash_popkey(gIcons, POINTER_FROM_INT(icon_id), nullptr);
496 if (icon) {
497 if (UNLIKELY(icon->flag & ICON_FLAG_MANAGED)) {
499 return false;
500 }
501
502 icon_free_data(icon_id, icon);
503 icon_free(icon);
504 return true;
505 }
506
507 return false;
508}
509
510/* -------------------------------------------------------------------- */
515{
517
518 if (geom->icon_id) {
519 return geom->icon_id;
520 }
521
522 geom->icon_id = get_next_free_id();
523
524 icon_create(geom->icon_id, ICON_DATA_GEOM, geom);
525 /* Not managed for now, we may want this to be configurable per icon). */
526
527 return geom->icon_id;
528}
529
531{
533 if (data_len <= 8) {
534 return nullptr;
535 }
536 /* Wrapper for RAII early exit cleanups. */
537 std::unique_ptr<uchar> data_wrapper(std::move(data));
538
539 /* Skip the header. */
540 data_len -= 8;
541 const int div = 3 * 2 * 3;
542 const int coords_len = data_len / div;
543 if (coords_len * div != data_len) {
544 return nullptr;
545 }
546
547 const uchar header[4] = {'V', 'C', 'O', 0};
548 uchar *p = data_wrapper.get();
549 if (memcmp(p, header, ARRAY_SIZE(header)) != 0) {
550 return nullptr;
551 }
552 p += 4;
553
554 Icon_Geom *geom = (Icon_Geom *)MEM_mallocN(sizeof(*geom), __func__);
555 geom->coords_range[0] = int(*p++);
556 geom->coords_range[1] = int(*p++);
557 /* x, y ignored for now */
558 p += 2;
559
560 geom->coords_len = coords_len;
561 geom->coords = reinterpret_cast<decltype(geom->coords)>(p);
562 geom->colors = reinterpret_cast<decltype(geom->colors)>(p + (data_len / 3));
563 geom->icon_id = 0;
564 /* Move buffer ownership to C buffer. */
565 geom->mem = data_wrapper.release();
566 return geom;
567}
568
569Icon_Geom *BKE_icon_geom_from_file(const char *filename)
570{
572 size_t data_len;
573 uchar *data = (uchar *)BLI_file_read_binary_as_mem(filename, 0, &data_len);
574 if (data == nullptr) {
575 return nullptr;
576 }
577 return BKE_icon_geom_from_memory(data, data_len);
578}
579
582/* -------------------------------------------------------------------- */
587{
588 int icon_id = get_next_free_id();
589 Icon *icon = icon_create(icon_id, ICON_DATA_STUDIOLIGHT, sl);
590 icon->id_type = id_type;
591 return icon_id;
592}
593
@ ICON_DATA_IMBUF
Definition BKE_icons.h:34
@ ICON_DATA_STUDIOLIGHT
Definition BKE_icons.h:40
@ ICON_DATA_PREVIEW
Definition BKE_icons.h:36
@ ICON_DATA_ID
Definition BKE_icons.h:32
@ ICON_DATA_GPLAYER
Definition BKE_icons.h:42
@ ICON_DATA_GEOM
Definition BKE_icons.h:38
PreviewImage * BKE_previewimg_id_ensure(ID *id)
PreviewImage ** BKE_previewimg_id_get_p(const ID *id)
void BKE_studiolight_unset_icon_id(StudioLight *sl, int icon_id)
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
File and directory operations.
void * BLI_file_read_binary_as_mem(const char *filepath, size_t pad_bytes, size_t *r_size)
Definition storage.cc:513
void * BLI_ghash_popkey(GHash *gh, const void *key, GHashKeyFreeFP keyfreefp) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:802
bool BLI_ghash_remove(GHash *gh, const void *key, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.c:787
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
GHash * BLI_ghash_int_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
bool BLI_ghash_ensure_p(GHash *gh, void *key, void ***r_val) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:752
unsigned char uchar
int BLI_thread_is_main(void)
Definition threads.cc:179
#define ARRAY_SIZE(arr)
#define POINTER_FROM_INT(i)
#define UNLIKELY(x)
#define ELEM(...)
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:182
ID and Library types, which are fundamental for SDNA.
@ PRV_CHANGED
Definition DNA_ID.h:581
@ NUM_ICON_SIZES
Contains defines and structs used throughout the imbuf module.
Read Guarded memory(de)allocation.
volatile int lock
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
void IMB_freeImBuf(ImBuf *)
static int gFirstIconId
Definition icons.cc:57
int BKE_icon_gplayer_color_ensure(bGPDlayer *gpl)
Definition icons.cc:313
static LockfreeLinkList g_icon_delete_queue
Definition icons.cc:67
static int icon_id_ensure_create_icon(ID *id)
Definition icons.cc:257
bool BKE_icon_is_preview(const int icon_id)
Definition icons.cc:417
void BKE_icon_changed(const int icon_id)
Definition icons.cc:205
void BKE_icons_deferred_free()
Definition icons.cc:191
static void icon_free(void *val)
Definition icons.cc:69
static int get_next_free_id()
Definition icons.cc:140
int BKE_icon_preview_ensure(ID *id, PreviewImage *preview)
Definition icons.cc:336
int BKE_icon_imbuf_create(ImBuf *ibuf)
Definition icons.cc:376
int BKE_icon_ensure_studio_light(StudioLight *sl, int id_type)
Definition icons.cc:586
static void icon_free_data(int icon_id, Icon *icon)
Definition icons.cc:98
static std::mutex gIconMutex
Definition icons.cc:59
void BKE_icon_id_delete(ID *id)
Definition icons.cc:451
void BKE_icons_free()
Definition icons.cc:179
bool BKE_icon_is_image(const int icon_id)
Definition icons.cc:423
static int icon_gplayer_color_ensure_create_icon(bGPDlayer *gpl)
Definition icons.cc:298
static int gNextIconId
Definition icons.cc:54
@ ICON_FLAG_MANAGED
Definition icons.cc:43
Icon_Geom * BKE_icon_geom_from_memory(uchar *data, size_t data_len)
Definition icons.cc:530
int BKE_icon_geom_ensure(Icon_Geom *geom)
Definition icons.cc:514
static Icon * icon_ghash_lookup(int icon_id)
Definition icons.cc:132
static GHash * gIcons
Definition icons.cc:51
static void icon_add_to_deferred_delete_queue(int icon_id)
Definition icons.cc:442
static CLG_LogRef LOG
Definition icons.cc:48
int BKE_icon_id_ensure(ID *id)
Definition icons.cc:268
void BKE_icon_set(const int icon_id, Icon *icon)
Definition icons.cc:429
bool BKE_icon_delete_unmanaged(const int icon_id)
Definition icons.cc:486
Icon_Geom * BKE_icon_geom_from_file(const char *filename)
Definition icons.cc:569
ImBuf * BKE_icon_imbuf_get_buffer(int icon_id)
Definition icons.cc:386
static Icon * icon_create(int icon_id, int obj_type, void *obj)
Definition icons.cc:236
Icon * BKE_icon_get(const int icon_id)
Definition icons.cc:401
void BKE_icons_init(int first_dyn_id)
Definition icons.cc:166
bool BKE_icon_delete(const int icon_id)
Definition icons.cc:469
#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
#define G(x, y, z)
DeferredIconDeleteNode * next
Definition icons.cc:63
Definition DNA_ID.h:413
int coords_len
Definition BKE_icons.h:67
const void * mem
Definition BKE_icons.h:72
unsigned char(* colors)[4]
Definition BKE_icons.h:70
int icon_id
Definition BKE_icons.h:66
unsigned char(* coords)[2]
Definition BKE_icons.h:69
int coords_range[2]
Definition BKE_icons.h:68
void * obj
Definition BKE_icons.h:55
char flag
Definition BKE_icons.h:58
char obj_type
Definition BKE_icons.h:56
void * drawinfo
Definition BKE_icons.h:49
short id_type
Definition BKE_icons.h:60
DrawInfoFreeFP drawinfo_free
Definition BKE_icons.h:61
struct LockfreeLinkNode * next
PreviewImageRuntimeHandle * runtime
Definition DNA_ID.h:609
bGPDlayer_Runtime runtime