Expose PreviewImage & custom icons to py API.
authorBastien Montagne <montagne29@wanadoo.fr>
Mon, 11 May 2015 14:29:12 +0000 (16:29 +0200)
committerBastien Montagne <montagne29@wanadoo.fr>
Mon, 11 May 2015 14:37:15 +0000 (16:37 +0200)
This commit mainly:

* Exposes PreviewImage struct in RNA, including ways for user to set images data.
* Adds a new kind of PreviewImage, using a file path and IMB_thumb to get image.
* Adds a new kind of custom icon using PreviewImage, unrelated to ID previews system.
* Adds a python API (utils.previews) to allow python scripts to access those custom previews/icons.

Note that loading image from files' thumbnails is done when needed (deferred loading), not
when defining the custom preview/icon.

WARNING: for release addons who would want to use this, please keep it to a strict minimum, really needed level.
We do not want our UI to explode under hundreds of different flashy icons!

For more info, see also the release notes of Blender 2.75 (http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.75/Addons)
and the example/templates featured with Blender.

Patch by Campbell (ideasman42), Inês (brita) and Bastien (mont29).

Differential Revision: https://developer.blender.org/D1255

32 files changed:
release/scripts/modules/bpy/utils/__init__.py
release/scripts/modules/bpy/utils/previews.py [new file with mode: 0644]
release/scripts/templates_py/ui_previews_custom_icon.py [new file with mode: 0644]
release/scripts/templates_py/ui_previews_dynamic_enum.py [new file with mode: 0644]
source/blender/blenkernel/BKE_icons.h
source/blender/blenkernel/intern/icons.c
source/blender/blenkernel/intern/image.c
source/blender/blenkernel/intern/lamp.c
source/blender/blenkernel/intern/material.c
source/blender/blenkernel/intern/texture.c
source/blender/blenkernel/intern/world.c
source/blender/editors/include/ED_render.h
source/blender/editors/include/UI_interface_icons.h
source/blender/editors/interface/interface.c
source/blender/editors/interface/interface_icons.c
source/blender/editors/interface/interface_intern.h
source/blender/editors/interface/interface_templates.c
source/blender/editors/render/render_preview.c
source/blender/editors/render/render_update.c
source/blender/editors/space_image/image_ops.c
source/blender/imbuf/IMB_thumbs.h
source/blender/imbuf/intern/thumbs.c
source/blender/makesdna/DNA_ID.h
source/blender/makesrna/RNA_access.h
source/blender/makesrna/intern/rna_ID.c
source/blender/makesrna/intern/rna_brush.c
source/blender/makesrna/intern/rna_main_api.c
source/blender/python/intern/CMakeLists.txt
source/blender/python/intern/bpy.c
source/blender/python/intern/bpy_utils_previews.c [new file with mode: 0644]
source/blender/python/intern/bpy_utils_previews.h [new file with mode: 0644]
source/blender/windowmanager/intern/wm_init_exit.c

index 5f235ae3958238676486550f2ecf392e927ed53e..fa97714eee61476032026162c1737884d98e194a 100644 (file)
@@ -38,6 +38,7 @@ __all__ = (
     "unregister_manual_map",
     "make_rna_paths",
     "manual_map",
+    "previews",
     "resource_path",
     "script_path_user",
     "script_path_pref",
diff --git a/release/scripts/modules/bpy/utils/previews.py b/release/scripts/modules/bpy/utils/previews.py
new file mode 100644 (file)
index 0000000..4e8adf8
--- /dev/null
@@ -0,0 +1,137 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+"""
+This module contains utility functions to handle custom previews.
+
+It behaves as a high-level 'cached' previews manager.
+
+This allows addons to generate their own previews, and use them as icons in UI widgets
+('icon_value' of UILayout functions).
+"""
+
+__all__ = (
+    "new",
+    "remove",
+    )
+
+import _bpy
+_utils_previews = _bpy._utils_previews
+del _bpy
+
+
+_uuid_open = set()
+
+
+# High-level previews manager.
+# not accessed directly
+class _BPyImagePreviewCollection(dict):
+    """
+    Dict-like class of previews.
+    """
+
+    # Internal notes:
+    # - keys in the dict are stored by name
+    # - values are instances of bpy.types.ImagePreview
+    # - Blender's internal 'PreviewImage' struct uses 'self._uuid' prefix.
+
+    def __init__(self):
+        super().__init__()
+        self._uuid = hex(id(self))
+        _uuid_open.add(self._uuid)
+
+    def __del__(self):
+        if self._uuid not in _uuid_open:
+            return
+
+        raise ResourceWarning(
+                "<%s id=%s[%d]>: left open, remove with "
+                "'bpy.utils.previews.remove()'" %
+                (self.__class__.__name__, self._uuid, len(self)))
+        self.close()
+
+    def _gen_key(self, name):
+        return ":".join((self._uuid, name))
+
+    def new(self, name):
+        if name in self:
+            raise KeyException("key %r already exists")
+        p = self[name] = _utils_previews.new(
+                self._gen_key(name))
+        return p
+    new.__doc__ = _utils_previews.new.__doc__
+
+    def load(self, name, path, path_type, force_reload=False):
+        if name in self:
+            raise KeyException("key %r already exists")
+        p = self[name] = _utils_previews.load(
+                self._gen_key(name), path, path_type, force_reload)
+        return p
+    load.__doc__ = _utils_previews.load.__doc__
+
+    def release(self, name):
+        p = self.pop(name, None)
+        if p is not None:
+            _utils_previews.release(self._gen_key(name))
+    release.__doc__ = _utils_previews.release.__doc__
+
+    def clear(self):
+        for name in self.keys():
+            _utils_previews.release(self._gen_key(name))
+        super().clear()
+
+    def close(self):
+        self.clear()
+        _uuid_open.remove(self._uuid)
+
+    def __delitem__(self, key):
+        return self.release(key)
+
+    def __repr__(self):
+        return "<%s id=%s[%d], %s>" % (
+                self.__class__.__name__,
+                self._uuid,
+                len(self),
+                super().__repr__())
+
+
+def new():
+    """
+    Return a new preview collection.
+    """
+
+    return _BPyImagePreviewCollection()
+
+
+def remove(p):
+    """
+    Remove the specified previews collection.
+    """
+    p.close()
+
+
+# don't complain about resources on exit (only unregister)
+import atexit
+
+def exit_clear_warning():
+    del _BPyImagePreviewCollection.__del__
+
+atexit.register(exit_clear_warning)
+del atexit, exit_clear_warning
diff --git a/release/scripts/templates_py/ui_previews_custom_icon.py b/release/scripts/templates_py/ui_previews_custom_icon.py
new file mode 100644 (file)
index 0000000..defa2d2
--- /dev/null
@@ -0,0 +1,82 @@
+# This sample script demonstrates how to place a custom icon on a button or
+# menu entry.
+#
+# IMPORTANT NOTE: if you run this sample, there will be no icon in the button
+# You need to replace the image path with a real existing one.
+# For distributable addons, it is recommended to place the icons inside the
+# addon folder and access it relative to the py script file for portability
+#
+#
+# Other use cases for UI-previews:
+# - provide a fixed list of previews to select from
+# - provide a dynamic list of preview (eg. calculated from reading a directory)
+#
+# For the above use cases, see the template 'ui_previews_dynamic_enum.py"
+
+
+import os
+import bpy
+
+
+class PreviewsExamplePanel(bpy.types.Panel):
+    """Creates a Panel in the Object properties window"""
+    bl_label = "Previews Example Panel"
+    bl_idname = "OBJECT_PT_previews"
+    bl_space_type = 'PROPERTIES'
+    bl_region_type = 'WINDOW'
+    bl_context = "object"
+
+    def draw(self, context):
+        layout = self.layout
+        pcoll = preview_collections["main"]
+
+        row = layout.row()
+        my_icon = pcoll.get("my_icon")
+        row.operator("render.render", icon_value=my_icon.icon_id)
+
+        # my_icon.icon_id can be used in any UI function that accepts
+        # icon_value # try also setting text=""
+        # to get an icon only operator button
+
+
+# We can store multiple preview collections here,
+# however in this example we only store "main"
+preview_collections = {}
+
+
+def register():
+
+    # Note that preview collections returned by bpy.utils.previews
+    # are regular py objects - you can use them to store custom data.
+    import bpy.utils.previews
+    pcoll = bpy.utils.previews.new()
+
+    # path to the folder where the icon is
+    # the path is calculated relative to this py file inside the addon folder
+    my_icons_dir = os.path.join(os.path.dirname(__file__), "icons")
+
+    # load a preview thumbnail of a file and store in the previews collection
+    pcoll.load(
+        # identifier
+        "my_icon",
+        # path to image
+        os.path.join(my_icons_dir, "icon-image.png"),
+        # file type to generate preview from. others are: MOVIE, FONT, BLEND
+        'IMAGE')
+
+    preview_collections["main"] = pcoll
+
+    bpy.utils.register_class(PreviewsExamplePanel)
+
+
+def unregister():
+
+    for pcoll in preview_collections.values():
+        bpy.utils.previews.remove(pcoll)
+    preview_collections.clear()
+
+    bpy.utils.unregister_class(PreviewsExamplePanel)
+
+
+if __name__ == "__main__":
+    register()
diff --git a/release/scripts/templates_py/ui_previews_dynamic_enum.py b/release/scripts/templates_py/ui_previews_dynamic_enum.py
new file mode 100644 (file)
index 0000000..1603df0
--- /dev/null
@@ -0,0 +1,136 @@
+# This sample script demonstrates a dynamic EnumProperty with custom icons.
+# The EnumProperty is populated dynamically with thumbnails of the contents of
+# a chosen directory in 'enum_previews_from_directory_items'.
+# Then, the same enum is displayed with different interfaces. Note that the
+# generated icon previews do not have Blender IDs, which means that they can
+# not be used with UILayout templates that require IDs,
+# such as template_list and template_ID_preview.
+#
+# Other use cases:
+# - make a fixed list of enum_items instead of calculating them in a function
+# - generate isolated thumbnails to use as custom icons in buttons
+#   and menu items
+#
+# For custom icons, see the template "ui_previews_custom_icon.py".
+#
+# For distributable addons, it is recommended to place the icons inside the
+# addon directory and access it relative to the py script file for portability:
+#
+#    os.path.join(os.path.dirname(__file__), "images")
+
+
+import os
+import bpy
+
+
+def enum_previews_from_directory_items(self, context):
+    """EnumProperty callback"""
+    wm = context.window_manager
+
+    enum_items = []
+    directory = wm.my_previews_dir
+
+    # Get the preview collection (defined in register func).
+    pcoll = preview_collections["main"]
+
+    if directory == pcoll.my_previews_dir:
+        return pcoll.my_previews
+
+    print("Scanning directory: %s" % directory)
+
+    if directory and os.path.exists(directory):
+        # Scan the directory for png files
+        image_paths = []
+        for fn in  os.listdir(directory):
+            if fn.lower().endswith(".png"):
+                image_paths.append(fn)
+
+        for i, name in enumerate(image_paths):
+            # generates a thumbnail preview for a file.
+            # Also works with previews for 'MOVIE', 'BLEND' and 'FONT'
+            filepath = os.path.join(directory, name)
+            thumb = pcoll.load(filepath, filepath, 'IMAGE')
+            # enum item: (identifier, name, description, icon, number)
+            enum_items.append((name, name, name, thumb.icon_id, i))
+
+    pcoll.my_previews = enum_items
+    pcoll.my_previews_dir = directory
+    return pcoll.my_previews
+
+
+class PreviewsExamplePanel(bpy.types.Panel):
+    """Creates a Panel in the Object properties window"""
+    bl_label = "Previews Example Panel"
+    bl_idname = "OBJECT_PT_previews"
+    bl_space_type = 'PROPERTIES'
+    bl_region_type = 'WINDOW'
+    bl_context = "object"
+
+    def draw(self, context):
+        layout = self.layout
+        wm = context.window_manager
+
+        row = layout.row()
+        row.prop(wm, "my_previews_dir")
+
+        row = layout.row()
+        row.template_icon_view(wm, "my_previews")
+
+        row = layout.row()
+        row.prop(wm, "my_previews")
+
+
+# We can store multiple preview collections here,
+# however in this example we only store "main"
+preview_collections = {}
+
+
+def register():
+    from bpy.types import WindowManager
+    from bpy.props import (
+            StringProperty,
+            EnumProperty,
+            )
+
+    WindowManager.my_previews_dir = StringProperty(
+            name="Folder Path",
+            subtype='DIR_PATH',
+            default=""
+            )
+
+    WindowManager.my_previews = EnumProperty(
+            items=enum_previews_from_directory_items,
+            )
+
+    # Note that preview collections returned by bpy.utils.previews
+    # are regular Python objects - you can use them to store custom data.
+    #
+    # This is especially useful here, since:
+    # - It avoids us regenerating the whole enum over and over.
+    # - It can store enum_items' strings
+    #   (remember you have to keep those strings somewhere in py,
+    #   else they get freed and Blender references invalid memory!).
+    import bpy.utils.previews
+    pcoll = bpy.utils.previews.new()
+    pcoll.my_previews_dir = ""
+    pcoll.my_previews = ()
+
+    preview_collections["main"] = pcoll
+
+    bpy.utils.register_class(PreviewsExamplePanel)
+
+
+def unregister():
+    from bpy.types import WindowManager
+
+    del WindowManager.my_previews
+
+    for pcoll in preview_collections.values():
+        bpy.utils.previews.remove(pcoll)
+    preview_collections.clear()
+
+    bpy.utils.unregister_class(PreviewsExamplePanel)
+
+
+if __name__ == "__main__":
+    register()
index 9af0d96884ae2936d16a56b96d4a67165dab5f88..763a3874d4e54c1c539776d36818e2244a0c3f76 100644 (file)
@@ -48,10 +48,14 @@ typedef struct Icon Icon;
 struct PreviewImage;
 struct ID;
 
+enum eIconSizes;
+
 void BKE_icons_init(int first_dyn_id);
 
 /* return icon id for library object or create new icon if not found */
-int BKE_icon_getid(struct ID *id);
+int BKE_icon_id_ensure(struct ID *id);
+
+int BKE_icon_preview_ensure(struct PreviewImage *preview);
 
 /* retrieve icon for id */
 struct Icon *BKE_icon_get(int icon_id);
@@ -60,8 +64,10 @@ struct Icon *BKE_icon_get(int icon_id);
 /* used for inserting the internal icons */
 void BKE_icon_set(int icon_id, struct Icon *icon);
 
-/* remove icon and free date if library object becomes invalid */
-void BKE_icon_delete(struct ID *id);
+/* remove icon and free data if library object becomes invalid */
+void BKE_icon_id_delete(struct ID *id);
+
+void BKE_icon_delete(int icon_id);
 
 /* report changes - icon needs to be recalculated */
 void BKE_icon_changed(int icon_id);
@@ -75,8 +81,17 @@ void BKE_previewimg_freefunc(void *link);
 /* free the preview image */
 void BKE_previewimg_free(struct PreviewImage **prv);
 
+/* clear the preview image or icon, but does not free it */
+void BKE_previewimg_clear(struct PreviewImage *prv);
+
+/* clear the preview image or icon at a specific size */
+void BKE_previewimg_clear_single(struct PreviewImage *prv, enum eIconSizes size);
+
+/* get the preview from any pointer */
+struct PreviewImage **BKE_previewimg_id_get_p(struct ID *id);
+
 /* free the preview image belonging to the id */
-void BKE_previewimg_free_id(struct ID *id);
+void BKE_previewimg_id_free(struct ID *id);
 
 /* create a new preview image */
 struct PreviewImage *BKE_previewimg_create(void);
@@ -85,6 +100,19 @@ struct PreviewImage *BKE_previewimg_create(void);
 struct PreviewImage *BKE_previewimg_copy(struct PreviewImage *prv);
 
 /* retrieve existing or create new preview image */
-struct PreviewImage *BKE_previewimg_get(struct ID *id);
+struct PreviewImage *BKE_previewimg_id_ensure(struct ID *id);
+
+void BKE_previewimg_ensure(struct PreviewImage *prv, const int size);
+
+struct PreviewImage *BKE_previewimg_cached_get(const char *name);
+
+struct PreviewImage *BKE_previewimg_cached_ensure(const char *name);
+
+struct PreviewImage *BKE_previewimg_cached_thumbnail_read(
+        const char *name, const char *path, const int source, bool force_update);
+
+void BKE_previewimg_cached_release(const char *name);
+
+#define ICON_RENDER_DEFAULT_HEIGHT 32
 
 #endif /*  __BKE_ICONS_H__ */
index d3225f3fa35a423aef99eb9be3c44e500cf7e189..450c1388f373a2bdb958b6044b0039f9f3e932a3 100644 (file)
@@ -45,6 +45,7 @@
 
 #include "BLI_utildefines.h"
 #include "BLI_ghash.h"
+#include "BLI_string.h"
 
 #include "BKE_icons.h"
 #include "BKE_global.h" /* only for G.background test */
 
 #include "GPU_extensions.h"
 
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
+#include "IMB_thumbs.h"
+
 /* GLOBALS */
 
 static GHash *gIcons = NULL;
@@ -61,6 +66,7 @@ static int gNextIconId = 1;
 
 static int gFirstIconId = 1;
 
+static GHash *gCachedPreviews = NULL;
 
 static void icon_free(void *val)
 {
@@ -105,30 +111,50 @@ void BKE_icons_init(int first_dyn_id)
        gFirstIconId = first_dyn_id;
 
        if (!gIcons)
-               gIcons = BLI_ghash_int_new("icons_init gh");
+               gIcons = BLI_ghash_int_new(__func__);
+
+       if (!gCachedPreviews) {
+               gCachedPreviews = BLI_ghash_str_new(__func__);
+       }
 }
 
 void BKE_icons_free(void)
 {
-       if (gIcons)
+       if (gIcons) {
                BLI_ghash_free(gIcons, NULL, icon_free);
-       gIcons = NULL;
+               gIcons = NULL;
+       }
+
+       if (gCachedPreviews) {
+               BLI_ghash_free(gCachedPreviews, MEM_freeN, BKE_previewimg_freefunc);
+               gCachedPreviews = NULL;
+       }
 }
 
-PreviewImage *BKE_previewimg_create(void)
+static PreviewImage *previewimg_create_ex(size_t deferred_data_size)
 {
        PreviewImage *prv_img = NULL;
        int i;
 
-       prv_img = MEM_callocN(sizeof(PreviewImage), "img_prv");
+       prv_img = MEM_mallocN(sizeof(PreviewImage) + deferred_data_size, "img_prv");
+       memset(prv_img, 0, sizeof(*prv_img));  /* leave deferred data dirty */
+
+       if (deferred_data_size) {
+               prv_img->use_deferred = true;
+       }
 
        for (i = 0; i < NUM_ICON_SIZES; ++i) {
-               prv_img->changed[i] = 1;
+               prv_img->flag[i] |= PRV_CHANGED;
                prv_img->changed_timestamp[i] = 0;
        }
        return prv_img;
 }
 
+PreviewImage *BKE_previewimg_create(void)
+{
+       return previewimg_create_ex(0);
+}
+
 void BKE_previewimg_freefunc(void *link)
 {
        PreviewImage *prv = (PreviewImage *)link;
@@ -138,7 +164,6 @@ void BKE_previewimg_freefunc(void *link)
                for (i = 0; i < NUM_ICON_SIZES; ++i) {
                        if (prv->rect[i]) {
                                MEM_freeN(prv->rect[i]);
-                               prv->rect[i] = NULL;
                        }
                        if (prv->gputexture[i])
                                GPU_texture_free(prv->gputexture[i]);
@@ -156,6 +181,25 @@ void BKE_previewimg_free(PreviewImage **prv)
        }
 }
 
+void BKE_previewimg_clear_single(struct PreviewImage *prv, enum eIconSizes size)
+{
+       MEM_SAFE_FREE(prv->rect[size]);
+       if (prv->gputexture[size]) {
+               GPU_texture_free(prv->gputexture[size]);
+       }
+       prv->h[size] = prv->w[size] = 0;
+       prv->flag[size] |= PRV_CHANGED;
+       prv->changed_timestamp[size] = 0;
+}
+
+void BKE_previewimg_clear(struct PreviewImage *prv)
+{
+       int i;
+       for (i = 0; i < NUM_ICON_SIZES; ++i) {
+               BKE_previewimg_clear_single(prv, i);
+       }
+}
+
 PreviewImage *BKE_previewimg_copy(PreviewImage *prv)
 {
        PreviewImage *prv_img = NULL;
@@ -167,79 +211,186 @@ PreviewImage *BKE_previewimg_copy(PreviewImage *prv)
                        if (prv->rect[i]) {
                                prv_img->rect[i] = MEM_dupallocN(prv->rect[i]);
                        }
-                       else {
-                               prv_img->rect[i] = NULL;
-                       }
                        prv_img->gputexture[i] = NULL;
                }
        }
        return prv_img;
 }
 
-void BKE_previewimg_free_id(ID *id) 
+PreviewImage **BKE_previewimg_id_get_p(ID *id)
 {
-       if (GS(id->name) == ID_MA) {
-               Material *mat = (Material *)id;
-               BKE_previewimg_free(&mat->preview);
-       }
-       else if (GS(id->name) == ID_TE) {
-               Tex *tex = (Tex *)id;
-               BKE_previewimg_free(&tex->preview);
-       }
-       else if (GS(id->name) == ID_WO) {
-               World *wo = (World *)id;
-               BKE_previewimg_free(&wo->preview);
+       switch (GS(id->name)) {
+#define ID_PRV_CASE(id_code, id_struct) case id_code: { return &((id_struct *)id)->preview; }
+               ID_PRV_CASE(ID_MA, Material);
+               ID_PRV_CASE(ID_TE, Tex);
+               ID_PRV_CASE(ID_WO, World);
+               ID_PRV_CASE(ID_LA, Lamp);
+               ID_PRV_CASE(ID_IM, Image);
+               ID_PRV_CASE(ID_BR, Brush);
+#undef ID_PRV_CASE
        }
-       else if (GS(id->name) == ID_LA) {
-               Lamp *la  = (Lamp *)id;
-               BKE_previewimg_free(&la->preview);
-       }
-       else if (GS(id->name) == ID_IM) {
-               Image *img  = (Image *)id;
-               BKE_previewimg_free(&img->preview);
+
+       return NULL;
+}
+
+void BKE_previewimg_id_free(ID *id)
+{
+       PreviewImage **prv_p = BKE_previewimg_id_get_p(id);
+       if (prv_p) {
+               BKE_previewimg_free(prv_p);
        }
-       else if (GS(id->name) == ID_BR) {
-               Brush *br  = (Brush *)id;
-               BKE_previewimg_free(&br->preview);
+}
+
+PreviewImage *BKE_previewimg_id_ensure(ID *id)
+{
+       PreviewImage **prv_p = BKE_previewimg_id_get_p(id);
+
+       if (prv_p) {
+               if (*prv_p == NULL) {
+                       *prv_p = BKE_previewimg_create();
+               }
+               return *prv_p;
        }
+
+       return NULL;
 }
 
-PreviewImage *BKE_previewimg_get(ID *id)
+PreviewImage *BKE_previewimg_cached_get(const char *name)
 {
-       PreviewImage *prv_img = NULL;
+       return BLI_ghash_lookup(gCachedPreviews, name);
+}
 
-       if (GS(id->name) == ID_MA) {
-               Material *mat = (Material *)id;
-               if (!mat->preview) mat->preview = BKE_previewimg_create();
-               prv_img = mat->preview;
+/**
+ * Generate an empty PreviewImage, if not yet existing.
+ */
+PreviewImage *BKE_previewimg_cached_ensure(const char *name)
+{
+       PreviewImage *prv = NULL;
+       void **prv_p;
+
+       if (!BLI_ghash_ensure_p_ex(gCachedPreviews, name, &prv_p, (GHashKeyCopyFP)BLI_strdup)) {
+               *prv_p = BKE_previewimg_create();
        }
-       else if (GS(id->name) == ID_TE) {
-               Tex *tex = (Tex *)id;
-               if (!tex->preview) tex->preview = BKE_previewimg_create();
-               prv_img = tex->preview;
+       prv = *prv_p;
+       BLI_assert(prv);
+
+       return prv;
+}
+
+/**
+ * Generate a PreviewImage from given file path, using thumbnails management, if not yet existing.
+ */
+PreviewImage *BKE_previewimg_cached_thumbnail_read(
+        const char *name, const char *path, const int source, bool force_update)
+{
+       PreviewImage *prv = NULL;
+       void **prv_p;
+
+       prv_p = BLI_ghash_lookup_p(gCachedPreviews, name);
+
+       if (prv_p) {
+               prv = *prv_p;
+               BLI_assert(prv);
        }
-       else if (GS(id->name) == ID_WO) {
-               World *wo = (World *)id;
-               if (!wo->preview) wo->preview = BKE_previewimg_create();
-               prv_img = wo->preview;
+
+       if (prv && force_update) {
+               const char *prv_deferred_data = PRV_DEFERRED_DATA(prv);
+               if (((int)prv_deferred_data[0] == source) && STREQ(&prv_deferred_data[1], path)) {
+                       /* If same path, no need to re-allocate preview, just clear it up. */
+                       BKE_previewimg_clear(prv);
+               }
+               else {
+                       BKE_previewimg_free(&prv);
+               }
        }
-       else if (GS(id->name) == ID_LA) {
-               Lamp *la  = (Lamp *)id;
-               if (!la->preview) la->preview = BKE_previewimg_create();
-               prv_img = la->preview;
+
+       if (!prv) {
+               /* We pack needed data for lazy loading (source type, in a single char, and path). */
+               const size_t deferred_data_size = strlen(path) + 2;
+               char *deferred_data;
+
+               prv = previewimg_create_ex(deferred_data_size);
+               deferred_data = PRV_DEFERRED_DATA(prv);
+               deferred_data[0] = source;
+               memcpy(&deferred_data[1], path, deferred_data_size - 1);
+
+               force_update = true;
        }
-       else if (GS(id->name) == ID_IM) {
-               Image *img  = (Image *)id;
-               if (!img->preview) img->preview = BKE_previewimg_create();
-               prv_img = img->preview;
+
+       if (force_update) {
+               if (prv_p) {
+                       *prv_p = prv;
+               }
+               else {
+                       BLI_ghash_insert(gCachedPreviews, BLI_strdup(name), prv);
+               }
        }
-       else if (GS(id->name) == ID_BR) {
-               Brush *br  = (Brush *)id;
-               if (!br->preview) br->preview = BKE_previewimg_create();
-               prv_img = br->preview;
+
+       return prv;
+}
+
+void BKE_previewimg_cached_release(const char *name)
+{
+       PreviewImage *prv = BLI_ghash_popkey(gCachedPreviews, name, MEM_freeN);
+
+       if (prv) {
+               if (prv->icon_id) {
+                       BKE_icon_delete(prv->icon_id);
+               }
+               BKE_previewimg_freefunc(prv);
        }
+}
 
-       return prv_img;
+/** Handle deferred (lazy) loading/generation of preview image, if needed.
+ * For now, only used with file thumbnails. */
+void BKE_previewimg_ensure(PreviewImage *prv, const int size)
+{
+       if (prv->use_deferred) {
+               const bool do_icon = ((size == ICON_SIZE_ICON) && !prv->rect[ICON_SIZE_ICON]);
+               const bool do_preview = ((size == ICON_SIZE_PREVIEW) && !prv->rect[ICON_SIZE_PREVIEW]);
+
+               if (do_icon || do_preview) {
+                       ImBuf *thumb;
+                       char *prv_deferred_data = PRV_DEFERRED_DATA(prv);
+                       int source =  prv_deferred_data[0];
+                       char *path = &prv_deferred_data[1];
+                       int icon_w, icon_h;
+
+                       thumb = IMB_thumb_manage(path, THB_LARGE, source);
+
+                       if (thumb) {
+                               /* PreviewImage assumes premultiplied alhpa... */
+                               IMB_premultiply_alpha(thumb);
+
+                               if (do_preview) {
+                                       prv->w[ICON_SIZE_PREVIEW] = thumb->x;
+                                       prv->h[ICON_SIZE_PREVIEW] = thumb->y;
+                                       prv->rect[ICON_SIZE_PREVIEW] = MEM_dupallocN(thumb->rect);
+                                       prv->flag[ICON_SIZE_PREVIEW] &= ~(PRV_CHANGED | PRV_USER_EDITED);
+                               }
+                               if (do_icon) {
+                                       if (thumb->x > thumb->y) {
+                                               icon_w = ICON_RENDER_DEFAULT_HEIGHT;
+                                               icon_h = (thumb->y * icon_w) / thumb->x + 1;
+                                       }
+                                       else if (thumb->x < thumb->y) {
+                                               icon_h = ICON_RENDER_DEFAULT_HEIGHT;
+                                               icon_w = (thumb->x * icon_h) / thumb->y + 1;
+                                       }
+                                       else {
+                                               icon_w = icon_h = ICON_RENDER_DEFAULT_HEIGHT;
+                                       }
+
+                                       IMB_scaleImBuf(thumb, icon_w, icon_h);
+                                       prv->w[ICON_SIZE_ICON] = icon_w;
+                                       prv->h[ICON_SIZE_ICON] = icon_h;
+                                       prv->rect[ICON_SIZE_ICON] = MEM_dupallocN(thumb->rect);
+                                       prv->flag[ICON_SIZE_ICON] &= ~(PRV_CHANGED | PRV_USER_EDITED);
+                               }
+                               IMB_freeImBuf(thumb);
+                       }
+               }
+       }
 }
 
 void BKE_icon_changed(int id)
@@ -251,20 +402,20 @@ void BKE_icon_changed(int id)
        icon = BLI_ghash_lookup(gIcons, SET_INT_IN_POINTER(id));
        
        if (icon) {
-               PreviewImage *prv = BKE_previewimg_get((ID *)icon->obj);
+               PreviewImage *prv = BKE_previewimg_id_ensure((ID *)icon->obj);
 
                /* all previews changed */
                if (prv) {
                        int i;
                        for (i = 0; i < NUM_ICON_SIZES; ++i) {
-                               prv->changed[i] = 1;
+                               prv->flag[i] |= PRV_CHANGED;
                                prv->changed_timestamp[i]++;
                        }
                }
        }
 }
 
-int BKE_icon_getid(struct ID *id)
+int BKE_icon_id_ensure(struct ID *id)
 {
        Icon *new_icon = NULL;
 
@@ -277,11 +428,11 @@ int BKE_icon_getid(struct ID *id)
        id->icon_id = get_next_free_id();
 
        if (!id->icon_id) {
-               printf("BKE_icon_getid: Internal error - not enough IDs\n");
+               printf("%s: Internal error - not enough IDs\n", __func__);
                return 0;
        }
 
-       new_icon = MEM_callocN(sizeof(Icon), "texicon");
+       new_icon = MEM_mallocN(sizeof(Icon), __func__);
 
        new_icon->obj = id;
        new_icon->type = GS(id->name);
@@ -295,6 +446,40 @@ int BKE_icon_getid(struct ID *id)
        return id->icon_id;
 }
 
+/**
+ * Return icon id of given preview, or create new icon if not found.
+ */
+int BKE_icon_preview_ensure(PreviewImage *preview)
+{
+       Icon *new_icon = NULL;
+
+       if (!preview || G.background)
+               return 0;
+
+       if (preview->icon_id)
+               return preview->icon_id;
+
+       preview->icon_id = get_next_free_id();
+
+       if (!preview->icon_id) {
+               printf("%s: Internal error - not enough IDs\n", __func__);
+               return 0;
+       }
+
+       new_icon = MEM_mallocN(sizeof(Icon), __func__);
+
+       new_icon->obj = preview;
+       new_icon->type = 0;  /* Special, tags as non-ID icon/preview. */
+
+       /* next two lines make sure image gets created */
+       new_icon->drawinfo = NULL;
+       new_icon->drawinfo_free = NULL;
+
+       BLI_ghash_insert(gIcons, SET_INT_IN_POINTER(preview->icon_id), new_icon);
+
+       return preview->icon_id;
+}
+
 Icon *BKE_icon_get(int icon_id)
 {
        Icon *icon = NULL;
@@ -302,7 +487,7 @@ Icon *BKE_icon_get(int icon_id)
        icon = BLI_ghash_lookup(gIcons, SET_INT_IN_POINTER(icon_id));
        
        if (!icon) {
-               printf("BKE_icon_get: Internal error, no icon for icon ID: %d\n", icon_id);
+               printf("%s: Internal error, no icon for icon ID: %d\n", __func__, icon_id);
                return NULL;
        }
 
@@ -314,18 +499,39 @@ void BKE_icon_set(int icon_id, struct Icon *icon)
        void **val_p;
 
        if (BLI_ghash_ensure_p(gIcons, SET_INT_IN_POINTER(icon_id), &val_p)) {
-               printf("BKE_icon_set: Internal error, icon already set: %d\n", icon_id);
+               printf("%s: Internal error, icon already set: %d\n", __func__, icon_id);
                return;
        }
 
        *val_p = icon;
 }
 
-void BKE_icon_delete(struct ID *id)
+void BKE_icon_id_delete(struct ID *id)
 {
-
        if (!id->icon_id) return;  /* no icon defined for library object */
 
        BLI_ghash_remove(gIcons, SET_INT_IN_POINTER(id->icon_id), NULL, icon_free);
        id->icon_id = 0;
 }
+
+/**
+ * Remove icon and free data.
+ */
+void BKE_icon_delete(int icon_id)
+{
+       Icon *icon;
+
+       if (!icon_id) return;  /* no icon defined for library object */
+
+       icon = BLI_ghash_popkey(gIcons, SET_INT_IN_POINTER(icon_id), NULL);
+
+       if (icon) {
+               if (icon->type) {
+                       ((ID *)(icon->obj))->icon_id = 0;
+               }
+               else {
+                       ((PreviewImage *)(icon->obj))->icon_id = 0;
+               }
+               icon_free(icon);
+       }
+}
index f3f4775dbf79d65df80d6e32ed572de9a3ed0a11..29576e4f1f650e513168591716ebacb75e60d865 100644 (file)
@@ -338,7 +338,7 @@ void BKE_image_free(Image *ima)
 
        image_free_packedfiles(ima);
 
-       BKE_icon_delete(&ima->id);
+       BKE_icon_id_delete(&ima->id);
        ima->id.icon_id = 0;
 
        BKE_previewimg_free(&ima->preview);
@@ -3001,7 +3001,7 @@ static void image_initialize_after_load(Image *ima, ImBuf *ibuf)
 {
        /* preview is NULL when it has never been used as an icon before */
        if (G.background == 0 && ima->preview == NULL)
-               BKE_icon_changed(BKE_icon_getid(&ima->id));
+               BKE_icon_changed(BKE_icon_id_ensure(&ima->id));
 
        /* fields */
        if (ima->flag & IMA_FIELDS) {
index 96b0b95adf3c4cdb1607c42fffd9139cf0305b5a..44e35c645debc5307ac37a69cbba953d2b035a13 100644 (file)
@@ -232,7 +232,7 @@ void BKE_lamp_free(Lamp *la)
        }
        
        BKE_previewimg_free(&la->preview);
-       BKE_icon_delete(&la->id);
+       BKE_icon_id_delete(&la->id);
        la->id.icon_id = 0;
 }
 
index eaaf1319167640cf91ddfad193d76363d3ef7e0c..283c7a6fc8894f044944ed224d94a0171f537970 100644 (file)
@@ -106,7 +106,7 @@ void BKE_material_free_ex(Material *ma, bool do_id_user)
        
        if (ma->preview)
                BKE_previewimg_free(&ma->preview);
-       BKE_icon_delete((struct ID *)ma);
+       BKE_icon_id_delete((struct ID *)ma);
        ma->id.icon_id = 0;
        
        /* is no lib link block, but material extension */
index 8e4fcc5d1cc0e75b80b533118c917eb6a26048dc..246e3f2a316856e6c6a4c522ad929111582ac020 100644 (file)
@@ -567,7 +567,7 @@ void BKE_texture_free(Tex *tex)
        BKE_animdata_free((struct ID *)tex);
        
        BKE_previewimg_free(&tex->preview);
-       BKE_icon_delete((struct ID *)tex);
+       BKE_icon_id_delete((struct ID *)tex);
        tex->id.icon_id = 0;
        
        if (tex->nodetree) {
index f26cbf7880a90a640768339e0027f75715f0220f..e4736b1f54c5fe1bc09a43502fff943df12ba142 100644 (file)
@@ -74,7 +74,7 @@ void BKE_world_free_ex(World *wrld, bool do_id_user)
        if (wrld->gpumaterial.first)
                GPU_material_free(&wrld->gpumaterial);
        
-       BKE_icon_delete((struct ID *)wrld);
+       BKE_icon_id_delete((struct ID *)wrld);
        wrld->id.icon_id = 0;
 }
 
index 2bc0566f4a606d8691bef2b8c0c159d7d656ba8a..414126cac138f470a495e9a3a6978d98640a379f 100644 (file)
@@ -60,11 +60,15 @@ struct Scene *ED_render_job_get_current_scene(const struct bContext *C);
  * - PR_BUTS_RENDER: preview is rendered for buttons window
  * - PR_ICON_RENDER: preview is rendered for icons. hopefully fast enough for at least 32x32
  * - PR_NODE_RENDER: preview is rendered for node editor
+ * - PR_ICON_DEFERRED: No render, we just ensure deferred icon data gets generated.
  */
 
-#define PR_BUTS_RENDER  0
-#define PR_ICON_RENDER  1
-#define PR_NODE_RENDER  2
+enum {
+       PR_BUTS_RENDER   = 0,
+       PR_ICON_RENDER   = 1,
+       PR_NODE_RENDER   = 2,
+       PR_ICON_DEFERRED = 3,
+};
 
 void ED_preview_init_dbase(void);
 void ED_preview_free_dbase(void);
index 9d190fa81c73de3cdff7debde2310b0842d2a1e2..92f3b0180c6d083abb6b3db2f4b08acad96afa01 100644 (file)
@@ -49,7 +49,7 @@ typedef struct IconFile {
 #define ICON_DEFAULT_HEIGHT_SCALE ((int)(UI_UNIT_Y * 0.8f))
 #define ICON_DEFAULT_WIDTH_SCALE  ((int)(UI_UNIT_X * 0.8f))
 
-#define PREVIEW_DEFAULT_HEIGHT 96
+#define PREVIEW_DEFAULT_HEIGHT 128
 
 /*
  * Resizable Icons for Blender
index b63871f5c82ab9eef8707c587da2b961becfedf1..a9cc926b275ac6b356c8497a01c9bd762fc3a7c4 100644 (file)
@@ -3237,6 +3237,18 @@ static uiBut *ui_def_but(
        return but;
 }
 
+void ui_def_but_icon(uiBut *but, const int icon, const int flag) {
+       if (icon) {
+               ui_icon_ensure_deferred(but->block->evil_C, icon, (flag & UI_BUT_ICON_PREVIEW) != 0);
+       }
+       but->icon = (BIFIconID)icon;
+       but->flag |= flag;
+
+       if (but->str && but->str[0]) {
+               but->drawflag |= UI_BUT_ICON_LEFT;
+       }
+}
+
 static void ui_def_but_rna__disable(uiBut *but)
 {
        but->flag |= UI_BUT_DISABLED;
@@ -3500,11 +3512,7 @@ static uiBut *ui_def_but_rna(
                but->rnaindex = 0;
 
        if (icon) {
-               but->icon = (BIFIconID)icon;
-               but->flag |= UI_HAS_ICON;
-               if (str[0]) {
-                       but->drawflag |= UI_BUT_ICON_LEFT;
-               }
+               ui_def_but_icon(but, icon, UI_HAS_ICON);
        }
        
        if ((type == UI_BTYPE_MENU) && (but->dt == UI_EMBOSS_PULLDOWN)) {
@@ -3691,8 +3699,7 @@ int UI_autocomplete_end(AutoComplete *autocpl, char *autoname)
 static void ui_but_update_and_icon_set(uiBut *but, int icon)
 {
        if (icon) {
-               but->icon = (BIFIconID) icon;
-               but->flag |= UI_HAS_ICON;
+               ui_def_but_icon(but, icon, UI_HAS_ICON);
        }
 
        ui_but_update(but);
@@ -4066,7 +4073,7 @@ void UI_but_drag_set_value(uiBut *but)
 void UI_but_drag_set_image(uiBut *but, const char *path, int icon, struct ImBuf *imb, float scale)
 {
        but->dragtype = WM_DRAG_PATH;
-       but->icon = icon; /* no flag UI_HAS_ICON, so icon doesnt draw in button */
+       ui_def_but_icon(but, icon, 0);  /* no flag UI_HAS_ICON, so icon doesnt draw in button */
        but->dragpoin = (void *)path;
        but->imb = imb;
        but->imb_scale = scale;
@@ -4220,8 +4227,7 @@ uiBut *uiDefIconTextMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, in
 {
        uiBut *but = ui_def_but(block, UI_BTYPE_PULLDOWN, 0, str, x, y, width, height, arg, 0.0, 0.0, 0.0, 0.0, tip);
 
-       but->icon = (BIFIconID) icon;
-       but->flag |= UI_HAS_ICON;
+       ui_def_but_icon(but, icon, UI_HAS_ICON);
 
        but->drawflag |= UI_BUT_ICON_LEFT;
        but->flag |= UI_BUT_ICON_SUBMENU;
@@ -4236,8 +4242,7 @@ uiBut *uiDefIconMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, int ic
 {
        uiBut *but = ui_def_but(block, UI_BTYPE_PULLDOWN, 0, "", x, y, width, height, arg, 0.0, 0.0, 0.0, 0.0, tip);
 
-       but->icon = (BIFIconID) icon;
-       but->flag |= UI_HAS_ICON;
+       ui_def_but_icon(but, icon, UI_HAS_ICON);
        but->drawflag &= ~UI_BUT_ICON_LEFT;
 
        but->menu_create_func = func;
@@ -4253,7 +4258,7 @@ uiBut *uiDefIconTextBlockBut(uiBlock *block, uiBlockCreateFunc func, void *arg,
        
        /* XXX temp, old menu calls pass on icon arrow, which is now UI_BUT_ICON_SUBMENU flag */
        if (icon != ICON_RIGHTARROW_THIN) {
-               but->icon = (BIFIconID) icon;
+               ui_def_but_icon(but, icon, 0);
                but->drawflag |= UI_BUT_ICON_LEFT;
        }
        but->flag |= UI_HAS_ICON;
@@ -4270,9 +4275,8 @@ uiBut *uiDefIconBlockBut(uiBlock *block, uiBlockCreateFunc func, void *arg, int
 {
        uiBut *but = ui_def_but(block, UI_BTYPE_BLOCK, retval, "", x, y, width, height, arg, 0.0, 0.0, 0.0, 0.0, tip);
        
-       but->icon = (BIFIconID) icon;
-       but->flag |= UI_HAS_ICON;
-       
+       ui_def_but_icon(but, icon, UI_HAS_ICON);
+
        but->drawflag |= UI_BUT_ICON_LEFT;
        
        but->block_create_func = func;
@@ -4305,9 +4309,8 @@ uiBut *uiDefSearchBut(uiBlock *block, void *arg, int retval, int icon, int maxle
 {
        uiBut *but = ui_def_but(block, UI_BTYPE_SEARCH_MENU, retval, "", x, y, width, height, arg, 0.0, maxlen, a1, a2, tip);
        
-       but->icon = (BIFIconID) icon;
-       but->flag |= UI_HAS_ICON;
-       
+       ui_def_but_icon(but, icon, UI_HAS_ICON);
+
        but->drawflag |= UI_BUT_ICON_LEFT | UI_BUT_TEXT_LEFT;
        
        ui_but_update(but);
index 6536c0a869edb0a4c7afb2cd43024a44c39dc872..7c05d5f4378bbf7b86bcea84737edc488a1845b2 100644 (file)
@@ -55,6 +55,7 @@
 
 #include "IMB_imbuf.h"
 #include "IMB_imbuf_types.h"
+#include "IMB_thumbs.h"
 
 #include "BIF_gl.h"
 #include "BIF_glutil.h"
@@ -904,10 +905,13 @@ void UI_icons_init(int first_dyn_id)
 static int preview_render_size(enum eIconSizes size)
 {
        switch (size) {
-               case ICON_SIZE_ICON:    return 32;
-               case ICON_SIZE_PREVIEW: return PREVIEW_DEFAULT_HEIGHT;
+               case ICON_SIZE_ICON:
+                       return ICON_RENDER_DEFAULT_HEIGHT;
+               case ICON_SIZE_PREVIEW:
+                       return PREVIEW_RENDER_DEFAULT_HEIGHT;
+               default:
+                       return 0;
        }
-       return 0;
 }
 
 /* Create rect for the icon
@@ -923,12 +927,49 @@ static void icon_create_rect(struct PreviewImage *prv_img, enum eIconSizes size)
        else if (!prv_img->rect[size]) {
                prv_img->w[size] = render_size;
                prv_img->h[size] = render_size;
-               prv_img->changed[size] = 1;
+               prv_img->flag[size] |= PRV_CHANGED;
                prv_img->changed_timestamp[size] = 0;
                prv_img->rect[size] = MEM_callocN(render_size * render_size * sizeof(unsigned int), "prv_rect");
        }
 }
 
+void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool big)
+{
+       Icon *icon = BKE_icon_get(icon_id);
+
+       if (icon) {
+               DrawInfo *di = (DrawInfo *)icon->drawinfo;
+
+               if (!di) {
+                       di = icon_create_drawinfo();
+
+                       icon->drawinfo = di;
+                       icon->drawinfo_free = UI_icons_free_drawinfo;
+               }
+
+               if (di) {
+                       if (di->type == ICON_TYPE_PREVIEW) {
+                               PreviewImage *prv = (icon->type != 0) ? BKE_previewimg_id_ensure((ID *)icon->obj) : icon->obj;
+
+                               if (prv) {
+                                       const int size = big ? ICON_SIZE_PREVIEW : ICON_SIZE_ICON;
+
+                                       if (!prv->use_deferred || prv->rect[size] || (prv->flag[size] & PRV_USER_EDITED)) {
+                                               return;
+                                       }
+
+                                       icon_create_rect(prv, size);
+
+                                       /* Always using job (background) version. */
+                                       ED_preview_icon_job(C, prv, NULL, prv->rect[size], prv->w[size], prv->h[size]);
+
+                                       prv->flag[size] &= ~PRV_CHANGED;
+                               }
+                       }
+               }
+       }
+}
+
 /* only called when icon has changed */
 /* only call with valid pointer from UI_icon_draw */
 static void icon_set_image(
@@ -940,6 +981,11 @@ static void icon_set_image(
                return;
        }
 
+       if (prv_img->flag[size] & PRV_USER_EDITED) {
+               /* user-edited preview, do not auto-update! */
+               return;
+       }
+
        icon_create_rect(prv_img, size);
 
        if (use_job) {
@@ -961,22 +1007,32 @@ PreviewImage *UI_icon_to_preview(int icon_id)
        
        if (icon) {
                DrawInfo *di = (DrawInfo *)icon->drawinfo;
-               if (di && di->data.buffer.image) {
-                       ImBuf *bbuf;
-                       
-                       bbuf = IMB_ibImageFromMemory(di->data.buffer.image->datatoc_rect, di->data.buffer.image->datatoc_size, IB_rect, NULL, "<matcap buffer>");
-                       if (bbuf) {
-                               PreviewImage *prv = BKE_previewimg_create();
-                               
-                               prv->rect[0] = bbuf->rect;
+               if (di) {
+                       if (di->type == ICON_TYPE_PREVIEW) {
+                               PreviewImage *prv = (icon->type != 0) ? BKE_previewimg_id_ensure((ID *)icon->obj) : icon->obj;
 
-                               prv->w[0] = bbuf->x;
-                               prv->h[0] = bbuf->y;
-                               
-                               bbuf->rect = NULL;
-                               IMB_freeImBuf(bbuf);
-                               
-                               return prv;
+                               if (prv) {
+                                       return BKE_previewimg_copy(prv);
+                               }
+                       }
+                       else if (di->data.buffer.image) {
+                               ImBuf *bbuf;
+
+                               bbuf = IMB_ibImageFromMemory(di->data.buffer.image->datatoc_rect, di->data.buffer.image->datatoc_size,
+                                                            IB_rect, NULL, __func__);
+                               if (bbuf) {
+                                       PreviewImage *prv = BKE_previewimg_create();
+
+                                       prv->rect[0] = bbuf->rect;
+
+                                       prv->w[0] = bbuf->x;
+                                       prv->h[0] = bbuf->y;
+
+                                       bbuf->rect = NULL;
+                                       IMB_freeImBuf(bbuf);
+
+                                       return prv;
+                               }
                        }
                }
        }
@@ -987,6 +1043,10 @@ static void icon_draw_rect(float x, float y, int w, int h, float UNUSED(aspect),
                            unsigned int *rect, float alpha, const float rgb[3], const bool is_preview)
 {
        ImBuf *ima = NULL;
+       int draw_w = w;
+       int draw_h = h;
+       int draw_x = x;
+       int draw_y = y;
 
        /* sanity check */
        if (w <= 0 || h <= 0 || w > 2000 || h > 2000) {
@@ -1006,21 +1066,34 @@ static void icon_draw_rect(float x, float y, int w, int h, float UNUSED(aspect),
        }
 
        /* rect contains image in 'rendersize', we only scale if needed */
-       if (rw != w && rh != h) {
+       if (rw != w || rh != h) {
+               /* preserve aspect ratio and center */
+               if (rw > rh) {
+                       draw_w = w;
+                       draw_h = (int)(((float)rh / (float)rw) * (float)w);
+                       draw_y += (h - draw_h) / 2;
+               }
+               else if (rw < rh) {
+                       draw_w = (int)(((float)rw / (float)rh) * (float)h);
+                       draw_h = h;
+                       draw_x += (w - draw_w) / 2;
+               }
+               /* if the image is squared, the draw_ initialization values are good */
+
                /* first allocate imbuf for scaling and copy preview into it */
                ima = IMB_allocImBuf(rw, rh, 32, IB_rect);
                memcpy(ima->rect, rect, rw * rh * sizeof(unsigned int));
-               IMB_scaleImBuf(ima, w, h); /* scale it */
+               IMB_scaleImBuf(ima, draw_w, draw_h); /* scale it */
                rect = ima->rect;
        }
 
        /* draw */
        if (is_preview) {
-               glaDrawPixelsSafe(x, y, w, h, w, GL_RGBA, GL_UNSIGNED_BYTE, rect);
+               glaDrawPixelsSafe(draw_x, draw_y, draw_w, draw_h, draw_w, GL_RGBA, GL_UNSIGNED_BYTE, rect);
        }
        else {
-               glRasterPos2f(x, y);
-               glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, rect);
+               glRasterPos2f(draw_x, draw_y);
+               glDrawPixels(draw_w, draw_h, GL_RGBA, GL_UNSIGNED_BYTE, rect);
        }
 
        if (ima)
@@ -1081,10 +1154,13 @@ static void icon_draw_texture(
 static int get_draw_size(enum eIconSizes size)
 {
        switch (size) {
-               case ICON_SIZE_ICON: return ICON_DEFAULT_HEIGHT;
-               case ICON_SIZE_PREVIEW: return PREVIEW_DEFAULT_HEIGHT;
+               case ICON_SIZE_ICON:
+                       return ICON_DEFAULT_HEIGHT;
+               case ICON_SIZE_PREVIEW:
+                       return PREVIEW_DEFAULT_HEIGHT;
+               default:
+                       return 0;
        }
-       return 0;
 }
 
 
@@ -1147,9 +1223,12 @@ static void icon_draw_size(
                glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        }
        else if (di->type == ICON_TYPE_PREVIEW) {
-               PreviewImage *pi = BKE_previewimg_get((ID *)icon->obj);
+               PreviewImage *pi = (icon->type != 0) ? BKE_previewimg_id_ensure((ID *)icon->obj) : icon->obj;
 
                if (pi) {
+                       /* Do deferred loading/generation if needed. */
+//                     BKE_previewimg_ensure(pi, size);
+
                        /* no create icon on this level in code */
                        if (!pi->rect[size]) return;  /* something has gone wrong! */
                        
@@ -1164,17 +1243,17 @@ static void icon_draw_size(
 static void ui_id_preview_image_render_size(
         const bContext *C, Scene *scene, ID *id, PreviewImage *pi, int size, const bool use_job)
 {
-       if ((pi->changed[size] || !pi->rect[size])) { /* changed only ever set by dynamic icons */
+       if (((pi->flag[size] & PRV_CHANGED) || !pi->rect[size])) { /* changed only ever set by dynamic icons */
                /* create the rect if necessary */
                icon_set_image(C, scene, id, pi, size, use_job);
 
-               pi->changed[size] = 0;
+               pi->flag[size] &= ~PRV_CHANGED;
        }
 }
 
 void UI_id_icon_render(const bContext *C, Scene *scene, ID *id, const bool big, const bool use_job)
 {
-       PreviewImage *pi = BKE_previewimg_get(id);
+       PreviewImage *pi = BKE_previewimg_id_ensure(id);
 
        if (pi) {
                if (big)
@@ -1186,7 +1265,7 @@ void UI_id_icon_render(const bContext *C, Scene *scene, ID *id, const bool big,
 
 static void ui_id_brush_render(const bContext *C, ID *id)
 {
-       PreviewImage *pi = BKE_previewimg_get(id); 
+       PreviewImage *pi = BKE_previewimg_id_ensure(id);
        enum eIconSizes i;
        
        if (!pi)
@@ -1195,9 +1274,9 @@ static void ui_id_brush_render(const bContext *C, ID *id)
        for (i = 0; i < NUM_ICON_SIZES; i++) {
                /* check if rect needs to be created; changed
                 * only set by dynamic icons */
-               if ((pi->changed[i] || !pi->rect[i])) {
+               if (((pi->flag[i] & PRV_CHANGED) || !pi->rect[i])) {
                        icon_set_image(C, NULL, id, pi, i, true);
-                       pi->changed[i] = 0;
+                       pi->flag[i] &= ~PRV_CHANGED;
                }
        }
 }
@@ -1208,7 +1287,7 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id)
        Brush *br = (Brush *)id;
 
        if (br->flag & BRUSH_CUSTOM_ICON) {
-               BKE_icon_getid(id);
+               BKE_icon_id_ensure(id);
                ui_id_brush_render(C, id);
        }
        else {
@@ -1270,7 +1349,7 @@ int ui_id_icon_get(const bContext *C, ID *id, const bool big)
                case ID_IM: /* fall through */
                case ID_WO: /* fall through */
                case ID_LA: /* fall through */
-                       iconid = BKE_icon_getid(id);
+                       iconid = BKE_icon_id_ensure(id);
                        /* checks if not exists, or changed */
                        UI_id_icon_render(C, NULL, id, big, true);
                        break;
index a3192d3b8d0ddf71dc30edfc662a11d0d91bc77b..9461547a164e27117e0d0595ebbe6cd6ace0e526 100644 (file)
@@ -465,6 +465,8 @@ extern bool ui_but_string_set(struct bContext *C, uiBut *but, const char *str) A
 extern bool ui_but_string_set_eval_num(struct bContext *C, uiBut *but, const char *str, double *value) ATTR_NONNULL();
 extern int  ui_but_string_get_max_length(uiBut *but);
 extern uiBut *ui_but_drag_multi_edit_get(uiBut *but);
+
+void ui_def_but_icon(uiBut *but, const int icon, const int flag);
 extern uiButExtraIconType ui_but_icon_extra_get(uiBut *but);
 
 extern void ui_but_default_set(struct bContext *C, const bool all, const bool use_afterfunc);
@@ -673,6 +675,7 @@ void ui_draw_preview_item(struct uiFontStyle *fstyle, rcti *rect, const char *na
 void uiStyleInit(void);
 
 /* interface_icons.c */
+void ui_icon_ensure_deferred(const struct bContext *C, const int icon_id, const bool big);
 int ui_id_icon_get(const struct bContext *C, struct ID *id, const bool big);
 
 /* resources.c */
index fe1a1b9096dce9eb9dddad4564fd57d871eda3da..9a34e4be98065d1b66209439dd5b2fb6c73ba9a1 100644 (file)
@@ -430,8 +430,8 @@ static void template_ID(
 
                but = uiDefBlockButN(block, id_search_menu, MEM_dupallocN(template), "", 0, 0, UI_UNIT_X * 6, UI_UNIT_Y * 6,
                                     TIP_(template_id_browse_tip(type)));
-               but->icon = id ? ui_id_icon_get(C, id, true) : RNA_struct_ui_icon(type);
-               UI_but_flag_enable(but, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
+               ui_def_but_icon(but, id ? ui_id_icon_get(C, id, true) : RNA_struct_ui_icon(type),
+                               UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
 
                if ((idfrom && idfrom->lib) || !editable)
                        UI_but_flag_enable(but, UI_BUT_DISABLED);
@@ -441,10 +441,9 @@ static void template_ID(
        else if (flag & UI_ID_BROWSE) {
                but = uiDefBlockButN(block, id_search_menu, MEM_dupallocN(template), "", 0, 0, UI_UNIT_X * 1.6, UI_UNIT_Y,
                                     TIP_(template_id_browse_tip(type)));
-               but->icon = RNA_struct_ui_icon(type);
+               ui_def_but_icon(but, RNA_struct_ui_icon(type), UI_HAS_ICON);
                /* default dragging of icon for id browse buttons */
                UI_but_drag_set_id(but, id);
-               UI_but_flag_enable(but, UI_HAS_ICON);
                UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT);
 
                if ((idfrom && idfrom->lib) || !editable)
@@ -1666,9 +1665,9 @@ static uiBlock *ui_icon_view_menu_cb(bContext *C, ARegion *ar, void *arg_litem)
                
                icon = item[a].icon;
                value = item[a].value;
-               but = uiDefIconButR_prop(block, UI_BTYPE_ROW, 0, icon, x, y, UI_UNIT_X * 5, UI_UNIT_Y * 5,
+               but = uiDefIconButR_prop(block, UI_BTYPE_ROW, 0, ICON_NONE, x, y, UI_UNIT_X * 5, UI_UNIT_Y * 5,
                                         &cb.ptr, cb.prop, -1, 0, value, -1, -1, NULL);
-               UI_but_flag_enable(but, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
+               ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
        }
 
        UI_block_bounds_set_normal(block, 0.3f * U.widget_unit);
@@ -1708,8 +1707,7 @@ void uiTemplateIconView(uiLayout *layout, PointerRNA *ptr, const char *propname)
 
        but = uiDefBlockButN(block, ui_icon_view_menu_cb, cb, "", 0, 0, UI_UNIT_X * 6, UI_UNIT_Y * 6, "");
 
-       but->icon = icon;
-       UI_but_flag_enable(but, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
+       ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
 
        if (free_items) {
                MEM_freeN(items);
index 65d4c2301a948ff4c8e9ca0d3897ae6c121359b8..5112cd84d0967e1158b69ebfe238a35a6e2ea1ad 100644 (file)
@@ -79,7 +79,7 @@
 
 #include "IMB_imbuf.h"
 #include "IMB_imbuf_types.h"
-
+#include "IMB_thumbs.h"
 
 #include "BIF_gl.h"
 #include "BIF_glutil.h"
@@ -128,7 +128,7 @@ ImBuf *get_brush_icon(Brush *brush)
                                }
 
                                if (brush->icon_imbuf)
-                                       BKE_icon_changed(BKE_icon_getid(&brush->id));
+                                       BKE_icon_changed(BKE_icon_id_ensure(&brush->id));
                        }
                }
        }
@@ -935,65 +935,87 @@ static void set_alpha(char *cp, int sizex, int sizey, char alpha)
 static void icon_preview_startjob(void *customdata, short *stop, short *do_update)
 {
        ShaderPreview *sp = customdata;
-       ID *id = sp->id;
-       short idtype = GS(id->name);
-       
-       if (idtype == ID_IM) {
-               Image *ima = (Image *)id;
-               ImBuf *ibuf = NULL;
-               ImageUser iuser = {NULL};
 
-               /* ima->ok is zero when Image cannot load */
-               if (ima == NULL || ima->ok == 0)
-                       return;
+       if (sp->pr_method == PR_ICON_DEFERRED) {
+               PreviewImage *prv = sp->owner;
+               ImBuf *thumb;
+               char *deferred_data = PRV_DEFERRED_DATA(prv);
+               int source =  deferred_data[0];
+               char *path = &deferred_data[1];
 
-               /* setup dummy image user */
-               iuser.ok = iuser.framenr = 1;
-               iuser.scene = sp->scene;
-               
-               /* elubie: this needs to be changed: here image is always loaded if not
-                * already there. Very expensive for large images. Need to find a way to 
-                * only get existing ibuf */
-               ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
-               if (ibuf == NULL || ibuf->rect == NULL) {
-                       BKE_image_release_ibuf(ima, ibuf, NULL);
-                       return;
-               }
-               
-               icon_copy_rect(ibuf, sp->sizex, sp->sizey, sp->pr_rect);
+//             printf("generating deferred %d×%d preview for %s\n", sp->sizex, sp->sizey, path);
+
+               thumb = IMB_thumb_manage(path, THB_LARGE, source);
 
-               *do_update = true;
+               if (thumb) {
+                       /* PreviewImage assumes premultiplied alhpa... */
+                       IMB_premultiply_alpha(thumb);
 
-               BKE_image_release_ibuf(ima, ibuf, NULL);
+                       icon_copy_rect(thumb, sp->sizex, sp->sizey, sp->pr_rect);
+                       IMB_freeImBuf(thumb);
+               }
        }
-       else if (idtype == ID_BR) {
-               Brush *br = (Brush *)id;
+       else {
+               ID *id = sp->id;
+               short idtype = GS(id->name);
+
+               if (idtype == ID_IM) {
+                       Image *ima = (Image *)id;
+                       ImBuf *ibuf = NULL;
+                       ImageUser iuser = {NULL};
+
+                       /* ima->ok is zero when Image cannot load */
+                       if (ima == NULL || ima->ok == 0)
+                               return;
+
+                       /* setup dummy image user */
+                       iuser.ok = iuser.framenr = 1;
+                       iuser.scene = sp->scene;
+
+                       /* elubie: this needs to be changed: here image is always loaded if not
+                        * already there. Very expensive for large images. Need to find a way to
+                        * only get existing ibuf */
+                       ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
+                       if (ibuf == NULL || ibuf->rect == NULL) {
+                               BKE_image_release_ibuf(ima, ibuf, NULL);
+                               return;
+                       }
 
-               br->icon_imbuf = get_brush_icon(br);
+                       icon_copy_rect(ibuf, sp->sizex, sp->sizey, sp->pr_rect);
 
-               memset(sp->pr_rect, 0x88, sp->sizex * sp->sizey * sizeof(unsigned int));
+                       *do_update = true;
 
-               if (!(br->icon_imbuf) || !(br->icon_imbuf->rect))
-                       return;
+                       BKE_image_release_ibuf(ima, ibuf, NULL);
+               }
+               else if (idtype == ID_BR) {
+                       Brush *br = (Brush *)id;
 
-               icon_copy_rect(br->icon_imbuf, sp->sizex, sp->sizey, sp->pr_rect);
+                       br->icon_imbuf = get_brush_icon(br);
 
-               *do_update = true;
-       }
-       else {
-               /* re-use shader job */
-               shader_preview_startjob(customdata, stop, do_update);
+                       memset(sp->pr_rect, 0x88, sp->sizex * sp->sizey * sizeof(unsigned int));
+
+                       if (!(br->icon_imbuf) || !(br->icon_imbuf->rect))
+                               return;
 
-               /* world is rendered with alpha=0, so it wasn't displayed 
-                * this could be render option for sky to, for later */
-               if (idtype == ID_WO) {
-                       set_alpha((char *)sp->pr_rect, sp->sizex, sp->sizey, 255);
+                       icon_copy_rect(br->icon_imbuf, sp->sizex, sp->sizey, sp->pr_rect);
+
+                       *do_update = true;
                }
-               else if (idtype == ID_MA) {
-                       Material *ma = (Material *)id;
+               else {
+                       /* re-use shader job */
+                       shader_preview_startjob(customdata, stop, do_update);
 
-                       if (ma->material_type == MA_TYPE_HALO)
+                       /* world is rendered with alpha=0, so it wasn't displayed
+                        * this could be render option for sky to, for later */
+                       if (idtype == ID_WO) {
                                set_alpha((char *)sp->pr_rect, sp->sizex, sp->sizey, 255);
+                       }
+                       else if (idtype == ID_MA) {
+                               Material *ma = (Material *)id;
+
+                               if (ma->material_type == MA_TYPE_HALO)
+                                       set_alpha((char *)sp->pr_rect, sp->sizex, sp->sizey, 255);
+                       }
                }
        }
 }
@@ -1005,7 +1027,7 @@ static void common_preview_startjob(void *customdata, short *stop, short *do_upd
 {
        ShaderPreview *sp = customdata;
 
-       if (sp->pr_method == PR_ICON_RENDER)
+       if (ELEM(sp->pr_method, PR_ICON_RENDER, PR_ICON_DEFERRED))
                icon_preview_startjob(customdata, stop, do_update);
        else
                shader_preview_startjob(customdata, stop, do_update);
@@ -1041,29 +1063,34 @@ static void icon_preview_startjob_all_sizes(void *customdata, short *stop, short
        const bool use_new_shading = BKE_scene_use_new_shading_nodes(ip->scene);
 
        while (cur_size) {
+               PreviewImage *prv = ip->owner;
                ShaderPreview *sp = MEM_callocN(sizeof(ShaderPreview), "Icon ShaderPreview");
+               const bool is_render = !prv->use_deferred;
 
                /* construct shader preview from image size and previewcustomdata */
                sp->scene = ip->scene;
                sp->owner = ip->owner;
                sp->sizex = cur_size->sizex;
                sp->sizey = cur_size->sizey;
-               sp->pr_method = PR_ICON_RENDER;
+               sp->pr_method = is_render ? PR_ICON_RENDER : PR_ICON_DEFERRED;
                sp->pr_rect = cur_size->rect;
                sp->id = ip->id;
 
-               if (use_new_shading) {
-                       /* texture icon rendering is hardcoded to use BI,
-                        * so don't even think of using cycle's bmain for
-                        * texture icons
-                        */
-                       if (GS(ip->id->name) != ID_TE)
-                               sp->pr_main = G_pr_main_cycles;
-                       else
+               if (is_render) {
+                       BLI_assert(ip->id);
+                       if (use_new_shading) {
+                               /* texture icon rendering is hardcoded to use BI,
+                                * so don't even think of using cycle's bmain for
+                                * texture icons
+                                */
+                               if (GS(ip->id->name) != ID_TE)
+                                       sp->pr_main = G_pr_main_cycles;
+                               else
+                                       sp->pr_main = G_pr_main;
+                       }
+                       else {
                                sp->pr_main = G_pr_main;
-               }
-               else {
-                       sp->pr_main = G_pr_main;
+                       }
                }
 
                common_preview_startjob(sp, stop, do_update, progress);
@@ -1145,7 +1172,7 @@ void ED_preview_icon_job(const bContext *C, void *owner, ID *id, unsigned int *r
 
        /* customdata for preview thread */
        ip->scene = CTX_data_scene(C);
-       ip->owner = id;
+       ip->owner = owner;
        ip->id = id;
 
        icon_preview_add_size(ip, rect, sizex, sizey);
index d3d5cb12ac8c8221a2225e7f5eb65b6014c169d9..dedcbb144aa4d29056a4e44cba0418510004fbb9 100644 (file)
@@ -270,7 +270,7 @@ static void material_changed(Main *bmain, Material *ma)
        int texture_draw = false;
 
        /* icons */
-       BKE_icon_changed(BKE_icon_getid(&ma->id));
+       BKE_icon_changed(BKE_icon_id_ensure(&ma->id));
 
        /* glsl */
        if (ma->gpumaterial.first)
@@ -285,7 +285,7 @@ static void material_changed(Main *bmain, Material *ma)
                        continue;
                }
 
-               BKE_icon_changed(BKE_icon_getid(&parent->id));
+               BKE_icon_changed(BKE_icon_id_ensure(&parent->id));
 
                if (parent->gpumaterial.first)
                        GPU_material_free(&parent->gpumaterial);
@@ -325,7 +325,7 @@ static void lamp_changed(Main *bmain, Lamp *la)
        Material *ma;
 
        /* icons */
-       BKE_icon_changed(BKE_icon_getid(&la->id));
+       BKE_icon_changed(BKE_icon_id_ensure(&la->id));
 
        /* glsl */
        for (ob = bmain->object.first; ob; ob = ob->id.next)
@@ -361,7 +361,7 @@ static void texture_changed(Main *bmain, Tex *tex)
        bool texture_draw = false;
 
        /* icons */
-       BKE_icon_changed(BKE_icon_getid(&tex->id));
+       BKE_icon_changed(BKE_icon_id_ensure(&tex->id));
 
        /* paint overlays */
        for (scene = bmain->scene.first; scene; scene = scene->id.next)
@@ -372,7 +372,7 @@ static void texture_changed(Main *bmain, Tex *tex)
                if (!material_uses_texture(ma, tex))
                        continue;
 
-               BKE_icon_changed(BKE_icon_getid(&ma->id));
+               BKE_icon_changed(BKE_icon_id_ensure(&ma->id));
 
                if (ma->gpumaterial.first)
                        GPU_material_free(&ma->gpumaterial);
@@ -403,7 +403,7 @@ static void texture_changed(Main *bmain, Tex *tex)
                        continue;
                }
 
-               BKE_icon_changed(BKE_icon_getid(&wo->id));
+               BKE_icon_changed(BKE_icon_id_ensure(&wo->id));
                
                if (wo->gpumaterial.first)
                        GPU_material_free(&wo->gpumaterial);            
@@ -451,7 +451,7 @@ static void world_changed(Main *bmain, World *wo)
        Material *ma;
 
        /* icons */
-       BKE_icon_changed(BKE_icon_getid(&wo->id));
+       BKE_icon_changed(BKE_icon_id_ensure(&wo->id));
        
        /* glsl */
        for (ma = bmain->mat.first; ma; ma = ma->id.next)
@@ -470,7 +470,7 @@ static void image_changed(Main *bmain, Image *ima)
        Tex *tex;
 
        /* icons */
-       BKE_icon_changed(BKE_icon_getid(&ima->id));
+       BKE_icon_changed(BKE_icon_id_ensure(&ima->id));
 
        /* textures */
        for (tex = bmain->tex.first; tex; tex = tex->id.next)
index 75e6f77ba49c0952b29bb4157efd5009ab3a134e..ddb1cec918064d6f1ecd1db314c4eeacd7a7fa30 100644 (file)
@@ -1346,7 +1346,7 @@ static int image_replace_exec(bContext *C, wmOperator *op)
        /* XXX unpackImage frees image buffers */
        ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
        
-       BKE_icon_changed(BKE_icon_getid(&sima->image->id));
+       BKE_icon_changed(BKE_icon_id_ensure(&sima->image->id));
        BKE_image_signal(sima->image, &sima->iuser, IMA_SIGNAL_RELOAD);
        WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, sima->image);
 
index f977a633c57e8941902d160c99b9943a628d5cca..2cb56832a003bac6cfe2e885afbf2b875b0e44de 100644 (file)
@@ -61,6 +61,8 @@ typedef enum ThumbSource {
 /* don't generate thumbs for images bigger then this (100mb) */
 #define THUMB_SIZE_MAX (100 * 1024 * 1024)
 
+#define PREVIEW_RENDER_DEFAULT_HEIGHT 128
+
 /* create thumbnail for file and returns new imbuf for thumbnail */
 ImBuf *IMB_thumb_create(const char *path, ThumbSize size, ThumbSource source, ImBuf *ibuf);
 
index a608aa0db34fd11fa77fe61ad0cd9acfd6b42931..626f02e8ade4750ee0f51fe17da43c2721c5f436 100644 (file)
@@ -314,10 +314,10 @@ static ImBuf *thumb_create_ex(
 
        switch (size) {
                case THB_NORMAL:
-                       tsize = 128;
+                       tsize = PREVIEW_RENDER_DEFAULT_HEIGHT;
                        break;
                case THB_LARGE:
-                       tsize = 256;
+                       tsize = PREVIEW_RENDER_DEFAULT_HEIGHT * 2;
                        break;
                case THB_FAIL:
                        tsize = 1;
index b51b53cc801b6305fed61bc57df6d4b47eb5b451..f68de03614bf659ade352864f264262c991848f6 100644 (file)
@@ -155,20 +155,36 @@ typedef struct Library {
 
 enum eIconSizes {
        ICON_SIZE_ICON = 0,
-       ICON_SIZE_PREVIEW = 1
+       ICON_SIZE_PREVIEW = 1,
+
+       NUM_ICON_SIZES
+};
+
+/* for PreviewImage->flag */
+enum ePreviewImage_Flag {
+       PRV_CHANGED          = (1 << 0),
+       PRV_USER_EDITED      = (1 << 1),  /* if user-edited, do not auto-update this anymore! */
 };
-#define NUM_ICON_SIZES (ICON_SIZE_PREVIEW + 1)
 
 typedef struct PreviewImage {
        /* All values of 2 are really NUM_ICON_SIZES */
        unsigned int w[2];
        unsigned int h[2];
-       short changed[2];
+       short flag[2];
        short changed_timestamp[2];
        unsigned int *rect[2];
+
+       /* Runtime-only data. */
        struct GPUTexture *gputexture[2];
+       int icon_id;  /* Used by previews outside of ID context. */
+
+       char pad[3];
+       char use_deferred;  /* for now a mere bool, if we add more deferred loading methods we can switch to bitflag. */
 } PreviewImage;
 
+#define PRV_DEFERRED_DATA(prv) \
+       (CHECK_TYPE_INLINE(prv, PreviewImage *), BLI_assert((prv)->use_deferred), (void *)((prv) + 1))
+
 /**
  * Defines for working with IDs.
  *
index cff9e424f3128f4c939dd33984e5ba50c2c33b5a..965d1d916144953629b83035224d052ac10969af 100644 (file)
@@ -280,6 +280,7 @@ extern StructRNA RNA_IKParam;
 extern StructRNA RNA_Image;
 extern StructRNA RNA_ImageFormatSettings;
 extern StructRNA RNA_ImagePaint;
+extern StructRNA RNA_ImagePreview;
 extern StructRNA RNA_ImageSequence;
 extern StructRNA RNA_ImageTexture;
 extern StructRNA RNA_ImageUser;
index db25786c599d8c890d0e53f7a00feac353cd1106..b87b455b36ff66bb0c9b4fbf82e4a7a0ec892181 100644 (file)
@@ -34,6 +34,8 @@
 
 #include "BLI_utildefines.h"
 
+#include "BKE_icons.h"
+
 #include "RNA_access.h"
 #include "RNA_define.h"
 #include "RNA_enum_types.h"
@@ -406,6 +408,183 @@ static void rna_Library_filepath_set(PointerRNA *ptr, const char *value)
        BKE_library_filepath_set(lib, value);
 }
 
+/* ***** ImagePreview ***** */
+
+static void rna_ImagePreview_is_custom_set(PointerRNA *ptr, int value, enum eIconSizes size)
+{
+       ID *id = ptr->id.data;
+       PreviewImage *prv_img = (PreviewImage *)ptr->data;
+
+       if (id != NULL) {
+               BLI_assert(prv_img == BKE_previewimg_id_ensure(id));
+       }
+
+       if ((value && (prv_img->flag[size] & PRV_USER_EDITED)) || (!value && !(prv_img->flag[size] & PRV_USER_EDITED))) {
+               return;
+       }
+
+       if (value)
+               prv_img->flag[size] |= PRV_USER_EDITED;
+       else
+               prv_img->flag[size] &= ~PRV_USER_EDITED;
+
+       prv_img->flag[size] |= PRV_CHANGED;
+
+       BKE_previewimg_clear_single(prv_img, size);
+}
+
+static void rna_ImagePreview_size_get(PointerRNA *ptr, int *values, enum eIconSizes size)
+{
+       ID *id = (ID *)ptr->id.data;
+       PreviewImage *prv_img = (PreviewImage *)ptr->data;
+
+       if (id != NULL) {
+               BLI_assert(prv_img == BKE_previewimg_id_ensure(id));
+       }
+
+       BKE_previewimg_ensure(prv_img, size);
+
+       values[0] = prv_img->w[size];
+       values[1] = prv_img->h[size];
+}
+
+static void rna_ImagePreview_size_set(PointerRNA *ptr, const int *values, enum eIconSizes size)
+{
+       ID *id = (ID *)ptr->id.data;
+       PreviewImage *prv_img = (PreviewImage *)ptr->data;
+
+       if (id != NULL) {
+               BLI_assert(prv_img == BKE_previewimg_id_ensure(id));
+       }
+
+       BKE_previewimg_ensure(prv_img, size);
+
+       if (values[0] && values[1]) {
+               prv_img->rect[size] = MEM_callocN(values[0] * values[1] * sizeof(unsigned int), "prv_rect");
+       }
+
+       prv_img->w[size] = values[0];
+       prv_img->h[size] = values[1];
+
+       prv_img->flag[size] |= (PRV_CHANGED | PRV_USER_EDITED);
+}
+
+static int rna_ImagePreview_pixels_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION], enum eIconSizes size)
+{
+       ID *id = ptr->id.data;
+       PreviewImage *prv_img = (PreviewImage *)ptr->data;
+
+       if (id != NULL) {
+               BLI_assert(prv_img == BKE_previewimg_id_ensure(id));
+       }
+
+       BKE_previewimg_ensure(prv_img, size);
+
+       length[0] = prv_img->w[size] * prv_img->h[size];
+
+       return length[0];
+}
+
+static void rna_ImagePreview_pixels_get(PointerRNA *ptr, int *values, enum eIconSizes size)
+{
+       ID *id = ptr->id.data;
+       PreviewImage *prv_img = (PreviewImage *)ptr->data;
+
+       if (id != NULL) {
+               BLI_assert(prv_img == BKE_previewimg_id_ensure(id));
+       }
+
+       BKE_previewimg_ensure(prv_img, size);
+
+       memcpy(values, prv_img->rect[size], prv_img->w[size] * prv_img->h[size] * sizeof(unsigned int));
+}
+
+static void rna_ImagePreview_pixels_set(PointerRNA *ptr, const int *values, enum eIconSizes size)
+{
+       ID *id = ptr->id.data;
+       PreviewImage *prv_img = (PreviewImage *)ptr->data;
+
+       if (id != NULL) {
+               BLI_assert(prv_img == BKE_previewimg_id_ensure(id));
+       }
+
+       memcpy(prv_img->rect[size], values, prv_img->w[size] * prv_img->h[size] * sizeof(unsigned int));
+       prv_img->flag[size] |= PRV_USER_EDITED;
+}
+
+static void rna_ImagePreview_is_image_custom_set(PointerRNA *ptr, int value)
+{
+       rna_ImagePreview_is_custom_set(ptr, value, ICON_SIZE_PREVIEW);
+}
+
+static void rna_ImagePreview_image_size_get(PointerRNA *ptr, int *values)
+{
+       rna_ImagePreview_size_get(ptr, values, ICON_SIZE_PREVIEW);
+}
+
+static void rna_ImagePreview_image_size_set(PointerRNA *ptr, const int *values)
+{
+       rna_ImagePreview_size_set(ptr, values, ICON_SIZE_PREVIEW);
+}
+
+static int rna_ImagePreview_image_pixels_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION])
+{
+       return rna_ImagePreview_pixels_get_length(ptr, length, ICON_SIZE_PREVIEW);
+}
+
+static void rna_ImagePreview_image_pixels_get(PointerRNA *ptr, int *values)
+{
+       rna_ImagePreview_pixels_get(ptr, values, ICON_SIZE_PREVIEW);
+}
+
+static void rna_ImagePreview_image_pixels_set(PointerRNA *ptr, const int *values)
+{
+       rna_ImagePreview_pixels_set(ptr, values, ICON_SIZE_PREVIEW);
+}
+
+static void rna_ImagePreview_is_icon_custom_set(PointerRNA *ptr, int value)
+{
+       rna_ImagePreview_is_custom_set(ptr, value, ICON_SIZE_ICON);
+}
+
+static void rna_ImagePreview_icon_size_get(PointerRNA *ptr, int *values)
+{
+       rna_ImagePreview_size_get(ptr, values, ICON_SIZE_ICON);
+}
+
+static void rna_ImagePreview_icon_size_set(PointerRNA *ptr, const int *values)
+{
+       rna_ImagePreview_size_set(ptr, values, ICON_SIZE_ICON);
+}
+
+static int rna_ImagePreview_icon_pixels_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION])
+{
+       return rna_ImagePreview_pixels_get_length(ptr, length, ICON_SIZE_ICON);
+}
+
+static void rna_ImagePreview_icon_pixels_get(PointerRNA *ptr, int *values)
+{
+       rna_ImagePreview_pixels_get(ptr, values, ICON_SIZE_ICON);
+}
+
+static void rna_ImagePreview_icon_pixels_set(PointerRNA *ptr, const int *values)
+{
+       rna_ImagePreview_pixels_set(ptr, values, ICON_SIZE_ICON);
+}
+
+static int rna_ImagePreview_icon_id_get(PointerRNA *ptr)
+{
+       /* Using a callback here allows us to only generate icon matching that preview when icon_id is requested. */
+       return BKE_icon_preview_ensure((PreviewImage *)(ptr->data));
+}
+static void rna_ImagePreview_icon_reload(PreviewImage *prv)
+{
+       /* will lazy load on next use, but only in case icon is not user-modified! */
+       if (!(prv->flag[ICON_SIZE_ICON] & PRV_USER_EDITED) && !(prv->flag[ICON_SIZE_PREVIEW] & PRV_USER_EDITED)) {
+               BKE_previewimg_clear(prv);
+       }
+}
+
 #else
 
 static void rna_def_ID_properties(BlenderRNA *brna)
@@ -524,6 +703,62 @@ static void rna_def_ID_materials(BlenderRNA *brna)
        RNA_def_boolean(func, "update_data", 0, "", "Update data by re-adjusting the material slots assigned");
 }
 
+static void rna_def_image_preview(BlenderRNA *brna)
+{
+       StructRNA *srna;
+       FunctionRNA *func;
+       PropertyRNA *prop;
+
+       srna = RNA_def_struct(brna, "ImagePreview", NULL);
+       RNA_def_struct_sdna(srna, "PreviewImage");
+       RNA_def_struct_ui_text(srna, "Image Preview", "Preview image and icon");
+
+       prop = RNA_def_property(srna, "is_image_custom", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "flag[ICON_SIZE_PREVIEW]", PRV_USER_EDITED);
+       RNA_def_property_boolean_funcs(prop, NULL, "rna_ImagePreview_is_image_custom_set");
+       RNA_def_property_ui_text(prop, "Custom Image", "True if this preview image has been modified by py script,"
+                                "and is no more auto-generated by Blender");
+
+       prop = RNA_def_int_vector(srna, "image_size", 2, NULL, 0, 0, "Image Size",
+                                 "Width and height in pixels", 0, 0);
+       RNA_def_property_subtype(prop, PROP_PIXEL);
+       RNA_def_property_int_funcs(prop, "rna_ImagePreview_image_size_get", "rna_ImagePreview_image_size_set", NULL);
+
+       prop = RNA_def_property(srna, "image_pixels", PROP_INT, PROP_NONE);
+       RNA_def_property_flag(prop, PROP_DYNAMIC);
+       RNA_def_property_multi_array(prop, 1, NULL);
+       RNA_def_property_ui_text(prop, "Image Pixels", "Image pixels, as bytes (always RGBA 32bits)");
+       RNA_def_property_dynamic_array_funcs(prop, "rna_ImagePreview_image_pixels_get_length");
+       RNA_def_property_int_funcs(prop, "rna_ImagePreview_image_pixels_get", "rna_ImagePreview_image_pixels_set", NULL);
+
+
+       prop = RNA_def_property(srna, "is_icon_custom", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "flag[ICON_SIZE_ICON]", PRV_USER_EDITED);
+       RNA_def_property_boolean_funcs(prop, NULL, "rna_ImagePreview_is_icon_custom_set");
+       RNA_def_property_ui_text(prop, "Custom Icon", "True if this preview icon has been modified by py script,"
+                                "and is no more auto-generated by Blender");
+
+       prop = RNA_def_int_vector(srna, "icon_size", 2, NULL, 0, 0, "Icon Size",
+                                 "Width and height in pixels", 0, 0);
+       RNA_def_property_subtype(prop, PROP_PIXEL);
+       RNA_def_property_int_funcs(prop, "rna_ImagePreview_icon_size_get", "rna_ImagePreview_icon_size_set", NULL);
+
+       prop = RNA_def_property(srna, "icon_pixels", PROP_INT, PROP_NONE);
+       RNA_def_property_flag(prop, PROP_DYNAMIC);
+       RNA_def_property_multi_array(prop, 1, NULL);
+       RNA_def_property_ui_text(prop, "Icon Pixels", "Icon pixels, as bytes (always RGBA 32bits)");
+       RNA_def_property_dynamic_array_funcs(prop, "rna_ImagePreview_icon_pixels_get_length");
+       RNA_def_property_int_funcs(prop, "rna_ImagePreview_icon_pixels_get", "rna_ImagePreview_icon_pixels_set", NULL);
+
+       prop = RNA_def_int(srna, "icon_id", 0, INT_MIN, INT_MAX, "Icon ID",
+                          "Unique integer identifying this preview as an icon (zero means invalid)", INT_MIN, INT_MAX);
+       RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+       RNA_def_property_int_funcs(prop, "rna_ImagePreview_icon_id_get", NULL, NULL);
+
+       func = RNA_def_function(srna, "reload", "rna_ImagePreview_icon_reload");
+       RNA_def_function_ui_description(func, "Reload the preview from its source path");
+}
+
 static void rna_def_ID(BlenderRNA *brna)
 {
        StructRNA *srna;
@@ -651,6 +886,7 @@ void RNA_def_ID(BlenderRNA *brna)
        RNA_def_struct_ui_text(srna, "Any Type", "RNA type used for pointers to any possible data");
 
        rna_def_ID(brna);
+       rna_def_image_preview(brna);
        rna_def_ID_properties(brna);
        rna_def_ID_materials(brna);
        rna_def_library(brna);
index 362cf77a332ae2f560ca7d62dd910289ff243160..e9e4282772d126800b3baf75d87c9de9265d489f 100644 (file)
@@ -342,8 +342,8 @@ static void rna_Brush_reset_icon(Brush *br, const char *UNUSED(type))
                return;
 
        if (id->icon_id >= BIFICONID_LAST) {
-               BKE_icon_delete(id);
-               BKE_previewimg_free_id(id);
+               BKE_icon_id_delete(id);
+               BKE_previewimg_id_free(id);
        }
 
        id->icon_id = 0;
@@ -415,8 +415,8 @@ static void rna_Brush_icon_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Poi
        br->id.icon_id = 0;
 
        if (br->flag & BRUSH_CUSTOM_ICON) {
-               BKE_previewimg_get(&br->id);
-               BKE_icon_changed(BKE_icon_getid(&br->id));
+               BKE_previewimg_id_ensure(&br->id);
+               BKE_icon_changed(BKE_icon_id_ensure(&br->id));
        }
 
        WM_main_add_notifier(NC_BRUSH | NA_EDITED, br);
index 168f23ceaa189745fc1a2b6358ef3dfc5648639f..8b5ed66e21754eb07b605a2400cfeeb89dbd5dfc 100644 (file)
@@ -35,6 +35,7 @@
 
 #include "DNA_ID.h"
 #include "DNA_modifier_types.h"
+#include "DNA_space_types.h"
 
 #include "BLI_utildefines.h"
 #include "BLI_path_util.h"
@@ -58,6 +59,7 @@
 #include "BKE_library.h"
 #include "BKE_object.h"
 #include "BKE_material.h"
+#include "BKE_icons.h"
 #include "BKE_image.h"
 #include "BKE_texture.h"
 #include "BKE_scene.h"
@@ -807,11 +809,12 @@ static int rna_Main_linestyle_is_updated_get(PointerRNA *ptr) { return DAG_id_ty
 
 #else
 
-void RNA_api_main(StructRNA *srna)
+void RNA_api_main(StructRNA *UNUSED(srna))
 {
 #if 0
        FunctionRNA *func;
        PropertyRNA *parm;
+
        /* maybe we want to add functions in 'bpy.data' still?
         * for now they are all in collections bpy.data.images.new(...) */
        func = RNA_def_function(srna, "add_image", "rna_Main_add_image");
@@ -820,8 +823,6 @@ void RNA_api_main(StructRNA *srna)
        RNA_def_property_flag(parm, PROP_REQUIRED);
        parm = RNA_def_pointer(func, "image", "Image", "", "New image");
        RNA_def_function_return(func, parm);
-#else
-       (void)srna;
 #endif
 }
 
index 8296027f044ba88532733a0fb95adf1eab588ec8..ed04152182eefe609691c9ac920d0e3fab8e31cc 100644 (file)
@@ -31,6 +31,7 @@ set(INC
        ../../blenloader
        ../../editors/include
        ../../gpu
+       ../../imbuf
        ../../makesdna
        ../../makesrna
        ../../windowmanager
@@ -69,6 +70,7 @@ set(SRC
        bpy_rna_callback.c
        bpy_traceback.c
        bpy_util.c
+       bpy_utils_previews.c
        bpy_utils_units.c
        stubs.c
 
@@ -94,6 +96,7 @@ set(SRC
        bpy_rna_callback.h
        bpy_traceback.h
        bpy_util.h
+       bpy_utils_previews.h
        bpy_utils_units.h
        ../BPY_extern.h
 )
index ec3c017a7edb1651c581fb49f40637d53ad1faa0..9a5e488850e19b5f62ef0590a2ba5f5eb21a6128 100644 (file)
@@ -48,6 +48,7 @@
 #include "bpy_props.h"
 #include "bpy_library.h"
 #include "bpy_operator.h"
+#include "bpy_utils_previews.h"
 #include "bpy_utils_units.h"
 
 #include "../generic/py_capi_utils.h"
@@ -330,6 +331,7 @@ void BPy_init_modules(void)
        PyModule_AddObject(mod, "ops", BPY_operator_module());
        PyModule_AddObject(mod, "app", BPY_app_struct());
        PyModule_AddObject(mod, "_utils_units", BPY_utils_units());
+       PyModule_AddObject(mod, "_utils_previews", BPY_utils_previews_module());
 
        /* bpy context */
        RNA_pointer_create(NULL, &RNA_Context, (void *)BPy_GetContext(), &ctx_ptr);
diff --git a/source/blender/python/intern/bpy_utils_previews.c b/source/blender/python/intern/bpy_utils_previews.c
new file mode 100644 (file)
index 0000000..5509ff5
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Bastien Montagne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/python/intern/bpy_utils_previews.c
+ *  \ingroup pythonintern
+ *
+ * This file defines a singleton py object accessed via 'bpy.utils.previews',
+ * which exposes low-level API for custom previews/icons.
+ * It is replaced in final API by an higher-level python wrapper, that handles previews by addon,
+ * and automatically release them on deletion.
+ */
+
+#include <Python.h>
+#include <structmember.h>
+
+#include "BLI_utildefines.h"
+
+#include "RNA_types.h"
+#include "RNA_access.h"
+
+#include "BPY_extern.h"
+#include "bpy_utils_previews.h"
+#include "bpy_rna.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
+#include "IMB_thumbs.h"
+
+#include "BKE_icons.h"
+
+#include "DNA_ID.h"
+
+#include "../generic/python_utildefines.h"
+
+#define STR_SOURCE_TYPES "'IMAGE', 'MOVIE', 'BLEND', 'FONT'"
+
+PyDoc_STRVAR(bpy_utils_previews_new_doc,
+".. method:: new(name)\n"
+"\n"
+"   Generate a new empty preview, or return existing one matching ``name``.\n"
+"\n"
+"   :arg name: The name (unique id) identifying the preview.\n"
+"   :type name: string\n"
+"   :return: The Preview matching given name, or a new empty one.\n"
+"   :rtype: :class:`bpy.types.ImagePreview`\n"
+);
+static PyObject *bpy_utils_previews_new(PyObject *UNUSED(self), PyObject *args)
+{
+       char *name;
+       PreviewImage *prv;
+       PointerRNA ptr;
+
+       if (!PyArg_ParseTuple(args, "s:new", &name)) {
+               return NULL;
+       }
+
+       prv = BKE_previewimg_cached_ensure(name);
+       RNA_pointer_create(NULL, &RNA_ImagePreview, prv, &ptr);
+
+       return pyrna_struct_CreatePyObject(&ptr);
+}
+
+PyDoc_STRVAR(bpy_utils_previews_load_doc,
+".. method:: load(name, path, path_type, force_reload)\n"
+"\n"
+"   Generate a new preview from given file path, or return existing one matching ``name``.\n"
+"\n"
+"   :arg name: The name (unique id) identifying the preview.\n"
+"   :type name: string\n"
+"   :arg path: The file path to generate the preview from.\n"
+"   :type path: string\n"
+"   :arg path_type: The type of file, needed to generate the preview in [" STR_SOURCE_TYPES "].\n"
+"   :type path_type: string\n"
+"   :arg force_reload: If True, force running thumbnail manager even if preview already exists in cache.\n"
+"   :type force_reload: bool\n"
+"   :return: The Preview matching given name, or a new empty one.\n"
+"   :rtype: :class:`bpy.types.ImagePreview`\n"
+);
+static PyObject *bpy_utils_previews_load(PyObject *UNUSED(self), PyObject *args)
+{
+       char *name, *path, *path_type_s;
+       int path_type, force_reload = false;
+
+       PreviewImage *prv;
+       PointerRNA ptr;
+
+       if (!PyArg_ParseTuple( args, "sss|p:load", &name, &path, &path_type_s, &force_reload)) {
+               return NULL;
+       }
+
+       if (STREQ(path_type_s, "IMAGE")) {
+               path_type = THB_SOURCE_IMAGE;
+       }
+       else if (STREQ(path_type_s, "MOVIE")) {
+               path_type = THB_SOURCE_MOVIE;
+       }
+       else if (STREQ(path_type_s, "BLEND")) {
+               path_type = THB_SOURCE_BLEND;
+       }
+       else if (STREQ(path_type_s, "FONT")) {
+               path_type = THB_SOURCE_FONT;
+       }
+       else {
+               PyErr_Format(PyExc_ValueError,
+                            "load: invalid '%' path type, only [" STR_SOURCE_TYPES "] "
+                            "are supported", path_type_s);
+               return NULL;
+       }
+
+       prv = BKE_previewimg_cached_thumbnail_read(name, path, path_type, force_reload);
+       RNA_pointer_create(NULL, &RNA_ImagePreview, prv, &ptr);
+
+       return pyrna_struct_CreatePyObject(&ptr);
+}
+
+PyDoc_STRVAR(bpy_utils_previews_release_doc,
+".. method:: release(name)\n"
+"\n"
+"   Release (free) a previously created preview.\n"
+"\n"
+"\n"
+"   :arg name: The name (unique id) identifying the preview.\n"
+"   :type name: string\n"
+);
+static PyObject *bpy_utils_previews_release(PyObject *UNUSED(self), PyObject *args)
+{
+       char *name;
+
+       if (!PyArg_ParseTuple(args, "s:release", &name)) {
+               return NULL;
+       }
+
+       BKE_previewimg_cached_release(name);
+
+       Py_RETURN_NONE;
+}
+
+static struct PyMethodDef bpy_utils_previews_methods[] = {
+       /* Can't use METH_KEYWORDS alone, see http://bugs.python.org/issue11587 */
+       {"new", (PyCFunction)bpy_utils_previews_new, METH_VARARGS, bpy_utils_previews_new_doc},
+       {"load", (PyCFunction)bpy_utils_previews_load, METH_VARARGS, bpy_utils_previews_load_doc},
+       {"release", (PyCFunction)bpy_utils_previews_release, METH_VARARGS, bpy_utils_previews_release_doc},
+       {NULL, NULL, 0, NULL}
+};
+
+PyDoc_STRVAR(bpy_utils_previews_doc,
+"This object contains basic static methods to handle cached (non-ID) previews in Blender\n"
+"(low-level API, not exposed to final users)."
+);
+static struct PyModuleDef bpy_utils_previews_module = {
+       PyModuleDef_HEAD_INIT,
+       "bpy._utils_previews",
+       bpy_utils_previews_doc,
+       0,
+       bpy_utils_previews_methods,
+       NULL, NULL, NULL, NULL
+};
+
+
+PyObject *BPY_utils_previews_module(void)
+{
+       PyObject *submodule;
+
+       submodule = PyModule_Create(&bpy_utils_previews_module);
+
+       return submodule;
+}
diff --git a/source/blender/python/intern/bpy_utils_previews.h b/source/blender/python/intern/bpy_utils_previews.h
new file mode 100644 (file)
index 0000000..3d7ade0
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Bastien Montagne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/python/intern/bpy_utils_previews.h
+ *  \ingroup pythonintern
+ */
+
+#ifndef __BPY_UTILS_PREVIEWS_H__
+#define __BPY_UTILS_PREVIEWS_H__
+
+PyObject *BPY_utils_previews_module(void);
+
+#endif /* __BPY_UTILS_PREVIEWS_H__ */
index b5c267e8a39c0bd3a7ac0a61ec1e2c9e59413fba..77d3e3762a856a1558abb292041742fcdd406031 100644 (file)
@@ -172,6 +172,17 @@ void WM_init(bContext *C, int argc, const char **argv)
        
        BLF_lang_set(NULL);
 
+       if (!G.background) {
+               GPU_init();
+
+               GPU_set_mipmap(!(U.gameflags & USER_DISABLE_MIPMAP));
+               GPU_set_linear_mipmap(true);
+               GPU_set_anisotropic(U.anisotropic_filter);
+               GPU_set_gpu_mipmapping(U.use_gpu_mipmap);
+
+               UI_init();
+       }
+
        ED_spacemacros_init();
 
        /* note: there is a bug where python needs initializing before loading the
@@ -197,17 +208,6 @@ void WM_init(bContext *C, int argc, const char **argv)
 
        wm_init_reports(C); /* reports cant be initialized before the wm */
 
-       if (!G.background) {
-               GPU_init();
-
-               GPU_set_mipmap(!(U.gameflags & USER_DISABLE_MIPMAP));
-               GPU_set_linear_mipmap(true);
-               GPU_set_anisotropic(U.anisotropic_filter);
-               GPU_set_gpu_mipmapping(U.use_gpu_mipmap);
-
-               UI_init();
-       }
-       
        clear_matcopybuf();
        ED_render_clear_mtex_copybuf();