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