mirror of
https://github.com/etlegacy/EasyGen.git
synced 2024-11-10 06:42:16 +00:00
569 lines
15 KiB
C++
569 lines
15 KiB
C++
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (c) 2004, 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 <ImfRgbaFile.h>
|
|
#include <ImfTiledRgbaFile.h>
|
|
#include <ImfMultiPartInputFile.h>
|
|
#include <ImfMultiPartOutputFile.h>
|
|
#include <ImfPartType.h>
|
|
#include <ImfInputPart.h>
|
|
#include <ImfOutputPart.h>
|
|
#include <ImfStdIO.h>
|
|
#include <ImfArray.h>
|
|
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include "Iex.h"
|
|
#include <errno.h>
|
|
|
|
#include <vector>
|
|
#include <ImfChannelList.h>
|
|
|
|
#include "tmpDir.h"
|
|
|
|
using namespace OPENEXR_IMF_NAMESPACE;
|
|
using namespace std;
|
|
using namespace IMATH_NAMESPACE;
|
|
|
|
namespace {
|
|
|
|
void
|
|
fillPixels1 (Array2D<Rgba> &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<Rgba> &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 OPENEXR_IMF_NAMESPACE::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<Rgba> &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<Rgba> 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<Rgba> 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<Rgba> &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<Header> 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<Rgba> 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<Rgba> 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<Rgba> &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<Rgba> 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<Rgba> 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 ()
|
|
{
|
|
try
|
|
{
|
|
cout << "Testing reading and writing using existing streams" << endl;
|
|
|
|
const int W = 119;
|
|
const int H = 237;
|
|
|
|
Array2D<Rgba> p1 (H, W);
|
|
|
|
fillPixels1 (p1, W, H);
|
|
writeReadScanLines (IMF_TMP_DIR "imf_test_streams.exr", W, H, p1);
|
|
|
|
fillPixels2 (p1, W, H);
|
|
writeReadTiles (IMF_TMP_DIR "imf_test_streams2.exr", W, H, p1);
|
|
|
|
fillPixels1 (p1, W, H);
|
|
writeReadMultiPart (IMF_TMP_DIR "imf_test_streams3.exr", W, H, p1);
|
|
|
|
|
|
cout << "ok\n" << endl;
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
cerr << "ERROR -- caught exception: " << e.what() << endl;
|
|
assert (false);
|
|
}
|
|
}
|