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