There exist a bunch of "give me a (filtered) image pixel at this location"
functions, some with duplicated functionality, some with almost the same but
not quite, some that look similar but behave slightly differently, etc.
Some of them were in BLI, some were in ImBuf.
This commit tries to improve the situation by:
* Adding low level interpolation functions to `BLI_math_interp.hh`
- With documentation on their behavior,
- And with more unit tests.
* At `ImBuf` level, there are only convenience inline wrappers to the above BLI
functions (split off into a separate header `IMB_interp.hh`). However, since
these wrappers are inline, some things get a tiny bit faster as a side
effect. E.g. VSE image strip, scaling to 4K resolution (Windows/Ryzen5950X):
- Nearest filter: 2.33 -> 1.94ms
- Bilinear filter: 5.83 -> 5.69ms
- Subsampled3x3 filter: 28.6 -> 22.4ms
Details on the functions:
- All of them have `_byte` and `_fl` suffixes.
- They exist in 4-channel byte (uchar4) and float (float4), as well as
explicitly passed amount of channels for other float images.
- New functions in BLI `blender::math` namespace:
- `interpolate_nearest`
- `interpolate_bilinear`
- `interpolate_bilinear_wrap`. Note that unlike previous "wrap" function,
this one no longer requires the caller to do their own wrapping.
- `interpolate_cubic_bspline`. Previous similar function was called just
"bicubic" which could mean many different things.
- Same functions exist in `IMB_interp.hh`, they are just convenience that takes
ImBuf and uses data pointer, width, height from that.
Other bits:
- Renamed `mod_f_positive` to `floored_fmod` (better matches `safe_floored_modf`
and `floored_modulo` that exist elsewhere), made it branchless and added more
unit tests.
- `interpolate_bilinear_wrap_fl` no longer clamps result to 0..1 range. Instead,
moved the clamp to be outside of the call in `paint_image_proj.cc` and
`paint_utils.cc`. Though the need for clamping in there is also questionable.
Pull Request: https://projects.blender.org/blender/blender/pulls/117387
158 lines
4.7 KiB
C++
158 lines
4.7 KiB
C++
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include "COM_MultilayerImageOperation.h"
|
|
|
|
#include "BLI_string.h"
|
|
|
|
#include "IMB_interp.hh"
|
|
|
|
namespace blender::compositor {
|
|
|
|
MultilayerBaseOperation::MultilayerBaseOperation(RenderLayer *render_layer,
|
|
RenderPass *render_pass,
|
|
int view)
|
|
{
|
|
pass_id_ = BLI_findindex(&render_layer->passes, render_pass);
|
|
view_ = view;
|
|
render_layer_ = render_layer;
|
|
render_pass_ = render_pass;
|
|
}
|
|
|
|
ImBuf *MultilayerBaseOperation::get_im_buf()
|
|
{
|
|
/* temporarily changes the view to get the right ImBuf */
|
|
int view = image_user_->view;
|
|
|
|
image_user_->view = view_;
|
|
image_user_->pass = pass_id_;
|
|
|
|
if (BKE_image_multilayer_index(image_->rr, image_user_)) {
|
|
ImBuf *ibuf = BaseImageOperation::get_im_buf();
|
|
image_user_->view = view;
|
|
return ibuf;
|
|
}
|
|
|
|
image_user_->view = view;
|
|
return nullptr;
|
|
}
|
|
|
|
void MultilayerBaseOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
|
const rcti &area,
|
|
Span<MemoryBuffer *> /*inputs*/)
|
|
{
|
|
if (buffer_) {
|
|
output->copy_from(buffer_, area);
|
|
}
|
|
else {
|
|
output->clear();
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<MetaData> MultilayerColorOperation::get_meta_data()
|
|
{
|
|
BLI_assert(buffer_);
|
|
MetaDataExtractCallbackData callback_data = {nullptr};
|
|
RenderResult *render_result = image_->rr;
|
|
if (render_result && render_result->stamp_data) {
|
|
RenderLayer *render_layer = render_layer_;
|
|
RenderPass *render_pass = render_pass_;
|
|
std::string full_layer_name =
|
|
std::string(render_layer->name,
|
|
BLI_strnlen(render_layer->name, sizeof(render_layer->name))) +
|
|
"." +
|
|
std::string(render_pass->name, BLI_strnlen(render_pass->name, sizeof(render_pass->name)));
|
|
blender::StringRef cryptomatte_layer_name =
|
|
blender::bke::cryptomatte::BKE_cryptomatte_extract_layer_name(full_layer_name);
|
|
callback_data.set_cryptomatte_keys(cryptomatte_layer_name);
|
|
|
|
BKE_stamp_info_callback(&callback_data,
|
|
render_result->stamp_data,
|
|
MetaDataExtractCallbackData::extract_cryptomatte_meta_data,
|
|
false);
|
|
}
|
|
|
|
return std::move(callback_data.meta_data);
|
|
}
|
|
|
|
void MultilayerColorOperation::execute_pixel_sampled(float output[4],
|
|
float x,
|
|
float y,
|
|
PixelSampler sampler)
|
|
{
|
|
if (image_float_buffer_ == nullptr) {
|
|
zero_v4(output);
|
|
}
|
|
else {
|
|
if (number_of_channels_ == 4) {
|
|
switch (sampler) {
|
|
case PixelSampler::Nearest:
|
|
imbuf::interpolate_nearest_fl(buffer_, output, x, y);
|
|
break;
|
|
case PixelSampler::Bilinear:
|
|
imbuf::interpolate_bilinear_fl(buffer_, output, x, y);
|
|
break;
|
|
case PixelSampler::Bicubic:
|
|
imbuf::interpolate_cubic_bspline_fl(buffer_, output, x, y);
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
int yi = y;
|
|
int xi = x;
|
|
if (xi < 0 || yi < 0 || uint(xi) >= this->get_width() || uint(yi) >= this->get_height()) {
|
|
zero_v4(output);
|
|
}
|
|
else {
|
|
int offset = (yi * this->get_width() + xi) * 3;
|
|
copy_v3_v3(output, &image_float_buffer_[offset]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MultilayerValueOperation::execute_pixel_sampled(float output[4],
|
|
float x,
|
|
float y,
|
|
PixelSampler /*sampler*/)
|
|
{
|
|
if (image_float_buffer_ == nullptr) {
|
|
output[0] = 0.0f;
|
|
}
|
|
else {
|
|
int yi = y;
|
|
int xi = x;
|
|
if (xi < 0 || yi < 0 || uint(xi) >= this->get_width() || uint(yi) >= this->get_height()) {
|
|
output[0] = 0.0f;
|
|
}
|
|
else {
|
|
float result = image_float_buffer_[yi * this->get_width() + xi];
|
|
output[0] = result;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MultilayerVectorOperation::execute_pixel_sampled(float output[4],
|
|
float x,
|
|
float y,
|
|
PixelSampler /*sampler*/)
|
|
{
|
|
if (image_float_buffer_ == nullptr) {
|
|
output[0] = 0.0f;
|
|
}
|
|
else {
|
|
int yi = y;
|
|
int xi = x;
|
|
if (xi < 0 || yi < 0 || uint(xi) >= this->get_width() || uint(yi) >= this->get_height()) {
|
|
output[0] = 0.0f;
|
|
}
|
|
else {
|
|
int offset = (yi * this->get_width() + xi) * 3;
|
|
copy_v3_v3(output, &image_float_buffer_[offset]);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace blender::compositor
|