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--intern/cycles/blender/blender_mesh.cpp1
-rw-r--r--intern/cycles/blender/blender_session.cpp10
-rw-r--r--intern/cycles/blender/blender_session.h2
-rw-r--r--intern/cycles/blender/blender_shader.cpp13
-rw-r--r--intern/cycles/blender/blender_sync.cpp2
-rw-r--r--intern/cycles/blender/blender_util.h19
-rw-r--r--intern/cycles/kernel/svm/svm.h2
-rw-r--r--intern/cycles/kernel/svm/svm_image.h38
-rw-r--r--intern/cycles/render/graph.cpp5
-rw-r--r--intern/cycles/render/graph.h5
-rw-r--r--intern/cycles/render/image.cpp11
-rw-r--r--intern/cycles/render/image.h6
-rw-r--r--intern/cycles/render/light.cpp10
-rw-r--r--intern/cycles/render/mesh.cpp58
-rw-r--r--intern/cycles/render/mesh.h2
-rw-r--r--intern/cycles/render/nodes.cpp230
-rw-r--r--intern/cycles/render/nodes.h14
-rw-r--r--intern/cycles/render/scene.h2
-rw-r--r--intern/cycles/render/shader.cpp16
-rw-r--r--release/scripts/startup/bl_ui/space_image.py32
-rw-r--r--source/blender/blenfont/BLF_api.h1
-rw-r--r--source/blender/blenfont/intern/blf.c12
-rw-r--r--source/blender/blenkernel/BKE_image.h22
-rw-r--r--source/blender/blenkernel/intern/bpath.c2
-rw-r--r--source/blender/blenkernel/intern/image.c519
-rw-r--r--source/blender/blenkernel/intern/packedFile.c2
-rw-r--r--source/blender/blenlib/BLI_math_vector.h2
-rw-r--r--source/blender/blenlib/intern/math_vector_inline.c5
-rw-r--r--source/blender/blenloader/intern/readfile.c50
-rw-r--r--source/blender/blenloader/intern/versioning_280.c23
-rw-r--r--source/blender/blenloader/intern/writefile.c2
-rw-r--r--source/blender/compositor/operations/COM_ViewerOperation.cpp3
-rw-r--r--source/blender/draw/engines/workbench/workbench_deferred.c47
-rw-r--r--source/blender/draw/engines/workbench/workbench_forward.c65
-rw-r--r--source/blender/draw/engines/workbench/workbench_materials.c7
-rw-r--r--source/blender/draw/engines/workbench/workbench_private.h1
-rw-r--r--source/blender/draw/intern/draw_cache.c8
-rw-r--r--source/blender/draw/intern/draw_cache.h10
-rw-r--r--source/blender/draw/intern/draw_cache_impl.h4
-rw-r--r--source/blender/draw/intern/draw_cache_impl_mesh.c280
-rw-r--r--source/blender/draw/intern/draw_manager_data.c11
-rw-r--r--source/blender/draw/modes/paint_texture_mode.c117
-rw-r--r--source/blender/editors/include/ED_image.h2
-rw-r--r--source/blender/editors/include/ED_paint.h2
-rw-r--r--source/blender/editors/include/ED_screen.h2
-rw-r--r--source/blender/editors/interface/interface_ops.c4
-rw-r--r--source/blender/editors/object/object_bake_api.c9
-rw-r--r--source/blender/editors/render/render_preview.c6
-rw-r--r--source/blender/editors/screen/area.c8
-rw-r--r--source/blender/editors/sculpt_paint/paint_image.c11
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_2d.c698
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c110
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h4
-rw-r--r--source/blender/editors/space_clip/clip_draw.c4
-rw-r--r--source/blender/editors/space_image/image_buttons.c2
-rw-r--r--source/blender/editors/space_image/image_draw.c141
-rw-r--r--source/blender/editors/space_image/image_edit.c9
-rw-r--r--source/blender/editors/space_image/image_intern.h5
-rw-r--r--source/blender/editors/space_image/image_ops.c458
-rw-r--r--source/blender/editors/space_image/space_image.c12
-rw-r--r--source/blender/gpu/GPU_material.h2
-rw-r--r--source/blender/gpu/intern/gpu_codegen.c24
-rw-r--r--source/blender/gpu/intern/gpu_codegen.h2
-rw-r--r--source/blender/gpu/intern/gpu_draw.c100
-rw-r--r--source/blender/gpu/shaders/gpu_shader_material.glsl61
-rw-r--r--source/blender/imbuf/IMB_moviecache.h1
-rw-r--r--source/blender/imbuf/intern/moviecache.c8
-rw-r--r--source/blender/makesdna/DNA_color_types.h1
-rw-r--r--source/blender/makesdna/DNA_image_types.h35
-rw-r--r--source/blender/makesdna/DNA_space_types.h2
-rw-r--r--source/blender/makesrna/intern/rna_image.c94
-rw-r--r--source/blender/makesrna/intern/rna_image_api.c23
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c5
-rw-r--r--source/blender/makesrna/intern/rna_space.c19
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_environment.c4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_image.c99
76 files changed, 2695 insertions, 943 deletions
diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp
index 8a6480a9a42..34f559674fa 100644
--- a/intern/cycles/blender/blender_mesh.cpp
+++ b/intern/cycles/blender/blender_mesh.cpp
@@ -348,6 +348,7 @@ static void create_mesh_volume_attribute(BL::Object& b_ob,
Attribute::standard_name(std),
b_ob.ptr.data,
animated,
+ 0,
frame,
INTERPOLATION_LINEAR,
EXTENSION_CLIP,
diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp
index 05adb6f5fe0..2c5ecc02e6d 100644
--- a/intern/cycles/blender/blender_session.cpp
+++ b/intern/cycles/blender/blender_session.cpp
@@ -142,8 +142,8 @@ void BlenderSession::create_session()
/* 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);
- scene->image_manager->builtin_image_float_pixels_cb = function_bind(&BlenderSession::builtin_image_float_pixels, this, _1, _2, _3, _4, _5);
+ scene->image_manager->builtin_image_pixels_cb = function_bind(&BlenderSession::builtin_image_pixels, this, _1, _2, _3, _4, _5, _6);
+ scene->image_manager->builtin_image_float_pixels_cb = function_bind(&BlenderSession::builtin_image_float_pixels, this, _1, _2, _3, _4, _5, _6);
session->scene = scene;
@@ -1119,6 +1119,7 @@ void BlenderSession::builtin_image_info(const string &builtin_name,
bool BlenderSession::builtin_image_pixels(const string &builtin_name,
void *builtin_data,
+ int tile,
unsigned char *pixels,
const size_t pixels_size,
const bool free_cache)
@@ -1137,7 +1138,7 @@ bool BlenderSession::builtin_image_pixels(const string &builtin_name,
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);
+ 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) {
@@ -1182,6 +1183,7 @@ bool BlenderSession::builtin_image_pixels(const string &builtin_name,
bool BlenderSession::builtin_image_float_pixels(const string &builtin_name,
void *builtin_data,
+ int tile,
float *pixels,
const size_t pixels_size,
const bool free_cache)
@@ -1204,7 +1206,7 @@ bool BlenderSession::builtin_image_float_pixels(const string &builtin_name,
const int channels = b_image.channels();
float *image_pixels;
- image_pixels = image_get_float_pixels_for_frame(b_image, frame);
+ 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) {
diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/blender_session.h
index 2be57f293b4..250b8ca9967 100644
--- a/intern/cycles/blender/blender_session.h
+++ b/intern/cycles/blender/blender_session.h
@@ -160,11 +160,13 @@ protected:
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 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 free_cache);
diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp
index 62c160ca503..830a3682a27 100644
--- a/intern/cycles/blender/blender_shader.cpp
+++ b/intern/cycles/blender/blender_shader.cpp
@@ -670,6 +670,12 @@ static ShaderNode *add_node(Scene *scene,
image->animated = b_image_node.image_user().use_auto_refresh();
image->use_alpha = b_image.use_alpha();
+ 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->tile_number());
+ }
+
/* TODO: restore */
/* TODO(sergey): Does not work properly when we change builtin type. */
#if 0
@@ -884,6 +890,7 @@ static ShaderNode *add_node(Scene *scene,
scene->image_manager->tag_reload_image(
point_density->filename.string(),
point_density->builtin_data,
+ 0,
point_density->interpolation,
EXTENSION_CLIP,
true);
@@ -1245,7 +1252,7 @@ void BlenderSync::sync_materials(BL::Depsgraph& b_depsgraph, bool update_all)
/* test if we need to sync */
if(shader_map.sync(&shader, b_mat) || shader->need_sync_object || update_all) {
- ShaderGraph *graph = new ShaderGraph();
+ ShaderGraph *graph = new ShaderGraph(shader);
shader->name = b_mat.name().c_str();
shader->pass_id = b_mat.pass_index();
@@ -1320,7 +1327,7 @@ void BlenderSync::sync_world(BL::Depsgraph& b_depsgraph, bool update_all)
if(world_recalc || update_all || b_world.ptr.data != world_map) {
Shader *shader = scene->default_background;
- ShaderGraph *graph = new ShaderGraph();
+ ShaderGraph *graph = new ShaderGraph(shader);
/* create nodes */
if(b_world && b_world.use_nodes() && b_world.node_tree()) {
@@ -1418,7 +1425,7 @@ void BlenderSync::sync_lights(BL::Depsgraph& b_depsgraph, bool update_all)
/* test if we need to sync */
if(shader_map.sync(&shader, b_light) || update_all) {
- ShaderGraph *graph = new ShaderGraph();
+ ShaderGraph *graph = new ShaderGraph(shader);
/* create nodes */
if(b_light.use_nodes() && b_light.node_tree()) {
diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp
index 93232fbf98f..d19eed0ea6b 100644
--- a/intern/cycles/blender/blender_sync.cpp
+++ b/intern/cycles/blender/blender_sync.cpp
@@ -620,6 +620,8 @@ SceneParams BlenderSync::get_scene_params(BL::Scene& b_scene,
params.bvh_layout = DebugFlags().cpu.bvh_layout;
+ params.background = background;
+
return params;
}
diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h
index 4e754d22984..04b2744cd10 100644
--- a/intern/cycles/blender/blender_util.h
+++ b/intern/cycles/blender/blender_util.h
@@ -34,8 +34,8 @@ extern "C" {
size_t BLI_timecode_string_from_time_simple(char *str, size_t maxlen, double time_seconds);
void BKE_image_user_frame_calc(void *iuser, int cfra, int fieldnr);
void BKE_image_user_file_path(void *iuser, void *ima, char *path);
-unsigned char *BKE_image_get_pixels_for_frame(void *image, int frame);
-float *BKE_image_get_float_pixels_for_frame(void *image, int frame);
+unsigned char *BKE_image_get_pixels_for_frame(void *image, int frame, int tile);
+float *BKE_image_get_float_pixels_for_frame(void *image, int frame, int tile);
}
CCL_NAMESPACE_BEGIN
@@ -220,8 +220,15 @@ static inline string image_user_file_path(BL::ImageUser& iuser,
int cfra)
{
char filepath[1024];
+ iuser.tile(0);
BKE_image_user_frame_calc(iuser.ptr.data, cfra, 0);
BKE_image_user_file_path(iuser.ptr.data, ima.ptr.data, filepath);
+ if(ima.source() == BL::Image::source_TILED) {
+ char *udim_id = strstr(filepath, "1001");
+ if(udim_id) {
+ memcpy(udim_id, "%04d", 4);
+ }
+ }
return string(filepath);
}
@@ -232,15 +239,15 @@ static inline int image_user_frame_number(BL::ImageUser& iuser, int cfra)
}
static inline unsigned char *image_get_pixels_for_frame(BL::Image& image,
- int frame)
+ int frame, int tile)
{
- return BKE_image_get_pixels_for_frame(image.ptr.data, frame);
+ return BKE_image_get_pixels_for_frame(image.ptr.data, frame, tile);
}
static inline float *image_get_float_pixels_for_frame(BL::Image& image,
- int frame)
+ int frame, int tile)
{
- return BKE_image_get_float_pixels_for_frame(image.ptr.data, frame);
+ return BKE_image_get_float_pixels_for_frame(image.ptr.data, frame, tile);
}
/* Utilities */
diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h
index ab69afa051e..6f0945b3d15 100644
--- a/intern/cycles/kernel/svm/svm.h
+++ b/intern/cycles/kernel/svm/svm.h
@@ -277,7 +277,7 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, ccl_a
# endif /* NODES_FEATURE(NODE_FEATURE_BUMP) */
# ifdef __TEXTURES__
case NODE_TEX_IMAGE:
- svm_node_tex_image(kg, sd, stack, node);
+ svm_node_tex_image(kg, sd, stack, node, &offset);
break;
case NODE_TEX_IMAGE_BOX:
svm_node_tex_image_box(kg, sd, stack, node);
diff --git a/intern/cycles/kernel/svm/svm_image.h b/intern/cycles/kernel/svm/svm_image.h
index 81ee79c984e..196c56b2cbe 100644
--- a/intern/cycles/kernel/svm/svm_image.h
+++ b/intern/cycles/kernel/svm/svm_image.h
@@ -18,6 +18,9 @@ CCL_NAMESPACE_BEGIN
ccl_device float4 svm_image_texture(KernelGlobals *kg, int id, float x, float y, uint srgb, uint use_alpha)
{
+ if(id == -1) {
+ return make_float4(TEX_IMAGE_MISSING_R, TEX_IMAGE_MISSING_G, TEX_IMAGE_MISSING_B, TEX_IMAGE_MISSING_A);
+ }
float4 r = kernel_tex_image_interp(kg, id, x, y);
const float alpha = r.w;
@@ -46,9 +49,8 @@ ccl_device_inline float3 texco_remap_square(float3 co)
return (co - make_float3(0.5f, 0.5f, 0.5f)) * 2.0f;
}
-ccl_device void svm_node_tex_image(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node)
+ccl_device void svm_node_tex_image(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset)
{
- uint id = node.y;
uint co_offset, out_offset, alpha_offset, srgb;
decode_node_uchar4(node.z, &co_offset, &out_offset, &alpha_offset, &srgb);
@@ -67,6 +69,38 @@ ccl_device void svm_node_tex_image(KernelGlobals *kg, ShaderData *sd, float *sta
else {
tex_co = make_float2(co.x, co.y);
}
+
+ int id = -1;
+ if(node.y > 0) {
+ uint num_nodes = node.y;
+ int next_offset = (*offset) + num_nodes;
+ if(tex_co.x >= 0.0f && tex_co.y < 10.0f && tex_co.y >= 0.0f) {
+ int tx = (int) tex_co.x;
+ int ty = (int) tex_co.y;
+ int tile = ty*10 + tx;
+ for(int i = 0; i < num_nodes; i++) {
+ uint4 node = read_node(kg, offset);
+ if(node.x == tile) {
+ id = node.y;
+ break;
+ }
+ if(node.z == tile) {
+ id = node.w;
+ break;
+ }
+ }
+
+ if(id != -1) {
+ tex_co.x -= tx;
+ tex_co.y -= ty;
+ }
+ }
+ *offset = next_offset;
+ }
+ else {
+ id = read_node(kg, offset).y;
+ }
+
float4 f = svm_image_texture(kg, id, tex_co.x, tex_co.y, srgb, use_alpha);
if(stack_valid(out_offset))
diff --git a/intern/cycles/render/graph.cpp b/intern/cycles/render/graph.cpp
index 3a9e2981418..d8781c2cf9a 100644
--- a/intern/cycles/render/graph.cpp
+++ b/intern/cycles/render/graph.cpp
@@ -193,7 +193,8 @@ bool ShaderNode::equals(const ShaderNode& other)
/* Graph */
-ShaderGraph::ShaderGraph()
+ShaderGraph::ShaderGraph(Shader *shader)
+ : shader(shader)
{
finalized = false;
simplified = false;
@@ -557,7 +558,7 @@ void ShaderGraph::constant_fold(Scene *scene)
void ShaderGraph::simplify_settings(Scene *scene)
{
foreach(ShaderNode *node, nodes) {
- node->simplify_settings(scene);
+ node->simplify_settings(scene, shader);
}
}
diff --git a/intern/cycles/render/graph.h b/intern/cycles/render/graph.h
index 426522066b3..7943d0ed063 100644
--- a/intern/cycles/render/graph.h
+++ b/intern/cycles/render/graph.h
@@ -147,7 +147,7 @@ public:
/* Simplify settings used by artists to the ones which are simpler to
* evaluate in the kernel but keep the final result unchanged.
*/
- virtual void simplify_settings(Scene * /*scene*/) {};
+ virtual void simplify_settings(Scene * /*scene*/, Shader * /*shader*/) {};
virtual bool has_surface_emission() { return false; }
virtual bool has_surface_transparent() { return false; }
@@ -246,8 +246,9 @@ public:
bool finalized;
bool simplified;
string displacement_hash;
+ Shader *shader;
- ShaderGraph();
+ ShaderGraph(Shader *shader);
~ShaderGraph();
ShaderNode *add(ShaderNode *node);
diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp
index 2865b0e5e97..bead0118a02 100644
--- a/intern/cycles/render/image.cpp
+++ b/intern/cycles/render/image.cpp
@@ -278,12 +278,14 @@ bool ImageManager::get_image_metadata(const string& filename,
static bool image_equals(ImageManager::Image *image,
const string& filename,
void *builtin_data,
+ int tile,
InterpolationType interpolation,
ExtensionType extension,
bool use_alpha)
{
return image->filename == filename &&
image->builtin_data == builtin_data &&
+ image->tile == tile &&
image->interpolation == interpolation &&
image->extension == extension &&
image->use_alpha == use_alpha;
@@ -292,6 +294,7 @@ static bool image_equals(ImageManager::Image *image,
int ImageManager::add_image(const string& filename,
void *builtin_data,
bool animated,
+ int tile,
float frame,
InterpolationType interpolation,
ExtensionType extension,
@@ -322,6 +325,7 @@ int ImageManager::add_image(const string& filename,
if(img && image_equals(img,
filename,
builtin_data,
+ tile,
interpolation,
extension,
use_alpha))
@@ -370,6 +374,7 @@ int ImageManager::add_image(const string& filename,
img->metadata = metadata;
img->need_load = true;
img->animated = animated;
+ img->tile = tile;
img->frame = frame;
img->interpolation = interpolation;
img->extension = extension;
@@ -406,6 +411,7 @@ void ImageManager::remove_image(int flat_slot)
void ImageManager::remove_image(const string& filename,
void *builtin_data,
+ int tile,
InterpolationType interpolation,
ExtensionType extension,
bool use_alpha)
@@ -417,6 +423,7 @@ void ImageManager::remove_image(const string& filename,
if(images[type][slot] && image_equals(images[type][slot],
filename,
builtin_data,
+ tile,
interpolation,
extension,
use_alpha))
@@ -434,6 +441,7 @@ void ImageManager::remove_image(const string& filename,
*/
void ImageManager::tag_reload_image(const string& filename,
void *builtin_data,
+ int tile,
InterpolationType interpolation,
ExtensionType extension,
bool use_alpha)
@@ -443,6 +451,7 @@ void ImageManager::tag_reload_image(const string& filename,
if(images[type][slot] && image_equals(images[type][slot],
filename,
builtin_data,
+ tile,
interpolation,
extension,
use_alpha))
@@ -581,6 +590,7 @@ bool ImageManager::file_load_image(Image *img,
if(FileFormat == TypeDesc::FLOAT) {
builtin_image_float_pixels_cb(img->filename,
img->builtin_data,
+ img->tile,
(float*)&pixels[0],
num_pixels * components,
img->metadata.builtin_free_cache);
@@ -588,6 +598,7 @@ bool ImageManager::file_load_image(Image *img,
else if(FileFormat == TypeDesc::UINT8) {
builtin_image_pixels_cb(img->filename,
img->builtin_data,
+ img->tile,
(uchar*)&pixels[0],
num_pixels * components,
img->metadata.builtin_free_cache);
diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h
index 0bf06c322d0..c8996b70b38 100644
--- a/intern/cycles/render/image.h
+++ b/intern/cycles/render/image.h
@@ -53,6 +53,7 @@ public:
int add_image(const string& filename,
void *builtin_data,
bool animated,
+ int tile,
float frame,
InterpolationType interpolation,
ExtensionType extension,
@@ -61,11 +62,13 @@ public:
void remove_image(int flat_slot);
void remove_image(const string& filename,
void *builtin_data,
+ int tile,
InterpolationType interpolation,
ExtensionType extension,
bool use_alpha);
void tag_reload_image(const string& filename,
void *builtin_data,
+ int tile,
InterpolationType interpolation,
ExtensionType extension,
bool use_alpha);
@@ -107,11 +110,13 @@ public:
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 free_cache)> builtin_image_pixels_cb;
function<bool(const string &filename,
void *data,
+ int tile,
float *pixels,
const size_t pixels_size,
const bool free_cache)> builtin_image_float_pixels_cb;
@@ -125,6 +130,7 @@ public:
bool need_load;
bool animated;
float frame;
+ int tile;
InterpolationType interpolation;
ExtensionType extension;
diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp
index 49cfae4888b..b8277932a55 100644
--- a/intern/cycles/render/light.cpp
+++ b/intern/cycles/render/light.cpp
@@ -555,9 +555,13 @@ void LightManager::device_update_background(Device *device,
if(node->type == EnvironmentTextureNode::node_type) {
EnvironmentTextureNode *env = (EnvironmentTextureNode*) node;
ImageMetaData metadata;
- if(env->image_manager && env->image_manager->get_image_metadata(env->slot, metadata)) {
- res.x = max(res.x, metadata.width);
- res.y = max(res.y, metadata.height);
+ if(env->image_manager) {
+ foreach(int slot, env->slots) {
+ if(env->image_manager->get_image_metadata(slot, metadata)) {
+ res.x = max(res.x, metadata.width);
+ res.y = max(res.y, metadata.height);
+ }
+ }
}
}
}
diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp
index ade575a52d6..3b85186948e 100644
--- a/intern/cycles/render/mesh.cpp
+++ b/intern/cycles/render/mesh.cpp
@@ -662,6 +662,61 @@ void Mesh::add_subd_face(int* corners, int num_corners, int shader_, bool smooth
subd_faces.push_back_reserved(face);
}
+static void get_uv_tiles_from_attribute(Attribute *attr, int num, vector<bool> &tiles)
+{
+ if(!attr) {
+ return;
+ }
+
+ const float3 *uv = attr->data_float3();
+ int size = tiles.size();
+ for(int i = 0; i < num; i++, uv++) {
+ float u = uv->x, v = uv->y;
+
+ if(u < 0.0f || v < 0.0f || u > 10.0f) continue;
+
+ int x = (int) u, y = (int) v;
+
+ /* Be conservative in corners - precisely touching the right-upper corner of a tile
+ * should not load its right-upper neighbor as well. */
+ if(x && (u == x)) x--;
+ if(y && (v == y)) y--;
+
+ int id = y*10 + x;
+ if(id >= size) {
+ tiles.resize(id+1);
+ size = id;
+ }
+ tiles[id] = true;
+ }
+}
+
+void Mesh::get_uv_tiles(ustring map, vector<bool> &tiles)
+{
+ if(map.empty()) {
+ get_uv_tiles_from_attribute(attributes.find(ATTR_STD_UV),
+ num_triangles()*3,
+ tiles);
+ get_uv_tiles_from_attribute(subd_attributes.find(ATTR_STD_UV),
+ subd_face_corners.size() + num_ngons,
+ tiles);
+ get_uv_tiles_from_attribute(curve_attributes.find(ATTR_STD_UV),
+ num_curves(),
+ tiles);
+ }
+ else {
+ get_uv_tiles_from_attribute(attributes.find(map),
+ num_triangles()*3,
+ tiles);
+ get_uv_tiles_from_attribute(subd_attributes.find(map),
+ subd_face_corners.size() + num_ngons,
+ tiles);
+ get_uv_tiles_from_attribute(curve_attributes.find(map),
+ num_curves(),
+ tiles);
+ }
+}
+
void Mesh::compute_bounds()
{
BoundBox bnds = BoundBox::empty;
@@ -1996,8 +2051,7 @@ void MeshManager::device_update_displacement_images(Device *device,
}
ImageSlotTextureNode *image_node = static_cast<ImageSlotTextureNode*>(node);
- int slot = image_node->slot;
- if(slot != -1) {
+ foreach(int slot, image_node->slots) {
bump_images.insert(slot);
}
}
diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h
index 444f03a3664..0cd3919a49c 100644
--- a/intern/cycles/render/mesh.h
+++ b/intern/cycles/render/mesh.h
@@ -280,6 +280,8 @@ public:
void add_vertex_normals();
void add_undisplaced();
+ void get_uv_tiles(ustring map, vector<bool> &tiles);
+
void pack_shaders(Scene *scene, uint *shader);
void pack_normals(float4 *vnormal);
void pack_verts(const vector<uint>& tri_prim_index,
diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp
index cce851612db..91fb04f1b75 100644
--- a/intern/cycles/render/nodes.cpp
+++ b/intern/cycles/render/nodes.cpp
@@ -18,6 +18,7 @@
#include "render/image.h"
#include "render/integrator.h"
#include "render/light.h"
+#include "render/mesh.h"
#include "render/nodes.h"
#include "render/scene.h"
#include "render/svm.h"
@@ -249,21 +250,19 @@ ImageTextureNode::ImageTextureNode()
: ImageSlotTextureNode(node_type)
{
image_manager = NULL;
- slot = -1;
- is_float = -1;
+ is_float = false;
is_linear = false;
builtin_data = NULL;
animated = false;
+ tiles.push_back(0);
}
ImageTextureNode::~ImageTextureNode()
{
if(image_manager) {
- image_manager->remove_image(filename.string(),
- builtin_data,
- interpolation,
- extension,
- use_alpha);
+ foreach(int slot, slots) {
+ image_manager->remove_image(slot);
+ }
}
}
@@ -271,12 +270,55 @@ ShaderNode *ImageTextureNode::clone() const
{
ImageTextureNode *node = new ImageTextureNode(*this);
node->image_manager = NULL;
- node->slot = -1;
- node->is_float = -1;
+ node->slots.clear();
+ node->is_float = false;
node->is_linear = false;
return node;
}
+void ImageTextureNode::simplify_settings(Scene *scene, Shader *shader)
+{
+ if(!scene->params.background) {
+ /* During interactive renders, all tiles are loaded.
+ * While we could support updating this when UVs change, that could lead
+ * to annoying interruptions when loading images while editing UVs. */
+ return;
+ }
+
+ ShaderInput *vector_in = input("Vector");
+ ustring attribute;
+ if(vector_in->link) {
+ ShaderNode *node = vector_in->link->parent;
+ if(node->type == UVMapNode::node_type) {
+ UVMapNode *uvmap = (UVMapNode*) node;
+ attribute = uvmap->attribute;
+ }
+ else if(node->type == TextureCoordinateNode::node_type) {
+ if(vector_in->link != node->output("UV")) {
+ return;
+ }
+ }
+ else {
+ return;
+ }
+ }
+
+ ccl::vector<bool> used_tiles;
+ foreach(Mesh *mesh, scene->meshes) {
+ if(std::find(mesh->used_shaders.begin(), mesh->used_shaders.end(), shader) != mesh->used_shaders.end()) {
+ mesh->get_uv_tiles(attribute, used_tiles);
+ }
+ }
+
+ ccl::vector<int> new_tiles;
+ foreach(int tile, tiles) {
+ if (tile < used_tiles.size() && used_tiles[tile]) {
+ new_tiles.push_back(tile);
+ }
+ }
+ tiles.swap(new_tiles);
+}
+
void ImageTextureNode::attributes(Shader *shader, AttributeRequestSet *attributes)
{
#ifdef WITH_PTEX
@@ -299,27 +341,52 @@ void ImageTextureNode::compile(SVMCompiler& compiler)
ShaderOutput *alpha_out = output("Alpha");
image_manager = compiler.image_manager;
- if(is_float == -1) {
- ImageMetaData metadata;
- slot = image_manager->add_image(filename.string(),
- builtin_data,
- animated,
- 0,
- interpolation,
- extension,
- use_alpha,
- metadata);
- is_float = metadata.is_float;
- is_linear = metadata.is_linear;
+ if(slots.size() < tiles.size()) {
+ slots.clear();
+ foreach(int tile, tiles) {
+ string tile_name;
+ if(tiles.size() == 1) {
+ tile_name = filename.string();
+ }
+ else {
+ tile_name = string_printf(filename.c_str(), 1001 + tile);
+ }
+ printf("Loading %s\n", tile_name.c_str());
+ ImageMetaData metadata;
+ slots.push_back(image_manager->add_image(tile_name,
+ builtin_data,
+ animated,
+ tile,
+ 0,
+ interpolation,
+ extension,
+ use_alpha,
+ metadata));
+ if(tile == 0) {
+ is_float = metadata.is_float;
+ is_linear = metadata.is_linear;
+ }
+ }
}
- if(slot != -1) {
+ bool has_image = false;
+ foreach(int slot, slots) {
+ if(slot != -1) {
+ has_image = true;
+ break;
+ }
+ }
+
+ if(has_image) {
+ int num_nodes = divide_up(slots.size(), 2);
int srgb = (is_linear || color_space != NODE_COLOR_SPACE_COLOR)? 0: 1;
int vector_offset = tex_mapping.compile_begin(compiler, vector_in);
+ /* TODO(lukas): Avoid extra node for common case of only one tile. */
+
if(projection != NODE_IMAGE_PROJ_BOX) {
compiler.add_node(NODE_TEX_IMAGE,
- slot,
+ num_nodes,
compiler.encode_uchar4(
vector_offset,
compiler.stack_assign_if_linked(color_out),
@@ -329,7 +396,7 @@ void ImageTextureNode::compile(SVMCompiler& compiler)
}
else {
compiler.add_node(NODE_TEX_IMAGE_BOX,
- slot,
+ num_nodes,
compiler.encode_uchar4(
vector_offset,
compiler.stack_assign_if_linked(color_out),
@@ -338,6 +405,21 @@ void ImageTextureNode::compile(SVMCompiler& compiler)
__float_as_int(projection_blend));
}
+ for(int i = 0; i < num_nodes; i++) {
+ int4 node;
+ node.x = tiles[2*i];
+ node.y = slots[2*i];
+ if(2*i+1 < slots.size()) {
+ node.z = tiles[2*i+1];
+ node.w = slots[2*i+1];
+ }
+ else {
+ node.z = -1;
+ node.w = -1;
+ }
+ compiler.add_node(node.x, node.y, node.z, node.w);
+ }
+
tex_mapping.compile_end(compiler, vector_in, vector_offset);
}
else {
@@ -360,26 +442,28 @@ void ImageTextureNode::compile(OSLCompiler& compiler)
tex_mapping.compile(compiler);
image_manager = compiler.image_manager;
- if(is_float == -1) {
+ if(slots.size() == 0) {
ImageMetaData metadata;
if(builtin_data == NULL) {
image_manager->get_image_metadata(filename.string(), NULL, metadata);
+ slots.push_back(-1);
}
else {
- slot = image_manager->add_image(filename.string(),
- builtin_data,
- animated,
- 0,
- interpolation,
- extension,
- use_alpha,
- metadata);
+ slots.push_back(image_manager->add_image(filename.string(),
+ builtin_data,
+ animated,
+ 0,
+ 0,
+ interpolation,
+ extension,
+ use_alpha,
+ metadata));
}
is_float = metadata.is_float;
is_linear = metadata.is_linear;
}
- if(slot == -1) {
+ if(slots[0] == -1) {
compiler.parameter(this, "filename");
}
else {
@@ -389,7 +473,7 @@ void ImageTextureNode::compile(OSLCompiler& compiler)
* "@i<slot_number>" and check whether file name matches this
* mask in the OSLRenderServices::texture().
*/
- compiler.parameter("filename", string_printf("@i%d", slot).c_str());
+ compiler.parameter("filename", string_printf("@i%d", slots[0]).c_str());
}
if(is_linear || color_space != NODE_COLOR_SPACE_COLOR)
compiler.parameter("color_space", "linear");
@@ -446,8 +530,7 @@ EnvironmentTextureNode::EnvironmentTextureNode()
: ImageSlotTextureNode(node_type)
{
image_manager = NULL;
- slot = -1;
- is_float = -1;
+ is_float = false;
is_linear = false;
builtin_data = NULL;
animated = false;
@@ -455,12 +538,8 @@ EnvironmentTextureNode::EnvironmentTextureNode()
EnvironmentTextureNode::~EnvironmentTextureNode()
{
- if(image_manager) {
- image_manager->remove_image(filename.string(),
- builtin_data,
- interpolation,
- EXTENSION_REPEAT,
- use_alpha);
+ if(image_manager && slots.size()) {
+ image_manager->remove_image(slots[0]);
}
}
@@ -468,8 +547,8 @@ ShaderNode *EnvironmentTextureNode::clone() const
{
EnvironmentTextureNode *node = new EnvironmentTextureNode(*this);
node->image_manager = NULL;
- node->slot = -1;
- node->is_float = -1;
+ node->slots.clear();
+ node->is_float = false;
node->is_linear = false;
return node;
}
@@ -494,26 +573,27 @@ void EnvironmentTextureNode::compile(SVMCompiler& compiler)
ShaderOutput *alpha_out = output("Alpha");
image_manager = compiler.image_manager;
- if(slot == -1) {
+ if(slots.size() == 0) {
ImageMetaData metadata;
- slot = image_manager->add_image(filename.string(),
- builtin_data,
- animated,
- 0,
- interpolation,
- EXTENSION_REPEAT,
- use_alpha,
- metadata);
+ slots.push_back(image_manager->add_image(filename.string(),
+ builtin_data,
+ animated,
+ 0,
+ 0,
+ interpolation,
+ EXTENSION_REPEAT,
+ use_alpha,
+ metadata));
is_float = metadata.is_float;
is_linear = metadata.is_linear;
}
- if(slot != -1) {
+ if(slots[0] != -1) {
int srgb = (is_linear || color_space != NODE_COLOR_SPACE_COLOR)? 0: 1;
int vector_offset = tex_mapping.compile_begin(compiler, vector_in);
compiler.add_node(NODE_TEX_ENVIRONMENT,
- slot,
+ slots[0],
compiler.encode_uchar4(
vector_offset,
compiler.stack_assign_if_linked(color_out),
@@ -546,30 +626,32 @@ void EnvironmentTextureNode::compile(OSLCompiler& compiler)
* of builtin images.
*/
image_manager = compiler.image_manager;
- if(is_float == -1) {
+ if(slots.size() == 0) {
+ slots.resize(1);
ImageMetaData metadata;
if(builtin_data == NULL) {
image_manager->get_image_metadata(filename.string(), NULL, metadata);
}
else {
- slot = image_manager->add_image(filename.string(),
- builtin_data,
- animated,
- 0,
- interpolation,
- EXTENSION_REPEAT,
- use_alpha,
- metadata);
+ slots[0] = image_manager->add_image(filename.string(),
+ builtin_data,
+ animated,
+ 0,
+ 0,
+ interpolation,
+ EXTENSION_REPEAT,
+ use_alpha,
+ metadata);
}
is_float = metadata.is_float;
is_linear = metadata.is_linear;
}
- if(slot == -1) {
+ if(slots[0] == -1) {
compiler.parameter(this, "filename");
}
else {
- compiler.parameter("filename", string_printf("@i%d", slot).c_str());
+ compiler.parameter("filename", string_printf("@i%d", slots[0]).c_str());
}
compiler.parameter(this, "projection");
if(is_linear || color_space != NODE_COLOR_SPACE_COLOR)
@@ -1495,11 +1577,7 @@ PointDensityTextureNode::PointDensityTextureNode()
PointDensityTextureNode::~PointDensityTextureNode()
{
if(image_manager) {
- image_manager->remove_image(filename.string(),
- builtin_data,
- interpolation,
- EXTENSION_CLIP,
- true);
+ image_manager->remove_image(slot);
}
}
@@ -1525,7 +1603,7 @@ void PointDensityTextureNode::add_image()
if(slot == -1) {
ImageMetaData metadata;
slot = image_manager->add_image(filename.string(), builtin_data,
- false, 0,
+ false, 0, 0,
interpolation,
EXTENSION_CLIP,
true,
@@ -2055,7 +2133,7 @@ GlossyBsdfNode::GlossyBsdfNode()
distribution_orig = NBUILTIN_CLOSURES;
}
-void GlossyBsdfNode::simplify_settings(Scene *scene)
+void GlossyBsdfNode::simplify_settings(Scene *scene, Shader * /*shader*/)
{
if(distribution_orig == NBUILTIN_CLOSURES) {
roughness_orig = roughness;
@@ -2150,7 +2228,7 @@ GlassBsdfNode::GlassBsdfNode()
distribution_orig = NBUILTIN_CLOSURES;
}
-void GlassBsdfNode::simplify_settings(Scene *scene)
+void GlassBsdfNode::simplify_settings(Scene *scene, Shader * /*shader*/)
{
if(distribution_orig == NBUILTIN_CLOSURES) {
roughness_orig = roughness;
@@ -2245,7 +2323,7 @@ RefractionBsdfNode::RefractionBsdfNode()
distribution_orig = NBUILTIN_CLOSURES;
}
-void RefractionBsdfNode::simplify_settings(Scene *scene)
+void RefractionBsdfNode::simplify_settings(Scene *scene, Shader * /*shader*/)
{
if(distribution_orig == NBUILTIN_CLOSURES) {
roughness_orig = roughness;
diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h
index c2cf13ad020..2e787aa3df2 100644
--- a/intern/cycles/render/nodes.h
+++ b/intern/cycles/render/nodes.h
@@ -74,7 +74,7 @@ public:
explicit ImageSlotTextureNode(const NodeType *node_type) : TextureNode(node_type) {
special_type = SHADER_SPECIAL_TYPE_IMAGE_SLOT;
}
- int slot;
+ vector<int> slots;
};
class ImageTextureNode : public ImageSlotTextureNode {
@@ -84,9 +84,10 @@ public:
ShaderNode *clone() const;
void attributes(Shader *shader, AttributeRequestSet *attributes);
bool has_attribute_dependency() { return true; }
+ void simplify_settings(Scene *scene, Shader *shader);
ImageManager *image_manager;
- int is_float;
+ bool is_float;
bool is_linear;
bool use_alpha;
ustring filename;
@@ -98,6 +99,7 @@ public:
float projection_blend;
bool animated;
float3 vector;
+ ccl::vector<int> tiles;
virtual bool equals(const ShaderNode& other)
{
@@ -118,7 +120,7 @@ public:
virtual int get_group() { return NODE_GROUP_LEVEL_2; }
ImageManager *image_manager;
- int is_float;
+ bool is_float;
bool is_linear;
bool use_alpha;
ustring filename;
@@ -450,7 +452,7 @@ class GlossyBsdfNode : public BsdfNode {
public:
SHADER_NODE_CLASS(GlossyBsdfNode)
- void simplify_settings(Scene *scene);
+ void simplify_settings(Scene *scene, Shader *shader);
bool has_integrator_dependency();
ClosureType get_closure_type() { return distribution; }
@@ -462,7 +464,7 @@ class GlassBsdfNode : public BsdfNode {
public:
SHADER_NODE_CLASS(GlassBsdfNode)
- void simplify_settings(Scene *scene);
+ void simplify_settings(Scene *scene, Shader *shader);
bool has_integrator_dependency();
ClosureType get_closure_type() { return distribution; }
@@ -474,7 +476,7 @@ class RefractionBsdfNode : public BsdfNode {
public:
SHADER_NODE_CLASS(RefractionBsdfNode)
- void simplify_settings(Scene *scene);
+ void simplify_settings(Scene *scene, Shader *shader);
bool has_integrator_dependency();
ClosureType get_closure_type() { return distribution; }
diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h
index dd8069537eb..c107a0b073d 100644
--- a/intern/cycles/render/scene.h
+++ b/intern/cycles/render/scene.h
@@ -170,6 +170,8 @@ public:
bool persistent_data;
int texture_limit;
+ bool background;
+
SceneParams()
{
shadingsystem = SHADINGSYSTEM_SVM;
diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp
index e428e174712..8148757b693 100644
--- a/intern/cycles/render/shader.cpp
+++ b/intern/cycles/render/shader.cpp
@@ -579,7 +579,8 @@ void ShaderManager::add_default(Scene *scene)
{
/* default surface */
{
- ShaderGraph *graph = new ShaderGraph();
+ Shader *shader = new Shader();
+ ShaderGraph *graph = new ShaderGraph(shader);
DiffuseBsdfNode *diffuse = new DiffuseBsdfNode();
diffuse->color = make_float3(0.8f, 0.8f, 0.8f);
@@ -587,7 +588,6 @@ void ShaderManager::add_default(Scene *scene)
graph->connect(diffuse->output("BSDF"), graph->output()->input("Surface"));
- Shader *shader = new Shader();
shader->name = "default_surface";
shader->graph = graph;
scene->shaders.push_back(shader);
@@ -596,7 +596,8 @@ void ShaderManager::add_default(Scene *scene)
/* default light */
{
- ShaderGraph *graph = new ShaderGraph();
+ Shader *shader = new Shader();
+ ShaderGraph *graph = new ShaderGraph(shader);
EmissionNode *emission = new EmissionNode();
emission->color = make_float3(0.8f, 0.8f, 0.8f);
@@ -605,7 +606,6 @@ void ShaderManager::add_default(Scene *scene)
graph->connect(emission->output("Emission"), graph->output()->input("Surface"));
- Shader *shader = new Shader();
shader->name = "default_light";
shader->graph = graph;
scene->shaders.push_back(shader);
@@ -614,9 +614,9 @@ void ShaderManager::add_default(Scene *scene)
/* default background */
{
- ShaderGraph *graph = new ShaderGraph();
-
Shader *shader = new Shader();
+ ShaderGraph *graph = new ShaderGraph(shader);
+
shader->name = "default_background";
shader->graph = graph;
scene->shaders.push_back(shader);
@@ -625,9 +625,9 @@ void ShaderManager::add_default(Scene *scene)
/* default empty */
{
- ShaderGraph *graph = new ShaderGraph();
-
Shader *shader = new Shader();
+ ShaderGraph *graph = new ShaderGraph(shader);
+
shader->name = "default_empty";
shader->graph = graph;
scene->shaders.push_back(shader);
diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py
index 1303e46ab6c..a47baec23dd 100644
--- a/release/scripts/startup/bl_ui/space_image.py
+++ b/release/scripts/startup/bl_ui/space_image.py
@@ -736,6 +736,10 @@ class IMAGE_PT_view_properties(Panel):
row.active = uvedit.show_other_objects
row.prop(uvedit, "other_uv_filter", text="Filter")
+ if ima is None:
+ row = layout.row()
+ row.prop(uvedit, "tile_grid_shape", text="Grid Shape")
+
class IMAGE_UL_render_slots(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
@@ -773,6 +777,33 @@ class IMAGE_PT_render_slots(Panel):
col.operator("image.clear_render_slot", icon='X', text="")
+class IMAGE_PT_tile_properties(Panel):
+ bl_space_type = 'IMAGE_EDITOR'
+ bl_region_type = 'UI'
+ bl_label = "Tiles"
+
+ @classmethod
+ def poll(cls, context):
+ sima = context.space_data
+ return (sima and sima.image and sima.image.source == 'TILED')
+
+ def draw(self, context):
+ layout = self.layout
+
+ sima = context.space_data
+ ima = sima.image
+
+ row = layout.row(align=True)
+ row.operator("image.add_tile")
+ row.operator("image.remove_tile")
+
+ tile = ima.tiles.get(sima.current_tile)
+ if tile:
+ col = layout.column(align=True)
+ col.operator("image.fill_tile")
+ col.prop(tile, "label")
+
+
class IMAGE_PT_tools_transform_uvs(Panel, UVToolsPanel):
bl_label = "Transform"
@@ -1407,6 +1438,7 @@ classes = (
IMAGE_UL_render_slots,
IMAGE_PT_render_slots,
IMAGE_PT_view_properties,
+ IMAGE_PT_tile_properties,
IMAGE_PT_tools_transform_uvs,
IMAGE_PT_tools_align_uvs,
IMAGE_PT_tools_uvs,
diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h
index 90c0016d0ed..f7a88ee4a11 100644
--- a/source/blender/blenfont/BLF_api.h
+++ b/source/blender/blenfont/BLF_api.h
@@ -74,6 +74,7 @@ void BLF_size(int fontid, int size, int dpi);
void BLF_color4ubv(int fontid, const unsigned char rgba[4]);
void BLF_color3ubv(int fontid, const unsigned char rgb[3]);
void BLF_color3ubv_alpha(int fontid, const unsigned char rgb[3], unsigned char alpha);
+void BLF_color4ub(int fontid, unsigned char r, unsigned char g, unsigned char b, unsigned char alpha);
void BLF_color3ub(int fontid, unsigned char r, unsigned char g, unsigned char b);
void BLF_color4f(int fontid, float r, float g, float b, float a);
void BLF_color4fv(int fontid, const float rgba[4]);
diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c
index b0e0cdac407..aeb69ff4b72 100644
--- a/source/blender/blenfont/intern/blf.c
+++ b/source/blender/blenfont/intern/blf.c
@@ -513,6 +513,18 @@ void BLF_color3ub(int fontid, unsigned char r, unsigned char g, unsigned char b)
}
}
+void BLF_color4ub(int fontid, unsigned char r, unsigned char g, unsigned char b, unsigned char alpha)
+{
+ FontBLF *font = blf_get(fontid);
+
+ if (font) {
+ font->color[0] = r;
+ font->color[1] = g;
+ font->color[2] = b;
+ font->color[3] = alpha;
+ }
+}
+
void BLF_color4fv(int fontid, const float rgba[4])
{
FontBLF *font = blf_get(fontid);
diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h
index a41d9582add..b36530c3db0 100644
--- a/source/blender/blenkernel/BKE_image.h
+++ b/source/blender/blenkernel/BKE_image.h
@@ -45,6 +45,7 @@ struct Scene;
struct Object;
struct ImageFormatData;
struct ImagePool;
+struct ImageTile;
struct Main;
struct ReportList;
struct RenderResult;
@@ -134,6 +135,7 @@ struct RenderResult;
#define IMA_SRC_MOVIE 3
#define IMA_SRC_GENERATED 4
#define IMA_SRC_VIEWER 5
+#define IMA_SRC_TILED 6
/* ima->type, how to handle/generate it */
#define IMA_TYPE_IMAGE 0
@@ -194,7 +196,7 @@ struct Image *BKE_image_load_exists(struct Main *bmain, const char *filepath);
/* adds image, adds ibuf, generates color or pattern */
struct Image *BKE_image_add_generated(
struct Main *bmain, unsigned int width, unsigned int height, const char *name,
- int depth, int floatbuf, short gen_type, const float color[4], const bool stereo3d);
+ int depth, int floatbuf, short gen_type, const float color[4], const bool stereo3d, bool tiled);
/* adds image from imbuf, owns imbuf */
struct Image *BKE_image_add_from_imbuf(struct Main *bmain, struct ImBuf *ibuf, const char *name);
@@ -271,6 +273,19 @@ bool BKE_image_has_alpha(struct Image *image);
/* check if texture has gpu texture code */
bool BKE_image_has_opengl_texture(struct Image *ima);
+/* get tile index for tiled images */
+void BKE_image_get_tile_label(struct Image *ima, struct ImageTile *tile, char *label, int len_label);
+
+struct ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *label);
+bool BKE_image_remove_tile(struct Image *ima, struct ImageTile *tile);
+
+bool BKE_image_fill_tile(struct Image *ima, struct ImageTile *tile, int width, int height, const float color[4], int gen_type);
+
+struct ImageTile *BKE_image_get_tile(struct Image *ima, int tile_number);
+struct ImageTile *BKE_image_get_tile_from_iuser(struct Image *ima, struct ImageUser *iuser);
+
+int BKE_image_get_tile_from_pos(struct Image *ima, const float uv[2], float new_uv[2], float ofs[2]);
+
void BKE_image_get_size(struct Image *image, struct ImageUser *iuser, int *width, int *height);
void BKE_image_get_size_fl(struct Image *image, struct ImageUser *iuser, float size[2]);
void BKE_image_get_aspect(struct Image *image, float *aspx, float *aspy);
@@ -281,14 +296,15 @@ void BKE_image_buf_fill_checker(unsigned char *rect, float *rect_float, int heig
void BKE_image_buf_fill_checker_color(unsigned char *rect, float *rect_float, int height, int width);
/* Cycles hookup */
-unsigned char *BKE_image_get_pixels_for_frame(struct Image *image, int frame);
-float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame);
+unsigned char *BKE_image_get_pixels_for_frame(struct Image *image, int frame, int tile);
+float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame, int tile);
/* Guess offset for the first frame in the sequence */
int BKE_image_sequence_guess_offset(struct Image *image);
bool BKE_image_has_anim(struct Image *image);
bool BKE_image_has_packedfile(struct Image *image);
bool BKE_image_is_animated(struct Image *image);
+bool BKE_image_has_multiple_ibufs(struct Image *image);
bool BKE_image_is_dirty(struct Image *image);
void BKE_image_file_format_set(struct Image *image, int ftype, const struct ImbFormatOptions *options);
bool BKE_image_has_loaded_ibuf(struct Image *image);
diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c
index 34f54704f75..ab859ae7c8e 100644
--- a/source/blender/blenkernel/intern/bpath.c
+++ b/source/blender/blenkernel/intern/bpath.c
@@ -435,7 +435,7 @@ void BKE_bpath_traverse_id(Main *bmain, ID *id, BPathVisitor visit_cb, const int
Image *ima;
ima = (Image *)id;
if (BKE_image_has_packedfile(ima) == false || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) {
- if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE)) {
+ if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) {
if (rewrite_path_fixed(ima->name, visit_cb, absbase, bpath_user_data)) {
if (flag & BKE_BPATH_TRAVERSE_RELOAD_EDITED) {
if (!BKE_image_has_packedfile(ima) &&
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index aedbd0673c2..806e7123244 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -91,6 +91,7 @@
#include "RE_pipeline.h"
#include "GPU_draw.h"
+#include "GPU_texture.h"
#include "BLI_sys_types.h" // for intptr_t support
@@ -111,9 +112,9 @@ static void image_add_view(Image *ima, const char *viewname, const char *filepat
/* max int, to indicate we don't store sequences in ibuf */
#define IMA_NO_INDEX 0x7FEFEFEF
-/* quick lookup: supports 1 million frames, thousand passes */
-#define IMA_MAKE_INDEX(frame, index) (((frame) << 10) + (index))
-#define IMA_INDEX_FRAME(index) ((index) >> 10)
+/* quick lookup: supports 1 million entries, thousand passes */
+#define IMA_MAKE_INDEX(entry, index) (((entry) << 10) + (index))
+#define IMA_INDEX_ENTRY(index) ((index) >> 10)
#if 0
#define IMA_INDEX_PASS(index) (index & ~1023)
#endif
@@ -142,7 +143,7 @@ static void imagecache_keydata(void *userkey, int *framenr, int *proxy, int *ren
{
ImageCacheKey *key = userkey;
- *framenr = IMA_INDEX_FRAME(key->index);
+ *framenr = IMA_INDEX_ENTRY(key->index);
*proxy = IMB_PROXY_NONE;
*render_flags = 0;
}
@@ -165,6 +166,16 @@ static void imagecache_put(Image *image, int index, ImBuf *ibuf)
IMB_moviecache_put(image->cache, &key, ibuf);
}
+static void imagecache_remove(Image *image, int index)
+{
+ if (!image->cache)
+ return;
+
+ ImageCacheKey key;
+ key.index = index;
+ IMB_moviecache_remove(image->cache, &key);
+}
+
static struct ImBuf *imagecache_get(Image *image, int index)
{
if (image->cache) {
@@ -257,7 +268,9 @@ void BKE_image_free_buffers_ex(Image *ima, bool do_lock)
GPU_free_image(ima);
}
- ima->ok = IMA_OK;
+ LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) {
+ tile->ok = IMA_OK;
+ }
if (do_lock) {
BLI_spin_unlock(&image_spin);
@@ -290,6 +303,8 @@ void BKE_image_free(Image *ima)
BKE_icon_id_delete(&ima->id);
BKE_previewimg_free(&ima->preview);
+
+ BLI_freelistN(&ima->tiles);
}
/* only image block itself */
@@ -297,8 +312,6 @@ static void image_init(Image *ima, short source, short type)
{
BLI_assert(MEMCMP_STRUCT_OFS_IS_ZERO(ima, id));
- ima->ok = IMA_OK;
-
ima->aspx = ima->aspy = 1.0;
ima->gen_x = 1024; ima->gen_y = 1024;
ima->gen_type = IMA_GENTYPE_GRID;
@@ -309,6 +322,10 @@ static void image_init(Image *ima, short source, short type)
if (source == IMA_SRC_VIEWER)
ima->flag |= IMA_VIEW_AS_RENDER;
+ ImageTile *tile = MEM_callocN(sizeof(ImageTile), "Image Tiles");
+ tile->ok = IMA_OK;
+ BLI_addtail(&ima->tiles, tile);
+
if (type == IMA_TYPE_R_RESULT) {
for (int i = 0; i < 8; i++) {
BKE_image_add_renderslot(ima, NULL);
@@ -338,33 +355,40 @@ static Image *image_alloc(Main *bmain, const char *name, short source, short typ
return ima;
}
-/* Get the ibuf from an image cache by it's index and frame.
+/* Get the ibuf from an image cache by it's index and entry.
* Local use here only.
*
* Returns referenced image buffer if it exists, callee is to
* call IMB_freeImBuf to de-reference the image buffer after
* it's done handling it.
*/
-static ImBuf *image_get_cached_ibuf_for_index_frame(Image *ima, int index, int frame)
+static ImBuf *image_get_cached_ibuf_for_index_entry(Image *ima, int index, int entry)
{
if (index != IMA_NO_INDEX) {
- index = IMA_MAKE_INDEX(frame, index);
+ index = IMA_MAKE_INDEX(entry, index);
}
return imagecache_get(ima, index);
}
/* no ima->ibuf anymore, but listbase */
-static void image_assign_ibuf(Image *ima, ImBuf *ibuf, int index, int frame)
+static void image_assign_ibuf(Image *ima, ImBuf *ibuf, int index, int entry)
{
if (ibuf) {
if (index != IMA_NO_INDEX)
- index = IMA_MAKE_INDEX(frame, index);
+ index = IMA_MAKE_INDEX(entry, index);
imagecache_put(ima, index, ibuf);
}
}
+static void image_remove_ibuf(Image *ima, int index, int entry)
+{
+ if (index != IMA_NO_INDEX)
+ index = IMA_MAKE_INDEX(entry, index);
+ imagecache_remove(ima, index);
+}
+
static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src)
{
const ImagePackedFile *imapf_src;
@@ -408,9 +432,11 @@ void BKE_image_copy_data(Main *UNUSED(bmain), Image *ima_dst, const Image *ima_s
}
BLI_listbase_clear(&ima_dst->anims);
-
- for (int i = 0; i < TEXTARGET_COUNT; i++) {
- ima_dst->gputexture[i] = NULL;
+ BLI_duplicatelist(&ima_dst->tiles, &ima_src->tiles);
+ LISTBASE_FOREACH(ImageTile*, tile, &ima_dst->tiles) {
+ for (int j = 0; j < TEXTARGET_COUNT; j++) {
+ tile->gputexture[j] = NULL;
+ }
}
if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0) {
@@ -476,14 +502,69 @@ bool BKE_image_scale(Image *image, int width, int height)
bool BKE_image_has_opengl_texture(Image *ima)
{
- for (int i = 0; i < TEXTARGET_COUNT; i++) {
- if (ima->gputexture[i]) {
- return true;
+ LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) {
+ for (int i = 0; i < TEXTARGET_COUNT; i++) {
+ if (tile->gputexture[i]) {
+ return true;
+ }
}
}
return false;
}
+ImageTile *BKE_image_get_tile(Image *ima, int tile_number)
+{
+ if (!ima) return NULL;
+ if (tile_number < 0) return NULL;
+
+ /* First tile is guaranteed to have number zero. */
+ if (tile_number == 0)
+ return ima->tiles.first;
+
+ if (ima->source != IMA_SRC_TILED) return NULL;
+
+ LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) {
+ if (tile->tile_number == tile_number) {
+ return tile;
+ }
+ }
+
+ return NULL;
+}
+
+ImageTile *BKE_image_get_tile_from_iuser(Image *ima, ImageUser *iuser)
+{
+ return BKE_image_get_tile(ima, iuser? iuser->tile : 0);
+}
+
+int BKE_image_get_tile_from_pos(struct Image *ima, const float uv[2], float new_uv[2], float ofs[2])
+{
+ float local_ofs[2];
+ if (!ofs) {
+ ofs = local_ofs;
+ }
+
+ copy_v2_v2(new_uv, uv);
+ zero_v2(ofs);
+
+ if ((ima->source != IMA_SRC_TILED) || uv[0] < 0.0f || uv[1] < 0.0f || uv[0] >= 10.0f) {
+ return 0;
+ }
+
+ int ix = (int) uv[0];
+ int iy = (int) uv[1];
+ int tile_number = 10*iy + ix;
+
+ if (BKE_image_get_tile(ima, tile_number) == NULL) {
+ return 0;
+ }
+ ofs[0] = ix;
+ ofs[1] = iy;
+ sub_v2_v2(new_uv, ofs);
+
+ return tile_number;
+}
+
static void image_init_color_management(Image *ima)
{
ImBuf *ibuf;
@@ -568,8 +649,10 @@ Image *BKE_image_load_exists_ex(Main *bmain, const char *filepath, bool *r_exist
(ima->id.us == 0))
{
id_us_plus(&ima->id); /* officially should not, it doesn't link here! */
- if (ima->ok == 0)
- ima->ok = IMA_OK;
+ LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) {
+ if (tile->ok == 0)
+ tile->ok = IMA_OK;
+ }
if (r_exists)
*r_exists = true;
return ima;
@@ -649,10 +732,16 @@ static ImBuf *add_ibuf_size(unsigned int width, unsigned int height, const char
/* adds new image block, creates ImBuf and initializes color */
Image *BKE_image_add_generated(
Main *bmain, unsigned int width, unsigned int height, const char *name,
- int depth, int floatbuf, short gen_type, const float color[4], const bool stereo3d)
+ int depth, int floatbuf, short gen_type, const float color[4], const bool stereo3d, const bool tiled)
{
/* on save, type is changed to FILE in editsima.c */
- Image *ima = image_alloc(bmain, name, IMA_SRC_GENERATED, IMA_TYPE_UV_TEST);
+ Image *ima;
+ if (tiled) {
+ ima = image_alloc(bmain, name, IMA_SRC_TILED, IMA_TYPE_IMAGE);
+ }
+ else {
+ ima = image_alloc(bmain, name, IMA_SRC_GENERATED, IMA_TYPE_UV_TEST);
+ }
if (ima) {
int view_id;
@@ -669,7 +758,7 @@ Image *BKE_image_add_generated(
for (view_id = 0; view_id < 2; view_id++) {
ImBuf *ibuf;
ibuf = add_ibuf_size(width, height, ima->name, depth, floatbuf, gen_type, color, &ima->colorspace_settings);
- image_assign_ibuf(ima, ibuf, stereo3d ? view_id : IMA_NO_INDEX, 0);
+ image_assign_ibuf(ima, ibuf, stereo3d ? view_id : (tiled ? 0 : IMA_NO_INDEX), 0);
/* image_assign_ibuf puts buffer to the cache, which increments user counter. */
IMB_freeImBuf(ibuf);
@@ -678,7 +767,8 @@ Image *BKE_image_add_generated(
image_add_view(ima, names[view_id], "");
}
- ima->ok = IMA_OK_LOADED;
+ ImageTile *tile = BKE_image_get_tile(ima, 0);
+ tile->ok = IMA_OK_LOADED;
}
return ima;
@@ -701,7 +791,8 @@ Image *BKE_image_add_from_imbuf(Main *bmain, ImBuf *ibuf, const char *name)
if (ima) {
STRNCPY(ima->name, ibuf->name);
image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0);
- ima->ok = IMA_OK_LOADED;
+ ImageTile *tile = BKE_image_get_tile(ima, 0);
+ tile->ok = IMA_OK_LOADED;
}
return ima;
@@ -718,7 +809,7 @@ static void image_memorypack_multiview(Image *ima)
image_free_packedfiles(ima);
for (i = 0, iv = ima->views.first; iv; iv = iv->next, i++) {
- ImBuf *ibuf = image_get_cached_ibuf_for_index_frame(ima, i, 0);
+ ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, i, 0);
ibuf->ftype = IMB_FTYPE_PNG;
ibuf->planes = R_IMF_PLANES_RGBA;
@@ -773,7 +864,7 @@ void BKE_image_memorypack(Image *ima)
return;
}
- ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0);
+ ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0);
if (ibuf == NULL)
return;
@@ -972,7 +1063,7 @@ static bool imagecache_check_free_anim(ImBuf *ibuf, void *UNUSED(userkey), void
int except_frame = *(int *)userdata;
return (ibuf->userflags & IB_BITMAPDIRTY) == 0 &&
(ibuf->index != IMA_NO_INDEX) &&
- (except_frame != IMA_INDEX_FRAME(ibuf->index));
+ (except_frame != IMA_INDEX_ENTRY(ibuf->index));
}
/* except_frame is weak, only works for seqs without offset... */
@@ -987,6 +1078,8 @@ void BKE_image_free_anim_ibufs(Image *ima, int except_frame)
void BKE_image_all_free_anim_ibufs(Main *bmain, int cfra)
{
+ /* Free all image buffers from frames other than cfra, for movies
+ * and image sequences. */
Image *ima;
for (ima = bmain->image.first; ima; ima = ima->id.next)
@@ -2628,6 +2721,26 @@ void BKE_image_init_imageuser(Image *ima, ImageUser *iuser)
image_init_imageuser(ima, iuser);
}
+static void image_free_tile(Image *ima, ImageTile *tile)
+{
+ for (int t = 0; t < TEXTARGET_COUNT; t++) {
+ if (tile->gputexture[t]) {
+ GPU_texture_free(tile->gputexture[t]);
+ tile->gputexture[t] = NULL;
+ }
+ }
+
+ if (BKE_image_is_multiview(ima)) {
+ const int totviews = BLI_listbase_count(&ima->views);
+ for (int i = 0; i < totviews; i++) {
+ image_remove_ibuf(ima, i, tile->tile_number);
+ }
+ }
+ else {
+ image_remove_ibuf(ima, 0, tile->tile_number);
+ }
+}
+
void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
{
if (ima == NULL)
@@ -2653,7 +2766,7 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
if (ima->source == IMA_SRC_GENERATED) {
if (ima->gen_x == 0 || ima->gen_y == 0) {
- ImBuf *ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0);
+ ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0);
if (ibuf) {
ima->gen_x = ibuf->x;
ima->gen_y = ibuf->y;
@@ -2671,6 +2784,17 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
ima->name[0] = '\0';
}
+ if (ima->source != IMA_SRC_TILED) {
+ /* Free all but the first tile. */
+ ImageTile *base_tile = BKE_image_get_tile(ima, 0);
+ for (ImageTile *tile = base_tile->next; tile; tile = tile->next) {
+ image_free_tile(ima, tile);
+ MEM_freeN(tile);
+ }
+ base_tile->next = NULL;
+ ima->tiles.last = base_tile;
+ }
+
#if 0
/* force reload on first use, but not for multilayer, that makes nodes and buttons in ui drawing fail */
if (ima->type != IMA_TYPE_MULTILAYER)
@@ -2686,7 +2810,10 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
BKE_image_free_buffers(ima);
#endif
- ima->ok = 1;
+ LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) {
+ tile->ok = 1;
+ }
+
if (iuser)
iuser->ok = 1;
@@ -2736,7 +2863,7 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
case IMA_SIGNAL_USER_NEW_IMAGE:
if (iuser) {
iuser->ok = 1;
- if (ima->source == IMA_SRC_FILE || ima->source == IMA_SRC_SEQUENCE) {
+ if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) {
if (ima->type == IMA_TYPE_MULTILAYER) {
image_init_imageuser(ima, iuser);
}
@@ -2746,7 +2873,9 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
case IMA_SIGNAL_COLORMANAGE:
BKE_image_free_buffers(ima);
- ima->ok = 1;
+ LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) {
+ tile->ok = 1;
+ }
if (iuser)
iuser->ok = 1;
@@ -2864,12 +2993,95 @@ void BKE_image_multiview_index(Image *ima, ImageUser *iuser)
}
}
+void BKE_image_get_tile_label(Image *ima, ImageTile *tile, char *label, int len_label)
+{
+ label[0] = '\0';
+ if (!ima || !tile) {
+ return;
+ }
+
+ if (tile->label[0])
+ BLI_strncpy(label, tile->label, len_label);
+ else
+ BLI_snprintf(label, len_label, "%d", 1001 + tile->tile_number);
+}
+
+ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *label)
+{
+ if (ima->source != IMA_SRC_TILED) {
+ return NULL;
+ }
+
+ LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) {
+ if (tile->tile_number == tile_number)
+ return NULL; /* TODO(lukas): Maybe return existing? */
+ }
+
+ ImageTile *tile = MEM_callocN(sizeof(ImageTile), "image new tile");
+ tile->ok = 1;
+ tile->tile_number = tile_number;
+ BLI_addtail(&ima->tiles, tile);
+
+ if (label) {
+ BLI_strncpy(tile->label, label, sizeof(tile->label));
+ }
+
+ return tile;
+}
+
+bool BKE_image_remove_tile(struct Image *ima, ImageTile *tile)
+{
+ if (!ima || !tile || ima->source != IMA_SRC_TILED) {
+ return false;
+ }
+
+ if (tile == ima->tiles.first) {
+ /* Can't remove first tile. */
+ return false;
+ }
+
+ image_free_tile(ima, tile);
+ BLI_remlink(&ima->renderslots, tile);
+ MEM_freeN(tile);
+
+ return true;
+}
+
+bool BKE_image_fill_tile(struct Image *ima, ImageTile *tile, int width, int height, const float color[4], int gen_type)
+{
+ if (!ima || !tile || ima->source != IMA_SRC_TILED) {
+ return false;
+ }
+
+ image_free_tile(ima, tile);
+
+ ImageUser iuser = {NULL};
+ iuser.ok = true;
+ ImBuf *main_ibuf = image_acquire_ibuf(ima, &iuser, NULL);
+ if (!main_ibuf) {
+ return false;
+ }
+ int planes = main_ibuf->planes;
+ bool is_float = (main_ibuf->rect_float != NULL);
+ BKE_image_release_ibuf(ima, main_ibuf, NULL);
+
+ ImBuf *tile_ibuf = add_ibuf_size(width, height, ima->name, planes, is_float, gen_type, color, &ima->colorspace_settings);
+
+ if (tile_ibuf) {
+ image_assign_ibuf(ima, tile_ibuf, 0, tile->tile_number);
+ BKE_image_release_ibuf(ima, tile_ibuf, NULL);
+ tile->ok = 1;
+ return true;
+ }
+ return false;
+}
+
/* if layer or pass changes, we need an index for the imbufs list */
/* note it is called for rendered results, but it doesnt use the index! */
/* and because rendered results use fake layer/passes, don't correct for wrong indices here */
bool BKE_image_is_multilayer(Image *ima)
{
- if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE)) {
+ if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) {
if (ima->type == IMA_TYPE_MULTILAYER) {
return true;
}
@@ -2957,7 +3169,7 @@ void BKE_image_release_renderresult(Scene *scene, Image *ima)
bool BKE_image_is_openexr(struct Image *ima)
{
#ifdef WITH_OPENEXR
- if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE)) {
+ if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) {
return BLI_path_extension_check(ima->name, ".exr");
}
#else
@@ -3059,7 +3271,7 @@ static void image_create_multilayer(Image *ima, ImBuf *ibuf, int framenr)
#endif /* WITH_OPENEXR */
/* common stuff to do with images after loading */
-static void image_initialize_after_load(Image *ima, ImBuf *UNUSED(ibuf))
+static void image_initialize_after_load(Image *ima, ImageUser *iuser, ImBuf *UNUSED(ibuf))
{
/* Preview is NULL when it has never been used as an icon before.
* Never handle previews/icons outside of main thread. */
@@ -3070,8 +3282,8 @@ static void image_initialize_after_load(Image *ima, ImBuf *UNUSED(ibuf))
/* timer */
BKE_image_tag_time(ima);
- ima->ok = IMA_OK_LOADED;
-
+ ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser);
+ tile->ok = IMA_OK_LOADED;
}
static int imbuf_alpha_flags_for_image(Image *ima)
@@ -3154,19 +3366,23 @@ static ImBuf *load_sequence_single(Image *ima, ImageUser *iuser, int frame, cons
}
}
else {
- image_initialize_after_load(ima, ibuf);
+ image_initialize_after_load(ima, iuser, ibuf);
*r_assign = true;
}
#else
- image_initialize_after_load(ima, ibuf);
+ image_initialize_after_load(ima, iuser, ibuf);
*r_assign = true;
#endif
}
+ else {
+ ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser);
+ tile->ok = 0;
+ }
return ibuf;
}
-static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame)
+static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int entry, int frame)
{
struct ImBuf *ibuf = NULL;
const bool is_multiview = BKE_image_is_multiview(ima);
@@ -3176,7 +3392,7 @@ static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame)
if (!is_multiview) {
ibuf = load_sequence_single(ima, iuser, frame, 0, &assign);
if (assign) {
- image_assign_ibuf(ima, ibuf, 0, frame);
+ image_assign_ibuf(ima, ibuf, 0, entry);
}
}
else {
@@ -3197,7 +3413,7 @@ static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame)
if (assign) {
for (i = 0; i < totviews; i++) {
- image_assign_ibuf(ima, ibuf_arr[i], i, frame);
+ image_assign_ibuf(ima, ibuf_arr[i], i, entry);
}
}
@@ -3215,9 +3431,10 @@ static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame)
return ibuf;
}
-static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int frame)
+static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int entry, int frame)
{
struct ImBuf *ibuf = NULL;
+ ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser);
/* either we load from RenderResult, or we have to load a new one */
@@ -3233,7 +3450,7 @@ static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int f
ima->rr = NULL;
}
- ibuf = image_load_sequence_file(ima, iuser, frame);
+ ibuf = image_load_sequence_file(ima, iuser, entry, frame);
if (ibuf) { /* actually an error */
ima->type = IMA_TYPE_IMAGE;
@@ -3252,17 +3469,17 @@ static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int f
ibuf->mall = IB_rectfloat;
ibuf->channels = rpass->channels;
- image_initialize_after_load(ima, ibuf);
- image_assign_ibuf(ima, ibuf, iuser ? iuser->multi_index : 0, frame);
+ image_initialize_after_load(ima, iuser, ibuf);
+ image_assign_ibuf(ima, ibuf, iuser ? iuser->multi_index : 0, entry);
}
// else printf("pass not found\n");
}
else
- ima->ok = 0;
+ tile->ok = 0;
if (iuser)
- iuser->ok = ima->ok;
+ iuser->ok = tile->ok;
return ibuf;
}
@@ -3274,6 +3491,8 @@ static ImBuf *load_movie_single(Image *ima, ImageUser *iuser, int frame, const i
ia = BLI_findlink(&ima->anims, view_id);
+ ImageTile *tile = BKE_image_get_tile(ima, 0);
+
if (ia->anim == NULL) {
char str[FILE_MAX];
int flags = IB_rect;
@@ -3312,13 +3531,13 @@ static ImBuf *load_movie_single(Image *ima, ImageUser *iuser, int frame, const i
IMB_PROXY_NONE));
if (ibuf) {
- image_initialize_after_load(ima, ibuf);
+ image_initialize_after_load(ima, iuser, ibuf);
}
else
- ima->ok = 0;
+ tile->ok = 0;
}
else
- ima->ok = 0;
+ tile->ok = 0;
return ibuf;
}
@@ -3329,6 +3548,7 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame)
const bool is_multiview = BKE_image_is_multiview(ima);
const int totfiles = image_num_files(ima);
int i;
+ ImageTile *tile = BKE_image_get_tile(ima, 0);
if (totfiles != BLI_listbase_count_at_most(&ima->anims, totfiles + 1)) {
image_free_anims(ima);
@@ -3362,7 +3582,7 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame)
image_assign_ibuf(ima, ibuf_arr[i], i, frame);
}
else {
- ima->ok = 0;
+ tile->ok = 0;
}
}
@@ -3381,7 +3601,7 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame)
}
if (iuser)
- iuser->ok = ima->ok;
+ iuser->ok = tile->ok;
return ibuf;
}
@@ -3447,7 +3667,7 @@ static ImBuf *load_image_single(
else
#endif
{
- image_initialize_after_load(ima, ibuf);
+ image_initialize_after_load(ima, iuser, ibuf);
*r_assign = true;
/* check if the image is a font image... */
@@ -3464,7 +3684,8 @@ static ImBuf *load_image_single(
}
}
else {
- ima->ok = 0;
+ ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser);
+ tile->ok = 0;
}
return ibuf;
@@ -3537,8 +3758,10 @@ static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra)
MEM_freeN(ibuf_arr);
}
- if (iuser)
- iuser->ok = ima->ok;
+ if (iuser) {
+ ImageTile *tile = BKE_image_get_tile(ima, 0);
+ iuser->ok = tile->ok;
+ }
return ibuf;
}
@@ -3560,7 +3783,7 @@ static ImBuf *image_get_ibuf_multilayer(Image *ima, ImageUser *iuser)
if (rpass) {
ibuf = IMB_allocImBuf(ima->rr->rectx, ima->rr->recty, 32, 0);
- image_initialize_after_load(ima, ibuf);
+ image_initialize_after_load(ima, iuser, ibuf);
ibuf->rect_float = rpass->rect;
ibuf->flags |= IB_rectfloat;
@@ -3570,10 +3793,11 @@ static ImBuf *image_get_ibuf_multilayer(Image *ima, ImageUser *iuser)
}
}
+ ImageTile *tile = BKE_image_get_tile(ima, 0);
if (ibuf == NULL)
- ima->ok = 0;
+ tile->ok = 0;
if (iuser)
- iuser->ok = ima->ok;
+ iuser->ok = tile->ok;
return ibuf;
}
@@ -3695,7 +3919,7 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **r_loc
}
}
- ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0);
+ ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0);
/* make ibuf if needed, and initialize it */
if (ibuf == NULL) {
@@ -3770,7 +3994,8 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **r_loc
ibuf->dither = dither;
- ima->ok = IMA_OK_LOADED;
+ ImageTile *tile = BKE_image_get_tile(ima, 0);
+ tile->ok = IMA_OK_LOADED;
return ibuf;
}
@@ -3779,7 +4004,7 @@ static int image_get_multiview_index(Image *ima, ImageUser *iuser)
{
const bool is_multilayer = BKE_image_is_multilayer(ima);
const bool is_backdrop = (ima->source == IMA_SRC_VIEWER) && (ima->type == IMA_TYPE_COMPOSITE) && (iuser == NULL);
- int index = BKE_image_is_animated(ima) ? 0 : IMA_NO_INDEX;
+ int index = BKE_image_has_multiple_ibufs(ima) ? 0 : IMA_NO_INDEX;
if (is_multilayer) {
return iuser ? iuser->multi_index : index;
@@ -3797,24 +4022,27 @@ static int image_get_multiview_index(Image *ima, ImageUser *iuser)
return index;
}
-static void image_get_frame_and_index(Image *ima, ImageUser *iuser, int *r_frame, int *r_index)
+static void image_get_entry_and_index(Image *ima, ImageUser *iuser, int *r_entry, int *r_index)
{
- int frame = 0, index = image_get_multiview_index(ima, iuser);
+ int entry = 0, index = image_get_multiview_index(ima, iuser);
/* see if we already have an appropriate ibuf, with image source and type */
if (ima->source == IMA_SRC_MOVIE) {
- frame = iuser ? iuser->framenr : ima->lastframe;
+ entry = iuser ? iuser->framenr : ima->lastframe;
}
else if (ima->source == IMA_SRC_SEQUENCE) {
if (ima->type == IMA_TYPE_IMAGE) {
- frame = iuser ? iuser->framenr : ima->lastframe;
+ entry = iuser ? iuser->framenr : ima->lastframe;
}
else if (ima->type == IMA_TYPE_MULTILAYER) {
- frame = iuser ? iuser->framenr : ima->lastframe;
+ entry = iuser ? iuser->framenr : ima->lastframe;
}
}
+ else if (ima->source == IMA_SRC_TILED) {
+ entry = iuser? iuser->tile : 0;
+ }
- *r_frame = frame;
+ *r_entry = entry;
*r_index = index;
}
@@ -3824,54 +4052,70 @@ static void image_get_frame_and_index(Image *ima, ImageUser *iuser, int *r_frame
* call IMB_freeImBuf to de-reference the image buffer after
* it's done handling it.
*/
-static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_frame, int *r_index)
+static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_entry, int *r_index)
{
ImBuf *ibuf = NULL;
- int frame = 0, index = image_get_multiview_index(ima, iuser);
+ int entry = 0, index = image_get_multiview_index(ima, iuser);
/* see if we already have an appropriate ibuf, with image source and type */
if (ima->source == IMA_SRC_MOVIE) {
- frame = iuser ? iuser->framenr : ima->lastframe;
- ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame);
+ entry = iuser ? iuser->framenr : ima->lastframe;
+ ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry);
/* XXX temp stuff? */
- if (ima->lastframe != frame)
+ if (ima->lastframe != entry)
ima->tpageflag |= IMA_TPAGE_REFRESH;
- ima->lastframe = frame;
+ ima->lastframe = entry;
}
else if (ima->source == IMA_SRC_SEQUENCE) {
if (ima->type == IMA_TYPE_IMAGE) {
- frame = iuser ? iuser->framenr : ima->lastframe;
- ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame);
+ entry = iuser ? iuser->framenr : ima->lastframe;
+ ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry);
/* XXX temp stuff? */
- if (ima->lastframe != frame) {
+ if (ima->lastframe != entry) {
ima->tpageflag |= IMA_TPAGE_REFRESH;
}
- ima->lastframe = frame;
+ ima->lastframe = entry;
/* counter the fact that image is set as invalid when loading a frame
* that is not in the cache (through image_acquire_ibuf for instance),
* yet we have valid frames in the cache loaded */
if (ibuf) {
- ima->ok = IMA_OK_LOADED;
+ ImageTile *tile = BKE_image_get_tile(ima, 0);
+ tile->ok = IMA_OK_LOADED;
if (iuser)
- iuser->ok = ima->ok;
+ iuser->ok = tile->ok;
}
}
else if (ima->type == IMA_TYPE_MULTILAYER) {
- frame = iuser ? iuser->framenr : ima->lastframe;
- ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame);
+ entry = iuser ? iuser->framenr : ima->lastframe;
+ ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry);
+ }
+ }
+ else if (ima->source == IMA_SRC_TILED) {
+ if (ELEM(ima->type, IMA_TYPE_IMAGE, IMA_TYPE_MULTILAYER)) {
+ entry = iuser? iuser->tile : 0;
+ ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry);
+
+ if ((ima->type == IMA_TYPE_IMAGE) && ibuf) {
+ ImageTile *tile = BKE_image_get_tile(ima, entry);
+ tile->ok = IMA_OK_LOADED;
+
+ /* iuser->ok is useless for tiled images because iuser->tile changes all the time. */
+ if (iuser)
+ iuser->ok = 1;
+ }
}
}
else if (ima->source == IMA_SRC_FILE) {
if (ima->type == IMA_TYPE_IMAGE)
- ibuf = image_get_cached_ibuf_for_index_frame(ima, index, 0);
+ ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0);
else if (ima->type == IMA_TYPE_MULTILAYER)
- ibuf = image_get_cached_ibuf_for_index_frame(ima, index, 0);
+ ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0);
}
else if (ima->source == IMA_SRC_GENERATED) {
- ibuf = image_get_cached_ibuf_for_index_frame(ima, index, 0);
+ ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0);
}
else if (ima->source == IMA_SRC_VIEWER) {
/* always verify entirely, not that this shouldn't happen
@@ -3879,8 +4123,8 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_frame,
* a big bottleneck */
}
- if (r_frame)
- *r_frame = frame;
+ if (r_entry)
+ *r_entry = entry;
if (r_index)
*r_index = index;
@@ -3893,11 +4137,17 @@ BLI_INLINE bool image_quick_test(Image *ima, ImageUser *iuser)
if (ima == NULL)
return false;
+ ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser);
+
if (iuser) {
if (iuser->ok == 0)
return false;
+ if (ima->source == IMA_SRC_TILED) {
+ if (tile->ok == 0)
+ return false;
+ }
}
- else if (ima->ok == 0)
+ else if (tile->ok == 0)
return false;
return true;
@@ -3910,7 +4160,7 @@ BLI_INLINE bool image_quick_test(Image *ima, ImageUser *iuser)
static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock)
{
ImBuf *ibuf = NULL;
- int frame = 0, index = 0;
+ int entry = 0, index = 0;
if (r_lock)
*r_lock = NULL;
@@ -3919,29 +4169,40 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock)
if (!image_quick_test(ima, iuser))
return NULL;
- ibuf = image_get_cached_ibuf(ima, iuser, &frame, &index);
+ ibuf = image_get_cached_ibuf(ima, iuser, &entry, &index);
if (ibuf == NULL) {
/* we are sure we have to load the ibuf, using source and type */
if (ima->source == IMA_SRC_MOVIE) {
/* source is from single file, use flipbook to store ibuf */
- ibuf = image_load_movie_file(ima, iuser, frame);
+ ibuf = image_load_movie_file(ima, iuser, entry);
}
else if (ima->source == IMA_SRC_SEQUENCE) {
if (ima->type == IMA_TYPE_IMAGE) {
/* regular files, ibufs in flipbook, allows saving */
- ibuf = image_load_sequence_file(ima, iuser, frame);
+ ibuf = image_load_sequence_file(ima, iuser, entry, entry);
+ }
+ /* no else; on load the ima type can change */
+ if (ima->type == IMA_TYPE_MULTILAYER) {
+ /* only 1 layer/pass stored in imbufs, no exrhandle anim storage, no saving */
+ ibuf = image_load_sequence_multilayer(ima, iuser, entry, entry);
+ }
+ }
+ else if (ima->source == IMA_SRC_TILED) {
+ if (ima->type == IMA_TYPE_IMAGE) {
+ /* regular files, ibufs in flipbook, allows saving */
+ ibuf = image_load_sequence_file(ima, iuser, entry, 0);
}
/* no else; on load the ima type can change */
if (ima->type == IMA_TYPE_MULTILAYER) {
/* only 1 layer/pass stored in imbufs, no exrhandle anim storage, no saving */
- ibuf = image_load_sequence_multilayer(ima, iuser, frame);
+ ibuf = image_load_sequence_multilayer(ima, iuser, entry, 0);
}
}
else if (ima->source == IMA_SRC_FILE) {
if (ima->type == IMA_TYPE_IMAGE)
- ibuf = image_load_image_file(ima, iuser, frame); /* cfra only for '#', this global is OK */
+ ibuf = image_load_image_file(ima, iuser, entry); /* cfra only for '#', this global is OK */
/* no else; on load the ima type can change */
if (ima->type == IMA_TYPE_MULTILAYER)
/* keeps render result, stores ibufs in listbase, allows saving */
@@ -3956,7 +4217,8 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock)
ibuf = add_ibuf_size(ima->gen_x, ima->gen_y, ima->name, ima->gen_depth, (ima->gen_flag & IMA_GEN_FLOAT) != 0, ima->gen_type,
ima->gen_color, &ima->colorspace_settings);
image_assign_ibuf(ima, ibuf, index, 0);
- ima->ok = IMA_OK_LOADED;
+ ImageTile *tile = BKE_image_get_tile(ima, 0);
+ tile->ok = IMA_OK_LOADED;
}
else if (ima->source == IMA_SRC_VIEWER) {
if (ima->type == IMA_TYPE_R_RESULT) {
@@ -3972,14 +4234,14 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock)
*r_lock = ima;
/* XXX anim play for viewer nodes not yet supported */
- frame = 0; // XXX iuser ? iuser->framenr : 0;
- ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame);
+ entry = 0; // XXX iuser ? iuser->framenr : 0;
+ ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry);
if (!ibuf) {
/* Composite Viewer, all handled in compositor */
/* fake ibuf, will be filled in compositor */
ibuf = IMB_allocImBuf(256, 256, 32, IB_rect | IB_rectfloat);
- image_assign_ibuf(ima, ibuf, index, frame);
+ image_assign_ibuf(ima, ibuf, index, entry);
}
}
}
@@ -4066,7 +4328,7 @@ typedef struct ImagePoolEntry {
Image *image;
ImBuf *ibuf;
int index;
- int frame;
+ int entry;
} ImagePoolEntry;
typedef struct ImagePool {
@@ -4100,16 +4362,16 @@ void BKE_image_pool_free(ImagePool *pool)
MEM_freeN(pool);
}
-BLI_INLINE ImBuf *image_pool_find_entry(ImagePool *pool, Image *image, int frame, int index, bool *found)
+BLI_INLINE ImBuf *image_pool_find_entry(ImagePool *pool, Image *image, int entry, int index, bool *found)
{
- ImagePoolEntry *entry;
+ ImagePoolEntry *pool_entry;
*found = false;
- for (entry = pool->image_buffers.first; entry; entry = entry->next) {
- if (entry->image == image && entry->frame == frame && entry->index == index) {
+ for (pool_entry = pool->image_buffers.first; pool_entry; pool_entry = pool_entry->next) {
+ if (pool_entry->image == image && pool_entry->entry == entry && pool_entry->index == index) {
*found = true;
- return entry->ibuf;
+ return pool_entry->ibuf;
}
}
@@ -4119,7 +4381,7 @@ BLI_INLINE ImBuf *image_pool_find_entry(ImagePool *pool, Image *image, int frame
ImBuf *BKE_image_pool_acquire_ibuf(Image *ima, ImageUser *iuser, ImagePool *pool)
{
ImBuf *ibuf;
- int index, frame;
+ int index, entry;
bool found;
if (!image_quick_test(ima, iuser))
@@ -4130,31 +4392,31 @@ ImBuf *BKE_image_pool_acquire_ibuf(Image *ima, ImageUser *iuser, ImagePool *pool
return BKE_image_acquire_ibuf(ima, iuser, NULL);
}
- image_get_frame_and_index(ima, iuser, &frame, &index);
+ image_get_entry_and_index(ima, iuser, &entry, &index);
- ibuf = image_pool_find_entry(pool, ima, frame, index, &found);
+ ibuf = image_pool_find_entry(pool, ima, entry, index, &found);
if (found)
return ibuf;
BLI_spin_lock(&image_spin);
- ibuf = image_pool_find_entry(pool, ima, frame, index, &found);
+ ibuf = image_pool_find_entry(pool, ima, entry, index, &found);
/* will also create entry even in cases image buffer failed to load,
* prevents trying to load the same buggy file multiple times
*/
if (!found) {
- ImagePoolEntry *entry;
+ ImagePoolEntry *pool_entry;
ibuf = image_acquire_ibuf(ima, iuser, NULL);
- entry = BLI_mempool_alloc(pool->memory_pool);
- entry->image = ima;
- entry->frame = frame;
- entry->index = index;
- entry->ibuf = ibuf;
+ pool_entry = BLI_mempool_alloc(pool->memory_pool);
+ pool_entry->image = ima;
+ pool_entry->entry = entry;
+ pool_entry->index = index;
+ pool_entry->ibuf = ibuf;
- BLI_addtail(&pool->image_buffers, entry);
+ BLI_addtail(&pool->image_buffers, pool_entry);
}
BLI_spin_unlock(&image_spin);
@@ -4289,13 +4551,20 @@ void BKE_image_user_file_path(ImageUser *iuser, Image *ima, char *filepath)
BLI_strncpy(filepath, ima->name, FILE_MAX);
}
- if (ima->source == IMA_SRC_SEQUENCE) {
+ if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) {
char head[FILE_MAX], tail[FILE_MAX];
unsigned short numlen;
- int frame = iuser ? iuser->framenr : ima->lastframe;
+
+ int index;
+ if (ima->source == IMA_SRC_SEQUENCE) {
+ index = iuser ? iuser->framenr : ima->lastframe;
+ }
+ else {
+ index = 1001 + (iuser ? iuser->tile : 0);
+ }
BLI_stringdec(filepath, head, tail, &numlen);
- BLI_stringenc(filepath, head, tail, numlen, frame);
+ BLI_stringenc(filepath, head, tail, numlen, index);
}
BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id));
@@ -4372,7 +4641,7 @@ void BKE_image_get_aspect(Image *image, float *aspx, float *aspy)
*aspy = 1.0f;
}
-unsigned char *BKE_image_get_pixels_for_frame(struct Image *image, int frame)
+unsigned char *BKE_image_get_pixels_for_frame(struct Image *image, int frame, int tile)
{
ImageUser iuser = {NULL};
void *lock;
@@ -4380,6 +4649,7 @@ unsigned char *BKE_image_get_pixels_for_frame(struct Image *image, int frame)
unsigned char *pixels = NULL;
iuser.framenr = frame;
+ iuser.tile = tile;
iuser.ok = true;
ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
@@ -4399,7 +4669,7 @@ unsigned char *BKE_image_get_pixels_for_frame(struct Image *image, int frame)
return pixels;
}
-float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame)
+float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame, int tile)
{
ImageUser iuser = {NULL};
void *lock;
@@ -4407,6 +4677,7 @@ float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame)
float *pixels = NULL;
iuser.framenr = frame;
+ iuser.tile = tile;
iuser.ok = true;
ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
@@ -4451,6 +4722,12 @@ bool BKE_image_is_animated(Image *image)
return ELEM(image->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE);
}
+/* Checks whether the image consists of multiple buffers. */
+bool BKE_image_has_multiple_ibufs(Image *image)
+{
+ return ELEM(image->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE, IMA_SRC_TILED);
+}
+
bool BKE_image_is_dirty(Image *image)
{
bool is_dirty = false;
diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c
index da455faaa86..cd596bfd26f 100644
--- a/source/blender/blenkernel/intern/packedFile.c
+++ b/source/blender/blenkernel/intern/packedFile.c
@@ -237,7 +237,7 @@ void packAll(Main *bmain, ReportList *reports, bool verbose)
BKE_image_packfiles(reports, ima, ID_BLEND_PATH(bmain, &ima->id));
tot ++;
}
- else if (BKE_image_is_animated(ima) && verbose) {
+ else if (BKE_image_has_multiple_ibufs(ima) && verbose) {
BKE_reportf(reports, RPT_WARNING, "Image '%s' skipped, movies and image sequences not supported",
ima->id.name + 2);
}
diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h
index ec16a6854c4..b391e77c31c 100644
--- a/source/blender/blenlib/BLI_math_vector.h
+++ b/source/blender/blenlib/BLI_math_vector.h
@@ -262,6 +262,8 @@ MINLINE bool equals_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED
MINLINE bool equals_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT;
MINLINE bool equals_v4v4(const float a[4], const float b[4]) ATTR_WARN_UNUSED_RESULT;
+MINLINE bool equals_v2v2_int(const int v1[2], const int v2[2]) ATTR_WARN_UNUSED_RESULT;
+
MINLINE bool compare_v2v2(const float a[2], const float b[2], const float limit) ATTR_WARN_UNUSED_RESULT;
MINLINE bool compare_v3v3(const float a[3], const float b[3], const float limit) ATTR_WARN_UNUSED_RESULT;
MINLINE bool compare_v4v4(const float a[4], const float b[4], const float limit) ATTR_WARN_UNUSED_RESULT;
diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c
index 189b94a6f13..8dd7c2ef945 100644
--- a/source/blender/blenlib/intern/math_vector_inline.c
+++ b/source/blender/blenlib/intern/math_vector_inline.c
@@ -1042,6 +1042,11 @@ MINLINE bool equals_v4v4(const float v1[4], const float v2[4])
return ((v1[0] == v2[0]) && (v1[1] == v2[1]) && (v1[2] == v2[2]) && (v1[3] == v2[3]));
}
+MINLINE bool equals_v2v2_int(const int v1[2], const int v2[2])
+{
+ return ((v1[0] == v2[0]) && (v1[1] == v2[1]));
+}
+
MINLINE bool compare_v2v2(const float v1[2], const float v2[2], const float limit)
{
return (compare_ff(v1[0], v2[0], limit) &&
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index f226693b989..91abcdf7a89 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -1677,21 +1677,24 @@ void blo_make_image_pointer_map(FileData *fd, Main *oldmain)
{
Image *ima = oldmain->image.first;
Scene *sce = oldmain->scene.first;
- int a;
fd->imamap = oldnewmap_new();
for (; ima; ima = ima->id.next) {
if (ima->cache)
oldnewmap_insert(fd->imamap, ima->cache, ima->cache, 0);
- for (a = 0; a < TEXTARGET_COUNT; a++)
- if (ima->gputexture[a])
- oldnewmap_insert(fd->imamap, ima->gputexture[a], ima->gputexture[a], 0);
if (ima->rr)
oldnewmap_insert(fd->imamap, ima->rr, ima->rr, 0);
- LISTBASE_FOREACH(RenderSlot *, slot, &ima->renderslots)
- if (slot->render)
- oldnewmap_insert(fd->imamap, slot->render, slot->render, 0);
+ LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) {
+ for (int a = 0; a < TEXTARGET_COUNT; a++) {
+ if (tile->gputexture[a]) {
+ oldnewmap_insert(fd->imamap, tile->gputexture[a], tile->gputexture[a], 0);
+ }
+ }
+ }
+ LISTBASE_FOREACH(RenderSlot *, slot, &ima->renderslots)
+ if (slot->render)
+ oldnewmap_insert(fd->imamap, slot->render, slot->render, 0);
}
for (; sce; sce = sce->id.next) {
if (sce->nodetree && sce->nodetree->previews) {
@@ -1711,10 +1714,9 @@ void blo_end_image_pointer_map(FileData *fd, Main *oldmain)
OldNew *entry = fd->imamap->entries;
Image *ima = oldmain->image.first;
Scene *sce = oldmain->scene.first;
- int i;
/* used entries were restored, so we put them to zero */
- for (i = 0; i < fd->imamap->nentries; i++, entry++) {
+ for (int i = 0; i < fd->imamap->nentries; i++, entry++) {
if (entry->nr > 0)
entry->newp = NULL;
}
@@ -1723,16 +1725,23 @@ void blo_end_image_pointer_map(FileData *fd, Main *oldmain)
ima->cache = newimaadr(fd, ima->cache);
if (ima->cache == NULL) {
ima->tpageflag &= ~IMA_GLBIND_IS_DATA;
- for (i = 0; i < TEXTARGET_COUNT; i++) {
- ima->gputexture[i] = NULL;
- }
ima->rr = NULL;
+ LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) {
+ for (int j = 0; j < TEXTARGET_COUNT; j++) {
+ tile->gputexture[j] = NULL;
+ }
+ }
}
+
LISTBASE_FOREACH(RenderSlot *, slot, &ima->renderslots)
slot->render = newimaadr(fd, slot->render);
- for (i = 0; i < TEXTARGET_COUNT; i++)
- ima->gputexture[i] = newimaadr(fd, ima->gputexture[i]);
+ LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) {
+ for (int j = 0; j < TEXTARGET_COUNT; j++) {
+ tile->gputexture[j] = newimaadr(fd, tile->gputexture[j]);
+ }
+ }
+
ima->rr = newimaadr(fd, ima->rr);
}
for (; sce; sce = sce->id.next) {
@@ -3950,11 +3959,15 @@ static void direct_link_image(FileData *fd, Image *ima)
else
ima->cache = NULL;
+ link_list(fd, &(ima->tiles));
+
/* if not restored, we keep the binded opengl index */
if (!ima->cache) {
ima->tpageflag &= ~IMA_GLBIND_IS_DATA;
- for (int i = 0; i < TEXTARGET_COUNT; i++) {
- ima->gputexture[i] = NULL;
+ LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) {
+ for (int i = 0; i < TEXTARGET_COUNT; i++) {
+ tile->gputexture[i] = NULL;
+ }
}
ima->rr = NULL;
}
@@ -3989,7 +4002,10 @@ static void direct_link_image(FileData *fd, Image *ima)
BLI_listbase_clear(&ima->anims);
ima->preview = direct_link_preview_image(fd, ima->preview);
ima->stereo3d_format = newdataadr(fd, ima->stereo3d_format);
- ima->ok = 1;
+
+ LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) {
+ tile->ok = 1;
+ }
}
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index 39ceb527209..d10e9ff294a 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -1490,6 +1490,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+
if (!DNA_struct_elem_find(fd->filesdna, "SpaceAction", "char", "mode_prev")) {
for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
@@ -1640,6 +1641,28 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
BKE_screen_view3d_shading_init(&scene->display.shading);
}
}
+
+ if (!DNA_struct_elem_find(fd->filesdna, "Image", "short", "num_tiles")) {
+ for (Image *ima = bmain->image.first; ima; ima = ima->id.next) {
+ ImageTile *tile = MEM_callocN(sizeof(ImageTile), "Image Tiles");
+ tile->ok = 1;
+ BLI_addtail(&ima->tiles, tile);
+ }
+ }
+
+ if (!DNA_struct_elem_find(fd->filesdna, "SpaceImage", "int", "tile_grid_shape[2]")) {
+ for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
+ for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
+ for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
+ if (sl->spacetype == SPACE_IMAGE) {
+ SpaceImage *sima = (SpaceImage *)sl;
+ sima->tile_grid_shape[0] = 1;
+ sima->tile_grid_shape[1] = 1;
+ }
+ }
+ }
+ }
+ }
}
}
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 503f8b44ec3..ce2362dacc8 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -2210,6 +2210,8 @@ static void write_image(WriteData *wd, Image *ima)
}
writestruct(wd, DATA, Stereo3dFormat, 1, ima->stereo3d_format);
+ writelist(wd, DATA, ImageTile, &ima->tiles);
+
ima->packedfile = NULL;
writelist(wd, DATA, RenderSlot, &ima->renderslots);
diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cpp b/source/blender/compositor/operations/COM_ViewerOperation.cpp
index 51fd5d4dc76..2734069a449 100644
--- a/source/blender/compositor/operations/COM_ViewerOperation.cpp
+++ b/source/blender/compositor/operations/COM_ViewerOperation.cpp
@@ -155,7 +155,8 @@ void ViewerOperation::initImage()
/* zero size can happen if no image buffers exist to define a sensible resolution */
if (ibuf->x > 0 && ibuf->y > 0)
imb_addrectfloatImBuf(ibuf);
- ima->ok = IMA_OK_LOADED;
+ ImageTile *tile = BKE_image_get_tile(ima, 0);
+ tile->ok = IMA_OK_LOADED;
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
}
diff --git a/source/blender/draw/engines/workbench/workbench_deferred.c b/source/blender/draw/engines/workbench/workbench_deferred.c
index 23fbbe56c16..4692b026d76 100644
--- a/source/blender/draw/engines/workbench/workbench_deferred.c
+++ b/source/blender/draw/engines/workbench/workbench_deferred.c
@@ -32,6 +32,7 @@
#include "BLI_utildefines.h"
#include "BLI_rand.h"
+#include "BKE_image.h"
#include "BKE_node.h"
#include "BKE_modifier.h"
#include "BKE_particle.h"
@@ -568,7 +569,7 @@ void workbench_deferred_cache_init(WORKBENCH_Data *vedata)
}
static WORKBENCH_MaterialData *get_or_create_material_data(
- WORKBENCH_Data *vedata, Object *ob, Material *mat, Image *ima, int color_type)
+ WORKBENCH_Data *vedata, Object *ob, Material *mat, Image *ima, int color_type, int image_tile)
{
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_PassList *psl = vedata->psl;
@@ -583,6 +584,7 @@ static WORKBENCH_MaterialData *get_or_create_material_data(
material_template.object_id = OBJECT_ID_PASS_ENABLED(wpd) ? engine_object_data->object_id : 1;
material_template.color_type = color_type;
material_template.ima = ima;
+ material_template.image_tile = image_tile;
uint hash = workbench_material_get_hash(&material_template);
material = BLI_ghash_lookup(wpd->material_hash, SET_UINT_IN_POINTER(hash));
@@ -628,7 +630,7 @@ static void workbench_cache_populate_particles(WORKBENCH_Data *vedata, Object *o
Material *mat = give_current_material(ob, part->omat);
ED_object_get_active_image(ob, part->omat, &image, NULL, NULL, NULL);
int color_type = workbench_material_determine_color_type(wpd, image);
- WORKBENCH_MaterialData *material = get_or_create_material_data(vedata, ob, mat, image, color_type);
+ WORKBENCH_MaterialData *material = get_or_create_material_data(vedata, ob, mat, image, color_type, 0);
struct GPUShader *shader = (color_type != V3D_SHADING_TEXTURE_COLOR) ?
wpd->prepass_solid_hair_sh :
@@ -680,23 +682,32 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob)
bool is_drawn = false;
if (!is_sculpt_mode && TEXTURE_DRAWING_ENABLED(wpd) && ELEM(ob->type, OB_MESH)) {
const Mesh *me = ob->data;
- if (me->mloopuv) {
+ if (me->mloopuv && me->totcol) {
const int materials_len = MAX2(1, (is_sculpt_mode ? 1 : ob->totcol));
- struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len);
- struct GPUBatch **geom_array = me->totcol ? DRW_cache_mesh_surface_texpaint_get(ob) : NULL;
- if (materials_len > 0 && geom_array) {
- for (int i = 0; i < materials_len; i++) {
- if (geom_array[i] == NULL) {
- continue;
- }
- Material *mat = give_current_material(ob, i + 1);
- Image *image;
- ED_object_get_active_image(ob, i + 1, &image, NULL, NULL, NULL);
- int color_type = workbench_material_determine_color_type(wpd, image);
- material = get_or_create_material_data(vedata, ob, mat, image, color_type);
- DRW_shgroup_call_object_add(material->shgrp, geom_array[i], ob);
+ bool *is_tiled = MEM_mallocN(sizeof(bool)*materials_len, "workbench deferred is tiled");
+ for(int i = 0; i < materials_len; i++) {
+ Image *image;
+ ED_object_get_active_image(ob, i + 1, &image, NULL, NULL, NULL);
+ is_tiled[i] = image && (image->source == IMA_SRC_TILED);
+ }
+
+ int num_batches = 0;
+ struct TexpaintCacheBatch *geom_batches = DRW_cache_mesh_surface_texpaint_get(ob, is_tiled, &num_batches);
+
+ for (int i = 0; i < num_batches; i++) {
+ if (geom_batches[i].batch == NULL) {
+ continue;
}
+
+ int matid = geom_batches[i].material + 1;
+ Material *mat = give_current_material(ob, matid);
+ Image *image;
+ ED_object_get_active_image(ob, matid, &image, NULL, NULL, NULL);
+ int color_type = workbench_material_determine_color_type(wpd, image);
+ material = get_or_create_material_data(vedata, ob, mat, image, color_type, geom_batches[i].tile);
+ DRW_shgroup_call_object_add(material->shgrp, geom_batches[i].batch, ob);
+
is_drawn = true;
}
}
@@ -708,7 +719,7 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob)
/* No material split needed */
struct GPUBatch *geom = DRW_cache_object_surface_get(ob);
if (geom) {
- material = get_or_create_material_data(vedata, ob, NULL, NULL, wpd->shading.color_type);
+ material = get_or_create_material_data(vedata, ob, NULL, NULL, wpd->shading.color_type, 0);
if (is_sculpt_mode) {
DRW_shgroup_call_sculpt_add(material->shgrp, ob, ob->obmat);
}
@@ -733,7 +744,7 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob)
}
Material *mat = give_current_material(ob, i + 1);
- material = get_or_create_material_data(vedata, ob, mat, NULL, V3D_SHADING_MATERIAL_COLOR);
+ material = get_or_create_material_data(vedata, ob, mat, NULL, V3D_SHADING_MATERIAL_COLOR, 0);
if (is_sculpt_mode) {
DRW_shgroup_call_sculpt_add(material->shgrp, ob, ob->obmat);
}
diff --git a/source/blender/draw/engines/workbench/workbench_forward.c b/source/blender/draw/engines/workbench/workbench_forward.c
index c81ecc492db..e4a9c8abe26 100644
--- a/source/blender/draw/engines/workbench/workbench_forward.c
+++ b/source/blender/draw/engines/workbench/workbench_forward.c
@@ -31,6 +31,7 @@
#include "BLI_dynstr.h"
#include "BLI_utildefines.h"
+#include "BKE_image.h"
#include "BKE_node.h"
#include "BKE_particle.h"
#include "BKE_modifier.h"
@@ -137,7 +138,7 @@ static void workbench_init_object_data(DrawData *dd)
}
static WORKBENCH_MaterialData *get_or_create_material_data(
- WORKBENCH_Data *vedata, Object *ob, Material *mat, Image *ima, int color_type)
+ WORKBENCH_Data *vedata, Object *ob, Material *mat, Image *ima, int color_type, int image_tile)
{
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_PassList *psl = vedata->psl;
@@ -153,6 +154,7 @@ static WORKBENCH_MaterialData *get_or_create_material_data(
material_template.object_id = OBJECT_ID_PASS_ENABLED(wpd) ? engine_object_data->object_id : 1;
material_template.color_type = color_type;
material_template.ima = ima;
+ material_template.image_tile = image_tile;
uint hash = workbench_material_get_hash(&material_template);
material = BLI_ghash_lookup(wpd->material_hash, SET_UINT_IN_POINTER(hash));
@@ -181,9 +183,12 @@ static WORKBENCH_MaterialData *get_or_create_material_data(
/* Depth */
if (workbench_material_determine_color_type(wpd, material->ima) == V3D_SHADING_TEXTURE_COLOR) {
+ ImageUser iuser = {NULL};
+ iuser.ok = 1;
+ iuser.tile = material->image_tile;
material->shgrp_object_outline = DRW_shgroup_create(
e_data.object_outline_texture_sh, psl->object_outline_pass);
- GPUTexture *tex = GPU_texture_from_blender(material->ima, NULL, GL_TEXTURE_2D, false, 0.0f);
+ GPUTexture *tex = GPU_texture_from_blender(material->ima, &iuser, GL_TEXTURE_2D, false, 0.0f);
DRW_shgroup_uniform_texture(material->shgrp_object_outline, "image", tex);
}
else {
@@ -420,7 +425,7 @@ static void workbench_forward_cache_populate_particles(WORKBENCH_Data *vedata, O
Material *mat = give_current_material(ob, part->omat);
ED_object_get_active_image(ob, part->omat, &image, NULL, NULL, NULL);
int color_type = workbench_material_determine_color_type(wpd, image);
- WORKBENCH_MaterialData *material = get_or_create_material_data(vedata, ob, mat, image, color_type);
+ WORKBENCH_MaterialData *material = get_or_create_material_data(vedata, ob, mat, image, color_type, 0);
struct GPUShader *shader = (color_type != V3D_SHADING_TEXTURE_COLOR)
? wpd->transparent_accum_hair_sh
@@ -490,31 +495,39 @@ void workbench_forward_cache_populate(WORKBENCH_Data *vedata, Object *ob)
const Mesh *me = ob->data;
if (me->mloopuv) {
const int materials_len = MAX2(1, (is_sculpt_mode ? 1 : ob->totcol));
- struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len);
- struct GPUBatch **geom_array = me->totcol ? DRW_cache_mesh_surface_texpaint_get(ob) : NULL;
- if (materials_len > 0 && geom_array) {
- for (int i = 0; i < materials_len; i++) {
- if (geom_array[i] == NULL) {
- continue;
- }
- Material *mat = give_current_material(ob, i + 1);
- Image *image;
- ED_object_get_active_image(ob, i + 1, &image, NULL, NULL, NULL);
- /* use OB_SOLID when no texture could be determined */
+ bool *is_tiled = MEM_mallocN(sizeof(bool)*materials_len, "workbench forward is tiled");
+ for(int i = 0; i < materials_len; i++) {
+ Image *image;
+ ED_object_get_active_image(ob, i + 1, &image, NULL, NULL, NULL);
+ is_tiled[i] = image && (image->source == IMA_SRC_TILED);
+ }
- int color_type = wpd->shading.color_type;
- if (color_type == V3D_SHADING_TEXTURE_COLOR) {
- /* use OB_SOLID when no texture could be determined */
- if (image == NULL) {
- color_type = V3D_SHADING_MATERIAL_COLOR;
- }
- }
+ int num_batches = 0;
+ struct TexpaintCacheBatch *geom_batches = DRW_cache_mesh_surface_texpaint_get(ob, is_tiled, &num_batches);
- material = get_or_create_material_data(vedata, ob, mat, image, color_type);
- DRW_shgroup_call_object_add(material->shgrp_object_outline, geom_array[i], ob);
- DRW_shgroup_call_object_add(material->shgrp, geom_array[i], ob);
+ for (int i = 0; i < num_batches; i++) {
+ if (geom_batches[i].batch == NULL) {
+ continue;
}
+
+ int matid = geom_batches[i].material + 1;
+ Material *mat = give_current_material(ob, matid);
+ Image *image;
+ ED_object_get_active_image(ob, matid, &image, NULL, NULL, NULL);
+ /* use OB_SOLID when no texture could be determined */
+
+ int color_type = wpd->shading.color_type;
+ if (color_type == V3D_SHADING_TEXTURE_COLOR) {
+ /* use OB_SOLID when no texture could be determined */
+ if (image == NULL) {
+ color_type = V3D_SHADING_MATERIAL_COLOR;
+ }
+ }
+
+ material = get_or_create_material_data(vedata, ob, mat, image, color_type, geom_batches[i].tile);
+ DRW_shgroup_call_object_add(material->shgrp_object_outline, geom_batches[i].batch, ob);
+ DRW_shgroup_call_object_add(material->shgrp, geom_batches[i].batch, ob);
is_drawn = true;
}
}
@@ -526,7 +539,7 @@ void workbench_forward_cache_populate(WORKBENCH_Data *vedata, Object *ob)
/* No material split needed */
struct GPUBatch *geom = DRW_cache_object_surface_get(ob);
if (geom) {
- material = get_or_create_material_data(vedata, ob, NULL, NULL, wpd->shading.color_type);
+ material = get_or_create_material_data(vedata, ob, NULL, NULL, wpd->shading.color_type, 0);
if (is_sculpt_mode) {
DRW_shgroup_call_sculpt_add(material->shgrp_object_outline, ob, ob->obmat);
DRW_shgroup_call_sculpt_add(material->shgrp, ob, ob->obmat);
@@ -553,7 +566,7 @@ void workbench_forward_cache_populate(WORKBENCH_Data *vedata, Object *ob)
}
Material *mat = give_current_material(ob, i + 1);
- material = get_or_create_material_data(vedata, ob, mat, NULL, V3D_SHADING_MATERIAL_COLOR);
+ material = get_or_create_material_data(vedata, ob, mat, NULL, V3D_SHADING_MATERIAL_COLOR, 0);
if (is_sculpt_mode) {
DRW_shgroup_call_sculpt_add(material->shgrp_object_outline, ob, ob->obmat);
DRW_shgroup_call_sculpt_add(material->shgrp, ob, ob->obmat);
diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c
index 54e8aa1c3b3..156cfb9f051 100644
--- a/source/blender/draw/engines/workbench/workbench_materials.c
+++ b/source/blender/draw/engines/workbench/workbench_materials.c
@@ -131,6 +131,7 @@ uint workbench_material_get_hash(WORKBENCH_MaterialData *material_template)
/* add texture reference */
if (material_template->ima) {
result += BLI_ghashutil_inthash_p_murmur(material_template->ima);
+ result += BLI_ghashutil_inthash(material_template->image_tile);
}
return result;
@@ -185,7 +186,10 @@ void workbench_material_shgroup_uniform(
WORKBENCH_PrivateData *wpd, DRWShadingGroup *grp, WORKBENCH_MaterialData *material)
{
if (workbench_material_determine_color_type(wpd, material->ima) == V3D_SHADING_TEXTURE_COLOR) {
- GPUTexture *tex = GPU_texture_from_blender(material->ima, NULL, GL_TEXTURE_2D, false, 0.0f);
+ ImageUser iuser = {NULL};
+ iuser.ok = 1;
+ iuser.tile = material->image_tile;
+ GPUTexture *tex = GPU_texture_from_blender(material->ima, &iuser, GL_TEXTURE_2D, false, 0.0f);
DRW_shgroup_uniform_texture(grp, "image", tex);
}
else {
@@ -205,4 +209,5 @@ void workbench_material_copy(WORKBENCH_MaterialData *dest_material, const WORKBE
copy_v4_v4(dest_material->specular_color, source_material->specular_color);
dest_material->roughness = source_material->roughness;
dest_material->ima = source_material->ima;
+ dest_material->image_tile = source_material->image_tile;
}
diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h
index 99d1b7060fd..795695c8a08 100644
--- a/source/blender/draw/engines/workbench/workbench_private.h
+++ b/source/blender/draw/engines/workbench/workbench_private.h
@@ -205,6 +205,7 @@ typedef struct WORKBENCH_MaterialData {
int object_id;
int color_type;
Image *ima;
+ int image_tile;
/* Linked shgroup for drawing */
DRWShadingGroup *shgrp;
diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c
index dff863b11bb..bd8a549dbca 100644
--- a/source/blender/draw/intern/draw_cache.c
+++ b/source/blender/draw/intern/draw_cache.c
@@ -2870,20 +2870,20 @@ GPUBatch **DRW_cache_mesh_surface_shaded_get(
}
/* Return list of batches */
-GPUBatch **DRW_cache_mesh_surface_texpaint_get(Object *ob)
+TexpaintCacheBatch *DRW_cache_mesh_surface_texpaint_get(Object *ob, bool *is_tiled, int *num_batches)
{
BLI_assert(ob->type == OB_MESH);
Mesh *me = ob->data;
- return DRW_mesh_batch_cache_get_surface_texpaint(me);
+ return DRW_mesh_batch_cache_get_surface_texpaint(me, is_tiled, num_batches);
}
-GPUBatch *DRW_cache_mesh_surface_texpaint_single_get(Object *ob)
+TexpaintCacheBatch *DRW_cache_mesh_surface_texpaint_single_get(Object *ob, bool is_tiled, int *num_batches)
{
BLI_assert(ob->type == OB_MESH);
Mesh *me = ob->data;
- return DRW_mesh_batch_cache_get_surface_texpaint_single(me);
+ return DRW_mesh_batch_cache_get_surface_texpaint_single(me, is_tiled, num_batches);
}
GPUBatch *DRW_cache_mesh_surface_verts_get(Object *ob)
diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h
index 129c0252f30..b43f93b5826 100644
--- a/source/blender/draw/intern/draw_cache.h
+++ b/source/blender/draw/intern/draw_cache.h
@@ -32,6 +32,12 @@ struct ModifierData;
struct Object;
struct PTCacheEdit;
+typedef struct TexpaintCacheBatch {
+ struct GPUBatch *batch;
+ int material;
+ int tile;
+} TexpaintCacheBatch;
+
void DRW_shape_cache_free(void);
void DRW_shape_cache_reset(void);
@@ -142,8 +148,8 @@ struct GPUBatch *DRW_cache_mesh_verts_weight_overlay_get(struct Object *ob);
struct GPUBatch **DRW_cache_mesh_surface_shaded_get(
struct Object *ob, struct GPUMaterial **gpumat_array, uint gpumat_array_len,
char **auto_layer_names, int **auto_layer_is_srgb, int *auto_layer_count);
-struct GPUBatch **DRW_cache_mesh_surface_texpaint_get(struct Object *ob);
-struct GPUBatch *DRW_cache_mesh_surface_texpaint_single_get(struct Object *ob);
+struct TexpaintCacheBatch *DRW_cache_mesh_surface_texpaint_get(struct Object *ob, bool *is_tiled, int *num_batches);
+struct TexpaintCacheBatch *DRW_cache_mesh_surface_texpaint_single_get(struct Object *ob, bool is_tiled, int *num_batches);
void DRW_cache_mesh_sculpt_coords_ensure(struct Object *ob);
diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h
index eeb7b1c41ee..173674a7a1f 100644
--- a/source/blender/draw/intern/draw_cache_impl.h
+++ b/source/blender/draw/intern/draw_cache_impl.h
@@ -97,8 +97,8 @@ struct GPUBatch *DRW_lattice_batch_cache_get_overlay_verts(struct Lattice *lt);
struct GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(
struct Mesh *me, struct GPUMaterial **gpumat_array, uint gpumat_array_len,
char **auto_layer_names, int **auto_layer_is_srgb, int *auto_layer_count);
-struct GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(struct Mesh *me);
-struct GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(struct Mesh *me);
+struct TexpaintCacheBatch *DRW_mesh_batch_cache_get_surface_texpaint(struct Mesh *me, bool *is_tiled, int *num_batches);
+struct TexpaintCacheBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(struct Mesh *me, bool is_tiled, int *num_batches);
struct GPUBatch *DRW_mesh_batch_cache_get_weight_overlay_edges(struct Mesh *me, bool use_wire, bool use_sel);
struct GPUBatch *DRW_mesh_batch_cache_get_weight_overlay_faces(struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_weight_overlay_verts(struct Mesh *me);
diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c
index 5ca1c65054c..1f0a6ed7ede 100644
--- a/source/blender/draw/intern/draw_cache_impl_mesh.c
+++ b/source/blender/draw/intern/draw_cache_impl_mesh.c
@@ -1558,6 +1558,8 @@ typedef struct MeshBatchCache {
GPUVertBuf *pos_with_normals;
GPUVertBuf *tri_aligned_uv; /* Active UV layer (mloopuv) */
+ int *tri_tiles;
+
/**
* Other uses are all positions or loose elements.
* This stores all visible elements, needed for selection.
@@ -1599,12 +1601,16 @@ typedef struct MeshBatchCache {
GPUVertFormat shaded_triangles_format;
GPUVertBuf *shaded_triangles_data;
GPUIndexBuf **shaded_triangles_in_order;
+ GPUIndexBuf **shaded_triangles_in_order_tiled;
+ GPUIndexBuf **shaded_triangles_in_order_tiled_single;
GPUBatch **shaded_triangles;
/* Texture Paint.*/
/* per-texture batch */
- GPUBatch **texpaint_triangles;
- GPUBatch *texpaint_triangles_single;
+ int num_texpaint_batches;
+ int num_texpaint_batches_single;
+ TexpaintCacheBatch *texpaint_triangles;
+ TexpaintCacheBatch *texpaint_triangles_single;
/* Edit Cage Mesh buffers */
GPUVertBuf *ed_tri_pos;
@@ -1801,12 +1807,17 @@ static void mesh_batch_cache_clear_selective(Mesh *me, GPUVertBuf *vert)
}
MEM_SAFE_FREE(cache->shaded_triangles);
if (cache->texpaint_triangles) {
- for (int i = 0; i < cache->mat_len; ++i) {
- GPU_BATCH_DISCARD_SAFE(cache->texpaint_triangles[i]);
+ for (int i = 0; i < cache->num_texpaint_batches; ++i) {
+ GPU_BATCH_DISCARD_SAFE(cache->texpaint_triangles[i].batch);
}
}
MEM_SAFE_FREE(cache->texpaint_triangles);
- GPU_BATCH_DISCARD_SAFE(cache->texpaint_triangles_single);
+ if (cache->texpaint_triangles_single) {
+ for (int i = 0; i < cache->num_texpaint_batches_single; ++i) {
+ GPU_BATCH_DISCARD_SAFE(cache->texpaint_triangles_single[i].batch);
+ }
+ }
+ MEM_SAFE_FREE(cache->texpaint_triangles_single);
}
/* TODO: add the other ones if needed. */
else {
@@ -1859,6 +1870,7 @@ static void mesh_batch_cache_clear(Mesh *me)
GPU_BATCH_DISCARD_SAFE(cache->triangles_with_weights);
GPU_BATCH_DISCARD_SAFE(cache->triangles_with_vert_colors);
GPU_VERTBUF_DISCARD_SAFE(cache->tri_aligned_uv);
+ MEM_SAFE_FREE(cache->tri_tiles);
GPU_VERTBUF_DISCARD_SAFE(cache->ed_fcenter_pos_with_nor_and_sel);
GPU_VERTBUF_DISCARD_SAFE(cache->ed_edge_pos);
GPU_VERTBUF_DISCARD_SAFE(cache->ed_vert_pos);
@@ -1877,31 +1889,50 @@ static void mesh_batch_cache_clear(Mesh *me)
DRW_TEXTURE_FREE_SAFE(cache->edges_face_overlay_tx);
GPU_VERTBUF_DISCARD_SAFE(cache->shaded_triangles_data);
+
if (cache->shaded_triangles_in_order) {
for (int i = 0; i < cache->mat_len; ++i) {
GPU_INDEXBUF_DISCARD_SAFE(cache->shaded_triangles_in_order[i]);
}
}
+ MEM_SAFE_FREE(cache->shaded_triangles_in_order);
+
+ if (cache->shaded_triangles_in_order_tiled) {
+ for (int i = 0; i < cache->num_texpaint_batches; ++i) {
+ GPU_INDEXBUF_DISCARD_SAFE(cache->shaded_triangles_in_order_tiled[i]);
+ }
+ }
+ MEM_SAFE_FREE(cache->shaded_triangles_in_order_tiled);
+
+ if (cache->shaded_triangles_in_order_tiled_single) {
+ for (int i = 0; i < cache->num_texpaint_batches_single; ++i) {
+ GPU_INDEXBUF_DISCARD_SAFE(cache->shaded_triangles_in_order_tiled_single[i]);
+ }
+ }
+ MEM_SAFE_FREE(cache->shaded_triangles_in_order_tiled_single);
+
if (cache->shaded_triangles) {
for (int i = 0; i < cache->mat_len; ++i) {
GPU_BATCH_DISCARD_SAFE(cache->shaded_triangles[i]);
}
}
-
- MEM_SAFE_FREE(cache->shaded_triangles_in_order);
MEM_SAFE_FREE(cache->shaded_triangles);
MEM_SAFE_FREE(cache->auto_layer_names);
MEM_SAFE_FREE(cache->auto_layer_is_srgb);
if (cache->texpaint_triangles) {
- for (int i = 0; i < cache->mat_len; ++i) {
- GPU_BATCH_DISCARD_SAFE(cache->texpaint_triangles[i]);
+ for (int i = 0; i < cache->num_texpaint_batches; ++i) {
+ GPU_BATCH_DISCARD_SAFE(cache->texpaint_triangles[i].batch);
}
}
- MEM_SAFE_FREE(cache->texpaint_triangles);
-
- GPU_BATCH_DISCARD_SAFE(cache->texpaint_triangles_single);
+ MEM_SAFE_FREE(cache->texpaint_triangles_single);
+ if (cache->texpaint_triangles_single) {
+ for (int i = 0; i < cache->num_texpaint_batches_single; ++i) {
+ GPU_BATCH_DISCARD_SAFE(cache->texpaint_triangles_single[i].batch);
+ }
+ }
+ MEM_SAFE_FREE(cache->texpaint_triangles_single);
}
@@ -2143,8 +2174,17 @@ static GPUVertBuf *mesh_batch_cache_get_tri_shading_data(MeshRenderData *rdata,
return cache->shaded_triangles_data;
}
+static int get_tile_index_from_uv(const float *uv)
+{
+ int x = uv[0];
+ int y = uv[1];
+ CLAMP(x, 0, 9);
+ CLAMP(y, 0, 9);
+ return y*10 + x;
+}
+
static GPUVertBuf *mesh_batch_cache_get_tri_uv_active(
- MeshRenderData *rdata, MeshBatchCache *cache)
+ MeshRenderData *rdata, MeshBatchCache *cache, bool has_tiles, bool *is_tiled)
{
BLI_assert(rdata->types & (MR_DATATYPE_VERT | MR_DATATYPE_LOOPTRI | MR_DATATYPE_LOOP | MR_DATATYPE_LOOPUV));
@@ -2164,6 +2204,20 @@ static GPUVertBuf *mesh_batch_cache_get_tri_uv_active(
}
const int tri_len = mesh_render_data_looptri_len_get(rdata);
+ const int mat_len = mesh_render_data_mat_len_get(rdata);
+
+ if (is_tiled) {
+ for (int i = 0; i < mat_len; i++) {
+ if (is_tiled[i]) {
+ has_tiles = true;
+ break;
+ }
+ }
+ }
+
+ if (has_tiles) {
+ cache->tri_tiles = MEM_callocN(sizeof(int) * tri_len, "mesh cache tri tiles");
+ }
GPUVertBuf *vbo = cache->tri_aligned_uv = GPU_vertbuf_create_with_format(&format);
@@ -2184,14 +2238,18 @@ static GPUVertBuf *mesh_batch_cache_get_tri_uv_active(
if (BM_elem_flag_test(bm_looptri[0]->f, BM_ELEM_HIDDEN)) {
continue;
}
+ const float *elem = NULL;
for (uint t = 0; t < 3; t++) {
const BMLoop *loop = bm_looptri[t];
const int index = BM_elem_index_get(loop);
if (index != -1) {
- const float *elem = ((MLoopUV *)BM_ELEM_CD_GET_VOID_P(loop, layer_offset))->uv;
+ elem = ((MLoopUV *)BM_ELEM_CD_GET_VOID_P(loop, layer_offset))->uv;
GPU_vertbuf_attr_set(vbo, attr_id.uv, vidx++, elem);
}
}
+ if (elem && has_tiles) {
+ cache->tri_tiles[i] = get_tile_index_from_uv(elem);
+ }
}
}
else {
@@ -2201,6 +2259,9 @@ static GPUVertBuf *mesh_batch_cache_get_tri_uv_active(
GPU_vertbuf_attr_set(vbo, attr_id.uv, vidx++, mloopuv[mlt->tri[0]].uv);
GPU_vertbuf_attr_set(vbo, attr_id.uv, vidx++, mloopuv[mlt->tri[1]].uv);
GPU_vertbuf_attr_set(vbo, attr_id.uv, vidx++, mloopuv[mlt->tri[2]].uv);
+ if (has_tiles) {
+ cache->tri_tiles[i] = get_tile_index_from_uv(mloopuv[mlt->tri[2]].uv);
+ }
}
}
@@ -3564,22 +3625,47 @@ static GPUIndexBuf *mesh_batch_cache_get_loose_edges(MeshRenderData *rdata, Mesh
return cache->ledges_in_order;
}
-static GPUIndexBuf **mesh_batch_cache_get_triangles_in_order_split_by_material(
- MeshRenderData *rdata, MeshBatchCache *cache)
+static inline int get_batch_index_for_tri(bool single, bool texpaint, short mat, int mat_len, int *tri_tiles, int tri)
+{
+ if (single || (mat >= mat_len)) {
+ mat = 0;
+ }
+ if (texpaint) {
+ return 100*mat + tri_tiles[tri];
+ }
+ return mat;
+}
+
+static GPUIndexBuf **mesh_batch_cache_get_triangles_in_order_split(
+ MeshRenderData *rdata, MeshBatchCache *cache, bool single, bool texpaint, int **batch_mapping, int *batch_num)
{
BLI_assert(rdata->types & (MR_DATATYPE_VERT | MR_DATATYPE_POLY));
- if (cache->shaded_triangles_in_order == NULL) {
+ GPUIndexBuf*** shaded_triangles;
+ if (texpaint) {
+ shaded_triangles = single? &cache->shaded_triangles_in_order_tiled_single : &cache->shaded_triangles_in_order_tiled;
+ }
+ else {
+ shaded_triangles = &cache->shaded_triangles_in_order;
+ }
+
+ if (*shaded_triangles == NULL) {
const int poly_len = mesh_render_data_polys_len_get(rdata);
const int tri_len = mesh_render_data_looptri_len_get(rdata);
const int mat_len = mesh_render_data_mat_len_get(rdata);
- int *mat_tri_len = MEM_callocN(sizeof(*mat_tri_len) * mat_len, __func__);
- cache->shaded_triangles_in_order = MEM_callocN(sizeof(*cache->shaded_triangles) * mat_len, __func__);
- GPUIndexBufBuilder *elb = MEM_callocN(sizeof(*elb) * mat_len, __func__);
+ if (cache->tri_tiles == NULL) {
+ texpaint = false;
+ }
+
+ /* Triangles are split according to material and/or tile index.
+ * To do this, we first need to find the combinations that are actually used. */
+ int index_num = (single? 1 : mat_len) * (texpaint? 100 : 1);
+ int *batch_len = MEM_callocN(sizeof(int) * index_num, __func__);
/* Note that polygons (not triangles) are used here.
* This OK because result is _guaranteed_ to be the same. */
+ uint tri = 0;
if (rdata->edit_bmesh) {
BMesh *bm = rdata->edit_bmesh->bm;
BMIter fiter;
@@ -3587,26 +3673,67 @@ static GPUIndexBuf **mesh_batch_cache_get_triangles_in_order_split_by_material(
BM_ITER_MESH(efa, &fiter, bm, BM_FACES_OF_MESH) {
if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
- const short ma_id = efa->mat_nr < mat_len ? efa->mat_nr : 0;
- mat_tri_len[ma_id] += (efa->len - 2);
+ for(int j = 2; j < efa->len; j++, tri++) {
+ int index = get_batch_index_for_tri(single, texpaint, efa->mat_nr, mat_len, cache->tri_tiles, tri);
+ batch_len[index]++;
+ }
}
}
}
else {
for (uint i = 0; i < poly_len; i++) {
- const MPoly *mp = &rdata->mpoly[i]; ;
- const short ma_id = mp->mat_nr < mat_len ? mp->mat_nr : 0;
- mat_tri_len[ma_id] += (mp->totloop - 2);
+ const MPoly *mp = &rdata->mpoly[i];
+ for(int j = 2; j < mp->totloop; j++, tri++) {
+ int index = get_batch_index_for_tri(single, texpaint, mp->mat_nr, mat_len, cache->tri_tiles, tri);
+ batch_len[index]++;
+ }
}
}
+ /* Now, all combinations that appear in the mesh get an assigned batch index.
+ * Also, we need to know each batches' length, so the batch_len array is replaced
+ * with each batches' length (instead of each combinations' length).
+ * This can be done in one pass since batch_num is guaranteed to be less or equal to i.
+ */
+ int *index_to_batch = MEM_mallocN(sizeof(int) * index_num, __func__);
+ *batch_num = 0;
+ for (int i = 0; i < index_num; i++) {
+ if (batch_len[i] > 0) {
+ index_to_batch[i] = *batch_num;
+ batch_len[*batch_num] = batch_len[i];
+ (*batch_num)++;
+ }
+ else {
+ index_to_batch[i] = -1;
+ }
+ }
+
+ /* Return the mapping from batches to material/tile pairs to the caller. */
+ int *mapping = *batch_mapping = MEM_mallocN(sizeof(int) * 2 * (*batch_num), __func__);
+ for (int i = 0; i < index_num; i++) {
+ if (index_to_batch[i] >= 0) {
+ int batch = index_to_batch[i];
+ if (texpaint) {
+ mapping[2*batch + 0] = (i / 100);
+ mapping[2*batch + 1] = (i % 100);
+ }
+ else {
+ mapping[2*batch + 0] = i;
+ mapping[2*batch + 1] = 0;
+ }
+ }
+ }
+
+ *shaded_triangles = MEM_callocN(sizeof(*cache->shaded_triangles) * (*batch_num), __func__);
+ GPUIndexBufBuilder *elb = MEM_callocN(sizeof(*elb) * (*batch_num), __func__);
+
/* Init ELBs. */
- for (int i = 0; i < mat_len; ++i) {
- GPU_indexbuf_init(&elb[i], GPU_PRIM_TRIS, mat_tri_len[i], tri_len * 3);
+ for (int i = 0; i < *batch_num; ++i) {
+ GPU_indexbuf_init(&elb[i], GPU_PRIM_TRIS, batch_len[i], tri_len * 3);
}
/* Populate ELBs. */
- uint nidx = 0;
+ tri = 0;
if (rdata->edit_bmesh) {
BMesh *bm = rdata->edit_bmesh->bm;
BMIter fiter;
@@ -3614,10 +3741,9 @@ static GPUIndexBuf **mesh_batch_cache_get_triangles_in_order_split_by_material(
BM_ITER_MESH(efa, &fiter, bm, BM_FACES_OF_MESH) {
if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
- const short ma_id = efa->mat_nr < mat_len ? efa->mat_nr : 0;
- for (int j = 2; j < efa->len; j++) {
- GPU_indexbuf_add_tri_verts(&elb[ma_id], nidx + 0, nidx + 1, nidx + 2);
- nidx += 3;
+ for(int j = 2; j < efa->len; j++, tri++) {
+ int index = get_batch_index_for_tri(single, texpaint, efa->mat_nr, mat_len, cache->tri_tiles, tri);
+ GPU_indexbuf_add_tri_verts(&elb[index_to_batch[index]], 3*tri + 0, 3*tri + 1, 3*tri + 2);
}
}
}
@@ -3625,24 +3751,24 @@ static GPUIndexBuf **mesh_batch_cache_get_triangles_in_order_split_by_material(
else {
for (uint i = 0; i < poly_len; i++) {
const MPoly *mp = &rdata->mpoly[i]; ;
- const short ma_id = mp->mat_nr < mat_len ? mp->mat_nr : 0;
- for (int j = 2; j < mp->totloop; j++) {
- GPU_indexbuf_add_tri_verts(&elb[ma_id], nidx + 0, nidx + 1, nidx + 2);
- nidx += 3;
+ for(int j = 2; j < mp->totloop; j++, tri++) {
+ int index = get_batch_index_for_tri(single, texpaint, mp->mat_nr, mat_len, cache->tri_tiles, tri);
+ GPU_indexbuf_add_tri_verts(&elb[index_to_batch[index]], 3*tri + 0, 3*tri + 1, 3*tri + 2);
}
}
}
/* Build ELBs. */
- for (int i = 0; i < mat_len; ++i) {
- cache->shaded_triangles_in_order[i] = GPU_indexbuf_build(&elb[i]);
+ for (int i = 0; i < *batch_num; ++i) {
+ (*shaded_triangles)[i] = GPU_indexbuf_build(&elb[i]);
}
- MEM_freeN(mat_tri_len);
+ MEM_freeN(batch_len);
+ MEM_freeN(index_to_batch);
MEM_freeN(elb);
}
- return cache->shaded_triangles_in_order;
+ return *shaded_triangles;
}
static GPUVertBuf *mesh_create_edge_pos_with_sel(
@@ -4311,19 +4437,23 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(
cache->shaded_triangles = MEM_callocN(sizeof(*cache->shaded_triangles) * mat_len, __func__);
- GPUIndexBuf **el = mesh_batch_cache_get_triangles_in_order_split_by_material(rdata, cache);
+ int *batch_mapping;
+ int batch_num;
+ GPUIndexBuf **el = mesh_batch_cache_get_triangles_in_order_split(rdata, cache, false, false, &batch_mapping, &batch_num);
GPUVertBuf *vbo = mesh_batch_cache_get_tri_pos_and_normals(rdata, cache);
GPUVertBuf *vbo_shading = mesh_batch_cache_get_tri_shading_data(rdata, cache);
- for (int i = 0; i < mat_len; ++i) {
- cache->shaded_triangles[i] = GPU_batch_create(
+ for (int i = 0; i < batch_num; ++i) {
+ int mat = batch_mapping[2*i + 0];
+ cache->shaded_triangles[mat] = GPU_batch_create(
GPU_PRIM_TRIS, vbo, el[i]);
if (vbo_shading) {
- GPU_batch_vertbuf_add(cache->shaded_triangles[i], vbo_shading);
+ GPU_batch_vertbuf_add(cache->shaded_triangles[mat], vbo_shading);
}
}
+ MEM_SAFE_FREE(batch_mapping);
mesh_render_data_free(rdata);
}
@@ -4336,7 +4466,7 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(
return cache->shaded_triangles;
}
-GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(Mesh *me)
+TexpaintCacheBatch *DRW_mesh_batch_cache_get_surface_texpaint(Mesh *me, bool *is_tiled, int *num_batches)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
@@ -4346,28 +4476,33 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(Mesh *me)
MR_DATATYPE_VERT | MR_DATATYPE_LOOP | MR_DATATYPE_POLY | MR_DATATYPE_LOOPTRI | MR_DATATYPE_LOOPUV;
MeshRenderData *rdata = mesh_render_data_create(me, datatype);
- const int mat_len = mesh_render_data_mat_len_get(rdata);
+ GPUVertBuf *vbo_uv = mesh_batch_cache_get_tri_uv_active(rdata, cache, false, is_tiled);
+ GPUVertBuf *vbo = mesh_batch_cache_get_tri_pos_and_normals(rdata, cache);
- cache->texpaint_triangles = MEM_callocN(sizeof(*cache->texpaint_triangles) * mat_len, __func__);
+ int *batch_mapping;
+ GPUIndexBuf **el = mesh_batch_cache_get_triangles_in_order_split(rdata, cache, false, true, &batch_mapping, &cache->num_texpaint_batches);
- GPUIndexBuf **el = mesh_batch_cache_get_triangles_in_order_split_by_material(rdata, cache);
+ cache->texpaint_triangles = MEM_callocN(sizeof(*cache->texpaint_triangles) * cache->num_texpaint_batches, __func__);
- GPUVertBuf *vbo = mesh_batch_cache_get_tri_pos_and_normals(rdata, cache);
- for (int i = 0; i < mat_len; ++i) {
- cache->texpaint_triangles[i] = GPU_batch_create(
+ for (int i = 0; i < cache->num_texpaint_batches; ++i) {
+ cache->texpaint_triangles[i].batch = GPU_batch_create(
GPU_PRIM_TRIS, vbo, el[i]);
- GPUVertBuf *vbo_uv = mesh_batch_cache_get_tri_uv_active(rdata, cache);
if (vbo_uv) {
- GPU_batch_vertbuf_add(cache->texpaint_triangles[i], vbo_uv);
+ GPU_batch_vertbuf_add(cache->texpaint_triangles[i].batch, vbo_uv);
}
+ cache->texpaint_triangles[i].material = batch_mapping[2*i + 0];
+ cache->texpaint_triangles[i].tile = batch_mapping[2*i + 1];
}
+
+ MEM_SAFE_FREE(batch_mapping);
mesh_render_data_free(rdata);
}
+ *num_batches = cache->num_texpaint_batches;
return cache->texpaint_triangles;
}
-GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(Mesh *me)
+TexpaintCacheBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(Mesh *me, bool is_tiled, int *num_batches)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
@@ -4377,16 +4512,43 @@ GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(Mesh *me)
MR_DATATYPE_VERT | MR_DATATYPE_LOOP | MR_DATATYPE_POLY | MR_DATATYPE_LOOPTRI | MR_DATATYPE_LOOPUV;
MeshRenderData *rdata = mesh_render_data_create(me, datatype);
+ GPUVertBuf *vbo_uv = mesh_batch_cache_get_tri_uv_active(rdata, cache, is_tiled, NULL);
GPUVertBuf *vbo = mesh_batch_cache_get_tri_pos_and_normals(rdata, cache);
- cache->texpaint_triangles_single = GPU_batch_create(
- GPU_PRIM_TRIS, vbo, NULL);
- GPUVertBuf *vbo_uv = mesh_batch_cache_get_tri_uv_active(rdata, cache);
- if (vbo_uv) {
- GPU_batch_vertbuf_add(cache->texpaint_triangles_single, vbo_uv);
+ if (cache->tri_tiles) {
+ int *batch_mapping;
+ GPUIndexBuf **el = mesh_batch_cache_get_triangles_in_order_split(rdata, cache, true, true, &batch_mapping, &cache->num_texpaint_batches_single);
+
+ cache->texpaint_triangles_single = MEM_callocN(sizeof(*cache->texpaint_triangles) * cache->num_texpaint_batches_single, __func__);
+
+ for (int i = 0; i < cache->num_texpaint_batches_single; ++i) {
+ cache->texpaint_triangles[i].batch = GPU_batch_create(
+ GPU_PRIM_TRIS, vbo, el[i]);
+ if (vbo_uv) {
+ GPU_batch_vertbuf_add(cache->texpaint_triangles[i].batch, vbo_uv);
+ }
+ cache->texpaint_triangles[i].material = batch_mapping[2*i + 0];
+ cache->texpaint_triangles[i].tile = batch_mapping[2*i + 1];
+ }
+
+ MEM_SAFE_FREE(batch_mapping);
}
+ else {
+ cache->texpaint_triangles_single = MEM_callocN(sizeof(*cache->texpaint_triangles), __func__);
+ cache->texpaint_triangles_single[0].batch = GPU_batch_create(
+ GPU_PRIM_TRIS, vbo, NULL);
+ if (vbo_uv) {
+ GPU_batch_vertbuf_add(cache->texpaint_triangles_single[0].batch, vbo_uv);
+ }
+ cache->texpaint_triangles_single[0].material = 0;
+ cache->texpaint_triangles_single[0].tile = 0;
+ cache->num_texpaint_batches = 1;
+ }
+
mesh_render_data_free(rdata);
}
+
+ *num_batches = cache->num_texpaint_batches;
return cache->texpaint_triangles_single;
}
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index dccb869c133..bdc4504e278 100644
--- a/source/blender/draw/intern/draw_manager_data.c
+++ b/source/blender/draw/intern/draw_manager_data.c
@@ -766,9 +766,18 @@ static DRWShadingGroup *drw_shgroup_material_inputs(DRWShadingGroup *grp, struct
for (GPUInput *input = inputs->first; input; input = input->next) {
/* Textures */
if (input->ima) {
+ ImageUser *tex_iuser = input->iuser;
+
+ /* If there's no specified iuser but we need a different tile, create a temporary one. */
+ ImageUser iuser = {NULL};
+ iuser.ok = true;
+ iuser.tile = input->image_tile;
+ if (!tex_iuser && iuser.tile != 0)
+ tex_iuser = &iuser;
+
double time = 0.0; /* TODO make time variable */
GPUTexture *tex = GPU_texture_from_blender(
- input->ima, input->iuser, input->textarget, input->image_isdata, time);
+ input->ima, tex_iuser, input->textarget, input->image_isdata, time);
if (input->bindtex) {
DRW_shgroup_uniform_texture(grp, input->shadername, tex);
diff --git a/source/blender/draw/modes/paint_texture_mode.c b/source/blender/draw/modes/paint_texture_mode.c
index 8256bb4d0d7..1f1bf606d14 100644
--- a/source/blender/draw/modes/paint_texture_mode.c
+++ b/source/blender/draw/modes/paint_texture_mode.c
@@ -26,6 +26,8 @@
#include "DRW_engine.h"
#include "DRW_render.h"
+#include "BKE_image.h"
+
#include "BIF_gl.h"
/* If builtin shaders are needed */
@@ -118,7 +120,6 @@ typedef struct PAINT_TEXTURE_PrivateData {
/* This keeps the references of the shading groups for
* easy access in PAINT_TEXTURE_cache_populate() */
DRWShadingGroup *shgroup_fallback;
- DRWShadingGroup **shgroup_image_array;
/* face-mask */
DRWShadingGroup *lwire_shgrp;
@@ -189,7 +190,6 @@ static void PAINT_TEXTURE_cache_init(void *vedata)
if (!stl->g_data) {
/* Alloc transient pointers */
stl->g_data = MEM_mallocN(sizeof(*stl->g_data), __func__);
- stl->g_data->shgroup_image_array = NULL;
}
{
@@ -203,53 +203,6 @@ static void PAINT_TEXTURE_cache_init(void *vedata)
* any given time (i.e. use static vars) */
static float color[4] = {1.0f, 0.0f, 1.0f, 1.0};
DRW_shgroup_uniform_vec4(stl->g_data->shgroup_fallback, "color", color, 1);
-
- MEM_SAFE_FREE(stl->g_data->shgroup_image_array);
-
- const DRWContextState *draw_ctx = DRW_context_state_get();
- Object *ob = draw_ctx->obact;
- if (ob && ob->type == OB_MESH) {
- Scene *scene = draw_ctx->scene;
- const bool use_material_slots = (scene->toolsettings->imapaint.mode == IMAGEPAINT_MODE_MATERIAL);
- const Mesh *me = ob->data;
-
- stl->g_data->shgroup_image_array = MEM_mallocN(
- sizeof(*stl->g_data->shgroup_image_array) * (use_material_slots ? me->totcol : 1), __func__);
-
- if (use_material_slots) {
- for (int i = 0; i < me->totcol; i++) {
- Material *ma = give_current_material(ob, i + 1);
- Image *ima = (ma && ma->texpaintslot) ? ma->texpaintslot[ma->paint_active_slot].ima : NULL;
- GPUTexture *tex = ima ?
- GPU_texture_from_blender(ima, NULL, GL_TEXTURE_2D, false, 0.0f) : NULL;
-
- if (tex) {
- DRWShadingGroup *grp = DRW_shgroup_create(e_data.image_sh, psl->image_faces);
- DRW_shgroup_uniform_texture(grp, "image", tex);
- DRW_shgroup_uniform_float(grp, "alpha", &draw_ctx->v3d->overlay.texture_paint_mode_opacity, 1);
- stl->g_data->shgroup_image_array[i] = grp;
- }
- else {
- stl->g_data->shgroup_image_array[i] = NULL;
- }
- }
- }
- else {
- Image *ima = scene->toolsettings->imapaint.canvas;
- GPUTexture *tex = ima ?
- GPU_texture_from_blender(ima, NULL, GL_TEXTURE_2D, false, 0.0f) : NULL;
-
- if (tex) {
- DRWShadingGroup *grp = DRW_shgroup_create(e_data.image_sh, psl->image_faces);
- DRW_shgroup_uniform_texture(grp, "image", tex);
- DRW_shgroup_uniform_float(grp, "alpha", &draw_ctx->v3d->overlay.texture_paint_mode_opacity, 1);
- stl->g_data->shgroup_image_array[0] = grp;
- }
- else {
- stl->g_data->shgroup_image_array[0] = NULL;
- }
- }
- }
}
/* Face Mask */
@@ -293,29 +246,62 @@ static void PAINT_TEXTURE_cache_populate(void *vedata, Object *ob)
if (use_surface) {
if (me->mloopuv != NULL) {
if (use_material_slots) {
- struct GPUBatch **geom_array = me->totcol ? DRW_cache_mesh_surface_texpaint_get(ob) : NULL;
- if ((me->totcol == 0) || (geom_array == NULL)) {
- struct GPUBatch *geom = DRW_cache_mesh_surface_get(ob);
- DRW_shgroup_call_add(stl->g_data->shgroup_fallback, geom, ob->obmat);
- ok = true;
- }
- else {
- for (int i = 0; i < me->totcol; i++) {
- if (stl->g_data->shgroup_image_array[i]) {
- DRW_shgroup_call_add(stl->g_data->shgroup_image_array[i], geom_array[i], ob->obmat);
+ //struct GPUBatch **geom_array = me->totcol ? DRW_cache_mesh_surface_texpaint_get(ob) : NULL;
+ if (me->totcol) {
+ bool *is_tiled = MEM_mallocN(sizeof(bool)*me->totcol, "paint texture is tiled");
+ for(int i = 0; i < me->totcol; i++) {
+ Material *ma = give_current_material(ob, i + 1);
+ Image *ima = (ma && ma->texpaintslot) ? ma->texpaintslot[ma->paint_active_slot].ima : NULL;
+ is_tiled[i] = ima && (ima->source == IMA_SRC_TILED);
+ }
+
+ int num_batches = 0;
+ struct TexpaintCacheBatch *geom_batches = DRW_cache_mesh_surface_texpaint_get(ob, is_tiled, &num_batches);
+
+ for (int i = 0; i < num_batches; i++) {
+ ImageUser iuser = {NULL};
+ iuser.ok = 1;
+ iuser.tile = geom_batches[i].tile;
+
+ Material *ma = give_current_material(ob, geom_batches[i].material + 1);
+ Image *ima = (ma && ma->texpaintslot) ? ma->texpaintslot[ma->paint_active_slot].ima : NULL;
+ GPUTexture *tex = ima ?
+ GPU_texture_from_blender(ima, &iuser, GL_TEXTURE_2D, false, 0.0f) : NULL;
+
+ if (tex) {
+ DRWShadingGroup *grp = DRW_shgroup_create(e_data.image_sh, psl->image_faces);
+ DRW_shgroup_uniform_texture(grp, "image", tex);
+ DRW_shgroup_uniform_float(grp, "alpha", &draw_ctx->v3d->overlay.texture_paint_mode_opacity, 1);
+ DRW_shgroup_call_add(grp, geom_batches[i].batch, ob->obmat);
}
else {
- DRW_shgroup_call_add(stl->g_data->shgroup_fallback, geom_array[i], ob->obmat);
+ DRW_shgroup_call_add(stl->g_data->shgroup_fallback, geom_batches[i].batch, ob->obmat);
}
ok = true;
}
}
}
else {
- struct GPUBatch *geom = DRW_cache_mesh_surface_texpaint_single_get(ob);
- if (geom && stl->g_data->shgroup_image_array[0]) {
- DRW_shgroup_call_add(stl->g_data->shgroup_image_array[0], geom, ob->obmat);
- ok = true;
+ Image *ima = scene->toolsettings->imapaint.canvas;
+
+ if (ima) {
+ bool is_tiled = (ima->source == IMA_SRC_TILED);
+ int num_batches = 0;
+ struct TexpaintCacheBatch *geom_batches = DRW_cache_mesh_surface_texpaint_single_get(ob, is_tiled, &num_batches);
+
+ for (int i = 0; i < num_batches; i++) {
+ ImageUser iuser = {NULL};
+ iuser.ok = 1;
+ iuser.tile = geom_batches[i].tile;
+ GPUTexture *tex = GPU_texture_from_blender(ima, &iuser, GL_TEXTURE_2D, false, 0.0f);
+ if (tex) {
+ DRWShadingGroup *grp = DRW_shgroup_create(e_data.image_sh, psl->image_faces);
+ DRW_shgroup_uniform_texture(grp, "image", tex);
+ DRW_shgroup_uniform_float(grp, "alpha", &draw_ctx->v3d->overlay.texture_paint_mode_opacity, 1);
+ DRW_shgroup_call_add(grp, geom_batches[i].batch, ob->obmat);
+ ok = true;
+ }
+ }
}
}
}
@@ -345,12 +331,9 @@ static void PAINT_TEXTURE_cache_populate(void *vedata, Object *ob)
static void PAINT_TEXTURE_cache_finish(void *vedata)
{
PAINT_TEXTURE_PassList *psl = ((PAINT_TEXTURE_Data *)vedata)->psl;
- PAINT_TEXTURE_StorageList *stl = ((PAINT_TEXTURE_Data *)vedata)->stl;
/* Do something here! dependant on the objects gathered */
UNUSED_VARS(psl);
-
- MEM_SAFE_FREE(stl->g_data->shgroup_image_array);
}
/* Draw time ! Control rendering pipeline from here */
diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h
index f8af67b55ff..74da8f73729 100644
--- a/source/blender/editors/include/ED_image.h
+++ b/source/blender/editors/include/ED_image.h
@@ -48,7 +48,7 @@ struct Mask *ED_space_image_get_mask(struct SpaceImage *sima);
void ED_space_image_set_mask(struct bContext *C, struct SpaceImage *sima, struct Mask *mask);
bool ED_space_image_color_sample(struct SpaceImage *sima, struct ARegion *ar, int mval[2], float r_col[3]);
-struct ImBuf *ED_space_image_acquire_buffer(struct SpaceImage *sima, void **r_lock);
+struct ImBuf *ED_space_image_acquire_buffer(struct SpaceImage *sima, void **r_lock, int tile);
void ED_space_image_release_buffer(struct SpaceImage *sima, struct ImBuf *ibuf, void *lock);
bool ED_space_image_has_buffer(struct SpaceImage *sima);
diff --git a/source/blender/editors/include/ED_paint.h b/source/blender/editors/include/ED_paint.h
index 246419d64aa..b35236cadd0 100644
--- a/source/blender/editors/include/ED_paint.h
+++ b/source/blender/editors/include/ED_paint.h
@@ -41,7 +41,7 @@ void ED_keymap_paint(struct wmKeyConfig *keyconf);
/* paint_image.c */
void ED_imapaint_clear_partial_redraw(void);
void ED_imapaint_dirty_region(struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h, bool find_old);
-void ED_imapaint_bucket_fill(struct bContext *C, float color[3], struct wmOperator *op);
+void ED_imapaint_bucket_fill(struct bContext *C, float color[3], struct wmOperator *op, const int mouse[2]);
/* paint_image_undo.c */
void ED_image_undo_push_begin(const char *name);
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index 914641c1add..6b27ef845b9 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -106,7 +106,7 @@ void ED_region_visibility_change_update(struct bContext *C, struct ARegion *a
void ED_region_info_draw(struct ARegion *ar, const char *text, float fill_color[4], const bool full_redraw);
void ED_region_info_draw_multiline(ARegion *ar, const char *text_array[], float fill_color[4], const bool full_redraw);
void ED_region_image_metadata_draw(int x, int y, struct ImBuf *ibuf, const rctf *frame, float zoomx, float zoomy);
-void ED_region_grid_draw(struct ARegion *ar, float zoomx, float zoomy);
+void ED_region_grid_draw(struct ARegion *ar, float zoomx, float zoomy, float x0, float y0);
float ED_region_blend_alpha(struct ARegion *ar);
void ED_region_visible_rect(struct ARegion *ar, struct rcti *rect);
bool ED_region_is_overlap(int spacetype, int regiontype);
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index ec10fc9d494..caae71c9df7 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -1272,7 +1272,7 @@ void UI_drop_color_copy(wmDrag *drag, wmDropBox *drop)
RNA_boolean_set(drop->ptr, "gamma", drag_info->gamma_corrected);
}
-static int drop_color_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int drop_color_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
ARegion *ar = CTX_wm_region(C);
uiBut *but = NULL;
@@ -1313,7 +1313,7 @@ static int drop_color_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(
srgb_to_linearrgb_v3_v3(color, color);
}
- ED_imapaint_bucket_fill(C, color, op);
+ ED_imapaint_bucket_fill(C, color, op, event->mval);
}
ED_region_tag_redraw(ar);
diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c
index e8fabdabf17..1d93d664a5a 100644
--- a/source/blender/editors/object/object_bake_api.c
+++ b/source/blender/editors/object/object_bake_api.c
@@ -275,9 +275,12 @@ static void refresh_images(BakeImages *bake_images)
int i;
for (i = 0; i < bake_images->size; i++) {
Image *ima = bake_images->data[i].image;
- if (ima->ok == IMA_OK_LOADED) {
- GPU_free_image(ima);
- DEG_id_tag_update(&ima->id, 0);
+ LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) {
+ if (tile->ok == IMA_OK_LOADED) {
+ GPU_free_image(ima);
+ DEG_id_tag_update(&ima->id, 0);
+ break;
+ }
}
}
}
diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c
index 3423eedf7ca..8adb77beac3 100644
--- a/source/blender/editors/render/render_preview.c
+++ b/source/blender/editors/render/render_preview.c
@@ -982,8 +982,12 @@ static void icon_preview_startjob(void *customdata, short *stop, short *do_updat
ImBuf *ibuf = NULL;
ImageUser iuser = {NULL};
+ if (ima == NULL)
+ return;
+
+ ImageTile *tile = BKE_image_get_tile(ima, 0);
/* ima->ok is zero when Image cannot load */
- if (ima == NULL || ima->ok == 0)
+ if (tile->ok == 0)
return;
/* setup dummy image user */
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index 1a63bc1cd53..ff12ed90621 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -2735,15 +2735,15 @@ void ED_region_image_metadata_draw(int x, int y, ImBuf *ibuf, const rctf *frame,
GPU_matrix_pop();
}
-void ED_region_grid_draw(ARegion *ar, float zoomx, float zoomy)
+void ED_region_grid_draw(ARegion *ar, float zoomx, float zoomy, float x0, float y0)
{
float gridsize, gridstep = 1.0f / 32.0f;
float fac, blendfac;
int x1, y1, x2, y2;
- /* the image is located inside (0, 0), (1, 1) as set by view2d */
- UI_view2d_view_to_region(&ar->v2d, 0.0f, 0.0f, &x1, &y1);
- UI_view2d_view_to_region(&ar->v2d, 1.0f, 1.0f, &x2, &y2);
+ /* the image is located inside (x0, y0), (x0+1, y0+1) as set by view2d */
+ UI_view2d_view_to_region(&ar->v2d, x0, y0, &x1, &y1);
+ UI_view2d_view_to_region(&ar->v2d, x0+1.0f, y0+1.0f, &x2, &y2);
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c
index 897a74eb497..df8d41d2568 100644
--- a/source/blender/editors/sculpt_paint/paint_image.c
+++ b/source/blender/editors/sculpt_paint/paint_image.c
@@ -163,7 +163,7 @@ void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int
IMB_freeImBuf(tmpibuf);
}
-void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short texpaint)
+void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, ImageUser *iuser, short texpaint)
{
if (imapaintpartial.x1 != imapaintpartial.x2 &&
imapaintpartial.y1 != imapaintpartial.y2)
@@ -182,7 +182,7 @@ void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short te
int h = imapaintpartial.y2 - imapaintpartial.y1;
if (w && h) {
/* Testing with partial update in uv editor too */
- GPU_paint_update_image(image, (sima ? &sima->iuser : NULL), imapaintpartial.x1, imapaintpartial.y1, w, h);
+ GPU_paint_update_image(image, iuser, imapaintpartial.x1, imapaintpartial.y1, w, h);
}
}
}
@@ -587,7 +587,7 @@ static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke)
float color[3];
srgb_to_linearrgb_v3_v3(color, BKE_brush_color_get(scene, brush));
- paint_2d_bucket_fill(C, color, brush, pop->prevmouse, pop->custom_paint);
+ paint_2d_bucket_fill(C, color, brush, pop->startmouse, pop->prevmouse, pop->custom_paint);
}
else {
paint_proj_stroke(
@@ -1224,7 +1224,7 @@ void PAINT_OT_brush_colors_flip(wmOperatorType *ot)
}
-void ED_imapaint_bucket_fill(struct bContext *C, float color[3], wmOperator *op)
+void ED_imapaint_bucket_fill(struct bContext *C, float color[3], wmOperator *op, const int mouse[2])
{
wmWindowManager *wm = CTX_wm_manager(C);
SpaceImage *sima = CTX_wm_space_image(C);
@@ -1234,7 +1234,8 @@ void ED_imapaint_bucket_fill(struct bContext *C, float color[3], wmOperator *op)
ED_image_undo_push_begin(op->type->name);
- paint_2d_bucket_fill(C, color, NULL, NULL, NULL);
+ float mouse_init[2] = {mouse[0], mouse[1]};
+ paint_2d_bucket_fill(C, color, NULL, mouse_init, NULL, NULL);
BKE_undosys_step_push(wm->undo_stack, C, op->type->name);
diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c
index da08766b322..62daf39e278 100644
--- a/source/blender/editors/sculpt_paint/paint_image_2d.c
+++ b/source/blender/editors/sculpt_paint/paint_image_2d.c
@@ -43,6 +43,7 @@
#include "BLI_stack.h"
#include "BLI_bitmap.h"
#include "BLI_task.h"
+#include "BLI_listbase.h"
#include "BKE_colorband.h"
#include "BKE_context.h"
@@ -82,7 +83,7 @@ typedef struct BrushPainterCache {
bool is_texbrush;
bool is_maskbrush;
- int lastdiameter;
+ int lastdiameter[2];
float last_tex_rotation;
float last_mask_rotation;
float last_pressure;
@@ -94,22 +95,21 @@ typedef struct BrushPainterCache {
unsigned short *tex_mask_old;
unsigned int tex_mask_old_w;
unsigned int tex_mask_old_h;
+
+ int image_size[2];
} BrushPainterCache;
typedef struct BrushPainter {
Scene *scene;
Brush *brush;
- float lastpaintpos[2]; /* position of last paint op */
- float startpaintpos[2]; /* position of first paint */
-
short firsttouch; /* first paint op */
struct ImagePool *pool; /* image pool */
rctf tex_mapping; /* texture coordinate mapping */
rctf mask_mapping; /* mask texture coordinate mapping */
- BrushPainterCache cache;
+ bool cache_invert;
} BrushPainter;
typedef struct ImagePaintRegion {
@@ -118,6 +118,28 @@ typedef struct ImagePaintRegion {
int width, height;
} ImagePaintRegion;
+typedef enum ImagePaintTileState {
+ PAINT2D_TILE_EMPTY = 0,
+ PAINT2D_TILE_FAILED,
+ PAINT2D_TILE_READY,
+} ImagePaintTileState;
+
+typedef struct ImagePaintTile {
+ ImageUser iuser;
+ ImBuf *canvas;
+ float radius_fac[2];
+ int size[2];
+ float uv_ofs[2];
+ bool need_redraw;
+ BrushPainterCache cache;
+ int tile_number;
+
+ ImagePaintTileState state;
+
+ float last_paintpos[2]; /* position of last paint op */
+ float start_paintpos[2]; /* position of first paint */
+} ImagePaintTile;
+
typedef struct ImagePaintState {
BrushPainter *painter;
SpaceImage *sima;
@@ -129,10 +151,7 @@ typedef struct ImagePaintState {
Brush *brush;
short tool, blend;
Image *image;
- ImBuf *canvas;
ImBuf *clonecanvas;
- const char *warnpackedfile;
- const char *warnmultifile;
bool do_masking;
@@ -143,7 +162,8 @@ typedef struct ImagePaintState {
int do_facesel;
int symmetry;
- bool need_redraw;
+ ImagePaintTile *tiles;
+ int num_tiles;
BlurKernel *blurkernel;
} ImagePaintState;
@@ -156,42 +176,39 @@ static BrushPainter *brush_painter_2d_new(Scene *scene, Brush *brush, bool inver
painter->brush = brush;
painter->scene = scene;
painter->firsttouch = 1;
- painter->cache.lastdiameter = -1; /* force ibuf create in refresh */
- painter->cache.invert = invert;
+ painter->cache_invert = invert;
return painter;
}
-static void brush_painter_2d_require_imbuf(BrushPainter *painter, bool use_float, bool use_color_correction)
+static void brush_painter_2d_require_imbuf(Brush *brush, ImagePaintTile *tile, bool use_float, bool use_color_correction)
{
- Brush *brush = painter->brush;
+ BrushPainterCache *cache = &tile->cache;
+ if ((cache->use_float != use_float)) {
+ if (cache->ibuf) IMB_freeImBuf(cache->ibuf);
+ if (cache->curve_mask) MEM_freeN(cache->curve_mask);
+ if (cache->tex_mask) MEM_freeN(cache->tex_mask);
+ if (cache->tex_mask_old) MEM_freeN(cache->tex_mask_old);
+ cache->ibuf = NULL;
+ cache->curve_mask = NULL;
+ cache->tex_mask = NULL;
+ cache->lastdiameter[0] = -1; /* force ibuf create in refresh */
+ }
- if ((painter->cache.use_float != use_float)) {
- if (painter->cache.ibuf) IMB_freeImBuf(painter->cache.ibuf);
- if (painter->cache.curve_mask) MEM_freeN(painter->cache.curve_mask);
- if (painter->cache.tex_mask) MEM_freeN(painter->cache.tex_mask);
- if (painter->cache.tex_mask_old) MEM_freeN(painter->cache.tex_mask_old);
- painter->cache.ibuf = NULL;
- painter->cache.curve_mask = NULL;
- painter->cache.tex_mask = NULL;
- painter->cache.lastdiameter = -1; /* force ibuf create in refresh */
- }
-
- painter->cache.use_float = use_float;
- painter->cache.use_color_correction = use_float && use_color_correction;
- painter->cache.is_texbrush = (brush->mtex.tex && brush->imagepaint_tool == PAINT_TOOL_DRAW) ? true : false;
- painter->cache.is_maskbrush = (brush->mask_mtex.tex) ? true : false;
+ cache->use_float = use_float;
+ cache->use_color_correction = use_float && use_color_correction;
+ cache->is_texbrush = (brush->mtex.tex && brush->imagepaint_tool == PAINT_TOOL_DRAW) ? true : false;
+ cache->is_maskbrush = (brush->mask_mtex.tex) ? true : false;
}
-static void brush_painter_2d_free(BrushPainter *painter)
+static void brush_painter_cache_2d_free(BrushPainterCache *cache)
{
- if (painter->cache.ibuf) IMB_freeImBuf(painter->cache.ibuf);
- if (painter->cache.texibuf) IMB_freeImBuf(painter->cache.texibuf);
- if (painter->cache.curve_mask) MEM_freeN(painter->cache.curve_mask);
- if (painter->cache.tex_mask) MEM_freeN(painter->cache.tex_mask);
- if (painter->cache.tex_mask_old) MEM_freeN(painter->cache.tex_mask_old);
- MEM_freeN(painter);
+ if (cache->ibuf) IMB_freeImBuf(cache->ibuf);
+ if (cache->texibuf) IMB_freeImBuf(cache->texibuf);
+ if (cache->curve_mask) MEM_freeN(cache->curve_mask);
+ if (cache->tex_mask) MEM_freeN(cache->tex_mask);
+ if (cache->tex_mask_old) MEM_freeN(cache->tex_mask_old);
}
static void brush_imbuf_tex_co(rctf *mapping, int x, int y, float texco[3])
@@ -202,7 +219,7 @@ static void brush_imbuf_tex_co(rctf *mapping, int x, int y, float texco[3])
}
/* create a mask with the mask texture */
-static unsigned short *brush_painter_mask_ibuf_new(BrushPainter *painter, int size)
+static unsigned short *brush_painter_mask_ibuf_new(BrushPainter *painter, const int size[2])
{
Scene *scene = painter->scene;
Brush *brush = painter->brush;
@@ -213,11 +230,11 @@ static unsigned short *brush_painter_mask_ibuf_new(BrushPainter *painter, int si
unsigned short *mask, *m;
int x, y, thread = 0;
- mask = MEM_mallocN(sizeof(unsigned short) * size * size, "brush_painter_mask");
+ mask = MEM_mallocN(sizeof(unsigned short) * size[0] * size[1], "brush_painter_mask");
m = mask;
- for (y = 0; y < size; y++) {
- for (x = 0; x < size; x++, m++) {
+ for (y = 0; y < size[1]; y++) {
+ for (x = 0; x < size[0]; x++, m++) {
float res;
brush_imbuf_tex_co(&mask_mapping, x, y, texco);
res = BKE_brush_sample_masktex(scene, brush, texco, thread, pool);
@@ -230,11 +247,12 @@ static unsigned short *brush_painter_mask_ibuf_new(BrushPainter *painter, int si
/* update rectangular section of the brush image */
static void brush_painter_mask_imbuf_update(
- BrushPainter *painter, unsigned short *tex_mask_old,
- int origx, int origy, int w, int h, int xt, int yt, int diameter)
+ BrushPainter *painter, ImagePaintTile *tile, unsigned short *tex_mask_old,
+ int origx, int origy, int w, int h, int xt, int yt, const int diameter[2])
{
Scene *scene = painter->scene;
Brush *brush = painter->brush;
+ BrushPainterCache *cache = &tile->cache;
rctf tex_mapping = painter->mask_mapping;
struct ImagePool *pool = painter->pool;
unsigned short res;
@@ -243,8 +261,8 @@ static void brush_painter_mask_imbuf_update(
int x, y, thread = 0;
- unsigned short *tex_mask = painter->cache.tex_mask;
- unsigned short *tex_mask_cur = painter->cache.tex_mask_old;
+ unsigned short *tex_mask = cache->tex_mask;
+ unsigned short *tex_mask_cur = cache->tex_mask_old;
/* fill pixels */
for (y = origy; y < h; y++) {
@@ -253,8 +271,8 @@ static void brush_painter_mask_imbuf_update(
float texco[3];
/* handle byte pixel */
- unsigned short *b = tex_mask + (y * diameter + x);
- unsigned short *t = tex_mask_cur + (y * diameter + x);
+ unsigned short *b = tex_mask + (y * diameter[0] + x);
+ unsigned short *t = tex_mask_cur + (y * diameter[0] + x);
if (!use_texture_old) {
brush_imbuf_tex_co(&tex_mapping, x, y, texco);
@@ -263,7 +281,7 @@ static void brush_painter_mask_imbuf_update(
/* read from old texture buffer */
if (use_texture_old) {
- res = *(tex_mask_old + ((y - origy + yt) * painter->cache.tex_mask_old_w + (x - origx + xt)));
+ res = *(tex_mask_old + ((y - origy + yt) * cache->tex_mask_old_w + (x - origx + xt)));
}
/* write to new texture mask */
@@ -280,32 +298,33 @@ static void brush_painter_mask_imbuf_update(
* This can be considerably faster for brushes that change size due to pressure or
* textures that stick to the surface where only part of the pixels are new
*/
-static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter, const float pos[2], int diameter)
+static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter, ImagePaintTile *tile, const float pos[2], const int diameter[2])
{
- BrushPainterCache *cache = &painter->cache;
+ BrushPainterCache *cache = &tile->cache;
unsigned short *tex_mask_old;
int destx, desty, srcx, srcy, w, h, x1, y1, x2, y2;
/* create brush image buffer if it didn't exist yet */
if (!cache->tex_mask)
- cache->tex_mask = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask");
+ cache->tex_mask = MEM_mallocN(sizeof(unsigned short) * diameter[0] * diameter[1], "brush_painter_mask");
/* create new texture image buffer with coordinates relative to old */
tex_mask_old = cache->tex_mask_old;
- cache->tex_mask_old = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask");
+ cache->tex_mask_old = MEM_mallocN(sizeof(unsigned short) * diameter[0] * diameter[1], "brush_painter_mask");
if (tex_mask_old) {
ImBuf maskibuf;
ImBuf maskibuf_old;
- maskibuf.x = maskibuf.y = diameter;
+ maskibuf.x = diameter[0];
+ maskibuf.y = diameter[1];
maskibuf_old.x = cache->tex_mask_old_w;
maskibuf_old.y = cache->tex_mask_old_h;
srcx = srcy = 0;
w = cache->tex_mask_old_w;
h = cache->tex_mask_old_h;
- destx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2);
- desty = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2);
+ destx = (int)floorf(tile->last_paintpos[0]) - (int)floorf(pos[0]) + (diameter[0] / 2 - w / 2);
+ desty = (int)floorf(tile->last_paintpos[1]) - (int)floorf(pos[1]) + (diameter[1] / 2 - h / 2);
/* hack, use temporary rects so that clipping works */
IMB_rectclip(&maskibuf, &maskibuf_old, &destx, &desty, &srcx, &srcy, &w, &h);
@@ -316,53 +335,55 @@ static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter, const
w = h = 0;
}
- x1 = min_ii(destx, diameter);
- y1 = min_ii(desty, diameter);
- x2 = min_ii(destx + w, diameter);
- y2 = min_ii(desty + h, diameter);
+ x1 = min_ii(destx, diameter[0]);
+ y1 = min_ii(desty, diameter[1]);
+ x2 = min_ii(destx + w, diameter[0]);
+ y2 = min_ii(desty + h, diameter[1]);
/* blend existing texture in new position */
if ((x1 < x2) && (y1 < y2))
- brush_painter_mask_imbuf_update(painter, tex_mask_old, x1, y1, x2, y2, srcx, srcy, diameter);
+ brush_painter_mask_imbuf_update(painter, tile, tex_mask_old, x1, y1, x2, y2, srcx, srcy, diameter);
if (tex_mask_old)
MEM_freeN(tex_mask_old);
/* sample texture in new areas */
- if ((0 < x1) && (0 < diameter))
- brush_painter_mask_imbuf_update(painter, NULL, 0, 0, x1, diameter, 0, 0, diameter);
- if ((x2 < diameter) && (0 < diameter))
- brush_painter_mask_imbuf_update(painter, NULL, x2, 0, diameter, diameter, 0, 0, diameter);
+ if ((0 < x1) && (0 < diameter[1]))
+ brush_painter_mask_imbuf_update(painter, tile, NULL, 0, 0, x1, diameter[1], 0, 0, diameter);
+ if ((x2 < diameter[0]) && (0 < diameter[1]))
+ brush_painter_mask_imbuf_update(painter, tile, NULL, x2, 0, diameter[0], diameter[1], 0, 0, diameter);
if ((x1 < x2) && (0 < y1))
- brush_painter_mask_imbuf_update(painter, NULL, x1, 0, x2, y1, 0, 0, diameter);
- if ((x1 < x2) && (y2 < diameter))
- brush_painter_mask_imbuf_update(painter, NULL, x1, y2, x2, diameter, 0, 0, diameter);
+ brush_painter_mask_imbuf_update(painter, tile, NULL, x1, 0, x2, y1, 0, 0, diameter);
+ if ((x1 < x2) && (y2 < diameter[1]))
+ brush_painter_mask_imbuf_update(painter, tile, NULL, x1, y2, x2, diameter[1], 0, 0, diameter);
/* through with sampling, now update sizes */
- cache->tex_mask_old_w = diameter;
- cache->tex_mask_old_h = diameter;
+ cache->tex_mask_old_w = diameter[0];
+ cache->tex_mask_old_h = diameter[1];
}
/* create a mask with the falloff strength */
-static unsigned short *brush_painter_curve_mask_new(BrushPainter *painter, int diameter, float radius)
+static unsigned short *brush_painter_curve_mask_new(BrushPainter *painter, const int diameter[2], const float radius[2])
{
Brush *brush = painter->brush;
- int xoff = -radius;
- int yoff = -radius;
unsigned short *mask, *m;
int x, y;
- mask = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask");
+ mask = MEM_mallocN(sizeof(unsigned short) * diameter[0] * diameter[1], "brush_painter_mask");
m = mask;
- for (y = 0; y < diameter; y++) {
- for (x = 0; x < diameter; x++, m++) {
- float xy[2] = {x + xoff, y + yoff};
+ float max_radius = max_ff(radius[0], radius[1]);
+ float xfac = max_radius/radius[0];
+ float yfac = max_radius/radius[1];
+
+ for (y = 0; y < diameter[1]; y++) {
+ for (x = 0; x < diameter[0]; x++, m++) {
+ float xy[2] = {x*xfac - max_radius, y*yfac - max_radius};
float len = len_v2(xy);
- *m = (unsigned short)(65535.0f * BKE_brush_curve_strength_clamped(brush, len, radius));
+ *m = (unsigned short)(65535.0f * BKE_brush_curve_strength_clamped(brush, len, max_radius));
}
}
@@ -371,10 +392,11 @@ static unsigned short *brush_painter_curve_mask_new(BrushPainter *painter, int d
/* create imbuf with brush color */
-static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size, float pressure, float distance)
+static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, ImagePaintTile *tile, const int size[2], float pressure, float distance)
{
Scene *scene = painter->scene;
Brush *brush = painter->brush;
+ BrushPainterCache *cache = &tile->cache;
const char *display_device = scene->display_settings.display_device;
struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device);
@@ -382,19 +404,19 @@ static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size, float pre
rctf tex_mapping = painter->tex_mapping;
struct ImagePool *pool = painter->pool;
- bool use_color_correction = painter->cache.use_color_correction;
- bool use_float = painter->cache.use_float;
- bool is_texbrush = painter->cache.is_texbrush;
+ bool use_color_correction = cache->use_color_correction;
+ bool use_float = cache->use_float;
+ bool is_texbrush = cache->is_texbrush;
int x, y, thread = 0;
float brush_rgb[3];
/* allocate image buffer */
- ImBuf *ibuf = IMB_allocImBuf(size, size, 32, (use_float) ? IB_rectfloat : IB_rect);
+ ImBuf *ibuf = IMB_allocImBuf(size[0], size[1], 32, (use_float) ? IB_rectfloat : IB_rect);
/* get brush color */
if (brush->imagepaint_tool == PAINT_TOOL_DRAW) {
- paint_brush_color_get(scene, brush, use_color_correction, painter->cache.invert, distance, pressure, brush_rgb, display);
+ paint_brush_color_get(scene, brush, use_color_correction, cache->invert, distance, pressure, brush_rgb, display);
}
else {
brush_rgb[0] = 1.0f;
@@ -403,8 +425,8 @@ static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size, float pre
}
/* fill image buffer */
- for (y = 0; y < size; y++) {
- for (x = 0; x < size; x++) {
+ for (y = 0; y < size[1]; y++) {
+ for (x = 0; x < size[0]; x++) {
/* sample texture and multiply with brush color */
float texco[3], rgba[4];
@@ -424,13 +446,13 @@ static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size, float pre
if (use_float) {
/* write to float pixel */
- float *dstf = ibuf->rect_float + (y * size + x) * 4;
+ float *dstf = ibuf->rect_float + (y * size[0] + x) * 4;
mul_v3_v3fl(dstf, rgba, rgba[3]); /* premultiply */
dstf[3] = rgba[3];
}
else {
/* write to byte pixel */
- unsigned char *dst = (unsigned char *)ibuf->rect + (y * size + x) * 4;
+ unsigned char *dst = (unsigned char *)ibuf->rect + (y * size[0] + x) * 4;
rgb_float_to_uchar(dst, rgba);
dst[3] = unit_float_to_uchar_clamp(rgba[3]);
@@ -443,11 +465,12 @@ static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size, float pre
/* update rectangular section of the brush image */
static void brush_painter_imbuf_update(
- BrushPainter *painter, ImBuf *oldtexibuf,
+ BrushPainter *painter, ImagePaintTile *tile, ImBuf *oldtexibuf,
int origx, int origy, int w, int h, int xt, int yt)
{
Scene *scene = painter->scene;
Brush *brush = painter->brush;
+ BrushPainterCache *cache = &tile->cache;
const char *display_device = scene->display_settings.display_device;
struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device);
@@ -455,20 +478,20 @@ static void brush_painter_imbuf_update(
rctf tex_mapping = painter->tex_mapping;
struct ImagePool *pool = painter->pool;
- bool use_color_correction = painter->cache.use_color_correction;
- bool use_float = painter->cache.use_float;
- bool is_texbrush = painter->cache.is_texbrush;
+ bool use_color_correction = cache->use_color_correction;
+ bool use_float = cache->use_float;
+ bool is_texbrush = cache->is_texbrush;
bool use_texture_old = (oldtexibuf != NULL);
int x, y, thread = 0;
float brush_rgb[3];
- ImBuf *ibuf = painter->cache.ibuf;
- ImBuf *texibuf = painter->cache.texibuf;
+ ImBuf *ibuf = cache->ibuf;
+ ImBuf *texibuf = cache->texibuf;
/* get brush color */
if (brush->imagepaint_tool == PAINT_TOOL_DRAW) {
- paint_brush_color_get(scene, brush, use_color_correction, painter->cache.invert, 0.0, 1.0, brush_rgb, display);
+ paint_brush_color_get(scene, brush, use_color_correction, cache->invert, 0.0, 1.0, brush_rgb, display);
}
else {
brush_rgb[0] = 1.0f;
@@ -553,28 +576,28 @@ static void brush_painter_imbuf_update(
/* update the brush image by trying to reuse the cached texture result. this
* can be considerably faster for brushes that change size due to pressure or
* textures that stick to the surface where only part of the pixels are new */
-static void brush_painter_imbuf_partial_update(BrushPainter *painter, const float pos[2], int diameter)
+static void brush_painter_imbuf_partial_update(BrushPainter *painter, ImagePaintTile *tile, const float pos[2], const int diameter[2])
{
- BrushPainterCache *cache = &painter->cache;
+ BrushPainterCache *cache = &tile->cache;
ImBuf *oldtexibuf, *ibuf;
int imbflag, destx, desty, srcx, srcy, w, h, x1, y1, x2, y2;
/* create brush image buffer if it didn't exist yet */
imbflag = (cache->use_float) ? IB_rectfloat : IB_rect;
if (!cache->ibuf)
- cache->ibuf = IMB_allocImBuf(diameter, diameter, 32, imbflag);
+ cache->ibuf = IMB_allocImBuf(diameter[0], diameter[1], 32, imbflag);
ibuf = cache->ibuf;
/* create new texture image buffer with coordinates relative to old */
oldtexibuf = cache->texibuf;
- cache->texibuf = IMB_allocImBuf(diameter, diameter, 32, imbflag);
+ cache->texibuf = IMB_allocImBuf(diameter[0], diameter[1], 32, imbflag);
if (oldtexibuf) {
srcx = srcy = 0;
w = oldtexibuf->x;
h = oldtexibuf->y;
- destx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2);
- desty = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2);
+ destx = (int)floorf(tile->last_paintpos[0]) - (int)floorf(pos[0]) + (diameter[0] / 2 - w / 2);
+ desty = (int)floorf(tile->last_paintpos[1]) - (int)floorf(pos[1]) + (diameter[1] / 2 - h / 2);
IMB_rectclip(cache->texibuf, oldtexibuf, &destx, &desty, &srcx, &srcy, &w, &h);
}
@@ -591,43 +614,43 @@ static void brush_painter_imbuf_partial_update(BrushPainter *painter, const floa
/* blend existing texture in new position */
if ((x1 < x2) && (y1 < y2))
- brush_painter_imbuf_update(painter, oldtexibuf, x1, y1, x2, y2, srcx, srcy);
+ brush_painter_imbuf_update(painter, tile, oldtexibuf, x1, y1, x2, y2, srcx, srcy);
if (oldtexibuf)
IMB_freeImBuf(oldtexibuf);
/* sample texture in new areas */
if ((0 < x1) && (0 < ibuf->y))
- brush_painter_imbuf_update(painter, NULL, 0, 0, x1, ibuf->y, 0, 0);
+ brush_painter_imbuf_update(painter, tile, NULL, 0, 0, x1, ibuf->y, 0, 0);
if ((x2 < ibuf->x) && (0 < ibuf->y))
- brush_painter_imbuf_update(painter, NULL, x2, 0, ibuf->x, ibuf->y, 0, 0);
+ brush_painter_imbuf_update(painter, tile, NULL, x2, 0, ibuf->x, ibuf->y, 0, 0);
if ((x1 < x2) && (0 < y1))
- brush_painter_imbuf_update(painter, NULL, x1, 0, x2, y1, 0, 0);
+ brush_painter_imbuf_update(painter, tile, NULL, x1, 0, x2, y1, 0, 0);
if ((x1 < x2) && (y2 < ibuf->y))
- brush_painter_imbuf_update(painter, NULL, x1, y2, x2, ibuf->y, 0, 0);
+ brush_painter_imbuf_update(painter, tile, NULL, x1, y2, x2, ibuf->y, 0, 0);
}
-static void brush_painter_2d_tex_mapping(ImagePaintState *s, int diameter, const float startpos[2], const float pos[2], const float mouse[2], int mapmode, rctf *mapping)
+static void brush_painter_2d_tex_mapping(ImagePaintState *s, ImBuf *canvas, const int diameter[2], const float startpos[2], const float pos[2], const float mouse[2], int mapmode, rctf *mapping)
{
- float invw = 1.0f / (float)s->canvas->x;
- float invh = 1.0f / (float)s->canvas->y;
+ float invw = 1.0f / (float)canvas->x;
+ float invh = 1.0f / (float)canvas->y;
int xmin, ymin, xmax, ymax;
int ipos[2];
/* find start coordinate of brush in canvas */
- ipos[0] = (int)floorf((pos[0] - diameter / 2) + 1.0f);
- ipos[1] = (int)floorf((pos[1] - diameter / 2) + 1.0f);
+ ipos[0] = (int)floorf((pos[0] - diameter[0] / 2) + 1.0f);
+ ipos[1] = (int)floorf((pos[1] - diameter[1] / 2) + 1.0f);
if (mapmode == MTEX_MAP_MODE_STENCIL) {
/* map from view coordinates of brush to region coordinates */
UI_view2d_view_to_region(s->v2d, ipos[0] * invw, ipos[1] * invh, &xmin, &ymin);
- UI_view2d_view_to_region(s->v2d, (ipos[0] + diameter) * invw, (ipos[1] + diameter) * invh, &xmax, &ymax);
+ UI_view2d_view_to_region(s->v2d, (ipos[0] + diameter[0]) * invw, (ipos[1] + diameter[1]) * invh, &xmax, &ymax);
/* output mapping from brush ibuf x/y to region coordinates */
mapping->xmin = xmin;
mapping->ymin = ymin;
- mapping->xmax = (xmax - xmin) / (float)diameter;
- mapping->ymax = (ymax - ymin) / (float)diameter;
+ mapping->xmax = (xmax - xmin) / (float)diameter[0];
+ mapping->ymax = (ymax - ymin) / (float)diameter[1];
}
else if (mapmode == MTEX_MAP_MODE_3D) {
/* 3D mapping, just mapping to canvas 0..1 */
@@ -638,26 +661,26 @@ static void brush_painter_2d_tex_mapping(ImagePaintState *s, int diameter, const
}
else if (ELEM(mapmode, MTEX_MAP_MODE_VIEW, MTEX_MAP_MODE_RANDOM)) {
/* view mapping */
- mapping->xmin = mouse[0] - diameter * 0.5f + 0.5f;
- mapping->ymin = mouse[1] - diameter * 0.5f + 0.5f;
+ mapping->xmin = mouse[0] - diameter[0] * 0.5f + 0.5f;
+ mapping->ymin = mouse[1] - diameter[1] * 0.5f + 0.5f;
mapping->xmax = 1.0f;
mapping->ymax = 1.0f;
}
else /* if (mapmode == MTEX_MAP_MODE_TILED) */ {
- mapping->xmin = (int)(-diameter * 0.5) + (int)floorf(pos[0]) - (int)floorf(startpos[0]);
- mapping->ymin = (int)(-diameter * 0.5) + (int)floorf(pos[1]) - (int)floorf(startpos[1]);
+ mapping->xmin = (int)(-diameter[0] * 0.5) + (int)floorf(pos[0]) - (int)floorf(startpos[0]);
+ mapping->ymin = (int)(-diameter[1] * 0.5) + (int)floorf(pos[1]) - (int)floorf(startpos[1]);
mapping->xmax = 1.0f;
mapping->ymax = 1.0f;
}
}
-static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *painter, const float pos[2], const float mouse[2], float pressure, float distance, float size)
+static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *painter, ImagePaintTile *tile, const float pos[2], const float mouse[2], float pressure, float distance, const float size[2])
{
const Scene *scene = painter->scene;
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
Brush *brush = painter->brush;
- BrushPainterCache *cache = &painter->cache;
- const int diameter = 2 * size;
+ const int diameter[2] = {2 * size[0], 2 * size[1]};
+ BrushPainterCache *cache = &tile->cache;
bool do_random = false;
bool do_partial_update = false;
@@ -673,7 +696,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *pai
painter->pool = BKE_image_pool_new();
/* determine how can update based on textures used */
- if (painter->cache.is_texbrush) {
+ if (cache->is_texbrush) {
if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) {
tex_rotation += ups->brush_rotation;
}
@@ -683,11 +706,11 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *pai
do_partial_update = true;
brush_painter_2d_tex_mapping(
- s, diameter, painter->startpaintpos, pos, mouse,
+ s, tile->canvas, diameter, tile->start_paintpos, pos, mouse,
brush->mtex.brush_map_mode, &painter->tex_mapping);
}
- if (painter->cache.is_maskbrush) {
+ if (cache->is_maskbrush) {
bool renew_maxmask = false;
bool do_partial_update_mask = false;
/* invalidate case for all mapping modes */
@@ -707,7 +730,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *pai
renew_maxmask = true;
}
- if ((diameter != cache->lastdiameter) ||
+ if (!equals_v2v2_int(diameter, cache->lastdiameter) ||
(mask_rotation != cache->last_mask_rotation) ||
renew_maxmask)
{
@@ -717,11 +740,11 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *pai
}
brush_painter_2d_tex_mapping(
- s, diameter, painter->startpaintpos, pos, mouse,
+ s, tile->canvas, diameter, tile->start_paintpos, pos, mouse,
brush->mask_mtex.brush_map_mode, &painter->mask_mapping);
if (do_partial_update_mask)
- brush_painter_mask_imbuf_partial_update(painter, pos, diameter);
+ brush_painter_mask_imbuf_partial_update(painter, tile, pos, diameter);
else
cache->tex_mask = brush_painter_mask_ibuf_new(painter, diameter);
cache->last_mask_rotation = mask_rotation;
@@ -729,7 +752,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *pai
}
/* curve mask can only change if the size changes */
- if (diameter != cache->lastdiameter) {
+ if (!equals_v2v2_int(diameter, cache->lastdiameter)) {
if (cache->curve_mask) {
MEM_freeN(cache->curve_mask);
cache->curve_mask = NULL;
@@ -739,7 +762,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *pai
}
/* detect if we need to recreate image brush buffer */
- if ((diameter != cache->lastdiameter) ||
+ if ((!equals_v2v2_int(diameter, cache->lastdiameter)) ||
(tex_rotation != cache->last_tex_rotation) ||
do_random ||
update_color)
@@ -751,24 +774,24 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *pai
if (do_partial_update) {
/* do partial update of texture */
- brush_painter_imbuf_partial_update(painter, pos, diameter);
+ brush_painter_imbuf_partial_update(painter, tile, pos, diameter);
}
else {
/* create brush from scratch */
- cache->ibuf = brush_painter_imbuf_new(painter, diameter, pressure, distance);
+ cache->ibuf = brush_painter_imbuf_new(painter, tile, diameter, pressure, distance);
}
- cache->lastdiameter = diameter;
+ copy_v2_v2_int(cache->lastdiameter, diameter);
cache->last_tex_rotation = tex_rotation;
cache->last_pressure = pressure;
}
else if (do_partial_update) {
/* do only partial update of texture */
- int dx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]);
- int dy = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]);
+ int dx = (int)floorf(tile->last_paintpos[0]) - (int)floorf(pos[0]);
+ int dy = (int)floorf(tile->last_paintpos[1]) - (int)floorf(pos[1]);
if ((dx != 0) || (dy != 0)) {
- brush_painter_imbuf_partial_update(painter, pos, diameter);
+ brush_painter_imbuf_partial_update(painter, tile, pos, diameter);
}
}
@@ -776,6 +799,52 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *pai
painter->pool = NULL;
}
+static bool paint_2d_check_tile(ImagePaintState *s, int i)
+{
+ if (i == 0)
+ return true;
+ if (i >= s->num_tiles)
+ return false;
+
+ if (s->tiles[i].state == PAINT2D_TILE_READY)
+ return true;
+ if (s->tiles[i].state == PAINT2D_TILE_FAILED)
+ return false;
+
+ s->tiles[i].cache.lastdiameter[0] = -1;
+
+ s->tiles[i].iuser.ok = true;
+
+ ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, &s->tiles[i].iuser, NULL);
+ if (ibuf) {
+ if (ibuf->channels != 4) {
+ s->tiles[i].state = PAINT2D_TILE_FAILED;
+ }
+ else if ((s->tiles[0].canvas->rect && !ibuf->rect ) ||
+ (s->tiles[0].canvas->rect_float && !ibuf->rect_float)) {
+ s->tiles[i].state = PAINT2D_TILE_FAILED;
+ }
+ else {
+ s->tiles[i].size[0] = ibuf->x;
+ s->tiles[i].size[1] = ibuf->y;
+ s->tiles[i].radius_fac[0] = ((float) ibuf->x) / s->tiles[0].size[0];
+ s->tiles[i].radius_fac[1] = ((float) ibuf->y) / s->tiles[0].size[1];
+ s->tiles[i].state = PAINT2D_TILE_READY;
+ }
+ }
+ else {
+ s->tiles[i].state = PAINT2D_TILE_FAILED;
+ }
+
+ if (s->tiles[i].state == PAINT2D_TILE_FAILED) {
+ BKE_image_release_ibuf(s->image, ibuf, NULL);
+ return false;
+ }
+
+ s->tiles[i].canvas = ibuf;
+ return true;
+}
+
/* keep these functions in sync */
static void paint_2d_ibuf_rgb_get(ImBuf *ibuf, int x, int y, float r_rgb[4])
{
@@ -816,24 +885,24 @@ static void paint_2d_ibuf_rgb_set(ImBuf *ibuf, int x, int y, const bool is_torus
}
}
-static void paint_2d_ibuf_tile_convert(ImBuf *ibuf, int *x, int *y, short tile)
+static void paint_2d_ibuf_tile_convert(ImBuf *ibuf, int *x, int *y, short paint_tile)
{
- if (tile & PAINT_TILE_X) {
+ if (paint_tile & PAINT_TILE_X) {
*x %= ibuf->x;
if (*x < 0) *x += ibuf->x;
}
- if (tile & PAINT_TILE_Y) {
+ if (paint_tile & PAINT_TILE_Y) {
*y %= ibuf->y;
if (*y < 0) *y += ibuf->y;
}
}
-static float paint_2d_ibuf_add_if(ImBuf *ibuf, int x, int y, float *outrgb, short tile, float w)
+static float paint_2d_ibuf_add_if(ImBuf *ibuf, int x, int y, float *outrgb, short paint_tile, float w)
{
float inrgb[4];
- if (tile) paint_2d_ibuf_tile_convert(ibuf, &x, &y, tile);
+ if (paint_tile) paint_2d_ibuf_tile_convert(ibuf, &x, &y, paint_tile);
/* need to also do clipping here always since tiled coordinates
* are not always within bounds */
if (x < ibuf->x && x >= 0 && y < ibuf->y && y >= 0) {
@@ -847,9 +916,9 @@ static float paint_2d_ibuf_add_if(ImBuf *ibuf, int x, int y, float *outrgb, shor
return w;
}
-static void paint_2d_lift_soften(ImagePaintState *s, ImBuf *ibuf, ImBuf *ibufb, int *pos, const short tile)
+static void paint_2d_lift_soften(ImagePaintState *s, ImagePaintTile *tile, ImBuf *ibuf, ImBuf *ibufb, int *pos, const short paint_tile)
{
- bool sharpen = (s->painter->cache.invert ^ ((s->brush->flag & BRUSH_DIR_IN) != 0));
+ bool sharpen = (tile->cache.invert ^ ((s->brush->flag & BRUSH_DIR_IN) != 0));
float threshold = s->brush->sharp_threshold;
int x, y, xi, yi, xo, yo, xk, yk;
float count;
@@ -865,7 +934,7 @@ static void paint_2d_lift_soften(ImagePaintState *s, ImBuf *ibuf, ImBuf *ibufb,
in_off[1] = pos[1];
out_off[0] = out_off[1] = 0;
- if (!tile) {
+ if (!paint_tile) {
IMB_rectclip(
ibuf, ibufb, &in_off[0], &in_off[1], &out_off[0],
&out_off[1], &dim[0], &dim[1]);
@@ -884,8 +953,8 @@ static void paint_2d_lift_soften(ImagePaintState *s, ImBuf *ibuf, ImBuf *ibufb,
yi = in_off[1] + y;
count = 0.0;
- if (tile) {
- paint_2d_ibuf_tile_convert(ibuf, &xi, &yi, tile);
+ if (paint_tile) {
+ paint_2d_ibuf_tile_convert(ibuf, &xi, &yi, paint_tile);
if (xi < ibuf->x && xi >= 0 && yi < ibuf->y && yi >= 0)
paint_2d_ibuf_rgb_get(ibuf, xi, yi, rgba);
else
@@ -901,7 +970,7 @@ static void paint_2d_lift_soften(ImagePaintState *s, ImBuf *ibuf, ImBuf *ibufb,
for (xk = 0; xk < kernel->side; xk++) {
count += paint_2d_ibuf_add_if(
ibuf, xi + xk - kernel->pixel_len,
- yi + yk - kernel->pixel_len, outrgb, tile,
+ yi + yk - kernel->pixel_len, outrgb, paint_tile,
kernel->wdata[xk + yk * kernel->side]);
}
}
@@ -949,7 +1018,7 @@ static void paint_2d_set_region(ImagePaintRegion *region, int destx, int desty,
region->height = height;
}
-static int paint_2d_torus_split_region(ImagePaintRegion region[4], ImBuf *dbuf, ImBuf *sbuf, short tile)
+static int paint_2d_torus_split_region(ImagePaintRegion region[4], ImBuf *dbuf, ImBuf *sbuf, short paint_tile)
{
int destx = region->destx;
int desty = region->desty;
@@ -960,13 +1029,13 @@ static int paint_2d_torus_split_region(ImagePaintRegion region[4], ImBuf *dbuf,
int origw, origh, w, h, tot = 0;
/* convert destination and source coordinates to be within image */
- if (tile & PAINT_TILE_X) {
+ if (paint_tile & PAINT_TILE_X) {
destx = destx % dbuf->x;
if (destx < 0) destx += dbuf->x;
srcx = srcx % sbuf->x;
if (srcx < 0) srcx += sbuf->x;
}
- if (tile & PAINT_TILE_Y) {
+ if (paint_tile & PAINT_TILE_Y) {
desty = desty % dbuf->y;
if (desty < 0) desty += dbuf->y;
srcy = srcy % sbuf->y;
@@ -982,23 +1051,23 @@ static int paint_2d_torus_split_region(ImagePaintRegion region[4], ImBuf *dbuf,
paint_2d_set_region(&region[tot++], destx, desty, srcx, srcy, w, h);
/* do 3 other rects if needed */
- if ((tile & PAINT_TILE_X) && w < origw)
+ if ((paint_tile & PAINT_TILE_X) && w < origw)
paint_2d_set_region(&region[tot++], (destx + w) % dbuf->x, desty, (srcx + w) % sbuf->x, srcy, origw - w, h);
- if ((tile & PAINT_TILE_Y) && h < origh)
+ if ((paint_tile & PAINT_TILE_Y) && h < origh)
paint_2d_set_region(&region[tot++], destx, (desty + h) % dbuf->y, srcx, (srcy + h) % sbuf->y, w, origh - h);
- if ((tile & PAINT_TILE_X) && (tile & PAINT_TILE_Y) && (w < origw) && (h < origh))
+ if ((paint_tile & PAINT_TILE_X) && (paint_tile & PAINT_TILE_Y) && (w < origw) && (h < origh))
paint_2d_set_region(&region[tot++], (destx + w) % dbuf->x, (desty + h) % dbuf->y, (srcx + w) % sbuf->x, (srcy + h) % sbuf->y, origw - w, origh - h);
return tot;
}
-static void paint_2d_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos, short tile)
+static void paint_2d_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos, short paint_tile)
{
ImagePaintRegion region[4];
int a, tot;
paint_2d_set_region(region, 0, 0, pos[0], pos[1], ibufb->x, ibufb->y);
- tot = paint_2d_torus_split_region(region, ibufb, ibuf, tile);
+ tot = paint_2d_torus_split_region(region, ibufb, ibuf, paint_tile);
for (a = 0; a < tot; a++)
IMB_rectblend(
@@ -1034,9 +1103,8 @@ static void paint_2d_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[
static void paint_2d_do_making_brush(
ImagePaintState *s,
+ ImagePaintTile *tile,
ImagePaintRegion *region,
- unsigned short *curveb,
- unsigned short *texmaskb,
ImBuf *frombuf,
float mask_max,
short blend,
@@ -1055,14 +1123,14 @@ static void paint_2d_do_making_brush(
int origx = region->destx - tx * IMAPAINT_TILE_SIZE;
int origy = region->desty - ty * IMAPAINT_TILE_SIZE;
- if (s->canvas->rect_float)
- tmpbuf.rect_float = image_undo_find_tile(undo_tiles, s->image, s->canvas, tx, ty, &mask, false);
+ if (tile->canvas->rect_float)
+ tmpbuf.rect_float = image_undo_find_tile(undo_tiles, s->image, tile->canvas, tx, ty, &mask, false);
else
- tmpbuf.rect = image_undo_find_tile(undo_tiles, s->image, s->canvas, tx, ty, &mask, false);
+ tmpbuf.rect = image_undo_find_tile(undo_tiles, s->image, tile->canvas, tx, ty, &mask, false);
IMB_rectblend(
- s->canvas, &tmpbuf, frombuf, mask,
- curveb, texmaskb, mask_max,
+ tile->canvas, &tmpbuf, frombuf, mask,
+ tile->cache.curve_mask, tile->cache.tex_mask, mask_max,
region->destx, region->desty,
origx, origy,
region->srcx, region->srcy,
@@ -1074,9 +1142,8 @@ static void paint_2d_do_making_brush(
typedef struct Paint2DForeachData {
ImagePaintState *s;
+ ImagePaintTile *tile;
ImagePaintRegion *region;
- unsigned short *curveb;
- unsigned short *texmaskb;
ImBuf *frombuf;
float mask_max;
short blend;
@@ -1091,19 +1158,21 @@ static void paint_2d_op_foreach_do(
{
Paint2DForeachData *data = (Paint2DForeachData *)data_v;
paint_2d_do_making_brush(
- data->s, data->region, data->curveb,
- data->texmaskb, data->frombuf, data->mask_max,
+ data->s, data->tile, data->region,
+ data->frombuf, data->mask_max,
data->blend,
data->tilex, iter,
data->tilew, iter);
}
-static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsigned short *texmaskb, const float lastpos[2], const float pos[2])
+static int paint_2d_op(void *state, ImagePaintTile *tile, const float lastpos[2], const float pos[2])
{
ImagePaintState *s = ((ImagePaintState *)state);
ImBuf *clonebuf = NULL, *frombuf;
+ ImBuf *canvas = tile->canvas;
+ ImBuf *ibufb = tile->cache.ibuf;
ImagePaintRegion region[4];
- short tile = s->symmetry & (PAINT_TILE_X | PAINT_TILE_Y);
+ short paint_tile = s->symmetry & (PAINT_TILE_X | PAINT_TILE_Y);
short blend = s->blend;
const float *offset = s->brush->clone.offset;
float liftpos[2];
@@ -1115,7 +1184,7 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign
/* lift from canvas */
if (s->tool == PAINT_TOOL_SOFTEN) {
- paint_2d_lift_soften(s, s->canvas, ibufb, bpos, tile);
+ paint_2d_lift_soften(s, tile, canvas, ibufb, bpos, paint_tile);
blend = IMB_BLEND_INTERPOLATE;
}
else if (s->tool == PAINT_TOOL_SMEAR) {
@@ -1123,12 +1192,12 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign
return 0;
paint_2d_convert_brushco(ibufb, lastpos, blastpos);
- paint_2d_lift_smear(s->canvas, ibufb, blastpos, tile);
+ paint_2d_lift_smear(canvas, ibufb, blastpos, paint_tile);
blend = IMB_BLEND_INTERPOLATE;
}
else if (s->tool == PAINT_TOOL_CLONE && s->clonecanvas) {
- liftpos[0] = pos[0] - offset[0] * s->canvas->x;
- liftpos[1] = pos[1] - offset[1] * s->canvas->y;
+ liftpos[0] = pos[0] - offset[0] * canvas->x;
+ liftpos[1] = pos[1] - offset[1] * canvas->y;
paint_2d_convert_brushco(ibufb, liftpos, bliftpos);
clonebuf = paint_2d_lift_clone(s->clonecanvas, ibufb, bliftpos);
@@ -1136,9 +1205,9 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign
frombuf = (clonebuf) ? clonebuf : ibufb;
- if (tile) {
+ if (paint_tile) {
paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y);
- tot = paint_2d_torus_split_region(region, s->canvas, frombuf, tile);
+ tot = paint_2d_torus_split_region(region, canvas, frombuf, paint_tile);
}
else {
paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y);
@@ -1148,7 +1217,7 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign
/* blend into canvas */
for (a = 0; a < tot; a++) {
ED_imapaint_dirty_region(
- s->image, s->canvas,
+ s->image, canvas,
region[a].destx, region[a].desty,
region[a].width, region[a].height, true);
@@ -1157,21 +1226,20 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign
int tilex, tiley, tilew, tileh;
imapaint_region_tiles(
- s->canvas, region[a].destx, region[a].desty,
+ canvas, region[a].destx, region[a].desty,
region[a].width, region[a].height,
&tilex, &tiley, &tilew, &tileh);
if (tiley == tileh) {
paint_2d_do_making_brush(
- s, &region[a], curveb, texmaskb, frombuf,
+ s, tile, &region[a], frombuf,
mask_max, blend, tilex, tiley, tilew, tileh);
}
else {
Paint2DForeachData data;
data.s = s;
+ data.tile = tile;
data.region = &region[a];
- data.curveb = curveb;
- data.texmaskb = texmaskb;
data.frombuf = frombuf;
data.mask_max = mask_max;
data.blend = blend;
@@ -1190,7 +1258,8 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign
else {
/* no masking, composite brush directly onto canvas */
IMB_rectblend_threaded(
- s->canvas, s->canvas, frombuf, NULL, curveb, texmaskb, mask_max,
+ canvas, canvas, frombuf, NULL,
+ tile->cache.curve_mask, tile->cache.tex_mask, mask_max,
region[a].destx, region[a].desty,
region[a].destx, region[a].desty,
region[a].srcx, region[a].srcy,
@@ -1204,46 +1273,25 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign
}
-static int paint_2d_canvas_set(ImagePaintState *s, Image *ima)
+static int paint_2d_canvas_set(ImagePaintState *s)
{
- ImBuf *ibuf = BKE_image_acquire_ibuf(ima, s->sima ? &s->sima->iuser : NULL, NULL);
-
- /* verify that we can paint and set canvas */
- if (ima == NULL) {
- return 0;
- }
- else if (BKE_image_has_packedfile(ima) && ima->rr) {
- s->warnpackedfile = ima->id.name + 2;
- return 0;
- }
- else if (ibuf && ibuf->channels != 4) {
- s->warnmultifile = ima->id.name + 2;
- return 0;
- }
- else if (!ibuf || !(ibuf->rect || ibuf->rect_float))
- return 0;
-
- s->image = ima;
- s->canvas = ibuf;
-
/* set clone canvas */
if (s->tool == PAINT_TOOL_CLONE) {
- ima = s->brush->clone.image;
- ibuf = BKE_image_acquire_ibuf(ima, s->sima ? &s->sima->iuser : NULL, NULL);
+ Image *ima = s->brush->clone.image;
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
if (!ima || !ibuf || !(ibuf->rect || ibuf->rect_float)) {
BKE_image_release_ibuf(ima, ibuf, NULL);
- BKE_image_release_ibuf(s->image, s->canvas, NULL);
return 0;
}
s->clonecanvas = ibuf;
/* temporarily add float rect for cloning */
- if (s->canvas->rect_float && !s->clonecanvas->rect_float) {
+ if (s->tiles[0].canvas->rect_float && !s->clonecanvas->rect_float) {
IMB_float_from_rect(s->clonecanvas);
}
- else if (!s->canvas->rect_float && !s->clonecanvas->rect)
+ else if (!s->tiles[0].canvas->rect_float && !s->clonecanvas->rect)
IMB_rect_from_float(s->clonecanvas);
}
@@ -1255,7 +1303,9 @@ static int paint_2d_canvas_set(ImagePaintState *s, Image *ima)
static void paint_2d_canvas_free(ImagePaintState *s)
{
- BKE_image_release_ibuf(s->image, s->canvas, NULL);
+ for (int i = 0; i < s->num_tiles; i++) {
+ BKE_image_release_ibuf(s->image, s->tiles[i].canvas, NULL);
+ }
BKE_image_release_ibuf(s->brush->clone.image, s->clonecanvas, NULL);
if (s->blurkernel) {
@@ -1266,57 +1316,94 @@ static void paint_2d_canvas_free(ImagePaintState *s)
image_undo_remove_masks();
}
-void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], const bool eraser, float pressure, float distance, float size)
+static void paint_2d_transform_mouse(ImagePaintState *s, const float in[2], float out[2])
{
- float newuv[2], olduv[2];
+ UI_view2d_region_to_view(s->v2d, in[0], in[1], &out[0], &out[1]);
+}
+
+static bool is_inside_tile(const int size[2], const float pos[2], const float brush[2])
+{
+ return (pos[0] >= -brush[0]) && (pos[0] < size[0]+brush[0]) &&
+ (pos[1] >= -brush[1]) && (pos[1] < size[1]+brush[1]);
+}
+
+static void paint_2d_uv_to_coord(ImagePaintTile *tile, const float uv[2], float coord[2])
+{
+ coord[0] = (uv[0] - tile->uv_ofs[0]) * tile->size[0];
+ coord[1] = (uv[1] - tile->uv_ofs[1]) * tile->size[1];
+}
+
+void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], const bool eraser, float pressure, float distance, float base_size)
+{
+ float new_uv[2], old_uv[2];
ImagePaintState *s = ps;
BrushPainter *painter = s->painter;
- ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, s->sima ? &s->sima->iuser : NULL, NULL);
- const bool is_data = (ibuf && ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA);
- if (!ibuf)
- return;
+ const bool is_data = s->tiles[0].canvas->colormanage_flag & IMB_COLORMANAGE_IS_DATA;
s->blend = s->brush->blend;
if (eraser)
s->blend = IMB_BLEND_ERASE_ALPHA;
- UI_view2d_region_to_view(s->v2d, mval[0], mval[1], &newuv[0], &newuv[1]);
- UI_view2d_region_to_view(s->v2d, prev_mval[0], prev_mval[1], &olduv[0], &olduv[1]);
-
- newuv[0] *= ibuf->x;
- newuv[1] *= ibuf->y;
-
- olduv[0] *= ibuf->x;
- olduv[1] *= ibuf->y;
+ UI_view2d_region_to_view(s->v2d, mval[0], mval[1], &new_uv[0], &new_uv[1]);
+ UI_view2d_region_to_view(s->v2d, prev_mval[0], prev_mval[1], &old_uv[0], &old_uv[1]);
+ float last_uv[2], start_uv[2];
+ UI_view2d_region_to_view(s->v2d, 0.0f, 0.0f, &start_uv[0], &start_uv[1]);
if (painter->firsttouch) {
- float startuv[2];
-
- UI_view2d_region_to_view(s->v2d, 0, 0, &startuv[0], &startuv[1]);
-
/* paint exactly once on first touch */
- painter->startpaintpos[0] = startuv[0] * ibuf->x;
- painter->startpaintpos[1] = startuv[1] * ibuf->y;
-
- painter->firsttouch = 0;
- copy_v2_v2(painter->lastpaintpos, newuv);
+ copy_v2_v2(last_uv, new_uv);
}
else {
- copy_v2_v2(painter->lastpaintpos, olduv);
+ copy_v2_v2(last_uv, old_uv);
}
- /* OCIO_TODO: float buffers are now always linear, so always use color correction
- * this should probably be changed when texture painting color space is supported
- */
- brush_painter_2d_require_imbuf(painter, (ibuf->rect_float != NULL), !is_data);
+ float uv_brush_size[2] = {base_size / s->tiles[0].size[0], base_size / s->tiles[0].size[1]};
+
+ for (int i = 0; i < s->num_tiles; i++) {
+ ImagePaintTile *tile = &s->tiles[i];
+
+ /* First test: Project brush into UV space, clip against tile. */
+ const int uv_size[2] = {1, 1};
+ float local_new_uv[2], local_old_uv[2];
+ sub_v2_v2v2(local_new_uv, new_uv, tile->uv_ofs);
+ sub_v2_v2v2(local_old_uv, old_uv, tile->uv_ofs);
+ if (!(is_inside_tile(uv_size, local_new_uv, uv_brush_size) || is_inside_tile(uv_size, local_old_uv, uv_brush_size)))
+ continue;
+
+ /* Lazy tile loading to get size in pixels. */
+ if (!paint_2d_check_tile(s, i))
+ continue;
+
+ float size[2];
+ mul_v2_v2fl(size, tile->radius_fac, base_size);
+
+ float new_coord[2], old_coord[2];
+ paint_2d_uv_to_coord(tile, new_uv, new_coord);
+ paint_2d_uv_to_coord(tile, old_uv, old_coord);
+ if (painter->firsttouch) {
+ paint_2d_uv_to_coord(tile, start_uv, tile->start_paintpos);
+ }
+ paint_2d_uv_to_coord(tile, last_uv, tile->last_paintpos);
+
+ /* Second check in pixel coordinates. */
+ if (!(is_inside_tile(tile->size, new_coord, size) || is_inside_tile(tile->size, old_coord, size)))
+ continue;
- brush_painter_2d_refresh_cache(s, painter, newuv, mval, pressure, distance, size);
+ ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, &tile->iuser, NULL);
- if (paint_2d_op(s, painter->cache.ibuf, painter->cache.curve_mask, painter->cache.tex_mask, olduv, newuv))
- s->need_redraw = true;
+ /* OCIO_TODO: float buffers are now always linear, so always use color correction
+ * this should probably be changed when texture painting color space is supported
+ */
+ brush_painter_2d_require_imbuf(painter->brush, tile, (ibuf->rect_float != NULL), !is_data);
- BKE_image_release_ibuf(s->image, ibuf, NULL);
+ brush_painter_2d_refresh_cache(s, painter, tile, new_coord, mval, pressure, distance, size);
+
+ if (paint_2d_op(s, tile, old_coord, new_coord))
+ tile->need_redraw = true;
+ }
+
+ painter->firsttouch = 0;
}
void *paint_2d_new_stroke(bContext *C, wmOperator *op, int mode)
@@ -1339,12 +1426,53 @@ void *paint_2d_new_stroke(bContext *C, wmOperator *op, int mode)
s->image = s->sima->image;
s->symmetry = settings->imapaint.paint.symmetry_flags;
- if (!paint_2d_canvas_set(s, s->image)) {
- if (s->warnmultifile)
- BKE_report(op->reports, RPT_WARNING, "Image requires 4 color channels to paint");
- if (s->warnpackedfile)
- BKE_report(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted");
+ if (s->image == NULL) {
+ MEM_freeN(s);
+ return NULL;
+ }
+ if (BKE_image_has_packedfile(s->image) && s->image->rr) {
+ BKE_report(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted");
+ MEM_freeN(s);
+ return 0;
+ }
+
+ s->num_tiles = BLI_listbase_count(&s->image->tiles);
+ s->tiles = MEM_callocN(sizeof(ImagePaintTile) * s->num_tiles, "ImagePaintTile");
+ s->tiles[0].iuser.ok = true;
+
+ zero_v2(s->tiles[0].uv_ofs);
+
+ ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, &s->tiles[0].iuser, NULL);
+ if (!ibuf) {
+ MEM_freeN(s->tiles);
+ MEM_freeN(s);
+ return NULL;
+ }
+ if (ibuf->channels != 4) {
+ BKE_report(op->reports, RPT_WARNING, "Image requires 4 color channels to paint");
+ MEM_freeN(s->tiles);
+ MEM_freeN(s);
+ return NULL;
+ }
+
+ s->tiles[0].size[0] = ibuf->x;
+ s->tiles[0].size[1] = ibuf->y;
+ copy_v2_fl(s->tiles[0].radius_fac, 1.0f);
+
+ s->tiles[0].canvas = ibuf;
+ s->tiles[0].state = PAINT2D_TILE_READY;
+
+ /* Initialize offsets here, they're needed for the uv space clip test before lazy-loading the tile properly. */
+ ImageTile *tile = BKE_image_get_tile(s->image, 0)->next;
+ for (int tile_idx = 1; tile; tile = tile->next, tile_idx++) {
+ s->tiles[tile_idx].iuser.tile = tile->tile_number;
+ s->tiles[tile_idx].uv_ofs[0] = (tile->tile_number % 10);
+ s->tiles[tile_idx].uv_ofs[1] = (tile->tile_number / 10);
+ }
+
+ if (!paint_2d_canvas_set(s)) {
+ MEM_freeN(s->tiles);
MEM_freeN(s);
return NULL;
}
@@ -1365,18 +1493,26 @@ void paint_2d_redraw(const bContext *C, void *ps, bool final)
{
ImagePaintState *s = ps;
- if (s->need_redraw) {
- ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, s->sima ? &s->sima->iuser : NULL, NULL);
+ bool had_redraw = false;
+ for (int i = 0; i < s->num_tiles; i++) {
+ if (s->tiles[i].need_redraw) {
+ ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, &s->tiles[i].iuser, NULL);
- imapaint_image_update(s->sima, s->image, ibuf, false);
- ED_imapaint_clear_partial_redraw();
+ imapaint_image_update(s->sima, s->image, ibuf, &s->tiles[i].iuser, false);
- BKE_image_release_ibuf(s->image, ibuf, NULL);
+ BKE_image_release_ibuf(s->image, ibuf, NULL);
- s->need_redraw = false;
+ s->tiles[i].need_redraw = false;
+ had_redraw = true;
+ }
}
- else if (!final) {
- return;
+
+ if (had_redraw) {
+ ED_imapaint_clear_partial_redraw();
+ if (!s->sima || !s->sima->lock)
+ ED_region_tag_redraw(CTX_wm_region(C));
+ else
+ WM_event_add_notifier(C, NC_IMAGE | NA_PAINTING, s->image);
}
if (final) {
@@ -1387,12 +1523,6 @@ void paint_2d_redraw(const bContext *C, void *ps, bool final)
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, s->image);
DEG_id_tag_update(&s->image->id, 0);
}
- else {
- if (!s->sima || !s->sima->lock)
- ED_region_tag_redraw(CTX_wm_region(C));
- else
- WM_event_add_notifier(C, NC_IMAGE | NA_PAINTING, s->image);
- }
}
void paint_2d_stroke_done(void *ps)
@@ -1400,7 +1530,11 @@ void paint_2d_stroke_done(void *ps)
ImagePaintState *s = ps;
paint_2d_canvas_free(s);
- brush_painter_2d_free(s->painter);
+ for (int i = 0; i < s->num_tiles; i++) {
+ brush_painter_cache_2d_free(&s->tiles[i].cache);
+ }
+ MEM_freeN(s->painter);
+ MEM_freeN(s->tiles);
paint_brush_exit_tex(s->brush);
MEM_freeN(s);
@@ -1451,7 +1585,7 @@ static void paint_2d_fill_add_pixel_float(
/* this function expects linear space color values */
void paint_2d_bucket_fill(
const bContext *C, const float color[3], Brush *br,
- const float mouse_init[2],
+ const float mouse_init[2], const float mouse_final[2],
void *ps)
{
SpaceImage *sima = CTX_wm_space_image(C);
@@ -1470,7 +1604,26 @@ void paint_2d_bucket_fill(
if (!ima)
return;
- ibuf = BKE_image_acquire_ibuf(ima, &sima->iuser, NULL);
+ float uv_ofs[2];
+ float image_init[2], image_final[2];
+ paint_2d_transform_mouse(s, mouse_init, image_init);
+ int tile_number = BKE_image_get_tile_from_pos(ima, image_init, image_init, uv_ofs);
+ if (mouse_final) {
+ paint_2d_transform_mouse(s, mouse_final, image_final);
+ sub_v2_v2(image_final, uv_ofs);
+ }
+
+ ImageUser *iuser = &s->tiles[0].iuser;
+ for (int i = 0; i < s->num_tiles; i++) {
+ if (s->tiles[i].iuser.tile == tile_number) {
+ if (!paint_2d_check_tile(s, i))
+ return;
+ iuser = &s->tiles[i].iuser;
+ break;
+ }
+ }
+
+ ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL);
if (!ibuf)
return;
@@ -1488,7 +1641,7 @@ void paint_2d_bucket_fill(
color_f[3] = strength;
}
- if (!mouse_init || !br) {
+ if (!mouse_final || !br) {
/* first case, no image UV, fill the whole image */
ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false);
@@ -1518,14 +1671,11 @@ void paint_2d_bucket_fill(
BLI_bitmap *touched;
size_t coordinate;
int width = ibuf->x;
- float image_init[2];
int minx = ibuf->x, miny = ibuf->y, maxx = 0, maxy = 0;
float pixel_color[4];
/* We are comparing to sum of three squared values (assumed in range [0,1]), so need to multiply... */
float threshold_sq = br->fill_threshold * br->fill_threshold * 3;
- UI_view2d_region_to_view(s->v2d, mouse_init[0], mouse_init[1], &image_init[0], &image_init[1]);
-
x_px = image_init[0] * ibuf->x;
y_px = image_init[1] * ibuf->y;
@@ -1622,7 +1772,7 @@ void paint_2d_bucket_fill(
BLI_stack_free(stack);
}
- imapaint_image_update(sima, ima, ibuf, false);
+ imapaint_image_update(sima, ima, ibuf, iuser, false);
ED_imapaint_clear_partial_redraw();
BKE_image_release_ibuf(ima, ibuf, NULL);
@@ -1652,13 +1802,29 @@ void paint_2d_gradient_fill(
if (!ima)
return;
- ibuf = BKE_image_acquire_ibuf(ima, &sima->iuser, NULL);
+ paint_2d_transform_mouse(s, mouse_final, image_final);
+ paint_2d_transform_mouse(s, mouse_init, image_init);
+
+ float uv_ofs[2];
+ int tile_number = BKE_image_get_tile_from_pos(ima, image_init, image_init, uv_ofs);
+ sub_v2_v2(image_init, uv_ofs);
+ sub_v2_v2(image_final, uv_ofs);
+
+ ImageUser *iuser = &s->tiles[0].iuser;
+ for (int i = 0; i < s->num_tiles; i++) {
+ if (s->tiles[i].iuser.tile == tile_number) {
+ if (!paint_2d_check_tile(s, i))
+ return;
+ iuser = &s->tiles[i].iuser;
+ break;
+ }
+ }
+
+ ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL);
if (!ibuf)
return;
- UI_view2d_region_to_view(s->v2d, mouse_final[0], mouse_final[1], &image_final[0], &image_final[1]);
- UI_view2d_region_to_view(s->v2d, mouse_init[0], mouse_init[1], &image_init[0], &image_init[1]);
image_final[0] *= ibuf->x;
image_final[1] *= ibuf->y;
@@ -1739,7 +1905,7 @@ void paint_2d_gradient_fill(
}
}
- imapaint_image_update(sima, ima, ibuf, false);
+ imapaint_image_update(sima, ima, ibuf, iuser, false);
ED_imapaint_clear_partial_redraw();
BKE_image_release_ibuf(ima, ibuf, NULL);
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index 7badd30e6d0..50d42aed248 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -193,6 +193,8 @@ BLI_INLINE unsigned char f_to_char(const float val)
* because 'partRedrawRect' and 'touch' values would not be thread safe */
typedef struct ProjPaintImage {
Image *ima;
+ int tile;
+ ImageUser iuser;
ImBuf *ibuf;
ImagePaintPartialRedraw *partRedrawRect;
volatile void **undoRect; /* only used to build undo tiles during painting */
@@ -468,6 +470,16 @@ BLI_INLINE const MPoly *ps_tri_index_to_mpoly(const ProjPaintState *ps, int tri_
/* Finish projection painting structs */
+static int project_paint_face_paint_tile(Image *ima, const float *uv)
+{
+ if (!ima || ima->source != IMA_SRC_TILED)
+ return 0;
+ /* Currently, faces are assumed to belong to one tile, so checking the first loop is enough. */
+ int tx = (int) uv[0];
+ int ty = (int) uv[1];
+ return 10*ty + tx;
+}
+
static TexPaintSlot *project_paint_face_paint_slot(const ProjPaintState *ps, int tri_index)
{
const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index);
@@ -664,8 +676,18 @@ static bool project_paint_PickColor(
interp_v2_v2v2v2(uv, UNPACK3(lt_tri_uv), w);
ima = project_paint_face_paint_image(ps, tri_index);
- ibuf = BKE_image_get_first_ibuf(ima); /* we must have got the imbuf before getting here */
- if (!ibuf) return 0;
+ int tile_number = project_paint_face_paint_tile(ima, lt_tri_uv[0]);
+ ImageUser iuser = {NULL};
+ iuser.ok = 1;
+ iuser.tile = tile_number;
+ ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
+ if (!ibuf) {
+ iuser.tile = 0;
+ ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
+ if (!ibuf) {
+ return 0;
+ }
+ }
if (interp) {
float x, y;
@@ -1052,6 +1074,8 @@ static bool check_seam(
const float *lt_tri_uv[3] = { PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt) };
Image *tpage = project_paint_face_paint_image(ps, tri_index);
Image *orig_tpage = project_paint_face_paint_image(ps, orig_face);
+ int tile = project_paint_face_paint_tile(tpage, lt_tri_uv[0]);
+ int orig_tile = project_paint_face_paint_tile(orig_tpage, orig_lt_tri_uv[0]);
BLI_assert(i1_fidx != -1);
@@ -1069,6 +1093,7 @@ static bool check_seam(
/* first test if they have the same image */
if ((orig_tpage == tpage) &&
+ (orig_tile == tile) &&
cmp_uv(orig_lt_tri_uv[orig_i1_fidx], lt_tri_uv[i1_fidx]) &&
cmp_uv(orig_lt_tri_uv[orig_i2_fidx], lt_tri_uv[i2_fidx]))
{
@@ -2921,6 +2946,7 @@ static void project_bucket_init(
ImBuf *ibuf = NULL;
Image *tpage_last = NULL, *tpage;
ImBuf *tmpibuf = NULL;
+ int tile_last = 0;
if (ps->image_tot == 1) {
/* Simple loop, no context switching */
@@ -2938,17 +2964,33 @@ static void project_bucket_init(
for (node = ps->bucketFaces[bucket_index]; node; node = node->next) {
tri_index = GET_INT_FROM_POINTER(node->link);
+ const MLoopTri *lt = &ps->mlooptri_eval[tri_index];
+ const float *lt_tri_uv[3] = { PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt) };
+
/* Image context switching */
tpage = project_paint_face_paint_image(ps, tri_index);
- if (tpage_last != tpage) {
+ int tile = project_paint_face_paint_tile(tpage, lt_tri_uv[0]);
+ if (tpage_last != tpage || tile_last != tile) {
tpage_last = tpage;
+ tile_last = tile;
for (image_index = 0; image_index < ps->image_tot; image_index++) {
- if (ps->projImages[image_index].ima == tpage_last) {
- ibuf = ps->projImages[image_index].ibuf;
+ ProjPaintImage *projIma = &ps->projImages[image_index];
+ if ((projIma->ima == tpage) && (projIma->tile == tile)) {
+ ibuf = projIma->ibuf;
break;
}
}
+ if (ibuf == NULL) {
+ /* Failed to find the specific tile, fall back to the primary tile. */
+ for (image_index = 0; image_index < ps->image_tot; image_index++) {
+ ProjPaintImage *projIma = &ps->projImages[image_index];
+ if ((projIma->ima == tpage) && (projIma->tile == 0)) {
+ ibuf = projIma->ibuf;
+ break;
+ }
+ }
+ }
}
/* context switching done */
@@ -3624,22 +3666,36 @@ static bool project_paint_winclip(
#endif //PROJ_DEBUG_WINCLIP
+typedef struct PrepareImageEntry {
+ struct PrepareImageEntry *next, *prev;
+ Image *ima;
+ int tile;
+} PrepareImageEntry;
+
static void project_paint_build_proj_ima(
ProjPaintState *ps, MemArena *arena,
- LinkNode *image_LinkList)
+ ListBase *used_images)
{
ProjPaintImage *projIma;
- LinkNode *node;
+ PrepareImageEntry *entry;
int i;
/* build an array of images we use */
projIma = ps->projImages = BLI_memarena_alloc(arena, sizeof(ProjPaintImage) * ps->image_tot);
- for (node = image_LinkList, i = 0; node; node = node->next, i++, projIma++) {
+ for (entry = used_images->first, i = 0; entry; entry = entry->next, i++, projIma++) {
+ memset(&projIma->iuser, 0, sizeof(ImageUser));
+ projIma->iuser.ok = 1;
+ projIma->iuser.tile = entry->tile;
int size;
- projIma->ima = node->link;
+ projIma->ima = entry->ima;
+ projIma->tile = entry->tile;
projIma->touch = 0;
- projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, NULL, NULL);
+ projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, &projIma->iuser, NULL);
+ if (!projIma->ibuf) {
+ projIma->iuser.tile = 0;
+ projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, &projIma->iuser, NULL);
+ }
size = sizeof(void **) * IMAPAINT_TILE_NUMBER(projIma->ibuf->x) * IMAPAINT_TILE_NUMBER(projIma->ibuf->y);
projIma->partRedrawRect = BLI_memarena_alloc(arena, sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED);
partial_redraw_array_init(projIma->partRedrawRect);
@@ -3660,15 +3716,18 @@ static void project_paint_prepare_all_faces(
const bool is_multi_view)
{
/* Image Vars - keep track of images we have used */
- LinkNodePair image_LinkList = {NULL, NULL};
+ ListBase used_images = {NULL};
Image *tpage_last = NULL, *tpage;
TexPaintSlot *slot_last = NULL;
TexPaintSlot *slot = NULL;
+ int tile_last = -1, tile;
const MLoopTri *lt;
int image_index = -1, tri_index;
int prev_poly = -1;
+ BLI_assert(ps->image_tot == 0);
+
for (tri_index = 0, lt = ps->mlooptri_eval; tri_index < ps->totlooptri_eval; tri_index++, lt++) {
bool is_face_sel;
@@ -3709,6 +3768,8 @@ static void project_paint_prepare_all_faces(
ps->poly_to_loop_uv[lt->poly] = mloopuv_base;
+ tile = project_paint_face_paint_tile(tpage, mloopuv_base[lt->tri[0]].uv);
+
if (project_paint_clone_face_skip(ps, layer_clone, slot, tri_index)) {
continue;
}
@@ -3766,17 +3827,24 @@ static void project_paint_prepare_all_faces(
}
}
- if (tpage_last != tpage) {
-
- image_index = BLI_linklist_index(image_LinkList.list, tpage);
+ if (tpage_last != tpage || tile_last != tile) {
+ image_index = 0;
+ for (PrepareImageEntry *e = used_images.first; e; e = e->next, image_index++) {
+ if (e->ima == tpage && e->tile == tile) {
+ break;
+ }
+ }
- if (image_index == -1 && BKE_image_has_ibuf(tpage, NULL)) { /* MemArena dosnt have an append func */
- BLI_linklist_append(&image_LinkList, tpage);
- image_index = ps->image_tot;
+ if (image_index == ps->image_tot) {
+ PrepareImageEntry *e = MEM_callocN(sizeof(PrepareImageEntry), "PrepareImageEntry");
+ e->ima = tpage;
+ e->tile = tile;
+ BLI_addtail(&used_images, e);
ps->image_tot++;
}
tpage_last = tpage;
+ tile_last = tile;
}
if (image_index != -1) {
@@ -3789,11 +3857,11 @@ static void project_paint_prepare_all_faces(
/* build an array of images we use*/
if (ps->is_shared_user == false) {
- project_paint_build_proj_ima(ps, arena, image_LinkList.list);
+ project_paint_build_proj_ima(ps, arena, &used_images);
}
/* we have built the array, discard the linked list */
- BLI_linklist_free(image_LinkList.list, NULL);
+ BLI_freelistN(&used_images);
}
/* run once per stroke before projection painting */
@@ -4036,7 +4104,7 @@ static bool project_image_refresh_tagged(ProjPaintState *ps)
pr = &(projIma->partRedrawRect[i]);
if (pr->x2 != -1) { /* TODO - use 'enabled' ? */
set_imapaintpartial(pr);
- imapaint_image_update(NULL, projIma->ima, projIma->ibuf, true);
+ imapaint_image_update(NULL, projIma->ima, projIma->ibuf, &projIma->iuser, true);
redraw = 1;
}
@@ -5644,7 +5712,7 @@ static Image *proj_paint_image_create(wmOperator *op, Main *bmain)
}
ima = BKE_image_add_generated(
bmain, width, height, imagename, alpha ? 32 : 24, use_float,
- gen_type, color, false);
+ gen_type, color, false, false);
return ima;
}
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index 7d3049434d6..eb4fb793967 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -181,7 +181,7 @@ typedef struct ImagePaintPartialRedraw {
#define IMAPAINT_TILE_NUMBER(size) (((size) + IMAPAINT_TILE_SIZE - 1) >> IMAPAINT_TILE_BITS)
bool image_texture_paint_poll(struct bContext *C);
-void imapaint_image_update(struct SpaceImage *sima, struct Image *image, struct ImBuf *ibuf, short texpaint);
+void imapaint_image_update(struct SpaceImage *sima, struct Image *image, struct ImBuf *ibuf, struct ImageUser *iuser, short texpaint);
struct ImagePaintPartialRedraw *get_imapaintpartial(void);
void set_imapaintpartial(struct ImagePaintPartialRedraw *ippr);
void imapaint_region_tiles(struct ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty, int *tw, int *th);
@@ -193,7 +193,7 @@ void paint_2d_stroke(
void *ps, const float prev_mval[2], const float mval[2],
const bool eraser, float pressure, float distance, float size);
void paint_2d_bucket_fill(
- const struct bContext *C, const float color[3], struct Brush *br, const float mouse_init[2], void *ps);
+ const struct bContext *C, const float color[3], struct Brush *br, const float mouse_init[2], const float mouse_final[2], void *ps);
void paint_2d_gradient_fill(
const struct bContext *C, struct Brush *br, const float mouse_init[2], const float mouse_final[2], void *ps);
void *paint_proj_new_stroke(
diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c
index ebdf5342172..a3f91bf5090 100644
--- a/source/blender/editors/space_clip/clip_draw.c
+++ b/source/blender/editors/space_clip/clip_draw.c
@@ -1790,7 +1790,7 @@ void clip_draw_main(const bContext *C, SpaceClip *sc, ARegion *ar)
/* if no clip, nothing to do */
if (!clip) {
- ED_region_grid_draw(ar, zoomx, zoomy);
+ ED_region_grid_draw(ar, zoomx, zoomy, 0.0f, 0.0f);
return;
}
@@ -1836,7 +1836,7 @@ void clip_draw_main(const bContext *C, SpaceClip *sc, ARegion *ar)
draw_movieclip_muted(ar, width, height, zoomx, zoomy);
}
else {
- ED_region_grid_draw(ar, zoomx, zoomy);
+ ED_region_grid_draw(ar, zoomx, zoomy, 0.0f, 0.0f);
}
if (width && height) {
diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c
index 1d8c6721b64..70358cc47fd 100644
--- a/source/blender/editors/space_image/image_buttons.c
+++ b/source/blender/editors/space_image/image_buttons.c
@@ -112,7 +112,7 @@ static void image_info(Scene *scene, ImageUser *iuser, Image *ima, ImBuf *ibuf,
if (ibuf->zbuf || ibuf->zbuf_float)
ofs += BLI_strncpy_rlen(str + ofs, IFACE_(" + Z"), len - ofs);
- if (ima->source == IMA_SRC_SEQUENCE) {
+ if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) {
const char *file = BLI_last_slash(ibuf->name);
if (file == NULL)
file = ibuf->name;
diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c
index 4cbe25462af..c49190a97af 100644
--- a/source/blender/editors/space_image/image_draw.c
+++ b/source/blender/editors/space_image/image_draw.c
@@ -49,6 +49,7 @@
#include "BLI_threads.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "BLI_listbase.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
@@ -479,7 +480,7 @@ static void sima_draw_zbuffloat_pixels(Scene *scene, float x1, float y1, int rec
MEM_freeN(rectf);
}
-static void draw_image_buffer(const bContext *C, SpaceImage *sima, ARegion *ar, Scene *scene, ImBuf *ibuf, float fx, float fy, float zoomx, float zoomy)
+static void draw_image_buffer(const bContext *C, SpaceImage *sima, ARegion *ar, Scene *scene, ImBuf *ibuf, float fx, float fy, float zoomx, float zoomy, const char *label)
{
int x, y;
@@ -547,6 +548,24 @@ static void draw_image_buffer(const bContext *C, SpaceImage *sima, ARegion *ar,
if (sima->flag & SI_USE_ALPHA)
GPU_blend(false);
}
+
+ if (label && label[0]) {
+ glEnable(GL_BLEND);
+ glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ BLF_size(blf_mono_font, 25 * U.pixelsize, U.dpi);
+
+ int textwidth = BLF_width(blf_mono_font, label, strlen(label)) + 10;
+ float opacity;
+ float stepx = BLI_rcti_size_x(&ar->v2d.mask) / BLI_rctf_size_x(&ar->v2d.cur);
+ if (textwidth < 0.5f*(stepx - 10)) opacity = 1.0f;
+ else if (textwidth < (stepx - 10)) opacity = 2.0f - 2.0f*(textwidth / (stepx - 10));
+ else opacity = 0.0f;
+ BLF_color4ub(blf_mono_font, 220, 220, 220, 150*opacity);
+ BLF_position(blf_mono_font, (int) (x + 10), (int) (y + 10), 0);
+ BLF_draw_ascii(blf_mono_font, label, strlen(label));
+
+ glDisable(GL_BLEND);
+ }
}
/* draw uv edit */
@@ -586,9 +605,13 @@ void draw_image_sample_line(SpaceImage *sima)
immUniformArray4fv("colors", (float *)(float[][4]){{1.0f, 1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 1.0f}}, 2);
immUniform1f("dash_width", 2.0f);
+ float co[2][2];
+ add_v2_v2v2(co[0], hist->co[0], hist->draw_offset);
+ add_v2_v2v2(co[1], hist->co[1], hist->draw_offset);
+
immBegin(GPU_PRIM_LINES, 2);
- immVertex2fv(shdr_dashed_pos, hist->co[0]);
- immVertex2fv(shdr_dashed_pos, hist->co[1]);
+ immVertex2fv(shdr_dashed_pos, co[0]);
+ immVertex2fv(shdr_dashed_pos, co[1]);
immEnd();
immUnbindProgram();
@@ -633,6 +656,69 @@ static void draw_image_paint_helpers(const bContext *C, ARegion *ar, Scene *scen
}
}
+static void draw_udim_tile_grid(unsigned int pos_attr, unsigned int color_attr,
+ ARegion *ar, int x, int y,
+ float stepx, float stepy, const float color[3])
+{
+ float x1, y1;
+ UI_view2d_view_to_region_fl(&ar->v2d, x, y, &x1, &y1);
+ int gridpos[5][2] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
+ for(int i = 0; i < 4; i++) {
+ immAttrib3fv(color_attr, color);
+ immVertex2f(pos_attr, x1 + gridpos[i][0]*stepx, y1 + gridpos[i][1]*stepy);
+ immAttrib3fv(color_attr, color);
+ immVertex2f(pos_attr, x1 + gridpos[i+1][0]*stepx, y1 + gridpos[i+1][1]*stepy);
+ }
+}
+
+static void draw_image_tiles(ARegion *ar, SpaceImage *sima, Image *ima)
+{
+ int num_tiles;
+ if (ima) {
+ num_tiles = BLI_listbase_count(&ima->tiles);
+ }
+ else {
+ num_tiles = sima->tile_grid_shape[0] * sima->tile_grid_shape[1];
+ }
+
+ float stepx = BLI_rcti_size_x(&ar->v2d.mask) / BLI_rctf_size_x(&ar->v2d.cur);
+ float stepy = BLI_rcti_size_y(&ar->v2d.mask) / BLI_rctf_size_y(&ar->v2d.cur);
+
+ GPUVertFormat *format = immVertexFormat();
+ unsigned int pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ unsigned color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+ immBegin(GPU_PRIM_LINES, 8*(num_tiles + 1));
+
+ float theme_color[3], selected_color[3];
+ UI_GetThemeColorShade3fv(TH_BACK, 60.0f, theme_color);
+ UI_GetThemeColor3fv(TH_FACE_SELECT, selected_color);
+
+ if (ima) {
+ LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) {
+ int x = tile->tile_number % 10;
+ int y = tile->tile_number / 10;
+ draw_udim_tile_grid(pos, color, ar, x, y, stepx, stepy, theme_color);
+ }
+ }
+ else {
+ for (int y = 0; y < sima->tile_grid_shape[1]; y++) {
+ for (int x = 0; x < sima->tile_grid_shape[0]; x++) {
+ draw_udim_tile_grid(pos, color, ar, x, y, stepx, stepy, theme_color);
+ }
+ }
+ }
+
+ int cur_x = sima->curtile % 10, cur_y = sima->curtile / 10;
+ draw_udim_tile_grid(pos, color, ar, cur_x, cur_y, stepx, stepy, selected_color);
+
+ immEnd();
+ immUnbindProgram();
+
+ return;
+}
+
/* draw main image region */
void draw_image_main(const bContext *C, ARegion *ar)
@@ -692,14 +778,35 @@ void draw_image_main(const bContext *C, ARegion *ar)
BKE_image_multiview_index(ima, &sima->iuser);
}
- ibuf = ED_space_image_acquire_buffer(sima, &lock);
+ ibuf = ED_space_image_acquire_buffer(sima, &lock, 0);
+
+ int main_w = 0;
+ int main_h = 0;
/* draw the image or grid */
if (ibuf == NULL) {
- ED_region_grid_draw(ar, zoomx, zoomy);
+ if (ima) {
+ LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) {
+ int x = tile->tile_number % 10;
+ int y = tile->tile_number / 10;
+ ED_region_grid_draw(ar, zoomx, zoomy, x, y);
+ }
+ }
+ else {
+ for (int y = 0; y < sima->tile_grid_shape[1]; y++) {
+ for (int x = 0; x < sima->tile_grid_shape[0]; x++) {
+ ED_region_grid_draw(ar, zoomx, zoomy, x, y);
+ }
+ }
+ }
}
else {
- draw_image_buffer(C, sima, ar, scene, ibuf, 0.0f, 0.0f, zoomx, zoomy);
+ main_w = ibuf->x;
+ main_h = ibuf->y;
+
+ char label[64];
+ BKE_image_get_tile_label(ima, 0, label, sizeof(label));
+ draw_image_buffer(C, sima, ar, scene, ibuf, 0.0f, 0.0f, zoomx, zoomy, label);
if (sima->flag & SI_DRAW_METADATA) {
int x, y;
@@ -714,6 +821,28 @@ void draw_image_main(const bContext *C, ARegion *ar)
ED_space_image_release_buffer(sima, ibuf, lock);
+ if (ima && ima->source == IMA_SRC_TILED) {
+ LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) {
+ if (tile->tile_number == 0)
+ continue;
+
+ ibuf = ED_space_image_acquire_buffer(sima, &lock, tile->tile_number);
+ if (ibuf) {
+ int x_pos = tile->tile_number % 10;
+ int y_pos = tile->tile_number / 10;
+ char label[64];
+ BKE_image_get_tile_label(ima, tile, label, sizeof(label));
+
+ float tile_zoomx = (zoomx * main_w) / ibuf->x;
+ float tile_zoomy = (zoomy * main_h) / ibuf->y;
+ draw_image_buffer(C, sima, ar, scene, ibuf, x_pos, y_pos, tile_zoomx, tile_zoomy, label);
+ }
+ ED_space_image_release_buffer(sima, ibuf, lock);
+ }
+ }
+
+ draw_image_tiles(ar, sima, ima);
+
/* paint helpers */
if (show_paint)
draw_image_paint_helpers(C, ar, scene, zoomx, zoomy);
diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c
index 4388accc534..140f4dcc097 100644
--- a/source/blender/editors/space_image/image_edit.c
+++ b/source/blender/editors/space_image/image_edit.c
@@ -105,7 +105,7 @@ void ED_space_image_set_mask(bContext *C, SpaceImage *sima, Mask *mask)
}
}
-ImBuf *ED_space_image_acquire_buffer(SpaceImage *sima, void **r_lock)
+ImBuf *ED_space_image_acquire_buffer(SpaceImage *sima, void **r_lock, int tile)
{
ImBuf *ibuf;
@@ -115,7 +115,9 @@ ImBuf *ED_space_image_acquire_buffer(SpaceImage *sima, void **r_lock)
return BIF_render_spare_imbuf();
else
#endif
+ sima->iuser.tile = tile;
ibuf = BKE_image_acquire_ibuf(sima->image, &sima->iuser, r_lock);
+ sima->iuser.tile = 0;
if (ibuf) {
if (ibuf->rect || ibuf->rect_float)
@@ -142,7 +144,7 @@ bool ED_space_image_has_buffer(SpaceImage *sima)
void *lock;
bool has_buffer;
- ibuf = ED_space_image_acquire_buffer(sima, &lock);
+ ibuf = ED_space_image_acquire_buffer(sima, &lock, 0);
has_buffer = (ibuf != NULL);
ED_space_image_release_buffer(sima, ibuf, lock);
@@ -155,7 +157,8 @@ void ED_space_image_get_size(SpaceImage *sima, int *width, int *height)
ImBuf *ibuf;
void *lock;
- ibuf = ED_space_image_acquire_buffer(sima, &lock);
+ /* TODO(lukas): Support tiled images with different sizes */
+ ibuf = ED_space_image_acquire_buffer(sima, &lock, 0);
if (ibuf && ibuf->x > 0 && ibuf->y > 0) {
*width = ibuf->x;
diff --git a/source/blender/editors/space_image/image_intern.h b/source/blender/editors/space_image/image_intern.h
index 5d9c496d584..143f2446b8d 100644
--- a/source/blender/editors/space_image/image_intern.h
+++ b/source/blender/editors/space_image/image_intern.h
@@ -97,6 +97,11 @@ void IMAGE_OT_read_viewlayers(struct wmOperatorType *ot);
void IMAGE_OT_render_border(struct wmOperatorType *ot);
void IMAGE_OT_clear_render_border(struct wmOperatorType *ot);
+void IMAGE_OT_add_tile(struct wmOperatorType *ot);
+void IMAGE_OT_remove_tile(struct wmOperatorType *ot);
+void IMAGE_OT_fill_tile(struct wmOperatorType *ot);
+void IMAGE_OT_select_tile(struct wmOperatorType *ot);
+
/* image_panels.c */
struct ImageUser *ntree_get_active_iuser(struct bNodeTree *ntree);
void image_buttons_register(struct ARegionType *art);
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index be91f7abf99..bcbfa1342d5 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -45,6 +45,9 @@
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
#include "BLI_string_utf8.h"
+#include "BLI_fileops.h"
+#include "BLI_fileops_types.h"
+#include "BLI_linklist.h"
#include "BLT_translation.h"
@@ -223,7 +226,7 @@ static bool space_image_file_exists_poll(bContext *C)
bool ret = false;
char name[FILE_MAX];
- ibuf = ED_space_image_acquire_buffer(sima, &lock);
+ ibuf = ED_space_image_acquire_buffer(sima, &lock, 0);
if (ibuf) {
BLI_strncpy(name, ibuf->name, FILE_MAX);
BLI_path_abs(name, BKE_main_blendfile_path(bmain));
@@ -1150,6 +1153,52 @@ static int image_cmp_frame(const void *a, const void *b)
return 0;
}
+static int image_get_udim(const char *filepath, LinkNodePair *udim_tiles)
+{
+ if (strstr(filepath, "1001") == NULL) {
+ return 0;
+ }
+
+ char filename[FILE_MAX], dirname[FILE_MAXDIR];
+ BLI_split_dirfile(filepath, dirname, filename, sizeof(dirname), sizeof(filename));
+
+ bool is_udim = true;
+ int max_udim = 0;
+
+ unsigned short digits;
+ char base_head[FILE_MAX], base_tail[FILE_MAX];
+ int id = BLI_stringdec(filename, base_head, base_tail, &digits);
+ if (id == 1001) {
+ struct direntry *dir;
+ uint totfile = BLI_filelist_dir_contents(dirname, &dir);
+ for (int i = 0; i < totfile; i++) {
+ if (!(dir[i].type & S_IFREG)) {
+ continue;
+ }
+ char head[FILE_MAX], tail[FILE_MAX];
+ id = BLI_stringdec(dir[i].relname, head, tail, &digits);
+
+ if (digits > 4 ||
+ !(STREQLEN(base_head, head, FILE_MAX)) ||
+ !(STREQLEN(base_tail, tail, FILE_MAX))) {
+ continue;
+ }
+
+ if (id < 1001 || id >= 2000) {
+ is_udim = false;
+ break;
+ }
+
+ BLI_linklist_append(udim_tiles, SET_INT_IN_POINTER(id - 1001));
+ max_udim = max_ii(max_udim, id);
+ }
+
+ BLI_filelist_free(dir, totfile);
+ }
+
+ return is_udim? (max_udim - 1001) : 0;
+}
+
/**
* Return the start (offset) and the length of the sequence of continuous frames in the list of frames
*
@@ -1157,21 +1206,27 @@ static int image_cmp_frame(const void *a, const void *b)
* \param ofs: [out] offset the first frame number in the sequence.
* \return the number of contiguous frames in the sequence
*/
-static int image_sequence_get_len(ListBase *frames, int *ofs)
+static int image_sequence_get_len(ImageFrameRange *frame_range, int *ofs, LinkNodePair *udim_tiles)
{
ImageFrame *frame;
- BLI_listbase_sort(frames, image_cmp_frame);
+ BLI_listbase_sort(&frame_range->frames, image_cmp_frame);
- frame = frames->first;
+ frame = frame_range->frames.first;
if (frame) {
int frame_curr = frame->framenr;
(*ofs) = frame_curr;
- while (frame && (frame->framenr == frame_curr)) {
- frame_curr++;
- frame = frame->next;
+
+ if (udim_tiles && (frame_curr == 1001)) {
+ return 1 + image_get_udim(frame_range->filepath, udim_tiles);
+ }
+ else {
+ while (frame && (frame->framenr == frame_curr)) {
+ frame_curr++;
+ frame = frame->next;
+ }
+ return frame_curr - (*ofs);
}
- return frame_curr - (*ofs);
}
*ofs = 0;
return 0;
@@ -1179,7 +1234,8 @@ static int image_sequence_get_len(ListBase *frames, int *ofs)
static Image *image_open_single(
Main *bmain, wmOperator *op, const char *filepath, const char *relbase,
- bool is_relative_path, bool use_multiview, int frame_seq_len)
+ bool is_relative_path, bool use_multiview, int frame_seq_len,
+ int frame_seq_ofs, LinkNodePair *udim_tiles)
{
bool exists = false;
Image *ima = NULL;
@@ -1215,7 +1271,15 @@ static Image *image_open_single(
}
if ((frame_seq_len > 1) && (ima->source == IMA_SRC_FILE)) {
- ima->source = IMA_SRC_SEQUENCE;
+ if (udim_tiles && frame_seq_ofs == 1001) {
+ ima->source = IMA_SRC_TILED;
+ for (LinkNode *node = udim_tiles->list; node; node = node->next) {
+ BKE_image_add_tile(ima, GET_INT_FROM_POINTER(node->link), NULL);
+ }
+ }
+ else {
+ ima->source = IMA_SRC_SEQUENCE;
+ }
}
}
@@ -1238,6 +1302,7 @@ static int image_open_exec(bContext *C, wmOperator *op)
const bool is_relative_path = RNA_boolean_get(op->ptr, "relative_path");
const bool use_multiview = RNA_boolean_get(op->ptr, "use_multiview");
+ const bool use_udim = RNA_boolean_get(op->ptr, "use_udim");
if (!op->customdata)
image_open_init(C, op);
@@ -1254,7 +1319,9 @@ static int image_open_exec(bContext *C, wmOperator *op)
image_sequence_get_frame_ranges(op->ptr, &frame_ranges_all);
for (ImageFrameRange *frame_range = frame_ranges_all.first; frame_range; frame_range = frame_range->next) {
int frame_range_ofs;
- int frame_range_seq_len = image_sequence_get_len(&frame_range->frames, &frame_range_ofs);
+ LinkNodePair udim_tiles = {NULL};
+ int frame_range_seq_len = image_sequence_get_len(frame_range, &frame_range_ofs,
+ use_udim? &udim_tiles : NULL);
BLI_freelistN(&frame_range->frames);
char filepath_range[FILE_MAX];
@@ -1266,7 +1333,10 @@ static int image_open_exec(bContext *C, wmOperator *op)
Image *ima_range = image_open_single(
bmain, op, filepath_range, BKE_main_blendfile_path(bmain),
- is_relative_path, use_multiview, frame_range_seq_len);
+ is_relative_path, use_multiview, frame_range_seq_len, frame_range_ofs,
+ use_udim? &udim_tiles : NULL);
+
+ BLI_linklist_free(udim_tiles.list, NULL);
/* take the first image */
if ((ima == NULL) && ima_range) {
@@ -1278,10 +1348,17 @@ static int image_open_exec(bContext *C, wmOperator *op)
BLI_freelistN(&frame_ranges_all);
}
else {
+ int sequence_len = 1;
+ LinkNodePair udim_tiles = {NULL};
+ if (use_udim) {
+ sequence_len = image_get_udim(filepath, &udim_tiles);
+ }
/* for drag & drop etc. */
ima = image_open_single(
bmain, op, filepath, BKE_main_blendfile_path(bmain),
- is_relative_path, use_multiview, 1);
+ is_relative_path, use_multiview, 1, sequence_len, &udim_tiles);
+
+ BLI_linklist_free(udim_tiles.list, NULL);
}
if (ima == NULL) {
@@ -1331,7 +1408,7 @@ static int image_open_exec(bContext *C, wmOperator *op)
/* initialize because of new image */
if (iuser) {
- iuser->frames = frame_seq_len;
+ iuser->frames = (ima->source == IMA_SRC_SEQUENCE)? frame_seq_len : 1;
iuser->sfra = 1;
iuser->framenr = 1;
if (ima->source == IMA_SRC_MOVIE) {
@@ -1464,6 +1541,7 @@ void IMAGE_OT_open(wmOperatorType *ot)
RNA_def_boolean(ot->srna, "use_sequence_detection", true, "Detect Sequences",
"Automatically detect animated sequences in selected images (based on file names)");
+ RNA_def_boolean(ot->srna, "use_udim", true, "Detect UDIMs", "Detect selected UDIM files and load all matching tiles");
}
/******************** Match movie length operator ********************/
@@ -1630,7 +1708,7 @@ static int save_image_options_init(Main *bmain, SaveImageOptions *simopts, Space
const bool guess_path, const bool save_as_render)
{
void *lock;
- ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock);
+ ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, 0);
if (ibuf) {
Image *ima = sima->image;
@@ -1698,6 +1776,11 @@ static int save_image_options_init(Main *bmain, SaveImageOptions *simopts, Space
BLI_snprintf(simopts->filepath, sizeof(simopts->filepath), "//%s", ima->id.name + 2);
BLI_path_abs(simopts->filepath, is_prev_save ? G.ima : BKE_main_blendfile_path(bmain));
}
+
+ /* append UDIM numbering if not present */
+ if (ima->source == IMA_SRC_TILED && (BLI_stringdec(ima->name, NULL, NULL, NULL) != 1001)) {
+ strncat(simopts->filepath, ".1001", sizeof(simopts->filepath) - 6);
+ }
}
/* color management */
@@ -1811,18 +1894,16 @@ static void save_imbuf_post(ImBuf *ibuf, ImBuf *colormanaged_ibuf)
* \note ``ima->name`` and ``ibuf->name`` should end up the same.
* \note for multiview the first ``ibuf`` is important to get the settings.
*/
-static bool save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveImageOptions *simopts, bool do_newpath)
+static bool save_image_single(bContext *C, SpaceImage *sima, wmOperator *op, SaveImageOptions *simopts, bool do_newpath, int tile)
{
Main *bmain = CTX_data_main(C);
Image *ima = ED_space_image(sima);
void *lock;
- ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock);
+ ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, tile);
Scene *scene;
RenderResult *rr = NULL;
bool ok = false;
- WM_cursor_wait(1);
-
if (ibuf) {
ImBuf *colormanaged_ibuf = NULL;
const char *relbase = ID_BLEND_PATH(CTX_data_main(C), &ima->id);
@@ -2036,11 +2117,49 @@ cleanup:
BKE_image_release_renderresult(scene, ima);
}
- WM_cursor_wait(0);
-
return ok;
}
+static bool save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveImageOptions *simopts, bool do_newpath)
+{
+ Image *ima = ED_space_image(sima);
+
+ if (ima->source == IMA_SRC_TILED) {
+ if (BLI_stringdec(simopts->filepath, NULL, NULL, NULL) != 1001) {
+ BKE_reportf(op->reports, RPT_ERROR,
+ "When saving a tiled image, the path '%s' must contain the UDIM tag 1001", simopts->filepath);
+ return false;
+ }
+ }
+
+ WM_cursor_wait(1);
+
+ if (!save_image_single(C, sima, op, simopts, do_newpath, 0)) {
+ WM_cursor_wait(0);
+ return false;
+ }
+
+ if (ima->source == IMA_SRC_TILED) {
+ char filepath[FILE_MAX];
+ BLI_strncpy(filepath, simopts->filepath, sizeof(filepath));
+ LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) {
+ if (tile->tile_number == 0)
+ continue;
+
+ char head[FILE_MAX], tail[FILE_MAX];
+ unsigned short numlen;
+ BLI_stringdec(filepath, head, tail, &numlen);
+ BLI_stringenc(simopts->filepath, head, tail, numlen, 1001 + tile->tile_number);
+
+ save_image_single(C, sima, op, simopts, do_newpath, tile->tile_number);
+ }
+ BLI_strncpy(simopts->filepath, filepath, sizeof(simopts->filepath));
+ }
+
+ WM_cursor_wait(0);
+ return true;
+}
+
static void image_save_as_free(wmOperator *op)
{
if (op->customdata) {
@@ -2258,7 +2377,7 @@ static int image_save_sequence_exec(bContext *C, wmOperator *op)
if (sima->image == NULL)
return OPERATOR_CANCELLED;
- if (sima->image->source != IMA_SRC_SEQUENCE) {
+ if (ELEM(sima->image->source, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) {
BKE_report(op->reports, RPT_ERROR, "Can only save sequence on image sequences");
return OPERATOR_CANCELLED;
}
@@ -2441,11 +2560,12 @@ static int image_new_exec(bContext *C, wmOperator *op)
RNA_float_get_array(op->ptr, "color", color);
alpha = RNA_boolean_get(op->ptr, "alpha");
stereo3d = RNA_boolean_get(op->ptr, "use_stereo_3d");
+ bool tiled = RNA_boolean_get(op->ptr, "tiled");
if (!alpha)
color[3] = 1.0f;
- ima = BKE_image_add_generated(bmain, width, height, name, alpha ? 32 : 24, floatbuf, gen_type, color, stereo3d);
+ ima = BKE_image_add_generated(bmain, width, height, name, alpha ? 32 : 24, floatbuf, gen_type, color, stereo3d, tiled);
if (!ima) {
image_new_free(op);
@@ -2529,6 +2649,9 @@ static void image_new_draw(bContext *UNUSED(C), wmOperator *op)
uiItemL(col[0], "", ICON_NONE);
uiItemR(col[1], &ptr, "float", 0, NULL, ICON_NONE);
+ uiItemL(col[0], "", ICON_NONE);
+ uiItemR(col[1], &ptr, "tiled", 0, NULL, ICON_NONE);
+
#if 0
if (is_multiview) {
uiItemL(col[0], "", ICON_NONE);
@@ -2577,6 +2700,8 @@ void IMAGE_OT_new(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN);
prop = RNA_def_boolean(ot->srna, "use_stereo_3d", 0, "Stereo 3D", "Create an image with left and right views");
RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
+ prop = RNA_def_boolean(ot->srna, "tiled", 0, "Tiled", "Create a tiled image");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
#undef IMA_DEF_NAME
@@ -2704,7 +2829,7 @@ static bool image_pack_test(bContext *C, wmOperator *op)
if (!as_png && BKE_image_has_packedfile(ima))
return 0;
- if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) {
+ if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) {
BKE_report(op->reports, RPT_ERROR, "Packing movies or image sequences not supported");
return 0;
}
@@ -2805,7 +2930,7 @@ static int image_unpack_exec(bContext *C, wmOperator *op)
if (!ima || !BKE_image_has_packedfile(ima))
return OPERATOR_CANCELLED;
- if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) {
+ if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) {
BKE_report(op->reports, RPT_ERROR, "Unpacking movies or image sequences not supported");
return OPERATOR_CANCELLED;
}
@@ -2833,7 +2958,7 @@ static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE
if (!ima || !BKE_image_has_packedfile(ima))
return OPERATOR_CANCELLED;
- if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) {
+ if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) {
BKE_report(op->reports, RPT_ERROR, "Unpacking movies or image sequences not supported");
return OPERATOR_CANCELLED;
}
@@ -2903,9 +3028,12 @@ static void image_sample_draw(const bContext *C, ARegion *ar, void *arg_info)
/* Returns color in linear space, matching ED_space_node_color_sample(). */
bool ED_space_image_color_sample(SpaceImage *sima, ARegion *ar, int mval[2], float r_col[3])
{
+ float uv[2];
+ UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &uv[0], &uv[1]);
+ int tile = BKE_image_get_tile_from_pos(sima->image, uv, uv, NULL);
+
void *lock;
- ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock);
- float fx, fy;
+ ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, tile);
bool ret = false;
if (ibuf == NULL) {
@@ -2913,12 +3041,10 @@ bool ED_space_image_color_sample(SpaceImage *sima, ARegion *ar, int mval[2], flo
return false;
}
- UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &fx, &fy);
-
- if (fx >= 0.0f && fy >= 0.0f && fx < 1.0f && fy < 1.0f) {
+ if (uv[0] >= 0.0f && uv[1] >= 0.0f && uv[0] < 1.0f && uv[1] < 1.0f) {
const float *fp;
unsigned char *cp;
- int x = (int)(fx * ibuf->x), y = (int)(fy * ibuf->y);
+ int x = (int)(uv[0] * ibuf->x), y = (int)(uv[1] * ibuf->y);
CLAMP(x, 0, ibuf->x - 1);
CLAMP(y, 0, ibuf->y - 1);
@@ -2944,10 +3070,15 @@ static void image_sample_apply(bContext *C, wmOperator *op, const wmEvent *event
{
SpaceImage *sima = CTX_wm_space_image(C);
ARegion *ar = CTX_wm_region(C);
+ Image *image = ED_space_image(sima);
+
+ float uv[2];
+ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &uv[0], &uv[1]);
+ int tile = BKE_image_get_tile_from_pos(sima->image, uv, uv, NULL);
+
void *lock;
- ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock);
+ ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, tile);
ImageSampleInfo *info = op->customdata;
- float fx, fy;
Scene *scene = CTX_data_scene(C);
CurveMapping *curve_mapping = scene->view_settings.curve_mapping;
@@ -2957,13 +3088,10 @@ static void image_sample_apply(bContext *C, wmOperator *op, const wmEvent *event
return;
}
- UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fx, &fy);
-
- if (fx >= 0.0f && fy >= 0.0f && fx < 1.0f && fy < 1.0f) {
+ if (uv[0] >= 0.0f && uv[1] >= 0.0f && uv[0] < 1.0f && uv[1] < 1.0f) {
const float *fp;
unsigned char *cp;
- int x = (int)(fx * ibuf->x), y = (int)(fy * ibuf->y);
- Image *image = ED_space_image(sima);
+ int x = (int)(uv[0] * ibuf->x), y = (int)(uv[1] * ibuf->y);
CLAMP(x, 0, ibuf->x - 1);
CLAMP(y, 0, ibuf->y - 1);
@@ -3171,12 +3299,29 @@ static int image_sample_line_exec(bContext *C, wmOperator *op)
int x_end = RNA_int_get(op->ptr, "xend");
int y_end = RNA_int_get(op->ptr, "yend");
+ float x1f, y1f, x2f, y2f;
+ UI_view2d_region_to_view(&ar->v2d, x_start, y_start, &x1f, &y1f);
+ UI_view2d_region_to_view(&ar->v2d, x_end, y_end, &x2f, &y2f);
+
+ /* If the image has tiles, shift the positions accordingly. */
+ Image *image = ED_space_image(sima);
+ int tile = 0, ix = 0, iy = 0;
+ if (image && image->source == IMA_SRC_TILED) {
+ ix = (int) x1f;
+ iy = (int) y1f;
+ CLAMP(ix, 0, 9);
+
+ x1f -= ix;
+ x2f -= ix;
+ y1f -= iy;
+ y2f -= iy;
+ tile = 10*iy + ix;
+ }
+
void *lock;
- ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock);
+ ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, tile);
Histogram *hist = &sima->sample_line_hist;
- float x1f, y1f, x2f, y2f;
-
if (ibuf == NULL) {
ED_space_image_release_buffer(sima, ibuf, lock);
return OPERATOR_CANCELLED;
@@ -3187,13 +3332,12 @@ static int image_sample_line_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- UI_view2d_region_to_view(&ar->v2d, x_start, y_start, &x1f, &y1f);
- UI_view2d_region_to_view(&ar->v2d, x_end, y_end, &x2f, &y2f);
-
hist->co[0][0] = x1f;
hist->co[0][1] = y1f;
hist->co[1][0] = x2f;
hist->co[1][1] = y2f;
+ hist->draw_offset[0] = ix;
+ hist->draw_offset[1] = iy;
/* enable line drawing */
hist->flag |= HISTO_FLAG_SAMPLELINE;
@@ -3807,3 +3951,227 @@ void IMAGE_OT_clear_render_border(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+
+/* ********************* Add tile operator ****************** */
+
+static bool add_tile_poll(bContext *C)
+{
+ SpaceImage *sima = CTX_wm_space_image(C);
+ Image *ima = CTX_data_edit_image(C);
+
+ return (ima && ima->source == IMA_SRC_TILED && (BKE_image_get_tile(ima, sima->curtile) == NULL));
+}
+
+static int add_tile_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceImage *sima = CTX_wm_space_image(C);
+ Image *ima = ED_space_image(sima);
+
+ if (BKE_image_add_tile(ima, sima->curtile, NULL) == NULL)
+ return OPERATOR_CANCELLED;
+
+ WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void IMAGE_OT_add_tile(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add tile";
+ ot->description = "Adds a tile to the image";
+ ot->idname = "IMAGE_OT_add_tile";
+
+ /* api callbacks */
+ ot->poll = add_tile_poll;
+ ot->exec = add_tile_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ********************* Remove tile operator ****************** */
+
+static bool remove_tile_poll(bContext *C)
+{
+ SpaceImage *sima = CTX_wm_space_image(C);
+ Image *ima = CTX_data_edit_image(C);
+
+ return (ima && ima->source == IMA_SRC_TILED && BKE_image_get_tile(ima, sima->curtile));
+}
+
+static int remove_tile_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceImage *sima = CTX_wm_space_image(C);
+ Image *ima = ED_space_image(sima);
+
+ ImageTile *tile = BKE_image_get_tile(ima, sima->curtile);
+ if (!BKE_image_remove_tile(ima, tile))
+ return OPERATOR_CANCELLED;
+
+ WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void IMAGE_OT_remove_tile(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove tile";
+ ot->description = "Removes a tile from the image";
+ ot->idname = "IMAGE_OT_remove_tile";
+
+ /* api callbacks */
+ ot->poll = remove_tile_poll;
+ ot->exec = remove_tile_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ********************* Fill tile operator ****************** */
+
+static bool fill_tile_poll(bContext *C)
+{
+ Image *ima = CTX_data_edit_image(C);
+ SpaceImage *sima = CTX_wm_space_image(C);
+
+ return (ima && ima->source == IMA_SRC_TILED && BKE_image_get_tile(ima, sima->curtile));
+}
+
+static int fill_tile_exec(bContext *C, wmOperator *op)
+{
+ SpaceImage *sima = CTX_wm_space_image(C);
+ Image *ima = ED_space_image(sima);
+
+ float color[4];
+ RNA_float_get_array(op->ptr, "color", color);
+ int gen_type = RNA_enum_get(op->ptr, "generated_type");
+ int width = RNA_int_get(op->ptr, "width");
+ int height = RNA_int_get(op->ptr, "height");
+
+ ImageTile *tile = BKE_image_get_tile(ima, sima->curtile);
+ if (!BKE_image_fill_tile(ima, tile, width, height, color, gen_type))
+ return OPERATOR_CANCELLED;
+
+ WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static int fill_tile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ SpaceImage *sima = CTX_wm_space_image(C);
+ Image *ima = ED_space_image(sima);
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
+ if (ibuf) {
+ RNA_int_set(op->ptr, "width", ibuf->x);
+ RNA_int_set(op->ptr, "height", ibuf->y);
+ BKE_image_release_ibuf(ima, ibuf, NULL);
+ }
+ return WM_operator_props_dialog_popup(C, op, 15 * UI_UNIT_X, 5 * UI_UNIT_Y);
+}
+
+static void fill_tile_draw(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *split, *col[2];
+ uiLayout *layout = op->layout;
+ PointerRNA ptr;
+
+ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
+
+ /* copy of WM_operator_props_dialog_popup() layout */
+
+ split = uiLayoutSplit(layout, 0.5f, false);
+ col[0] = uiLayoutColumn(split, false);
+ col[1] = uiLayoutColumn(split, false);
+
+ uiItemL(col[0], IFACE_("Color"), ICON_NONE);
+ uiItemR(col[1], &ptr, "color", 0, "", ICON_NONE);
+
+ uiItemL(col[0], IFACE_("Width"), ICON_NONE);
+ uiItemR(col[1], &ptr, "width", 0, "", ICON_NONE);
+
+ uiItemL(col[0], IFACE_("Height"), ICON_NONE);
+ uiItemR(col[1], &ptr, "height", 0, "", ICON_NONE);
+
+ uiItemL(col[0], IFACE_("Generated Type"), ICON_NONE);
+ uiItemR(col[1], &ptr, "generated_type", 0, "", ICON_NONE);
+}
+
+void IMAGE_OT_fill_tile(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Fill tile";
+ ot->description = "Fill the current tile with a generated image";
+ ot->idname = "IMAGE_OT_fill_tile";
+
+ /* api callbacks */
+ ot->poll = fill_tile_poll;
+ ot->exec = fill_tile_exec;
+ ot->invoke = fill_tile_invoke;
+ ot->ui = fill_tile_draw;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ PropertyRNA *prop;
+ static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+ prop = RNA_def_float_color(ot->srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f);
+ RNA_def_property_subtype(prop, PROP_COLOR_GAMMA);
+ RNA_def_property_float_array_default(prop, default_color);
+ RNA_def_enum(ot->srna, "generated_type", rna_enum_image_generated_type_items, IMA_GENTYPE_BLANK,
+ "Generated Type", "Fill the image with a grid for UV map testing");
+ prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384);
+ RNA_def_property_subtype(prop, PROP_PIXEL);
+ prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384);
+ RNA_def_property_subtype(prop, PROP_PIXEL);
+}
+
+/* ********************* Select tile operator ****************** */
+
+static bool image_select_tile_poll(bContext *C)
+{
+ SpaceImage *sima = CTX_wm_space_image(C);
+ Image *ima = CTX_data_edit_image(C);
+
+ if (ima) {
+ return (ima->source == IMA_SRC_TILED);
+ }
+ else {
+ return (sima->tile_grid_shape[0] > 1) || (sima->tile_grid_shape[1] > 1);
+ }
+}
+
+static int image_select_tile_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+{
+ SpaceImage *sima = CTX_wm_space_image(C);
+ ARegion *ar = CTX_wm_region(C);
+
+ float uv[2];
+ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &uv[0], &uv[1]);
+
+ if (uv[0] >= 0.0f && uv[1] >= 0.0f && uv[0] < 10.0f) {
+ int tx = (int) uv[0];
+ int ty = (int) uv[1];
+
+ sima->curtile = 10*ty + tx;
+
+ WM_event_add_notifier(C, NC_WINDOW, NULL);
+ return OPERATOR_FINISHED;
+ }
+
+ return OPERATOR_CANCELLED;
+}
+
+void IMAGE_OT_select_tile(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select tile";
+ ot->idname = "IMAGE_OT_select_tile";
+ ot->description = "Use mouse to select a tile of the image";
+
+ /* api callbacks */
+ ot->invoke = image_select_tile_invoke;
+ ot->poll = image_select_tile_poll;
+} \ No newline at end of file
diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c
index d87bfbe00ce..b35e05a43a3 100644
--- a/source/blender/editors/space_image/space_image.c
+++ b/source/blender/editors/space_image/space_image.c
@@ -183,6 +183,9 @@ static SpaceLink *image_new(const ScrArea *UNUSED(area), const Scene *UNUSED(sce
scopes_new(&simage->scopes);
simage->sample_line_hist.height = 100;
+ simage->tile_grid_shape[0] = 1;
+ simage->tile_grid_shape[1] = 1;
+
/* header */
ar = MEM_callocN(sizeof(ARegion), "header for image");
@@ -289,6 +292,11 @@ static void image_operatortypes(void)
WM_operatortype_append(IMAGE_OT_read_viewlayers);
WM_operatortype_append(IMAGE_OT_render_border);
WM_operatortype_append(IMAGE_OT_clear_render_border);
+
+ WM_operatortype_append(IMAGE_OT_add_tile);
+ WM_operatortype_append(IMAGE_OT_remove_tile);
+ WM_operatortype_append(IMAGE_OT_fill_tile);
+ WM_operatortype_append(IMAGE_OT_select_tile);
}
static void image_keymap(struct wmKeyConfig *keyconf)
@@ -355,6 +363,7 @@ static void image_keymap(struct wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "IMAGE_OT_sample", ACTIONMOUSE, KM_PRESS, 0, 0);
RNA_enum_set(WM_keymap_add_item(keymap, "IMAGE_OT_curves_point_set", ACTIONMOUSE, KM_PRESS, KM_CTRL, 0)->ptr, "point", 0);
RNA_enum_set(WM_keymap_add_item(keymap, "IMAGE_OT_curves_point_set", ACTIONMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "point", 1);
+ WM_keymap_add_item(keymap, "IMAGE_OT_select_tile", ACTIONMOUSE, KM_PRESS, KM_ALT, 0);
/* toggle editmode is handy to have while UV unwrapping */
kmi = WM_keymap_add_item(keymap, "OBJECT_OT_mode_set", TABKEY, KM_PRESS, 0, 0);
@@ -929,7 +938,8 @@ static void image_tools_region_draw(const bContext *C, ARegion *ar)
SpaceImage *sima = CTX_wm_space_image(C);
Scene *scene = CTX_data_scene(C);
void *lock;
- ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock);
+ /* TODO(lukas): Support tiles in scopes? */
+ ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, 0);
/* XXX performance regression if name of scopes category changes! */
PanelCategoryStack *category = UI_panel_category_active_find(ar, "Scopes");
diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h
index 9fea9eaf4e1..d8132e7c088 100644
--- a/source/blender/gpu/GPU_material.h
+++ b/source/blender/gpu/GPU_material.h
@@ -220,7 +220,7 @@ GPUNodeLink *GPU_attribute(CustomDataType type, const char *name);
GPUNodeLink *GPU_uniform(float *num);
GPUNodeLink *GPU_dynamic_uniform(float *num, GPUDynamicType dynamictype, void *data);
GPUNodeLink *GPU_uniform_buffer(float *num, GPUType gputype);
-GPUNodeLink *GPU_image(struct Image *ima, struct ImageUser *iuser, bool is_data);
+GPUNodeLink *GPU_image(struct Image *ima, struct ImageUser *iuser, bool is_data, int tile);
GPUNodeLink *GPU_cube_map(struct Image *ima, struct ImageUser *iuser, bool is_data);
GPUNodeLink *GPU_image_preview(struct PreviewImage *prv);
GPUNodeLink *GPU_texture(int size, float *pixels);
diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c
index 3b9d2e08769..3a1a7190cc5 100644
--- a/source/blender/gpu/intern/gpu_codegen.c
+++ b/source/blender/gpu/intern/gpu_codegen.c
@@ -515,17 +515,19 @@ const char *GPU_builtin_name(GPUBuiltin builtin)
}
/* assign only one texid per buffer to avoid sampling the same texture twice */
-static void codegen_set_texid(GHash *bindhash, GPUInput *input, int *texid, void *key)
+static void codegen_set_texid(GHash *bindhash, GPUInput *input, int *texid, void *key1, int key2)
{
- if (BLI_ghash_haskey(bindhash, key)) {
+ GHashPair pair = {key1, SET_INT_IN_POINTER(key2 << 16)};
+ if (BLI_ghash_haskey(bindhash, &pair)) {
/* Reuse existing texid */
- input->texid = GET_INT_FROM_POINTER(BLI_ghash_lookup(bindhash, key));
+ input->texid = GET_INT_FROM_POINTER(BLI_ghash_lookup(bindhash, &pair));
}
else {
/* Allocate new texid */
input->texid = *texid;
(*texid)++;
input->bindtex = true;
+ void *key = BLI_ghashutil_pairalloc(key1, SET_INT_IN_POINTER(key2));
BLI_ghash_insert(bindhash, key, SET_INT_IN_POINTER(input->texid));
}
}
@@ -538,7 +540,7 @@ static void codegen_set_unique_ids(ListBase *nodes)
GPUOutput *output;
int id = 1, texid = 0;
- bindhash = BLI_ghash_ptr_new("codegen_set_unique_ids1 gh");
+ bindhash = BLI_ghash_pair_new("codegen_set_unique_ids1 gh");
definehash = BLI_ghash_ptr_new("codegen_set_unique_ids2 gh");
for (node = nodes->first; node; node = node->next) {
@@ -556,19 +558,19 @@ static void codegen_set_unique_ids(ListBase *nodes)
* the same texture twice */
if (input->link) {
/* input is texture from buffer */
- codegen_set_texid(bindhash, input, &texid, input->link);
+ codegen_set_texid(bindhash, input, &texid, input->link, 0);
}
else if (input->ima) {
/* input is texture from image */
- codegen_set_texid(bindhash, input, &texid, input->ima);
+ codegen_set_texid(bindhash, input, &texid, input->ima, input->image_tile);
}
else if (input->prv) {
/* input is texture from preview render */
- codegen_set_texid(bindhash, input, &texid, input->prv);
+ codegen_set_texid(bindhash, input, &texid, input->prv, 0);
}
else if (input->tex) {
/* input is user created texture, check tex pointer */
- codegen_set_texid(bindhash, input, &texid, input->tex);
+ codegen_set_texid(bindhash, input, &texid, input->tex, 0);
}
/* make sure this pixel is defined exactly once */
@@ -594,7 +596,7 @@ static void codegen_set_unique_ids(ListBase *nodes)
output->id = id++;
}
- BLI_ghash_free(bindhash, NULL, NULL);
+ BLI_ghash_free(bindhash, BLI_ghashutil_pairfree, NULL);
BLI_ghash_free(definehash, NULL, NULL);
}
@@ -1355,6 +1357,7 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const GPUType
input->ima = link->ptr1;
input->iuser = link->ptr2;
input->image_isdata = link->image_isdata;
+ input->image_tile = link->val1;
input->textarget = GL_TEXTURE_2D;
input->textype = GPU_TEX2D;
}
@@ -1652,13 +1655,14 @@ GPUNodeLink *GPU_uniform_buffer(float *num, GPUType gputype)
return link;
}
-GPUNodeLink *GPU_image(Image *ima, ImageUser *iuser, bool is_data)
+GPUNodeLink *GPU_image(Image *ima, ImageUser *iuser, bool is_data, int tile)
{
GPUNodeLink *link = GPU_node_link_create();
link->image = GPU_NODE_LINK_IMAGE_BLENDER;
link->ptr1 = ima;
link->ptr2 = iuser;
+ link->val1 = tile;
link->image_isdata = is_data;
return link;
diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h
index 278843fc948..0523332adc4 100644
--- a/source/blender/gpu/intern/gpu_codegen.h
+++ b/source/blender/gpu/intern/gpu_codegen.h
@@ -93,6 +93,7 @@ struct GPUNodeLink {
int texturesize;
void *ptr1, *ptr2;
+ int val1;
bool dynamic;
GPUDynamicType dynamictype;
@@ -137,6 +138,7 @@ typedef struct GPUInput {
struct Image *ima; /* image */
struct ImageUser *iuser; /* image user */
+ int image_tile; /* image tile */
struct PreviewImage *prv; /* preview images & icons */
bool image_isdata; /* image does not contain color data */
float *dynamicvec; /* vector data in case it is dynamic */
diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c
index 965caba0955..6e3ad4127eb 100644
--- a/source/blender/gpu/intern/gpu_draw.c
+++ b/source/blender/gpu/intern/gpu_draw.c
@@ -224,16 +224,6 @@ float GPU_get_anisotropic(void)
/* Set OpenGL state for an MTFace */
-static GPUTexture **gpu_get_image_gputexture(Image *ima, GLenum textarget)
-{
- if (textarget == GL_TEXTURE_2D)
- return &ima->gputexture[TEXTARGET_TEXTURE_2D];
- else if (textarget == GL_TEXTURE_CUBE_MAP)
- return &ima->gputexture[TEXTARGET_TEXTURE_CUBE_MAP];
-
- return NULL;
-}
-
typedef struct VerifyThreadData {
ImBuf *ibuf;
float *srgb_frect;
@@ -301,18 +291,22 @@ GPUTexture *GPU_texture_from_blender(
return NULL;
}
+ int gputex_type = (textarget == GL_TEXTURE_CUBE_MAP)? TEXTARGET_TEXTURE_CUBE_MAP : TEXTARGET_TEXTURE_2D;
+ ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser);
+
/* Test if we already have a texture. */
- GPUTexture **tex = gpu_get_image_gputexture(ima, textarget);
- if (*tex) {
- return *tex;
+ GPUTexture *tex = tile->gputexture[gputex_type];
+ if (tex) {
+ return tex;
}
/* Check if we have a valid image. If not, we return a dummy
* texture with zero bindcode so we don't keep trying. */
unsigned int bindcode = 0;
- if (ima->ok == 0) {
- *tex = GPU_texture_from_bindcode(textarget, bindcode);
- return *tex;
+ if (tile->ok == 0) {
+ tex = GPU_texture_from_bindcode(textarget, bindcode);
+ tile->gputexture[gputex_type] = tex;
+ return tex;
}
/* currently, tpage refresh is used by ima sequences */
@@ -324,8 +318,9 @@ GPUTexture *GPU_texture_from_blender(
/* check if we have a valid image buffer */
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL);
if (ibuf == NULL) {
- *tex = GPU_texture_from_bindcode(textarget, bindcode);
- return *tex;
+ tex = GPU_texture_from_bindcode(textarget, bindcode);
+ tile->gputexture[gputex_type] = tex;
+ return tex;
}
/* flag to determine whether deep format is used */
@@ -393,8 +388,9 @@ GPUTexture *GPU_texture_from_blender(
BKE_image_release_ibuf(ima, ibuf, NULL);
- *tex = GPU_texture_from_bindcode(textarget, bindcode);
- return *tex;
+ tex = GPU_texture_from_bindcode(textarget, bindcode);
+ tile->gputexture[gputex_type] = tex;
+ return tex;
}
static void **gpu_gen_cube_map(unsigned int *rect, float *frect, int rectw, int recth, bool use_high_bit_depth)
@@ -703,44 +699,43 @@ void GPU_paint_set_mipmap(Main *bmain, bool mipmap)
if (mipmap) {
for (Image *ima = bmain->image.first; ima; ima = ima->id.next) {
- if (BKE_image_has_opengl_texture(ima)) {
+ bool has_gputexture = false;
+ LISTBASE_FOREACH(ImageTile *, tile, &ima->tiles) {
if (ima->tpageflag & IMA_MIPMAP_COMPLETE) {
- if (ima->gputexture[TEXTARGET_TEXTURE_2D]) {
- GPU_texture_bind(ima->gputexture[TEXTARGET_TEXTURE_2D], 0);
+ GPUTexture *tex = tile->gputexture[TEXTARGET_TEXTURE_2D];
+ if (tex) {
+ GPU_texture_bind(tex, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gpu_get_mipmap_filter(0));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1));
- GPU_texture_unbind(ima->gputexture[TEXTARGET_TEXTURE_2D]);
+ GPU_texture_unbind(tex);
}
}
- else
- GPU_free_image(ima);
}
- else
- ima->tpageflag &= ~IMA_MIPMAP_COMPLETE;
+ if (!has_gputexture) GPU_free_image(ima);
}
-
}
else {
for (Image *ima = bmain->image.first; ima; ima = ima->id.next) {
- if (BKE_image_has_opengl_texture(ima)) {
- if (ima->gputexture[TEXTARGET_TEXTURE_2D]) {
- GPU_texture_bind(ima->gputexture[TEXTARGET_TEXTURE_2D], 0);
+ LISTBASE_FOREACH(ImageTile *, tile, &ima->tiles) {
+ GPUTexture *tex = tile->gputexture[TEXTARGET_TEXTURE_2D];
+ if (tex) {
+ GPU_texture_bind(tex, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1));
- GPU_texture_unbind(ima->gputexture[TEXTARGET_TEXTURE_2D]);
+ GPU_texture_unbind(tex);
}
}
- else
- ima->tpageflag &= ~IMA_MIPMAP_COMPLETE;
}
}
}
/* check if image has been downscaled and do scaled partial update */
-static bool gpu_check_scaled_image(ImBuf *ibuf, Image *ima, float *frect, int x, int y, int w, int h)
+static bool gpu_check_scaled_image(ImBuf *ibuf, Image *ima, ImageTile *tile, float *frect, int x, int y, int w, int h)
{
if (is_over_resolution_limit(GL_TEXTURE_2D, ibuf->x, ibuf->y)) {
+ GPUTexture *tex = tile->gputexture[TEXTARGET_TEXTURE_2D];
+
int x_limit = smaller_power_of_2_limit(ibuf->x);
int y_limit = smaller_power_of_2_limit(ibuf->y);
@@ -760,7 +755,7 @@ static bool gpu_check_scaled_image(ImBuf *ibuf, Image *ima, float *frect, int x,
if (rectw + x > x_limit) rectw--;
if (recth + y > y_limit) recth--;
- GPU_texture_bind(ima->gputexture[TEXTARGET_TEXTURE_2D], 0);
+ GPU_texture_bind(tex, 0);
/* float rectangles are already continuous in memory so we can use IMB_scaleImBuf */
if (frect) {
@@ -802,7 +797,7 @@ static bool gpu_check_scaled_image(ImBuf *ibuf, Image *ima, float *frect, int x,
ima->tpageflag &= ~IMA_MIPMAP_COMPLETE;
}
- GPU_texture_unbind(ima->gputexture[TEXTARGET_TEXTURE_2D]);
+ GPU_texture_unbind(tex);
return true;
}
@@ -813,9 +808,11 @@ static bool gpu_check_scaled_image(ImBuf *ibuf, Image *ima, float *frect, int x,
void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, int h)
{
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL);
+ ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser);
+ GPUTexture *tex = tile->gputexture[TEXTARGET_TEXTURE_2D];
if ((!GTS.gpu_mipmap && GPU_get_mipmap()) ||
- (ima->gputexture[TEXTARGET_TEXTURE_2D] == NULL) ||
+ (tex == NULL) ||
(ibuf == NULL) ||
(w == 0) || (h == 0))
{
@@ -833,13 +830,13 @@ void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, i
bool is_data = (ima->tpageflag & IMA_GLBIND_IS_DATA) != 0;
IMB_partial_rect_from_float(ibuf, buffer, x, y, w, h, is_data);
- if (gpu_check_scaled_image(ibuf, ima, buffer, x, y, w, h)) {
+ if (gpu_check_scaled_image(ibuf, ima, tile, buffer, x, y, w, h)) {
MEM_freeN(buffer);
BKE_image_release_ibuf(ima, ibuf, NULL);
return;
}
- GPU_texture_bind(ima->gputexture[TEXTARGET_TEXTURE_2D], 0);
+ GPU_texture_bind(tex, 0);
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_FLOAT, buffer);
MEM_freeN(buffer);
@@ -853,18 +850,18 @@ void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, i
ima->tpageflag &= ~IMA_MIPMAP_COMPLETE;
}
- GPU_texture_unbind(ima->gputexture[TEXTARGET_TEXTURE_2D]);
+ GPU_texture_unbind(tex);
BKE_image_release_ibuf(ima, ibuf, NULL);
return;
}
- if (gpu_check_scaled_image(ibuf, ima, NULL, x, y, w, h)) {
+ if (gpu_check_scaled_image(ibuf, ima, tile, NULL, x, y, w, h)) {
BKE_image_release_ibuf(ima, ibuf, NULL);
return;
}
- GPU_texture_bind(ima->gputexture[TEXTARGET_TEXTURE_2D], 0);
+ GPU_texture_bind(tex, 0);
glGetIntegerv(GL_UNPACK_ROW_LENGTH, &row_length);
glGetIntegerv(GL_UNPACK_SKIP_PIXELS, &skip_pixels);
@@ -890,7 +887,7 @@ void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, i
ima->tpageflag &= ~IMA_MIPMAP_COMPLETE;
}
- GPU_texture_unbind(ima->gputexture[TEXTARGET_TEXTURE_2D]);
+ GPU_texture_unbind(tex);
}
BKE_image_release_ibuf(ima, ibuf, NULL);
@@ -1077,11 +1074,14 @@ void GPU_free_image(Image *ima)
return;
}
- for (int i = 0; i < TEXTARGET_COUNT; i++) {
- /* free glsl image binding */
- if (ima->gputexture[i]) {
- GPU_texture_free(ima->gputexture[i]);
- ima->gputexture[i] = NULL;
+ LISTBASE_FOREACH(ImageTile *, tile, &ima->tiles) {
+ for (int i = 0; i < TEXTARGET_COUNT; i++) {
+ /* free glsl image binding */
+ GPUTexture *tex = tile->gputexture[i];
+ if (tex) {
+ GPU_texture_free(tex);
+ tile->gputexture[i] = NULL;
+ }
}
}
diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl
index 464851bae21..fe57394f9af 100644
--- a/source/blender/gpu/shaders/gpu_shader_material.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_material.glsl
@@ -1879,6 +1879,67 @@ void node_tex_image_smart(vec3 co, sampler2D ima, out vec4 color, out float alph
node_tex_image_cubic(co, ima, color, alpha);
}
+void node_tex_image_tiled_map(vec3 co, out vec4 color, out vec3 map)
+{
+ float tx = floor(co.x);
+ float ty = floor(co.y);
+
+ if (tx < 0 || ty < 0 || tx >= 10)
+ map = vec3(0, 0, -1);
+ else
+ map = vec3(co.x - tx, co.y - ty, 10*ty + tx);
+
+ color = vec4(1.0, 0.0, 1.0, 1.0);
+}
+
+void node_tex_image_tile_linear(vec3 map, float tile_id, sampler2D ima, vec4 in_color, out vec4 color, out float alpha)
+{
+ if (map.z == tile_id) {
+ vec3 co = map.xyy;
+ node_tex_image_linear(co, ima, color, alpha);
+ }
+ else {
+ color = in_color;
+ alpha = color.a;
+ }
+}
+
+void node_tex_image_tile_nearest(vec3 map, float tile_id, sampler2D ima, vec4 in_color, out vec4 color, out float alpha)
+{
+ if (map.z == tile_id) {
+ vec3 co = map.xyy;
+ node_tex_image_nearest(co, ima, color, alpha);
+ }
+ else {
+ color = in_color;
+ alpha = color.a;
+ }
+}
+
+void node_tex_image_tile_cubic(vec3 map, float tile_id, sampler2D ima, vec4 in_color, out vec4 color, out float alpha)
+{
+ if (map.z == tile_id) {
+ vec3 co = map.xyy;
+ node_tex_image_cubic(co, ima, color, alpha);
+ }
+ else {
+ color = in_color;
+ alpha = color.a;
+ }
+}
+
+void node_tex_image_tile_smart(vec3 map, float tile_id, sampler2D ima, vec4 in_color, out vec4 color, out float alpha)
+{
+ if (map.z == tile_id) {
+ vec3 co = map.xyy;
+ node_tex_image_smart(co, ima, color, alpha);
+ }
+ else {
+ color = in_color;
+ alpha = color.a;
+ }
+}
+
void tex_box_sample_linear(vec3 texco,
vec3 N,
sampler2D ima,
diff --git a/source/blender/imbuf/IMB_moviecache.h b/source/blender/imbuf/IMB_moviecache.h
index 0c72d1b4f01..756210a306e 100644
--- a/source/blender/imbuf/IMB_moviecache.h
+++ b/source/blender/imbuf/IMB_moviecache.h
@@ -60,6 +60,7 @@ void IMB_moviecache_set_priority_callback(struct MovieCache *cache, MovieCacheGe
void IMB_moviecache_put(struct MovieCache *cache, void *userkey, struct ImBuf *ibuf);
bool IMB_moviecache_put_if_possible(struct MovieCache *cache, void *userkey, struct ImBuf *ibuf);
struct ImBuf *IMB_moviecache_get(struct MovieCache *cache, void *userkey);
+void IMB_moviecache_remove(struct MovieCache *cache, void *userkey);
bool IMB_moviecache_has_frame(struct MovieCache *cache, void *userkey);
void IMB_moviecache_free(struct MovieCache *cache);
diff --git a/source/blender/imbuf/intern/moviecache.c b/source/blender/imbuf/intern/moviecache.c
index daf062f5499..9ad9f6b9b41 100644
--- a/source/blender/imbuf/intern/moviecache.c
+++ b/source/blender/imbuf/intern/moviecache.c
@@ -415,6 +415,14 @@ bool IMB_moviecache_put_if_possible(MovieCache *cache, void *userkey, ImBuf *ibu
return result;
}
+void IMB_moviecache_remove(MovieCache *cache, void *userkey)
+{
+ MovieCacheKey key;
+ key.cache_owner = cache;
+ key.userkey = userkey;
+ BLI_ghash_remove(cache->hash, &key, moviecache_keyfree, moviecache_valfree);
+}
+
ImBuf *IMB_moviecache_get(MovieCache *cache, void *userkey)
{
MovieCacheKey key;
diff --git a/source/blender/makesdna/DNA_color_types.h b/source/blender/makesdna/DNA_color_types.h
index 8ed38b0b05d..15db4100c75 100644
--- a/source/blender/makesdna/DNA_color_types.h
+++ b/source/blender/makesdna/DNA_color_types.h
@@ -132,6 +132,7 @@ typedef struct Histogram {
/* sample line only */
/* image coords src -> dst */
float co[2][2];
+ float draw_offset[2];
} Histogram;
diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h
index a7ec121efda..7a2d43a173f 100644
--- a/source/blender/makesdna/DNA_image_types.h
+++ b/source/blender/makesdna/DNA_image_types.h
@@ -58,6 +58,9 @@ typedef struct ImageUser {
short pass;
short pad;
+ int tile; /* current tile (for internal use) */
+ int pad2;
+
short multi_index, view, layer; /* listbase indices, for menu browsing or retrieve buffer */
short flag;
} ImageUser;
@@ -85,6 +88,21 @@ typedef struct RenderSlot {
struct RenderResult *render;
} RenderSlot;
+enum {
+ TEXTARGET_TEXTURE_2D = 0,
+ TEXTARGET_TEXTURE_CUBE_MAP = 1,
+ TEXTARGET_COUNT = 2
+};
+
+typedef struct ImageTile {
+ struct ImageTile *next, *prev;
+ struct GPUTexture *gputexture[2]; /* TEXTARGET_COUNT */
+ char ok;
+ char pad[3];
+ int tile_number;
+ char label[64];
+} ImageTile;
+
/* iuser->flag */
#define IMA_ANIM_ALWAYS 1
#define IMA_ANIM_REFRESHED 2
@@ -92,11 +110,6 @@ typedef struct RenderSlot {
#define IMA_NEED_FRAME_RECALC 8
#define IMA_SHOW_STEREO 16
-enum {
- TEXTARGET_TEXTURE_2D = 0,
- TEXTARGET_TEXTURE_CUBE_MAP = 1,
- TEXTARGET_COUNT = 2
-};
typedef struct Image {
ID id;
@@ -104,7 +117,6 @@ typedef struct Image {
char name[1024]; /* file path, 1024 = FILE_MAX */
struct MovieCache *cache; /* not written in file */
- struct GPUTexture *gputexture[2]; /* not written in file 2 = TEXTARGET_COUNT */
/* sources from: */
ListBase anims;
@@ -119,16 +131,17 @@ typedef struct Image {
/* texture page */
short tpageflag;
- short pad2;
- unsigned int pad3;
+ short pad3;
+
+ int lastused;
+
+ ListBase tiles;
struct PackedFile *packedfile DNA_DEPRECATED; /* deprecated */
struct ListBase packedfiles;
struct PreviewImage *preview;
- int lastused;
- short ok;
- short pad4[3];
+ int pad2;
/* for generated images */
int gen_x, gen_y;
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index d6d043b03ae..0af0ec5e105 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -922,6 +922,8 @@ typedef struct SpaceImage {
int flag;
+ int tile_grid_shape[2];
+
MaskSpaceInfo mask_info;
} SpaceImage;
diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c
index 1759eb812e6..36aed870f04 100644
--- a/source/blender/makesrna/intern/rna_image.c
+++ b/source/blender/makesrna/intern/rna_image.c
@@ -59,6 +59,7 @@ static const EnumPropertyItem image_source_items[] = {
{IMA_SRC_MOVIE, "MOVIE", 0, "Movie", "Movie file"},
{IMA_SRC_GENERATED, "GENERATED", 0, "Generated", "Generated image"},
{IMA_SRC_VIEWER, "VIEWER", 0, "Viewer", "Compositing node viewer"},
+ {IMA_SRC_TILED, "TILED", 0, "Tiled", "Tiled image texture"},
{0, NULL, 0, NULL, NULL}
};
@@ -190,6 +191,7 @@ static const EnumPropertyItem *rna_Image_source_itemf(bContext *UNUSED(C), Point
RNA_enum_items_add_value(&item, &totitem, image_source_items, IMA_SRC_SEQUENCE);
RNA_enum_items_add_value(&item, &totitem, image_source_items, IMA_SRC_MOVIE);
RNA_enum_items_add_value(&item, &totitem, image_source_items, IMA_SRC_GENERATED);
+ RNA_enum_items_add_value(&item, &totitem, image_source_items, IMA_SRC_TILED);
}
RNA_enum_item_end(&item, &totitem);
@@ -279,13 +281,6 @@ static void rna_Image_resolution_set(PointerRNA *ptr, const float *values)
BKE_image_release_ibuf(im, ibuf, lock);
}
-static int rna_Image_bindcode_get(PointerRNA *ptr)
-{
- Image *ima = (Image *)ptr->data;
- GPUTexture *tex = ima->gputexture[TEXTARGET_TEXTURE_2D];
- return (tex) ? GPU_texture_opengl_bindcode(tex) : 0;
-}
-
static int rna_Image_depth_get(PointerRNA *ptr)
{
Image *im = (Image *)ptr->data;
@@ -491,6 +486,23 @@ static void rna_render_slots_active_index_range(PointerRNA *ptr, int *min, int *
*max = max_ii(0, BLI_listbase_count(&image->renderslots) - 1);
}
+static ImageTile *rna_ImageTile_new(Image *image, int tile_number, const char *label)
+{
+ ImageTile *tile = BKE_image_add_tile(image, tile_number, label);
+
+ WM_main_add_notifier(NC_IMAGE | ND_DRAW, NULL);
+
+ return tile;
+}
+
+static void rna_ImageTile_remove(ID *id, ImageTile *tile)
+{
+ Image *image = (Image *)id;
+ BKE_image_remove_tile(image, tile);
+
+ WM_main_add_notifier(NC_IMAGE | ND_DRAW, NULL);
+}
+
#else
static void rna_def_imageuser(BlenderRNA *brna)
@@ -555,6 +567,11 @@ static void rna_def_imageuser(BlenderRNA *brna)
RNA_def_property_int_sdna(prop, NULL, "view");
RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* image_multi_cb */
RNA_def_property_ui_text(prop, "View", "View in multilayer image");
+
+ prop = RNA_def_property(srna, "tile", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_sdna(prop, NULL, "tile");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Tile", "Tile in tiled image");
}
/* image.packed_files */
@@ -632,6 +649,56 @@ static void rna_def_render_slots(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_function_return(func, parm);
}
+static void rna_def_image_tile(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ FunctionRNA *func;
+ PropertyRNA *prop;
+ srna = RNA_def_struct(brna, "ImageTile", NULL);
+ RNA_def_struct_ui_text(srna, "Image Tile", "Properties of the image tile");
+
+ prop = RNA_def_property(srna, "label", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "label");
+ RNA_def_property_ui_text(prop, "Label", "Tile label");
+ RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, NULL);
+
+ prop = RNA_def_property(srna, "tile_number", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "tile_number");
+ RNA_def_property_ui_text(prop, "Tile Number", "Number of the position that this tile covers");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
+ func = RNA_def_function(srna, "remove", "rna_ImageTile_remove");
+ RNA_def_function_ui_description(func, "Remove this tile from the image");
+ RNA_def_function_flag(func, FUNC_USE_SELF_ID);
+}
+
+static void rna_def_image_tiles(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "ImageTiles");
+ srna = RNA_def_struct(brna, "ImageTiles", NULL);
+ RNA_def_struct_sdna(srna, "Image");
+ RNA_def_struct_ui_text(srna, "Image Tiles", "Collection of the image tiles");
+
+ func = RNA_def_function(srna, "new", "rna_ImageTile_new");
+ RNA_def_function_ui_description(func, "Add a tile to the image");
+ parm = RNA_def_int(func, "tile_number", 1, 1, INT_MAX, "", "Number of the newly created tile", 1, 100);
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_string(func, "label", NULL, 0, "", "Optional label for the tile");
+ parm = RNA_def_pointer(func, "result", "ImageTile", "", "Newly created image tile");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "get", "BKE_image_get_tile");
+ RNA_def_function_ui_description(func, "Get a tile based on its tile number");
+ parm = RNA_def_int(func, "tile_number", 0, 0, INT_MAX, "", "Number of the tile", 1, 100);
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(func, "result", "ImageTile", "", "The tile");
+ RNA_def_function_return(func, parm);
+}
+
static void rna_def_image(BlenderRNA *brna)
{
StructRNA *srna;
@@ -782,18 +849,18 @@ static void rna_def_image(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Display Aspect", "Display Aspect for this image, does not affect rendering");
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, NULL);
- prop = RNA_def_property(srna, "bindcode", PROP_INT, PROP_UNSIGNED);
- RNA_def_property_int_funcs(prop, "rna_Image_bindcode_get", NULL, NULL);
- RNA_def_property_clear_flag(prop, PROP_EDITABLE);
- RNA_def_property_ui_text(prop, "Bindcode", "OpenGL bindcode");
- RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, NULL);
-
prop = RNA_def_property(srna, "render_slots", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "RenderSlot");
RNA_def_property_collection_sdna(prop, NULL, "renderslots", NULL);
RNA_def_property_ui_text(prop, "Render Slots", "Render slots of the image");
rna_def_render_slots(brna, prop);
+ prop = RNA_def_property(srna, "tiles", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "ImageTile");
+ RNA_def_property_collection_sdna(prop, NULL, "tiles", NULL);
+ RNA_def_property_ui_text(prop, "Image Tiles", "Tiles of the image");
+ rna_def_image_tiles(brna, prop);
+
/*
* Image.has_data and Image.depth are temporary,
* Update import_obj.py when they are replaced (Arystan)
@@ -875,6 +942,7 @@ static void rna_def_image(BlenderRNA *brna)
void RNA_def_image(BlenderRNA *brna)
{
rna_def_render_slot(brna);
+ rna_def_image_tile(brna);
rna_def_image(brna);
rna_def_imageuser(brna);
rna_def_image_packed_files(brna);
diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c
index c7a3e103281..7030ec865ef 100644
--- a/source/blender/makesrna/intern/rna_image_api.c
+++ b/source/blender/makesrna/intern/rna_image_api.c
@@ -181,8 +181,8 @@ static void rna_Image_unpack(Image *image, Main *bmain, ReportList *reports, int
if (!BKE_image_has_packedfile(image)) {
BKE_report(reports, RPT_ERROR, "Image not packed");
}
- else if (BKE_image_is_animated(image)) {
- BKE_report(reports, RPT_ERROR, "Unpacking movies or image sequences not supported");
+ else if (BKE_image_has_multiple_ibufs(image)) {
+ BKE_report(reports, RPT_ERROR, "Unpacking movies, image sequences and tiled images not supported");
return;
}
else {
@@ -220,9 +220,10 @@ static void rna_Image_scale(Image *image, ReportList *reports, int width, int he
}
}
-static int rna_Image_gl_load(Image *image, ReportList *reports, int frame, int filter, int mag)
+static int rna_Image_gl_load(Image *image, ReportList *reports, int frame, int tile_number, int filter, int mag)
{
- GPUTexture *tex = image->gputexture[TEXTARGET_TEXTURE_2D];
+ ImageTile *tile = BKE_image_get_tile(image, tile_number);
+ GPUTexture *tex = tile->gputexture[TEXTARGET_TEXTURE_2D];
int error = GL_NO_ERROR;
if (tex)
@@ -231,6 +232,7 @@ static int rna_Image_gl_load(Image *image, ReportList *reports, int frame, int f
ImageUser iuser = {NULL};
iuser.framenr = frame;
iuser.ok = true;
+ iuser.tile = tile_number;
void *lock;
ImBuf *ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
@@ -259,7 +261,7 @@ static int rna_Image_gl_load(Image *image, ReportList *reports, int frame, int f
glDeleteTextures(1, (GLuint *)&bindcode);
}
else {
- image->gputexture[TEXTARGET_TEXTURE_2D] = GPU_texture_from_bindcode(GL_TEXTURE_2D, bindcode);
+ tile->gputexture[TEXTARGET_TEXTURE_2D] = GPU_texture_from_bindcode(GL_TEXTURE_2D, bindcode);
}
BKE_image_release_ibuf(image, ibuf, lock);
@@ -267,14 +269,15 @@ static int rna_Image_gl_load(Image *image, ReportList *reports, int frame, int f
return error;
}
-static int rna_Image_gl_touch(Image *image, ReportList *reports, int frame, int filter, int mag)
+static int rna_Image_gl_touch(Image *image, ReportList *reports, int frame, int tile_number, int filter, int mag)
{
int error = GL_NO_ERROR;
BKE_image_tag_time(image);
- if (image->gputexture[TEXTARGET_TEXTURE_2D] == NULL)
- error = rna_Image_gl_load(image, reports, frame, filter, mag);
+ ImageTile *tile = BKE_image_get_tile(image, tile_number);
+ if (tile->gputexture[TEXTARGET_TEXTURE_2D] == NULL)
+ error = rna_Image_gl_load(image, reports, frame, tile_number, filter, mag);
return error;
}
@@ -359,6 +362,8 @@ void RNA_api_image(StructRNA *srna)
RNA_def_function_flag(func, FUNC_USE_REPORTS);
RNA_def_int(func, "frame", 0, 0, INT_MAX, "Frame",
"Frame of image sequence or movie", 0, INT_MAX);
+ RNA_def_int(func, "tile_number", 0, 0, INT_MAX, "Tile",
+ "Tile of a tiled image", 0, INT_MAX);
RNA_def_int(func, "filter", GL_LINEAR_MIPMAP_NEAREST, -INT_MAX, INT_MAX, "Filter",
"The texture minifying function to use if the image wasn't loaded", -INT_MAX, INT_MAX);
RNA_def_int(func, "mag", GL_LINEAR, -INT_MAX, INT_MAX, "Magnification",
@@ -372,6 +377,8 @@ void RNA_api_image(StructRNA *srna)
RNA_def_function_flag(func, FUNC_USE_REPORTS);
RNA_def_int(func, "frame", 0, 0, INT_MAX, "Frame",
"Frame of image sequence or movie", 0, INT_MAX);
+ RNA_def_int(func, "tile_number", 0, 0, INT_MAX, "Tile",
+ "Tile of a tiled image", 0, INT_MAX);
RNA_def_int(func, "filter", GL_LINEAR_MIPMAP_NEAREST, -INT_MAX, INT_MAX, "Filter",
"The texture minifying function", -INT_MAX, INT_MAX);
RNA_def_int(func, "mag", GL_LINEAR, -INT_MAX, INT_MAX, "Magnification",
diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c
index febe74f63c9..974503bfaab 100644
--- a/source/blender/makesrna/intern/rna_main_api.c
+++ b/source/blender/makesrna/intern/rna_main_api.c
@@ -330,13 +330,13 @@ static Lamp *rna_Main_lights_new(Main *bmain, const char *name, int type)
return lamp;
}
-static Image *rna_Main_images_new(Main *bmain, const char *name, int width, int height, bool alpha, bool float_buffer, bool stereo3d)
+static Image *rna_Main_images_new(Main *bmain, const char *name, int width, int height, bool alpha, bool float_buffer, bool stereo3d, bool tiled)
{
char safe_name[MAX_ID_NAME - 2];
rna_idname_validate(name, safe_name);
float color[4] = {0.0, 0.0, 0.0, 1.0};
- Image *image = BKE_image_add_generated(bmain, width, height, safe_name, alpha ? 32 : 24, float_buffer, 0, color, stereo3d);
+ Image *image = BKE_image_add_generated(bmain, width, height, safe_name, alpha ? 32 : 24, float_buffer, 0, color, stereo3d, tiled);
id_us_min(&image->id);
return image;
}
@@ -1004,6 +1004,7 @@ void RNA_def_main_images(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_boolean(func, "alpha", 0, "Alpha", "Use alpha channel");
RNA_def_boolean(func, "float_buffer", 0, "Float Buffer", "Create an image with floating point color");
RNA_def_boolean(func, "stereo3d", 0, "Stereo 3D", "Create left and right views");
+ RNA_def_boolean(func, "tiled", 0, "Tiled", "Create a tiled image");
/* return type */
parm = RNA_def_pointer(func, "image", "Image", "", "New image data-block");
RNA_def_function_return(func, parm);
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 2f009238851..1bdaa569604 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -1069,7 +1069,7 @@ static const EnumPropertyItem *rna_SpaceImageEditor_draw_channels_itemf(
void *lock;
int zbuf, alpha, totitem = 0;
- ibuf = ED_space_image_acquire_buffer(sima, &lock);
+ ibuf = ED_space_image_acquire_buffer(sima, &lock, 0);
alpha = ibuf && (ibuf->channels == 4);
zbuf = ibuf && (ibuf->zbuf || ibuf->zbuf_float || (ibuf->channels == 1));
@@ -1174,7 +1174,8 @@ static void rna_SpaceImageEditor_scopes_update(struct bContext *C, struct Pointe
ImBuf *ibuf;
void *lock;
- ibuf = ED_space_image_acquire_buffer(sima, &lock);
+ /* TODO(lukas): Support tiles in scopes? */
+ ibuf = ED_space_image_acquire_buffer(sima, &lock, 0);
if (ibuf) {
ED_space_image_scopes_update(C, sima, ibuf, true);
WM_main_add_notifier(NC_IMAGE, sima->image);
@@ -2235,6 +2236,14 @@ static void rna_def_space_image_uv(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Draw Modified Edges", "Draw edges after modifiers are applied");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL);
+ prop = RNA_def_property(srna, "tile_grid_shape", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "tile_grid_shape");
+ RNA_def_property_array(prop, 2);
+ RNA_def_property_int_default(prop, 1);
+ RNA_def_property_range(prop, 1, 10);
+ RNA_def_property_ui_text(prop, "Tile Grid Shape", "How many tiles will be shown in the background");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL);
+
prop = RNA_def_property(srna, "show_other_objects", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SI_DRAW_OTHER);
RNA_def_property_ui_text(prop, "Draw Other Objects", "Draw other selected objects that share the same image");
@@ -3376,6 +3385,12 @@ static void rna_def_space_image(BlenderRNA *brna)
RNA_def_property_float_funcs(prop, "rna_SpaceImageEditor_zoom_get", NULL, NULL);
RNA_def_property_ui_text(prop, "Zoom", "Zoom factor");
+ prop = RNA_def_property(srna, "current_tile", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "curtile");
+ RNA_def_property_range(prop, 0, 1000);
+ RNA_def_property_ui_text(prop, "Current Tile", "The currently selected tile");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL);
+
/* image draw */
prop = RNA_def_property(srna, "show_repeat", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SI_DRAW_TILE);
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c b/source/blender/nodes/shader/nodes/node_shader_tex_environment.c
index 41860df5670..47001c5ffdc 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_environment.c
@@ -75,9 +75,9 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat, bNode *node, bNodeE
node_shader_gpu_tex_mapping(mat, node, in, out);
if (tex->projection == SHD_PROJ_EQUIRECTANGULAR)
- GPU_stack_link(mat, node, "node_tex_environment_equirectangular", in, out, GPU_image(ima, iuser, isdata));
+ GPU_stack_link(mat, node, "node_tex_environment_equirectangular", in, out, GPU_image(ima, iuser, isdata, 0));
else
- GPU_stack_link(mat, node, "node_tex_environment_mirror_ball", in, out, GPU_image(ima, iuser, isdata));
+ GPU_stack_link(mat, node, "node_tex_environment_mirror_ball", in, out, GPU_image(ima, iuser, isdata, 0));
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL);
if (ibuf && (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) == 0 &&
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.c b/source/blender/nodes/shader/nodes/node_shader_tex_image.c
index 9782df2638f..d68a67ab11d 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_image.c
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.c
@@ -67,6 +67,12 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, bNode *node, bNodeExecDat
"tex_box_sample_cubic",
"tex_box_sample_smart"
};
+ static const char *names_tiled[] = {
+ "node_tex_image_tile_linear",
+ "node_tex_image_tile_nearest",
+ "node_tex_image_tile_cubic",
+ "node_tex_image_tile_smart"
+ };
Image *ima = (Image *)node->id;
ImageUser *iuser = NULL;
@@ -98,46 +104,59 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, bNode *node, bNodeExecDat
node_shader_gpu_tex_mapping(mat, node, in, out);
- switch (tex->projection) {
- case SHD_PROJ_FLAT:
- GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser, isdata));
- break;
- case SHD_PROJ_BOX:
- GPU_link(mat, "direction_transform_m4v3", GPU_builtin(GPU_VIEW_NORMAL),
- GPU_builtin(GPU_INVERSE_VIEW_MATRIX),
- &norm);
- GPU_link(mat, "direction_transform_m4v3", norm,
- GPU_builtin(GPU_INVERSE_OBJECT_MATRIX),
- &norm);
- GPU_link(mat, gpu_node_name, in[0].link,
- norm,
- GPU_image(ima, iuser, isdata),
- &col1,
- &col2,
- &col3);
- if (do_color_correction) {
- GPU_link(mat, "srgb_to_linearrgb", col1, &col1);
- GPU_link(mat, "srgb_to_linearrgb", col2, &col2);
- GPU_link(mat, "srgb_to_linearrgb", col3, &col3);
- }
- GPU_link(mat, "node_tex_image_box", in[0].link,
- norm,
- col1, col2, col3,
- GPU_image(ima, iuser, isdata),
- GPU_uniform(&blend),
- &out[0].link,
- &out[1].link);
- break;
- case SHD_PROJ_SPHERE:
- GPU_link(mat, "point_texco_remap_square", in[0].link, &in[0].link);
- GPU_link(mat, "point_map_to_sphere", in[0].link, &in[0].link);
- GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser, isdata));
- break;
- case SHD_PROJ_TUBE:
- GPU_link(mat, "point_texco_remap_square", in[0].link, &in[0].link);
- GPU_link(mat, "point_map_to_tube", in[0].link, &in[0].link);
- GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser, isdata));
- break;
+ if (ima->source == IMA_SRC_TILED) {
+ GPUNodeLink *map;
+ GPU_link(mat, "node_tex_image_tiled_map", in[0].link, &out[0].link, &map);
+ LISTBASE_FOREACH(ImageTile *, tile, &ima->tiles) {
+ float tile_number = tile->tile_number;
+ GPU_link(mat, names_tiled[tex->interpolation],
+ map, GPU_uniform(&tile_number),
+ GPU_image(ima, iuser, isdata, tile->tile_number),
+ out[0].link, &out[0].link, &out[1].link);
+ }
+ }
+ else {
+ switch (tex->projection) {
+ case SHD_PROJ_FLAT:
+ GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser, isdata, 0));
+ break;
+ case SHD_PROJ_BOX:
+ GPU_link(mat, "direction_transform_m4v3", GPU_builtin(GPU_VIEW_NORMAL),
+ GPU_builtin(GPU_INVERSE_VIEW_MATRIX),
+ &norm);
+ GPU_link(mat, "direction_transform_m4v3", norm,
+ GPU_builtin(GPU_INVERSE_OBJECT_MATRIX),
+ &norm);
+ GPU_link(mat, gpu_node_name, in[0].link,
+ norm,
+ GPU_image(ima, iuser, isdata, 0),
+ &col1,
+ &col2,
+ &col3);
+ if (do_color_correction) {
+ GPU_link(mat, "srgb_to_linearrgb", col1, &col1);
+ GPU_link(mat, "srgb_to_linearrgb", col2, &col2);
+ GPU_link(mat, "srgb_to_linearrgb", col3, &col3);
+ }
+ GPU_link(mat, "node_tex_image_box", in[0].link,
+ norm,
+ col1, col2, col3,
+ GPU_image(ima, iuser, isdata, 0),
+ GPU_uniform(&blend),
+ &out[0].link,
+ &out[1].link);
+ break;
+ case SHD_PROJ_SPHERE:
+ GPU_link(mat, "point_texco_remap_square", in[0].link, &in[0].link);
+ GPU_link(mat, "point_map_to_sphere", in[0].link, &in[0].link);
+ GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser, isdata, 0));
+ break;
+ case SHD_PROJ_TUBE:
+ GPU_link(mat, "point_texco_remap_square", in[0].link, &in[0].link);
+ GPU_link(mat, "point_map_to_tube", in[0].link, &in[0].link);
+ GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser, isdata, 0));
+ break;
+ }
}
if (do_color_correction && (tex->projection != SHD_PROJ_BOX)) {