Previously, `ImageTile->ok` and `ImageUser->ok` were used to indicate whether an image failed to load. There were three possible values which (probably) had the following meanings: * `0`: There was an error while loading the image. Don't try to load again. * `1`: Default value. Try to load the image. * `2`: The image was loaded successfully. This image-wide flag did not make sense unfortunately, because loading may work for some frames of an image sequence but not for others. Remember than an image data block can also contain a movie. The purpose of the `->ok` flag was to serve as an optimization to avoid trying to load a file over and over again when there is an error (e.g. the file does not exist or is invalid). To get the optimization back, the patch is changing `MovieCache` so that it can also cache failed load attempts. As a consequence, `ibuf` is allowed to be `NULL` in a few more places. I added the appropriate null checks. This also solves issues when image sequences are used with the Image Texture node in Geometry nodes (also see D12827). Differential Revision: https://developer.blender.org/D12957
289 lines
8.2 KiB
C++
289 lines
8.2 KiB
C++
/*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* Copyright 2011, Blender Foundation.
|
|
*/
|
|
|
|
#include "COM_ViewerOperation.h"
|
|
#include "BKE_image.h"
|
|
#include "BKE_scene.h"
|
|
#include "COM_ExecutionSystem.h"
|
|
|
|
#include "IMB_colormanagement.h"
|
|
#include "IMB_imbuf.h"
|
|
#include "IMB_imbuf_types.h"
|
|
|
|
namespace blender::compositor {
|
|
|
|
static int MAX_VIEWER_TRANSLATION_PADDING = 12000;
|
|
|
|
ViewerOperation::ViewerOperation()
|
|
{
|
|
this->set_image(nullptr);
|
|
this->set_image_user(nullptr);
|
|
output_buffer_ = nullptr;
|
|
depth_buffer_ = nullptr;
|
|
active_ = false;
|
|
do_depth_buffer_ = false;
|
|
view_settings_ = nullptr;
|
|
display_settings_ = nullptr;
|
|
use_alpha_input_ = false;
|
|
|
|
this->add_input_socket(DataType::Color);
|
|
this->add_input_socket(DataType::Value);
|
|
this->add_input_socket(DataType::Value);
|
|
|
|
image_input_ = nullptr;
|
|
alpha_input_ = nullptr;
|
|
depth_input_ = nullptr;
|
|
rd_ = nullptr;
|
|
view_name_ = nullptr;
|
|
flags_.use_viewer_border = true;
|
|
flags_.is_viewer_operation = true;
|
|
}
|
|
|
|
void ViewerOperation::init_execution()
|
|
{
|
|
/* When initializing the tree during initial load the width and height can be zero. */
|
|
image_input_ = get_input_socket_reader(0);
|
|
alpha_input_ = get_input_socket_reader(1);
|
|
depth_input_ = get_input_socket_reader(2);
|
|
do_depth_buffer_ = (depth_input_ != nullptr);
|
|
|
|
if (is_active_viewer_output() && !exec_system_->is_breaked()) {
|
|
init_image();
|
|
}
|
|
}
|
|
|
|
void ViewerOperation::deinit_execution()
|
|
{
|
|
image_input_ = nullptr;
|
|
alpha_input_ = nullptr;
|
|
depth_input_ = nullptr;
|
|
output_buffer_ = nullptr;
|
|
}
|
|
|
|
void ViewerOperation::execute_region(rcti *rect, unsigned int /*tile_number*/)
|
|
{
|
|
float *buffer = output_buffer_;
|
|
float *depthbuffer = depth_buffer_;
|
|
if (!buffer) {
|
|
return;
|
|
}
|
|
const int x1 = rect->xmin;
|
|
const int y1 = rect->ymin;
|
|
const int x2 = rect->xmax;
|
|
const int y2 = rect->ymax;
|
|
const int offsetadd = (this->get_width() - (x2 - x1));
|
|
const int offsetadd4 = offsetadd * 4;
|
|
int offset = (y1 * this->get_width() + x1);
|
|
int offset4 = offset * 4;
|
|
float alpha[4], depth[4];
|
|
int x;
|
|
int y;
|
|
bool breaked = false;
|
|
|
|
for (y = y1; y < y2 && (!breaked); y++) {
|
|
for (x = x1; x < x2; x++) {
|
|
image_input_->read_sampled(&(buffer[offset4]), x, y, PixelSampler::Nearest);
|
|
if (use_alpha_input_) {
|
|
alpha_input_->read_sampled(alpha, x, y, PixelSampler::Nearest);
|
|
buffer[offset4 + 3] = alpha[0];
|
|
}
|
|
depth_input_->read_sampled(depth, x, y, PixelSampler::Nearest);
|
|
depthbuffer[offset] = depth[0];
|
|
|
|
offset++;
|
|
offset4 += 4;
|
|
}
|
|
if (is_braked()) {
|
|
breaked = true;
|
|
}
|
|
offset += offsetadd;
|
|
offset4 += offsetadd4;
|
|
}
|
|
update_image(rect);
|
|
}
|
|
|
|
void ViewerOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
|
|
{
|
|
const int scene_render_width = rd_->xsch * rd_->size / 100;
|
|
const int scene_render_height = rd_->ysch * rd_->size / 100;
|
|
|
|
rcti local_preferred = preferred_area;
|
|
local_preferred.xmax = local_preferred.xmin + scene_render_width;
|
|
local_preferred.ymax = local_preferred.ymin + scene_render_height;
|
|
|
|
NodeOperation::determine_canvas(local_preferred, r_area);
|
|
}
|
|
|
|
void ViewerOperation::init_image()
|
|
{
|
|
Image *ima = image_;
|
|
ImageUser iuser = *image_user_;
|
|
void *lock;
|
|
ImBuf *ibuf;
|
|
|
|
/* make sure the image has the correct number of views */
|
|
if (ima && BKE_scene_multiview_is_render_view_first(rd_, view_name_)) {
|
|
BKE_image_ensure_viewer_views(rd_, ima, image_user_);
|
|
}
|
|
|
|
BLI_thread_lock(LOCK_DRAW_IMAGE);
|
|
|
|
/* local changes to the original ImageUser */
|
|
iuser.multi_index = BKE_scene_multiview_view_id_get(rd_, view_name_);
|
|
ibuf = BKE_image_acquire_ibuf(ima, &iuser, &lock);
|
|
|
|
if (!ibuf) {
|
|
BLI_thread_unlock(LOCK_DRAW_IMAGE);
|
|
return;
|
|
}
|
|
|
|
int padding_x = abs(canvas_.xmin) * 2;
|
|
int padding_y = abs(canvas_.ymin) * 2;
|
|
if (padding_x > MAX_VIEWER_TRANSLATION_PADDING) {
|
|
padding_x = MAX_VIEWER_TRANSLATION_PADDING;
|
|
}
|
|
if (padding_y > MAX_VIEWER_TRANSLATION_PADDING) {
|
|
padding_y = MAX_VIEWER_TRANSLATION_PADDING;
|
|
}
|
|
|
|
display_width_ = get_width() + padding_x;
|
|
display_height_ = get_height() + padding_y;
|
|
if (ibuf->x != display_width_ || ibuf->y != display_height_) {
|
|
imb_freerectImBuf(ibuf);
|
|
imb_freerectfloatImBuf(ibuf);
|
|
IMB_freezbuffloatImBuf(ibuf);
|
|
ibuf->x = display_width_;
|
|
ibuf->y = display_height_;
|
|
/* zero size can happen if no image buffers exist to define a sensible resolution */
|
|
if (ibuf->x > 0 && ibuf->y > 0) {
|
|
imb_addrectfloatImBuf(ibuf);
|
|
}
|
|
|
|
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
|
|
}
|
|
|
|
if (do_depth_buffer_) {
|
|
addzbuffloatImBuf(ibuf);
|
|
}
|
|
|
|
/* now we combine the input with ibuf */
|
|
output_buffer_ = ibuf->rect_float;
|
|
|
|
/* needed for display buffer update */
|
|
ibuf_ = ibuf;
|
|
|
|
if (do_depth_buffer_) {
|
|
depth_buffer_ = ibuf->zbuf_float;
|
|
}
|
|
|
|
BKE_image_release_ibuf(image_, ibuf_, lock);
|
|
|
|
BLI_thread_unlock(LOCK_DRAW_IMAGE);
|
|
}
|
|
|
|
void ViewerOperation::update_image(const rcti *rect)
|
|
{
|
|
if (exec_system_->is_breaked()) {
|
|
return;
|
|
}
|
|
|
|
float *buffer = output_buffer_;
|
|
IMB_partial_display_buffer_update(ibuf_,
|
|
buffer,
|
|
nullptr,
|
|
display_width_,
|
|
0,
|
|
0,
|
|
view_settings_,
|
|
display_settings_,
|
|
rect->xmin,
|
|
rect->ymin,
|
|
rect->xmax,
|
|
rect->ymax);
|
|
image_->gpuflag |= IMA_GPU_REFRESH;
|
|
this->update_draw();
|
|
}
|
|
|
|
eCompositorPriority ViewerOperation::get_render_priority() const
|
|
{
|
|
if (this->is_active_viewer_output()) {
|
|
return eCompositorPriority::High;
|
|
}
|
|
|
|
return eCompositorPriority::Low;
|
|
}
|
|
|
|
void ViewerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output),
|
|
const rcti &area,
|
|
Span<MemoryBuffer *> inputs)
|
|
{
|
|
if (!output_buffer_) {
|
|
return;
|
|
}
|
|
|
|
const int offset_x = area.xmin + (canvas_.xmin > 0 ? canvas_.xmin * 2 : 0);
|
|
const int offset_y = area.ymin + (canvas_.ymin > 0 ? canvas_.ymin * 2 : 0);
|
|
MemoryBuffer output_buffer(
|
|
output_buffer_, COM_DATA_TYPE_COLOR_CHANNELS, display_width_, display_height_);
|
|
const MemoryBuffer *input_image = inputs[0];
|
|
output_buffer.copy_from(input_image, area, offset_x, offset_y);
|
|
if (use_alpha_input_) {
|
|
const MemoryBuffer *input_alpha = inputs[1];
|
|
output_buffer.copy_from(
|
|
input_alpha, area, 0, COM_DATA_TYPE_VALUE_CHANNELS, offset_x, offset_y, 3);
|
|
}
|
|
|
|
if (depth_buffer_) {
|
|
MemoryBuffer depth_buffer(
|
|
depth_buffer_, COM_DATA_TYPE_VALUE_CHANNELS, display_width_, display_height_);
|
|
const MemoryBuffer *input_depth = inputs[2];
|
|
depth_buffer.copy_from(input_depth, area, offset_x, offset_y);
|
|
}
|
|
|
|
rcti display_area;
|
|
BLI_rcti_init(&display_area,
|
|
offset_x,
|
|
offset_x + BLI_rcti_size_x(&area),
|
|
offset_y,
|
|
offset_y + BLI_rcti_size_y(&area));
|
|
update_image(&display_area);
|
|
}
|
|
|
|
void ViewerOperation::clear_display_buffer()
|
|
{
|
|
BLI_assert(is_active_viewer_output());
|
|
if (exec_system_->is_breaked()) {
|
|
return;
|
|
}
|
|
|
|
init_image();
|
|
if (output_buffer_ == nullptr) {
|
|
return;
|
|
}
|
|
|
|
size_t buf_bytes = (size_t)ibuf_->y * ibuf_->x * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float);
|
|
if (buf_bytes > 0) {
|
|
memset(output_buffer_, 0, buf_bytes);
|
|
rcti display_area;
|
|
BLI_rcti_init(&display_area, 0, ibuf_->x, 0, ibuf_->y);
|
|
update_image(&display_area);
|
|
}
|
|
}
|
|
|
|
} // namespace blender::compositor
|