From f0e92c695d5df145477176052a9edacbc35e2b12 Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Thu, 20 Feb 2025 07:29:34 +0100 Subject: [PATCH] Fix: File Output node crash when saving vector image The File Output node crashes when saving a 16-bit vector image in an RGBA image. That's because the OIIO writer assumes 4-channel buffer while the buffer provided by the node is only 3-channel. To fix this, the OIIO writer is extended to support all possible combination of source and target channels. Pull Request: https://projects.blender.org/blender/blender/pulls/134789 --- .../imbuf/intern/oiio/openimageio_support.cc | 66 ++++++++++++------- 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/source/blender/imbuf/intern/oiio/openimageio_support.cc b/source/blender/imbuf/intern/oiio/openimageio_support.cc index afc772dd4d4..2f10ceb3b63 100644 --- a/source/blender/imbuf/intern/oiio/openimageio_support.cc +++ b/source/blender/imbuf/intern/oiio/openimageio_support.cc @@ -300,35 +300,55 @@ bool imb_oiio_write(const WriteContext &ctx, const char *filepath, const ImageSp ImageBuf orig_buf(ctx.mem_spec, ctx.mem_start, ctx.mem_xstride, -ctx.mem_ystride, AutoStride); ImageBuf final_buf{}; - /* Grayscale images need to be based on luminance weights rather than only - * using a single channel from the source. */ - if (ctx.ibuf->channels > 1 && file_spec.nchannels == 1) { - float weights[4] = {}; #if OIIO_VERSION_MAJOR >= 3 - const size_t nchannels = orig_buf.nchannels(); + const size_t original_channels_count = orig_buf.nchannels(); #else - const int nchannels = orig_buf.nchannels(); + const int original_channels_count = orig_buf.nchannels(); #endif + + if (original_channels_count > 1 && file_spec.nchannels == 1) { + /* Convert to gray-scale image by computing the luminance. Make sure the weight of alpha + * channel is zero since it should not contribute to the luminance. */ + float weights[4] = {0.0f, 0.0f, 0.0f, 0.0f}; IMB_colormanagement_get_luminance_coefficients(weights); - ImageBufAlgo::channel_sum(final_buf, orig_buf, {weights, nchannels}); + ImageBufAlgo::channel_sum(final_buf, orig_buf, {weights, original_channels_count}); + } + else if (original_channels_count == 1 && file_spec.nchannels > 1) { + /* Broadcast the gray-scale channel to as many channels as needed, filling the alpha channel + * with ones if needed. 0 channel order mean we will be copying from the first channel, while + * -1 means we will be filling based on the corresponding value from the defined channel + * values. */ + const int channel_order[] = {0, 0, 0, -1}; + const float channel_values[] = {0.0f, 0.0f, 0.0f, 1.0f}; + const std::string channel_names[] = {"R", "G", "B", "A"}; + ImageBufAlgo::channels(final_buf, + orig_buf, + file_spec.nchannels, + cspan(channel_order, file_spec.nchannels), + cspan(channel_values, file_spec.nchannels), + cspan(channel_names, file_spec.nchannels)); + } + else if (original_channels_count != file_spec.nchannels) { + /* Either trim or fill new channels based on the needed channels count. */ + int channel_order[4]; + for (int i = 0; i < 4; i++) { + /* If a channel exists in the original buffer, we copy it, if not, we fill it by supplying + * -1, which is a special value that means filling based on the value in the defined channels + * values. So alpha is filled with 1, and other channels are filled with zero. */ + const bool channel_exists = i + 1 <= original_channels_count; + channel_order[i] = channel_exists ? i : -1; + } + const float channel_values[] = {0.0f, 0.0f, 0.0f, 1.0f}; + const std::string channel_names[] = {"R", "G", "B", "A"}; + ImageBufAlgo::channels(final_buf, + orig_buf, + file_spec.nchannels, + cspan(channel_order, file_spec.nchannels), + cspan(channel_values, file_spec.nchannels), + cspan(channel_names, file_spec.nchannels)); } else { - /* If we are moving from an 1-channel format to n-channel we need to - * ensure the original data is copied into the higher channels. */ - if (ctx.ibuf->channels == 1 && file_spec.nchannels > 1) { - final_buf = ImageBuf(file_spec, InitializePixels::No); - ImageBufAlgo::paste(final_buf, 0, 0, 0, 0, orig_buf); - ImageBufAlgo::paste(final_buf, 0, 0, 0, 1, orig_buf); - ImageBufAlgo::paste(final_buf, 0, 0, 0, 2, orig_buf); - if (file_spec.alpha_channel == 3) { - ROI alpha_roi = file_spec.roi(); - alpha_roi.chbegin = file_spec.alpha_channel; - ImageBufAlgo::fill(final_buf, {0, 0, 0, 1.0f}, alpha_roi); - } - } - else { - final_buf = std::move(orig_buf); - } + final_buf = std::move(orig_buf); } bool write_ok = false;