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:
authorBrecht Van Lommel <brechtvanlommel@gmail.com>2020-03-08 16:21:29 +0300
committerBrecht Van Lommel <brecht@blender.org>2020-03-11 22:45:39 +0300
commit6cf4861c3ac09fd65a765e8f8e3584713cc5303b (patch)
treeb2b104fbda65b67c56dd2a39ad812c89bc5b1ee2
parentd8aa613d94caf6a3d82a8f4e9e90b9b8f5c61a7d (diff)
Cleanup: refactor image loading to use abstract ImageLoader base class
Rather than passing around void pointers, various Blender image sources now subclass this. OIIO is also just another type of image loader. Also fixes T67718: Cycles viewport render crash editing point density settings
-rw-r--r--intern/cycles/blender/CMakeLists.txt1
-rw-r--r--intern/cycles/blender/blender_image.cpp380
-rw-r--r--intern/cycles/blender/blender_image.h61
-rw-r--r--intern/cycles/blender/blender_session.cpp8
-rw-r--r--intern/cycles/blender/blender_session.h16
-rw-r--r--intern/cycles/blender/blender_shader.cpp53
-rw-r--r--intern/cycles/blender/blender_volume.cpp155
-rw-r--r--intern/cycles/render/CMakeLists.txt2
-rw-r--r--intern/cycles/render/attribute.cpp79
-rw-r--r--intern/cycles/render/attribute.h9
-rw-r--r--intern/cycles/render/image.cpp538
-rw-r--r--intern/cycles/render/image.h172
-rw-r--r--intern/cycles/render/image_oiio.cpp236
-rw-r--r--intern/cycles/render/image_oiio.h48
-rw-r--r--intern/cycles/render/nodes.cpp86
-rw-r--r--intern/cycles/render/nodes.h31
-rw-r--r--source/blender/makesrna/intern/makesrna.c4
17 files changed, 1022 insertions, 857 deletions
diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt
index d9a2ebf8571..ae191b6c0f7 100644
--- a/intern/cycles/blender/CMakeLists.txt
+++ b/intern/cycles/blender/CMakeLists.txt
@@ -38,6 +38,7 @@ set(SRC
CCL_api.h
blender_device.h
blender_id_map.h
+ blender_image.h
blender_object_cull.h
blender_sync.h
blender_session.h
diff --git a/intern/cycles/blender/blender_image.cpp b/intern/cycles/blender/blender_image.cpp
index 55ce6a295d1..459dc1779fb 100644
--- a/intern/cycles/blender/blender_image.cpp
+++ b/intern/cycles/blender/blender_image.cpp
@@ -14,206 +14,71 @@
* limitations under the License.
*/
-#include "render/image.h"
+#include "MEM_guardedalloc.h"
-#include "blender/blender_sync.h"
+#include "blender/blender_image.h"
#include "blender/blender_session.h"
#include "blender/blender_util.h"
CCL_NAMESPACE_BEGIN
-/* builtin image file name is actually an image datablock name with
- * absolute sequence frame number concatenated via '@' character
- *
- * this function splits frame from builtin name
- */
-int BlenderSession::builtin_image_frame(const string &builtin_name)
+/* Packed Images */
+
+BlenderImageLoader::BlenderImageLoader(BL::Image b_image, int frame)
+ : b_image(b_image), frame(frame), free_cache(!b_image.has_data())
{
- int last = builtin_name.find_last_of('@');
- return atoi(builtin_name.substr(last + 1, builtin_name.size() - last - 1).c_str());
}
-void BlenderSession::builtin_image_info(const string &builtin_name,
- void *builtin_data,
- ImageMetaData &metadata)
+bool BlenderImageLoader::load_metadata(ImageMetaData &metadata)
{
- /* empty image */
- metadata.width = 1;
- metadata.height = 1;
-
- if (!builtin_data)
- return;
-
- /* recover ID pointer */
- PointerRNA ptr;
- RNA_id_pointer_create((ID *)builtin_data, &ptr);
- BL::ID b_id(ptr);
-
- if (b_id.is_a(&RNA_Image)) {
- /* image data */
- BL::Image b_image(b_id);
+ metadata.width = b_image.size()[0];
+ metadata.height = b_image.size()[1];
+ metadata.depth = 1;
+ metadata.channels = b_image.channels();
- metadata.builtin_free_cache = !b_image.has_data();
- metadata.is_float = b_image.is_float();
- metadata.width = b_image.size()[0];
- metadata.height = b_image.size()[1];
- metadata.depth = 1;
- metadata.channels = b_image.channels();
-
- if (metadata.is_float) {
- /* Float images are already converted on the Blender side,
- * no need to do anything in Cycles. */
- metadata.colorspace = u_colorspace_raw;
+ if (b_image.is_float()) {
+ if (metadata.channels == 1) {
+ metadata.type = IMAGE_DATA_TYPE_FLOAT;
}
- }
- else if (b_id.is_a(&RNA_Object)) {
- /* smoke volume data */
- BL::Object b_ob(b_id);
- BL::FluidDomainSettings b_domain = object_fluid_gas_domain_find(b_ob);
-
- metadata.is_float = true;
- metadata.depth = 1;
- metadata.channels = 1;
-
- if (!b_domain)
- return;
-
- if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_DENSITY) ||
- builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_FLAME) ||
- builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_HEAT) ||
- builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_TEMPERATURE))
- metadata.channels = 1;
- else if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_COLOR))
- metadata.channels = 4;
- else if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY))
- metadata.channels = 3;
- else
- return;
-
- int3 resolution = get_int3(b_domain.domain_resolution());
- int amplify = (b_domain.use_noise()) ? b_domain.noise_scale() : 1;
-
- /* Velocity and heat data is always low-resolution. */
- if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY) ||
- builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_HEAT)) {
- amplify = 1;
+ else if (metadata.channels == 4) {
+ metadata.type = IMAGE_DATA_TYPE_FLOAT4;
}
-
- metadata.width = resolution.x * amplify;
- metadata.height = resolution.y * amplify;
- metadata.depth = resolution.z * amplify;
- }
- else {
- /* TODO(sergey): Check we're indeed in shader node tree. */
- PointerRNA ptr;
- RNA_pointer_create(NULL, &RNA_Node, builtin_data, &ptr);
- BL::Node b_node(ptr);
- if (b_node.is_a(&RNA_ShaderNodeTexPointDensity)) {
- BL::ShaderNodeTexPointDensity b_point_density_node(b_node);
- metadata.channels = 4;
- metadata.width = b_point_density_node.resolution();
- metadata.height = metadata.width;
- metadata.depth = metadata.width;
- metadata.is_float = true;
+ else {
+ return false;
}
- }
-}
-bool BlenderSession::builtin_image_pixels(const string &builtin_name,
- void *builtin_data,
- int tile,
- unsigned char *pixels,
- const size_t pixels_size,
- const bool associate_alpha,
- const bool free_cache)
-{
- if (!builtin_data) {
- return false;
- }
-
- const int frame = builtin_image_frame(builtin_name);
-
- PointerRNA ptr;
- RNA_id_pointer_create((ID *)builtin_data, &ptr);
- BL::Image b_image(ptr);
-
- const int width = b_image.size()[0];
- const int height = b_image.size()[1];
- const int channels = b_image.channels();
-
- unsigned char *image_pixels = image_get_pixels_for_frame(b_image, frame, tile);
- const size_t num_pixels = ((size_t)width) * height;
-
- if (image_pixels && num_pixels * channels == pixels_size) {
- memcpy(pixels, image_pixels, pixels_size * sizeof(unsigned char));
+ /* Float images are already converted on the Blender side,
+ * no need to do anything in Cycles. */
+ metadata.colorspace = u_colorspace_raw;
}
else {
- if (channels == 1) {
- memset(pixels, 0, pixels_size * sizeof(unsigned char));
+ if (metadata.channels == 1) {
+ metadata.type = IMAGE_DATA_TYPE_BYTE;
+ }
+ else if (metadata.channels == 4) {
+ metadata.type = IMAGE_DATA_TYPE_BYTE4;
}
else {
- const size_t num_pixels_safe = pixels_size / channels;
- unsigned char *cp = pixels;
- for (size_t i = 0; i < num_pixels_safe; i++, cp += channels) {
- cp[0] = 255;
- cp[1] = 0;
- cp[2] = 255;
- if (channels == 4) {
- cp[3] = 255;
- }
- }
+ return false;
}
}
- if (image_pixels) {
- MEM_freeN(image_pixels);
- }
-
- /* Free image buffers to save memory during render. */
- if (free_cache) {
- b_image.buffers_free();
- }
-
- if (associate_alpha) {
- /* Premultiply, byte images are always straight for Blender. */
- unsigned char *cp = pixels;
- for (size_t i = 0; i < num_pixels; i++, cp += channels) {
- cp[0] = (cp[0] * cp[3]) >> 8;
- cp[1] = (cp[1] * cp[3]) >> 8;
- cp[2] = (cp[2] * cp[3]) >> 8;
- }
- }
return true;
}
-bool BlenderSession::builtin_image_float_pixels(const string &builtin_name,
- void *builtin_data,
- int tile,
- float *pixels,
- const size_t pixels_size,
- const bool,
- const bool free_cache)
+bool BlenderImageLoader::load_pixels(const ImageMetaData &metadata,
+ void *pixels,
+ const size_t pixels_size,
+ const bool associate_alpha)
{
- if (!builtin_data) {
- return false;
- }
+ const size_t num_pixels = ((size_t)metadata.width) * metadata.height;
+ const int channels = metadata.channels;
+ const int tile = 0; /* TODO(lukas): Support tiles here? */
- PointerRNA ptr;
- RNA_id_pointer_create((ID *)builtin_data, &ptr);
- BL::ID b_id(ptr);
-
- if (b_id.is_a(&RNA_Image)) {
+ if (b_image.is_float()) {
/* image data */
- BL::Image b_image(b_id);
- int frame = builtin_image_frame(builtin_name);
-
- const int width = b_image.size()[0];
- const int height = b_image.size()[1];
- const int channels = b_image.channels();
-
float *image_pixels;
image_pixels = image_get_float_pixels_for_frame(b_image, frame, tile);
- const size_t num_pixels = ((size_t)width) * height;
if (image_pixels && num_pixels * channels == pixels_size) {
memcpy(pixels, image_pixels, pixels_size * sizeof(float));
@@ -224,7 +89,7 @@ bool BlenderSession::builtin_image_float_pixels(const string &builtin_name,
}
else {
const size_t num_pixels_safe = pixels_size / channels;
- float *fp = pixels;
+ float *fp = (float *)pixels;
for (int i = 0; i < num_pixels_safe; i++, fp += channels) {
fp[0] = 1.0f;
fp[1] = 0.0f;
@@ -239,107 +104,91 @@ bool BlenderSession::builtin_image_float_pixels(const string &builtin_name,
if (image_pixels) {
MEM_freeN(image_pixels);
}
-
- /* Free image buffers to save memory during render. */
- if (free_cache) {
- b_image.buffers_free();
- }
-
- return true;
}
- else if (b_id.is_a(&RNA_Object)) {
- /* smoke volume data */
- BL::Object b_ob(b_id);
- BL::FluidDomainSettings b_domain = object_fluid_gas_domain_find(b_ob);
-
- if (!b_domain) {
- return false;
- }
-#ifdef WITH_FLUID
- int3 resolution = get_int3(b_domain.domain_resolution());
- int length, amplify = (b_domain.use_noise()) ? b_domain.noise_scale() : 1;
-
- /* Velocity and heat data is always low-resolution. */
- if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY) ||
- builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_HEAT)) {
- amplify = 1;
- }
-
- const int width = resolution.x * amplify;
- const int height = resolution.y * amplify;
- const int depth = resolution.z * amplify;
- const size_t num_pixels = ((size_t)width) * height * depth;
+ else {
+ unsigned char *image_pixels = image_get_pixels_for_frame(b_image, frame, tile);
- if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_DENSITY)) {
- FluidDomainSettings_density_grid_get_length(&b_domain.ptr, &length);
- if (length == num_pixels) {
- FluidDomainSettings_density_grid_get(&b_domain.ptr, pixels);
- return true;
- }
- }
- else if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_FLAME)) {
- /* this is in range 0..1, and interpreted by the OpenGL smoke viewer
- * as 1500..3000 K with the first part faded to zero density */
- FluidDomainSettings_flame_grid_get_length(&b_domain.ptr, &length);
- if (length == num_pixels) {
- FluidDomainSettings_flame_grid_get(&b_domain.ptr, pixels);
- return true;
- }
+ if (image_pixels && num_pixels * channels == pixels_size) {
+ memcpy(pixels, image_pixels, pixels_size * sizeof(unsigned char));
}
- else if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_COLOR)) {
- /* the RGB is "premultiplied" by density for better interpolation results */
- FluidDomainSettings_color_grid_get_length(&b_domain.ptr, &length);
- if (length == num_pixels * 4) {
- FluidDomainSettings_color_grid_get(&b_domain.ptr, pixels);
- return true;
+ else {
+ if (channels == 1) {
+ memset(pixels, 0, pixels_size * sizeof(unsigned char));
}
- }
- else if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY)) {
- FluidDomainSettings_velocity_grid_get_length(&b_domain.ptr, &length);
- if (length == num_pixels * 3) {
- FluidDomainSettings_velocity_grid_get(&b_domain.ptr, pixels);
- return true;
+ else {
+ const size_t num_pixels_safe = pixels_size / channels;
+ unsigned char *cp = (unsigned char *)pixels;
+ for (size_t i = 0; i < num_pixels_safe; i++, cp += channels) {
+ cp[0] = 255;
+ cp[1] = 0;
+ cp[2] = 255;
+ if (channels == 4) {
+ cp[3] = 255;
+ }
+ }
}
}
- else if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_HEAT)) {
- FluidDomainSettings_heat_grid_get_length(&b_domain.ptr, &length);
- if (length == num_pixels) {
- FluidDomainSettings_heat_grid_get(&b_domain.ptr, pixels);
- return true;
- }
+
+ if (image_pixels) {
+ MEM_freeN(image_pixels);
}
- else if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_TEMPERATURE)) {
- FluidDomainSettings_temperature_grid_get_length(&b_domain.ptr, &length);
- if (length == num_pixels) {
- FluidDomainSettings_temperature_grid_get(&b_domain.ptr, pixels);
- return true;
+
+ if (associate_alpha) {
+ /* Premultiply, byte images are always straight for Blender. */
+ unsigned char *cp = (unsigned char *)pixels;
+ for (size_t i = 0; i < num_pixels; i++, cp += channels) {
+ cp[0] = (cp[0] * cp[3]) >> 8;
+ cp[1] = (cp[1] * cp[3]) >> 8;
+ cp[2] = (cp[2] * cp[3]) >> 8;
}
}
- else {
- fprintf(
- stderr, "Cycles error: unknown volume attribute %s, skipping\n", builtin_name.c_str());
- pixels[0] = 0.0f;
- return false;
- }
-#endif
- fprintf(stderr, "Cycles error: unexpected smoke volume resolution, skipping\n");
}
- else {
- /* We originally were passing view_layer here but in reality we need a
- * a depsgraph to pass to the RE_point_density_minmax() function.
- */
- /* TODO(sergey): Check we're indeed in shader node tree. */
- PointerRNA ptr;
- RNA_pointer_create(NULL, &RNA_Node, builtin_data, &ptr);
- BL::Node b_node(ptr);
- if (b_node.is_a(&RNA_ShaderNodeTexPointDensity)) {
- BL::ShaderNodeTexPointDensity b_point_density_node(b_node);
- int length;
- b_point_density_node.calc_point_density(b_depsgraph, &length, &pixels);
- }
+
+ /* Free image buffers to save memory during render. */
+ if (free_cache) {
+ b_image.buffers_free();
}
- return false;
+ return true;
+}
+
+string BlenderImageLoader::name() const
+{
+ return BL::Image(b_image).name();
+}
+
+bool BlenderImageLoader::equals(const ImageLoader &other) const
+{
+ const BlenderImageLoader &other_loader = (const BlenderImageLoader &)other;
+ return b_image == other_loader.b_image && frame == other_loader.frame;
+}
+
+/* Point Density */
+
+BlenderPointDensityLoader::BlenderPointDensityLoader(BL::Depsgraph b_depsgraph,
+ BL::ShaderNodeTexPointDensity b_node)
+ : b_depsgraph(b_depsgraph), b_node(b_node)
+{
+}
+
+bool BlenderPointDensityLoader::load_metadata(ImageMetaData &metadata)
+{
+ metadata.channels = 4;
+ metadata.width = b_node.resolution();
+ metadata.height = metadata.width;
+ metadata.depth = metadata.width;
+ metadata.type = IMAGE_DATA_TYPE_FLOAT4;
+ return true;
+}
+
+bool BlenderPointDensityLoader::load_pixels(const ImageMetaData &,
+ void *pixels,
+ const size_t,
+ const bool)
+{
+ int length;
+ b_node.calc_point_density(b_depsgraph, &length, (float **)&pixels);
+ return true;
}
void BlenderSession::builtin_images_load()
@@ -357,4 +206,15 @@ void BlenderSession::builtin_images_load()
manager->device_load_builtin(device, session->scene, session->progress);
}
+string BlenderPointDensityLoader::name() const
+{
+ return BL::ShaderNodeTexPointDensity(b_node).name();
+}
+
+bool BlenderPointDensityLoader::equals(const ImageLoader &other) const
+{
+ const BlenderPointDensityLoader &other_loader = (const BlenderPointDensityLoader &)other;
+ return b_node == other_loader.b_node && b_depsgraph == other_loader.b_depsgraph;
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/blender/blender_image.h b/intern/cycles/blender/blender_image.h
new file mode 100644
index 00000000000..b58a159a6ba
--- /dev/null
+++ b/intern/cycles/blender/blender_image.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2011-2020 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BLENDER_IMAGE_H__
+#define __BLENDER_IMAGE_H__
+
+#include "RNA_blender_cpp.h"
+
+#include "render/image.h"
+
+CCL_NAMESPACE_BEGIN
+
+class BlenderImageLoader : public ImageLoader {
+ public:
+ BlenderImageLoader(BL::Image b_image, int frame);
+
+ bool load_metadata(ImageMetaData &metadata) override;
+ bool load_pixels(const ImageMetaData &metadata,
+ void *pixels,
+ const size_t pixels_size,
+ const bool associate_alpha) override;
+ string name() const override;
+ bool equals(const ImageLoader &other) const override;
+
+ BL::Image b_image;
+ int frame;
+ bool free_cache;
+};
+
+class BlenderPointDensityLoader : public ImageLoader {
+ public:
+ BlenderPointDensityLoader(BL::Depsgraph depsgraph, BL::ShaderNodeTexPointDensity b_node);
+
+ bool load_metadata(ImageMetaData &metadata) override;
+ bool load_pixels(const ImageMetaData &metadata,
+ void *pixels,
+ const size_t pixels_size,
+ const bool associate_alpha) override;
+ string name() const override;
+ bool equals(const ImageLoader &other) const override;
+
+ BL::Depsgraph b_depsgraph;
+ BL::ShaderNodeTexPointDensity b_node;
+};
+
+CCL_NAMESPACE_END
+
+#endif /* __BLENDER_IMAGE_H__ */
diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp
index ac307743e48..8e01ad9a96d 100644
--- a/intern/cycles/blender/blender_session.cpp
+++ b/intern/cycles/blender/blender_session.cpp
@@ -138,14 +138,6 @@ void BlenderSession::create_session()
scene = new Scene(scene_params, session->device);
scene->name = b_scene.name();
- /* setup callbacks for builtin image support */
- scene->image_manager->builtin_image_info_cb = function_bind(
- &BlenderSession::builtin_image_info, this, _1, _2, _3);
- scene->image_manager->builtin_image_pixels_cb = function_bind(
- &BlenderSession::builtin_image_pixels, this, _1, _2, _3, _4, _5, _6, _7);
- scene->image_manager->builtin_image_float_pixels_cb = function_bind(
- &BlenderSession::builtin_image_float_pixels, this, _1, _2, _3, _4, _5, _6, _7);
-
session->scene = scene;
/* There is no single depsgraph to use for the entire render.
diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/blender_session.h
index a107d526dd6..3e6498bb655 100644
--- a/intern/cycles/blender/blender_session.h
+++ b/intern/cycles/blender/blender_session.h
@@ -157,22 +157,6 @@ class BlenderSession {
bool do_update_only);
void do_write_update_render_tile(RenderTile &rtile, bool do_update_only, bool highlight);
- int builtin_image_frame(const string &builtin_name);
- void builtin_image_info(const string &builtin_name, void *builtin_data, ImageMetaData &metadata);
- bool builtin_image_pixels(const string &builtin_name,
- void *builtin_data,
- int tile,
- unsigned char *pixels,
- const size_t pixels_size,
- const bool associate_alpha,
- const bool free_cache);
- bool builtin_image_float_pixels(const string &builtin_name,
- void *builtin_data,
- int tile,
- float *pixels,
- const size_t pixels_size,
- const bool associate_alpha,
- const bool free_cache);
void builtin_images_load();
/* Update tile manager to reflect resumable render settings. */
diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp
index 64367d5fcd0..dc226805664 100644
--- a/intern/cycles/blender/blender_shader.cpp
+++ b/intern/cycles/blender/blender_shader.cpp
@@ -23,6 +23,7 @@
#include "render/scene.h"
#include "render/shader.h"
+#include "blender/blender_image.h"
#include "blender/blender_texture.h"
#include "blender/blender_sync.h"
#include "blender/blender_util.h"
@@ -650,6 +651,18 @@ static ShaderNode *add_node(Scene *scene,
get_tex_mapping(&image->tex_mapping, b_texture_mapping);
if (b_image) {
+ PointerRNA colorspace_ptr = b_image.colorspace_settings().ptr;
+ image->colorspace = get_enum_identifier(colorspace_ptr, "name");
+
+ image->animated = b_image_node.image_user().use_auto_refresh();
+ image->alpha_type = get_image_alpha_type(b_image);
+
+ image->tiles.clear();
+ BL::Image::tiles_iterator b_iter;
+ for (b_image.tiles.begin(b_iter); b_iter != b_image.tiles.end(); ++b_iter) {
+ image->tiles.push_back(b_iter->number());
+ }
+
/* builtin images will use callback-based reading because
* they could only be loaded correct from blender side
*/
@@ -666,25 +679,12 @@ static ShaderNode *add_node(Scene *scene,
*/
int scene_frame = b_scene.frame_current();
int image_frame = image_user_frame_number(b_image_user, scene_frame);
- image->filename = b_image.name() + "@" + string_printf("%d", image_frame);
- image->builtin_data = b_image.ptr.data;
+ image->handle = scene->image_manager->add_image(
+ new BlenderImageLoader(b_image, image_frame), image->image_params());
}
else {
image->filename = image_user_file_path(
b_image_user, b_image, b_scene.frame_current(), true);
- image->builtin_data = NULL;
- }
-
- PointerRNA colorspace_ptr = b_image.colorspace_settings().ptr;
- image->colorspace = get_enum_identifier(colorspace_ptr, "name");
-
- image->animated = b_image_node.image_user().use_auto_refresh();
- image->alpha_type = get_image_alpha_type(b_image);
-
- image->tiles.clear();
- BL::Image::tiles_iterator b_iter;
- for (b_image.tiles.begin(b_iter); b_iter != b_image.tiles.end(); ++b_iter) {
- image->tiles.push_back(b_iter->number());
}
}
node = image;
@@ -701,6 +701,12 @@ static ShaderNode *add_node(Scene *scene,
get_tex_mapping(&env->tex_mapping, b_texture_mapping);
if (b_image) {
+ PointerRNA colorspace_ptr = b_image.colorspace_settings().ptr;
+ env->colorspace = get_enum_identifier(colorspace_ptr, "name");
+
+ env->animated = b_env_node.image_user().use_auto_refresh();
+ env->alpha_type = get_image_alpha_type(b_image);
+
bool is_builtin = b_image.packed_file() || b_image.source() == BL::Image::source_GENERATED ||
b_image.source() == BL::Image::source_MOVIE ||
(b_engine.is_preview() && b_image.source() != BL::Image::source_SEQUENCE);
@@ -708,20 +714,13 @@ static ShaderNode *add_node(Scene *scene,
if (is_builtin) {
int scene_frame = b_scene.frame_current();
int image_frame = image_user_frame_number(b_image_user, scene_frame);
- env->filename = b_image.name() + "@" + string_printf("%d", image_frame);
- env->builtin_data = b_image.ptr.data;
+ env->handle = scene->image_manager->add_image(new BlenderImageLoader(b_image, image_frame),
+ env->image_params());
}
else {
env->filename = image_user_file_path(
b_image_user, b_image, b_scene.frame_current(), false);
- env->builtin_data = NULL;
}
-
- PointerRNA colorspace_ptr = b_image.colorspace_settings().ptr;
- env->colorspace = get_enum_identifier(colorspace_ptr, "name");
-
- env->animated = b_env_node.image_user().use_auto_refresh();
- env->alpha_type = get_image_alpha_type(b_image);
}
node = env;
}
@@ -865,12 +864,12 @@ static ShaderNode *add_node(Scene *scene,
else if (b_node.is_a(&RNA_ShaderNodeTexPointDensity)) {
BL::ShaderNodeTexPointDensity b_point_density_node(b_node);
PointDensityTextureNode *point_density = new PointDensityTextureNode();
- point_density->filename = b_point_density_node.name();
point_density->space = (NodeTexVoxelSpace)b_point_density_node.space();
point_density->interpolation = get_image_interpolation(b_point_density_node);
- point_density->builtin_data = b_point_density_node.ptr.data;
+ point_density->handle = scene->image_manager->add_image(
+ new BlenderPointDensityLoader(b_depsgraph, b_point_density_node),
+ point_density->image_params());
- point_density->add_image(scene->image_manager);
b_point_density_node.cache_point_density(b_depsgraph);
node = point_density;
diff --git a/intern/cycles/blender/blender_volume.cpp b/intern/cycles/blender/blender_volume.cpp
index e11cc4ab18f..60ef4c5ed87 100644
--- a/intern/cycles/blender/blender_volume.cpp
+++ b/intern/cycles/blender/blender_volume.cpp
@@ -23,6 +23,152 @@
CCL_NAMESPACE_BEGIN
+/* TODO: verify this is not loading unnecessary attributes. */
+class BlenderSmokeLoader : public ImageLoader {
+ public:
+ BlenderSmokeLoader(const BL::Object &b_ob, AttributeStandard attribute)
+ : b_ob(b_ob), attribute(attribute)
+ {
+ }
+
+ bool load_metadata(ImageMetaData &metadata) override
+ {
+ BL::FluidDomainSettings b_domain = object_fluid_gas_domain_find(b_ob);
+
+ if (!b_domain) {
+ return false;
+ }
+
+ if (attribute == ATTR_STD_VOLUME_DENSITY || attribute == ATTR_STD_VOLUME_FLAME ||
+ attribute == ATTR_STD_VOLUME_HEAT || attribute == ATTR_STD_VOLUME_TEMPERATURE) {
+ metadata.type = IMAGE_DATA_TYPE_FLOAT;
+ metadata.channels = 1;
+ }
+ else if (attribute == ATTR_STD_VOLUME_COLOR) {
+ metadata.type = IMAGE_DATA_TYPE_FLOAT4;
+ metadata.channels = 4;
+ }
+ else if (attribute == ATTR_STD_VOLUME_VELOCITY) {
+ metadata.type = IMAGE_DATA_TYPE_FLOAT4;
+ metadata.channels = 3;
+ }
+ else {
+ return false;
+ }
+
+ int3 resolution = get_int3(b_domain.domain_resolution());
+ int amplify = (b_domain.use_noise()) ? b_domain.noise_scale() : 1;
+
+ /* Velocity and heat data is always low-resolution. */
+ if (attribute == ATTR_STD_VOLUME_VELOCITY || attribute == ATTR_STD_VOLUME_HEAT) {
+ amplify = 1;
+ }
+
+ metadata.width = resolution.x * amplify;
+ metadata.height = resolution.y * amplify;
+ metadata.depth = resolution.z * amplify;
+
+ return true;
+ }
+
+ bool load_pixels(const ImageMetaData &, void *pixels, const size_t, const bool) override
+ {
+ /* smoke volume data */
+ BL::FluidDomainSettings b_domain = object_fluid_gas_domain_find(b_ob);
+
+ if (!b_domain) {
+ return false;
+ }
+#ifdef WITH_FLUID
+ int3 resolution = get_int3(b_domain.domain_resolution());
+ int length, amplify = (b_domain.use_noise()) ? b_domain.noise_scale() : 1;
+
+ /* Velocity and heat data is always low-resolution. */
+ if (attribute == ATTR_STD_VOLUME_VELOCITY || attribute == ATTR_STD_VOLUME_HEAT) {
+ amplify = 1;
+ }
+
+ const int width = resolution.x * amplify;
+ const int height = resolution.y * amplify;
+ const int depth = resolution.z * amplify;
+ const size_t num_pixels = ((size_t)width) * height * depth;
+
+ float *fpixels = (float *)pixels;
+
+ if (attribute == ATTR_STD_VOLUME_DENSITY) {
+ FluidDomainSettings_density_grid_get_length(&b_domain.ptr, &length);
+ if (length == num_pixels) {
+ FluidDomainSettings_density_grid_get(&b_domain.ptr, fpixels);
+ return true;
+ }
+ }
+ else if (attribute == ATTR_STD_VOLUME_FLAME) {
+ /* this is in range 0..1, and interpreted by the OpenGL smoke viewer
+ * as 1500..3000 K with the first part faded to zero density */
+ FluidDomainSettings_flame_grid_get_length(&b_domain.ptr, &length);
+ if (length == num_pixels) {
+ FluidDomainSettings_flame_grid_get(&b_domain.ptr, fpixels);
+ return true;
+ }
+ }
+ else if (attribute == ATTR_STD_VOLUME_COLOR) {
+ /* the RGB is "premultiplied" by density for better interpolation results */
+ FluidDomainSettings_color_grid_get_length(&b_domain.ptr, &length);
+ if (length == num_pixels * 4) {
+ FluidDomainSettings_color_grid_get(&b_domain.ptr, fpixels);
+ return true;
+ }
+ }
+ else if (attribute == ATTR_STD_VOLUME_VELOCITY) {
+ FluidDomainSettings_velocity_grid_get_length(&b_domain.ptr, &length);
+ if (length == num_pixels * 3) {
+ FluidDomainSettings_velocity_grid_get(&b_domain.ptr, fpixels);
+ return true;
+ }
+ }
+ else if (attribute == ATTR_STD_VOLUME_HEAT) {
+ FluidDomainSettings_heat_grid_get_length(&b_domain.ptr, &length);
+ if (length == num_pixels) {
+ FluidDomainSettings_heat_grid_get(&b_domain.ptr, fpixels);
+ return true;
+ }
+ }
+ else if (attribute == ATTR_STD_VOLUME_TEMPERATURE) {
+ FluidDomainSettings_temperature_grid_get_length(&b_domain.ptr, &length);
+ if (length == num_pixels) {
+ FluidDomainSettings_temperature_grid_get(&b_domain.ptr, fpixels);
+ return true;
+ }
+ }
+ else {
+ fprintf(stderr,
+ "Cycles error: unknown volume attribute %s, skipping\n",
+ Attribute::standard_name(attribute));
+ fpixels[0] = 0.0f;
+ return false;
+ }
+#else
+ (void)pixels;
+#endif
+ fprintf(stderr, "Cycles error: unexpected smoke volume resolution, skipping\n");
+ return false;
+ }
+
+ string name() const override
+ {
+ return Attribute::standard_name(attribute);
+ }
+
+ bool equals(const ImageLoader &other) const override
+ {
+ const BlenderSmokeLoader &other_loader = (const BlenderSmokeLoader &)other;
+ return b_ob == other_loader.b_ob && attribute == other_loader.attribute;
+ }
+
+ BL::Object b_ob;
+ AttributeStandard attribute;
+};
+
static void sync_smoke_volume(Scene *scene, BL::Object &b_ob, Mesh *mesh, float frame)
{
BL::FluidDomainSettings b_domain = object_fluid_gas_domain_find(b_ob);
@@ -30,7 +176,6 @@ static void sync_smoke_volume(Scene *scene, BL::Object &b_ob, Mesh *mesh, float
return;
}
- ImageManager *image_manager = scene->image_manager;
AttributeStandard attributes[] = {ATTR_STD_VOLUME_DENSITY,
ATTR_STD_VOLUME_COLOR,
ATTR_STD_VOLUME_FLAME,
@@ -49,11 +194,11 @@ static void sync_smoke_volume(Scene *scene, BL::Object &b_ob, Mesh *mesh, float
Attribute *attr = mesh->attributes.add(std);
- ImageKey key;
- key.filename = Attribute::standard_name(std);
- key.builtin_data = b_ob.ptr.data;
+ ImageLoader *loader = new BlenderSmokeLoader(b_ob, std);
+ ImageParams params;
+ params.frame = frame;
- attr->add(image_manager->add_image(key, frame));
+ attr->data_voxel() = scene->image_manager->add_image(loader, params);
}
/* Create a matrix to transform from object space to mesh texture space.
diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt
index 6cde52bb89d..9922547a8d2 100644
--- a/intern/cycles/render/CMakeLists.txt
+++ b/intern/cycles/render/CMakeLists.txt
@@ -23,6 +23,7 @@ set(SRC
graph.cpp
hair.cpp
image.cpp
+ image_oiio.cpp
integrator.cpp
jitter.cpp
light.cpp
@@ -61,6 +62,7 @@ set(SRC_HEADERS
graph.h
hair.h
image.h
+ image_oiio.h
integrator.h
light.h
jitter.h
diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp
index fffd26f906a..d0d9888ac88 100644
--- a/intern/cycles/render/attribute.cpp
+++ b/intern/cycles/render/attribute.cpp
@@ -26,6 +26,25 @@ CCL_NAMESPACE_BEGIN
/* Attribute */
+Attribute::Attribute(
+ ustring name, TypeDesc type, AttributeElement element, Geometry *geom, AttributePrimitive prim)
+ : name(name), std(ATTR_STD_NONE), type(type), element(element), flags(0)
+{
+ /* string and matrix not supported! */
+ assert(type == TypeDesc::TypeFloat || type == TypeDesc::TypeColor ||
+ type == TypeDesc::TypePoint || type == TypeDesc::TypeVector ||
+ type == TypeDesc::TypeNormal || type == TypeDesc::TypeMatrix || type == TypeFloat2 ||
+ type == TypeRGBA);
+
+ if (element == ATTR_ELEMENT_VOXEL) {
+ buffer.resize(sizeof(ImageHandle));
+ new (buffer.data()) ImageHandle();
+ }
+ else {
+ resize(geom, prim, false);
+ }
+}
+
Attribute::~Attribute()
{
/* For voxel data, we need to free the image handle. */
@@ -35,34 +54,23 @@ Attribute::~Attribute()
}
}
-void Attribute::set(ustring name_, TypeDesc type_, AttributeElement element_)
-{
- name = name_;
- type = type_;
- element = element_;
- std = ATTR_STD_NONE;
- flags = 0;
-
- /* string and matrix not supported! */
- assert(type == TypeDesc::TypeFloat || type == TypeDesc::TypeColor ||
- type == TypeDesc::TypePoint || type == TypeDesc::TypeVector ||
- type == TypeDesc::TypeNormal || type == TypeDesc::TypeMatrix || type == TypeFloat2 ||
- type == TypeRGBA);
-}
-
void Attribute::resize(Geometry *geom, AttributePrimitive prim, bool reserve_only)
{
- if (reserve_only) {
- buffer.reserve(buffer_size(geom, prim));
- }
- else {
- buffer.resize(buffer_size(geom, prim), 0);
+ if (element != ATTR_ELEMENT_VOXEL) {
+ if (reserve_only) {
+ buffer.reserve(buffer_size(geom, prim));
+ }
+ else {
+ buffer.resize(buffer_size(geom, prim), 0);
+ }
}
}
void Attribute::resize(size_t num_elements)
{
- buffer.resize(num_elements * data_sizeof(), 0);
+ if (element != ATTR_ELEMENT_VOXEL) {
+ buffer.resize(num_elements * data_sizeof(), 0);
+ }
}
void Attribute::add(const float &f)
@@ -120,15 +128,6 @@ void Attribute::add(const Transform &f)
buffer.push_back(data[i]);
}
-void Attribute::add(const ImageHandle &handle)
-{
- assert(data_sizeof() == sizeof(ImageHandle));
- assert(buffer.size() == 0);
-
- buffer.resize(sizeof(ImageHandle));
- new (buffer.data()) ImageHandle(handle);
-}
-
void Attribute::add(const char *data)
{
size_t size = data_sizeof();
@@ -409,23 +408,9 @@ Attribute *AttributeSet::add(ustring name, TypeDesc type, AttributeElement eleme
remove(name);
}
-#if __cplusplus >= 201103L
- attributes.emplace_back();
- attr = &attributes.back();
- attr->set(name, type, element);
-#else
- {
- Attribute attr_temp;
- attr_temp.set(name, type, element);
- attributes.push_back(attr_temp);
- attr = &attributes.back();
- }
-#endif
-
- /* this is weak .. */
- attr->resize(geometry, prim, false);
-
- return attr;
+ Attribute new_attr(name, type, element, geometry, prim);
+ attributes.emplace_back(std::move(new_attr));
+ return &attributes.back();
}
Attribute *AttributeSet::find(ustring name) const
diff --git a/intern/cycles/render/attribute.h b/intern/cycles/render/attribute.h
index 23626410ac1..351357a7f7a 100644
--- a/intern/cycles/render/attribute.h
+++ b/intern/cycles/render/attribute.h
@@ -54,9 +54,11 @@ class Attribute {
AttributeElement element;
uint flags; /* enum AttributeFlag */
- Attribute()
- {
- }
+ Attribute(ustring name,
+ TypeDesc type,
+ AttributeElement element,
+ Geometry *geom,
+ AttributePrimitive prim);
~Attribute();
void set(ustring name, TypeDesc type, AttributeElement element);
void resize(Geometry *geom, AttributePrimitive prim, bool reserve_only);
@@ -151,7 +153,6 @@ class Attribute {
void add(const float3 &f);
void add(const uchar4 &f);
void add(const Transform &tfm);
- void add(const ImageHandle &handle);
void add(const char *data);
static bool same_storage(TypeDesc a, TypeDesc b);
diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp
index 123bb129466..b4539b5ce3c 100644
--- a/intern/cycles/render/image.cpp
+++ b/intern/cycles/render/image.cpp
@@ -15,12 +15,14 @@
*/
#include "render/image.h"
+#include "render/image_oiio.h"
#include "device/device.h"
#include "render/colorspace.h"
#include "render/scene.h"
#include "render/stats.h"
#include "util/util_foreach.h"
+#include "util/util_image.h"
#include "util/util_image_impl.h"
#include "util/util_logging.h"
#include "util/util_path.h"
@@ -85,10 +87,11 @@ ImageHandle::ImageHandle() : manager(NULL)
{
}
-ImageHandle::ImageHandle(const ImageHandle &other) : slots(other.slots), manager(other.manager)
+ImageHandle::ImageHandle(const ImageHandle &other)
+ : tile_slots(other.tile_slots), manager(other.manager)
{
/* Increase image user count. */
- foreach (const int slot, slots) {
+ foreach (const int slot, tile_slots) {
manager->add_image_user(slot);
}
}
@@ -97,9 +100,9 @@ ImageHandle &ImageHandle::operator=(const ImageHandle &other)
{
clear();
manager = other.manager;
- slots = other.slots;
+ tile_slots = other.tile_slots;
- foreach (const int slot, slots) {
+ foreach (const int slot, tile_slots) {
manager->add_image_user(slot);
}
@@ -113,275 +116,265 @@ ImageHandle::~ImageHandle()
void ImageHandle::clear()
{
- foreach (const int slot, slots) {
+ foreach (const int slot, tile_slots) {
manager->remove_image_user(slot);
}
}
bool ImageHandle::empty()
{
- return slots.empty();
+ return tile_slots.empty();
}
int ImageHandle::num_tiles()
{
- return slots.size();
+ return tile_slots.size();
}
ImageMetaData ImageHandle::metadata()
{
- if (slots.empty()) {
+ if (tile_slots.empty()) {
return ImageMetaData();
}
- return manager->images[slots.front()]->metadata;
+ ImageManager::Image *img = manager->images[tile_slots.front()];
+ manager->load_image_metadata(img);
+ return img->metadata;
}
-int ImageHandle::svm_slot(const int tile_index)
+int ImageHandle::svm_slot(const int tile_index) const
{
- if (tile_index >= slots.size()) {
+ if (tile_index >= tile_slots.size()) {
return -1;
}
if (manager->osl_texture_system) {
- ImageManager::Image *img = manager->images[slots[tile_index]];
- if (!img->key.builtin_data) {
+ ImageManager::Image *img = manager->images[tile_slots[tile_index]];
+ if (img->loader->osl_filepath()) {
return -1;
}
}
- return slots[tile_index];
+ return tile_slots[tile_index];
}
-device_memory *ImageHandle::image_memory(const int tile_index)
+device_memory *ImageHandle::image_memory(const int tile_index) const
{
- if (tile_index >= slots.size()) {
+ if (tile_index >= tile_slots.size()) {
return NULL;
}
- ImageManager::Image *img = manager->images[slots[tile_index]];
+ ImageManager::Image *img = manager->images[tile_slots[tile_index]];
return img ? img->mem : NULL;
}
-/* Image Manager */
-
-ImageManager::ImageManager(const DeviceInfo &info)
+bool ImageHandle::operator==(const ImageHandle &other) const
{
- need_update = true;
- osl_texture_system = NULL;
- animation_frame = 0;
-
- /* Set image limits */
- has_half_images = info.has_half_images;
+ return manager == other.manager && tile_slots == other.tile_slots;
}
-ImageManager::~ImageManager()
+/* Image MetaData */
+
+ImageMetaData::ImageMetaData()
+ : channels(0),
+ width(0),
+ height(0),
+ depth(0),
+ type(IMAGE_DATA_NUM_TYPES),
+ colorspace(u_colorspace_raw),
+ colorspace_file_format(""),
+ compress_as_srgb(false)
{
- for (size_t slot = 0; slot < images.size(); slot++)
- assert(!images[slot]);
}
-void ImageManager::set_osl_texture_system(void *texture_system)
+bool ImageMetaData::operator==(const ImageMetaData &other) const
{
- osl_texture_system = texture_system;
+ return channels == other.channels && width == other.width && height == other.height &&
+ depth == other.depth && type == other.type && colorspace == other.colorspace &&
+ compress_as_srgb == other.compress_as_srgb;
}
-bool ImageManager::set_animation_frame_update(int frame)
+bool ImageMetaData::is_float() const
{
- if (frame != animation_frame) {
- animation_frame = frame;
-
- for (size_t slot = 0; slot < images.size(); slot++) {
- if (images[slot] && images[slot]->key.animated)
- return true;
- }
- }
-
- return false;
+ return (type == IMAGE_DATA_TYPE_FLOAT || type == IMAGE_DATA_TYPE_FLOAT4 ||
+ type == IMAGE_DATA_TYPE_HALF || type == IMAGE_DATA_TYPE_HALF4);
}
-void ImageManager::metadata_detect_colorspace(ImageMetaData &metadata, const char *file_format)
+void ImageMetaData::detect_colorspace()
{
/* Convert used specified color spaces to one we know how to handle. */
- metadata.colorspace = ColorSpaceManager::detect_known_colorspace(
- metadata.colorspace, file_format, metadata.is_float || metadata.is_half);
+ colorspace = ColorSpaceManager::detect_known_colorspace(
+ colorspace, colorspace_file_format, is_float());
- if (metadata.colorspace == u_colorspace_raw) {
+ if (colorspace == u_colorspace_raw) {
/* Nothing to do. */
}
- else if (metadata.colorspace == u_colorspace_srgb) {
+ else if (colorspace == u_colorspace_srgb) {
/* Keep sRGB colorspace stored as sRGB, to save memory and/or loading time
* for the common case of 8bit sRGB images like PNG. */
- metadata.compress_as_srgb = true;
+ compress_as_srgb = true;
}
else {
/* Always compress non-raw 8bit images as scene linear + sRGB, as a
* heuristic to keep memory usage the same without too much data loss
* due to quantization in common cases. */
- metadata.compress_as_srgb = (metadata.type == IMAGE_DATA_TYPE_BYTE ||
- metadata.type == IMAGE_DATA_TYPE_BYTE4);
+ compress_as_srgb = (type == IMAGE_DATA_TYPE_BYTE || type == IMAGE_DATA_TYPE_BYTE4);
/* If colorspace conversion needed, use half instead of short so we can
* represent HDR values that might result from conversion. */
- if (metadata.type == IMAGE_DATA_TYPE_USHORT) {
- metadata.type = IMAGE_DATA_TYPE_HALF;
+ if (type == IMAGE_DATA_TYPE_USHORT) {
+ type = IMAGE_DATA_TYPE_HALF;
}
- else if (metadata.type == IMAGE_DATA_TYPE_USHORT4) {
- metadata.type = IMAGE_DATA_TYPE_HALF4;
+ else if (type == IMAGE_DATA_TYPE_USHORT4) {
+ type = IMAGE_DATA_TYPE_HALF4;
}
}
}
-bool ImageManager::load_image_metadata(const ImageKey &key, ImageMetaData &metadata)
-{
- metadata = ImageMetaData();
- metadata.colorspace = key.colorspace;
-
- if (key.builtin_data) {
- if (builtin_image_info_cb) {
- builtin_image_info_cb(key.filename, key.builtin_data, metadata);
- }
- else {
- return false;
- }
+/* Image Loader */
- if (metadata.is_float) {
- metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_FLOAT4 : IMAGE_DATA_TYPE_FLOAT;
- }
- else {
- metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_BYTE4 : IMAGE_DATA_TYPE_BYTE;
- }
+ImageLoader::ImageLoader()
+{
+}
- metadata_detect_colorspace(metadata, "");
+ustring ImageLoader::osl_filepath() const
+{
+ return ustring();
+}
+bool ImageLoader::equals(const ImageLoader *a, const ImageLoader *b)
+{
+ if (a == NULL && b == NULL) {
return true;
}
-
- /* Perform preliminary checks, with meaningful logging. */
- if (!path_exists(key.filename)) {
- VLOG(1) << "File '" << key.filename << "' does not exist.";
- return false;
- }
- if (path_is_directory(key.filename)) {
- VLOG(1) << "File '" << key.filename << "' is a directory, can't use as image.";
- return false;
+ else {
+ return (a && b && typeid(*a) == typeid(*b) && a->equals(*b));
}
+}
- unique_ptr<ImageInput> in(ImageInput::create(key.filename));
+/* Image Manager */
- if (!in) {
- return false;
- }
+ImageManager::ImageManager(const DeviceInfo &info)
+{
+ need_update = true;
+ osl_texture_system = NULL;
+ animation_frame = 0;
- ImageSpec spec;
- if (!in->open(key.filename, spec)) {
- return false;
- }
+ /* Set image limits */
+ has_half_images = info.has_half_images;
+}
- metadata.width = spec.width;
- metadata.height = spec.height;
- metadata.depth = spec.depth;
- metadata.compress_as_srgb = false;
+ImageManager::~ImageManager()
+{
+ for (size_t slot = 0; slot < images.size(); slot++)
+ assert(!images[slot]);
+}
- /* Check the main format, and channel formats. */
- size_t channel_size = spec.format.basesize();
+void ImageManager::set_osl_texture_system(void *texture_system)
+{
+ osl_texture_system = texture_system;
+}
- if (spec.format.is_floating_point()) {
- metadata.is_float = true;
- }
+bool ImageManager::set_animation_frame_update(int frame)
+{
+ if (frame != animation_frame) {
+ animation_frame = frame;
- for (size_t channel = 0; channel < spec.channelformats.size(); channel++) {
- channel_size = max(channel_size, spec.channelformats[channel].basesize());
- if (spec.channelformats[channel].is_floating_point()) {
- metadata.is_float = true;
+ for (size_t slot = 0; slot < images.size(); slot++) {
+ if (images[slot] && images[slot]->params.animated)
+ return true;
}
}
- /* check if it's half float */
- if (spec.format == TypeDesc::HALF) {
- metadata.is_half = true;
- }
-
- /* set type and channels */
- metadata.channels = spec.nchannels;
+ return false;
+}
- if (metadata.is_half) {
- metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_HALF4 : IMAGE_DATA_TYPE_HALF;
- }
- else if (metadata.is_float) {
- metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_FLOAT4 : IMAGE_DATA_TYPE_FLOAT;
- }
- else if (spec.format == TypeDesc::USHORT) {
- metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_USHORT4 : IMAGE_DATA_TYPE_USHORT;
+void ImageManager::load_image_metadata(Image *img)
+{
+ if (!img->need_metadata) {
+ return;
}
- else {
- metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_BYTE4 : IMAGE_DATA_TYPE_BYTE;
+
+ thread_scoped_lock image_lock(img->mutex);
+ if (!img->need_metadata) {
+ return;
}
- metadata_detect_colorspace(metadata, in->format_name());
+ ImageMetaData &metadata = img->metadata;
+ metadata = ImageMetaData();
+ metadata.colorspace = img->params.colorspace;
- in->close();
+ img->loader->load_metadata(metadata);
- return true;
+ metadata.detect_colorspace();
+
+ /* No half textures on OpenCL, use full float instead. */
+ if (!has_half_images) {
+ if (metadata.type == IMAGE_DATA_TYPE_HALF4) {
+ metadata.type = IMAGE_DATA_TYPE_FLOAT4;
+ }
+ else if (metadata.type == IMAGE_DATA_TYPE_HALF) {
+ metadata.type = IMAGE_DATA_TYPE_FLOAT;
+ }
+ }
+
+ img->need_metadata = false;
}
-ImageHandle ImageManager::add_image(const ImageKey &key, float frame)
+ImageHandle ImageManager::add_image(const string &filename, const ImageParams &params)
{
+ const int slot = add_image_slot(new OIIOImageLoader(filename), params, false);
+
ImageHandle handle;
- handle.slots.push_back(add_image_slot(key, frame));
+ handle.tile_slots.push_back(slot);
handle.manager = this;
return handle;
}
-ImageHandle ImageManager::add_image(const ImageKey &key, float frame, const vector<int> &tiles)
+ImageHandle ImageManager::add_image(const string &filename,
+ const ImageParams &params,
+ const vector<int> &tiles)
{
ImageHandle handle;
handle.manager = this;
foreach (int tile, tiles) {
- ImageKey tile_key = key;
+ string tile_filename = filename;
if (tile != 0) {
- string_replace(tile_key.filename, "<UDIM>", string_printf("%04d", tile));
+ string_replace(tile_filename, "<UDIM>", string_printf("%04d", tile));
}
- handle.slots.push_back(add_image_slot(tile_key, frame));
+ const int slot = add_image_slot(new OIIOImageLoader(tile_filename), params, false);
+ handle.tile_slots.push_back(slot);
}
return handle;
}
-int ImageManager::add_image_slot(const ImageKey &key, float frame)
+ImageHandle ImageManager::add_image(ImageLoader *loader, const ImageParams &params)
+{
+ const int slot = add_image_slot(loader, params, true);
+
+ ImageHandle handle;
+ handle.tile_slots.push_back(slot);
+ handle.manager = this;
+ return handle;
+}
+
+int ImageManager::add_image_slot(ImageLoader *loader,
+ const ImageParams &params,
+ const bool builtin)
{
Image *img;
size_t slot;
- ImageMetaData metadata;
- load_image_metadata(key, metadata);
-
thread_scoped_lock device_lock(device_mutex);
- /* No half textures on OpenCL, use full float instead. */
- if (!has_half_images) {
- if (metadata.type == IMAGE_DATA_TYPE_HALF4) {
- metadata.type = IMAGE_DATA_TYPE_FLOAT4;
- }
- else if (metadata.type == IMAGE_DATA_TYPE_HALF) {
- metadata.type = IMAGE_DATA_TYPE_FLOAT;
- }
- }
-
/* Fnd existing image. */
for (slot = 0; slot < images.size(); slot++) {
img = images[slot];
- if (img && img->key == key) {
- if (img->frame != frame) {
- img->frame = frame;
- img->need_load = true;
- }
- if (!(img->metadata == metadata)) {
- img->metadata = metadata;
- img->need_load = true;
- }
+ if (img && ImageLoader::equals(img->loader, loader) && img->params == params) {
img->users++;
+ delete loader;
return slot;
}
}
@@ -398,10 +391,11 @@ int ImageManager::add_image_slot(const ImageKey &key, float frame)
/* Add new image. */
img = new Image();
- img->key = key;
- img->frame = frame;
- img->metadata = metadata;
- img->need_load = true;
+ img->params = params;
+ img->loader = loader;
+ img->need_metadata = true;
+ img->need_load = !(osl_texture_system && img->loader->osl_filepath());
+ img->builtin = builtin;
img->users = 1;
img->mem = NULL;
@@ -439,54 +433,9 @@ static bool image_associate_alpha(ImageManager::Image *img)
{
/* For typical RGBA images we let OIIO convert to associated alpha,
* but some types we want to leave the RGB channels untouched. */
- return !(ColorSpaceManager::colorspace_is_data(img->key.colorspace) ||
- img->key.alpha_type == IMAGE_ALPHA_IGNORE ||
- img->key.alpha_type == IMAGE_ALPHA_CHANNEL_PACKED);
-}
-
-bool ImageManager::file_load_image_generic(Image *img, unique_ptr<ImageInput> *in)
-{
- if (img->key.filename == "")
- return false;
-
- if (!img->key.builtin_data) {
- /* NOTE: Error logging is done in meta data acquisition. */
- if (!path_exists(img->key.filename) || path_is_directory(img->key.filename)) {
- return false;
- }
-
- /* load image from file through OIIO */
- *in = unique_ptr<ImageInput>(ImageInput::create(img->key.filename));
-
- if (!*in)
- return false;
-
- ImageSpec spec = ImageSpec();
- ImageSpec config = ImageSpec();
-
- if (!image_associate_alpha(img)) {
- config.attribute("oiio:UnassociatedAlpha", 1);
- }
-
- if (!(*in)->open(img->key.filename, spec, config)) {
- return false;
- }
- }
- else {
- /* load image using builtin images callbacks */
- if (!builtin_image_info_cb || !builtin_image_pixels_cb)
- return false;
- }
-
- /* we only handle certain number of components */
- if (!(img->metadata.channels >= 1 && img->metadata.channels <= 4)) {
- if (*in) {
- (*in)->close();
- }
- return false;
- }
-
- return true;
+ return !(ColorSpaceManager::colorspace_is_data(img->params.colorspace) ||
+ img->params.alpha_type == IMAGE_ALPHA_IGNORE ||
+ img->params.alpha_type == IMAGE_ALPHA_CHANNEL_PACKED);
}
template<TypeDesc::BASETYPE FileFormat, typename StorageType, typename DeviceType>
@@ -494,8 +443,8 @@ bool ImageManager::file_load_image(Image *img,
int texture_limit,
device_vector<DeviceType> &tex_img)
{
- unique_ptr<ImageInput> in = NULL;
- if (!file_load_image_generic(img, &in)) {
+ /* we only handle certain number of components */
+ if (!(img->metadata.channels >= 1 && img->metadata.channels <= 4)) {
return false;
}
@@ -529,67 +478,9 @@ bool ImageManager::file_load_image(Image *img,
return false;
}
- bool cmyk = false;
const size_t num_pixels = ((size_t)width) * height * depth;
- if (in) {
- /* Read pixels through OpenImageIO. */
- StorageType *readpixels = pixels;
- vector<StorageType> tmppixels;
- if (components > 4) {
- tmppixels.resize(((size_t)width) * height * components);
- readpixels = &tmppixels[0];
- }
-
- if (depth <= 1) {
- size_t scanlinesize = ((size_t)width) * components * sizeof(StorageType);
- in->read_image(FileFormat,
- (uchar *)readpixels + (height - 1) * scanlinesize,
- AutoStride,
- -scanlinesize,
- AutoStride);
- }
- else {
- in->read_image(FileFormat, (uchar *)readpixels);
- }
-
- if (components > 4) {
- size_t dimensions = ((size_t)width) * height;
- for (size_t i = dimensions - 1, pixel = 0; pixel < dimensions; pixel++, i--) {
- pixels[i * 4 + 3] = tmppixels[i * components + 3];
- pixels[i * 4 + 2] = tmppixels[i * components + 2];
- pixels[i * 4 + 1] = tmppixels[i * components + 1];
- pixels[i * 4 + 0] = tmppixels[i * components + 0];
- }
- tmppixels.clear();
- }
-
- cmyk = strcmp(in->format_name(), "jpeg") == 0 && components == 4;
- in->close();
- }
- else {
- /* Read pixels through callback. */
- if (FileFormat == TypeDesc::FLOAT) {
- builtin_image_float_pixels_cb(img->key.filename,
- img->key.builtin_data,
- 0, /* TODO(lukas): Support tiles here? */
- (float *)&pixels[0],
- num_pixels * components,
- image_associate_alpha(img),
- img->metadata.builtin_free_cache);
- }
- else if (FileFormat == TypeDesc::UINT8) {
- builtin_image_pixels_cb(img->key.filename,
- img->key.builtin_data,
- 0, /* TODO(lukas): Support tiles here? */
- (uchar *)&pixels[0],
- num_pixels * components,
- image_associate_alpha(img),
- img->metadata.builtin_free_cache);
- }
- else {
- /* TODO(dingto): Support half for ImBuf. */
- }
- }
+ img->loader->load_pixels(
+ img->metadata, pixels, num_pixels * components, image_associate_alpha(img));
/* The kernel can handle 1 and 4 channel images. Anything that is not a single
* channel image is converted to RGBA format. */
@@ -601,20 +492,7 @@ bool ImageManager::file_load_image(Image *img,
if (is_rgba) {
const StorageType one = util_image_cast_from_float<StorageType>(1.0f);
- if (cmyk) {
- /* CMYK to RGBA. */
- for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) {
- float c = util_image_cast_to_float(pixels[i * 4 + 0]);
- float m = util_image_cast_to_float(pixels[i * 4 + 1]);
- float y = util_image_cast_to_float(pixels[i * 4 + 2]);
- float k = util_image_cast_to_float(pixels[i * 4 + 3]);
- pixels[i * 4 + 0] = util_image_cast_from_float<StorageType>((1.0f - c) * (1.0f - k));
- pixels[i * 4 + 1] = util_image_cast_from_float<StorageType>((1.0f - m) * (1.0f - k));
- pixels[i * 4 + 2] = util_image_cast_from_float<StorageType>((1.0f - y) * (1.0f - k));
- pixels[i * 4 + 3] = one;
- }
- }
- else if (components == 2) {
+ if (components == 2) {
/* Grayscale + alpha to RGBA. */
for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) {
pixels[i * 4 + 3] = pixels[i * 2 + 1];
@@ -643,7 +521,7 @@ bool ImageManager::file_load_image(Image *img,
}
/* Disable alpha if requested by the user. */
- if (img->key.alpha_type == IMAGE_ALPHA_IGNORE) {
+ if (img->params.alpha_type == IMAGE_ALPHA_IGNORE) {
for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) {
pixels[i * 4 + 3] = one;
}
@@ -690,7 +568,8 @@ bool ImageManager::file_load_image(Image *img,
while (max_size * scale_factor > texture_limit) {
scale_factor *= 0.5f;
}
- VLOG(1) << "Scaling image " << img->key.filename << " by a factor of " << scale_factor << ".";
+ VLOG(1) << "Scaling image " << img->loader->name() << " by a factor of " << scale_factor
+ << ".";
vector<StorageType> scaled_pixels;
size_t scaled_width, scaled_height, scaled_depth;
util_image_resize_pixels(pixels_storage,
@@ -721,25 +600,23 @@ static void image_set_device_memory(ImageManager::Image *img, device_memory *mem
{
img->mem = mem;
mem->image_data_type = img->metadata.type;
- mem->interpolation = img->key.interpolation;
- mem->extension = img->key.extension;
+ mem->interpolation = img->params.interpolation;
+ mem->extension = img->params.extension;
}
void ImageManager::device_load_image(Device *device, Scene *scene, int slot, Progress *progress)
{
- if (progress->get_cancel())
+ if (progress->get_cancel()) {
return;
+ }
Image *img = images[slot];
- if (osl_texture_system && !img->key.builtin_data)
- return;
-
- string filename = path_filename(images[slot]->key.filename);
- progress->set_status("Updating Images", "Loading " + filename);
+ progress->set_status("Updating Images", "Loading " + img->loader->name());
const int texture_limit = scene->params.texture_limit;
+ load_image_metadata(img);
ImageDataType type = img->metadata.type;
/* Slot assignment */
@@ -901,29 +778,36 @@ void ImageManager::device_load_image(Device *device, Scene *scene, int slot, Pro
thread_scoped_lock device_lock(device_mutex);
tex_img->copy_to_device();
}
+
+ /* Cleanup memory in image loader. */
+ img->loader->cleanup();
img->need_load = false;
}
void ImageManager::device_free_image(Device *, int slot)
{
Image *img = images[slot];
+ if (img == NULL) {
+ return;
+ }
- if (img) {
- if (osl_texture_system && !img->key.builtin_data) {
+ if (osl_texture_system) {
#ifdef WITH_OSL
- ustring filename(images[slot]->key.filename);
- ((OSL::TextureSystem *)osl_texture_system)->invalidate(filename);
-#endif
- }
-
- if (img->mem) {
- thread_scoped_lock device_lock(device_mutex);
- delete img->mem;
+ ustring filepath = img->loader->osl_filepath();
+ if (filepath) {
+ ((OSL::TextureSystem *)osl_texture_system)->invalidate(filepath);
}
+#endif
+ }
- delete img;
- images[slot] = NULL;
+ if (img->mem) {
+ thread_scoped_lock device_lock(device_mutex);
+ delete img->mem;
}
+
+ delete img->loader;
+ delete img;
+ images[slot] = NULL;
}
void ImageManager::device_update(Device *device, Scene *scene, Progress &progress)
@@ -934,20 +818,13 @@ void ImageManager::device_update(Device *device, Scene *scene, Progress &progres
TaskPool pool;
for (size_t slot = 0; slot < images.size(); slot++) {
- if (!images[slot])
- continue;
-
- if (images[slot]->users == 0) {
+ Image *img = images[slot];
+ if (img && img->users == 0) {
device_free_image(device, slot);
}
- else if (images[slot]->need_load) {
- if (osl_texture_system && !images[slot]->key.builtin_data) {
- images[slot]->need_load = false;
- }
- else {
- pool.push(
- function_bind(&ImageManager::device_load_image, this, device, scene, slot, &progress));
- }
+ else if (img && img->need_load) {
+ pool.push(
+ function_bind(&ImageManager::device_load_image, this, device, scene, slot, &progress));
}
}
@@ -958,19 +835,14 @@ void ImageManager::device_update(Device *device, Scene *scene, Progress &progres
void ImageManager::device_update_slot(Device *device, Scene *scene, int slot, Progress *progress)
{
- Image *image = images[slot];
- assert(image != NULL);
+ Image *img = images[slot];
+ assert(img != NULL);
- if (image->users == 0) {
+ if (img->users == 0) {
device_free_image(device, slot);
}
- else if (image->need_load) {
- if (osl_texture_system && !image->key.builtin_data) {
- images[slot]->need_load = false;
- }
- else {
- device_load_image(device, scene, slot, progress);
- }
+ else if (img->need_load) {
+ device_load_image(device, scene, slot, progress);
}
}
@@ -984,14 +856,10 @@ void ImageManager::device_load_builtin(Device *device, Scene *scene, Progress &p
TaskPool pool;
for (size_t slot = 0; slot < images.size(); slot++) {
- if (!images[slot])
- continue;
-
- if (images[slot]->need_load) {
- if (images[slot]->key.builtin_data) {
- pool.push(
- function_bind(&ImageManager::device_load_image, this, device, scene, slot, &progress));
- }
+ Image *img = images[slot];
+ if (img && img->need_load && img->builtin) {
+ pool.push(
+ function_bind(&ImageManager::device_load_image, this, device, scene, slot, &progress));
}
}
@@ -1001,8 +869,10 @@ void ImageManager::device_load_builtin(Device *device, Scene *scene, Progress &p
void ImageManager::device_free_builtin(Device *device)
{
for (size_t slot = 0; slot < images.size(); slot++) {
- if (images[slot] && images[slot]->key.builtin_data)
+ Image *img = images[slot];
+ if (img && img->builtin) {
device_free_image(device, slot);
+ }
}
}
@@ -1018,7 +888,7 @@ void ImageManager::collect_statistics(RenderStats *stats)
{
foreach (const Image *image, images) {
stats->image.textures.add_entry(
- NamedSizeEntry(path_filename(image->key.filename), image->mem->memory_size()));
+ NamedSizeEntry(image->loader->name(), image->mem->memory_size()));
}
}
diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h
index b23bb7bed63..35e6d4dcb08 100644
--- a/intern/cycles/render/image.h
+++ b/intern/cycles/render/image.h
@@ -22,7 +22,6 @@
#include "render/colorspace.h"
-#include "util/util_image.h"
#include "util/util_string.h"
#include "util/util_thread.h"
#include "util/util_unique_ptr.h"
@@ -40,78 +39,89 @@ class RenderStats;
class Scene;
class ColorSpaceProcessor;
-/* Image MetaData
- *
- * Information about the image that is available before the image pxeisl are loaded. */
-class ImageMetaData {
+/* Image Parameters */
+class ImageParams {
public:
- /* Must be set by image file or builtin callback. */
- bool is_float, is_half;
- int channels;
- size_t width, height, depth;
- bool builtin_free_cache;
-
- /* Automatically set. */
- ImageDataType type;
- ustring colorspace;
- bool compress_as_srgb;
-
- ImageMetaData()
- : is_float(false),
- is_half(false),
- channels(0),
- width(0),
- height(0),
- depth(0),
- builtin_free_cache(false),
- type(IMAGE_DATA_NUM_TYPES),
- colorspace(u_colorspace_raw),
- compress_as_srgb(false)
- {
- }
-
- bool operator==(const ImageMetaData &other) const
- {
- return is_float == other.is_float && is_half == other.is_half && channels == other.channels &&
- width == other.width && height == other.height && depth == other.depth &&
- type == other.type && colorspace == other.colorspace &&
- compress_as_srgb == other.compress_as_srgb;
- }
-};
-
-/* Image Key
- *
- * Image description that uniquely identifies and images. When adding images
- * with the same key, they will be internally deduplicated. */
-class ImageKey {
- public:
- string filename;
- void *builtin_data;
bool animated;
InterpolationType interpolation;
ExtensionType extension;
ImageAlphaType alpha_type;
ustring colorspace;
+ float frame;
- ImageKey()
- : builtin_data(NULL),
- animated(false),
+ ImageParams()
+ : animated(false),
interpolation(INTERPOLATION_LINEAR),
extension(EXTENSION_CLIP),
alpha_type(IMAGE_ALPHA_AUTO),
- colorspace(u_colorspace_raw)
+ colorspace(u_colorspace_raw),
+ frame(0.0f)
{
}
- bool operator==(const ImageKey &other) const
+ bool operator==(const ImageParams &other) const
{
- return (filename == other.filename && builtin_data == other.builtin_data &&
- animated == other.animated && interpolation == other.interpolation &&
+ return (animated == other.animated && interpolation == other.interpolation &&
extension == other.extension && alpha_type == other.alpha_type &&
- colorspace == other.colorspace);
+ colorspace == other.colorspace && frame == other.frame);
}
};
+/* Image MetaData
+ *
+ * Information about the image that is available before the image pxeisl are loaded. */
+class ImageMetaData {
+ public:
+ /* Set by ImageLoader.load_metadata(). */
+ int channels;
+ size_t width, height, depth;
+ ImageDataType type;
+
+ /* Optional color space, defaults to raw. */
+ ustring colorspace;
+ const char *colorspace_file_format;
+
+ /* Automatically set. */
+ bool compress_as_srgb;
+
+ ImageMetaData();
+ bool operator==(const ImageMetaData &other) const;
+ bool is_float() const;
+ void detect_colorspace();
+};
+
+/* Image loader base class, that can be subclassed to load image data
+ * from custom sources (file, memory, procedurally generated, etc). */
+class ImageLoader {
+ public:
+ ImageLoader();
+ virtual ~ImageLoader(){};
+
+ /* Load metadata without actual image yet, should be fast. */
+ virtual bool load_metadata(ImageMetaData &metadata) = 0;
+
+ /* Load actual image contents. */
+ virtual bool load_pixels(const ImageMetaData &metadata,
+ void *pixels,
+ const size_t pixels_size,
+ const bool associate_alpha) = 0;
+
+ /* Name for logs and stats. */
+ virtual string name() const = 0;
+
+ /* Optional for OSL texture cache. */
+ virtual ustring osl_filepath() const;
+
+ /* Free any memory used for loading metadata and pixels. */
+ virtual void cleanup(){};
+
+ /* Compare avoid loading the same image multiple times. */
+ virtual bool equals(const ImageLoader &other) const = 0;
+ static bool equals(const ImageLoader *a, const ImageLoader *b);
+
+ /* Work around for no RTTI. */
+};
+
/* Image Handle
*
* Access handle for image in the image manager. Multiple shader nodes may
@@ -123,17 +133,19 @@ class ImageHandle {
ImageHandle &operator=(const ImageHandle &other);
~ImageHandle();
+ bool operator==(const ImageHandle &other) const;
+
void clear();
bool empty();
int num_tiles();
ImageMetaData metadata();
- int svm_slot(const int tile_index = 0);
- device_memory *image_memory(const int tile_index = 0);
+ int svm_slot(const int tile_index = 0) const;
+ device_memory *image_memory(const int tile_index = 0) const;
protected:
- vector<int> slots;
+ vector<int> tile_slots;
ImageManager *manager;
friend class ImageManager;
@@ -148,8 +160,11 @@ class ImageManager {
explicit ImageManager(const DeviceInfo &info);
~ImageManager();
- ImageHandle add_image(const ImageKey &key, float frame);
- ImageHandle add_image(const ImageKey &key, float frame, const vector<int> &tiles);
+ ImageHandle add_image(const string &filename, const ImageParams &params);
+ ImageHandle add_image(const string &filename,
+ const ImageParams &params,
+ const vector<int> &tiles);
+ ImageHandle add_image(ImageLoader *loader, const ImageParams &params);
void device_update(Device *device, Scene *scene, Progress &progress);
void device_update_slot(Device *device, Scene *scene, int slot, Progress *progress);
@@ -165,40 +180,21 @@ class ImageManager {
bool need_update;
- /* NOTE: Here pixels_size is a size of storage, which equals to
- * width * height * depth.
- * Use this to avoid some nasty memory corruptions.
- */
- function<void(const string &filename, void *data, ImageMetaData &metadata)>
- builtin_image_info_cb;
- function<bool(const string &filename,
- void *data,
- int tile,
- unsigned char *pixels,
- const size_t pixels_size,
- const bool associate_alpha,
- const bool free_cache)>
- builtin_image_pixels_cb;
- function<bool(const string &filename,
- void *data,
- int tile,
- float *pixels,
- const size_t pixels_size,
- const bool associate_alpha,
- const bool free_cache)>
- builtin_image_float_pixels_cb;
-
struct Image {
- ImageKey key;
+ ImageParams params;
ImageMetaData metadata;
+ ImageLoader *loader;
float frame;
+ bool need_metadata;
bool need_load;
+ bool builtin;
string mem_name;
device_memory *mem;
int users;
+ thread_mutex mutex;
};
private:
@@ -210,19 +206,15 @@ class ImageManager {
vector<Image *> images;
void *osl_texture_system;
- int add_image_slot(const ImageKey &key, float frame);
+ int add_image_slot(ImageLoader *loader, const ImageParams &params, const bool builtin);
void add_image_user(int slot);
void remove_image_user(int slot);
- bool load_image_metadata(const ImageKey &key, ImageMetaData &metadata);
-
- bool file_load_image_generic(Image *img, unique_ptr<ImageInput> *in);
+ void load_image_metadata(Image *img);
template<TypeDesc::BASETYPE FileFormat, typename StorageType, typename DeviceType>
bool file_load_image(Image *img, int texture_limit, device_vector<DeviceType> &tex_img);
- void metadata_detect_colorspace(ImageMetaData &metadata, const char *file_format);
-
void device_load_image(Device *device, Scene *scene, int slot, Progress *progress);
void device_free_image(Device *device, int slot);
diff --git a/intern/cycles/render/image_oiio.cpp b/intern/cycles/render/image_oiio.cpp
new file mode 100644
index 00000000000..c4f95c6b4bc
--- /dev/null
+++ b/intern/cycles/render/image_oiio.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2011-2020 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "render/image_oiio.h"
+
+#include "util/util_image.h"
+#include "util/util_logging.h"
+#include "util/util_path.h"
+
+CCL_NAMESPACE_BEGIN
+
+OIIOImageLoader::OIIOImageLoader(const string &filepath) : filepath(filepath)
+{
+}
+
+OIIOImageLoader::~OIIOImageLoader()
+{
+}
+
+bool OIIOImageLoader::load_metadata(ImageMetaData &metadata)
+{
+ /* Perform preliminary checks, with meaningful logging. */
+ if (!path_exists(filepath.string())) {
+ VLOG(1) << "File '" << filepath.string() << "' does not exist.";
+ return false;
+ }
+ if (path_is_directory(filepath.string())) {
+ VLOG(1) << "File '" << filepath.string() << "' is a directory, can't use as image.";
+ return false;
+ }
+
+ unique_ptr<ImageInput> in(ImageInput::create(filepath.string()));
+
+ if (!in) {
+ return false;
+ }
+
+ ImageSpec spec;
+ if (!in->open(filepath.string(), spec)) {
+ return false;
+ }
+
+ metadata.width = spec.width;
+ metadata.height = spec.height;
+ metadata.depth = spec.depth;
+ metadata.compress_as_srgb = false;
+
+ /* Check the main format, and channel formats. */
+ size_t channel_size = spec.format.basesize();
+
+ bool is_float = false;
+ bool is_half = false;
+
+ if (spec.format.is_floating_point()) {
+ is_float = true;
+ }
+
+ for (size_t channel = 0; channel < spec.channelformats.size(); channel++) {
+ channel_size = max(channel_size, spec.channelformats[channel].basesize());
+ if (spec.channelformats[channel].is_floating_point()) {
+ is_float = true;
+ }
+ }
+
+ /* check if it's half float */
+ if (spec.format == TypeDesc::HALF) {
+ is_half = true;
+ }
+
+ /* set type and channels */
+ metadata.channels = spec.nchannels;
+
+ if (is_half) {
+ metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_HALF4 : IMAGE_DATA_TYPE_HALF;
+ }
+ else if (is_float) {
+ metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_FLOAT4 : IMAGE_DATA_TYPE_FLOAT;
+ }
+ else if (spec.format == TypeDesc::USHORT) {
+ metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_USHORT4 : IMAGE_DATA_TYPE_USHORT;
+ }
+ else {
+ metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_BYTE4 : IMAGE_DATA_TYPE_BYTE;
+ }
+
+ metadata.colorspace_file_format = in->format_name();
+
+ in->close();
+
+ return true;
+}
+
+template<TypeDesc::BASETYPE FileFormat, typename StorageType>
+static void oiio_load_pixels(const ImageMetaData &metadata,
+ const unique_ptr<ImageInput> &in,
+ StorageType *pixels)
+{
+ const int width = metadata.width;
+ const int height = metadata.height;
+ const int depth = metadata.depth;
+ const int components = metadata.channels;
+
+ /* Read pixels through OpenImageIO. */
+ StorageType *readpixels = pixels;
+ vector<StorageType> tmppixels;
+ if (components > 4) {
+ tmppixels.resize(((size_t)width) * height * components);
+ readpixels = &tmppixels[0];
+ }
+
+ if (depth <= 1) {
+ size_t scanlinesize = ((size_t)width) * components * sizeof(StorageType);
+ in->read_image(FileFormat,
+ (uchar *)readpixels + (height - 1) * scanlinesize,
+ AutoStride,
+ -scanlinesize,
+ AutoStride);
+ }
+ else {
+ in->read_image(FileFormat, (uchar *)readpixels);
+ }
+
+ if (components > 4) {
+ size_t dimensions = ((size_t)width) * height;
+ for (size_t i = dimensions - 1, pixel = 0; pixel < dimensions; pixel++, i--) {
+ pixels[i * 4 + 3] = tmppixels[i * components + 3];
+ pixels[i * 4 + 2] = tmppixels[i * components + 2];
+ pixels[i * 4 + 1] = tmppixels[i * components + 1];
+ pixels[i * 4 + 0] = tmppixels[i * components + 0];
+ }
+ tmppixels.clear();
+ }
+
+ /* CMYK to RGBA. */
+ const bool cmyk = strcmp(in->format_name(), "jpeg") == 0 && components == 4;
+ if (cmyk) {
+ const StorageType one = util_image_cast_from_float<StorageType>(1.0f);
+
+ const size_t num_pixels = ((size_t)width) * height * depth;
+ for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) {
+ float c = util_image_cast_to_float(pixels[i * 4 + 0]);
+ float m = util_image_cast_to_float(pixels[i * 4 + 1]);
+ float y = util_image_cast_to_float(pixels[i * 4 + 2]);
+ float k = util_image_cast_to_float(pixels[i * 4 + 3]);
+ pixels[i * 4 + 0] = util_image_cast_from_float<StorageType>((1.0f - c) * (1.0f - k));
+ pixels[i * 4 + 1] = util_image_cast_from_float<StorageType>((1.0f - m) * (1.0f - k));
+ pixels[i * 4 + 2] = util_image_cast_from_float<StorageType>((1.0f - y) * (1.0f - k));
+ pixels[i * 4 + 3] = one;
+ }
+ }
+}
+
+bool OIIOImageLoader::load_pixels(const ImageMetaData &metadata,
+ void *pixels,
+ const size_t,
+ const bool associate_alpha)
+{
+ unique_ptr<ImageInput> in = NULL;
+
+ /* NOTE: Error logging is done in meta data acquisition. */
+ if (!path_exists(filepath.string()) || path_is_directory(filepath.string())) {
+ return false;
+ }
+
+ /* load image from file through OIIO */
+ in = unique_ptr<ImageInput>(ImageInput::create(filepath.string()));
+ if (!in) {
+ return false;
+ }
+
+ ImageSpec spec = ImageSpec();
+ ImageSpec config = ImageSpec();
+
+ if (!associate_alpha) {
+ config.attribute("oiio:UnassociatedAlpha", 1);
+ }
+
+ if (!in->open(filepath.string(), spec, config)) {
+ return false;
+ }
+
+ switch (metadata.type) {
+ case IMAGE_DATA_TYPE_BYTE:
+ case IMAGE_DATA_TYPE_BYTE4:
+ oiio_load_pixels<TypeDesc::UINT8, uchar>(metadata, in, (uchar *)pixels);
+ break;
+ case IMAGE_DATA_TYPE_USHORT:
+ case IMAGE_DATA_TYPE_USHORT4:
+ oiio_load_pixels<TypeDesc::USHORT, uint16_t>(metadata, in, (uint16_t *)pixels);
+ break;
+ case IMAGE_DATA_TYPE_HALF:
+ case IMAGE_DATA_TYPE_HALF4:
+ oiio_load_pixels<TypeDesc::HALF, half>(metadata, in, (half *)pixels);
+ break;
+ case IMAGE_DATA_TYPE_FLOAT:
+ case IMAGE_DATA_TYPE_FLOAT4:
+ oiio_load_pixels<TypeDesc::FLOAT, float>(metadata, in, (float *)pixels);
+ break;
+ case IMAGE_DATA_NUM_TYPES:
+ break;
+ }
+
+ in->close();
+ return true;
+}
+
+string OIIOImageLoader::name() const
+{
+ return path_filename(filepath.string());
+}
+
+ustring OIIOImageLoader::osl_filepath() const
+{
+ return filepath;
+}
+
+bool OIIOImageLoader::equals(const ImageLoader &other) const
+{
+ const OIIOImageLoader &other_loader = (const OIIOImageLoader &)other;
+ return filepath == other_loader.filepath;
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/render/image_oiio.h b/intern/cycles/render/image_oiio.h
new file mode 100644
index 00000000000..a234b968557
--- /dev/null
+++ b/intern/cycles/render/image_oiio.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2011-2020 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __IMAGE_OIIO__
+#define __IMAGE_OIIO__
+
+#include "render/image.h"
+
+CCL_NAMESPACE_BEGIN
+
+class OIIOImageLoader : public ImageLoader {
+ public:
+ OIIOImageLoader(const string &filepath);
+ ~OIIOImageLoader();
+
+ bool load_metadata(ImageMetaData &metadata) override;
+
+ bool load_pixels(const ImageMetaData &metadata,
+ void *pixels,
+ const size_t pixels_size,
+ const bool associate_alpha) override;
+
+ string name() const override;
+
+ ustring osl_filepath() const override;
+
+ bool equals(const ImageLoader &other) const override;
+
+ protected:
+ ustring filepath;
+};
+
+CCL_NAMESPACE_END
+
+#endif /* __IMAGE_OIIO__ */
diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp
index bbdb2572392..26b816b65e9 100644
--- a/intern/cycles/render/nodes.cpp
+++ b/intern/cycles/render/nodes.cpp
@@ -256,7 +256,6 @@ NODE_DEFINE(ImageTextureNode)
ImageTextureNode::ImageTextureNode() : ImageSlotTextureNode(node_type)
{
colorspace = u_colorspace_raw;
- builtin_data = NULL;
animated = false;
tiles.push_back(1001);
}
@@ -268,17 +267,15 @@ ShaderNode *ImageTextureNode::clone() const
return node;
}
-ImageKey ImageTextureNode::image_key() const
+ImageParams ImageTextureNode::image_params() const
{
- ImageKey key;
- key.filename = filename.string();
- key.builtin_data = builtin_data;
- key.animated = animated;
- key.interpolation = interpolation;
- key.extension = extension;
- key.alpha_type = alpha_type;
- key.colorspace = colorspace;
- return key;
+ ImageParams params;
+ params.animated = animated;
+ params.interpolation = interpolation;
+ params.extension = extension;
+ params.alpha_type = alpha_type;
+ params.colorspace = colorspace;
+ return params;
}
void ImageTextureNode::cull_tiles(Scene *scene, ShaderGraph *graph)
@@ -365,9 +362,8 @@ void ImageTextureNode::compile(SVMCompiler &compiler)
if (handle.empty()) {
cull_tiles(compiler.scene, compiler.current_graph);
-
ImageManager *image_manager = compiler.scene->image_manager;
- handle = image_manager->add_image(image_key(), 0, tiles);
+ handle = image_manager->add_image(filename.string(), image_params(), tiles);
}
/* All tiles have the same metadata. */
@@ -448,11 +444,11 @@ void ImageTextureNode::compile(OSLCompiler &compiler)
if (handle.empty()) {
ImageManager *image_manager = compiler.scene->image_manager;
- handle = image_manager->add_image(image_key(), 0);
+ handle = image_manager->add_image(filename.string(), image_params());
}
const ImageMetaData metadata = handle.metadata();
- const bool is_float = metadata.is_float;
+ const bool is_float = metadata.is_float();
const bool compress_as_srgb = metadata.compress_as_srgb;
const ustring known_colorspace = metadata.colorspace;
@@ -524,7 +520,6 @@ NODE_DEFINE(EnvironmentTextureNode)
EnvironmentTextureNode::EnvironmentTextureNode() : ImageSlotTextureNode(node_type)
{
colorspace = u_colorspace_raw;
- builtin_data = NULL;
animated = false;
}
@@ -535,17 +530,15 @@ ShaderNode *EnvironmentTextureNode::clone() const
return node;
}
-ImageKey EnvironmentTextureNode::image_key() const
+ImageParams EnvironmentTextureNode::image_params() const
{
- ImageKey key;
- key.filename = filename.string();
- key.builtin_data = builtin_data;
- key.animated = animated;
- key.interpolation = interpolation;
- key.extension = EXTENSION_REPEAT;
- key.alpha_type = alpha_type;
- key.colorspace = colorspace;
- return key;
+ ImageParams params;
+ params.animated = animated;
+ params.interpolation = interpolation;
+ params.extension = EXTENSION_REPEAT;
+ params.alpha_type = alpha_type;
+ params.colorspace = colorspace;
+ return params;
}
void EnvironmentTextureNode::attributes(Shader *shader, AttributeRequestSet *attributes)
@@ -569,7 +562,7 @@ void EnvironmentTextureNode::compile(SVMCompiler &compiler)
if (handle.empty()) {
ImageManager *image_manager = compiler.scene->image_manager;
- handle = image_manager->add_image(image_key(), 0);
+ handle = image_manager->add_image(filename.string(), image_params());
}
const ImageMetaData metadata = handle.metadata();
@@ -596,18 +589,15 @@ void EnvironmentTextureNode::compile(SVMCompiler &compiler)
void EnvironmentTextureNode::compile(OSLCompiler &compiler)
{
- tex_mapping.compile(compiler);
-
- /* See comments in ImageTextureNode::compile about support
- * of builtin images.
- */
if (handle.empty()) {
ImageManager *image_manager = compiler.scene->image_manager;
- handle = image_manager->add_image(image_key(), 0);
+ handle = image_manager->add_image(filename.string(), image_params());
}
+ tex_mapping.compile(compiler);
+
const ImageMetaData metadata = handle.metadata();
- const bool is_float = metadata.is_float;
+ const bool is_float = metadata.is_float();
const bool compress_as_srgb = metadata.compress_as_srgb;
const ustring known_colorspace = metadata.colorspace;
@@ -1655,7 +1645,6 @@ NODE_DEFINE(PointDensityTextureNode)
PointDensityTextureNode::PointDensityTextureNode() : ShaderNode(node_type)
{
- builtin_data = NULL;
}
PointDensityTextureNode::~PointDensityTextureNode()
@@ -1668,7 +1657,7 @@ ShaderNode *PointDensityTextureNode::clone() const
* add_image again, to work around access of freed data on the Blender
* side. A better solution should be found to avoid this. */
PointDensityTextureNode *node = new PointDensityTextureNode(*this);
- node->handle = handle;
+ node->handle = handle; /* TODO: not needed? */
return node;
}
@@ -1680,18 +1669,11 @@ void PointDensityTextureNode::attributes(Shader *shader, AttributeRequestSet *at
ShaderNode::attributes(shader, attributes);
}
-void PointDensityTextureNode::add_image(ImageManager *image_manager)
+ImageParams PointDensityTextureNode::image_params() const
{
- if (!handle.empty()) {
- return;
- }
-
- ImageKey key;
- key.filename = filename.string();
- key.builtin_data = builtin_data;
- key.interpolation = interpolation;
-
- handle = image_manager->add_image(key, 0);
+ ImageParams params;
+ params.interpolation = interpolation;
+ return params;
}
void PointDensityTextureNode::compile(SVMCompiler &compiler)
@@ -1704,7 +1686,10 @@ void PointDensityTextureNode::compile(SVMCompiler &compiler)
const bool use_color = !color_out->links.empty();
if (use_density || use_color) {
- add_image(compiler.scene->image_manager);
+ if (handle.empty()) {
+ ImageManager *image_manager = compiler.scene->image_manager;
+ handle = image_manager->add_image(filename.string(), image_params());
+ }
const int slot = handle.svm_slot();
if (slot != -1) {
@@ -1744,7 +1729,10 @@ void PointDensityTextureNode::compile(OSLCompiler &compiler)
const bool use_color = !color_out->links.empty();
if (use_density || use_color) {
- add_image(compiler.scene->image_manager);
+ if (handle.empty()) {
+ ImageManager *image_manager = compiler.scene->image_manager;
+ handle = image_manager->add_image(filename.string(), image_params());
+ }
compiler.parameter_texture("filename", handle.svm_slot());
if (space == NODE_TEX_VOXEL_SPACE_WORLD) {
diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h
index 99e676fc8cb..1f52a2a49ab 100644
--- a/intern/cycles/render/nodes.h
+++ b/intern/cycles/render/nodes.h
@@ -80,6 +80,12 @@ class ImageSlotTextureNode : public TextureNode {
special_type = SHADER_SPECIAL_TYPE_IMAGE_SLOT;
}
+ virtual bool equals(const ShaderNode &other)
+ {
+ const ImageSlotTextureNode &other_node = (const ImageSlotTextureNode &)other;
+ return TextureNode::equals(other) && handle == other_node.handle;
+ }
+
ImageHandle handle;
};
@@ -95,16 +101,14 @@ class ImageTextureNode : public ImageSlotTextureNode {
virtual bool equals(const ShaderNode &other)
{
- const ImageTextureNode &image_node = (const ImageTextureNode &)other;
- return ImageSlotTextureNode::equals(other) && builtin_data == image_node.builtin_data &&
- animated == image_node.animated;
+ const ImageTextureNode &other_node = (const ImageTextureNode &)other;
+ return ImageSlotTextureNode::equals(other) && animated == other_node.animated;
}
- ImageKey image_key() const;
+ ImageParams image_params() const;
/* Parameters. */
ustring filename;
- void *builtin_data;
ustring colorspace;
ImageAlphaType alpha_type;
NodeImageProjection projection;
@@ -135,16 +139,14 @@ class EnvironmentTextureNode : public ImageSlotTextureNode {
virtual bool equals(const ShaderNode &other)
{
- const EnvironmentTextureNode &env_node = (const EnvironmentTextureNode &)other;
- return ImageSlotTextureNode::equals(other) && builtin_data == env_node.builtin_data &&
- animated == env_node.animated;
+ const EnvironmentTextureNode &other_node = (const EnvironmentTextureNode &)other;
+ return ImageSlotTextureNode::equals(other) && animated == other_node.animated;
}
- ImageKey image_key() const;
+ ImageParams image_params() const;
/* Parameters. */
ustring filename;
- void *builtin_data;
ustring colorspace;
ImageAlphaType alpha_type;
NodeEnvironmentProjection projection;
@@ -357,23 +359,22 @@ class PointDensityTextureNode : public ShaderNode {
return true;
}
- void add_image(ImageManager *image_manager);
-
/* Parameters. */
ustring filename;
NodeTexVoxelSpace space;
InterpolationType interpolation;
Transform tfm;
float3 vector;
- void *builtin_data;
/* Runtime. */
ImageHandle handle;
+ ImageParams image_params() const;
+
virtual bool equals(const ShaderNode &other)
{
- const PointDensityTextureNode &point_dendity_node = (const PointDensityTextureNode &)other;
- return ShaderNode::equals(other) && builtin_data == point_dendity_node.builtin_data;
+ const PointDensityTextureNode &other_node = (const PointDensityTextureNode &)other;
+ return ShaderNode::equals(other) && handle == other_node.handle;
}
};
diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index b1143acfbee..af3069cf95e 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -4653,8 +4653,8 @@ static const char *cpp_classes =
" operator void*() { return ptr.data; }\n"
" operator bool() { return ptr.data != NULL; }\n"
"\n"
- " bool operator==(const Pointer &other) { return ptr.data == other.ptr.data; }\n"
- " bool operator!=(const Pointer &other) { return ptr.data != other.ptr.data; }\n"
+ " bool operator==(const Pointer &other) const { return ptr.data == other.ptr.data; }\n"
+ " bool operator!=(const Pointer &other) const { return ptr.data != other.ptr.data; }\n"
"\n"
" PointerRNA ptr;\n"
"};\n"