Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/modules/bpy/utils/__init__.py1
-rw-r--r--release/scripts/modules/bpy/utils/previews.py137
-rw-r--r--release/scripts/templates_py/ui_previews_custom_icon.py82
-rw-r--r--release/scripts/templates_py/ui_previews_dynamic_enum.py136
-rw-r--r--source/blender/blenkernel/BKE_icons.h38
-rw-r--r--source/blender/blenkernel/intern/icons.c342
-rw-r--r--source/blender/blenkernel/intern/image.c4
-rw-r--r--source/blender/blenkernel/intern/lamp.c2
-rw-r--r--source/blender/blenkernel/intern/material.c2
-rw-r--r--source/blender/blenkernel/intern/texture.c2
-rw-r--r--source/blender/blenkernel/intern/world.c2
-rw-r--r--source/blender/editors/include/ED_render.h10
-rw-r--r--source/blender/editors/include/UI_interface_icons.h2
-rw-r--r--source/blender/editors/interface/interface.c41
-rw-r--r--source/blender/editors/interface/interface_icons.c151
-rw-r--r--source/blender/editors/interface/interface_intern.h3
-rw-r--r--source/blender/editors/interface/interface_templates.c14
-rw-r--r--source/blender/editors/render/render_preview.c149
-rw-r--r--source/blender/editors/render/render_update.c16
-rw-r--r--source/blender/editors/space_image/image_ops.c2
-rw-r--r--source/blender/imbuf/IMB_thumbs.h2
-rw-r--r--source/blender/imbuf/intern/thumbs.c4
-rw-r--r--source/blender/makesdna/DNA_ID.h22
-rw-r--r--source/blender/makesrna/RNA_access.h1
-rw-r--r--source/blender/makesrna/intern/rna_ID.c236
-rw-r--r--source/blender/makesrna/intern/rna_brush.c8
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c7
-rw-r--r--source/blender/python/intern/CMakeLists.txt3
-rw-r--r--source/blender/python/intern/bpy.c2
-rw-r--r--source/blender/python/intern/bpy_utils_previews.c188
-rw-r--r--source/blender/python/intern/bpy_utils_previews.h32
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c22
32 files changed, 1424 insertions, 239 deletions
diff --git a/release/scripts/modules/bpy/utils/__init__.py b/release/scripts/modules/bpy/utils/__init__.py
index 5f235ae3958..fa97714eee6 100644
--- a/release/scripts/modules/bpy/utils/__init__.py
+++ b/release/scripts/modules/bpy/utils/__init__.py
@@ -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
index 00000000000..4e8adf8e209
--- /dev/null
+++ b/release/scripts/modules/bpy/utils/previews.py
@@ -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
index 00000000000..defa2d266e6
--- /dev/null
+++ b/release/scripts/templates_py/ui_previews_custom_icon.py
@@ -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
index 00000000000..1603df0335f
--- /dev/null
+++ b/release/scripts/templates_py/ui_previews_dynamic_enum.py
@@ -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()
diff --git a/source/blender/blenkernel/BKE_icons.h b/source/blender/blenkernel/BKE_icons.h
index 9af0d96884a..763a3874d4e 100644
--- a/source/blender/blenkernel/BKE_icons.h
+++ b/source/blender/blenkernel/BKE_icons.h
@@ -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__ */
diff --git a/source/blender/blenkernel/intern/icons.c b/source/blender/blenkernel/intern/icons.c
index d3225f3fa35..450c1388f37 100644
--- a/source/blender/blenkernel/intern/icons.c
+++ b/source/blender/blenkernel/intern/icons.c
@@ -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 */
@@ -53,6 +54,10 @@
#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);
+ }
+}
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index f3f4775dbf7..29576e4f1f6 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -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) {
diff --git a/source/blender/blenkernel/intern/lamp.c b/source/blender/blenkernel/intern/lamp.c
index 96b0b95adf3..44e35c645de 100644
--- a/source/blender/blenkernel/intern/lamp.c
+++ b/source/blender/blenkernel/intern/lamp.c
@@ -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;
}
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index eaaf1319167..283c7a6fc88 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -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 */
diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c
index 8e4fcc5d1cc..246e3f2a316 100644
--- a/source/blender/blenkernel/intern/texture.c
+++ b/source/blender/blenkernel/intern/texture.c
@@ -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) {
diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c
index f26cbf7880a..e4736b1f54c 100644
--- a/source/blender/blenkernel/intern/world.c
+++ b/source/blender/blenkernel/intern/world.c
@@ -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;
}
diff --git a/source/blender/editors/include/ED_render.h b/source/blender/editors/include/ED_render.h
index 2bc0566f4a6..414126cac13 100644
--- a/source/blender/editors/include/ED_render.h
+++ b/source/blender/editors/include/ED_render.h
@@ -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);
diff --git a/source/blender/editors/include/UI_interface_icons.h b/source/blender/editors/include/UI_interface_icons.h
index 9d190fa81c7..92f3b0180c6 100644
--- a/source/blender/editors/include/UI_interface_icons.h
+++ b/source/blender/editors/include/UI_interface_icons.h
@@ -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
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index b63871f5c82..a9cc926b275 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -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);
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index 6536c0a869e..7c05d5f4378 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -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;
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index a3192d3b8d0..9461547a164 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -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 */
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index fe1a1b9096d..9a34e4be980 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -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);
diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c
index 65d4c2301a9..5112cd84d09 100644
--- a/source/blender/editors/render/render_preview.c
+++ b/source/blender/editors/render/render_preview.c
@@ -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);
diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c
index d3d5cb12ac8..dedcbb144aa 100644
--- a/source/blender/editors/render/render_update.c
+++ b/source/blender/editors/render/render_update.c
@@ -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)
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index 75e6f77ba49..ddb1cec9180 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -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);
diff --git a/source/blender/imbuf/IMB_thumbs.h b/source/blender/imbuf/IMB_thumbs.h
index f977a633c57..2cb56832a00 100644
--- a/source/blender/imbuf/IMB_thumbs.h
+++ b/source/blender/imbuf/IMB_thumbs.h
@@ -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);
diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c
index a608aa0db34..626f02e8ade 100644
--- a/source/blender/imbuf/intern/thumbs.c
+++ b/source/blender/imbuf/intern/thumbs.c
@@ -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;
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index b51b53cc801..f68de03614b 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -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.
*
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index cff9e424f31..965d1d91614 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -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;
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index db25786c599..b87b455b36f 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -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);
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index 362cf77a332..e9e4282772d 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -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);
diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c
index 168f23ceaa1..8b5ed66e217 100644
--- a/source/blender/makesrna/intern/rna_main_api.c
+++ b/source/blender/makesrna/intern/rna_main_api.c
@@ -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
}
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt
index 8296027f044..ed04152182e 100644
--- a/source/blender/python/intern/CMakeLists.txt
+++ b/source/blender/python/intern/CMakeLists.txt
@@ -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
)
diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c
index ec3c017a7ed..9a5e488850e 100644
--- a/source/blender/python/intern/bpy.c
+++ b/source/blender/python/intern/bpy.c
@@ -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
index 00000000000..5509ff50f5f
--- /dev/null
+++ b/source/blender/python/intern/bpy_utils_previews.c
@@ -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
index 00000000000..3d7ade04b9f
--- /dev/null
+++ b/source/blender/python/intern/bpy_utils_previews.h
@@ -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__ */
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index b5c267e8a39..77d3e3762a8 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -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();