diff --git a/application/testing/CMakeLists.txt b/application/testing/CMakeLists.txt index 8029ae7791..550e702b78 100644 --- a/application/testing/CMakeLists.txt +++ b/application/testing/CMakeLists.txt @@ -680,6 +680,9 @@ if(F3D_PLUGIN_BUILD_USD) # https://gitlab.kitware.com/vtk/vtk/-/merge_requests/8224 if(VTK_VERSION VERSION_GREATER_EQUAL 9.0.20210805) # for embedded material support + if(F3D_MODULE_EXR) + f3d_test(NAME TestEXRReadMemory DATA small.usdz ARGS --load-plugins=usd DEFAULT_LIGHTS) + endif() f3d_test(NAME TestUSDZAnimated DATA AnimatedCube.usdz ARGS --load-plugins=usd --animation-time=0.3 --animation-progress) f3d_test(NAME TestUSDZRigged DATA RiggedSimple.usdz ARGS --load-plugins=usd --animation-time=0.3) diff --git a/testing/baselines/TestEXRReadMemory.png b/testing/baselines/TestEXRReadMemory.png new file mode 100644 index 0000000000..ffe28c6f8a --- /dev/null +++ b/testing/baselines/TestEXRReadMemory.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4f910864b9340e6cd52290610c62494bbbbcc18ab1dafb923e6c26a5a0e809ff +size 50088 diff --git a/testing/data/DATA_LICENSES.md b/testing/data/DATA_LICENSES.md index 4e79d32600..96258d1ba4 100644 --- a/testing/data/DATA_LICENSES.md +++ b/testing/data/DATA_LICENSES.md @@ -53,6 +53,8 @@ - world*: VTK Data: BSD-3-Clause - 16bits.*: @bisechen: [CC0](https://creativecommons.org/publicdomain/zero/1.0/) - (ノಠ益ಠ )ノ.vtp: VTK Data: BSD-3-Clause +- Rec709.exr: [Copyright 2006 Industrial Light & Magic](https://github.com/AcademySoftwareFoundation/openexr/blob/370db2835843ac75f85e1386c05455f26a6ff58c/website/test_images/Chromaticities/Rec709.rst): BSD-3-Clause +- small.usdz: Copyright USD contributors: CC-BY 4.0 All other datasets are licensed under the BSD-3-Clause F3D license. diff --git a/testing/data/Rec709.exr b/testing/data/Rec709.exr new file mode 100644 index 0000000000..ce1412e961 --- /dev/null +++ b/testing/data/Rec709.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15028cbf7bf0efa43738890af715ec550d26ae9e32a1020d0f4e7c785d3a370e +size 908168 diff --git a/testing/data/small.usdz b/testing/data/small.usdz new file mode 100644 index 0000000000..032d3da132 --- /dev/null +++ b/testing/data/small.usdz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:db6cd84120a9e61b18b05edcb6f825994da59a5878229658648e76af546f5514 +size 103799 diff --git a/vtkext/private/module/Testing/CMakeLists.txt b/vtkext/private/module/Testing/CMakeLists.txt index f98d493b38..9dbe869d28 100644 --- a/vtkext/private/module/Testing/CMakeLists.txt +++ b/vtkext/private/module/Testing/CMakeLists.txt @@ -17,7 +17,8 @@ endif() if(F3D_MODULE_EXR) list(APPEND test_sources TestF3DEXRReader.cxx - TestF3DEXRReaderInvalid.cxx) + TestF3DEXRReaderInvalid.cxx + TestF3DEXRMemReader.cxx) endif() if(VTK_VERSION VERSION_LESS_EQUAL 9.1.0) diff --git a/vtkext/private/module/Testing/TestF3DEXRMemReader.cxx b/vtkext/private/module/Testing/TestF3DEXRMemReader.cxx new file mode 100644 index 0000000000..a7fe9a5a46 --- /dev/null +++ b/vtkext/private/module/Testing/TestF3DEXRMemReader.cxx @@ -0,0 +1,53 @@ +#include +#include + +#include "vtkF3DEXRReader.h" + +#include +#include +#include +/** + * image file taken from + * https://github.com/AcademySoftwareFoundation/openexr/blob/370db2835843ac75f85e1386c05455f26a6ff58c/website/test_images/Chromaticities/Rec709.rst + */ +bool readFileToVector(const std::string& filename, std::vector& buffer) +{ + std::ifstream file(filename, std::ios::binary | std::ios::ate); + if (!file) + { + return false; + } + + std::streamsize size = file.tellg(); + file.seekg(0, std::ios::beg); + buffer.resize(size); + return file.read(buffer.data(), size) ? true : false; +} + +int TestF3DEXRMemReader(int argc, char* argv[]) +{ + vtkNew reader; + + std::string actual_filename = std::string(argv[1]) + "data/Rec709.exr"; + std::string filename = "readFromMem.exr"; + reader->SetFileName(filename.c_str()); + std::vector buff; + readFileToVector(actual_filename, buff); + reader->SetMemoryBuffer(buff.data()); + reader->SetMemoryBufferLength(buff.size()); + reader->Update(); + + reader->Print(cout); + + vtkImageData* img = reader->GetOutput(); + + int* dims = img->GetDimensions(); + + if (dims[0] != 610 && dims[1] != 406) + { + std::cerr << "Incorrect EXR image size." << dims[0] << ":" << dims[1] << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/vtkext/private/module/vtkF3DEXRReader.cxx b/vtkext/private/module/vtkF3DEXRReader.cxx index 4eb4f28dff..20c319fa97 100644 --- a/vtkext/private/module/vtkF3DEXRReader.cxx +++ b/vtkext/private/module/vtkF3DEXRReader.cxx @@ -8,11 +8,60 @@ #include "vtksys/FStream.hxx" #include +#include #include +#include #include #include +/** + * Class to treat file contents in memory like it were still in a file. + */ +class MemStream : public Imf::IStream +{ +public: + MemStream(const char* name, const void* buff, vtkIdType bufferLen) + : Imf::IStream(name) + , buffer(static_cast(buff)) + , bufflen(static_cast(bufferLen)) + { + } + + bool read(char c[], int n) override + { + if (pos + n <= bufflen) + { + memcpy(c, buffer + pos, n); + pos += n; + return true; + } + return false; + } + + /** + * returns the current reading position, in bytes, from the beginning of the file. + * The next read() call will begin reading at the indicated position + */ + uint64_t tellg() override + { + return pos; + } + + /** + * sets the current reading position to pos bytes from the beginning of the "file" + */ + void seekg(uint64_t new_pos) override + { + pos = new_pos; + } + +private: + const char* buffer; + size_t bufflen; + uint64_t pos{ 0 }; +}; + vtkStandardNewMacro(vtkF3DEXRReader); //------------------------------------------------------------------------------ @@ -36,15 +85,14 @@ void vtkF3DEXRReader::ExecuteInformation() // Setup filename to read the header this->ComputeInternalFileName(this->DataExtent[4]); - if (this->InternalFileName == nullptr || this->InternalFileName[0] == '\0') + if ((this->InternalFileName == nullptr || this->InternalFileName[0] == '\0') && + !this->MemoryBuffer) { return; } - try + auto execute = [&](Imf::RgbaInputFile& file) { - Imf::RgbaInputFile file(this->InternalFileName); - Imath::Box2i dw = file.dataWindow(); this->DataExtent[0] = dw.min.x; this->DataExtent[1] = dw.max.x; @@ -56,6 +104,21 @@ void vtkF3DEXRReader::ExecuteInformation() { throw std::runtime_error("only RGB and RGBA channels are supported"); } + }; + + try + { + if (this->MemoryBuffer) + { + MemStream memoryStream("EXRmemoryStream", this->MemoryBuffer, this->MemoryBufferLength); + Imf::RgbaInputFile file = Imf::RgbaInputFile(memoryStream); + execute(file); + } + else + { + Imf::RgbaInputFile file(this->InternalFileName); + execute(file); + } } catch (const std::exception& e) { @@ -112,12 +175,8 @@ void vtkF3DEXRReader::ExecuteDataWithInformation(vtkDataObject* output, vtkInfor scalars->SetName("Pixels"); float* dataPtr = scalars->GetPointer(0); - try + auto execute = [&](Imf::RgbaInputFile& file) { - assert(this->InternalFileName); - Imf::setGlobalThreadCount(std::thread::hardware_concurrency()); - Imf::RgbaInputFile file(this->InternalFileName); - Imf::Array2D pixels(this->GetHeight(), this->GetWidth()); file.setFrameBuffer(&pixels[0][0], 1, this->GetWidth()); @@ -134,6 +193,27 @@ void vtkF3DEXRReader::ExecuteDataWithInformation(vtkDataObject* output, vtkInfor dataPtr += 3; } } + }; + + try + { + Imf::setGlobalThreadCount(std::thread::hardware_concurrency()); + + if (this->MemoryBuffer) + { + MemStream memoryStream("EXRmemoryStream", this->MemoryBuffer, this->MemoryBufferLength); + Imf::RgbaInputFile file = Imf::RgbaInputFile(memoryStream); + execute(file); + } + else + { + if (!(this->InternalFileName)) + { + throw std::invalid_argument("Not filename in EXR Reader when no Memory Buffer is set."); + } + Imf::RgbaInputFile file(this->InternalFileName); + execute(file); + } } catch (const std::exception& e) {