Refactor: OpenEXR: Simplify multi layer read and write implementation
* Read directly into ExrChannel, eliminate intermediate MultiViewChannelName * Pass multiple channels to EXR writer together in IMB_exr_add_channels * Avoid various channel name parsing by passing components separately * Simplify logic for writing multichannel and multiview metadata * Remove unused global EXR handle storage * No longer use void pointer for EXR handle. * Use blender::Vector and std::string. * Slightly reshuffle code so multipart support will have smaller diff. * Add various comments. Pull Request: https://projects.blender.org/blender/blender/pulls/146650
This commit is contained in:
@@ -3945,12 +3945,12 @@ static void image_create_multilayer(Image *ima, ImBuf *ibuf, int framenr)
|
||||
|
||||
/* only load rr once for multiview */
|
||||
if (!ima->rr) {
|
||||
ima->rr = RE_MultilayerConvert(ibuf->userdata, colorspace, predivide, ibuf->x, ibuf->y);
|
||||
ima->rr = RE_MultilayerConvert(ibuf->exrhandle, colorspace, predivide, ibuf->x, ibuf->y);
|
||||
}
|
||||
|
||||
IMB_exr_close(ibuf->userdata);
|
||||
IMB_exr_close(ibuf->exrhandle);
|
||||
|
||||
ibuf->userdata = nullptr;
|
||||
ibuf->exrhandle = nullptr;
|
||||
if (ima->rr != nullptr) {
|
||||
ima->rr->framenr = framenr;
|
||||
BKE_stamp_info_from_imbuf(ima->rr, ibuf);
|
||||
@@ -4217,10 +4217,10 @@ static ImBuf *load_image_single(Image *ima,
|
||||
|
||||
if (ibuf) {
|
||||
#ifdef WITH_IMAGE_OPENEXR
|
||||
if (ibuf->ftype == IMB_FTYPE_OPENEXR && ibuf->userdata) {
|
||||
if (ibuf->ftype == IMB_FTYPE_OPENEXR && ibuf->exrhandle) {
|
||||
/* Handle multilayer and multiview cases, don't assign ibuf here.
|
||||
* will be set layer in BKE_image_acquire_ibuf from ima->rr. */
|
||||
if (IMB_exr_has_multilayer(ibuf->userdata)) {
|
||||
if (IMB_exr_has_multilayer(ibuf->exrhandle)) {
|
||||
image_create_multilayer(ima, ibuf, cfra);
|
||||
ima->type = IMA_TYPE_MULTILAYER;
|
||||
IMB_freeImBuf(ibuf);
|
||||
|
||||
@@ -765,7 +765,7 @@ static float *image_exr_opaque_alpha_buffer(int width,
|
||||
return alpha_output;
|
||||
}
|
||||
|
||||
static void add_exr_compositing_result(void *exr_handle,
|
||||
static void add_exr_compositing_result(ExrHandle *exr_handle,
|
||||
const RenderResult *render_result,
|
||||
const ImageFormatData *imf,
|
||||
bool save_as_render,
|
||||
@@ -816,18 +816,14 @@ static void add_exr_compositing_result(void *exr_handle,
|
||||
/* For multi-layer EXRs, we write the buffer as is with all its 4 channels. */
|
||||
const bool half_float = (imf && imf->depth == R_IMF_CHAN_DEPTH_16);
|
||||
if (is_multi_layer) {
|
||||
for (int i = 0; i < channels_count_in_buffer; i++) {
|
||||
char passname[EXR_PASS_MAXNAME];
|
||||
RE_render_result_full_channel_name(passname, nullptr, "Combined", nullptr, "RGBA", i);
|
||||
IMB_exr_add_channel(exr_handle,
|
||||
"Composite",
|
||||
passname,
|
||||
render_view_name,
|
||||
channels_count_in_buffer,
|
||||
channels_count_in_buffer * render_result->rectx,
|
||||
output_buffer + i,
|
||||
half_float);
|
||||
}
|
||||
IMB_exr_add_channels(exr_handle,
|
||||
"Composite.Combined",
|
||||
"RGBA",
|
||||
render_view_name,
|
||||
channels_count_in_buffer,
|
||||
channels_count_in_buffer * render_result->rectx,
|
||||
output_buffer,
|
||||
half_float);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -842,29 +838,28 @@ static void add_exr_compositing_result(void *exr_handle,
|
||||
render_result->recty,
|
||||
channels_count_in_buffer,
|
||||
temporary_buffers);
|
||||
IMB_exr_add_channel(exr_handle,
|
||||
"",
|
||||
"V",
|
||||
render_view_name,
|
||||
1,
|
||||
render_result->rectx,
|
||||
gray_scale_output,
|
||||
half_float);
|
||||
IMB_exr_add_channels(exr_handle,
|
||||
"",
|
||||
"V",
|
||||
render_view_name,
|
||||
1,
|
||||
render_result->rectx,
|
||||
gray_scale_output,
|
||||
half_float);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Add RGB[A] channels. This will essentially skip the alpha channel if only three channels
|
||||
* were required. */
|
||||
for (int i = 0; i < required_channels; i++) {
|
||||
IMB_exr_add_channel(exr_handle,
|
||||
"",
|
||||
std::string(1, "RGBA"[i]).c_str(),
|
||||
render_view_name,
|
||||
channels_count_in_buffer,
|
||||
channels_count_in_buffer * render_result->rectx,
|
||||
output_buffer + i,
|
||||
half_float);
|
||||
}
|
||||
std::string channelnames = blender::StringRef("RGBA").substr(0, required_channels);
|
||||
IMB_exr_add_channels(exr_handle,
|
||||
"",
|
||||
channelnames,
|
||||
render_view_name,
|
||||
channels_count_in_buffer,
|
||||
channels_count_in_buffer * render_result->rectx,
|
||||
output_buffer,
|
||||
half_float);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -876,7 +871,7 @@ bool BKE_image_render_write_exr(ReportList *reports,
|
||||
const char *view,
|
||||
int layer)
|
||||
{
|
||||
void *exrhandle = IMB_exr_get_handle();
|
||||
ExrHandle *exrhandle = IMB_exr_get_handle();
|
||||
const bool multi_layer = !(imf && imf->imtype == R_IMF_IMTYPE_OPENEXR);
|
||||
|
||||
/* Write first layer if not multilayer and no layer was specified. */
|
||||
@@ -884,7 +879,7 @@ bool BKE_image_render_write_exr(ReportList *reports,
|
||||
layer = 0;
|
||||
}
|
||||
|
||||
/* First add views since IMB_exr_add_channel checks number of views. */
|
||||
/* First add views since IMB_exr_add_channels checks number of views. */
|
||||
const RenderView *first_rview = (const RenderView *)rr->views.first;
|
||||
if (first_rview && (first_rview->next || first_rview->name[0])) {
|
||||
LISTBASE_FOREACH (RenderView *, rview, &rr->views) {
|
||||
@@ -943,32 +938,22 @@ bool BKE_image_render_write_exr(ReportList *reports,
|
||||
|
||||
/* For multi-layer EXRs, we write the pass as is with all of its channels. */
|
||||
if (multi_layer) {
|
||||
for (int i = 0; i < render_pass->channels; i++) {
|
||||
char passname[EXR_PASS_MAXNAME];
|
||||
char layname[EXR_PASS_MAXNAME];
|
||||
std::string layer_pass_name = render_pass->name;
|
||||
|
||||
/* A single unnamed layer indicates that the pass name should be used as the layer name,
|
||||
* while the pass name should be the channel ID. */
|
||||
if (!has_multiple_layers && rl->name[0] == '\0') {
|
||||
passname[0] = render_pass->chan_id[i];
|
||||
passname[1] = '\0';
|
||||
STRNCPY(layname, render_pass->name);
|
||||
}
|
||||
else {
|
||||
RE_render_result_full_channel_name(
|
||||
passname, nullptr, render_pass->name, nullptr, render_pass->chan_id, i);
|
||||
STRNCPY(layname, rl->name);
|
||||
}
|
||||
|
||||
IMB_exr_add_channel(exrhandle,
|
||||
layname,
|
||||
passname,
|
||||
viewname,
|
||||
render_pass->channels,
|
||||
render_pass->channels * rr->rectx,
|
||||
output_rect + i,
|
||||
pass_half_float);
|
||||
/* Unless we have a single unnamed layer, include the layer name. */
|
||||
if (has_multiple_layers || rl->name[0] != '\0') {
|
||||
layer_pass_name = rl->name + ("." + layer_pass_name);
|
||||
}
|
||||
|
||||
std::string channelnames = blender::StringRef(render_pass->chan_id, render_pass->channels);
|
||||
IMB_exr_add_channels(exrhandle,
|
||||
layer_pass_name,
|
||||
channelnames,
|
||||
viewname,
|
||||
render_pass->channels,
|
||||
render_pass->channels * rr->rectx,
|
||||
output_rect,
|
||||
pass_half_float);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -982,47 +967,47 @@ bool BKE_image_render_write_exr(ReportList *reports,
|
||||
if (required_channels == render_pass->channels ||
|
||||
(required_channels != 1 && render_pass->channels != 1))
|
||||
{
|
||||
for (int i = 0; i < std::min(required_channels, render_pass->channels); i++) {
|
||||
IMB_exr_add_channel(exrhandle,
|
||||
"",
|
||||
std::string(1, render_pass->chan_id[i]).c_str(),
|
||||
viewname,
|
||||
render_pass->channels,
|
||||
render_pass->channels * rr->rectx,
|
||||
output_rect + i,
|
||||
pass_half_float);
|
||||
}
|
||||
std::string channelnames = blender::StringRef(
|
||||
render_pass->chan_id, std::min(required_channels, render_pass->channels));
|
||||
IMB_exr_add_channels(exrhandle,
|
||||
"",
|
||||
channelnames,
|
||||
viewname,
|
||||
render_pass->channels,
|
||||
render_pass->channels * rr->rectx,
|
||||
output_rect,
|
||||
pass_half_float);
|
||||
}
|
||||
else if (required_channels == 1) {
|
||||
/* In case of a single required channel, we need to do RGB[A] to BW conversion. We know the
|
||||
* input is RGB[A] and not single channel because it filed the condition above. */
|
||||
/* In case of a single required channel, we need to do RGB[A] to BW conversion. We know
|
||||
* the input is RGB[A] and not single channel because it filed the condition above. */
|
||||
float *gray_scale_output = image_exr_from_rgb_to_bw(
|
||||
output_rect, rr->rectx, rr->recty, render_pass->channels, tmp_output_rects);
|
||||
IMB_exr_add_channel(
|
||||
IMB_exr_add_channels(
|
||||
exrhandle, "", "V", viewname, 1, rr->rectx, gray_scale_output, pass_half_float);
|
||||
}
|
||||
else if (render_pass->channels == 1) {
|
||||
/* In case of a single channel pass, we need to broadcast the same channel for each of the
|
||||
* RGB channels that are required. We know the RGB is required because single channel
|
||||
/* In case of a single channel pass, we need to broadcast the same channel for each of
|
||||
* the RGB channels that are required. We know the RGB is required because single channel
|
||||
* requirement was handled above. The alpha channel will be added later. */
|
||||
for (int i = 0; i < 3; i++) {
|
||||
IMB_exr_add_channel(exrhandle,
|
||||
"",
|
||||
std::string(1, "RGB"[i]).c_str(),
|
||||
viewname,
|
||||
1,
|
||||
rr->rectx,
|
||||
output_rect,
|
||||
pass_half_float);
|
||||
IMB_exr_add_channels(exrhandle,
|
||||
"",
|
||||
std::string(1, "RGB"[i]).c_str(),
|
||||
viewname,
|
||||
1,
|
||||
rr->rectx,
|
||||
output_rect,
|
||||
pass_half_float);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add an opaque alpha channel if the pass contains no alpha channel but an alpha channel is
|
||||
* required. */
|
||||
/* Add an opaque alpha channel if the pass contains no alpha channel but an alpha channel
|
||||
* is required. */
|
||||
if (required_channels == 4 && render_pass->channels < 4) {
|
||||
float *alpha_output = image_exr_opaque_alpha_buffer(
|
||||
rr->rectx, rr->recty, tmp_output_rects);
|
||||
IMB_exr_add_channel(
|
||||
IMB_exr_add_channels(
|
||||
exrhandle, "", "A", viewname, 1, rr->rectx, alpha_output, pass_half_float);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -524,13 +524,13 @@ void BKE_movieclip_convert_multilayer_ibuf(ImBuf *ibuf)
|
||||
return;
|
||||
}
|
||||
#ifdef WITH_IMAGE_OPENEXR
|
||||
if (ibuf->ftype != IMB_FTYPE_OPENEXR || ibuf->userdata == nullptr) {
|
||||
if (ibuf->ftype != IMB_FTYPE_OPENEXR || ibuf->exrhandle == nullptr) {
|
||||
return;
|
||||
}
|
||||
MultilayerConvertContext ctx;
|
||||
ctx.combined_pass = nullptr;
|
||||
ctx.num_combined_channels = 0;
|
||||
IMB_exr_multilayer_convert(ibuf->userdata,
|
||||
IMB_exr_multilayer_convert(ibuf->exrhandle,
|
||||
&ctx,
|
||||
movieclip_convert_multilayer_add_view,
|
||||
movieclip_convert_multilayer_add_layer,
|
||||
@@ -540,8 +540,8 @@ void BKE_movieclip_convert_multilayer_ibuf(ImBuf *ibuf)
|
||||
IMB_assign_float_buffer(ibuf, ctx.combined_pass, IB_TAKE_OWNERSHIP);
|
||||
ibuf->channels = ctx.num_combined_channels;
|
||||
}
|
||||
IMB_exr_close(ibuf->userdata);
|
||||
ibuf->userdata = nullptr;
|
||||
IMB_exr_close(ibuf->exrhandle);
|
||||
ibuf->exrhandle = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -356,13 +356,13 @@ static void studiolight_load_equirect_image(StudioLight *sl)
|
||||
const bool failed = (ibuf == nullptr);
|
||||
|
||||
if (ibuf) {
|
||||
if (ibuf->ftype == IMB_FTYPE_OPENEXR && ibuf->userdata) {
|
||||
/* the read file is a multilayered openexr file (userdata != nullptr)
|
||||
if (ibuf->ftype == IMB_FTYPE_OPENEXR && ibuf->exrhandle) {
|
||||
/* the read file is a multilayered openexr file (exrhandle != nullptr)
|
||||
* This file is currently only supported for MATCAPS where
|
||||
* the first found 'diffuse' pass will be used for diffuse lighting
|
||||
* and the first found 'specular' pass will be used for specular lighting */
|
||||
MultilayerConvertContext ctx = {0};
|
||||
IMB_exr_multilayer_convert(ibuf->userdata,
|
||||
IMB_exr_multilayer_convert(ibuf->exrhandle,
|
||||
&ctx,
|
||||
&studiolight_multilayer_addview,
|
||||
&studiolight_multilayer_addlayer,
|
||||
@@ -388,8 +388,8 @@ static void studiolight_load_equirect_image(StudioLight *sl)
|
||||
nullptr, converted_pass, ibuf->x, ibuf->y, ctx.num_specular_channels);
|
||||
}
|
||||
|
||||
IMB_exr_close(ibuf->userdata);
|
||||
ibuf->userdata = nullptr;
|
||||
IMB_exr_close(ibuf->exrhandle);
|
||||
ibuf->exrhandle = nullptr;
|
||||
IMB_freeImBuf(ibuf);
|
||||
ibuf = nullptr;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "IMB_imbuf_enums.h"
|
||||
|
||||
struct ColormanageCache;
|
||||
struct ExrHandle;
|
||||
namespace blender::gpu {
|
||||
class Texture;
|
||||
}
|
||||
@@ -228,8 +229,8 @@ struct ImBuf {
|
||||
int userflags;
|
||||
/** image metadata */
|
||||
IDProperty *metadata;
|
||||
/** temporary storage */
|
||||
void *userdata;
|
||||
/** OpenEXR handle. */
|
||||
ExrHandle *exrhandle;
|
||||
|
||||
/* file information */
|
||||
/** file type we are going to save as */
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
/* API for reading and writing multi-layer EXR files. */
|
||||
|
||||
/* XXX layer+pass name max 64? */
|
||||
@@ -19,33 +21,34 @@
|
||||
#define EXR_PASS_MAXCHAN 24
|
||||
|
||||
struct StampData;
|
||||
struct ExrHandle;
|
||||
|
||||
void *IMB_exr_get_handle();
|
||||
void *IMB_exr_get_handle_name(const char *name);
|
||||
ExrHandle *IMB_exr_get_handle();
|
||||
|
||||
/**
|
||||
* Adds flattened #ExrChannel's
|
||||
* `xstride`, `ystride` and `rect` can be done in set_channel too, for tile writing.
|
||||
* \param passname: Does not include view.
|
||||
* Add multiple channels to EXR file.
|
||||
* The number of channels is determined by channelnames.size() without
|
||||
* each character a channel name.
|
||||
* Layer and pass name, and view name are optional.
|
||||
*/
|
||||
void IMB_exr_add_channel(void *handle,
|
||||
const char *layname,
|
||||
const char *passname,
|
||||
const char *viewname,
|
||||
int xstride,
|
||||
int ystride,
|
||||
float *rect,
|
||||
bool use_half_float);
|
||||
void IMB_exr_add_channels(ExrHandle *handle,
|
||||
blender::StringRefNull layerpassname,
|
||||
blender::StringRefNull channelnames,
|
||||
blender::StringRefNull viewname,
|
||||
size_t xstride,
|
||||
size_t ystride,
|
||||
float *rect,
|
||||
bool use_half_float);
|
||||
|
||||
/**
|
||||
* Read from file.
|
||||
*/
|
||||
bool IMB_exr_begin_read(
|
||||
void *handle, const char *filepath, int *width, int *height, bool parse_channels);
|
||||
ExrHandle *handle, const char *filepath, int *width, int *height, bool parse_channels);
|
||||
/**
|
||||
* Used for output files (from #RenderResult) (single and multi-layer, single and multi-view).
|
||||
*/
|
||||
bool IMB_exr_begin_write(void *handle,
|
||||
bool IMB_exr_begin_write(ExrHandle *handle,
|
||||
const char *filepath,
|
||||
int width,
|
||||
int height,
|
||||
@@ -55,21 +58,16 @@ bool IMB_exr_begin_write(void *handle,
|
||||
const StampData *stamp);
|
||||
|
||||
/**
|
||||
* Still clumsy name handling, layers/channels can be ordered as list in list later.
|
||||
*
|
||||
* \param passname: Here is the raw channel name without the layer.
|
||||
* For reading, set the rect buffer to write into.
|
||||
* \param passname: Full channel name including layer, pass, view and channel.
|
||||
*/
|
||||
bool IMB_exr_set_channel(void *handle,
|
||||
const char *layname,
|
||||
const char *passname,
|
||||
int xstride,
|
||||
int ystride,
|
||||
float *rect);
|
||||
bool IMB_exr_set_channel(
|
||||
ExrHandle *handle, blender::StringRefNull full_name, int xstride, int ystride, float *rect);
|
||||
|
||||
void IMB_exr_read_channels(void *handle);
|
||||
void IMB_exr_write_channels(void *handle);
|
||||
void IMB_exr_read_channels(ExrHandle *handle);
|
||||
void IMB_exr_write_channels(ExrHandle *handle);
|
||||
|
||||
void IMB_exr_multilayer_convert(void *handle,
|
||||
void IMB_exr_multilayer_convert(ExrHandle *handle,
|
||||
void *base,
|
||||
void *(*addview)(void *base, const char *str),
|
||||
void *(*addlayer)(void *base, const char *str),
|
||||
@@ -81,10 +79,10 @@ void IMB_exr_multilayer_convert(void *handle,
|
||||
const char *chan_id,
|
||||
const char *view));
|
||||
|
||||
void IMB_exr_close(void *handle);
|
||||
void IMB_exr_close(ExrHandle *handle);
|
||||
|
||||
void IMB_exr_add_view(void *handle, const char *name);
|
||||
void IMB_exr_add_view(ExrHandle *handle, const char *name);
|
||||
|
||||
bool IMB_exr_has_multilayer(void *handle);
|
||||
bool IMB_exr_has_multilayer(ExrHandle *handle);
|
||||
|
||||
bool IMB_exr_get_ppm(void *handle, double ppm[2]);
|
||||
bool IMB_exr_get_ppm(ExrHandle *handle, double ppm[2]);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include "IMB_filetype.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
@@ -83,7 +84,6 @@
|
||||
#include "BLI_math_base.hh"
|
||||
#include "BLI_math_color.h"
|
||||
#include "BLI_mmap.h"
|
||||
#include "BLI_path_utils.hh"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_string_utf8.h"
|
||||
@@ -108,15 +108,9 @@ using namespace Imf;
|
||||
using namespace Imath;
|
||||
|
||||
/* prototype */
|
||||
static struct ExrPass *imb_exr_get_pass(ListBase *lb, const char *passname);
|
||||
static bool exr_has_multiview(MultiPartInputFile &file);
|
||||
static bool exr_has_multipart_file(MultiPartInputFile &file);
|
||||
static bool exr_has_alpha(MultiPartInputFile &file);
|
||||
static void imb_exr_type_by_channels(ChannelList &channels,
|
||||
StringVector &views,
|
||||
bool *r_singlelayer,
|
||||
bool *r_multilayer,
|
||||
bool *r_multiview);
|
||||
|
||||
/* XYZ with Illuminant E */
|
||||
static Imf::Chromaticities CHROMATICITIES_XYZ_E{
|
||||
@@ -502,14 +496,16 @@ static int openexr_header_get_compression(const Header &header)
|
||||
return R_IMF_EXR_CODEC_NONE;
|
||||
}
|
||||
|
||||
static void openexr_header_metadata(Header *header, ImBuf *ibuf)
|
||||
static void openexr_header_metadata_global(Header *header,
|
||||
IDProperty *metadata,
|
||||
const double ppm[2])
|
||||
{
|
||||
header->insert(
|
||||
"Software",
|
||||
TypedAttribute<std::string>(std::string("Blender ") + BKE_blender_version_string()));
|
||||
|
||||
if (ibuf->metadata) {
|
||||
LISTBASE_FOREACH (IDProperty *, prop, &ibuf->metadata->data.group) {
|
||||
if (metadata) {
|
||||
LISTBASE_FOREACH (IDProperty *, prop, &metadata->data.group) {
|
||||
/* Do not blindly pass along compression or colorInteropID, as they might have
|
||||
* changed and will already be written when appropriate. */
|
||||
if ((prop->type == IDP_STRING) && !STR_ELEM(prop->name, "compression", "colorInteropID")) {
|
||||
@@ -518,12 +514,15 @@ static void openexr_header_metadata(Header *header, ImBuf *ibuf)
|
||||
}
|
||||
}
|
||||
|
||||
if (ibuf->ppm[0] > 0.0 && ibuf->ppm[1] > 0.0) {
|
||||
if (ppm[0] > 0.0 && ppm[1] > 0.0) {
|
||||
/* Convert meters to inches. */
|
||||
addXDensity(*header, ibuf->ppm[0] * 0.0254);
|
||||
header->pixelAspectRatio() = blender::math::safe_divide(ibuf->ppm[1], ibuf->ppm[0]);
|
||||
addXDensity(*header, ppm[0] * 0.0254);
|
||||
header->pixelAspectRatio() = blender::math::safe_divide(ppm[1], ppm[0]);
|
||||
}
|
||||
}
|
||||
|
||||
static void openexr_header_metadata_colorspace(Header *header, ImBuf *ibuf)
|
||||
{
|
||||
/* Get colorspace from image buffer. */
|
||||
const ColorSpace *colorspace = nullptr;
|
||||
if (ibuf->float_buffer.data) {
|
||||
@@ -578,7 +577,8 @@ static bool imb_save_openexr_half(ImBuf *ibuf, const char *filepath, const int f
|
||||
|
||||
openexr_header_compression(
|
||||
&header, ibuf->foptions.flag & OPENEXR_CODEC_MASK, ibuf->foptions.quality);
|
||||
openexr_header_metadata(&header, ibuf);
|
||||
openexr_header_metadata_global(&header, ibuf->metadata, ibuf->ppm);
|
||||
openexr_header_metadata_colorspace(&header, ibuf);
|
||||
|
||||
/* create channels */
|
||||
header.channels().insert("R", Channel(HALF));
|
||||
@@ -680,7 +680,8 @@ static bool imb_save_openexr_float(ImBuf *ibuf, const char *filepath, const int
|
||||
|
||||
openexr_header_compression(
|
||||
&header, ibuf->foptions.flag & OPENEXR_CODEC_MASK, ibuf->foptions.quality);
|
||||
openexr_header_metadata(&header, ibuf);
|
||||
openexr_header_metadata_global(&header, ibuf->metadata, ibuf->ppm);
|
||||
openexr_header_metadata_colorspace(&header, ibuf);
|
||||
|
||||
/* create channels */
|
||||
header.channels().insert("R", Channel(Imf::FLOAT));
|
||||
@@ -766,99 +767,85 @@ bool imb_save_openexr(ImBuf *ibuf, const char *filepath, int flags)
|
||||
* - separated with a dot: the Layer name (like "Light1" or "Walls" or "Characters")
|
||||
*/
|
||||
|
||||
static ListBase exrhandles = {nullptr, nullptr};
|
||||
|
||||
struct ExrHandle {
|
||||
ExrHandle *next, *prev;
|
||||
char name[FILE_MAX];
|
||||
|
||||
IStream *ifile_stream;
|
||||
MultiPartInputFile *ifile;
|
||||
|
||||
OFileStream *ofile_stream;
|
||||
MultiPartOutputFile *mpofile;
|
||||
OutputFile *ofile;
|
||||
|
||||
int tilex, tiley;
|
||||
int width, height;
|
||||
int mipmap;
|
||||
|
||||
/** It needs to be a pointer due to Windows release builds of EXR2.0
|
||||
* segfault when opening EXR bug. */
|
||||
StringVector *multiView;
|
||||
|
||||
int parts;
|
||||
|
||||
ListBase channels; /* flattened out, ExrChannel */
|
||||
ListBase layers; /* hierarchical, pointing in end to ExrChannel */
|
||||
|
||||
/** Used during file save, allows faster temporary buffers allocation. */
|
||||
int num_half_channels;
|
||||
};
|
||||
|
||||
/* flattened out channel */
|
||||
struct ExrChannel {
|
||||
ExrChannel *next, *prev;
|
||||
/* Number of the part. */
|
||||
int part_number;
|
||||
|
||||
char name[EXR_TOT_MAXNAME + 1]; /* full name with everything */
|
||||
MultiViewChannelName *m; /* struct to store all multipart channel info */
|
||||
int xstride, ystride; /* step to next pixel, to next scan-line. */
|
||||
float *rect; /* first pointer to write in */
|
||||
char chan_id; /* quick lookup of channel char */
|
||||
int view_id; /* quick lookup of channel view */
|
||||
bool use_half_float; /* when saving use half float for file storage */
|
||||
/* Full name of the chanel. */
|
||||
std::string name;
|
||||
/* Name as stored in the header. */
|
||||
std::string internal_name;
|
||||
/* Channel view. */
|
||||
std::string view;
|
||||
|
||||
int xstride = 0, ystride = 0; /* step to next pixel, to next scan-line. */
|
||||
float *rect = nullptr; /* first pointer to write in */
|
||||
char chan_id = 0; /* quick lookup of channel char */
|
||||
bool use_half_float = false; /* when saving use half float for file storage */
|
||||
};
|
||||
|
||||
/* hierarchical; layers -> passes -> channels[] */
|
||||
struct ExrPass {
|
||||
ExrPass *next, *prev;
|
||||
char name[EXR_PASS_MAXNAME];
|
||||
int totchan;
|
||||
float *rect;
|
||||
ExrChannel *chan[EXR_PASS_MAXCHAN];
|
||||
char chan_id[EXR_PASS_MAXCHAN];
|
||||
~ExrPass()
|
||||
{
|
||||
if (rect) {
|
||||
MEM_freeN(rect);
|
||||
}
|
||||
}
|
||||
|
||||
char internal_name[EXR_PASS_MAXNAME]; /* name with no view */
|
||||
char view[EXR_VIEW_MAXNAME];
|
||||
int view_id;
|
||||
std::string name;
|
||||
int totchan = 0;
|
||||
float *rect = nullptr;
|
||||
ExrChannel *chan[EXR_PASS_MAXCHAN] = {};
|
||||
char chan_id[EXR_PASS_MAXCHAN] = {};
|
||||
|
||||
std::string internal_name; /* Name with no view. */
|
||||
std::string view;
|
||||
};
|
||||
|
||||
struct ExrLayer {
|
||||
ExrLayer *next, *prev;
|
||||
char name[EXR_LAY_MAXNAME + 1];
|
||||
ListBase passes;
|
||||
std::string name;
|
||||
blender::Vector<ExrPass> passes;
|
||||
};
|
||||
|
||||
static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *data);
|
||||
struct ExrHandle {
|
||||
std::string name;
|
||||
|
||||
IStream *ifile_stream = nullptr;
|
||||
MultiPartInputFile *ifile = nullptr;
|
||||
|
||||
OFileStream *ofile_stream = nullptr;
|
||||
MultiPartOutputFile *ofile = nullptr;
|
||||
|
||||
bool write_multichannel = false;
|
||||
|
||||
int tilex = 0, tiley = 0;
|
||||
int width = 0, height = 0;
|
||||
int mipmap = 0;
|
||||
|
||||
StringVector views;
|
||||
|
||||
blender::Vector<ExrChannel> channels; /* flattened out channels. */
|
||||
blender::Vector<ExrLayer> layers; /* layers and passes. */
|
||||
};
|
||||
|
||||
static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *handle);
|
||||
static blender::Vector<ExrChannel> exr_channels_in_multi_part_file(const MultiPartInputFile &file,
|
||||
const bool parse_layers);
|
||||
|
||||
/* ********************** */
|
||||
|
||||
void *IMB_exr_get_handle()
|
||||
ExrHandle *IMB_exr_get_handle()
|
||||
{
|
||||
ExrHandle *data = MEM_callocN<ExrHandle>("exr handle");
|
||||
data->multiView = new StringVector();
|
||||
|
||||
BLI_addtail(&exrhandles, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
void *IMB_exr_get_handle_name(const char *name)
|
||||
{
|
||||
ExrHandle *data = (ExrHandle *)BLI_rfindstring(&exrhandles, name, offsetof(ExrHandle, name));
|
||||
|
||||
if (data == nullptr) {
|
||||
data = (ExrHandle *)IMB_exr_get_handle();
|
||||
STRNCPY(data->name, name);
|
||||
}
|
||||
return data;
|
||||
return MEM_new<ExrHandle>("ExrHandle");
|
||||
}
|
||||
|
||||
/* multiview functions */
|
||||
|
||||
void IMB_exr_add_view(void *handle, const char *name)
|
||||
void IMB_exr_add_view(ExrHandle *handle, const char *name)
|
||||
{
|
||||
ExrHandle *data = (ExrHandle *)handle;
|
||||
data->multiView->emplace_back(name);
|
||||
handle->views.emplace_back(name);
|
||||
}
|
||||
|
||||
static int imb_exr_get_multiView_id(StringVector &views, const std::string &name)
|
||||
@@ -876,117 +863,98 @@ static int imb_exr_get_multiView_id(StringVector &views, const std::string &name
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void imb_exr_get_views(MultiPartInputFile &file, StringVector &views)
|
||||
static StringVector imb_exr_get_views(MultiPartInputFile &file)
|
||||
{
|
||||
if (exr_has_multipart_file(file) == false) {
|
||||
if (exr_has_multiview(file)) {
|
||||
StringVector sv = multiView(file.header(0));
|
||||
for (const std::string &view_name : sv) {
|
||||
views.push_back(view_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
for (int p = 0; p < file.parts(); p++) {
|
||||
std::string view;
|
||||
if (file.header(p).hasView()) {
|
||||
view = file.header(p).view();
|
||||
}
|
||||
StringVector views;
|
||||
|
||||
for (int p = 0; p < file.parts(); p++) {
|
||||
/* Views stored in separate parts. */
|
||||
if (file.header(p).hasView()) {
|
||||
const std::string &view = file.header(p).view();
|
||||
if (imb_exr_get_multiView_id(views, view) == -1) {
|
||||
views.push_back(view);
|
||||
}
|
||||
}
|
||||
|
||||
/* Part containing multiple views. */
|
||||
if (hasMultiView(file.header(p))) {
|
||||
StringVector multiview = multiView(file.header(p));
|
||||
for (const std::string &view : multiview) {
|
||||
if (imb_exr_get_multiView_id(views, view) == -1) {
|
||||
views.push_back(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return views;
|
||||
}
|
||||
|
||||
/* Multi-layer Blender files have the view name in all the passes (even the default view one). */
|
||||
static void imb_exr_insert_view_name(char name_full[EXR_TOT_MAXNAME + 1],
|
||||
const char *passname,
|
||||
const char *viewname)
|
||||
void IMB_exr_add_channels(ExrHandle *handle,
|
||||
blender::StringRefNull layerpassname,
|
||||
blender::StringRefNull channelnames,
|
||||
blender::StringRefNull viewname,
|
||||
size_t xstride,
|
||||
size_t ystride,
|
||||
float *rect,
|
||||
bool use_half_float)
|
||||
{
|
||||
/* Match: `sizeof(ExrChannel::name)`. */
|
||||
const size_t name_full_maxncpy = EXR_TOT_MAXNAME + 1;
|
||||
BLI_assert(!ELEM(name_full, passname, viewname));
|
||||
handle->channels.append_as();
|
||||
ExrChannel &echan = handle->channels.last();
|
||||
|
||||
if (viewname == nullptr || viewname[0] == '\0') {
|
||||
BLI_strncpy(name_full, passname, name_full_maxncpy);
|
||||
return;
|
||||
/* If there are layer and pass names, we will write Blender multichannel metadata. */
|
||||
if (!layerpassname.is_empty()) {
|
||||
handle->write_multichannel = true;
|
||||
}
|
||||
|
||||
const char delims[] = {'.', '\0'};
|
||||
const char *sep;
|
||||
const char *token;
|
||||
size_t len;
|
||||
for (size_t channel = 0; channel < channelnames.size(); channel++) {
|
||||
/* Full channel name including view (when not using multipart) and channel. */
|
||||
std::string full_name = layerpassname;
|
||||
if (!viewname.is_empty()) {
|
||||
if (full_name.empty()) {
|
||||
full_name = viewname;
|
||||
}
|
||||
else {
|
||||
full_name = full_name + "." + viewname;
|
||||
}
|
||||
}
|
||||
if (full_name.empty()) {
|
||||
full_name = channelnames[channel];
|
||||
}
|
||||
else {
|
||||
full_name = full_name + "." + channelnames[channel];
|
||||
}
|
||||
|
||||
len = BLI_str_rpartition(passname, delims, &sep, &token);
|
||||
echan.name = full_name;
|
||||
echan.internal_name = full_name;
|
||||
echan.view = viewname;
|
||||
|
||||
if (sep) {
|
||||
BLI_snprintf(name_full, name_full_maxncpy, "%.*s.%s.%s", int(len), passname, viewname, token);
|
||||
}
|
||||
else {
|
||||
BLI_snprintf(name_full, name_full_maxncpy, "%s.%s", passname, viewname);
|
||||
echan.xstride = xstride;
|
||||
echan.ystride = ystride;
|
||||
echan.rect = rect + channel;
|
||||
echan.use_half_float = use_half_float;
|
||||
}
|
||||
|
||||
CLOG_DEBUG(&LOG, "Added pass %s %s", layerpassname.c_str(), channelnames.c_str());
|
||||
}
|
||||
|
||||
void IMB_exr_add_channel(void *handle,
|
||||
const char *layname,
|
||||
const char *passname,
|
||||
const char *viewname,
|
||||
int xstride,
|
||||
int ystride,
|
||||
float *rect,
|
||||
bool use_half_float)
|
||||
static void openexr_header_metadata_multi(ExrHandle *handle,
|
||||
Header &header,
|
||||
const double ppm[2],
|
||||
const StampData *stamp)
|
||||
{
|
||||
ExrHandle *data = (ExrHandle *)handle;
|
||||
ExrChannel *echan;
|
||||
|
||||
echan = MEM_callocN<ExrChannel>("exr channel");
|
||||
echan->m = new MultiViewChannelName();
|
||||
|
||||
if (layname && layname[0] != '\0') {
|
||||
echan->m->name = layname;
|
||||
echan->m->name.append(".");
|
||||
echan->m->name.append(passname);
|
||||
openexr_header_metadata_global(&header, nullptr, ppm);
|
||||
if (handle->write_multichannel) {
|
||||
header.insert("BlenderMultiChannel", StringAttribute("Blender V2.55.1 and newer"));
|
||||
}
|
||||
else {
|
||||
echan->m->name.assign(passname);
|
||||
if (!handle->views.empty() && !handle->views[0].empty()) {
|
||||
addMultiView(header, handle->views);
|
||||
}
|
||||
|
||||
echan->m->internal_name = echan->m->name;
|
||||
|
||||
echan->m->view.assign(viewname ? viewname : "");
|
||||
|
||||
/* quick look up */
|
||||
echan->view_id = std::max(0, imb_exr_get_multiView_id(*data->multiView, echan->m->view));
|
||||
|
||||
/* name has to be unique, thus it's a combination of layer, pass, view, and channel */
|
||||
if (layname && layname[0] != '\0') {
|
||||
imb_exr_insert_view_name(echan->name, echan->m->name.c_str(), echan->m->view.c_str());
|
||||
}
|
||||
else if (!data->multiView->empty()) {
|
||||
std::string raw_name = insertViewName(echan->m->name, *data->multiView, echan->view_id);
|
||||
STRNCPY(echan->name, raw_name.c_str());
|
||||
}
|
||||
else {
|
||||
STRNCPY(echan->name, echan->m->name.c_str());
|
||||
}
|
||||
|
||||
echan->xstride = xstride;
|
||||
echan->ystride = ystride;
|
||||
echan->rect = rect;
|
||||
echan->use_half_float = use_half_float;
|
||||
|
||||
if (echan->use_half_float) {
|
||||
data->num_half_channels++;
|
||||
}
|
||||
|
||||
CLOG_DEBUG(&LOG, "Added channel %s", echan->name);
|
||||
BLI_addtail(&data->channels, echan);
|
||||
BKE_stamp_info_callback(
|
||||
&header, const_cast<StampData *>(stamp), openexr_header_metadata_callback, false);
|
||||
}
|
||||
|
||||
bool IMB_exr_begin_write(void *handle,
|
||||
bool IMB_exr_begin_write(ExrHandle *handle,
|
||||
const char *filepath,
|
||||
int width,
|
||||
int height,
|
||||
@@ -995,73 +963,49 @@ bool IMB_exr_begin_write(void *handle,
|
||||
int quality,
|
||||
const StampData *stamp)
|
||||
{
|
||||
ExrHandle *data = (ExrHandle *)handle;
|
||||
Header header(width, height);
|
||||
|
||||
data->width = width;
|
||||
data->height = height;
|
||||
|
||||
bool is_singlelayer, is_multilayer, is_multiview;
|
||||
|
||||
LISTBASE_FOREACH (ExrChannel *, echan, &data->channels) {
|
||||
header.channels().insert(echan->name, Channel(echan->use_half_float ? Imf::HALF : Imf::FLOAT));
|
||||
}
|
||||
handle->width = width;
|
||||
handle->height = height;
|
||||
|
||||
openexr_header_compression(&header, compress, quality);
|
||||
BKE_stamp_info_callback(
|
||||
&header, const_cast<StampData *>(stamp), openexr_header_metadata_callback, false);
|
||||
/* header.lineOrder() = DECREASING_Y; this crashes in windows for file read! */
|
||||
openexr_header_metadata_multi(handle, header, ppm, stamp);
|
||||
|
||||
imb_exr_type_by_channels(
|
||||
header.channels(), *data->multiView, &is_singlelayer, &is_multilayer, &is_multiview);
|
||||
|
||||
if (is_multilayer) {
|
||||
header.insert("BlenderMultiChannel", StringAttribute("Blender V2.55.1 and newer"));
|
||||
}
|
||||
|
||||
if (is_multiview) {
|
||||
addMultiView(header, *data->multiView);
|
||||
}
|
||||
|
||||
if (ppm[0] != 0.0 && ppm[1] != 0.0) {
|
||||
addXDensity(header, ppm[0] * 0.0254);
|
||||
header.pixelAspectRatio() = blender::math::safe_divide(ppm[1], ppm[0]);
|
||||
for (const ExrChannel &echan : handle->channels) {
|
||||
header.channels().insert(echan.name, Channel(echan.use_half_float ? Imf::HALF : Imf::FLOAT));
|
||||
}
|
||||
|
||||
/* Avoid crash/abort when we don't have permission to write here. */
|
||||
/* Manually create `ofstream`, so we can handle UTF8 file-paths on windows. */
|
||||
try {
|
||||
data->ofile_stream = new OFileStream(filepath);
|
||||
data->ofile = new OutputFile(*(data->ofile_stream), header);
|
||||
handle->ofile_stream = new OFileStream(filepath);
|
||||
handle->ofile = new MultiPartOutputFile(*(handle->ofile_stream), &header, 1);
|
||||
}
|
||||
catch (const std::exception &exc) {
|
||||
CLOG_ERROR(&LOG, "%s: %s", __func__, exc.what());
|
||||
|
||||
delete data->ofile;
|
||||
delete data->ofile_stream;
|
||||
delete handle->ofile;
|
||||
delete handle->ofile_stream;
|
||||
|
||||
data->ofile = nullptr;
|
||||
data->ofile_stream = nullptr;
|
||||
handle->ofile = nullptr;
|
||||
handle->ofile_stream = nullptr;
|
||||
}
|
||||
catch (...) { /* Catch-all for edge cases or compiler bugs. */
|
||||
CLOG_ERROR(&LOG, "Unknown error in %s", __func__);
|
||||
|
||||
delete data->ofile;
|
||||
delete data->ofile_stream;
|
||||
delete handle->ofile;
|
||||
delete handle->ofile_stream;
|
||||
|
||||
data->ofile = nullptr;
|
||||
data->ofile_stream = nullptr;
|
||||
handle->ofile = nullptr;
|
||||
handle->ofile_stream = nullptr;
|
||||
}
|
||||
|
||||
return (data->ofile != nullptr);
|
||||
return (handle->ofile != nullptr);
|
||||
}
|
||||
|
||||
bool IMB_exr_begin_read(
|
||||
void *handle, const char *filepath, int *width, int *height, const bool parse_channels)
|
||||
ExrHandle *handle, const char *filepath, int *width, int *height, const bool parse_channels)
|
||||
{
|
||||
ExrHandle *data = (ExrHandle *)handle;
|
||||
ExrChannel *echan;
|
||||
|
||||
/* 32 is arbitrary, but zero length files crashes exr. */
|
||||
if (!(BLI_exists(filepath) && BLI_file_size(filepath) > 32)) {
|
||||
return false;
|
||||
@@ -1069,125 +1013,111 @@ bool IMB_exr_begin_read(
|
||||
|
||||
/* avoid crash/abort when we don't have permission to write here */
|
||||
try {
|
||||
data->ifile_stream = new IFileStream(filepath);
|
||||
data->ifile = new MultiPartInputFile(*(data->ifile_stream));
|
||||
handle->ifile_stream = new IFileStream(filepath);
|
||||
handle->ifile = new MultiPartInputFile(*(handle->ifile_stream));
|
||||
}
|
||||
catch (...) { /* Catch-all for edge cases or compiler bugs. */
|
||||
delete data->ifile;
|
||||
delete data->ifile_stream;
|
||||
delete handle->ifile;
|
||||
delete handle->ifile_stream;
|
||||
|
||||
data->ifile = nullptr;
|
||||
data->ifile_stream = nullptr;
|
||||
handle->ifile = nullptr;
|
||||
handle->ifile_stream = nullptr;
|
||||
}
|
||||
|
||||
if (!data->ifile) {
|
||||
if (!handle->ifile) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Box2i dw = data->ifile->header(0).dataWindow();
|
||||
data->width = *width = dw.max.x - dw.min.x + 1;
|
||||
data->height = *height = dw.max.y - dw.min.y + 1;
|
||||
Box2i dw = handle->ifile->header(0).dataWindow();
|
||||
handle->width = *width = dw.max.x - dw.min.x + 1;
|
||||
handle->height = *height = dw.max.y - dw.min.y + 1;
|
||||
|
||||
if (parse_channels) {
|
||||
/* Parse channels into view/layer/pass. */
|
||||
if (!imb_exr_multilayer_parse_channels_from_file(data)) {
|
||||
if (!imb_exr_multilayer_parse_channels_from_file(handle)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Read view and channels without parsing. */
|
||||
imb_exr_get_views(*data->ifile, *data->multiView);
|
||||
|
||||
std::vector<MultiViewChannelName> channels;
|
||||
GetChannelsInMultiPartFile(*data->ifile, channels);
|
||||
|
||||
for (const MultiViewChannelName &channel : channels) {
|
||||
IMB_exr_add_channel(
|
||||
data, nullptr, channel.name.c_str(), channel.view.c_str(), 0, 0, nullptr, false);
|
||||
|
||||
echan = (ExrChannel *)data->channels.last;
|
||||
echan->m->name = channel.name;
|
||||
echan->m->view = channel.view;
|
||||
echan->m->part_number = channel.part_number;
|
||||
echan->m->internal_name = channel.internal_name;
|
||||
}
|
||||
/* Read view and channels without parsing layers and passes. */
|
||||
handle->views = imb_exr_get_views(*handle->ifile);
|
||||
handle->channels = exr_channels_in_multi_part_file(*handle->ifile, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IMB_exr_set_channel(
|
||||
void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect)
|
||||
ExrHandle *handle, blender::StringRefNull full_name, int xstride, int ystride, float *rect)
|
||||
{
|
||||
ExrHandle *data = (ExrHandle *)handle;
|
||||
char name[EXR_TOT_MAXNAME + 1];
|
||||
|
||||
if (layname && layname[0] != '\0') {
|
||||
char lay[EXR_LAY_MAXNAME + 1], pass[EXR_PASS_MAXNAME + 1];
|
||||
BLI_strncpy(lay, layname, EXR_LAY_MAXNAME);
|
||||
BLI_strncpy(pass, passname, EXR_PASS_MAXNAME);
|
||||
|
||||
SNPRINTF(name, "%s.%s", lay, pass);
|
||||
}
|
||||
else {
|
||||
BLI_strncpy(name, passname, EXR_TOT_MAXNAME - 1);
|
||||
for (ExrChannel &echan : handle->channels) {
|
||||
if (echan.name == full_name) {
|
||||
echan.xstride = xstride;
|
||||
echan.ystride = ystride;
|
||||
echan.rect = rect;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ExrChannel *echan = (ExrChannel *)BLI_findstring(
|
||||
&data->channels, name, offsetof(ExrChannel, name));
|
||||
|
||||
if (echan == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
echan->xstride = xstride;
|
||||
echan->ystride = ystride;
|
||||
echan->rect = rect;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void IMB_exr_write_channels(void *handle)
|
||||
void IMB_exr_write_channels(ExrHandle *handle)
|
||||
{
|
||||
ExrHandle *data = (ExrHandle *)handle;
|
||||
FrameBuffer frameBuffer;
|
||||
if (handle->channels.is_empty()) {
|
||||
CLOG_ERROR(&LOG, "Attempt to save MultiLayer without layers.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (data->channels.first) {
|
||||
const size_t num_pixels = size_t(data->width) * data->height;
|
||||
half *rect_half = nullptr, *current_rect_half = nullptr;
|
||||
const size_t num_pixels = size_t(handle->width) * handle->height;
|
||||
|
||||
{
|
||||
size_t part_num = 0;
|
||||
/* We allocate temporary storage for half pixels for all the channels at once. */
|
||||
if (data->num_half_channels != 0) {
|
||||
rect_half = MEM_malloc_arrayN<half>(size_t(data->num_half_channels) * num_pixels, __func__);
|
||||
current_rect_half = rect_half;
|
||||
int num_half_channels = 0;
|
||||
for (const ExrChannel &echan : handle->channels) {
|
||||
if (echan.use_half_float) {
|
||||
num_half_channels++;
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (ExrChannel *, echan, &data->channels) {
|
||||
blender::Vector<half> rect_half;
|
||||
half *current_rect_half = nullptr;
|
||||
if (num_half_channels > 0) {
|
||||
rect_half.resize(size_t(num_half_channels) * num_pixels);
|
||||
current_rect_half = rect_half.data();
|
||||
}
|
||||
|
||||
FrameBuffer frameBuffer;
|
||||
|
||||
for (const ExrChannel &echan : handle->channels) {
|
||||
/* Writing starts from last scan-line, stride negative. */
|
||||
if (echan->use_half_float) {
|
||||
const float *rect = echan->rect;
|
||||
if (echan.use_half_float) {
|
||||
const float *rect = echan.rect;
|
||||
half *cur = current_rect_half;
|
||||
for (size_t i = 0; i < num_pixels; i++, cur++) {
|
||||
*cur = float_to_half_safe(rect[i * echan->xstride]);
|
||||
*cur = float_to_half_safe(rect[i * echan.xstride]);
|
||||
}
|
||||
half *rect_to_write = current_rect_half + (data->height - 1L) * data->width;
|
||||
half *rect_to_write = current_rect_half + (handle->height - 1L) * handle->width;
|
||||
frameBuffer.insert(
|
||||
echan->name,
|
||||
Slice(Imf::HALF, (char *)rect_to_write, sizeof(half), -data->width * sizeof(half)));
|
||||
echan.name,
|
||||
Slice(Imf::HALF, (char *)rect_to_write, sizeof(half), -handle->width * sizeof(half)));
|
||||
current_rect_half += num_pixels;
|
||||
}
|
||||
else {
|
||||
float *rect = echan->rect + echan->xstride * (data->height - 1L) * data->width;
|
||||
frameBuffer.insert(echan->name,
|
||||
float *rect = echan.rect + echan.xstride * (handle->height - 1L) * handle->width;
|
||||
frameBuffer.insert(echan.name,
|
||||
Slice(Imf::FLOAT,
|
||||
(char *)rect,
|
||||
echan->xstride * sizeof(float),
|
||||
-echan->ystride * sizeof(float)));
|
||||
echan.xstride * sizeof(float),
|
||||
-echan.ystride * sizeof(float)));
|
||||
}
|
||||
}
|
||||
|
||||
data->ofile->setFrameBuffer(frameBuffer);
|
||||
OutputPart part(*handle->ofile, part_num);
|
||||
part.setFrameBuffer(frameBuffer);
|
||||
try {
|
||||
data->ofile->writePixels(data->height);
|
||||
part.writePixels(handle->height);
|
||||
}
|
||||
catch (const std::exception &exc) {
|
||||
CLOG_ERROR(&LOG, "%s: %s", __func__, exc.what());
|
||||
@@ -1195,23 +1125,15 @@ void IMB_exr_write_channels(void *handle)
|
||||
catch (...) { /* Catch-all for edge cases or compiler bugs. */
|
||||
CLOG_ERROR(&LOG, "Unknown error in %s", __func__);
|
||||
}
|
||||
/* Free temporary buffers. */
|
||||
if (rect_half != nullptr) {
|
||||
MEM_freeN(rect_half);
|
||||
}
|
||||
}
|
||||
else {
|
||||
CLOG_ERROR(&LOG, "Attempt to save MultiLayer without layers.");
|
||||
}
|
||||
}
|
||||
|
||||
void IMB_exr_read_channels(void *handle)
|
||||
void IMB_exr_read_channels(ExrHandle *handle)
|
||||
{
|
||||
ExrHandle *data = (ExrHandle *)handle;
|
||||
int numparts = data->ifile->parts();
|
||||
int numparts = handle->ifile->parts();
|
||||
|
||||
/* Check if EXR was saved with previous versions of blender which flipped images. */
|
||||
const StringAttribute *ta = data->ifile->header(0).findTypedAttribute<StringAttribute>(
|
||||
const StringAttribute *ta = handle->ifile->header(0).findTypedAttribute<StringAttribute>(
|
||||
"BlenderMultiChannel");
|
||||
|
||||
/* 'previous multilayer attribute, flipped. */
|
||||
@@ -1227,44 +1149,43 @@ void IMB_exr_read_channels(void *handle)
|
||||
|
||||
for (int i = 0; i < numparts; i++) {
|
||||
/* Read part header. */
|
||||
InputPart in(*data->ifile, i);
|
||||
InputPart in(*handle->ifile, i);
|
||||
Header header = in.header();
|
||||
Box2i dw = header.dataWindow();
|
||||
|
||||
/* Insert all matching channel into frame-buffer. */
|
||||
FrameBuffer frameBuffer;
|
||||
|
||||
LISTBASE_FOREACH (ExrChannel *, echan, &data->channels) {
|
||||
if (echan->m->part_number != i) {
|
||||
for (const ExrChannel &echan : handle->channels) {
|
||||
if (echan.part_number != i) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CLOG_DEBUG(&LOG,
|
||||
"%d %-6s %-22s \"%s\"\n",
|
||||
echan->m->part_number,
|
||||
echan->m->view.c_str(),
|
||||
echan->m->name.c_str(),
|
||||
echan->m->internal_name.c_str());
|
||||
echan.part_number,
|
||||
echan.view.c_str(),
|
||||
echan.name.c_str(),
|
||||
echan.internal_name.c_str());
|
||||
|
||||
if (echan->rect) {
|
||||
float *rect = echan->rect;
|
||||
size_t xstride = echan->xstride * sizeof(float);
|
||||
size_t ystride = echan->ystride * sizeof(float);
|
||||
if (echan.rect) {
|
||||
float *rect = echan.rect;
|
||||
size_t xstride = echan.xstride * sizeof(float);
|
||||
size_t ystride = echan.ystride * sizeof(float);
|
||||
|
||||
if (!flip) {
|
||||
/* Inverse correct first pixel for data-window coordinates. */
|
||||
rect -= echan->xstride * (dw.min.x - dw.min.y * data->width);
|
||||
rect -= echan.xstride * (dw.min.x - dw.min.y * handle->width);
|
||||
/* Move to last scan-line to flip to Blender convention. */
|
||||
rect += echan->xstride * (data->height - 1) * data->width;
|
||||
rect += echan.xstride * (handle->height - 1) * handle->width;
|
||||
ystride = -ystride;
|
||||
}
|
||||
else {
|
||||
/* Inverse correct first pixel for data-window coordinates. */
|
||||
rect -= echan->xstride * (dw.min.x + dw.min.y * data->width);
|
||||
rect -= echan.xstride * (dw.min.x + dw.min.y * handle->width);
|
||||
}
|
||||
|
||||
frameBuffer.insert(echan->m->internal_name,
|
||||
Slice(Imf::FLOAT, (char *)rect, xstride, ystride));
|
||||
frameBuffer.insert(echan.internal_name, Slice(Imf::FLOAT, (char *)rect, xstride, ystride));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1285,7 +1206,7 @@ void IMB_exr_read_channels(void *handle)
|
||||
}
|
||||
}
|
||||
|
||||
void IMB_exr_multilayer_convert(void *handle,
|
||||
void IMB_exr_multilayer_convert(ExrHandle *handle,
|
||||
void *base,
|
||||
void *(*addview)(void *base, const char *str),
|
||||
void *(*addlayer)(void *base, const char *str),
|
||||
@@ -1297,75 +1218,52 @@ void IMB_exr_multilayer_convert(void *handle,
|
||||
const char *chan_id,
|
||||
const char *view))
|
||||
{
|
||||
ExrHandle *data = (ExrHandle *)handle;
|
||||
|
||||
/* RenderResult needs at least one RenderView */
|
||||
if (data->multiView->empty()) {
|
||||
if (handle->views.empty()) {
|
||||
addview(base, "");
|
||||
}
|
||||
else {
|
||||
/* add views to RenderResult */
|
||||
for (const std::string &view_name : *data->multiView) {
|
||||
for (const std::string &view_name : handle->views) {
|
||||
addview(base, view_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (BLI_listbase_is_empty(&data->layers)) {
|
||||
if (handle->layers.is_empty()) {
|
||||
CLOG_WARN(&LOG, "Cannot convert multilayer, no layers in handle");
|
||||
return;
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (ExrLayer *, lay, &data->layers) {
|
||||
void *laybase = addlayer(base, lay->name);
|
||||
for (ExrLayer &lay : handle->layers) {
|
||||
void *laybase = addlayer(base, lay.name.c_str());
|
||||
if (laybase) {
|
||||
LISTBASE_FOREACH (ExrPass *, pass, &lay->passes) {
|
||||
for (ExrPass &pass : lay.passes) {
|
||||
addpass(base,
|
||||
laybase,
|
||||
pass->internal_name,
|
||||
pass->rect,
|
||||
pass->totchan,
|
||||
pass->chan_id,
|
||||
pass->view);
|
||||
pass->rect = nullptr;
|
||||
pass.internal_name.c_str(),
|
||||
pass.rect,
|
||||
pass.totchan,
|
||||
pass.chan_id,
|
||||
pass.view.c_str());
|
||||
pass.rect = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IMB_exr_close(void *handle)
|
||||
void IMB_exr_close(ExrHandle *handle)
|
||||
{
|
||||
ExrHandle *data = (ExrHandle *)handle;
|
||||
delete handle->ifile;
|
||||
delete handle->ifile_stream;
|
||||
delete handle->ofile;
|
||||
delete handle->ofile_stream;
|
||||
|
||||
delete data->ifile;
|
||||
delete data->ifile_stream;
|
||||
delete data->ofile;
|
||||
delete data->mpofile;
|
||||
delete data->ofile_stream;
|
||||
delete data->multiView;
|
||||
handle->ifile = nullptr;
|
||||
handle->ifile_stream = nullptr;
|
||||
handle->ofile = nullptr;
|
||||
handle->ofile_stream = nullptr;
|
||||
|
||||
data->ifile = nullptr;
|
||||
data->ifile_stream = nullptr;
|
||||
data->ofile = nullptr;
|
||||
data->mpofile = nullptr;
|
||||
data->ofile_stream = nullptr;
|
||||
|
||||
LISTBASE_FOREACH (ExrChannel *, chan, &data->channels) {
|
||||
delete chan->m;
|
||||
}
|
||||
BLI_freelistN(&data->channels);
|
||||
|
||||
LISTBASE_FOREACH (ExrLayer *, lay, &data->layers) {
|
||||
LISTBASE_FOREACH (ExrPass *, pass, &lay->passes) {
|
||||
if (pass->rect) {
|
||||
MEM_freeN(pass->rect);
|
||||
}
|
||||
}
|
||||
BLI_freelistN(&lay->passes);
|
||||
}
|
||||
BLI_freelistN(&data->layers);
|
||||
|
||||
BLI_remlink(&exrhandles, data);
|
||||
MEM_freeN(data);
|
||||
MEM_delete(handle);
|
||||
}
|
||||
|
||||
/* ********* */
|
||||
@@ -1386,19 +1284,19 @@ static int imb_exr_split_token(const char *str, const char *end, const char **to
|
||||
}
|
||||
|
||||
static void imb_exr_pass_name_from_channel(char *passname,
|
||||
const ExrChannel *echan,
|
||||
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"))) {
|
||||
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) {
|
||||
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')) {
|
||||
else if (ELEM(echan.chan_id, 'R', 'G', 'B', 'A', 'V', 'X', 'Y', 'Z')) {
|
||||
BLI_strncpy(passname, "Combined", passname_maxncpy);
|
||||
}
|
||||
else {
|
||||
@@ -1407,7 +1305,7 @@ static void imb_exr_pass_name_from_channel(char *passname,
|
||||
}
|
||||
|
||||
static void imb_exr_pass_name_from_channel_name(char *passname,
|
||||
const ExrChannel * /*echan*/,
|
||||
const ExrChannel & /*echan*/,
|
||||
const char *channelname,
|
||||
const bool /*has_xyz_channels*/)
|
||||
{
|
||||
@@ -1421,13 +1319,13 @@ static void imb_exr_pass_name_from_channel_name(char *passname,
|
||||
BLI_strncpy(passname, channelname, passname_maxncpy);
|
||||
}
|
||||
|
||||
static int imb_exr_split_channel_name(ExrChannel *echan,
|
||||
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 char *name = echan->m->name.c_str();
|
||||
const char *name = echan.name.c_str();
|
||||
const char *end = name + strlen(name);
|
||||
const char *token;
|
||||
|
||||
@@ -1439,7 +1337,7 @@ static int imb_exr_split_channel_name(ExrChannel *echan,
|
||||
/* Notice that we will be comparing with this upper-case version of the channel name, so the
|
||||
* below comparisons are effectively not case sensitive, and would also consider lowercase
|
||||
* versions of the listed channels. */
|
||||
echan->chan_id = BLI_toupper_ascii(name[0]);
|
||||
echan.chan_id = BLI_toupper_ascii(name[0]);
|
||||
layname[0] = '\0';
|
||||
imb_exr_pass_name_from_channel(passname, echan, name, has_xyz_channels);
|
||||
return 1;
|
||||
@@ -1456,7 +1354,7 @@ static int imb_exr_split_channel_name(ExrChannel *echan,
|
||||
BLI_strncpy(channelname, token, std::min(len + 1, sizeof(channelname)));
|
||||
|
||||
if (len == 1) {
|
||||
echan->chan_id = BLI_toupper_ascii(channelname[0]);
|
||||
echan.chan_id = BLI_toupper_ascii(channelname[0]);
|
||||
}
|
||||
else {
|
||||
BLI_assert(len > 1); /* Checks above ensure. */
|
||||
@@ -1471,29 +1369,29 @@ static int imb_exr_split_channel_name(ExrChannel *echan,
|
||||
*/
|
||||
const char chan_id = BLI_toupper_ascii(channelname[1]);
|
||||
if (ELEM(chan_id, 'X', 'Y', 'Z', 'R', 'G', 'B', 'U', 'V', 'A')) {
|
||||
echan->chan_id = chan_id;
|
||||
echan.chan_id = chan_id;
|
||||
}
|
||||
else {
|
||||
echan->chan_id = 'X'; /* Default to X if unknown. */
|
||||
echan.chan_id = 'X'; /* Default to X if unknown. */
|
||||
}
|
||||
}
|
||||
else if (BLI_strcaseeq(channelname, "red")) {
|
||||
echan->chan_id = 'R';
|
||||
echan.chan_id = 'R';
|
||||
}
|
||||
else if (BLI_strcaseeq(channelname, "green")) {
|
||||
echan->chan_id = 'G';
|
||||
echan.chan_id = 'G';
|
||||
}
|
||||
else if (BLI_strcaseeq(channelname, "blue")) {
|
||||
echan->chan_id = 'B';
|
||||
echan.chan_id = 'B';
|
||||
}
|
||||
else if (BLI_strcaseeq(channelname, "alpha")) {
|
||||
echan->chan_id = 'A';
|
||||
echan.chan_id = 'A';
|
||||
}
|
||||
else if (BLI_strcaseeq(channelname, "depth")) {
|
||||
echan->chan_id = 'Z';
|
||||
echan.chan_id = 'Z';
|
||||
}
|
||||
else {
|
||||
echan->chan_id = 'X'; /* Default to X if unknown. */
|
||||
echan.chan_id = 'X'; /* Default to X if unknown. */
|
||||
}
|
||||
}
|
||||
end -= len + 1; /* +1 to skip '.' separator */
|
||||
@@ -1524,37 +1422,38 @@ static int imb_exr_split_channel_name(ExrChannel *echan,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static ExrLayer *imb_exr_get_layer(ListBase *lb, const char *layname)
|
||||
static ExrLayer *imb_exr_get_layer(ExrHandle *handle, const char *layname)
|
||||
{
|
||||
ExrLayer *lay = (ExrLayer *)BLI_findstring(lb, layname, offsetof(ExrLayer, name));
|
||||
|
||||
if (lay == nullptr) {
|
||||
lay = MEM_callocN<ExrLayer>("exr layer");
|
||||
BLI_addtail(lb, lay);
|
||||
BLI_strncpy(lay->name, layname, EXR_LAY_MAXNAME);
|
||||
for (ExrLayer &lay : handle->layers) {
|
||||
if (lay.name == layname) {
|
||||
return &lay;
|
||||
}
|
||||
}
|
||||
|
||||
return lay;
|
||||
handle->layers.append_as();
|
||||
ExrLayer &lay = handle->layers.last();
|
||||
lay.name = layname;
|
||||
return &lay;
|
||||
}
|
||||
|
||||
static ExrPass *imb_exr_get_pass(ListBase *lb, const char *passname)
|
||||
static ExrPass *imb_exr_get_pass(ExrLayer &lay, const char *passname)
|
||||
{
|
||||
ExrPass *pass = (ExrPass *)BLI_findstring(lb, passname, offsetof(ExrPass, name));
|
||||
|
||||
if (pass == nullptr) {
|
||||
pass = MEM_callocN<ExrPass>("exr pass");
|
||||
|
||||
if (STREQ(passname, "Combined")) {
|
||||
BLI_addhead(lb, pass);
|
||||
}
|
||||
else {
|
||||
BLI_addtail(lb, pass);
|
||||
for (ExrPass &pass : lay.passes) {
|
||||
if (pass.name == passname) {
|
||||
return &pass;
|
||||
}
|
||||
}
|
||||
|
||||
STRNCPY(pass->name, passname);
|
||||
ExrPass pass;
|
||||
pass.name = passname;
|
||||
|
||||
return pass;
|
||||
if (STREQ(passname, "Combined")) {
|
||||
lay.passes.prepend(std::move(pass));
|
||||
return &lay.passes.first();
|
||||
}
|
||||
|
||||
lay.passes.append(std::move(pass));
|
||||
return &lay.passes.last();
|
||||
}
|
||||
|
||||
static bool exr_has_xyz_channels(ExrHandle *exr_handle)
|
||||
@@ -1562,14 +1461,14 @@ static bool exr_has_xyz_channels(ExrHandle *exr_handle)
|
||||
bool x_found = false;
|
||||
bool y_found = false;
|
||||
bool z_found = false;
|
||||
LISTBASE_FOREACH (ExrChannel *, channel, &exr_handle->channels) {
|
||||
if (ELEM(channel->m->name, "X", "x")) {
|
||||
for (const ExrChannel &echan : exr_handle->channels) {
|
||||
if (ELEM(echan.name, "X", "x")) {
|
||||
x_found = true;
|
||||
}
|
||||
if (ELEM(channel->m->name, "Y", "y")) {
|
||||
if (ELEM(echan.name, "Y", "y")) {
|
||||
y_found = true;
|
||||
}
|
||||
if (ELEM(channel->m->name, "Z", "z")) {
|
||||
if (ELEM(echan.name, "Z", "z")) {
|
||||
z_found = true;
|
||||
}
|
||||
}
|
||||
@@ -1579,98 +1478,88 @@ static bool exr_has_xyz_channels(ExrHandle *exr_handle)
|
||||
|
||||
/* 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)
|
||||
static blender::Vector<ExrChannel> exr_channels_in_multi_part_file(const MultiPartInputFile &file,
|
||||
const bool parse_layers)
|
||||
{
|
||||
std::vector<MultiViewChannelName> 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;
|
||||
}
|
||||
}
|
||||
|
||||
blender::Vector<ExrChannel> channels;
|
||||
/* Get channels from each part. */
|
||||
for (int p = 0; p < file.parts(); p++) {
|
||||
const ChannelList &c = file.header(p).channels();
|
||||
|
||||
/* There are two ways of storing multiview EXRs:
|
||||
* - Multiple views in part with multiView attribute.
|
||||
* - Each view in its own part with view attribute. */
|
||||
const bool has_multiple_views_in_part = hasMultiView(file.header(p));
|
||||
StringVector views_in_part;
|
||||
if (has_multiple_views_in_part) {
|
||||
views_in_part = multiView(file.header(p));
|
||||
}
|
||||
blender::StringRef part_view;
|
||||
if (file.header(p).hasView()) {
|
||||
part_view = file.header(p).view();
|
||||
}
|
||||
|
||||
/* Parse part name. */
|
||||
blender::StringRef part_name;
|
||||
if (file.header(p).hasName()) {
|
||||
if (parse_layers && file.header(p).hasName()) {
|
||||
part_name = file.header(p).name();
|
||||
|
||||
/* Strip view name suffix if views are stored in separate parts.
|
||||
* They need to be included to make the part names unique. */
|
||||
if (!has_multiple_views_in_part) {
|
||||
if (part_name.endswith("." + part_view)) {
|
||||
part_name = part_name.drop_known_suffix("." + part_view);
|
||||
}
|
||||
else if (part_name.endswith("-" + part_view)) {
|
||||
part_name = part_name.drop_known_suffix("-" + part_view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Strip part suffix from name. */
|
||||
if (part_name.endswith("." + part_view)) {
|
||||
part_name = part_name.drop_known_suffix("." + part_view);
|
||||
}
|
||||
else if (part_name.endswith("-" + part_view)) {
|
||||
part_name = part_name.drop_known_suffix("-" + part_view);
|
||||
}
|
||||
|
||||
/* Parse channels. */
|
||||
for (ChannelList::ConstIterator i = c.begin(); i != c.end(); i++) {
|
||||
MultiViewChannelName m;
|
||||
m.name = std::string(i.name());
|
||||
m.internal_name = m.name;
|
||||
ExrChannel echan;
|
||||
echan.name = std::string(i.name());
|
||||
echan.internal_name = echan.name;
|
||||
|
||||
if (has_multiview) {
|
||||
m.view = viewFromChannelName(m.name, multiview);
|
||||
m.name = removeViewName(m.internal_name, m.view);
|
||||
if (has_multiple_views_in_part) {
|
||||
echan.view = viewFromChannelName(echan.name, views_in_part);
|
||||
echan.name = removeViewName(echan.internal_name, echan.view);
|
||||
}
|
||||
else {
|
||||
m.view = part_view;
|
||||
echan.view = part_view;
|
||||
}
|
||||
|
||||
/* Prepend part name as potential layer or pass name. According to OpenEXR docs
|
||||
* this should not be needed, but Houdini writes files like this. */
|
||||
if (!part_name.is_empty() && !blender::StringRef(m.name).startswith(part_name + ".")) {
|
||||
m.name = part_name + "." + m.name;
|
||||
if (parse_layers) {
|
||||
/* Prepend part name as potential layer or pass name. According to OpenEXR docs
|
||||
* this should not be needed, but Houdini writes files like this. */
|
||||
if (!part_name.is_empty() && !blender::StringRef(echan.name).startswith(part_name + ".")) {
|
||||
echan.name = part_name + "." + echan.name;
|
||||
}
|
||||
}
|
||||
|
||||
m.part_number = p;
|
||||
channels.push_back(m);
|
||||
echan.part_number = p;
|
||||
channels.append(std::move(echan));
|
||||
}
|
||||
}
|
||||
|
||||
return channels;
|
||||
}
|
||||
|
||||
static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *data)
|
||||
static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *handle)
|
||||
{
|
||||
std::vector<MultiViewChannelName> channels = exr_channels_in_multi_part_file(*data->ifile);
|
||||
handle->views = imb_exr_get_views(*handle->ifile);
|
||||
handle->channels = exr_channels_in_multi_part_file(*handle->ifile, true);
|
||||
|
||||
imb_exr_get_views(*data->ifile, *data->multiView);
|
||||
|
||||
for (const MultiViewChannelName &channel : channels) {
|
||||
IMB_exr_add_channel(
|
||||
data, nullptr, channel.name.c_str(), channel.view.c_str(), 0, 0, nullptr, false);
|
||||
|
||||
ExrChannel *echan = (ExrChannel *)data->channels.last;
|
||||
echan->m->name = channel.name;
|
||||
echan->m->view = channel.view;
|
||||
echan->m->part_number = channel.part_number;
|
||||
echan->m->internal_name = channel.internal_name;
|
||||
}
|
||||
|
||||
const bool has_xyz_channels = exr_has_xyz_channels(data);
|
||||
const bool has_xyz_channels = exr_has_xyz_channels(handle);
|
||||
|
||||
/* now try to sort out how to assign memory to the channels */
|
||||
/* first build hierarchical layer list */
|
||||
ExrChannel *echan = (ExrChannel *)data->channels.first;
|
||||
for (; echan; echan = echan->next) {
|
||||
for (ExrChannel &echan : handle->channels) {
|
||||
char layname[EXR_TOT_MAXNAME], passname[EXR_TOT_MAXNAME];
|
||||
if (imb_exr_split_channel_name(echan, layname, passname, has_xyz_channels)) {
|
||||
const char *view = echan->m->view.c_str();
|
||||
char internal_name[EXR_PASS_MAXNAME];
|
||||
|
||||
STRNCPY(internal_name, passname);
|
||||
const char *view = echan.view.c_str();
|
||||
std::string internal_name = passname;
|
||||
|
||||
if (view[0] != '\0') {
|
||||
char tmp_pass[EXR_PASS_MAXNAME];
|
||||
@@ -1678,37 +1567,33 @@ static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *data)
|
||||
STRNCPY(passname, tmp_pass);
|
||||
}
|
||||
|
||||
ExrLayer *lay = imb_exr_get_layer(&data->layers, layname);
|
||||
ExrPass *pass = imb_exr_get_pass(&lay->passes, passname);
|
||||
ExrLayer *lay = imb_exr_get_layer(handle, layname);
|
||||
ExrPass *pass = imb_exr_get_pass(*lay, passname);
|
||||
|
||||
pass->chan[pass->totchan] = echan;
|
||||
pass->chan[pass->totchan] = &echan;
|
||||
pass->totchan++;
|
||||
pass->view_id = echan->view_id;
|
||||
STRNCPY(pass->view, view);
|
||||
STRNCPY(pass->internal_name, internal_name);
|
||||
pass->view = view;
|
||||
pass->internal_name = internal_name;
|
||||
|
||||
if (pass->totchan >= EXR_PASS_MAXCHAN) {
|
||||
break;
|
||||
CLOG_ERROR(&LOG, "Too many channels in one pass: %s", echan.name.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (echan) {
|
||||
CLOG_ERROR(&LOG, "Too many channels in one pass: %s", echan->m->name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
/* with some heuristics, try to merge the channels in buffers */
|
||||
LISTBASE_FOREACH (ExrLayer *, lay, &data->layers) {
|
||||
LISTBASE_FOREACH (ExrPass *, pass, &lay->passes) {
|
||||
if (pass->totchan) {
|
||||
pass->rect = MEM_calloc_arrayN<float>(
|
||||
size_t(data->width) * size_t(data->height) * size_t(pass->totchan), "pass rect");
|
||||
if (pass->totchan == 1) {
|
||||
ExrChannel *echan = pass->chan[0];
|
||||
echan->rect = pass->rect;
|
||||
echan->xstride = 1;
|
||||
echan->ystride = data->width;
|
||||
pass->chan_id[0] = echan->chan_id;
|
||||
for (ExrLayer &lay : handle->layers) {
|
||||
for (ExrPass &pass : lay.passes) {
|
||||
if (pass.totchan) {
|
||||
pass.rect = MEM_calloc_arrayN<float>(
|
||||
size_t(handle->width) * size_t(handle->height) * size_t(pass.totchan), "pass rect");
|
||||
if (pass.totchan == 1) {
|
||||
ExrChannel &echan = *pass.chan[0];
|
||||
echan.rect = pass.rect;
|
||||
echan.xstride = 1;
|
||||
echan.ystride = handle->width;
|
||||
pass.chan_id[0] = echan.chan_id;
|
||||
}
|
||||
else {
|
||||
char lookup[256];
|
||||
@@ -1716,17 +1601,17 @@ static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *data)
|
||||
memset(lookup, 0, sizeof(lookup));
|
||||
|
||||
/* we can have RGB(A), XYZ(W), UVA */
|
||||
if (ELEM(pass->totchan, 3, 4)) {
|
||||
if (pass->chan[0]->chan_id == 'B' || pass->chan[1]->chan_id == 'B' ||
|
||||
pass->chan[2]->chan_id == 'B')
|
||||
if (ELEM(pass.totchan, 3, 4)) {
|
||||
if (pass.chan[0]->chan_id == 'B' || pass.chan[1]->chan_id == 'B' ||
|
||||
pass.chan[2]->chan_id == 'B')
|
||||
{
|
||||
lookup[uint('R')] = 0;
|
||||
lookup[uint('G')] = 1;
|
||||
lookup[uint('B')] = 2;
|
||||
lookup[uint('A')] = 3;
|
||||
}
|
||||
else if (pass->chan[0]->chan_id == 'Y' || pass->chan[1]->chan_id == 'Y' ||
|
||||
pass->chan[2]->chan_id == 'Y')
|
||||
else if (pass.chan[0]->chan_id == 'Y' || pass.chan[1]->chan_id == 'Y' ||
|
||||
pass.chan[2]->chan_id == 'Y')
|
||||
{
|
||||
lookup[uint('X')] = 0;
|
||||
lookup[uint('Y')] = 1;
|
||||
@@ -1738,21 +1623,21 @@ static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *data)
|
||||
lookup[uint('V')] = 1;
|
||||
lookup[uint('A')] = 2;
|
||||
}
|
||||
for (int a = 0; a < pass->totchan; a++) {
|
||||
echan = pass->chan[a];
|
||||
echan->rect = pass->rect + lookup[uint(echan->chan_id)];
|
||||
echan->xstride = pass->totchan;
|
||||
echan->ystride = data->width * pass->totchan;
|
||||
pass->chan_id[uint(lookup[uint(echan->chan_id)])] = echan->chan_id;
|
||||
for (int a = 0; a < pass.totchan; a++) {
|
||||
ExrChannel &echan = *pass.chan[a];
|
||||
echan.rect = pass.rect + lookup[uint(echan.chan_id)];
|
||||
echan.xstride = pass.totchan;
|
||||
echan.ystride = handle->width * pass.totchan;
|
||||
pass.chan_id[uint(lookup[uint(echan.chan_id)])] = echan.chan_id;
|
||||
}
|
||||
}
|
||||
else { /* unknown */
|
||||
for (int a = 0; a < pass->totchan; a++) {
|
||||
ExrChannel *echan = pass->chan[a];
|
||||
echan->rect = pass->rect + a;
|
||||
echan->xstride = pass->totchan;
|
||||
echan->ystride = data->width * pass->totchan;
|
||||
pass->chan_id[a] = echan->chan_id;
|
||||
for (int a = 0; a < pass.totchan; a++) {
|
||||
ExrChannel &echan = *pass.chan[a];
|
||||
echan.rect = pass.rect + a;
|
||||
echan.xstride = pass.totchan;
|
||||
echan.ystride = handle->width * pass.totchan;
|
||||
pass.chan_id[a] = echan.chan_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1769,20 +1654,20 @@ static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
ExrHandle *data = (ExrHandle *)IMB_exr_get_handle();
|
||||
ExrHandle *handle = IMB_exr_get_handle();
|
||||
|
||||
data->ifile_stream = &file_stream;
|
||||
data->ifile = &file;
|
||||
handle->ifile_stream = &file_stream;
|
||||
handle->ifile = &file;
|
||||
|
||||
data->width = width;
|
||||
data->height = height;
|
||||
handle->width = width;
|
||||
handle->height = height;
|
||||
|
||||
if (!imb_exr_multilayer_parse_channels_from_file(data)) {
|
||||
IMB_exr_close(data);
|
||||
if (!imb_exr_multilayer_parse_channels_from_file(handle)) {
|
||||
IMB_exr_close(handle);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return data;
|
||||
return handle;
|
||||
}
|
||||
|
||||
/* ********************************************************* */
|
||||
@@ -1922,62 +1807,6 @@ static bool imb_exr_is_multilayer_file(MultiPartInputFile &file)
|
||||
return !layerNames.empty();
|
||||
}
|
||||
|
||||
static void imb_exr_type_by_channels(ChannelList &channels,
|
||||
StringVector &views,
|
||||
bool *r_singlelayer,
|
||||
bool *r_multilayer,
|
||||
bool *r_multiview)
|
||||
{
|
||||
std::set<std::string> layerNames;
|
||||
|
||||
*r_singlelayer = true;
|
||||
*r_multilayer = *r_multiview = false;
|
||||
|
||||
/* will not include empty layer names */
|
||||
channels.layers(layerNames);
|
||||
|
||||
if (!views.empty() && !views[0].empty()) {
|
||||
*r_multiview = true;
|
||||
}
|
||||
else {
|
||||
*r_singlelayer = false;
|
||||
*r_multilayer = (layerNames.size() > 1);
|
||||
*r_multiview = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!layerNames.empty()) {
|
||||
/* If `layerNames` is not empty, it means at least one layer is non-empty,
|
||||
* but it also could be layers without names in the file and such case
|
||||
* shall be considered a multi-layer EXR.
|
||||
*
|
||||
* That's what we do here: test whether there are empty layer names together
|
||||
* with non-empty ones in the file.
|
||||
*/
|
||||
for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); i++) {
|
||||
for (const std::string &layer_name : layerNames) {
|
||||
/* see if any layer_name differs from a view_name. */
|
||||
if (imb_exr_get_multiView_id(views, layer_name) == -1) {
|
||||
std::string layerName = layer_name;
|
||||
size_t pos = layerName.rfind('.');
|
||||
|
||||
if (pos == std::string::npos) {
|
||||
*r_multilayer = true;
|
||||
*r_singlelayer = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
*r_singlelayer = true;
|
||||
*r_multilayer = false;
|
||||
}
|
||||
|
||||
BLI_assert(r_singlelayer != r_multilayer);
|
||||
}
|
||||
|
||||
static bool exr_has_multiview(MultiPartInputFile &file)
|
||||
{
|
||||
for (int p = 0; p < file.parts(); p++) {
|
||||
@@ -2014,10 +1843,9 @@ static bool imb_exr_is_multi(MultiPartInputFile &file)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IMB_exr_has_multilayer(void *handle)
|
||||
bool IMB_exr_has_multilayer(ExrHandle *handle)
|
||||
{
|
||||
ExrHandle *data = (ExrHandle *)handle;
|
||||
return imb_exr_is_multi(*data->ifile);
|
||||
return imb_exr_is_multi(*handle->ifile);
|
||||
}
|
||||
|
||||
static bool imb_check_chromaticity_val(float test_v, float ref_v)
|
||||
@@ -2096,10 +1924,9 @@ static bool exr_get_ppm(MultiPartInputFile &file, double ppm[2])
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IMB_exr_get_ppm(void *handle, double ppm[2])
|
||||
bool IMB_exr_get_ppm(ExrHandle *handle, double ppm[2])
|
||||
{
|
||||
ExrHandle *data = (ExrHandle *)handle;
|
||||
return exr_get_ppm(*data->ifile, ppm);
|
||||
return exr_get_ppm(*handle->ifile, ppm);
|
||||
}
|
||||
|
||||
ImBuf *imb_load_openexr(const uchar *mem, size_t size, int flags, ImFileColorSpace &r_colorspace)
|
||||
@@ -2172,7 +1999,7 @@ ImBuf *imb_load_openexr(const uchar *mem, size_t size, int flags, ImFileColorSpa
|
||||
ExrHandle *handle = imb_exr_begin_read_mem(*membuf, *file, width, height);
|
||||
if (handle) {
|
||||
IMB_exr_read_channels(handle);
|
||||
ibuf->userdata = handle; /* potential danger, the caller has to check for this! */
|
||||
ibuf->exrhandle = handle; /* potential danger, the caller has to check for this! */
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -6,28 +6,25 @@
|
||||
* \ingroup openexr
|
||||
*/
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "IMB_openexr.hh"
|
||||
|
||||
void *IMB_exr_get_handle()
|
||||
ExrHandle *IMB_exr_get_handle()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
void *IMB_exr_get_handle_name(const char * /*name*/)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
void IMB_exr_add_channel(void * /*handle*/,
|
||||
const char * /*layname*/,
|
||||
const char * /*passname*/,
|
||||
const char * /*view*/,
|
||||
int /*xstride*/,
|
||||
int /*ystride*/,
|
||||
float * /*rect*/,
|
||||
bool /*use_half_float*/)
|
||||
void IMB_exr_add_channels(ExrHandle * /*handle*/,
|
||||
blender::StringRefNull /*layerpassname*/,
|
||||
blender::StringRefNull /*channelnames*/,
|
||||
blender::StringRefNull /*viewname*/,
|
||||
size_t /*xstride*/,
|
||||
size_t /*ystride*/,
|
||||
float * /*rect*/,
|
||||
bool /*use_half_float*/)
|
||||
{
|
||||
}
|
||||
|
||||
bool IMB_exr_begin_read(void * /*handle*/,
|
||||
bool IMB_exr_begin_read(ExrHandle * /*handle*/,
|
||||
const char * /*filepath*/,
|
||||
int * /*width*/,
|
||||
int * /*height*/,
|
||||
@@ -35,7 +32,7 @@ bool IMB_exr_begin_read(void * /*handle*/,
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool IMB_exr_begin_write(void * /*handle*/,
|
||||
bool IMB_exr_begin_write(ExrHandle * /*handle*/,
|
||||
const char * /*filepath*/,
|
||||
int /*width*/,
|
||||
int /*height*/,
|
||||
@@ -47,9 +44,8 @@ bool IMB_exr_begin_write(void * /*handle*/,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IMB_exr_set_channel(void * /*handle*/,
|
||||
const char * /*layname*/,
|
||||
const char * /*passname*/,
|
||||
bool IMB_exr_set_channel(ExrHandle * /*handle*/,
|
||||
blender::StringRefNull /*full_name*/,
|
||||
int /*xstride*/,
|
||||
int /*ystride*/,
|
||||
float * /*rect*/)
|
||||
@@ -57,10 +53,10 @@ bool IMB_exr_set_channel(void * /*handle*/,
|
||||
return false;
|
||||
}
|
||||
|
||||
void IMB_exr_read_channels(void * /*handle*/) {}
|
||||
void IMB_exr_write_channels(void * /*handle*/) {}
|
||||
void IMB_exr_read_channels(ExrHandle * /*handle*/) {}
|
||||
void IMB_exr_write_channels(ExrHandle * /*handle*/) {}
|
||||
|
||||
void IMB_exr_multilayer_convert(void * /*handle*/,
|
||||
void IMB_exr_multilayer_convert(ExrHandle * /*handle*/,
|
||||
void * /*base*/,
|
||||
void *(* /*addview*/)(void *base, const char *str),
|
||||
void *(* /*addlayer*/)(void *base, const char *str),
|
||||
@@ -74,15 +70,15 @@ void IMB_exr_multilayer_convert(void * /*handle*/,
|
||||
{
|
||||
}
|
||||
|
||||
void IMB_exr_close(void * /*handle*/) {}
|
||||
void IMB_exr_close(ExrHandle * /*handle*/) {}
|
||||
|
||||
void IMB_exr_add_view(void * /*handle*/, const char * /*name*/) {}
|
||||
bool IMB_exr_has_multilayer(void * /*handle*/)
|
||||
void IMB_exr_add_view(ExrHandle * /*handle*/, const char * /*name*/) {}
|
||||
bool IMB_exr_has_multilayer(ExrHandle * /*handle*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IMB_exr_get_ppm(void * /*handle*/, double /*ppm*/[2])
|
||||
bool IMB_exr_get_ppm(ExrHandle * /*handle*/, double /*ppm*/[2])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
namespace blender::gpu {
|
||||
class Texture;
|
||||
}
|
||||
struct ExrHandle;
|
||||
struct ImBuf;
|
||||
struct Image;
|
||||
struct ImageFormatData;
|
||||
@@ -90,9 +91,6 @@ struct RenderLayer {
|
||||
|
||||
int rectx, recty;
|
||||
|
||||
/** Optional saved end-result on disk. */
|
||||
void *exrhandle;
|
||||
|
||||
ListBase passes;
|
||||
};
|
||||
|
||||
@@ -416,7 +414,7 @@ void RE_PreviewRender(struct Render *re, struct Main *bmain, struct Scene *scene
|
||||
bool RE_ReadRenderResult(struct Scene *scene, struct Scene *scenode);
|
||||
|
||||
struct RenderResult *RE_MultilayerConvert(
|
||||
void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty);
|
||||
ExrHandle *exrhandle, const char *colorspace, bool predivide, int rectx, int recty);
|
||||
|
||||
/* Display and event callbacks. */
|
||||
|
||||
|
||||
@@ -262,7 +262,7 @@ bool RE_HasSingleLayer(Render *re)
|
||||
}
|
||||
|
||||
RenderResult *RE_MultilayerConvert(
|
||||
void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty)
|
||||
ExrHandle *exrhandle, const char *colorspace, bool predivide, int rectx, int recty)
|
||||
{
|
||||
return render_result_new_from_exr(exrhandle, colorspace, predivide, rectx, recty);
|
||||
}
|
||||
|
||||
@@ -249,16 +249,6 @@ RenderPass *render_layer_add_pass(RenderResult *rr,
|
||||
RE_render_result_full_channel_name(
|
||||
rpass->fullname, nullptr, rpass->name, rpass->view, rpass->chan_id, -1);
|
||||
|
||||
if (rl->exrhandle) {
|
||||
int a;
|
||||
for (a = 0; a < channels; a++) {
|
||||
char passname[EXR_PASS_MAXNAME];
|
||||
RE_render_result_full_channel_name(
|
||||
passname, nullptr, rpass->name, nullptr, rpass->chan_id, a);
|
||||
IMB_exr_add_channel(rl->exrhandle, rl->name, passname, viewname, 0, 0, nullptr, false);
|
||||
}
|
||||
}
|
||||
|
||||
BLI_addtail(&rl->passes, rpass);
|
||||
|
||||
if (allocate) {
|
||||
@@ -384,10 +374,6 @@ void render_result_passes_allocated_ensure(RenderResult *rr)
|
||||
|
||||
LISTBASE_FOREACH (RenderLayer *, rl, &rr->layers) {
|
||||
LISTBASE_FOREACH (RenderPass *, rp, &rl->passes) {
|
||||
if (rl->exrhandle != nullptr && !STREQ(rp->name, RE_PASSNAME_COMBINED)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
render_layer_allocate_pass(rr, rp);
|
||||
}
|
||||
}
|
||||
@@ -710,7 +696,7 @@ static int order_render_passes(const void *a, const void *b)
|
||||
}
|
||||
|
||||
RenderResult *render_result_new_from_exr(
|
||||
void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty)
|
||||
ExrHandle *exrhandle, const char *colorspace, bool predivide, int rectx, int recty)
|
||||
{
|
||||
RenderResult *rr = MEM_callocN<RenderResult>(__func__);
|
||||
const char *to_colorspace = IMB_colormanagement_role_colorspace_name_get(
|
||||
@@ -910,7 +896,7 @@ bool render_result_exr_file_read_path(RenderResult *rr,
|
||||
ReportList *reports,
|
||||
const char *filepath)
|
||||
{
|
||||
void *exrhandle = IMB_exr_get_handle();
|
||||
ExrHandle *exrhandle = IMB_exr_get_handle();
|
||||
int rectx, recty;
|
||||
|
||||
if (!IMB_exr_begin_read(exrhandle, filepath, &rectx, &recty, false)) {
|
||||
@@ -946,25 +932,20 @@ bool render_result_exr_file_read_path(RenderResult *rr,
|
||||
char fullname[EXR_PASS_MAXNAME];
|
||||
|
||||
for (a = 0; a < xstride; a++) {
|
||||
/* First try with layer included. */
|
||||
RE_render_result_full_channel_name(
|
||||
fullname, nullptr, rpass->name, rpass->view, rpass->chan_id, a);
|
||||
|
||||
if (IMB_exr_set_channel(exrhandle,
|
||||
rl->name,
|
||||
fullname,
|
||||
xstride,
|
||||
ystride,
|
||||
rpass->ibuf->float_buffer.data + a))
|
||||
fullname, rl->name, rpass->name, rpass->view, rpass->chan_id, a);
|
||||
if (IMB_exr_set_channel(
|
||||
exrhandle, fullname, xstride, ystride, rpass->ibuf->float_buffer.data + a))
|
||||
{
|
||||
found_channels = true;
|
||||
}
|
||||
else if (rl_single) {
|
||||
if (IMB_exr_set_channel(exrhandle,
|
||||
nullptr,
|
||||
fullname,
|
||||
xstride,
|
||||
ystride,
|
||||
rpass->ibuf->float_buffer.data + a))
|
||||
/* Then try without layer name. */
|
||||
RE_render_result_full_channel_name(
|
||||
fullname, nullptr, rpass->name, rpass->view, rpass->chan_id, a);
|
||||
if (IMB_exr_set_channel(
|
||||
exrhandle, fullname, xstride, ystride, rpass->ibuf->float_buffer.data + a))
|
||||
{
|
||||
found_channels = true;
|
||||
}
|
||||
@@ -1070,7 +1051,7 @@ bool render_result_exr_file_cache_read(Render *re)
|
||||
printf("read exr cache file: %s\n", filepath);
|
||||
|
||||
/* Try opening the file. */
|
||||
void *exrhandle = IMB_exr_get_handle();
|
||||
ExrHandle *exrhandle = IMB_exr_get_handle();
|
||||
int rectx, recty;
|
||||
|
||||
if (!IMB_exr_begin_read(exrhandle, filepath, &rectx, &recty, true)) {
|
||||
@@ -1325,7 +1306,6 @@ static RenderLayer *duplicate_render_layer(RenderLayer *rl)
|
||||
RenderLayer *new_rl = MEM_dupallocN<RenderLayer>("new render layer", *rl);
|
||||
new_rl->next = new_rl->prev = nullptr;
|
||||
new_rl->passes.first = new_rl->passes.last = nullptr;
|
||||
new_rl->exrhandle = nullptr;
|
||||
LISTBASE_FOREACH (RenderPass *, rpass, &rl->passes) {
|
||||
RenderPass *new_rpass = duplicate_render_pass(rpass);
|
||||
BLI_addtail(&new_rl->passes, new_rpass);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
struct ColorManagedDisplaySettings;
|
||||
struct ColorManagedViewSettings;
|
||||
struct ExrHandle;
|
||||
struct ImBuf;
|
||||
struct ListBase;
|
||||
struct Render;
|
||||
@@ -46,7 +47,7 @@ void render_result_passes_allocated_ensure(struct RenderResult *rr);
|
||||
* it's not a single-layer multi-view we convert this to render result.
|
||||
*/
|
||||
struct RenderResult *render_result_new_from_exr(
|
||||
void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty);
|
||||
ExrHandle *exrhandle, const char *colorspace, bool predivide, int rectx, int recty);
|
||||
|
||||
void render_result_view_new(struct RenderResult *rr, const char *viewname);
|
||||
void render_result_views_new(struct RenderResult *rr, const struct RenderData *rd);
|
||||
|
||||
Reference in New Issue
Block a user