Fix #125711: Crash saving stereo EXR image from command line

* Ensure valid bit depth is set along with file type
* Guard against invalid inputs in stereo imbuf creation
* Remove some unused code

Thanks Yiming Wu for finding the cause.

Pull Request: https://projects.blender.org/blender/blender/pulls/133499
This commit is contained in:
Brecht Van Lommel
2025-01-23 19:15:17 +01:00
committed by Brecht Van Lommel
parent cfd5d9e3ad
commit 7584ccc28d
8 changed files with 113 additions and 230 deletions

View File

@@ -32,6 +32,8 @@ void BKE_image_format_update_color_space_for_type(ImageFormatData *format);
void BKE_image_format_blend_read_data(BlendDataReader *reader, ImageFormatData *imf);
void BKE_image_format_blend_write(BlendWriter *writer, ImageFormatData *imf);
void BKE_image_format_set(ImageFormatData *imf, ID *owner_id, const char imtype);
/* File Paths */
void BKE_image_path_from_imformat(char *filepath,
@@ -88,6 +90,7 @@ bool BKE_imtype_requires_linear_float(char imtype);
char BKE_imtype_valid_channels(char imtype, bool write_file);
char BKE_imtype_valid_depths(char imtype);
char BKE_imtype_valid_depths_with_video(char imtype, const ID *owner_id);
char BKE_imtype_first_valid_depth(const char valid_depths);
/**
* String is from command line `--render-format` argument,

View File

@@ -87,6 +87,40 @@ void BKE_image_format_blend_write(BlendWriter *writer, ImageFormatData *imf)
BKE_color_managed_view_settings_blend_write(writer, &imf->view_settings);
}
void BKE_image_format_set(ImageFormatData *imf, ID *owner_id, const char imtype)
{
imf->imtype = imtype;
const bool is_render = (owner_id && GS(owner_id->name) == ID_SCE);
/* see note below on why this is */
const char chan_flag = BKE_imtype_valid_channels(imf->imtype, true) |
(is_render ? IMA_CHAN_FLAG_BW : 0);
/* ensure depth and color settings match */
if ((imf->planes == R_IMF_PLANES_BW) && !(chan_flag & IMA_CHAN_FLAG_BW)) {
imf->planes = R_IMF_PLANES_RGBA;
}
if ((imf->planes == R_IMF_PLANES_RGBA) && !(chan_flag & IMA_CHAN_FLAG_RGBA)) {
imf->planes = R_IMF_PLANES_RGB;
}
/* ensure usable depth */
{
const int depth_ok = BKE_imtype_valid_depths(imf->imtype);
if ((imf->depth & depth_ok) == 0) {
imf->depth = BKE_imtype_first_valid_depth(depth_ok);
}
}
if (owner_id && GS(owner_id->name) == ID_SCE) {
Scene *scene = reinterpret_cast<Scene *>(owner_id);
RenderData *rd = &scene->r;
MOV_validate_output_settings(rd, imf);
}
BKE_image_format_update_color_space_for_type(imf);
}
/* File Types */
int BKE_imtype_to_ftype(const char imtype, ImbFormatOptions *r_options)
@@ -338,6 +372,27 @@ char BKE_imtype_valid_depths_with_video(char imtype, const ID *owner_id)
return depths;
}
char BKE_imtype_first_valid_depth(const char valid_depths)
{
/* set first available depth */
const char depth_ls[] = {
R_IMF_CHAN_DEPTH_32,
R_IMF_CHAN_DEPTH_24,
R_IMF_CHAN_DEPTH_16,
R_IMF_CHAN_DEPTH_12,
R_IMF_CHAN_DEPTH_10,
R_IMF_CHAN_DEPTH_8,
R_IMF_CHAN_DEPTH_1,
0,
};
for (int i = 0; depth_ls[i]; i++) {
if (valid_depths & depth_ls[i]) {
return depth_ls[i];
}
}
return R_IMF_CHAN_DEPTH_8;
}
char BKE_imtype_from_arg(const char *imtype_arg)
{
if (STREQ(imtype_arg, "TGA")) {

View File

@@ -579,9 +579,11 @@ static bool image_save_single(ReportList *reports,
ibuf = IMB_stereo3d_ImBuf(imf, ibuf_stereo[0], ibuf_stereo[1]);
/* save via traditional path */
ok = BKE_imbuf_write_as(ibuf, opts->filepath, imf, save_copy);
if (ibuf) {
ok = BKE_imbuf_write_as(ibuf, opts->filepath, imf, save_copy);
IMB_freeImBuf(ibuf);
IMB_freeImBuf(ibuf);
}
}
for (int i = 0; i < 2; i++) {
@@ -1169,28 +1171,36 @@ bool BKE_image_render_write(ReportList *reports,
ibuf_arr[2] = IMB_stereo3d_ImBuf(&image_format, ibuf_arr[0], ibuf_arr[1]);
ok = image_render_write_stamp_test(
reports, scene, rr, ibuf_arr[2], filepath, &image_format, stamp);
/* optional preview images for exr */
if (ok && is_exr_rr && (image_format.flag & R_IMF_FLAG_PREVIEW_JPG)) {
image_format.imtype = R_IMF_IMTYPE_JPEG90;
image_format.depth = R_IMF_CHAN_DEPTH_8;
if (BLI_path_extension_check(filepath, ".exr")) {
filepath[strlen(filepath) - 4] = 0;
}
BKE_image_path_ext_from_imformat_ensure(filepath, sizeof(filepath), &image_format);
ibuf_arr[2]->planes = 24;
if (ibuf_arr[2]) {
ok = image_render_write_stamp_test(
reports, scene, rr, ibuf_arr[2], filepath, &image_format, stamp);
/* optional preview images for exr */
if (ok && is_exr_rr && (image_format.flag & R_IMF_FLAG_PREVIEW_JPG)) {
image_format.imtype = R_IMF_IMTYPE_JPEG90;
image_format.depth = R_IMF_CHAN_DEPTH_8;
if (BLI_path_extension_check(filepath, ".exr")) {
filepath[strlen(filepath) - 4] = 0;
}
BKE_image_path_ext_from_imformat_ensure(filepath, sizeof(filepath), &image_format);
ibuf_arr[2]->planes = 24;
ok = image_render_write_stamp_test(
reports, scene, rr, ibuf_arr[2], filepath, &image_format, stamp);
}
}
else {
BKE_reportf(reports, RPT_ERROR, "Failed to create stereo image buffer");
ok = false;
}
/* imbuf knows which rects are not part of ibuf */
for (i = 0; i < 3; i++) {
IMB_freeImBuf(ibuf_arr[i]);
if (ibuf_arr[i]) {
IMB_freeImBuf(ibuf_arr[i]);
}
}
}
}

View File

@@ -676,18 +676,6 @@ void IMB_stereo3d_write_dimensions(
char mode, bool is_squeezed, size_t width, size_t height, size_t *r_width, size_t *r_height);
void IMB_stereo3d_read_dimensions(
char mode, bool is_squeezed, size_t width, size_t height, size_t *r_width, size_t *r_height);
int *IMB_stereo3d_from_rect(const ImageFormatData *im_format,
size_t x,
size_t y,
size_t channels,
int *rect_left,
int *rect_right);
float *IMB_stereo3d_from_rectf(const ImageFormatData *im_format,
size_t x,
size_t y,
size_t channels,
float *rectf_left,
float *rectf_right);
/**
* Left/right are always float.
*/

View File

@@ -592,73 +592,6 @@ static void imb_stereo3d_unsqueeze_ImBuf(ImBuf *ibuf,
IMB_scale(ibuf, x, y, IMBScaleFilter::Bilinear);
}
static void imb_stereo3d_squeeze_rectf(
float *rectf, const Stereo3dFormat *s3d, const size_t x, const size_t y, const size_t channels)
{
ImBuf *ibuf;
size_t width, height;
if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false) {
return;
}
if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0) {
return;
}
/* creates temporary imbuf to store the rectf */
IMB_stereo3d_write_dimensions(s3d->display_mode, false, x, y, &width, &height);
ibuf = IMB_allocImBuf(width, height, channels, IB_rectfloat);
IMB_buffer_float_from_float(ibuf->float_buffer.data,
rectf,
channels,
IB_PROFILE_LINEAR_RGB,
IB_PROFILE_LINEAR_RGB,
false,
width,
height,
width,
width);
IMB_scale(ibuf, x, y, IMBScaleFilter::Bilinear);
memcpy(rectf, ibuf->float_buffer.data, x * y * sizeof(float[4]));
IMB_freeImBuf(ibuf);
}
static void imb_stereo3d_squeeze_rect(
int *rect, const Stereo3dFormat *s3d, const size_t x, const size_t y, const size_t channels)
{
ImBuf *ibuf;
size_t width, height;
if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false) {
return;
}
if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0) {
return;
}
/* creates temporary imbuf to store the rectf */
IMB_stereo3d_write_dimensions(s3d->display_mode, false, x, y, &width, &height);
ibuf = IMB_allocImBuf(width, height, channels, IB_rect);
IMB_buffer_byte_from_byte(ibuf->byte_buffer.data,
(uchar *)rect,
IB_PROFILE_SRGB,
IB_PROFILE_SRGB,
false,
width,
height,
width,
width);
IMB_scale(ibuf, x, y, IMBScaleFilter::Bilinear);
memcpy(rect, ibuf->byte_buffer.data, x * y * sizeof(uint));
IMB_freeImBuf(ibuf);
}
/** \} */
/* -------------------------------------------------------------------- */
@@ -689,79 +622,17 @@ static void imb_stereo3d_data_init(Stereo3DData *s3d_data,
s3d_data->rectf.stereo = rectf_stereo;
}
int *IMB_stereo3d_from_rect(const ImageFormatData *im_format,
const size_t x,
const size_t y,
const size_t channels,
int *rect_left,
int *rect_right)
{
int *rect_result;
Stereo3DData s3d_data = {{nullptr}};
size_t width, height;
const bool is_float = im_format->depth > 8;
IMB_stereo3d_write_dimensions(
im_format->stereo3d_format.display_mode, false, x, y, &width, &height);
rect_result = static_cast<int *>(MEM_mallocN(channels * sizeof(int) * width * height, __func__));
imb_stereo3d_data_init(&s3d_data,
is_float,
x,
y,
channels,
rect_left,
rect_right,
rect_result,
nullptr,
nullptr,
nullptr);
imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format);
imb_stereo3d_squeeze_rect(rect_result, &im_format->stereo3d_format, x, y, channels);
return rect_result;
}
float *IMB_stereo3d_from_rectf(const ImageFormatData *im_format,
const size_t x,
const size_t y,
const size_t channels,
float *rectf_left,
float *rectf_right)
{
float *rectf_result;
Stereo3DData s3d_data = {{nullptr}};
size_t width, height;
const bool is_float = im_format->depth > 8;
IMB_stereo3d_write_dimensions(
im_format->stereo3d_format.display_mode, false, x, y, &width, &height);
rectf_result = static_cast<float *>(
MEM_mallocN(channels * sizeof(float) * width * height, __func__));
imb_stereo3d_data_init(&s3d_data,
is_float,
x,
y,
channels,
nullptr,
nullptr,
nullptr,
rectf_left,
rectf_right,
rectf_result);
imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format);
imb_stereo3d_squeeze_rectf(rectf_result, &im_format->stereo3d_format, x, y, channels);
return rectf_result;
}
ImBuf *IMB_stereo3d_ImBuf(const ImageFormatData *im_format, ImBuf *ibuf_left, ImBuf *ibuf_right)
{
ImBuf *ibuf_stereo = nullptr;
Stereo3DData s3d_data = {{nullptr}};
size_t width, height;
const bool is_float = im_format->depth > 8;
const bool is_float = ibuf_left->float_buffer.data && ibuf_right->float_buffer.data;
const bool is_byte = ibuf_left->byte_buffer.data && ibuf_right->byte_buffer.data;
if (!(is_float || is_byte)) {
return nullptr;
}
IMB_stereo3d_write_dimensions(
im_format->stereo3d_format.display_mode, false, ibuf_left->x, ibuf_left->y, &width, &height);

View File

@@ -1327,61 +1327,9 @@ static bool rna_RenderSettings_is_movie_format_get(PointerRNA *ptr)
return BKE_imtype_is_movie(rd->im_format.imtype);
}
static int get_first_valid_depth(const int valid_depths)
{
/* set first available depth */
const char depth_ls[] = {
R_IMF_CHAN_DEPTH_32,
R_IMF_CHAN_DEPTH_24,
R_IMF_CHAN_DEPTH_16,
R_IMF_CHAN_DEPTH_12,
R_IMF_CHAN_DEPTH_10,
R_IMF_CHAN_DEPTH_8,
R_IMF_CHAN_DEPTH_1,
0,
};
for (int i = 0; depth_ls[i]; i++) {
if (valid_depths & depth_ls[i]) {
return depth_ls[i];
}
}
return R_IMF_CHAN_DEPTH_8;
}
static void rna_ImageFormatSettings_file_format_set(PointerRNA *ptr, int value)
{
ImageFormatData *imf = (ImageFormatData *)ptr->data;
ID *id = ptr->owner_id;
imf->imtype = value;
const bool is_render = (id && GS(id->name) == ID_SCE);
/* see note below on why this is */
const char chan_flag = BKE_imtype_valid_channels(imf->imtype, true) |
(is_render ? IMA_CHAN_FLAG_BW : 0);
/* ensure depth and color settings match */
if ((imf->planes == R_IMF_PLANES_BW) && !(chan_flag & IMA_CHAN_FLAG_BW)) {
imf->planes = R_IMF_PLANES_RGBA;
}
if ((imf->planes == R_IMF_PLANES_RGBA) && !(chan_flag & IMA_CHAN_FLAG_RGBA)) {
imf->planes = R_IMF_PLANES_RGB;
}
/* ensure usable depth */
{
const int depth_ok = BKE_imtype_valid_depths(imf->imtype);
if ((imf->depth & depth_ok) == 0) {
imf->depth = get_first_valid_depth(depth_ok);
}
}
if (id && GS(id->name) == ID_SCE) {
Scene *scene = (Scene *)ptr->owner_id;
RenderData *rd = &scene->r;
MOV_validate_output_settings(rd, imf);
}
BKE_image_format_update_color_space_for_type(imf);
BKE_image_format_set((ImageFormatData *)ptr->data, ptr->owner_id, value);
}
static const EnumPropertyItem *rna_ImageFormatSettings_file_format_itemf(bContext * /*C*/,
@@ -2984,7 +2932,7 @@ static void rna_FFmpegSettings_codec_update(Main * /*bmain*/, Scene * /*scene*/,
Scene *scene = (Scene *)ptr->owner_id;
const int valid_depths = BKE_imtype_valid_depths_with_video(scene->r.im_format.imtype, id);
if ((scene->r.im_format.depth & valid_depths) == 0) {
scene->r.im_format.depth = get_first_valid_depth(valid_depths);
scene->r.im_format.depth = BKE_imtype_first_valid_depth(valid_depths);
}
}
}

View File

@@ -2208,21 +2208,29 @@ bool RE_WriteRenderViewsMovie(ReportList *reports,
ibuf_arr[2] = IMB_stereo3d_ImBuf(&image_format, ibuf_arr[0], ibuf_arr[1]);
BLI_assert(movie_writers[0] != nullptr);
if (!MOV_write_append(movie_writers[0],
rd,
preview ? scene->r.psfra : scene->r.sfra,
scene->r.cfra,
ibuf_arr[2],
"",
reports))
{
if (ibuf_arr[2]) {
BLI_assert(movie_writers[0] != nullptr);
if (!MOV_write_append(movie_writers[0],
rd,
preview ? scene->r.psfra : scene->r.sfra,
scene->r.cfra,
ibuf_arr[2],
"",
reports))
{
ok = false;
}
}
else {
BKE_report(reports, RPT_ERROR, "Failed to create stereo image buffer");
ok = false;
}
for (i = 0; i < 3; i++) {
/* imbuf knows which rects are not part of ibuf */
IMB_freeImBuf(ibuf_arr[i]);
if (ibuf_arr[i]) {
IMB_freeImBuf(ibuf_arr[i]);
}
}
}

View File

@@ -2007,7 +2007,7 @@ static int arg_handle_image_type_set(int argc, const char **argv, void *data)
"release.\n");
}
else {
scene->r.im_format.imtype = imtype_new;
BKE_image_format_set(&scene->r.im_format, &scene->id, imtype_new);
DEG_id_tag_update(&scene->id, ID_RECALC_SYNC_TO_EVAL);
}
}