Fix #114260: Compositor sometimes produces straight alpha #114305
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue