diff --git a/src/lib/OpenEXR/ImfDwaCompressor.cpp b/src/lib/OpenEXR/ImfDwaCompressor.cpp index ce0e79232a..5e05fd2026 100644 --- a/src/lib/OpenEXR/ImfDwaCompressor.cpp +++ b/src/lib/OpenEXR/ImfDwaCompressor.cpp @@ -121,6 +121,7 @@ #include "half.h" #include +#include #include #include #include @@ -2250,10 +2251,12 @@ DwaCompressor::uncompress ( // Flip the counters from XDR to NATIVE // + std::array counterBuf; + memcpy (counterBuf.data (), inPtr, counterBuf.size() * sizeof (uint64_t)); for (int i = 0; i < NUM_SIZES_SINGLE; ++i) { - uint64_t* dst = (((uint64_t*) inPtr) + i); - const char* src = (char*) (((uint64_t*) inPtr) + i); + uint64_t* dst = counterBuf.data() + i; + const char* src = (char*) (counterBuf.data() + i); Xdr::read (src, *dst); } @@ -2262,7 +2265,7 @@ DwaCompressor::uncompress ( // Unwind all the counter info // - const uint64_t* inPtr64 = (const uint64_t*) inPtr; + const uint64_t* inPtr64 = counterBuf.data(); uint64_t version = *(inPtr64 + VERSION); uint64_t unknownUncompressedSize = *(inPtr64 + UNKNOWN_UNCOMPRESSED_SIZE); diff --git a/src/lib/OpenEXR/ImfMisc.cpp b/src/lib/OpenEXR/ImfMisc.cpp index 09c820bc40..0d66693f6d 100644 --- a/src/lib/OpenEXR/ImfMisc.cpp +++ b/src/lib/OpenEXR/ImfMisc.cpp @@ -23,6 +23,9 @@ #include #include +#include +#include + OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER using IMATH_NAMESPACE::Box2i; @@ -1981,4 +1984,11 @@ getChunkOffsetTableSize (const Header& header) return getTiledChunkOffsetTableSize (header); } +std::wstring +WidenFilename (const char* filename) +{ + std::wstring_convert, wchar_t> converter; + return converter.from_bytes (filename); +} + OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT diff --git a/src/lib/OpenEXR/ImfMisc.h b/src/lib/OpenEXR/ImfMisc.h index 41e6944f62..801639a25d 100644 --- a/src/lib/OpenEXR/ImfMisc.h +++ b/src/lib/OpenEXR/ImfMisc.h @@ -434,6 +434,14 @@ bool usesLongNames (const Header& header); IMF_EXPORT int getChunkOffsetTableSize (const Header& header); +// +// Convert a filename to a wide string. This is useful for working with +// filenames on Windows. +// + +IMF_EXPORT +std::wstring WidenFilename (const char* filename); + OPENEXR_IMF_INTERNAL_NAMESPACE_HEADER_EXIT #endif diff --git a/src/lib/OpenEXR/ImfStdIO.cpp b/src/lib/OpenEXR/ImfStdIO.cpp index 8f6fcc946c..a0559b80cf 100644 --- a/src/lib/OpenEXR/ImfStdIO.cpp +++ b/src/lib/OpenEXR/ImfStdIO.cpp @@ -11,6 +11,7 @@ //----------------------------------------------------------------------------- #include "Iex.h" +#include #include #include #ifdef _WIN32 @@ -35,20 +36,6 @@ namespace { #ifdef _WIN32 -wstring -WidenFilename (const char* filename) -{ - wstring ret; - int fnlen = static_cast (strlen (filename)); - int len = MultiByteToWideChar (CP_UTF8, 0, filename, fnlen, NULL, 0); - if (len > 0) - { - ret.resize (len); - MultiByteToWideChar (CP_UTF8, 0, filename, fnlen, &ret[0], len); - } - return ret; -} - # if defined(__GLIBCXX__) && \ !(defined(_GLIBCXX_HAVE_WFOPEN) && defined(_GLIBCXX_USE_WCHAR_T)) # define USE_CUSTOM_WIDE_OPEN 1 diff --git a/src/test/OpenEXRTest/TestUtilFStream.h b/src/test/OpenEXRTest/TestUtilFStream.h index 3666259f90..c2d58efd9c 100644 --- a/src/test/OpenEXRTest/TestUtilFStream.h +++ b/src/test/OpenEXRTest/TestUtilFStream.h @@ -6,6 +6,8 @@ #ifndef INCLUDE_TestUtilFStream_h_ # define INCLUDE_TestUtilFStream_h_ 1 +# include + # include # include @@ -26,20 +28,6 @@ namespace testutil { # ifdef _WIN32 -inline std::wstring -WidenFilename (const char* filename) -{ - std::wstring ret; - int fnlen = static_cast (strlen (filename)); - int len = MultiByteToWideChar (CP_UTF8, 0, filename, fnlen, NULL, 0); - if (len > 0) - { - ret.resize (len); - MultiByteToWideChar (CP_UTF8, 0, filename, fnlen, &ret[0], len); - } - return ret; -} - // This is a big work around mechanism for compiling using mingw / gcc under windows // until mingw 9 where they add the wide filename version of open # if ( \ diff --git a/src/test/OpenEXRTest/testExistingStreams.cpp b/src/test/OpenEXRTest/testExistingStreams.cpp index 7e8fac9809..16ba9ab3dd 100644 --- a/src/test/OpenEXRTest/testExistingStreams.cpp +++ b/src/test/OpenEXRTest/testExistingStreams.cpp @@ -8,7 +8,9 @@ #endif #include +#include #include +#include #include #include #include @@ -22,6 +24,14 @@ #include #include +#ifdef _WIN32 +#else +#include +#include +#include +#include +#endif + #include #include @@ -70,7 +80,7 @@ fillPixels2 (Array2D& pixels, int w, int h) // // class MMIFStream -- a memory-mapped implementation of -// class IStream based on class std::ifstream +// class IStream // class MMIFStream : public OPENEXR_IMF_NAMESPACE::IStream @@ -78,8 +88,6 @@ class MMIFStream : public OPENEXR_IMF_NAMESPACE::IStream public: //------------------------------------------------------- // A constructor that opens the file with the given name. - // It reads the whole file into an internal buffer and - // then immediately closes the file. //------------------------------------------------------- MMIFStream (const char fileName[]); @@ -95,46 +103,127 @@ class MMIFStream : public OPENEXR_IMF_NAMESPACE::IStream virtual void clear () {} private: - char* _buffer; - uint64_t _length; +#ifdef _WIN32 + HANDLE _f = INVALID_HANDLE_VALUE; +#else + int _f; +#endif + void* _mmap; + const char* _mmapStart; uint64_t _pos; + uint64_t _length; }; MMIFStream::MMIFStream (const char fileName[]) : OPENEXR_IMF_NAMESPACE::IStream (fileName) - , _buffer (0) - , _length (0) +#ifdef _WIN32 + , _f (INVALID_HANDLE_VALUE) +#else + , _f (-1) +#endif + , _mmap (reinterpret_cast(-1)) + , _mmapStart (nullptr) , _pos (0) + , _length (0) { - std::ifstream ifs; - testutil::OpenStreamWithUTF8Name ( - ifs, fileName, ios::in | ios_base::binary); +#ifdef _WIN32 + const std::wstring fileNameWide = WidenFilename (fileName); + try + { + _f = CreateFileW ( + fileNameWide.c_str (), + GENERIC_READ, + FILE_SHARE_READ, + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + 0); + } + catch (const std::exception&) + { + _f = INVALID_HANDLE_VALUE; + } + if (INVALID_HANDLE_VALUE == _f) + { + throw IEX_NAMESPACE::IoExc ("Cannot open file."); + } - // - // Get length of file - // + struct _stati64 s; + memset (&s, 0, sizeof (struct _stati64)); + if (_wstati64 (fileNameWide.c_str (), &s) != 0) + { + throw IEX_NAMESPACE::IoExc ("Cannot stat file."); + } - ifs.seekg (0, ios::end); - _length = ifs.tellg (); - ifs.seekg (0, ios::beg); + _length = s.st_size; - // - // Allocate memory - // + _mmap = CreateFileMapping (_f, 0, PAGE_READONLY, 0, 0, 0); + if (!_mmap) + { + throw IEX_NAMESPACE::IoExc ("Cannot memory map file."); + } - _buffer = new char[_length]; + _mmapStart = reinterpret_cast ( + MapViewOfFile (_mmap, FILE_MAP_READ, 0, 0, 0)); + if (!_mmapStart) + { + throw IEX_NAMESPACE::IoExc ("Cannot map view of file."); + } - // - // Read the entire file - // +#else + + _f = open(fileName, O_RDONLY); + if (-1 == _f) + { + throw IEX_NAMESPACE::IoExc ("Cannot open file."); + } + + struct stat s; + memset(&s, 0, sizeof(struct stat)); + if (stat(fileName, &s) != 0) + { + throw IEX_NAMESPACE::IoExc ("Cannot stat file."); + } + + _length = s.st_size; - ifs.read (_buffer, _length); - ifs.close (); + _mmap = mmap(0, _length, PROT_READ, MAP_SHARED, _f, 0); + if (_mmap == (void*)-1) + { + throw IEX_NAMESPACE::IoExc ("Cannot memory map file."); + } + + _mmapStart = reinterpret_cast (_mmap); +#endif } MMIFStream::~MMIFStream () { - delete[] _buffer; +#ifdef _WIN32 + if (_mmapStart) + { + UnmapViewOfFile ((void*) _mmapStart); + } + if (_mmap) + { + CloseHandle (_mmap); + } + if (_f != INVALID_HANDLE_VALUE) + { + CloseHandle (_f); + } + +#else + + if (_mmap != (void*)-1) + { + munmap(_mmap, _length); + } + if (_f != -1) + { + close(_f); + } +#endif } bool @@ -152,7 +241,7 @@ MMIFStream::read (char c[/*n*/], int n) retVal = false; } - memcpy (c, &(_buffer[_pos]), n2); + memcpy (c, _mmapStart + _pos, n2); _pos += n2; return retVal; } @@ -166,14 +255,18 @@ MMIFStream::readMemoryMapped (int n) if (_pos + n > _length) throw IEX_NAMESPACE::InputExc ("Reading past end of file."); - char* retVal = &(_buffer[_pos]); + char* retVal = const_cast(_mmapStart) + _pos; _pos += n; return retVal; } void writeReadScanLines ( - const char fileName[], int width, int height, const Array2D& p1) + const char fileName[], + int width, + int height, + Compression compression, + const Array2D& p1) { // // Save a scanline-based RGBA image, but instead of @@ -194,8 +287,15 @@ writeReadScanLines ( std::ofstream os; testutil::OpenStreamWithUTF8Name ( os, fileName, ios::out | ios_base::binary); - StdOFStream ofs (os, fileName); - Header header (width, height); + StdOFStream ofs (os, fileName); + Header header ( + width, + height, + 1, + IMATH_NAMESPACE::V2f (0, 0), + 1, + INCREASING_Y, + compression); RgbaOutputFile out (ofs, header, WRITE_RGBA); out.setFrameBuffer (&p1[0][0], 1, width); out.writePixels (height); @@ -219,15 +319,18 @@ writeReadScanLines ( in.setFrameBuffer (&p2[-dy][-dx], 1, w); in.readPixels (dw.min.y, dw.max.y); - cout << ", comparing"; - for (int y = 0; y < h; ++y) + if (!isLossyCompression (compression)) { - for (int x = 0; x < w; ++x) + cout << ", comparing"; + for (int y = 0; y < h; ++y) { - assert (p2[y][x].r == p1[y][x].r); - assert (p2[y][x].g == p1[y][x].g); - assert (p2[y][x].b == p1[y][x].b); - assert (p2[y][x].a == p1[y][x].a); + for (int x = 0; x < w; ++x) + { + assert (p2[y][x].r == p1[y][x].r); + assert (p2[y][x].g == p1[y][x].g); + assert (p2[y][x].b == p1[y][x].b); + assert (p2[y][x].a == p1[y][x].a); + } } } } @@ -247,15 +350,18 @@ writeReadScanLines ( in.setFrameBuffer (&p2[-dy][-dx], 1, w); in.readPixels (dw.min.y, dw.max.y); - cout << ", comparing"; - for (int y = 0; y < h; ++y) + if (!isLossyCompression(compression)) { - for (int x = 0; x < w; ++x) + cout << ", comparing"; + for (int y = 0; y < h; ++y) { - assert (p2[y][x].r == p1[y][x].r); - assert (p2[y][x].g == p1[y][x].g); - assert (p2[y][x].b == p1[y][x].b); - assert (p2[y][x].a == p1[y][x].a); + for (int x = 0; x < w; ++x) + { + assert (p2[y][x].r == p1[y][x].r); + assert (p2[y][x].g == p1[y][x].g); + assert (p2[y][x].b == p1[y][x].b); + assert (p2[y][x].a == p1[y][x].a); + } } } } @@ -264,9 +370,14 @@ writeReadScanLines ( remove (fileName); } + void writeReadMultiPart ( - const char fileName[], int width, int height, const Array2D& p1) + const char fileName[], + int width, + int height, + Compression compression, + const Array2D& p1) { // // Save a two scanline parts in an image, but instead of @@ -290,7 +401,14 @@ writeReadMultiPart ( StdOFStream ofs (os, fileName); vector
headers (2); - headers[0] = Header (width, height); + headers[0] = Header ( + width, + height, + 1, + IMATH_NAMESPACE::V2f (0, 0), + 1, + INCREASING_Y, + compression); headers[0].setName ("part1"); headers[0].channels ().insert ("R", Channel ()); headers[0].channels ().insert ("G", Channel ()); @@ -395,15 +513,18 @@ writeReadMultiPart ( p.setFrameBuffer (f); p.readPixels (dw.min.y, dw.max.y); - cout << ", comparing pt " << part; - for (int y = 0; y < h; ++y) + if (!isLossyCompression (compression)) { - for (int x = 0; x < w; ++x) + cout << ", comparing pt " << part; + for (int y = 0; y < h; ++y) { - assert (p2[y][x].r == p1[y][x].r); - assert (p2[y][x].g == p1[y][x].g); - assert (p2[y][x].b == p1[y][x].b); - assert (p2[y][x].a == p1[y][x].a); + for (int x = 0; x < w; ++x) + { + assert (p2[y][x].r == p1[y][x].r); + assert (p2[y][x].g == p1[y][x].g); + assert (p2[y][x].b == p1[y][x].b); + assert (p2[y][x].a == p1[y][x].a); + } } } } @@ -461,15 +582,18 @@ writeReadMultiPart ( p.setFrameBuffer (f); p.readPixels (dw.min.y, dw.max.y); - cout << ", comparing pt " << part; - for (int y = 0; y < h; ++y) + if (!isLossyCompression (compression)) { - for (int x = 0; x < w; ++x) + cout << ", comparing pt " << part; + for (int y = 0; y < h; ++y) { - assert (p2[y][x].r == p1[y][x].r); - assert (p2[y][x].g == p1[y][x].g); - assert (p2[y][x].b == p1[y][x].b); - assert (p2[y][x].a == p1[y][x].a); + for (int x = 0; x < w; ++x) + { + assert (p2[y][x].r == p1[y][x].r); + assert (p2[y][x].g == p1[y][x].g); + assert (p2[y][x].b == p1[y][x].b); + assert (p2[y][x].a == p1[y][x].a); + } } } } @@ -482,7 +606,11 @@ writeReadMultiPart ( void writeReadTiles ( - const char fileName[], int width, int height, const Array2D& p1) + const char fileName[], + int width, + int height, + Compression compression, + const Array2D& p1) { // // Save a tiled RGBA image, but instead of letting @@ -501,8 +629,15 @@ writeReadTiles ( std::ofstream os; testutil::OpenStreamWithUTF8Name ( os, fileName, ios_base::out | ios_base::binary); - StdOFStream ofs (os, fileName); - Header header (width, height); + StdOFStream ofs (os, fileName); + Header header ( + width, + height, + 1, + IMATH_NAMESPACE::V2f (0, 0), + 1, + INCREASING_Y, + compression); TiledRgbaOutputFile out (ofs, header, WRITE_RGBA, 20, 20, ONE_LEVEL); out.setFrameBuffer (&p1[0][0], 1, width); out.writeTiles (0, out.numXTiles () - 1, 0, out.numYTiles () - 1); @@ -526,15 +661,18 @@ writeReadTiles ( in.setFrameBuffer (&p2[-dy][-dx], 1, w); in.readTiles (0, in.numXTiles () - 1, 0, in.numYTiles () - 1); - cout << ", comparing"; - for (int y = 0; y < h; ++y) + if (!isLossyCompression (compression)) { - for (int x = 0; x < w; ++x) + cout << ", comparing"; + for (int y = 0; y < h; ++y) { - assert (p2[y][x].r == p1[y][x].r); - assert (p2[y][x].g == p1[y][x].g); - assert (p2[y][x].b == p1[y][x].b); - assert (p2[y][x].a == p1[y][x].a); + for (int x = 0; x < w; ++x) + { + assert (p2[y][x].r == p1[y][x].r); + assert (p2[y][x].g == p1[y][x].g); + assert (p2[y][x].b == p1[y][x].b); + assert (p2[y][x].a == p1[y][x].a); + } } } } @@ -554,15 +692,18 @@ writeReadTiles ( in.setFrameBuffer (&p2[-dy][-dx], 1, w); in.readTiles (0, in.numXTiles () - 1, 0, in.numYTiles () - 1); - cout << ", comparing"; - for (int y = 0; y < h; ++y) + if (!isLossyCompression (compression)) { - for (int x = 0; x < w; ++x) + cout << ", comparing"; + for (int y = 0; y < h; ++y) { - assert (p2[y][x].r == p1[y][x].r); - assert (p2[y][x].g == p1[y][x].g); - assert (p2[y][x].b == p1[y][x].b); - assert (p2[y][x].a == p1[y][x].a); + for (int x = 0; x < w; ++x) + { + assert (p2[y][x].r == p1[y][x].r); + assert (p2[y][x].g == p1[y][x].g); + assert (p2[y][x].b == p1[y][x].b); + assert (p2[y][x].a == p1[y][x].a); + } } } } @@ -855,21 +996,40 @@ testExistingStreams (const std::string& tempDir) const int W = 119; const int H = 237; - Array2D p1 (H, W); - - fillPixels1 (p1, W, H); - writeReadScanLines ( - (tempDir + "imf_test_streams.exr").c_str (), W, H, p1); - writeReadScanLines (W, H, p1); - - fillPixels2 (p1, W, H); - writeReadTiles ((tempDir + "imf_test_streams2.exr").c_str (), W, H, p1); - writeReadTiles (W, H, p1); - - fillPixels1 (p1, W, H); - writeReadMultiPart ( - (tempDir + "imf_test_streams3.exr").c_str (), W, H, p1); - writeReadMultiPart (W, H, p1); + for (int compression = 0; compression < NUM_COMPRESSION_METHODS; + ++compression) + { + cout << "compression: " << compression << endl; + + Array2D p1 (H, W); + + fillPixels1 (p1, W, H); + writeReadScanLines ( + (tempDir + "imf_test_streams.exr").c_str (), + W, + H, + static_cast(compression), + p1); + writeReadScanLines (W, H, p1); + + fillPixels2 (p1, W, H); + writeReadTiles ( + (tempDir + "imf_test_streams2.exr").c_str (), + W, + H, + static_cast (compression), + p1); + writeReadTiles (W, H, p1); + + fillPixels1 (p1, W, H); + writeReadMultiPart ( + (tempDir + "imf_test_streams3.exr").c_str (), + W, + H, + static_cast (compression), + p1); + writeReadMultiPart (W, H, p1); + } cout << "ok\n" << endl; }