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