1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2006-2007 Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup bke
22 */
23
24 #include <math.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "CLG_log.h"
29
30 #include "MEM_guardedalloc.h"
31
32 #include "DNA_brush_types.h"
33 #include "DNA_collection_types.h"
34 #include "DNA_gpencil_types.h"
35 #include "DNA_light_types.h"
36 #include "DNA_material_types.h"
37 #include "DNA_object_types.h"
38 #include "DNA_scene_types.h"
39 #include "DNA_screen_types.h"
40 #include "DNA_texture_types.h"
41 #include "DNA_world_types.h"
42
43 #include "BLI_fileops.h"
44 #include "BLI_ghash.h"
45 #include "BLI_linklist_lockfree.h"
46 #include "BLI_string.h"
47 #include "BLI_threads.h"
48 #include "BLI_utildefines.h"
49
50 #include "BKE_global.h" /* only for G.background test */
51 #include "BKE_icons.h"
52 #include "BKE_studiolight.h"
53
54 #include "BLI_sys_types.h" /* for intptr_t support */
55
56 #include "GPU_texture.h"
57
58 #include "IMB_imbuf.h"
59 #include "IMB_imbuf_types.h"
60 #include "IMB_thumbs.h"
61
62 #include "BLO_read_write.h"
63
64 /**
65 * Only allow non-managed icons to be removed (by Python for eg).
66 * Previews & ID's have their own functions to remove icons.
67 */
68 enum {
69 ICON_FLAG_MANAGED = (1 << 0),
70 };
71
72 /* GLOBALS */
73
74 static CLG_LogRef LOG = {"bke.icons"};
75
76 static GHash *gIcons = NULL;
77
78 static int gNextIconId = 1;
79
80 static int gFirstIconId = 1;
81
82 static GHash *gCachedPreviews = NULL;
83
84 /* Queue of icons for deferred deletion. */
85 typedef struct DeferredIconDeleteNode {
86 struct DeferredIconDeleteNode *next;
87 int icon_id;
88 } DeferredIconDeleteNode;
89 static LockfreeLinkList g_icon_delete_queue;
90
icon_free(void * val)91 static void icon_free(void *val)
92 {
93 Icon *icon = val;
94
95 if (icon) {
96 if (icon->obj_type == ICON_DATA_GEOM) {
97 struct Icon_Geom *obj = icon->obj;
98 if (obj->mem) {
99 /* coords & colors are part of this memory. */
100 MEM_freeN((void *)obj->mem);
101 }
102 else {
103 MEM_freeN((void *)obj->coords);
104 MEM_freeN((void *)obj->colors);
105 }
106 MEM_freeN(icon->obj);
107 }
108
109 if (icon->drawinfo_free) {
110 icon->drawinfo_free(icon->drawinfo);
111 }
112 else if (icon->drawinfo) {
113 MEM_freeN(icon->drawinfo);
114 }
115 MEM_freeN(icon);
116 }
117 }
118
icon_free_data(int icon_id,Icon * icon)119 static void icon_free_data(int icon_id, Icon *icon)
120 {
121 if (icon->obj_type == ICON_DATA_ID) {
122 ((ID *)(icon->obj))->icon_id = 0;
123 }
124 else if (icon->obj_type == ICON_DATA_PREVIEW) {
125 ((PreviewImage *)(icon->obj))->icon_id = 0;
126 }
127 else if (icon->obj_type == ICON_DATA_GPLAYER) {
128 ((bGPDlayer *)(icon->obj))->runtime.icon_id = 0;
129 }
130 else if (icon->obj_type == ICON_DATA_GEOM) {
131 ((struct Icon_Geom *)(icon->obj))->icon_id = 0;
132 }
133 else if (icon->obj_type == ICON_DATA_STUDIOLIGHT) {
134 StudioLight *sl = icon->obj;
135 if (sl != NULL) {
136 BKE_studiolight_unset_icon_id(sl, icon_id);
137 }
138 }
139 else {
140 BLI_assert(0);
141 }
142 }
143
144 /* create an id for a new icon and make sure that ids from deleted icons get reused
145 * after the integer number range is used up */
get_next_free_id(void)146 static int get_next_free_id(void)
147 {
148 BLI_assert(BLI_thread_is_main());
149 int startId = gFirstIconId;
150
151 /* if we haven't used up the int number range, we just return the next int */
152 if (gNextIconId >= gFirstIconId) {
153 return gNextIconId++;
154 }
155
156 /* now we try to find the smallest icon id not stored in the gIcons hash */
157 while (BLI_ghash_lookup(gIcons, POINTER_FROM_INT(startId)) && startId >= gFirstIconId) {
158 startId++;
159 }
160
161 /* if we found a suitable one that isn't used yet, return it */
162 if (startId >= gFirstIconId) {
163 return startId;
164 }
165
166 /* fail */
167 return 0;
168 }
169
BKE_icons_init(int first_dyn_id)170 void BKE_icons_init(int first_dyn_id)
171 {
172 BLI_assert(BLI_thread_is_main());
173
174 gNextIconId = first_dyn_id;
175 gFirstIconId = first_dyn_id;
176
177 if (!gIcons) {
178 gIcons = BLI_ghash_int_new(__func__);
179 BLI_linklist_lockfree_init(&g_icon_delete_queue);
180 }
181
182 if (!gCachedPreviews) {
183 gCachedPreviews = BLI_ghash_str_new(__func__);
184 }
185 }
186
BKE_icons_free(void)187 void BKE_icons_free(void)
188 {
189 BLI_assert(BLI_thread_is_main());
190
191 if (gIcons) {
192 BLI_ghash_free(gIcons, NULL, icon_free);
193 gIcons = NULL;
194 }
195
196 if (gCachedPreviews) {
197 BLI_ghash_free(gCachedPreviews, MEM_freeN, BKE_previewimg_freefunc);
198 gCachedPreviews = NULL;
199 }
200
201 BLI_linklist_lockfree_free(&g_icon_delete_queue, MEM_freeN);
202 }
203
BKE_icons_deferred_free(void)204 void BKE_icons_deferred_free(void)
205 {
206 BLI_assert(BLI_thread_is_main());
207
208 for (DeferredIconDeleteNode *node =
209 (DeferredIconDeleteNode *)BLI_linklist_lockfree_begin(&g_icon_delete_queue);
210 node != NULL;
211 node = node->next) {
212 BLI_ghash_remove(gIcons, POINTER_FROM_INT(node->icon_id), NULL, icon_free);
213 }
214 BLI_linklist_lockfree_clear(&g_icon_delete_queue, MEM_freeN);
215 }
216
previewimg_create_ex(size_t deferred_data_size)217 static PreviewImage *previewimg_create_ex(size_t deferred_data_size)
218 {
219 PreviewImage *prv_img = MEM_mallocN(sizeof(PreviewImage) + deferred_data_size, "img_prv");
220 memset(prv_img, 0, sizeof(*prv_img)); /* leave deferred data dirty */
221
222 if (deferred_data_size) {
223 prv_img->tag |= PRV_TAG_DEFFERED;
224 }
225
226 for (int i = 0; i < NUM_ICON_SIZES; i++) {
227 prv_img->flag[i] |= PRV_CHANGED;
228 prv_img->changed_timestamp[i] = 0;
229 }
230 return prv_img;
231 }
232
BKE_previewimg_create(void)233 PreviewImage *BKE_previewimg_create(void)
234 {
235 return previewimg_create_ex(0);
236 }
237
BKE_previewimg_freefunc(void * link)238 void BKE_previewimg_freefunc(void *link)
239 {
240 PreviewImage *prv = (PreviewImage *)link;
241 if (prv) {
242 for (int i = 0; i < NUM_ICON_SIZES; i++) {
243 if (prv->rect[i]) {
244 MEM_freeN(prv->rect[i]);
245 }
246 if (prv->gputexture[i]) {
247 GPU_texture_free(prv->gputexture[i]);
248 }
249 }
250
251 MEM_freeN(prv);
252 }
253 }
254
BKE_previewimg_free(PreviewImage ** prv)255 void BKE_previewimg_free(PreviewImage **prv)
256 {
257 if (prv && (*prv)) {
258 BKE_previewimg_freefunc(*prv);
259 *prv = NULL;
260 }
261 }
262
BKE_previewimg_clear_single(struct PreviewImage * prv,enum eIconSizes size)263 void BKE_previewimg_clear_single(struct PreviewImage *prv, enum eIconSizes size)
264 {
265 MEM_SAFE_FREE(prv->rect[size]);
266 if (prv->gputexture[size]) {
267 GPU_texture_free(prv->gputexture[size]);
268 }
269 prv->h[size] = prv->w[size] = 0;
270 prv->flag[size] |= PRV_CHANGED;
271 prv->flag[size] &= ~PRV_USER_EDITED;
272 prv->changed_timestamp[size] = 0;
273 }
274
BKE_previewimg_clear(struct PreviewImage * prv)275 void BKE_previewimg_clear(struct PreviewImage *prv)
276 {
277 for (int i = 0; i < NUM_ICON_SIZES; i++) {
278 BKE_previewimg_clear_single(prv, i);
279 }
280 }
281
BKE_previewimg_copy(const PreviewImage * prv)282 PreviewImage *BKE_previewimg_copy(const PreviewImage *prv)
283 {
284 PreviewImage *prv_img = NULL;
285
286 if (prv) {
287 prv_img = MEM_dupallocN(prv);
288 for (int i = 0; i < NUM_ICON_SIZES; i++) {
289 if (prv->rect[i]) {
290 prv_img->rect[i] = MEM_dupallocN(prv->rect[i]);
291 }
292 prv_img->gputexture[i] = NULL;
293 }
294 }
295 return prv_img;
296 }
297
298 /**
299 * Duplicate preview image from \a id and clear icon_id,
300 * to be used by datablock copy functions.
301 */
BKE_previewimg_id_copy(ID * new_id,const ID * old_id)302 void BKE_previewimg_id_copy(ID *new_id, const ID *old_id)
303 {
304 PreviewImage **old_prv_p = BKE_previewimg_id_get_p(old_id);
305 PreviewImage **new_prv_p = BKE_previewimg_id_get_p(new_id);
306
307 if (old_prv_p && *old_prv_p) {
308 BLI_assert(new_prv_p != NULL && ELEM(*new_prv_p, NULL, *old_prv_p));
309 // const int new_icon_id = get_next_free_id();
310
311 // if (new_icon_id == 0) {
312 // return; /* Failure. */
313 // }
314 *new_prv_p = BKE_previewimg_copy(*old_prv_p);
315 new_id->icon_id = (*new_prv_p)->icon_id = 0;
316 }
317 }
318
BKE_previewimg_id_get_p(const ID * id)319 PreviewImage **BKE_previewimg_id_get_p(const ID *id)
320 {
321 switch (GS(id->name)) {
322 #define ID_PRV_CASE(id_code, id_struct) \
323 case id_code: { \
324 return &((id_struct *)id)->preview; \
325 } \
326 ((void)0)
327 ID_PRV_CASE(ID_MA, Material);
328 ID_PRV_CASE(ID_TE, Tex);
329 ID_PRV_CASE(ID_WO, World);
330 ID_PRV_CASE(ID_LA, Light);
331 ID_PRV_CASE(ID_IM, Image);
332 ID_PRV_CASE(ID_BR, Brush);
333 ID_PRV_CASE(ID_OB, Object);
334 ID_PRV_CASE(ID_GR, Collection);
335 ID_PRV_CASE(ID_SCE, Scene);
336 ID_PRV_CASE(ID_SCR, bScreen);
337 #undef ID_PRV_CASE
338 default:
339 break;
340 }
341
342 return NULL;
343 }
344
BKE_previewimg_id_free(ID * id)345 void BKE_previewimg_id_free(ID *id)
346 {
347 PreviewImage **prv_p = BKE_previewimg_id_get_p(id);
348 if (prv_p) {
349 BKE_previewimg_free(prv_p);
350 }
351 }
352
BKE_previewimg_id_ensure(ID * id)353 PreviewImage *BKE_previewimg_id_ensure(ID *id)
354 {
355 PreviewImage **prv_p = BKE_previewimg_id_get_p(id);
356
357 if (prv_p) {
358 if (*prv_p == NULL) {
359 *prv_p = BKE_previewimg_create();
360 }
361 return *prv_p;
362 }
363
364 return NULL;
365 }
366
BKE_previewimg_cached_get(const char * name)367 PreviewImage *BKE_previewimg_cached_get(const char *name)
368 {
369 return BLI_ghash_lookup(gCachedPreviews, name);
370 }
371
372 /**
373 * Generate an empty PreviewImage, if not yet existing.
374 */
BKE_previewimg_cached_ensure(const char * name)375 PreviewImage *BKE_previewimg_cached_ensure(const char *name)
376 {
377 PreviewImage *prv = NULL;
378 void **key_p, **prv_p;
379
380 if (!BLI_ghash_ensure_p_ex(gCachedPreviews, name, &key_p, &prv_p)) {
381 *key_p = BLI_strdup(name);
382 *prv_p = BKE_previewimg_create();
383 }
384 prv = *prv_p;
385 BLI_assert(prv);
386
387 return prv;
388 }
389
390 /**
391 * Generate a PreviewImage from given file path, using thumbnails management, if not yet existing.
392 */
BKE_previewimg_cached_thumbnail_read(const char * name,const char * path,const int source,bool force_update)393 PreviewImage *BKE_previewimg_cached_thumbnail_read(const char *name,
394 const char *path,
395 const int source,
396 bool force_update)
397 {
398 PreviewImage *prv = NULL;
399 void **prv_p;
400
401 prv_p = BLI_ghash_lookup_p(gCachedPreviews, name);
402
403 if (prv_p) {
404 prv = *prv_p;
405 BLI_assert(prv);
406 }
407
408 if (prv && force_update) {
409 const char *prv_deferred_data = PRV_DEFERRED_DATA(prv);
410 if (((int)prv_deferred_data[0] == source) && STREQ(&prv_deferred_data[1], path)) {
411 /* If same path, no need to re-allocate preview, just clear it up. */
412 BKE_previewimg_clear(prv);
413 }
414 else {
415 BKE_previewimg_free(&prv);
416 }
417 }
418
419 if (!prv) {
420 /* We pack needed data for lazy loading (source type, in a single char, and path). */
421 const size_t deferred_data_size = strlen(path) + 2;
422 char *deferred_data;
423
424 prv = previewimg_create_ex(deferred_data_size);
425 deferred_data = PRV_DEFERRED_DATA(prv);
426 deferred_data[0] = source;
427 memcpy(&deferred_data[1], path, deferred_data_size - 1);
428
429 force_update = true;
430 }
431
432 if (force_update) {
433 if (prv_p) {
434 *prv_p = prv;
435 }
436 else {
437 BLI_ghash_insert(gCachedPreviews, BLI_strdup(name), prv);
438 }
439 }
440
441 return prv;
442 }
443
BKE_previewimg_cached_release_pointer(PreviewImage * prv)444 void BKE_previewimg_cached_release_pointer(PreviewImage *prv)
445 {
446 if (prv) {
447 if (prv->tag & PRV_TAG_DEFFERED_RENDERING) {
448 /* We cannot delete the preview while it is being loaded in another thread... */
449 prv->tag |= PRV_TAG_DEFFERED_DELETE;
450 return;
451 }
452 if (prv->icon_id) {
453 BKE_icon_delete(prv->icon_id);
454 }
455 BKE_previewimg_freefunc(prv);
456 }
457 }
458
BKE_previewimg_cached_release(const char * name)459 void BKE_previewimg_cached_release(const char *name)
460 {
461 PreviewImage *prv = BLI_ghash_popkey(gCachedPreviews, name, MEM_freeN);
462
463 BKE_previewimg_cached_release_pointer(prv);
464 }
465
466 /**
467 * Handle deferred (lazy) loading/generation of preview image, if needed.
468 * For now, only used with file thumbnails.
469 */
BKE_previewimg_ensure(PreviewImage * prv,const int size)470 void BKE_previewimg_ensure(PreviewImage *prv, const int size)
471 {
472 if ((prv->tag & PRV_TAG_DEFFERED) != 0) {
473 const bool do_icon = ((size == ICON_SIZE_ICON) && !prv->rect[ICON_SIZE_ICON]);
474 const bool do_preview = ((size == ICON_SIZE_PREVIEW) && !prv->rect[ICON_SIZE_PREVIEW]);
475
476 if (do_icon || do_preview) {
477 ImBuf *thumb;
478 char *prv_deferred_data = PRV_DEFERRED_DATA(prv);
479 int source = prv_deferred_data[0];
480 char *path = &prv_deferred_data[1];
481 int icon_w, icon_h;
482
483 thumb = IMB_thumb_manage(path, THB_LARGE, source);
484
485 if (thumb) {
486 /* PreviewImage assumes premultiplied alhpa... */
487 IMB_premultiply_alpha(thumb);
488
489 if (do_preview) {
490 prv->w[ICON_SIZE_PREVIEW] = thumb->x;
491 prv->h[ICON_SIZE_PREVIEW] = thumb->y;
492 prv->rect[ICON_SIZE_PREVIEW] = MEM_dupallocN(thumb->rect);
493 prv->flag[ICON_SIZE_PREVIEW] &= ~(PRV_CHANGED | PRV_USER_EDITED);
494 }
495 if (do_icon) {
496 if (thumb->x > thumb->y) {
497 icon_w = ICON_RENDER_DEFAULT_HEIGHT;
498 icon_h = (thumb->y * icon_w) / thumb->x + 1;
499 }
500 else if (thumb->x < thumb->y) {
501 icon_h = ICON_RENDER_DEFAULT_HEIGHT;
502 icon_w = (thumb->x * icon_h) / thumb->y + 1;
503 }
504 else {
505 icon_w = icon_h = ICON_RENDER_DEFAULT_HEIGHT;
506 }
507
508 IMB_scaleImBuf(thumb, icon_w, icon_h);
509 prv->w[ICON_SIZE_ICON] = icon_w;
510 prv->h[ICON_SIZE_ICON] = icon_h;
511 prv->rect[ICON_SIZE_ICON] = MEM_dupallocN(thumb->rect);
512 prv->flag[ICON_SIZE_ICON] &= ~(PRV_CHANGED | PRV_USER_EDITED);
513 }
514 IMB_freeImBuf(thumb);
515 }
516 }
517 }
518 }
519
BKE_previewimg_blend_write(BlendWriter * writer,const PreviewImage * prv)520 void BKE_previewimg_blend_write(BlendWriter *writer, const PreviewImage *prv)
521 {
522 /* Note we write previews also for undo steps. It takes up some memory,
523 * but not doing so would causes all previews to be re-rendered after
524 * undo which is too expensive. */
525
526 if (prv == NULL) {
527 return;
528 }
529
530 PreviewImage prv_copy = *prv;
531 /* don't write out large previews if not requested */
532 if (!(U.flag & USER_SAVE_PREVIEWS)) {
533 prv_copy.w[1] = 0;
534 prv_copy.h[1] = 0;
535 prv_copy.rect[1] = NULL;
536 }
537 BLO_write_struct_at_address(writer, PreviewImage, prv, &prv_copy);
538 if (prv_copy.rect[0]) {
539 BLO_write_uint32_array(writer, prv_copy.w[0] * prv_copy.h[0], prv_copy.rect[0]);
540 }
541 if (prv_copy.rect[1]) {
542 BLO_write_uint32_array(writer, prv_copy.w[1] * prv_copy.h[1], prv_copy.rect[1]);
543 }
544 }
545
BKE_previewimg_blend_read(BlendDataReader * reader,PreviewImage * prv)546 void BKE_previewimg_blend_read(BlendDataReader *reader, PreviewImage *prv)
547 {
548 if (prv == NULL) {
549 return;
550 }
551
552 for (int i = 0; i < NUM_ICON_SIZES; i++) {
553 if (prv->rect[i]) {
554 BLO_read_data_address(reader, &prv->rect[i]);
555 }
556 prv->gputexture[i] = NULL;
557 }
558 prv->icon_id = 0;
559 prv->tag = 0;
560 }
561
BKE_icon_changed(const int icon_id)562 void BKE_icon_changed(const int icon_id)
563 {
564 BLI_assert(BLI_thread_is_main());
565
566 Icon *icon = NULL;
567
568 if (!icon_id || G.background) {
569 return;
570 }
571
572 icon = BLI_ghash_lookup(gIcons, POINTER_FROM_INT(icon_id));
573
574 if (icon) {
575 /* We *only* expect ID-tied icons here, not non-ID icon/preview! */
576 BLI_assert(icon->id_type != 0);
577 BLI_assert(icon->obj_type == ICON_DATA_ID);
578
579 /* Do not enforce creation of previews for valid ID types using BKE_previewimg_id_ensure()
580 * here, we only want to ensure *existing* preview images are properly tagged as
581 * changed/invalid, that's all. */
582 PreviewImage **p_prv = BKE_previewimg_id_get_p((ID *)icon->obj);
583
584 /* If we have previews, they all are now invalid changed. */
585 if (p_prv && *p_prv) {
586 for (int i = 0; i < NUM_ICON_SIZES; i++) {
587 (*p_prv)->flag[i] |= PRV_CHANGED;
588 (*p_prv)->changed_timestamp[i]++;
589 }
590 }
591 }
592 }
593
icon_create(int icon_id,int obj_type,void * obj)594 static Icon *icon_create(int icon_id, int obj_type, void *obj)
595 {
596 Icon *new_icon = MEM_mallocN(sizeof(Icon), __func__);
597
598 new_icon->obj_type = obj_type;
599 new_icon->obj = obj;
600 new_icon->id_type = 0;
601 new_icon->flag = 0;
602
603 /* next two lines make sure image gets created */
604 new_icon->drawinfo = NULL;
605 new_icon->drawinfo_free = NULL;
606
607 BLI_ghash_insert(gIcons, POINTER_FROM_INT(icon_id), new_icon);
608
609 return new_icon;
610 }
611
icon_id_ensure_create_icon(struct ID * id)612 static int icon_id_ensure_create_icon(struct ID *id)
613 {
614 BLI_assert(BLI_thread_is_main());
615
616 Icon *icon = icon_create(id->icon_id, ICON_DATA_ID, id);
617 icon->id_type = GS(id->name);
618 icon->flag = ICON_FLAG_MANAGED;
619
620 return id->icon_id;
621 }
622
BKE_icon_id_ensure(struct ID * id)623 int BKE_icon_id_ensure(struct ID *id)
624 {
625 /* Never handle icons in non-main thread! */
626 BLI_assert(BLI_thread_is_main());
627
628 if (!id || G.background) {
629 return 0;
630 }
631
632 if (id->icon_id) {
633 return id->icon_id;
634 }
635
636 id->icon_id = get_next_free_id();
637
638 if (!id->icon_id) {
639 CLOG_ERROR(&LOG, "not enough IDs");
640 return 0;
641 }
642
643 /* Ensure we synchronize ID icon_id with its previewimage if it has one. */
644 PreviewImage **p_prv = BKE_previewimg_id_get_p(id);
645 if (p_prv && *p_prv) {
646 BLI_assert(ELEM((*p_prv)->icon_id, 0, id->icon_id));
647 (*p_prv)->icon_id = id->icon_id;
648 }
649
650 return icon_id_ensure_create_icon(id);
651 }
652
icon_gplayer_color_ensure_create_icon(bGPDlayer * gpl)653 static int icon_gplayer_color_ensure_create_icon(bGPDlayer *gpl)
654 {
655 BLI_assert(BLI_thread_is_main());
656
657 /* NOTE: The color previews for GP Layers don't really need
658 * to be "rendered" to image per se (as it will just be a plain
659 * colored rectangle), we need to define icon data here so that
660 * we can store a pointer to the layer data in icon->obj.
661 */
662 Icon *icon = icon_create(gpl->runtime.icon_id, ICON_DATA_GPLAYER, gpl);
663 icon->flag = ICON_FLAG_MANAGED;
664
665 return gpl->runtime.icon_id;
666 }
667
BKE_icon_gplayer_color_ensure(bGPDlayer * gpl)668 int BKE_icon_gplayer_color_ensure(bGPDlayer *gpl)
669 {
670 /* Never handle icons in non-main thread! */
671 BLI_assert(BLI_thread_is_main());
672
673 if (!gpl || G.background) {
674 return 0;
675 }
676
677 if (gpl->runtime.icon_id) {
678 return gpl->runtime.icon_id;
679 }
680
681 gpl->runtime.icon_id = get_next_free_id();
682
683 if (!gpl->runtime.icon_id) {
684 CLOG_ERROR(&LOG, "not enough IDs");
685 return 0;
686 }
687
688 return icon_gplayer_color_ensure_create_icon(gpl);
689 }
690
691 /**
692 * Return icon id of given preview, or create new icon if not found.
693 */
BKE_icon_preview_ensure(ID * id,PreviewImage * preview)694 int BKE_icon_preview_ensure(ID *id, PreviewImage *preview)
695 {
696 if (!preview || G.background) {
697 return 0;
698 }
699
700 if (id) {
701 BLI_assert(BKE_previewimg_id_ensure(id) == preview);
702 }
703
704 if (preview->icon_id) {
705 BLI_assert(!id || !id->icon_id || id->icon_id == preview->icon_id);
706 return preview->icon_id;
707 }
708
709 if (id && id->icon_id) {
710 preview->icon_id = id->icon_id;
711 return preview->icon_id;
712 }
713
714 preview->icon_id = get_next_free_id();
715
716 if (!preview->icon_id) {
717 CLOG_ERROR(&LOG, "not enough IDs");
718 return 0;
719 }
720
721 /* Ensure we synchronize ID icon_id with its previewimage if available,
722 * and generate suitable 'ID' icon. */
723 if (id) {
724 id->icon_id = preview->icon_id;
725 return icon_id_ensure_create_icon(id);
726 }
727
728 Icon *icon = icon_create(preview->icon_id, ICON_DATA_PREVIEW, preview);
729 icon->flag = ICON_FLAG_MANAGED;
730
731 return preview->icon_id;
732 }
733
BKE_icon_get(const int icon_id)734 Icon *BKE_icon_get(const int icon_id)
735 {
736 BLI_assert(BLI_thread_is_main());
737
738 Icon *icon = NULL;
739
740 icon = BLI_ghash_lookup(gIcons, POINTER_FROM_INT(icon_id));
741
742 if (!icon) {
743 CLOG_ERROR(&LOG, "no icon for icon ID: %d", icon_id);
744 return NULL;
745 }
746
747 return icon;
748 }
749
BKE_icon_set(const int icon_id,struct Icon * icon)750 void BKE_icon_set(const int icon_id, struct Icon *icon)
751 {
752 BLI_assert(BLI_thread_is_main());
753
754 void **val_p;
755
756 if (BLI_ghash_ensure_p(gIcons, POINTER_FROM_INT(icon_id), &val_p)) {
757 CLOG_ERROR(&LOG, "icon already set: %d", icon_id);
758 return;
759 }
760
761 *val_p = icon;
762 }
763
icon_add_to_deferred_delete_queue(int icon_id)764 static void icon_add_to_deferred_delete_queue(int icon_id)
765 {
766 DeferredIconDeleteNode *node = MEM_mallocN(sizeof(DeferredIconDeleteNode), __func__);
767 node->icon_id = icon_id;
768 BLI_linklist_lockfree_insert(&g_icon_delete_queue, (LockfreeLinkNode *)node);
769 }
770
BKE_icon_id_delete(struct ID * id)771 void BKE_icon_id_delete(struct ID *id)
772 {
773 const int icon_id = id->icon_id;
774 if (!icon_id) {
775 return; /* no icon defined for library object */
776 }
777 id->icon_id = 0;
778
779 if (!BLI_thread_is_main()) {
780 icon_add_to_deferred_delete_queue(icon_id);
781 return;
782 }
783
784 BKE_icons_deferred_free();
785 BLI_ghash_remove(gIcons, POINTER_FROM_INT(icon_id), NULL, icon_free);
786 }
787
788 /**
789 * Remove icon and free data.
790 */
BKE_icon_delete(const int icon_id)791 bool BKE_icon_delete(const int icon_id)
792 {
793 if (icon_id == 0) {
794 /* no icon defined for library object */
795 return false;
796 }
797
798 Icon *icon = BLI_ghash_popkey(gIcons, POINTER_FROM_INT(icon_id), NULL);
799 if (icon) {
800 icon_free_data(icon_id, icon);
801 icon_free(icon);
802 return true;
803 }
804
805 return false;
806 }
807
BKE_icon_delete_unmanaged(const int icon_id)808 bool BKE_icon_delete_unmanaged(const int icon_id)
809 {
810 if (icon_id == 0) {
811 /* no icon defined for library object */
812 return false;
813 }
814
815 Icon *icon = BLI_ghash_popkey(gIcons, POINTER_FROM_INT(icon_id), NULL);
816 if (icon) {
817 if (UNLIKELY(icon->flag & ICON_FLAG_MANAGED)) {
818 BLI_ghash_insert(gIcons, POINTER_FROM_INT(icon_id), icon);
819 return false;
820 }
821
822 icon_free_data(icon_id, icon);
823 icon_free(icon);
824 return true;
825 }
826
827 return false;
828 }
829
830 /* -------------------------------------------------------------------- */
831 /** \name Geometry Icon
832 * \{ */
833
BKE_icon_geom_ensure(struct Icon_Geom * geom)834 int BKE_icon_geom_ensure(struct Icon_Geom *geom)
835 {
836 BLI_assert(BLI_thread_is_main());
837
838 if (geom->icon_id) {
839 return geom->icon_id;
840 }
841
842 geom->icon_id = get_next_free_id();
843
844 icon_create(geom->icon_id, ICON_DATA_GEOM, geom);
845 /* Not managed for now, we may want this to be configurable per icon). */
846
847 return geom->icon_id;
848 }
849
BKE_icon_geom_from_memory(const uchar * data,size_t data_len)850 struct Icon_Geom *BKE_icon_geom_from_memory(const uchar *data, size_t data_len)
851 {
852 BLI_assert(BLI_thread_is_main());
853 if (data_len <= 8) {
854 goto fail;
855 }
856 /* Skip the header. */
857 data_len -= 8;
858 const int div = 3 * 2 * 3;
859 const int coords_len = data_len / div;
860 if (coords_len * div != data_len) {
861 goto fail;
862 }
863
864 const uchar header[4] = {'V', 'C', 'O', 0};
865 const uchar *p = data;
866 if (memcmp(p, header, ARRAY_SIZE(header)) != 0) {
867 goto fail;
868 }
869 p += 4;
870
871 struct Icon_Geom *geom = MEM_mallocN(sizeof(*geom), __func__);
872 geom->coords_range[0] = (int)*p++;
873 geom->coords_range[1] = (int)*p++;
874 /* x, y ignored for now */
875 p += 2;
876
877 geom->coords_len = coords_len;
878 geom->coords = (void *)p;
879 geom->colors = (void *)(p + (data_len / 3));
880 geom->icon_id = 0;
881 geom->mem = data;
882 return geom;
883
884 fail:
885 MEM_freeN((void *)data);
886 return NULL;
887 }
888
BKE_icon_geom_from_file(const char * filename)889 struct Icon_Geom *BKE_icon_geom_from_file(const char *filename)
890 {
891 BLI_assert(BLI_thread_is_main());
892 size_t data_len;
893 uchar *data = BLI_file_read_binary_as_mem(filename, 0, &data_len);
894 if (data == NULL) {
895 return NULL;
896 }
897 return BKE_icon_geom_from_memory(data, data_len);
898 }
899
900 /** \} */
901
902 /** \name Studio Light Icon
903 * \{ */
BKE_icon_ensure_studio_light(struct StudioLight * sl,int id_type)904 int BKE_icon_ensure_studio_light(struct StudioLight *sl, int id_type)
905 {
906 int icon_id = get_next_free_id();
907 Icon *icon = icon_create(icon_id, ICON_DATA_STUDIOLIGHT, sl);
908 icon->id_type = id_type;
909 return icon_id;
910 }
911 /** \} */
912