Fix #114260: Compositor sometimes produces straight alpha #114305

Merged
Omar Emara merged 6 commits from OmarEmaraDev/blender:fix-114260 into main 2023-11-06 15:15:33 +01:00
5 changed files with 74 additions and 9 deletions

View File

@ -312,9 +312,21 @@ static void colorspace_to_scene_linear(MemoryBuffer *buf, const rcti &area, Colo
}
}
void MemoryBuffer::copy_from(const ImBuf *src, const rcti &area, const bool ensure_linear_space)
static void premultiply_alpha(MemoryBuffer *buf, const rcti &area)
{
copy_from(src, area, 0, this->get_num_channels(), 0, ensure_linear_space);
for (int y = area.ymin; y < area.ymax; y++) {
for (int x = area.xmin; x < area.xmax; x++) {
straight_to_premul_v4(buf->get_elem(x, y));
}
}
}
void MemoryBuffer::copy_from(const ImBuf *src,
const rcti &area,
const bool ensure_premultiplied,
const bool ensure_linear_space)
{
copy_from(src, area, 0, this->get_num_channels(), 0, ensure_premultiplied, ensure_linear_space);
}
void MemoryBuffer::copy_from(const ImBuf *src,
@ -322,6 +334,7 @@ void MemoryBuffer::copy_from(const ImBuf *src,
const int channel_offset,
const int elem_size,
const int to_channel_offset,
const bool ensure_premultiplied,
const bool ensure_linear_space)
{
copy_from(src,
@ -331,6 +344,7 @@ void MemoryBuffer::copy_from(const ImBuf *src,
area.xmin,
area.ymin,
to_channel_offset,
ensure_premultiplied,
ensure_linear_space);
}
@ -341,6 +355,7 @@ void MemoryBuffer::copy_from(const ImBuf *src,
const int to_x,
const int to_y,
const int to_channel_offset,
const bool ensure_premultiplied,
const bool ensure_linear_space)
{
if (src->float_buffer.data) {
@ -363,6 +378,9 @@ void MemoryBuffer::copy_from(const ImBuf *src,
if (ensure_linear_space) {
colorspace_to_scene_linear(this, area, src->byte_buffer.colorspace);
}
if (ensure_premultiplied) {
premultiply_alpha(this, area);
}
}
else {
/* Empty ImBuf source. Fill destination with empty values. */

View File

@ -603,12 +603,16 @@ class MemoryBuffer {
int to_x,
int to_y,
int to_channel_offset);
void copy_from(const struct ImBuf *src, const rcti &area, bool ensure_linear_space = false);
void copy_from(const struct ImBuf *src,
const rcti &area,
bool ensure_premultiplied = false,
bool ensure_linear_space = false);
void copy_from(const struct ImBuf *src,
const rcti &area,
int channel_offset,
int elem_size,
int to_channel_offset,
bool ensure_premultiplied = false,
bool ensure_linear_space = false);
void copy_from(const struct ImBuf *src,
const rcti &src_area,
@ -617,6 +621,7 @@ class MemoryBuffer {
int to_x,
int to_y,
int to_channel_offset,
bool ensure_premultiplied = false,
bool ensure_linear_space = false);
void fill(const rcti &area, const float *value);

View File

@ -91,8 +91,13 @@ void BaseImageOperation::determine_canvas(const rcti & /*preferred_area*/, rcti
BKE_image_release_ibuf(image_, stackbuf, nullptr);
}
static void sample_image_at_location(
ImBuf *ibuf, float x, float y, PixelSampler sampler, bool make_linear_rgb, float color[4])
static void sample_image_at_location(ImBuf *ibuf,
float x,
float y,
PixelSampler sampler,
bool make_linear_rgb,
bool ensure_premultiplied,
float color[4])
{
if (ibuf->float_buffer.data) {
switch (sampler) {
@ -125,6 +130,9 @@ static void sample_image_at_location(
IMB_colormanagement_colorspace_to_scene_linear_v4(
color, false, ibuf->byte_buffer.colorspace);
}
if (ensure_premultiplied) {
straight_to_premul_v4(color);
}
}
}
@ -138,7 +146,9 @@ void ImageOperation::execute_pixel_sampled(float output[4], float x, float y, Pi
zero_v4(output);
}
else {
sample_image_at_location(buffer_, x, y, sampler, true, output);
const bool ensure_premultiplied = !ELEM(
image_->alpha_mode, IMA_ALPHA_CHANNEL_PACKED, IMA_ALPHA_IGNORE);
sample_image_at_location(buffer_, x, y, sampler, true, ensure_premultiplied, output);
}
}
@ -146,7 +156,9 @@ void ImageOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> /*inputs*/)
{
output->copy_from(buffer_, area, true);
const bool ensure_premultiplied = !ELEM(
image_->alpha_mode, IMA_ALPHA_CHANNEL_PACKED, IMA_ALPHA_IGNORE);
output->copy_from(buffer_, area, ensure_premultiplied, true);
}
void ImageAlphaOperation::execute_pixel_sampled(float output[4],
@ -161,7 +173,7 @@ void ImageAlphaOperation::execute_pixel_sampled(float output[4],
}
else {
tempcolor[3] = 1.0f;
sample_image_at_location(buffer_, x, y, sampler, false, tempcolor);
sample_image_at_location(buffer_, x, y, sampler, false, false, tempcolor);
output[0] = tempcolor[3];
}
}

View File

@ -24,8 +24,10 @@ GPU_SHADER_CREATE_INFO(compositor_read_input_vector)
GPU_SHADER_CREATE_INFO(compositor_read_input_color)
.additional_info("compositor_read_input_shared")
.push_constant(Type::BOOL, "premultiply_alpha")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.define("READ_EXPRESSION(input_color)", "input_color")
.define("READ_EXPRESSION(input_color)",
"input_color * vec4(vec3(premultiply_alpha ? input_color.a : 1.0), 1.0)")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_read_input_alpha)

View File

@ -23,6 +23,7 @@
#include "DEG_depsgraph_query.hh"
#include "DNA_image_types.h"
#include "DNA_scene_types.h"
#include "DNA_vec_types.h"
@ -519,6 +520,10 @@ class ImageOperation : public NodeOperation {
const int2 lower_bound = int2(0);
GPU_shader_uniform_2iv(shader, "lower_bound", lower_bound);
if (result.type() == ResultType::Color) {
GPU_shader_uniform_1b(shader, "premultiply_alpha", should_premultiply_alpha(image_user));
}
const int input_unit = GPU_shader_get_sampler_binding(shader, "input_tx");
GPU_texture_bind(image_texture, input_unit);
@ -570,6 +575,29 @@ class ImageOperation : public NodeOperation {
}
}
/* Compositor image inputs are expected to be always premultiplied, so identify if the GPU
* texture returned by the image module is straight and needs to be premultiplied. An exception
* is when the image has an alpha mode of channel packed or alpha ignore, in which case, we
* always ignore premultiplication. */
bool should_premultiply_alpha(ImageUser &image_user)
{
Image *image = get_image();
if (ELEM(image->alpha_mode, IMA_ALPHA_CHANNEL_PACKED, IMA_ALPHA_IGNORE)) {
return false;
}
ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &image_user, nullptr);
if (!image_buffer) {
return false;
}
const bool has_premultiplied_alpha = BKE_image_has_gpu_texture_premultiplied_alpha(
image, image_buffer);
BKE_image_release_ibuf(image, image_buffer, nullptr);
return !has_premultiplied_alpha;
}
Image *get_image()
{
return (Image *)bnode().id;