Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions src/iconvert/iconvert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -473,16 +473,37 @@ convert_file(const std::string& in_filename, const std::string& out_filename)
"iconvert ERROR copying \"{}\" to \"{}\" :\n\t{}\n",
in_filename, out_filename, out->geterror());
} else {
// Need to do it by hand for some reason. Future expansion in which
// only a subset of channels are copied, or some such.
std::vector<char> pixels((size_t)outspec.image_bytes(true));
// Need to do it by hand for some reason. A format of UNKNOWN
// requests a per-channel native copy to preserve mixed
// per-channel formats. That is only valid if the output
// actually stores each channel in the input's native format.
// If it remaps any channel (e.g. OpenEXR promoting
// uint16/uint8 to half), copy through the output's stored
// format so integer values are rescaled rather than
// bit-reinterpreted.
TypeDesc xferformat = outspec.format;
if (xferformat == TypeDesc::UNKNOWN) {
const ImageSpec& storedspec(out->spec());
for (int c = 0; c < outspec.nchannels; ++c)
if (inspec.channelformat(c)
!= storedspec.channelformat(c)) {
xferformat = storedspec.format;
break;
}
}
size_t bufbytes = (xferformat == TypeDesc::UNKNOWN)
? (size_t)outspec.image_bytes(true)
: (size_t)outspec.image_pixels()
* outspec.nchannels
* xferformat.size();
std::vector<char> pixels(bufbytes);
ok = in->read_image(subimage, miplevel, 0, outspec.nchannels,
outspec.format, &pixels[0]);
xferformat, &pixels[0]);
if (!ok) {
OIIO::print(stderr, "iconvert ERROR reading \"{}\": {}\n",
in_filename, in->geterror());
} else {
ok = out->write_image(outspec.format, &pixels[0]);
ok = out->write_image(xferformat, &pixels[0]);
if (!ok)
OIIO::print(stderr,
"iconvert ERROR writing \"{}\": {}\n",
Expand Down
13 changes: 13 additions & 0 deletions src/libOpenImageIO/imageoutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,19 @@ ImageOutput::copy_image(ImageInput* in)
// FIXME -- a smarter implementation would read scanlines or tiles at
// a time, to minimize mem footprint.
bool native = supports("channelformats") && inspec.channelformats.size();
// The native (per-channel, no-conversion) copy is only valid if this
// output actually stores each channel in the same format the input
// supplies. If the output remaps any channel (e.g. OpenEXR promoting
// uint16 to half), a raw native copy would reinterpret the bytes rather
// than convert them, so fall back to the converting path in that case.
if (native) {
const ImageSpec& outspec(spec());
for (int c = 0; c < inspec.nchannels; ++c)
if (inspec.channelformat(c) != outspec.channelformat(c)) {
native = false;
break;
}
}
TypeDesc format = native ? TypeDesc::UNKNOWN : inspec.format;
std::unique_ptr<char[]> pixels(new char[inspec.image_bytes(native)]);
bool ok = in->read_image(in->current_subimage(), in->current_miplevel(), 0,
Expand Down
30 changes: 22 additions & 8 deletions src/openexr.imageio/exroutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -865,14 +865,28 @@ bool
OpenEXROutput::spec_to_header(ImageSpec& spec, int subimage,
Imf::Header& header)
{
// Force use of one of the three data types that OpenEXR supports
switch (spec.format.basetype) {
case TypeDesc::UINT: spec.format = TypeDesc::UINT; break;
case TypeDesc::FLOAT:
case TypeDesc::DOUBLE: spec.format = TypeDesc::FLOAT; break;
default:
// Everything else defaults to half
spec.format = TypeDesc::HALF;
// OpenEXR only stores three data types (half, float, uint); map anything
// else onto them. Do this for the overall format and, so the spec stays
// consistent with what is actually stored, for any per-channel formats.
auto exr_stored_format = [](TypeDesc t) -> TypeDesc {
switch (t.basetype) {
case TypeDesc::UINT: return TypeDesc::UINT;
case TypeDesc::FLOAT:
case TypeDesc::DOUBLE: return TypeDesc::FLOAT;
default: return TypeDesc::HALF; // everything else defaults to half
}
};
spec.format = exr_stored_format(spec.format);
if (spec.channelformats.size() == size_t(spec.nchannels)) {
bool allsame = true;
for (int c = 0; c < spec.nchannels; ++c) {
spec.channelformats[c] = exr_stored_format(spec.channelformats[c]);
allsame &= (spec.channelformats[c] == spec.channelformats[0]);
}
// If every channel ended up the same, the per-channel list is
// redundant -- drop it so the spec advertises a single format.
if (allsame)
spec.channelformats.clear();
}

Imath::Box2i dataWindow(Imath::V2i(spec.x, spec.y),
Expand Down
10 changes: 10 additions & 0 deletions testsuite/softimage/ref/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,13 @@ src/mixed-bitdepth.pic : 2 x 1, 2 channel, uint16 softimage
Stats FiniteCount: 2 2
Constant: No
Monochrome: No
mixed-bitdepth.exr : 2 x 1, 2 channel, half openexr
Stats Min: 0.066956 0.333252 (float)
Stats Max: 0.200317 0.399902 (float)
Stats Avg: 0.133636 0.366577 (float)
Stats StdDev: 0.066681 0.033325 (float)
Stats NanCount: 0 0
Stats InfCount: 0 0
Stats FiniteCount: 2 2
Constant: No
Monochrome: No
8 changes: 8 additions & 0 deletions testsuite/softimage/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,11 @@
# single bit depth was assumed for all channels.
command += info_command ("--stats src/mixed-bitdepth.pic",
info_program="iinfo", failureok=True)
# Converting that mixed-bit-depth file to a format that stores a single/other
# channel type (OpenEXR promotes both channels to half) must rescale each
# channel's values, not reinterpret its raw bytes. Convert and check the
# resulting normalized values.
command += (oiio_app("iconvert")
+ "src/mixed-bitdepth.pic mixed-bitdepth.exr >> out.txt 2>&1 ;\n")
command += info_command ("mixed-bitdepth.exr", extraargs="--stats",
verbose=False, hash=False)
Loading