/////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2004-2012, Industrial Light & Magic, a division of Lucas // Digital Ltd. LLC // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Industrial Light & Magic nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include #include "Iex.h" #include #include #include using namespace OPENEXR_IMF_NAMESPACE; using namespace std; using namespace IMATH_NAMESPACE; namespace { void fillPixels1 (Array2D &pixels, int w, int h) { for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { Rgba &p = pixels[y][x]; p.r = (x & 1); p.g = ((x + y) & 1); p.b = (y & 1); p.a = (p.r + p.b + p.g) / 3.0; } } } void fillPixels2 (Array2D &pixels, int w, int h) { for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { Rgba &p = pixels[y][x]; p.r = (x & 2); p.g = ((x + y) & 2); p.b = (y & 2); p.a = (p.r + p.b + p.g) / 3.0; } } } // // class MMIFStream -- a memory-mapped implementation of // class IStream based on class std::ifstream // 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[]); virtual ~MMIFStream (); virtual bool isMemoryMapped () const {return true;} virtual bool read (char c[/*n*/], int n); virtual char* readMemoryMapped (int n); virtual Int64 tellg () {return _pos;} virtual void seekg (Int64 pos) {_pos = pos;} virtual void clear () {} private: char* _buffer; Int64 _length; Int64 _pos; }; MMIFStream::MMIFStream (const char fileName[]): OPENEXR_IMF_NAMESPACE::IStream (fileName), _buffer (0), _length (0), _pos (0) { std::ifstream ifs (fileName, ios_base::binary); // // Get length of file // ifs.seekg (0, ios::end); _length = ifs.tellg(); ifs.seekg (0, ios::beg); // // Allocate memory // _buffer = new char [_length]; // // Read the entire file // ifs.read (_buffer, _length); ifs.close(); } MMIFStream::~MMIFStream () { delete [] _buffer; } bool MMIFStream::read (char c[/*n*/], int n) { if ((_pos < 0 || _pos >= _length) && n != 0) throw IEX_NAMESPACE::InputExc ("Unexpected end of file."); Int64 n2 = n; bool retVal = true; if (_length - _pos <= n2) { n2 = _length - _pos; retVal = false; } memcpy (c, &(_buffer[_pos]), n2); _pos += n2; return retVal; } char* MMIFStream::readMemoryMapped (int n) { if (_pos < 0 || _pos >= _length) throw IEX_NAMESPACE::InputExc ("Unexpected end of file."); if (_pos + n > _length) throw IEX_NAMESPACE::InputExc ("Reading past end of file."); char* retVal = &(_buffer[_pos]); _pos += n; return retVal; } void writeReadScanLines (const char fileName[], int width, int height, const Array2D &p1) { // // Save a scanline-based RGBA image, but instead of // letting the RgbaOutputFile object open the file, // make the RgbaOutputFile object use an existing // StdOFStream. Read the image back, using an // existing StdIFStream, and compare the pixels // with the original data. Then read the image // back a second time using a memory-mapped // MMIFStream (see above). // cout << "scan-line based file:" << endl; Header header (width, height); { cout << "writing"; remove (fileName); std::ofstream os (fileName, ios_base::binary); StdOFStream ofs (os, fileName); RgbaOutputFile out (ofs, header, WRITE_RGBA); out.setFrameBuffer (&p1[0][0], 1, width); out.writePixels (height); } { cout << ", reading"; std::ifstream is (fileName, ios_base::binary); StdIFStream ifs (is, fileName); RgbaInputFile in (ifs); const Box2i &dw = in.dataWindow(); int w = dw.max.x - dw.min.x + 1; int h = dw.max.y - dw.min.y + 1; int dx = dw.min.x; int dy = dw.min.y; Array2D p2 (h, w); in.setFrameBuffer (&p2[-dy][-dx], 1, w); in.readPixels (dw.min.y, dw.max.y); cout << ", comparing"; for (int y = 0; y < h; ++y) { 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); } } } { cout << ", reading (memory-mapped)"; MMIFStream ifs (fileName); RgbaInputFile in (ifs); const Box2i &dw = in.dataWindow(); int w = dw.max.x - dw.min.x + 1; int h = dw.max.y - dw.min.y + 1; int dx = dw.min.x; int dy = dw.min.y; Array2D p2 (h, w); in.setFrameBuffer (&p2[-dy][-dx], 1, w); in.readPixels (dw.min.y, dw.max.y); cout << ", comparing"; for (int y = 0; y < h; ++y) { 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); } } } cout << endl; remove (fileName); } void writeReadMultiPart (const char fileName[], int width, int height, const Array2D &p1) { // // Save a two scanline parts in an image, but instead of // letting the MultiPartOutputFile object open the file, // make the MultiPartOutputFile object use an existing // StdOFStream. Read the image back, using an // existing StdIFStream, and compare the pixels // with the original data. Then read the image // back a second time using a memory-mapped // MMIFStream (see above). // cout << "scan-line based mulitpart file:" << endl; vector
headers(2); headers[0] = Header(width, height); headers[0].setName("part1"); headers[0].channels().insert("R",Channel()); headers[0].channels().insert("G",Channel()); headers[0].channels().insert("B",Channel()); headers[0].channels().insert("A",Channel()); headers[0].setType(SCANLINEIMAGE); headers[1]=headers[0]; headers[1].setName("part2"); { cout << "writing"; remove (fileName); std::ofstream os (fileName, ios_base::binary); StdOFStream ofs (os, fileName); MultiPartOutputFile out (ofs, &headers[0],2); FrameBuffer f; f.insert("R",Slice(HALF,(char *) &p1[0][0].r,sizeof(Rgba),width*sizeof(Rgba))); f.insert("G",Slice(HALF,(char *) &p1[0][0].g,sizeof(Rgba),width*sizeof(Rgba))); f.insert("B",Slice(HALF,(char *) &p1[0][0].b,sizeof(Rgba),width*sizeof(Rgba))); f.insert("A",Slice(HALF,(char *) &p1[0][0].a,sizeof(Rgba),width*sizeof(Rgba))); for(int i=0;i<2;i++) { OutputPart p(out,i); p.setFrameBuffer (f); p.writePixels (height); } } { cout << ", reading"; std::ifstream is (fileName, ios_base::binary); StdIFStream ifs (is, fileName); MultiPartInputFile in (ifs); assert(in.parts() == 2); assert(in.header(0).dataWindow()==in.header(1).dataWindow()); const Box2i &dw = in.header(0).dataWindow(); int w = dw.max.x - dw.min.x + 1; int h = dw.max.y - dw.min.y + 1; int dx = dw.min.x; int dy = dw.min.y; Array2D p2 (h, w); FrameBuffer f; f.insert("R",Slice(HALF,(char *) &p2[-dy][-dx].r,sizeof(Rgba),w*sizeof(Rgba))); f.insert("G",Slice(HALF,(char *) &p2[-dy][-dx].g,sizeof(Rgba),w*sizeof(Rgba))); f.insert("B",Slice(HALF,(char *) &p2[-dy][-dx].b,sizeof(Rgba),w*sizeof(Rgba))); f.insert("A",Slice(HALF,(char *) &p2[-dy][-dx].a,sizeof(Rgba),w*sizeof(Rgba))); for(int part=0;part<2;part++) { InputPart p(in,part); p.setFrameBuffer(f); p.readPixels (dw.min.y, dw.max.y); cout << ", comparing pt " << part; for (int y = 0; y < h; ++y) { 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); } } } } { cout << ", reading (memory-mapped)"; MMIFStream ifs (fileName); MultiPartInputFile in (ifs); assert(in.parts() == 2); assert(in.header(0).dataWindow()==in.header(1).dataWindow()); const Box2i &dw = in.header(0).dataWindow(); int w = dw.max.x - dw.min.x + 1; int h = dw.max.y - dw.min.y + 1; int dx = dw.min.x; int dy = dw.min.y; Array2D p2 (h, w); FrameBuffer f; f.insert("R",Slice(HALF,(char *) &p2[-dy][-dx].r,sizeof(Rgba),w*sizeof(Rgba))); f.insert("G",Slice(HALF,(char *) &p2[-dy][-dx].g,sizeof(Rgba),w*sizeof(Rgba))); f.insert("B",Slice(HALF,(char *) &p2[-dy][-dx].b,sizeof(Rgba),w*sizeof(Rgba))); f.insert("A",Slice(HALF,(char *) &p2[-dy][-dx].a,sizeof(Rgba),w*sizeof(Rgba))); for(int part=0;part<2;part++) { InputPart p(in,part); p.setFrameBuffer(f); p.readPixels (dw.min.y, dw.max.y); cout << ", comparing pt " << part; for (int y = 0; y < h; ++y) { 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); } } } } cout << endl; remove (fileName); } void writeReadTiles (const char fileName[], int width, int height, const Array2D &p1) { // // Save a tiled RGBA image, but instead of letting // the TiledRgbaOutputFile object open the file, make // it use an existing StdOFStream. Read the image back, // using an existing StdIFStream, and compare the pixels // with the original data. Then read the image back a // second time using a memory-mapped MMIFStream (see above). // cout << "tiled file:" << endl; Header header (width, height); { cout << "writing"; remove (fileName); std::ofstream os (fileName, ios_base::binary); StdOFStream ofs (os, fileName); 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); } { cout << ", reading"; std::ifstream is (fileName, ios_base::binary); StdIFStream ifs (is, fileName); TiledRgbaInputFile in (ifs); const Box2i &dw = in.dataWindow(); int w = dw.max.x - dw.min.x + 1; int h = dw.max.y - dw.min.y + 1; int dx = dw.min.x; int dy = dw.min.y; Array2D p2 (h, w); 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) { 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); } } } { cout << ", reading (memory-mapped)"; MMIFStream ifs (fileName); TiledRgbaInputFile in (ifs); const Box2i &dw = in.dataWindow(); int w = dw.max.x - dw.min.x + 1; int h = dw.max.y - dw.min.y + 1; int dx = dw.min.x; int dy = dw.min.y; Array2D p2 (h, w); 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) { 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); } } } cout << endl; remove (fileName); } } // namespace void testExistingStreams (const std::string &tempDir) { try { cout << "Testing reading and writing using existing streams" << endl; 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); fillPixels2 (p1, W, H); writeReadTiles ((tempDir + "imf_test_streams2.exr").c_str(), W, H, p1); fillPixels1 (p1, W, H); writeReadMultiPart ((tempDir + "imf_test_streams3.exr").c_str(), W, H, p1); cout << "ok\n" << endl; } catch (const std::exception &e) { cerr << "ERROR -- caught exception: " << e.what() << endl; assert (false); } }