#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2021 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License #ifndef NUCLEX_PIXELS_STORAGE_EXR_OPENEXRHELPERS_H #define NUCLEX_PIXELS_STORAGE_EXR_OPENEXRHELPERS_H #include "Nuclex/Pixels/Config.h" #if defined(NUCLEX_PIXELS_HAVE_OPENEXR) #include "Nuclex/Pixels/PixelFormat.h" #include "Nuclex/Pixels/Storage/VirtualFile.h" #include // Sadly, OpenEXR has lots of really poor programming practices and // also doesn't seem to care much about compiler warnings... #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable:4244) // conversion from 'int' to 'unsigned short' #pragma warning(disable:4996) // function or variable may be unsafe #elif defined(__clang__) #pragma clang diagnostic push //#pragma clang diagnostic ignored "-Wfloat-equal -Wdeprecated" #elif defined(__GNUC__) || defined(__GNUG__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-copy" // default cctor deprecated #endif #include #include #include #include #include #if defined(_MSC_VER) #pragma warning(pop) #elif defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUC__) || defined(__GNUG__) #pragma GCC diagnostic pop #endif namespace Nuclex { namespace Pixels { namespace Storage { namespace Exr { // ------------------------------------------------------------------------------------------- // /// Size of the smallest valid .exr file /// /// This is just a guess. I created a 1x1 pixel .exr image in Krita and disabled /// everything possible upon saving. The result was a 355 byte file. Any useful /// .exr image will thus have more than 355 bytes. To be on the safe side, I went /// with 256 bytes, in case there are some optimizations Krita didn't offer. /// constexpr const std::size_t SmallestPossibleExrSize = 256; // ------------------------------------------------------------------------------------------- // /// Helper class for reading .exr image files using OpenEXR class Helpers { /// /// Checks whether the first 8 bytes in a file are a valid .exr file header /// /// File header that will be checked /// True if the file header is a valid .exr file header /// /// The file header must contain at least the first 8 bytes of the file, /// otherwise this will segfault. /// public: static bool IsValidExrHeader(const std::uint8_t *fileHeader); /// Sets up an OpenEXR frame buffer matching the specified pixel format /// Frame buffer that will be set up /// Pixel format the frame buffer will be set to /// Address of the pixel buffer the frame buffer will use /// /// OpenEXR allows the frame buffer format to be set relatively freely (if one /// foregoes the RgbaInputFile wrapper). This method makes use of that feature. /// public: static void AddChannelsToFrameBuffer( Imf::FrameBuffer &frameBuffer, PixelFormat pixelFormat, void *pixels ); }; // ------------------------------------------------------------------------------------------- // /// Adapter that allows OpenEXR to access a VirtualFile as an IStream class VirtualFileInputStream : public Imf::IStream { /// Initializes a new VirtualFile IStream adapter /// File from which the IStream adapter will read public: VirtualFileInputStream(const Nuclex::Pixels::Storage::VirtualFile &file) : IStream(u8"VirtualFile adapter stream"), file(file), position(0), length(file.GetSize()) {} /// Frees all resources owned by the virtual file steam public: virtual ~VirtualFileInputStream() = default; /// Does this input stream support memory-mapped IO? public: bool isMemoryMapped() const override { return false; } /// Read from the stream /// Buffer in which the data will be stored /// Number of bytes that will be read /// Whether more bytes are available from the file public: bool read(char buffer[/*byteCount*/], int byteCount) override { this->file.ReadAt( this->position, byteCount, reinterpret_cast(buffer) ); this->position += byteCount; return (this->position < this->length); } /// Throws an exception if a memory-mapped read is attempted /// Numeber of bytes to read from the file /// Address containing the memory-mapped file's data public: char *readMemoryMapped(int byteCount) override { (void)byteCount; // Unused throw std::logic_error(u8"Stream is not memory mapped"); } /// Looks up the current position of the file cursor /// The current position of the file cursor public: Imf::Int64 tellg() override { // Signed? Non-const? WTF?! return static_cast(this->length); } /// Moves the file cursor to the specified position /// Position the file cursor will be moved to public: void seekg(Imf::Int64 newPosition) override { //assert(pos >= 0 && pos <= length); this->position = static_cast(newPosition); } /// Clears any error conditions if a read failed public: void clear() override {} // Virtual files are stateless /// Virtual file from which the IStream adapter is reading private: const Nuclex::Pixels::Storage::VirtualFile &file; /// Current position of the file pointer private: std::uint64_t position; /// Total length of the file in bytes private: std::uint64_t length; }; // ------------------------------------------------------------------------------------------- // }}}} // namespace Nuclex::Pixels::Storage::Exr #endif // defined(NUCLEX_PIXELS_HAVE_OPENEXR) #endif // NUCLEX_PIXELS_STORAGE_EXR_OPENEXRHELPERS_H