Improved IFF support reading and writing z buffers#4673
Improved IFF support reading and writing z buffers#4673lgritz merged 11 commits intoAcademySoftwareFoundation:mainfrom mikaelsundell:ms-IFF-250319
Conversation
| } else { | ||
| // skip to the next block. | ||
| if (!ioseek(chunksize, SEEK_CUR)) { | ||
| errorfmt("IFF error io seek failed"); |
There was a problem hiding this comment.
The ImageInput helper methods ioseek() and ioread() handle calling errorfmt for you. If they fail, you just need to return false, you don't need to clutter with another error message call.
| readimg(); | ||
| if (m_buf.empty()) { | ||
| if (!readimg()) { | ||
| errorfmt("IFF error could not read image for tile"); |
There was a problem hiding this comment.
If readimg() itself calls errorfmt for each way that it can fail, you don't need to do it again here.
| // IFF image files only supports UINT8 and UINT16. If something | ||
| // else was requested, revert to the one most likely to be readable | ||
| // by any IFF reader: UINT8 | ||
|
|
||
| TypeDesc base_format = spec.format; | ||
| if (base_format != TypeDesc::UINT8 && base_format != TypeDesc::UINT16) { | ||
| errorfmt("Unsupported format {}. Converting to UINT8.", base_format); | ||
| base_format = TypeDesc::UINT8; | ||
| } |
There was a problem hiding this comment.
I think that most of the readers pick the supported data type that loses the least precision versus the one requested. So if they ask for something at least as big as uint16, we should probably give them uint16 rather than uint8, which is even less fidelity.
|
There will be an update on this one today! |
| // skip to the next block | ||
| if (!ioseek(chunksize)) | ||
| if (!ioseek(chunksize, SEEK_CUR)) { | ||
| errorfmt("IFF error io seek failed"); |
There was a problem hiding this comment.
Not necessary -- the ioseek method itself calls errorfmt if it fails. All you have to do here is return.
|
Unfortunately, this dumps core on some example iff files I have. It's not something I can send. Let me see if I can at least point you to the part of the code where it crashes. |
| for (uint16_t px = xmin; px <= xmax; px++) { | ||
| uint8_t* out_p = out_dy + px * m_header.pixel_bytes() | ||
| + m_header.rgba_channels_bytes() + c; | ||
| *out_p++ = *in_p++; | ||
| } |
There was a problem hiding this comment.
For an RGBAZ iff file (uint8 RGBA channels, float Z channel), I'm getting crashes at line 852 when releasing a std::vector (presumably the "in" vector declared at line 834).
I suspect that this loop may be the culprit. Are you sure about those offets? I believe that here, we are taking the compressed single z channel (stored at this point in scratch and copying it to the right postion in the output buffer. But the
... + m_header.rgba_channels_bytes() + c
looks wrong to me, and also the next line
*outp++ = *in_p++;
These are uint8_t pointers, so you're incrementing them by 1 each here, but isn't the data in "in" really float (the Z channel) and isn't the spacing of the output buffer going to be m_header.pixel_bytes() ?
Or am I misunderstanding what's happening here?
There was a problem hiding this comment.
The reason this happens is that each call to uncompress_rle_channel decompresses floats and uint16 values byte by byte, while also handling planar storage and byte order at the same time. That’s why uint8_t is used across uint8, uint16, and float decompression and compression—it's one RLE run per byte, not per channel in this case.
pixel_bytes() is full RGB(A)+Z while rgba_channel_bytes() is RGB(A) only cause RGBA and Z chunks are stored in the actual IFF layout while we store interleaved/ packed.
My guess is that there’s a mismatch between the expected chunk size and what’s actually in the chunk (RLE vs uncompressed etc), which might be causing the issue. Are you able to open your sample files in Nuke without any problems? Or maybe try FCheck on a Windows machine? I could be wrong of course.
I could write out the binary layout if we can't reproduce it with other non-confidential files. May I ask—what software wrote the IFF files, maybe that's an easier way to the issue. After all, you are probably right there is a run over somewhere and a byte is all it needs. There must be a glitch in the code unless the original software you used writes IFF in a different way or with incorrect or non-aligned chunk sizes. If it works in Nuke etc it should work in my code.
This old legacy format is fun—if you had asked me to write it today, I would’ve done things slightly differently. 😄
There was a problem hiding this comment.
The good news is that I have at least 2 different iff files that crash here -- they are RGBAZ (uint8 for RGBA, float for Z), RLE compression. The bad news is that they are production frames and I can't send them out.
They appear to be Maya playblast output. They're from different shows at different times, so I think maybe it won't be difficult for you to reproduce from scratch on your end?
Will try loading with Nuke and report back...
There was a problem hiding this comment.
BTW, those failing frames do not crash in the mainline OIIO (not using this PR's patch)... but then again, I think it may simply be ignoring the Z channel entirely in that case, so I wouldn't say they are "correct" exactly.
There was a problem hiding this comment.
Nuke does load it. I'm not sure how to verify that it interprets the depth values correctly.
There was a problem hiding this comment.
Must be fixed! if the test files are coming from Maya it should be an easy thing to reproduce. You are able to open the earlier test file from Nicholas, it's also Maya and RGBAZ.
There was a problem hiding this comment.
Nuke will scream in red ERROR if the Z buffer reading is off, my code must be off/ wrong somewhere. Let's try to find it, try to open the Nicholas file and see if you get the same problem. I will also switch between a few different configuration and see if I can trigger the issue.
Visualise depth in rgba mode using something like an Expression node putting in (-1/z)/max value in R channel and zero in G and B. Should give you an OK visualisation.
Are you doing something like a RGBAZ channels from iff => exr with oiiotool when testing?
There was a problem hiding this comment.
Which earlier test file from Nicholas?
There was a problem hiding this comment.
I fail just doing a straightforward iconvert foo.iff foo.exr or iinfo -v --stats or oiiotool, yes.
|
New version in place with both support for uncompressed data (not verified with real IFF/z-buffer files) and the use of spans to avoid buffer runovers. I also removed some redundant error messages. Hopefully this will either fire in debug or switch over to non compressed. |
|
Just to make sure we're all on the same page, this PR as it currently stands no longer crashes on my problem iff files, but it does (gracefully) error on the file with a "Could not read : uncompressed span overflow at offset ..." |
|
(Mikael and I have been discussing this off-channel, but wanted to be sure people knew progress was being made here.) |
|
Thanks Larry, will be back this weekend to finish up, out traveling this week. |
|
Excuse my cluttering things up, but I'm using this PR as a guinea pig to request a GH Copilot review just to see what it says. Feel free to ignore any of it, this is just an experiment. |
There was a problem hiding this comment.
Copilot reviewed 7 out of 8 changed files in this pull request and generated no comments.
Files not reviewed (1)
- src/iff.imageio/CMakeLists.txt: Language not supported
Comments suppressed due to low confidence (2)
src/iff.imageio/iffoutput.cpp:165
- The write_scanline function always returns false after a successful memcpy. Returning true here would indicate the scanline was written successfully.
return false;
src/iff.imageio/iffoutput.cpp:156
- Verify that the updated check_open parameters (with a maximum channel value of 5) correctly support only RGB, RGBA, and RGBAZ formats as desired.
if (!check_open(mode, spec, { 0, 8192, 0, 8192, 0, 1, 0, 5 }))
|
Great! I'm back now, will finish this up in the next couple of days, will make sure I copilot my changes before commit. Let the bot battle begin! :-) |
…ffers or depth maps Removed noproxy versions, no longer needed Signed-off-by: Mikael Sundell <mikael.sundell@gmail.com>
Signed-off-by: Mikael Sundell <mikael.sundell@gmail.com>
Improved format precision handling: now falls back to UINT16 instead of UINT8 for high-precision formats Signed-off-by: Mikael Sundell <mikael.sundell@gmail.com>
Signed-off-by: Mikael Sundell <mikael.sundell@gmail.com>
Added initial implementation of z-buffer decoding using span Added support for uncompressed z-buffer tiles Signed-off-by: Mikael Sundell <mikael.sundell@gmail.com>
Signed-off-by: Mikael Sundell <mikael.sundell@gmail.com>
Fixed channel ordering issues in uncompressed data. Signed-off-by: Mikael Sundell <mikael.sundell@gmail.com>
Signed-off-by: Mikael Sundell <mikael.sundell@gmail.com>
Signed-off-by: Mikael Sundell <mikael.sundell@gmail.com>
|
New commits, spans + reading and writing of uncompressed IFF data verified. I've tested both uncompressed + rle with nuke and photoshop and both works for RGBAZ in 8/16-bit. Hopefully these are the updates we need to close this PR. |
Updated uncompressed z-buffer writing to use spans Signed-off-by: Mikael Sundell <mikael.sundell@gmail.com>
lgritz
left a comment
There was a problem hiding this comment.
Overall, LGTM and works on my iff files that once had trouble.
I did spot some problems with errorfmt calls getting confused about printf vs the newer format specifiers (throughout OIIO, all formatted string functions ending in "fmt" use the new syntax, not the old printf one). Those will need to get cleaned up, so some searching to be sure to find all of them.
With those changes, I think we are ready to merge this.
|
|
||
| // helper to uncompress a rle channel | ||
| size_t uncompress_rle_channel(const uint8_t* in, uint8_t* out, int size); | ||
| size_t uncompress_rle_channel(span<uint8_t> in, span<uint8_t> out, |
There was a problem hiding this comment.
Let's make that cspan<uint8_t> in since it's const data?
| in_span, tw * th); | ||
| if (used > scratch_span.size()) { | ||
| errorfmt( | ||
| "RLE uncompress exceeds span size (channel byte %d)", |
There was a problem hiding this comment.
All the functions ending in fmt take the new std::format idioms, so this should be:
| "RLE uncompress exceeds span size (channel byte %d)", | |
| "RLE uncompress exceeds span size (channel byte {})", |
|
|
||
| for (uint16_t px = xmin; px <= xmax; ++px) { | ||
| if (offset >= in_span.size()) { | ||
| errorfmt("in_span underflow at (%u, %u)", |
There was a problem hiding this comment.
| errorfmt("in_span underflow at (%u, %u)", | |
| errorfmt("in_span underflow at ({}, {})", |
|
|
||
| if (sl_span.empty()) { | ||
| errorfmt( | ||
| "scanline span overflow at (%u, %u)", |
There was a problem hiding this comment.
| "scanline span overflow at (%u, %u)", | |
| "scanline span overflow at ({}, {})", |
|
|
||
| if (pixel_offset + m_header.zbuffer_bytes() | ||
| > scratch_span.size()) { | ||
| errorfmt("in span overflow at pixel (%u, %u)", px, |
There was a problem hiding this comment.
| errorfmt("in span overflow at pixel (%u, %u)", px, | |
| errorfmt("in span overflow at pixel ({}, {})", px, |
I'm not going to mark all of them, you can do it as well as I can
Signed-off-by: Mikael Sundell <mikael.sundell@gmail.com>
…SoftwareFoundation#4673) Added support for reading and writing ZBUF chunks, also known as Z-buffers or depth maps. Removed noproxy versions, no longer needed. --------- Signed-off-by: Mikael Sundell <mikael.sundell@gmail.com>
|
Awesome 🙂 |
…SoftwareFoundation#4673) Added support for reading and writing ZBUF chunks, also known as Z-buffers or depth maps. Removed noproxy versions, no longer needed. --------- Signed-off-by: Mikael Sundell <mikael.sundell@gmail.com> Signed-off-by: Scott Wilson <scott@propersquid.com>
…SoftwareFoundation#4673) Added support for reading and writing ZBUF chunks, also known as Z-buffers or depth maps. Removed noproxy versions, no longer needed. --------- Signed-off-by: Mikael Sundell <mikael.sundell@gmail.com> Signed-off-by: Scott Wilson <scott@propersquid.com>
…SoftwareFoundation#4673) Added support for reading and writing ZBUF chunks, also known as Z-buffers or depth maps. Removed noproxy versions, no longer needed. --------- Signed-off-by: Mikael Sundell <mikael.sundell@gmail.com>
Description
Added support for reading and writing ZBUF chunks, also known as Z-buffers or depth maps
Removed noproxy versions, no longer needed
Checklist:
need to update the documentation, for example if this is a bug fix that
doesn't change the API.)
(adding new test cases if necessary).
corresponding Python bindings (and if altering ImageBufAlgo functions, also
exposed the new functionality as oiiotool options).
already run clang-format before submitting, I definitely will look at the CI
test that runs clang-format and fix anything that it highlights as being
nonconforming.