|
5 | 5 | #pragma once |
6 | 6 |
|
7 | 7 |
|
| 8 | +#include <OpenImageIO/filesystem.h> |
8 | 9 | #include <OpenImageIO/Imath.h> |
| 10 | +#include <OpenImageIO/imageio.h> |
9 | 11 | #include <OpenImageIO/platform.h> |
10 | 12 | #include <OpenImageIO/string_view.h> |
11 | 13 | #include <OpenImageIO/typedesc.h> |
12 | 14 |
|
| 15 | +#include <OpenEXR/IexThrowErrnoExc.h> |
13 | 16 | #include <OpenEXR/ImfChannelList.h> |
| 17 | +#include <OpenEXR/ImfIO.h> |
| 18 | +#include <ImathBox.h> |
14 | 19 |
|
15 | 20 | #ifdef OPENEXR_VERSION_MAJOR |
16 | 21 | # define OPENEXR_CODED_VERSION \ |
@@ -65,4 +70,188 @@ split_name(string_view fullname, string_view& layer, string_view& suffix) |
65 | 70 | } |
66 | 71 |
|
67 | 72 |
|
| 73 | + |
| 74 | +// Custom file input stream, copying code from the class StdIFStream in OpenEXR, |
| 75 | +// which would have been used if we just provided a filename. The difference is |
| 76 | +// that this can handle UTF-8 file paths on all platforms. |
| 77 | +class OpenEXRInputStream final : public Imf::IStream { |
| 78 | +public: |
| 79 | + OpenEXRInputStream(const char* filename, Filesystem::IOProxy* io) |
| 80 | + : Imf::IStream(filename) |
| 81 | + , m_io(io) |
| 82 | + { |
| 83 | + if (!io || io->mode() != Filesystem::IOProxy::Read) |
| 84 | + throw Iex::IoExc("File input failed."); |
| 85 | + } |
| 86 | + bool read(char c[], int n) override |
| 87 | + { |
| 88 | + OIIO_DASSERT(m_io); |
| 89 | + if (m_io->read(c, n) != size_t(n)) |
| 90 | + throw Iex::IoExc("Unexpected end of file."); |
| 91 | + return n; |
| 92 | + } |
| 93 | +#if OIIO_USING_IMATH >= 3 |
| 94 | + uint64_t tellg() override { return m_io->tell(); } |
| 95 | + void seekg(uint64_t pos) override |
| 96 | + { |
| 97 | + if (!m_io->seek(pos)) |
| 98 | + throw Iex::IoExc("File input failed."); |
| 99 | + } |
| 100 | +#else |
| 101 | + Imath::Int64 tellg() override { return m_io->tell(); } |
| 102 | + void seekg(Imath::Int64 pos) override |
| 103 | + { |
| 104 | + if (!m_io->seek(pos)) |
| 105 | + throw Iex::IoExc("File input failed."); |
| 106 | + } |
| 107 | +#endif |
| 108 | + void clear() override {} |
| 109 | + |
| 110 | +private: |
| 111 | + Filesystem::IOProxy* m_io = nullptr; |
| 112 | +}; |
| 113 | + |
| 114 | + |
| 115 | + |
| 116 | +class OpenEXRInput final : public ImageInput { |
| 117 | +public: |
| 118 | + OpenEXRInput(); |
| 119 | + ~OpenEXRInput() override { close(); } |
| 120 | + const char* format_name(void) const override { return "openexr"; } |
| 121 | + int supports(string_view feature) const override |
| 122 | + { |
| 123 | + return (feature == "arbitrary_metadata" |
| 124 | + || feature == "exif" // Because of arbitrary_metadata |
| 125 | + || feature == "iptc" // Because of arbitrary_metadata |
| 126 | + || feature == "ioproxy"); |
| 127 | + } |
| 128 | + bool valid_file(Filesystem::IOProxy* ioproxy) const override; |
| 129 | + bool open(const std::string& name, ImageSpec& newspec, |
| 130 | + const ImageSpec& config) override; |
| 131 | + bool open(const std::string& name, ImageSpec& newspec) override |
| 132 | + { |
| 133 | + return open(name, newspec, ImageSpec()); |
| 134 | + } |
| 135 | + bool close() override; |
| 136 | + int current_subimage(void) const override { return m_subimage; } |
| 137 | + int current_miplevel(void) const override { return m_miplevel; } |
| 138 | + bool seek_subimage(int subimage, int miplevel) override; |
| 139 | + ImageSpec spec(int subimage, int miplevel) override; |
| 140 | + ImageSpec spec_dimensions(int subimage, int miplevel) override; |
| 141 | + bool read_native_scanline(int subimage, int miplevel, int y, int z, |
| 142 | + void* data) override; |
| 143 | + bool read_native_scanlines(int subimage, int miplevel, int ybegin, int yend, |
| 144 | + int z, void* data) override; |
| 145 | + bool read_native_scanlines(int subimage, int miplevel, int ybegin, int yend, |
| 146 | + int z, int chbegin, int chend, |
| 147 | + void* data) override; |
| 148 | + bool read_native_tile(int subimage, int miplevel, int x, int y, int z, |
| 149 | + void* data) override; |
| 150 | + bool read_native_tiles(int subimage, int miplevel, int xbegin, int xend, |
| 151 | + int ybegin, int yend, int zbegin, int zend, |
| 152 | + void* data) override; |
| 153 | + bool read_native_tiles(int subimage, int miplevel, int xbegin, int xend, |
| 154 | + int ybegin, int yend, int zbegin, int zend, |
| 155 | + int chbegin, int chend, void* data) override; |
| 156 | + bool read_native_deep_scanlines(int subimage, int miplevel, int ybegin, |
| 157 | + int yend, int z, int chbegin, int chend, |
| 158 | + DeepData& deepdata) override; |
| 159 | + bool read_native_deep_tiles(int subimage, int miplevel, int xbegin, |
| 160 | + int xend, int ybegin, int yend, int zbegin, |
| 161 | + int zend, int chbegin, int chend, |
| 162 | + DeepData& deepdata) override; |
| 163 | + |
| 164 | + bool set_ioproxy(Filesystem::IOProxy* ioproxy) override |
| 165 | + { |
| 166 | + m_io = ioproxy; |
| 167 | + return true; |
| 168 | + } |
| 169 | + |
| 170 | +private: |
| 171 | + struct PartInfo { |
| 172 | + std::atomic_bool initialized; |
| 173 | + ImageSpec spec; |
| 174 | + int topwidth; ///< Width of top mip level |
| 175 | + int topheight; ///< Height of top mip level |
| 176 | + int levelmode; ///< The level mode |
| 177 | + int roundingmode; ///< Rounding mode |
| 178 | + bool cubeface; ///< It's a cubeface environment map |
| 179 | + int nmiplevels; ///< How many MIP levels are there? |
| 180 | + Imath::Box2i top_datawindow; |
| 181 | + Imath::Box2i top_displaywindow; |
| 182 | + std::vector<Imf::PixelType> pixeltype; ///< Imf pixel type for each chan |
| 183 | + std::vector<int> chanbytes; ///< Size (in bytes) of each channel |
| 184 | + |
| 185 | + PartInfo() |
| 186 | + : initialized(false) |
| 187 | + { |
| 188 | + } |
| 189 | + PartInfo(const PartInfo& p) |
| 190 | + : initialized((bool)p.initialized) |
| 191 | + , spec(p.spec) |
| 192 | + , topwidth(p.topwidth) |
| 193 | + , topheight(p.topheight) |
| 194 | + , levelmode(p.levelmode) |
| 195 | + , roundingmode(p.roundingmode) |
| 196 | + , cubeface(p.cubeface) |
| 197 | + , nmiplevels(p.nmiplevels) |
| 198 | + , top_datawindow(p.top_datawindow) |
| 199 | + , top_displaywindow(p.top_displaywindow) |
| 200 | + , pixeltype(p.pixeltype) |
| 201 | + , chanbytes(p.chanbytes) |
| 202 | + { |
| 203 | + } |
| 204 | + ~PartInfo() {} |
| 205 | + bool parse_header(OpenEXRInput* in, const Imf::Header* header); |
| 206 | + bool query_channels(OpenEXRInput* in, const Imf::Header* header); |
| 207 | + void compute_mipres(int miplevel, ImageSpec& spec) const; |
| 208 | + }; |
| 209 | + friend struct PartInfo; |
| 210 | + |
| 211 | + std::vector<PartInfo> m_parts; ///< Image parts |
| 212 | + OpenEXRInputStream* m_input_stream; ///< Stream for input file |
| 213 | + Imf::MultiPartInputFile* m_input_multipart; ///< Multipart input |
| 214 | + Imf::InputPart* m_scanline_input_part; |
| 215 | + Imf::TiledInputPart* m_tiled_input_part; |
| 216 | + Imf::DeepScanLineInputPart* m_deep_scanline_input_part; |
| 217 | + Imf::DeepTiledInputPart* m_deep_tiled_input_part; |
| 218 | + Filesystem::IOProxy* m_io = nullptr; |
| 219 | + std::unique_ptr<Filesystem::IOProxy> m_local_io; |
| 220 | + int m_subimage; ///< What subimage are we looking at? |
| 221 | + int m_nsubimages; ///< How many subimages are there? |
| 222 | + int m_miplevel; ///< What MIP level are we looking at? |
| 223 | + std::vector<float> m_missingcolor; ///< Color for missing tile/scanline |
| 224 | + |
| 225 | + void init() |
| 226 | + { |
| 227 | + m_input_stream = NULL; |
| 228 | + m_input_multipart = NULL; |
| 229 | + m_scanline_input_part = NULL; |
| 230 | + m_tiled_input_part = NULL; |
| 231 | + m_deep_scanline_input_part = NULL; |
| 232 | + m_deep_tiled_input_part = NULL; |
| 233 | + m_subimage = -1; |
| 234 | + m_miplevel = -1; |
| 235 | + m_io = nullptr; |
| 236 | + m_local_io.reset(); |
| 237 | + m_missingcolor.clear(); |
| 238 | + } |
| 239 | + |
| 240 | + bool read_native_tiles_individually(int subimage, int miplevel, int xbegin, |
| 241 | + int xend, int ybegin, int yend, |
| 242 | + int zbegin, int zend, int chbegin, |
| 243 | + int chend, void* data, stride_t xstride, |
| 244 | + stride_t ystride); |
| 245 | + |
| 246 | + // Fill in with 'missing' color/pattern. |
| 247 | + void fill_missing(int xbegin, int xend, int ybegin, int yend, int zbegin, |
| 248 | + int zend, int chbegin, int chend, void* data, |
| 249 | + stride_t xstride, stride_t ystride); |
| 250 | + |
| 251 | + // Prepare friend function for copyPixels |
| 252 | + friend class OpenEXROutput; |
| 253 | +}; |
| 254 | + |
| 255 | + |
| 256 | + |
68 | 257 | OIIO_PLUGIN_NAMESPACE_END |
0 commit comments