Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Read EXR files from memory #1733

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
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
3 changes: 3 additions & 0 deletions testing/baselines/TestEXRReadMemory.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions testing/data/DATA_LICENSES.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,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](https://github.com/usd-wg/assets/tree/main/full_assets/StandardShaderBall): [CC-BY 4.0](https://creativecommons.org/licenses/by/4.0/)

All other datasets are licensed under the BSD-3-Clause F3D license.

Expand Down
3 changes: 3 additions & 0 deletions testing/data/Rec709.exr
Git LFS file not shown
3 changes: 3 additions & 0 deletions testing/data/small.usdz
Git LFS file not shown
3 changes: 2 additions & 1 deletion vtkext/private/module/Testing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ endif()
if(F3D_MODULE_EXR)
list(APPEND test_sources
TestF3DEXRReader.cxx
TestF3DEXRReaderInvalid.cxx)
TestF3DEXRReaderInvalid.cxx
TestF3DEXRMemReader.cxx)
endif()

vtk_add_test_cxx(vtkextPrivateTests tests
Expand Down
50 changes: 50 additions & 0 deletions vtkext/private/module/Testing/TestF3DEXRMemReader.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include <vtkImageData.h>
#include <vtkNew.h>

#include "vtkF3DEXRReader.h"

#include <fstream>
#include <iostream>
#include <vector>

bool readFileToVector(const std::string& filename, std::vector<char>& 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<vtkF3DEXRReader> reader;

std::string actual_filename = std::string(argv[1]) + "data/Rec709.exr";
std::string filename = "readFromMem.exr";
reader->SetFileName(filename.c_str());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the filename should be set here.

std::vector<char> 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;
}
94 changes: 85 additions & 9 deletions vtkext/private/module/vtkF3DEXRReader.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,60 @@
#include "vtksys/FStream.hxx"

#include <ImfArray.h>
#include <ImfIO.h>
#include <ImfRgbaFile.h>

#include <algorithm>
#include <sstream>
#include <thread>

/**
* 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<const char*>(buff))
, bufflen(static_cast<size_t>(bufferLen))
{
}

bool read(char c[], int n) override
{
if (pos + n <= bufflen)
{
std::copy_n(buffer + pos, n, c);
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);

//------------------------------------------------------------------------------
Expand All @@ -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;
Expand All @@ -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)
{
Expand Down Expand Up @@ -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<Imf::Rgba> pixels(this->GetHeight(), this->GetWidth());

file.setFrameBuffer(&pixels[0][0], 1, this->GetWidth());
Expand All @@ -134,6 +193,23 @@ 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
{
Imf::RgbaInputFile file(this->InternalFileName);
execute(file);
}
}
catch (const std::exception& e)
{
Expand Down
Loading