Fix #101227: Crash and other issues with non-multiview multipart EXR

Some software stores passes or layers as parts. This case was not
supported by the OpenEXR reader yet.

Pull Request: https://projects.blender.org/blender/blender/pulls/118867
This commit is contained in:
Brecht Van Lommel
2024-02-29 13:50:18 +01:00
committed by Brecht Van Lommel
parent 1e44d811ac
commit a060e96103

View File

@@ -1430,13 +1430,33 @@ static int imb_exr_split_token(const char *str, const char *end, const char **to
return int(end - *token);
}
static void imb_exr_pass_name_from_channel(char *passname,
const ExrChannel *echan,
const char *channelname,
const bool has_xyz_channels)
{
const int passname_maxncpy = EXR_TOT_MAXNAME;
if (echan->chan_id == 'Z' && (!has_xyz_channels || BLI_strcaseeq(channelname, "depth"))) {
BLI_strncpy(passname, "Depth", passname_maxncpy);
}
else if (echan->chan_id == 'Y' && !has_xyz_channels) {
BLI_strncpy(passname, channelname, passname_maxncpy);
}
else if (ELEM(echan->chan_id, 'R', 'G', 'B', 'A', 'V', 'X', 'Y', 'Z')) {
BLI_strncpy(passname, "Combined", passname_maxncpy);
}
else {
BLI_strncpy(passname, channelname, passname_maxncpy);
}
}
static int imb_exr_split_channel_name(ExrChannel *echan,
char *layname,
char *passname,
bool has_xyz_channels)
{
const int layname_maxncpy = EXR_TOT_MAXNAME;
const int passname_maxncpy = EXR_TOT_MAXNAME;
const char *name = echan->m->name.c_str();
const char *end = name + strlen(name);
const char *token;
@@ -1451,20 +1471,7 @@ static int imb_exr_split_channel_name(ExrChannel *echan,
* versions of the listed channels. */
echan->chan_id = BLI_toupper_ascii(name[0]);
layname[0] = '\0';
if (echan->chan_id == 'Z' && !has_xyz_channels) {
BLI_strncpy(passname, "Depth", passname_maxncpy);
}
else if (echan->chan_id == 'Y' && !has_xyz_channels) {
BLI_strncpy(passname, name, passname_maxncpy);
}
else if (ELEM(echan->chan_id, 'R', 'G', 'B', 'A', 'V', 'X', 'Y', 'Z')) {
BLI_strncpy(passname, "Combined", passname_maxncpy);
}
else {
BLI_strncpy(passname, name, passname_maxncpy);
}
imb_exr_pass_name_from_channel(passname, echan, name, has_xyz_channels);
return 1;
}
@@ -1520,14 +1527,20 @@ static int imb_exr_split_channel_name(ExrChannel *echan,
}
end -= len + 1; /* +1 to skip '.' separator */
/* second token is pass name */
len = imb_exr_split_token(name, end, &token);
if (len == 0) {
printf("multilayer read: bad channel name: %s\n", name);
return 0;
if (end > name) {
/* second token is pass name */
len = imb_exr_split_token(name, end, &token);
if (len == 0) {
printf("multilayer read: bad channel name: %s\n", name);
return 0;
}
BLI_strncpy(passname, token, len + 1);
end -= len + 1; /* +1 to skip '.' separator */
}
else {
/* Single token, determine pass name from channel name. */
imb_exr_pass_name_from_channel(passname, echan, channelname, has_xyz_channels);
}
BLI_strncpy(passname, token, len + 1);
end -= len + 1; /* +1 to skip '.' separator */
/* all preceding tokens combined as layer name */
if (end > name) {
@@ -1593,10 +1606,65 @@ static bool exr_has_xyz_channels(ExrHandle *exr_handle)
return x_found && y_found && z_found;
}
static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *data)
/* Replacement for OpenEXR GetChannelsInMultiPartFile, that also handles the
* case where parts are used for passes instead of multiview. */
static std::vector<MultiViewChannelName> exr_channels_in_multi_part_file(
const MultiPartInputFile &file)
{
std::vector<MultiViewChannelName> channels;
GetChannelsInMultiPartFile(*data->ifile, channels);
/* Detect if file has multiview. */
StringVector multiview;
bool has_multiview = false;
if (file.parts() == 1) {
if (hasMultiView(file.header(0))) {
multiview = multiView(file.header(0));
has_multiview = true;
}
}
/* Get channels from each part. */
for (int p = 0; p < file.parts(); p++) {
const ChannelList &c = file.header(p).channels();
std::string part_view = "";
if (file.header(p).hasView()) {
part_view = file.header(p).view();
}
std::string part_name = "";
if (file.header(p).hasName()) {
part_name = file.header(p).name();
}
for (ChannelList::ConstIterator i = c.begin(); i != c.end(); i++) {
MultiViewChannelName m;
m.name = std::string(i.name());
m.internal_name = m.name;
if (has_multiview) {
m.view = viewFromChannelName(m.name, multiview);
m.name = removeViewName(m.internal_name, m.view);
}
else {
m.view = part_view;
}
/* Prepend part name as potential layer or pass name. */
if (!part_name.empty()) {
m.name = part_name + "." + m.name;
}
m.part_number = p;
channels.push_back(m);
}
}
return channels;
}
static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *data)
{
std::vector<MultiViewChannelName> channels = exr_channels_in_multi_part_file(*data->ifile);
imb_exr_get_views(*data->ifile, *data->multiView);