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:
committed by
Brecht Van Lommel
parent
cfd5d9e3ad
commit
7584ccc28d
@@ -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,
|
||||
|
||||
@@ -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")) {
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user