26#include <OpenEXR/OpenEXRConfig.h>
27#define COMBINED_OPENEXR_VERSION \
28 ((10000 * OPENEXR_VERSION_MAJOR) + (100 * OPENEXR_VERSION_MINOR) + OPENEXR_VERSION_PATCH)
30#if COMBINED_OPENEXR_VERSION >= 20599
32# include <Imath/half.h>
33# include <OpenEXR/ImfFrameBuffer.h>
34# define exr_file_offset_t uint64_t
37# include <OpenEXR/half.h>
38# define exr_file_offset_t Int64
41#include <OpenEXR/Iex.h>
42#include <OpenEXR/ImfArray.h>
43#include <OpenEXR/ImfAttribute.h>
44#include <OpenEXR/ImfChannelList.h>
45#include <OpenEXR/ImfChromaticities.h>
46#include <OpenEXR/ImfCompression.h>
47#include <OpenEXR/ImfCompressionAttribute.h>
48#include <OpenEXR/ImfIO.h>
49#include <OpenEXR/ImfInputFile.h>
50#include <OpenEXR/ImfIntAttribute.h>
51#include <OpenEXR/ImfOutputFile.h>
52#include <OpenEXR/ImfPixelType.h>
53#include <OpenEXR/ImfPreviewImage.h>
54#include <OpenEXR/ImfRgbaFile.h>
55#include <OpenEXR/ImfStandardAttributes.h>
56#include <OpenEXR/ImfStringAttribute.h>
57#include <OpenEXR/ImfVersion.h>
60#include <OpenEXR/ImfInputPart.h>
61#include <OpenEXR/ImfMultiPartInputFile.h>
62#include <OpenEXR/ImfMultiPartOutputFile.h>
63#include <OpenEXR/ImfMultiView.h>
64#include <OpenEXR/ImfOutputPart.h>
65#include <OpenEXR/ImfPartHelper.h>
66#include <OpenEXR/ImfPartType.h>
67#include <OpenEXR/ImfTiledOutputPart.h>
108using namespace Imath;
118 {1.0f, 0.0f}, {0.0f, 1.0f}, {0.0f, 0.0f}, {1.0f / 3.0f, 1.0f / 3.0f}};
121 {0.7347f, 0.2653f}, {0.0f, 1.0f}, {0.0001f, -0.077f}, {0.32168f, 0.33767f}};
127 IMemStream(
uchar *exrbuf,
size_t exrsize) : IStream(
"<memory>"), _exrpos(0), _exrsize(exrsize)
132 bool read(
char c[],
int n)
override
134 if (n + _exrpos <= _exrsize) {
135 memcpy(c, (
void *)(&_exrbuf[_exrpos]), n);
143 if (n + _exrpos < _exrsize + 8192) {
144 const size_t remainder = _exrsize - _exrpos;
146 memcpy(c, (
void *)(&_exrbuf[_exrpos]), remainder);
147 memset(c + remainder, 0, n - remainder);
182 throw IEX_NAMESPACE::InputExc(
"file not found");
187 if (_mmap_file ==
nullptr) {
188 throw IEX_NAMESPACE::InputExc(
"BLI_mmap_open failed");
201 bool read(
char c[],
int n)
override
203 if (_exrpos + n > _exrsize) {
204 throw Iex::InputExc(
"Unexpected end of file.");
208 throw Iex::InputExc(
"Error reading file.");
213 return _exrpos < _exrsize;
241 ifs.open(wfilepath, std::ios_base::binary);
244 ifs.open(filepath, std::ios_base::binary);
248 Iex::throwErrnoExc();
252 bool read(
char c[],
int n)
override
255 throw Iex::InputExc(
"Unexpected end of file.");
260 return check_error();
265 return std::streamoff(ifs.tellg());
284 Iex::throwErrnoExc();
302 void write(
const char c[],
int n)
override
304 ensure_size(offset + n);
305 memcpy(ibuf->encoded_buffer.data + offset, c, n);
307 ibuf->encoded_size += n;
327 throw Iex::ErrnoExc(
"Out of memory.");
345 ofs.open(wfilepath, std::ios_base::binary);
348 ofs.open(filepath, std::ios_base::binary);
352 Iex::throwErrnoExc();
356 void write(
const char c[],
int n)
override
365 return std::streamoff(ofs.tellp());
379 Iex::throwErrnoExc();
382 throw Iex::ErrnoExc(
"File output failed.");
410 return Imf::isImfMagic((
const char *)mem);
420 constexpr int x0 = 100, y0 = 0;
421 constexpr int x1 = 90, y1 = 45;
422 q = y0 + (q - x0) * (y1 - y0) / (x1 - x0);
428 switch (compression) {
430 header->compression() = NO_COMPRESSION;
433 header->compression() = PXR24_COMPRESSION;
436 header->compression() = ZIP_COMPRESSION;
439 header->compression() = PIZ_COMPRESSION;
442 header->compression() = RLE_COMPRESSION;
445 header->compression() = ZIPS_COMPRESSION;
448 header->compression() = B44_COMPRESSION;
451 header->compression() = B44A_COMPRESSION;
453#if OPENEXR_VERSION_MAJOR > 2 || (OPENEXR_VERSION_MAJOR >= 2 && OPENEXR_VERSION_MINOR >= 2)
455 header->compression() = DWAA_COMPRESSION;
459 header->compression() = DWAB_COMPRESSION;
464 header->compression() = ZIP_COMPRESSION;
471 switch (header.compression()) {
474 case RLE_COMPRESSION:
476 case ZIPS_COMPRESSION:
478 case ZIP_COMPRESSION:
480 case PIZ_COMPRESSION:
482 case PXR24_COMPRESSION:
484 case B44_COMPRESSION:
486 case B44A_COMPRESSION:
488 case DWAA_COMPRESSION:
490 case DWAB_COMPRESSION:
492 case NUM_COMPRESSION_METHODS:
510 if ((prop->type ==
IDP_STRING) && !
STR_ELEM(prop->name,
"compression",
"colorInteropID")) {
511 header->insert(prop->name, StringAttribute(
IDP_string_get(prop)));
516 if (ppm[0] > 0.0 && ppm[1] > 0.0) {
518 addXDensity(*header, ppm[0] * 0.0254);
525 if (colorspace ==
nullptr) {
534 if (aces_colorspace &&
STREQ(aces_colorspace, ibuf_colorspace)) {
542 header->insert(
"colorInteropID", TypedAttribute<std::string>(interop_id));
552 if (colorspace ==
nullptr) {
565 const char *propname,
570 header->insert(propname, StringAttribute(prop));
575 const int channels = ibuf->
channels;
576 const bool is_alpha = (channels >= 4) && (ibuf->
planes == 32);
577 const int width = ibuf->
x;
578 const int height = ibuf->
y;
579 OStream *file_stream =
nullptr;
582 Header header(width, height);
590 header.channels().insert(
"R", Channel(
HALF));
591 header.channels().insert(
"G", Channel(
HALF));
592 header.channels().insert(
"B", Channel(
HALF));
594 header.channels().insert(
"A", Channel(
HALF));
597 FrameBuffer frameBuffer;
606 OutputFile file(*file_stream, header);
609 std::unique_ptr<RGBAZ[]> pixels = std::unique_ptr<RGBAZ[]>(
new RGBAZ[
int64_t(height) * width]);
610 RGBAZ *to = pixels.get();
611 int xstride =
sizeof(
RGBAZ);
612 int ystride = xstride * width;
615 frameBuffer.insert(
"R", Slice(
HALF, (
char *)&to->
r, xstride, ystride));
616 frameBuffer.insert(
"G", Slice(
HALF, (
char *)&to->
g, xstride, ystride));
617 frameBuffer.insert(
"B", Slice(
HALF, (
char *)&to->
b, xstride, ystride));
619 frameBuffer.insert(
"A", Slice(
HALF, (
char *)&to->
a, xstride, ystride));
624 for (
int i = ibuf->
y - 1;
i >= 0;
i--) {
627 for (
int j = ibuf->
x; j > 0; j--) {
640 for (
int i = ibuf->
y - 1;
i >= 0;
i--) {
643 for (
int j = ibuf->
x; j > 0; j--) {
647 to->
a = channels >= 4 ?
float(from[3]) / 255.0f : 1.0f;
654 CLOG_DEBUG(&
LOG,
"Writing OpenEXR file of height %d", height);
656 file.setFrameBuffer(frameBuffer);
657 file.writePixels(height);
659 catch (
const std::exception &exc) {
678 const int channels = ibuf->
channels;
679 const bool is_alpha = (channels >= 4) && (ibuf->
planes == 32);
680 const int width = ibuf->
x;
681 const int height = ibuf->
y;
682 OStream *file_stream =
nullptr;
685 Header header(width, height);
693 header.channels().insert(
"R", Channel(Imf::FLOAT));
694 header.channels().insert(
"G", Channel(Imf::FLOAT));
695 header.channels().insert(
"B", Channel(Imf::FLOAT));
697 header.channels().insert(
"A", Channel(Imf::FLOAT));
700 FrameBuffer frameBuffer;
709 OutputFile file(*file_stream, header);
711 int xstride =
sizeof(
float) * channels;
712 int ystride = -xstride * width;
715 float *rect[4] = {
nullptr,
nullptr,
nullptr,
nullptr};
717 rect[1] = (channels >= 2) ? rect[0] + 1 : rect[0];
718 rect[2] = (channels >= 3) ? rect[0] + 2 : rect[0];
719 rect[3] = (channels >= 4) ?
723 frameBuffer.insert(
"R", Slice(Imf::FLOAT, (
char *)rect[0], xstride, ystride));
724 frameBuffer.insert(
"G", Slice(Imf::FLOAT, (
char *)rect[1], xstride, ystride));
725 frameBuffer.insert(
"B", Slice(Imf::FLOAT, (
char *)rect[2], xstride, ystride));
727 frameBuffer.insert(
"A", Slice(Imf::FLOAT, (
char *)rect[3], xstride, ystride));
730 file.setFrameBuffer(frameBuffer);
731 file.writePixels(height);
733 catch (
const std::exception &exc) {
826 MultiPartInputFile *
ifile =
nullptr;
847 const bool parse_layers);
853 ExrHandle *handle = MEM_new<ExrHandle>(
"ExrHandle");
868 for (StringVector::const_iterator
i = views.begin();
count < views.size(); ++
i) {
884 for (
int p = 0; p < file.parts(); p++) {
886 if (file.header(p).hasView()) {
887 const std::string &
view = file.header(p).view();
889 views.push_back(
view);
894 if (hasMultiView(file.header(p))) {
895 StringVector multiview = multiView(file.header(p));
896 for (
const std::string &
view : multiview) {
898 views.push_back(
view);
918 std::string part_name;
920 part_name = layerpassname;
922 if (part_name.empty()) {
923 part_name = viewname;
926 part_name = part_name +
"-" + viewname;
936 for (
size_t channel = 0; channel < channelnames.
size(); channel++) {
938 std::string full_name = layerpassname;
940 if (full_name.empty()) {
941 full_name = viewname;
944 full_name = full_name +
"." + viewname;
947 if (full_name.empty()) {
948 full_name = channelnames[channel];
951 full_name = full_name +
"." + channelnames[channel];
957 echan.
name = full_name;
960 echan.
view = viewname;
965 echan.
rect = rect + channel;
979 header.insert(
"BlenderMultiChannel", StringAttribute(
"Blender V2.55.1 and newer"));
982 addMultiView(header, handle->
views);
989 const char *filepath,
998 CLOG_ERROR(&
LOG,
"Attempt to save MultiLayer without layers.");
1002 Header header(width, height);
1004 handle->
width = width;
1035 Header part_header = header;
1040 if (!echan.
view.empty()) {
1041 part_header.insert(
"view", StringAttribute(echan.
view));
1043 part_header.insert(
"type", StringAttribute(SCANLINEIMAGE));
1053 part_headers.
append(std::move(part_header));
1057 part_headers.
last().channels().insert(echan.
name,
1068 handle->
mpofile =
new MultiPartOutputFile(
1075 catch (
const std::exception &exc) {
1078 delete handle->
ofile;
1082 handle->
ofile =
nullptr;
1089 delete handle->
ofile;
1093 handle->
ofile =
nullptr;
1098 return (handle->
ofile !=
nullptr || handle->
mpofile !=
nullptr);
1115 delete handle->
ifile;
1118 handle->
ifile =
nullptr;
1122 if (!handle->
ifile) {
1126 Box2i dw = handle->
ifile->header(0).dataWindow();
1127 handle->
width = *width = dw.max.x - dw.min.x + 1;
1128 handle->
height = *height = dw.max.y - dw.min.y + 1;
1149 if (echan.
name == full_name) {
1163 CLOG_ERROR(&
LOG,
"Attempt to save MultiLayer without layers.");
1167 const size_t num_pixels = size_t(handle->
width) * handle->
height;
1168 const size_t num_parts = (handle->
mpofile) ? handle->
mpofile->parts() : 1;
1170 for (
size_t part_num = 0; part_num < num_parts; part_num++) {
1171 const std::string &part_id = (handle->
mpofile) ? handle->
mpofile->header(part_num).name() :
"";
1173 int num_half_channels = 0;
1176 num_half_channels++;
1181 half *current_rect_half =
nullptr;
1182 if (num_half_channels > 0) {
1183 rect_half.
resize(
size_t(num_half_channels) * num_pixels);
1184 current_rect_half = rect_half.
data();
1187 FrameBuffer frameBuffer;
1196 const float *rect = echan.
rect;
1197 half *cur = current_rect_half;
1198 for (
size_t i = 0;
i < num_pixels;
i++, cur++) {
1201 half *rect_to_write = current_rect_half + (handle->
height - 1L) * handle->
width;
1204 Slice(Imf::HALF, (
char *)rect_to_write,
sizeof(
half), -handle->
width *
sizeof(
half)));
1205 current_rect_half += num_pixels;
1209 frameBuffer.insert(echan.
name,
1212 echan.
xstride *
sizeof(
float),
1213 -echan.
ystride *
sizeof(
float)));
1219 OutputPart part(*handle->
mpofile, part_num);
1220 part.setFrameBuffer(frameBuffer);
1221 part.writePixels(handle->
height);
1224 handle->
ofile->setFrameBuffer(frameBuffer);
1228 catch (
const std::exception &exc) {
1239 int numparts = handle->
ifile->parts();
1242 const StringAttribute *ta = handle->
ifile->header(0).findTypedAttribute<StringAttribute>(
1243 "BlenderMultiChannel");
1246 short flip = (ta &&
STRPREFIX(ta->value().c_str(),
"Blender V2.43"));
1249 "\nIMB_exr_read_channels\n%s %-6s %-22s "
1250 "\"%s\"\n---------------------------------------------------------------------",
1256 for (
int i = 0;
i < numparts;
i++) {
1260 Box2i dw = header.dataWindow();
1263 FrameBuffer frameBuffer;
1271 "%d %-6s %-22s \"%s\"\n",
1278 float *rect = echan.
rect;
1284 rect -= echan.
xstride * (dw.min.x - dw.min.y * handle->
width);
1291 rect -= echan.
xstride * (dw.min.x + dw.min.y * handle->
width);
1294 frameBuffer.insert(echan.
internal_name, Slice(Imf::FLOAT, (
char *)rect, xstride, ystride));
1300 in.setFrameBuffer(frameBuffer);
1301 CLOG_DEBUG(&
LOG,
"readPixels:readPixels[%d]: min.y: %d, max.y: %d",
i, dw.min.y, dw.max.y);
1302 in.readPixels(dw.min.y, dw.max.y);
1304 catch (
const std::exception &exc) {
1317 void *(*addview)(
void *base,
const char *
str),
1318 void *(*addlayer)(
void *base,
const char *
str),
1319 void (*addpass)(
void *base,
1324 const char *chan_id,
1328 if (handle->
views.empty()) {
1333 for (
const std::string &view_name : handle->
views) {
1334 addview(base, view_name.c_str());
1339 CLOG_WARN(&
LOG,
"Cannot convert multilayer, no layers in handle");
1344 void *laybase = addlayer(base, lay.name.c_str());
1346 for (
ExrPass &pass : lay.passes) {
1354 pass.
rect =
nullptr;
1362 delete handle->
ifile;
1364 delete handle->
ofile;
1376 const char delims[] = {
'.',
'\0'};
1385 return int(end - *token);
1390 const char *channelname,
1391 const bool has_xyz_channels)
1398 else if (echan.
chan_id ==
'Y' && !has_xyz_channels) {
1399 BLI_strncpy(passname, channelname, passname_maxncpy);
1401 else if (
ELEM(echan.
chan_id,
'R',
'G',
'B',
'A',
'V',
'X',
'Y',
'Z')) {
1402 BLI_strncpy(passname,
"Combined", passname_maxncpy);
1405 BLI_strncpy(passname, channelname, passname_maxncpy);
1411 const char *channelname,
1421 BLI_strncpy(passname, channelname, passname_maxncpy);
1427 bool has_xyz_channels)
1430 const char *
name = echan.
name.c_str();
1431 const char *end =
name + strlen(
name);
1456 BLI_strncpy(channelname, token, std::min(
len + 1,
sizeof(channelname)));
1473 if (
ELEM(chan_id,
'X',
'Y',
'Z',
'R',
'G',
'B',
'U',
'V',
'A')) {
1530 if (lay.
name == layname) {
1544 if (pass.
name == passname) {
1550 pass.
name = passname;
1552 if (
STREQ(passname,
"Combined")) {
1563 bool x_found =
false;
1564 bool y_found =
false;
1565 bool z_found =
false;
1578 return x_found && y_found && z_found;
1584 const bool parse_layers)
1590 for (
int p = 0; p < file.parts(); p++) {
1591 const ChannelList &c = file.header(p).channels();
1596 if (colorspace ==
nullptr) {
1597 colorspace = global_colorspace;
1603 const bool has_multiple_views_in_part = hasMultiView(file.header(p));
1604 StringVector views_in_part;
1605 if (has_multiple_views_in_part) {
1606 views_in_part = multiView(file.header(p));
1609 if (file.header(p).hasView()) {
1610 part_view = file.header(p).view();
1615 if (parse_layers && file.header(p).hasName()) {
1616 part_name = file.header(p).name();
1620 if (!has_multiple_views_in_part) {
1621 if (part_name.
endswith(
"." + part_view)) {
1624 else if (part_name.
endswith(
"-" + part_view)) {
1631 for (ChannelList::ConstIterator
i = c.begin();
i != c.end();
i++) {
1633 echan.
name = std::string(
i.name());
1636 if (has_multiple_views_in_part) {
1637 echan.
view = viewFromChannelName(echan.
name, views_in_part);
1641 echan.
view = part_view;
1648 echan.
name = part_name +
"." + echan.
name;
1654 channels.
append(std::move(echan));
1673 const char *
view = echan.
view.c_str();
1674 std::string internal_name = passname;
1676 if (
view[0] !=
'\0') {
1713 memset(lookup, 0,
sizeof(lookup));
1720 lookup[
uint(
'R')] = 0;
1721 lookup[
uint(
'G')] = 1;
1722 lookup[
uint(
'B')] = 2;
1723 lookup[
uint(
'A')] = 3;
1728 lookup[
uint(
'X')] = 0;
1729 lookup[
uint(
'Y')] = 1;
1730 lookup[
uint(
'Z')] = 2;
1731 lookup[
uint(
'W')] = 3;
1734 lookup[
uint(
'U')] = 0;
1735 lookup[
uint(
'V')] = 1;
1736 lookup[
uint(
'A')] = 2;
1738 for (
int a = 0; a < pass.
totchan; a++) {
1747 for (
int a = 0; a < pass.
totchan; a++) {
1765 MultiPartInputFile &file,
1772 handle->
ifile = &file;
1774 handle->
width = width;
1789 int numparts = file.parts();
1790 if (numparts == 1 && hasMultiView(file.header(0))) {
1791 const StringVector views = multiView(file.header(0));
1793 CLOG_DEBUG(&
LOG,
"Default view: %s", defaultViewName(views).c_str());
1794 for (
const std::string &
view : views) {
1798 else if (numparts > 1) {
1800 for (
int i = 0;
i < numparts;
i++) {
1801 if (file.header(
i).hasView()) {
1802 CLOG_DEBUG(&
LOG,
"Part %d: view = \"%s\"",
i, file.header(
i).view().c_str());
1807 for (
int j = 0; j < numparts; j++) {
1808 const ChannelList &channels = file.header(j).channels();
1809 for (ChannelList::ConstIterator
i = channels.begin();
i != channels.end(); ++
i) {
1810 const Channel &channel =
i.channel();
1811 CLOG_DEBUG(&
LOG,
"Found channel %s of type %d",
i.name(), channel.type);
1819 const ChannelList &channels = file.header(0).
channels();
1821 for (ChannelList::ConstIterator
i = channels.begin();
i != channels.end(); ++
i) {
1823 const char *
str =
i.name();
1834static int exr_has_rgb(MultiPartInputFile &file,
const char *rgb_channels[3])
1838 static const char *channel_names[] = {
1839 "V",
"R",
"Red",
"G",
"Green",
"B",
"Blue",
"AR",
"RA",
"AG",
"GA",
"AB",
"BA",
nullptr};
1841 const Header &header = file.header(0);
1842 int num_channels = 0;
1844 for (
int i = 0; channel_names[
i];
i++) {
1846 std::string lower_case_name = std::string(channel_names[
i]);
1847 std::transform(lower_case_name.begin(),
1848 lower_case_name.end(),
1849 lower_case_name.begin(),
1850 [](
uchar c) { return std::tolower(c); });
1852 if (header.channels().findChannel(channel_names[
i]) ||
1853 header.channels().findChannel(lower_case_name))
1855 rgb_channels[num_channels++] = channel_names[
i];
1856 if (num_channels == 3) {
1862 return num_channels;
1870 const Header &header = file.header(0);
1871 return header.channels().findChannel(
"Y") !=
nullptr;
1876 const Header &header = file.header(0);
1877 return header.channels().findChannel(
"BY") !=
nullptr &&
1878 header.channels().findChannel(
"RY") !=
nullptr;
1883 const Header &header = file.header(0);
1884 return !(header.channels().findChannel(
"A") ==
nullptr);
1889 const Header &header = file.header(0);
1890 return (header.channels().findChannel(
"X") !=
nullptr ||
1891 header.channels().findChannel(
"x") !=
nullptr) &&
1892 (header.channels().findChannel(
"Y") !=
nullptr ||
1893 header.channels().findChannel(
"y") !=
nullptr) &&
1894 (header.channels().findChannel(
"Z") !=
nullptr ||
1895 header.channels().findChannel(
"z") !=
nullptr);
1900 const ChannelList &channels = file.header(0).channels();
1901 for (ChannelList::ConstIterator
i = channels.begin();
i != channels.end(); ++
i) {
1902 const Channel &channel =
i.channel();
1903 if (channel.type !=
HALF) {
1912 const ChannelList &channels = file.header(0).channels();
1913 std::set<std::string> layerNames;
1917 channels.layers(layerNames);
1919 return !layerNames.empty();
1924 for (
int p = 0; p < file.parts(); p++) {
1925 if (hasMultiView(file.header(p))) {
1935 return file.parts() > 1;
1965 const float tolerance_v = 0.000001f;
1966 return (test_v < (ref_v + tolerance_v)) && (test_v > (ref_v - tolerance_v));
1971 const Imf::Chromaticities &
b)
1988 const IntAttribute *header_aces_container = header.findTypedAttribute<IntAttribute>(
1989 "acesImageContainerFlag");
1990 const ChromaticitiesAttribute *header_chromaticities =
1991 header.findTypedAttribute<ChromaticitiesAttribute>(
"chromaticities");
1993 if ((header_aces_container && header_aces_container->value() == 1) ||
1994 (header_chromaticities &&
1999 if (known_colorspace) {
2005 const StringAttribute *header_interop_id = header.findTypedAttribute<StringAttribute>(
2009 if (header_interop_id && !header_interop_id->value().empty()) {
2011 header_interop_id->value());
2020 if (header_chromaticities &&
2037 const Header &header = file.header(0);
2038 if (!hasXDensity(header)) {
2041 ppm[0] = double(xDensity(header)) / 0.0254;
2042 ppm[1] = ppm[0] * double(header.pixelAspectRatio());
2053 ImBuf *ibuf =
nullptr;
2055 MultiPartInputFile *file =
nullptr;
2065 file =
new MultiPartInputFile(*membuf);
2067 const Header &file_header = file->header(0);
2068 Box2i dw = file_header.dataWindow();
2069 const size_t width = dw.max.x - dw.min.x + 1;
2070 const size_t height = dw.max.y - dw.min.y + 1;
2072 CLOG_DEBUG(&
LOG,
"Image data window %d %d %d %d", dw.min.x, dw.min.y, dw.max.x, dw.max.y);
2100 Header::ConstIterator iter;
2103 for (iter = file_header.begin(); iter != file_header.end(); iter++) {
2104 const StringAttribute *attr = file_header.findTypedAttribute<StringAttribute>(
2125 const char *rgb_channels[3];
2126 const int num_rgb_channels =
exr_has_rgb(*file, rgb_channels);
2129 FrameBuffer frameBuffer;
2131 size_t xstride =
sizeof(
float[4]);
2132 size_t ystride = -xstride * width;
2141 first += 4 * (height - 1) * width;
2143 if (num_rgb_channels > 0) {
2144 for (
int i = 0;
i < num_rgb_channels;
i++) {
2146 Slice(Imf::FLOAT, (
char *)(first +
i), xstride, ystride));
2151 Slice(Imf::FLOAT, (
char *)first, xstride, ystride));
2153 Slice(Imf::FLOAT, (
char *)(first + 1), xstride, ystride));
2155 Slice(Imf::FLOAT, (
char *)(first + 2), xstride, ystride));
2157 else if (has_luma) {
2159 Slice(Imf::FLOAT, (
char *)first, xstride, ystride));
2162 Slice(Imf::FLOAT, (
char *)(first + 1), xstride, ystride, 1, 1, 0.5f));
2165 Slice(Imf::FLOAT, (
char *)(first + 2), xstride, ystride, 1, 1, 0.5f));
2170 Slice(Imf::FLOAT, (
char *)(first + 3), xstride, ystride, 1, 1, 1.0f));
2172 InputPart
in(*file, 0);
2173 in.setFrameBuffer(frameBuffer);
2174 in.readPixels(dw.min.y, dw.max.y);
2184 if (
flag & IM_rect) {
2189 if (num_rgb_channels == 0 && has_luma &&
exr_has_chroma(*file)) {
2190 for (
size_t a = 0; a < size_t(ibuf->
x) * ibuf->
y; a++) {
2201 else if (!has_xyz && num_rgb_channels <= 1) {
2203 for (
size_t a = 0; a < size_t(ibuf->
x) * ibuf->
y; a++) {
2205 color[1] = color[0];
2206 color[2] = color[0];
2226 catch (
const std::exception &exc) {
2250 const size_t max_thumb_size,
2255 ImBuf *ibuf =
nullptr;
2256 IStream *stream =
nullptr;
2257 Imf::RgbaInputFile *file =
nullptr;
2275 file =
new RgbaInputFile(*stream, 1);
2277 if (!file->isComplete()) {
2283 Imath::Box2i dw = file->dataWindow();
2284 int source_w = dw.max.x - dw.min.x + 1;
2285 int source_h = dw.max.y - dw.min.y + 1;
2286 *r_width = source_w;
2287 *r_height = source_h;
2289 const Header &file_header = file->header();
2292 if (file_header.hasPreviewImage()) {
2293 const Imf::PreviewImage &preview = file->header().previewImage();
2295 (uint8_t *)preview.pixels(),
nullptr, preview.width(), preview.height(), 4);
2306 float scale_factor = std::min(
float(max_thumb_size) /
float(source_w),
2307 float(max_thumb_size) /
float(source_h));
2308 int dest_w = std::max(
int(source_w * scale_factor), 1);
2309 int dest_h = std::max(
int(source_h * scale_factor), 1);
2314 Imf::Array<Imf::Rgba> pixels(source_w);
2317 for (
int h = 0; h < dest_h; h++) {
2320 int source_y = int(
float(h) / scale_factor) + dw.min.y;
2321 file->setFrameBuffer(&pixels[0] - dw.min.x - source_y * source_w, 1, source_w);
2322 file->readPixels(source_y);
2324 for (
int w = 0;
w < dest_w;
w++) {
2326 int source_x = int(std::min<int>((
w / scale_factor), dw.max.x - 1));
2328 dest_px[0] = pixels[source_x].r;
2329 dest_px[1] = pixels[source_x].g;
2330 dest_px[2] = pixels[source_x].b;
2331 dest_px[3] = pixels[source_x].a;
2335 if (file->lineOrder() == INCREASING_Y) {
2345 catch (
const std::exception &exc) {
2373 Imf::staticInitialize();
2380 Imf::setGlobalThreadCount(0);
const char * BKE_blender_version_string(void)
#define IDP_string_get(prop)
void BKE_stamp_info_callback(void *data, StampData *stamp_data, StampCallback callback, bool noskip)
blender::ocio::ColorSpace ColorSpace
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
size_t BLI_file_size(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
int BLI_open(const char *filepath, int oflag, int pmode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
void BLI_kdtree_nd_ free(KDTree *tree)
#define LISTBASE_FOREACH(type, var, list)
MINLINE float clamp_f(float value, float min, float max)
void ycc_to_rgb(float y, float cb, float cr, float *r_r, float *r_g, float *r_b, int colorspace)
float srgb_to_linearrgb(float c)
#define BLI_YCC_ITU_BT709
bool BLI_mmap_read(BLI_mmap_file *file, void *dest, size_t offset, size_t length) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void BLI_mmap_free(BLI_mmap_file *file) ATTR_NONNULL(1)
BLI_mmap_file * BLI_mmap_open(int fd) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
size_t BLI_mmap_get_length(const BLI_mmap_file *file) ATTR_WARN_UNUSED_RESULT
#define SNPRINTF(dst, format,...)
char BLI_toupper_ascii(const char c) ATTR_WARN_UNUSED_RESULT
int char char int BLI_strcasecmp(const char *s1, const char *s2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
char * STRNCPY(char(&dst)[N], const char *src)
int bool bool bool size_t size_t size_t BLI_str_partition_ex(const char *str, const char *end, const char delim[], const char **sep, const char **suf, bool from_right) ATTR_NONNULL(1
int BLI_strcaseeq(const char *a, const char *b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define STRNCPY_UTF8(dst, src)
int BLI_system_thread_count(void)
#define CLOG_ERROR(clg_ref,...)
#define CLOG_DEBUG(clg_ref,...)
#define CLOG_WARN(clg_ref,...)
#define CLOG_CHECK(clg_ref, verbose_level,...)
blender::StringRefNull IMB_colormanagement_space_get_interop_id(const ColorSpace *colorspace)
const ColorSpace * IMB_colormanagement_space_get_named(const char *name)
bool IMB_colormanagement_space_is_data(const ColorSpace *colorspace)
@ COLOR_ROLE_ACES_INTERCHANGE
@ COLOR_ROLE_SCENE_LINEAR
const char * IMB_colormanagement_colorspace_get_name(const ColorSpace *colorspace)
const char * IMB_colormanagement_role_colorspace_name_get(int role)
const ColorSpace * IMB_colormanagement_space_from_interop_id(blender::StringRefNull interop_id)
void IMB_flipy(ImBuf *ibuf)
ImBuf * IMB_allocFromBuffer(const uint8_t *byte_buffer, const float *float_buffer, unsigned int w, unsigned int h, unsigned int channels)
void IMB_byte_from_float(ImBuf *ibuf)
void IMB_freeImBuf(ImBuf *ibuf)
ImBuf * IMB_allocImBuf(unsigned int x, unsigned int y, unsigned char planes, unsigned int flags)
bool IMB_alloc_float_pixels(ImBuf *ibuf, const unsigned int channels, bool initialize_pixels=true)
#define OPENEXR_CODEC_MASK
Read Guarded memory(de)allocation.
bool imb_enlargeencodedbufferImBuf(ImBuf *ibuf)
bool imb_addencodedbufferImBuf(ImBuf *ibuf)
BMesh const char void * data
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
void seekg(exr_file_offset_t pos) override
bool read(char c[], int n) override
exr_file_offset_t tellg() override
IFileStream(const char *filepath)
void seekg(exr_file_offset_t pos) override
IMMapStream(const char *filepath)
exr_file_offset_t tellg() override
bool read(char c[], int n) override
IMemStream(uchar *exrbuf, size_t exrsize)
void seekg(exr_file_offset_t pos) override
bool read(char c[], int n) override
exr_file_offset_t tellg() override
OFileStream(const char *filepath)
exr_file_offset_t tellp() override
void seekp(exr_file_offset_t pos) override
void write(const char c[], int n) override
void write(const char c[], int n) override
void seekp(exr_file_offset_t pos) override
exr_file_offset_t tellp() override
void prepend(const T &value)
void append(const T &value)
const T & last(const int64_t n=0) const
void append_as(ForwardValue &&...value)
constexpr bool is_empty() const
constexpr bool startswith(StringRef prefix) const
constexpr bool endswith(StringRef suffix) const
constexpr int64_t size() const
constexpr const char * c_str() const
constexpr StringRef drop_known_suffix(StringRef suffix) const
void append(const T &value)
const T & last(const int64_t n=0) const
void resize(const int64_t new_size)
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
void MEM_freeN(void *vmemh)
static bool parse_channels(const ImageSpec &in_spec, vector< MergeImageLayer > &layers, string &error)
T clamp(const T &a, const T &min, const T &max)
T safe_divide(const T &a, const T &b)
static Imf::Chromaticities CHROMATICITIES_ACES_2065_1
void IMB_exr_add_channels(ExrHandle *handle, blender::StringRefNull layerpassname, blender::StringRefNull channelnames, blender::StringRefNull viewname, blender::StringRefNull colorspace, size_t xstride, size_t ystride, float *rect, bool use_half_float)
bool IMB_exr_begin_write(ExrHandle *handle, const char *filepath, int width, int height, const double ppm[2], int compress, int quality, const StampData *stamp)
static void imb_exr_pass_name_from_channel_name(char *passname, const ExrChannel &, const char *channelname, const bool)
static StringVector imb_exr_get_views(MultiPartInputFile &file)
static bool exr_has_multiview(MultiPartInputFile &file)
static void openexr_header_metadata_global(Header *header, IDProperty *metadata, const double ppm[2])
void IMB_exr_close(ExrHandle *handle)
static bool exr_has_xyz(MultiPartInputFile &file)
static void openexr_header_metadata_callback(void *data, const char *propname, char *prop, int)
static int imb_exr_get_multiView_id(StringVector &views, const std::string &name)
ExrHandle * IMB_exr_get_handle(const bool write_multipart)
bool IMB_exr_begin_read(ExrHandle *handle, const char *filepath, int *width, int *height, const bool parse_channels)
static bool imb_save_openexr_float(ImBuf *ibuf, const char *filepath, const int flags)
static ExrHandle * imb_exr_begin_read_mem(IStream &file_stream, MultiPartInputFile &file, int width, int height)
static bool exr_is_half_float(MultiPartInputFile &file)
void IMB_exr_write_channels(ExrHandle *handle)
static bool imb_exr_is_multi(MultiPartInputFile &file)
static bool exr_has_luma(MultiPartInputFile &file)
ImBuf * imb_load_filepath_thumbnail_openexr(const char *filepath, const int, const size_t max_thumb_size, ImFileColorSpace &r_colorspace, size_t *r_width, size_t *r_height)
static int exr_has_rgb(MultiPartInputFile &file, const char *rgb_channels[3])
static bool exr_get_ppm(MultiPartInputFile &file, double ppm[2])
static bool imb_check_chromaticity_matches(const Imf::Chromaticities &a, const Imf::Chromaticities &b)
static int imb_exr_split_token(const char *str, const char *end, const char **token)
static void imb_exr_set_known_colorspace(const Header &header, ImFileColorSpace &r_colorspace)
static int openexr_jpg_like_quality_to_dwa_quality(int q)
#define exr_file_offset_t
static ExrPass * imb_exr_get_pass(ExrLayer &lay, const char *passname)
static bool exr_has_chroma(MultiPartInputFile &file)
static bool imb_exr_is_multilayer_file(MultiPartInputFile &file)
static blender::Vector< ExrChannel > exr_channels_in_multi_part_file(const MultiPartInputFile &file, const bool parse_layers)
bool imb_save_openexr(ImBuf *ibuf, const char *filepath, int flags)
bool imb_is_a_openexr(const uchar *mem, const size_t size)
static void openexr_header_metadata_colorspace(Header *header, const ColorSpace *colorspace)
static half float_to_half_safe(const float value)
ImBuf * imb_load_openexr(const uchar *mem, size_t size, int flags, ImFileColorSpace &r_colorspace)
static void openexr_header_compression(Header *header, int compression, int quality)
bool IMB_exr_has_multilayer(ExrHandle *handle)
static void imb_exr_pass_name_from_channel(char *passname, const ExrChannel &echan, const char *channelname, const bool has_xyz_channels)
void IMB_exr_multilayer_convert(ExrHandle *handle, void *base, void *(*addview)(void *base, const char *str), void *(*addlayer)(void *base, const char *str), void(*addpass)(void *base, void *lay, const char *str, float *rect, int totchan, const char *chan_id, const char *view))
static void openexr_header_metadata_multi(ExrHandle *handle, Header &header, const double ppm[2], const StampData *stamp)
bool IMB_exr_get_ppm(ExrHandle *handle, double ppm[2])
static int imb_exr_split_channel_name(ExrChannel &echan, char *layname, char *passname, bool has_xyz_channels)
static bool imb_check_chromaticity_val(float test_v, float ref_v)
static bool exr_has_multipart_file(MultiPartInputFile &file)
static void exr_print_filecontents(MultiPartInputFile &file)
void IMB_exr_read_channels(ExrHandle *handle)
static bool exr_has_xyz_channels(ExrHandle *exr_handle)
static int openexr_header_get_compression(const Header &header)
static Imf::Chromaticities CHROMATICITIES_XYZ_E
static bool imb_save_openexr_half(ImBuf *ibuf, const char *filepath, const int flags)
bool IMB_exr_set_channel(ExrHandle *handle, blender::StringRefNull full_name, int xstride, int ystride, float *rect)
static ExrLayer * imb_exr_get_layer(ExrHandle *handle, const char *layname)
static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *handle)
void IMB_exr_add_view(ExrHandle *handle, const char *name)
static const ColorSpace * imb_exr_part_colorspace(const Header &header)
static const char * exr_rgba_channelname(MultiPartInputFile &file, const char *chan)
static bool exr_has_alpha(MultiPartInputFile &file)
const ColorSpace * colorspace
std::string internal_name
OFileStream * ofile_stream
blender::Vector< ExrChannel > channels
MultiPartInputFile * ifile
blender::Vector< ExrLayer > layers
MultiPartOutputFile * mpofile
bool has_layer_pass_names
blender::Vector< ExrPass > passes
std::string internal_name
char chan_id[EXR_PASS_MAXCHAN]
ExrChannel * chan[EXR_PASS_MAXCHAN]
const ColorSpace * colorspace
const ColorSpace * colorspace
ImBufFloatBuffer float_buffer
ImbFormatOptions foptions
ImBufByteBuffer byte_buffer
unsigned int encoded_buffer_size
unsigned int encoded_size
char metadata_colorspace[IM_MAX_SPACE]
wchar_t * alloc_utf16_from_8(const char *in8, size_t add)