1
///-----------------------------------------
2
/// OpenEXR file read & write
3
///-----------------------------------------
7
#include <ImfTiledOutputFile.h>
8
#include <ImfChannelList.h>
9
#include <ImfMatrixAttribute.h>
10
#include <ImfIntAttribute.h>
11
#include <ImfBoxAttribute.h>
12
// for rgba image loader
13
#include <ImfRgbaFile.h>
15
// same, using general interface
16
#include <ImfInputFile.h>
22
using namespace Imath;
26
// fixed tilesize value for now, but probably will leave it like that anyway, seems good average value
27
// WARNING, duplicate! also defined in maketexture.cpp
29
void writeTiledZ(const char fileName[], float* Zbuf, int width, int height,
30
int ortho, const float w2c[4][4], const float c2r[4][4])
34
Header header(width, height);
35
// default zip is best probably, lossles too, pxr24 is possible (though lossy) alternative, seems to work well for Z
36
//header.compression() = PXR24_COMPRESSION;
37
//header.compression() = NO_COMPRESSION;
38
header.channels().insert("Z", Channel(FLOAT));
39
header.setTileDescription(TileDescription(TILESIZE, TILESIZE, ONE_LEVEL));
40
// this is used as ID, value not used, text itself is the id
41
header.insert("QD_shadowmap", IntAttribute(0));
42
header.insert("w2c", M44fAttribute(M44f(w2c)));
43
header.insert("c2r", M44fAttribute(M44f(c2r)));
44
header.insert("ortho", IntAttribute(ortho));
45
TiledOutputFile out(fileName, header);
46
FrameBuffer frameBuffer;
47
frameBuffer.insert("Z", Slice(FLOAT, (char*)Zbuf, sizeof(float), sizeof(float)*width));
48
out.setFrameBuffer(frameBuffer);
49
for (int tileY=0; tileY<out.numYTiles(); ++tileY)
50
for (int tileX=0; tileX<out.numXTiles(); ++tileX)
51
out.writeTile(tileX, tileY);
53
catch (const std::exception &exc)
55
cout << "writeTiledZ() -> " << exc.what() << endl;
60
float* readTiledZ(const char fileName[], int& width, int& height,
61
int& ortho, float w2c[4][4], float c2r[4][4])
66
TiledInputFile in(fileName);
67
const Header& h = in.header();
68
const M44fAttribute& ma1 = h.typedAttribute<M44fAttribute>("w2c").value();
69
const M44fAttribute& ma2 = h.typedAttribute<M44fAttribute>("c2r").value();
70
const IntAttribute& ia1 = h.typedAttribute<IntAttribute>("ortho").value();
71
memcpy(w2c, &(ma1.value()[0][0]), sizeof(float)*16);
72
memcpy(c2r, &(ma2.value()[0][0]), sizeof(float)*16);
74
const Box2i dw = h.dataWindow();
75
width = dw.max.x - dw.min.x + 1;
76
height = dw.max.y - dw.min.y + 1;
77
const int dx = dw.min.x, dy = dw.min.y;
78
zmap = new float[width*height];
79
FrameBuffer frameBuffer;
80
frameBuffer.insert("Z", Slice(FLOAT, (char*)&zmap[-dy*width - dx], sizeof(float), sizeof(float)*width));
81
in.setFrameBuffer(frameBuffer);
82
for (int tileY=0; tileY<in.numYTiles(); ++tileY)
83
for (int tileX=0; tileX<in.numXTiles(); ++tileX)
84
in.readTile(tileX, tileY);
87
catch (const std::exception &exc)
89
cout << "readTiledZ() -> " << exc.what() << endl;
90
if (zmap) { delete[] zmap; zmap = NULL; }
96
//----------------------------------------------------------------------------------------------------------------
99
// yet another stupid error was here, used TILESIZE in the loading routines,
100
// but of course the image does not have necessarily that tilesize, must get from file
101
EXRbuf_t::EXRbuf_t(const char fname[]) : in(NULL), tmp_tile(NULL), tdsize(0), numchan(0), have_alpha(false)
105
in = new TiledInputFile(fname);
106
const Header& h = in->header();
107
if (h.findTypedAttribute<IntAttribute>("QD_shadowmap")) {
108
// regular shadowmap, Z only
109
const M44fAttribute& ma1 = h.typedAttribute<M44fAttribute>("w2c").value();
110
memcpy(w2c, &(ma1.value()[0][0]), sizeof(float)*16);
111
const M44fAttribute& ma2 = h.typedAttribute<M44fAttribute>("c2r").value();
112
memcpy(c2r, &(ma2.value()[0][0]), sizeof(float)*16);
113
const IntAttribute& ia1 = h.typedAttribute<IntAttribute>("ortho").value();
115
imgtype = IT_SHADOWMAP;
118
// no temporary tile needed in this case
121
// must be texmap, value not used, just here to trigger exception if not found
122
h.typedAttribute<IntAttribute>("QD_texturemap");
123
imgtype = IT_TEXTUREMAP;
124
const Channel* Ychan = in->header().channels().findChannel("Y");
125
// get image data type
132
const Channel* rchan = in->header().channels().findChannel("R");
133
if ((rchan == NULL) || (in->header().channels().findChannel("G") == NULL) || (in->header().channels().findChannel("B") == NULL))
134
throw Iex::InputExc("Image has some or all required RGB channels missing?");
136
have_alpha = (in->header().channels().findChannel("A") != NULL);
137
numchan += have_alpha ? 4 : 3;
139
const int tilesize = in->tileXSize() * in->tileYSize() * numchan;
142
tmp_tile = new unsigned int [tilesize];
144
else if (tp == HALF) {
146
tmp_tile = new half [tilesize];
148
else if (tp == FLOAT) {
150
tmp_tile = new float [tilesize];
152
else // unless a new type gets added, this should never happen
153
throw Iex::InputExc("EXRbuf_t() -> Unknown image data type?");
156
catch (const std::exception &exc)
158
cout << "EXRbuf_t() -> " << exc.what() << endl;
164
EXRbuf_t::~EXRbuf_t()
166
if (in) { delete in; in = NULL; }
167
if (tmp_tile) { delete[] (int*)tmp_tile; tmp_tile = NULL; } // cast only to make compiler happy (delete void undefined)
170
template<typename cpptype, PixelType exrtype>
171
bool getTile(TiledInputFile* in, const tileID_t& tileID,
172
cpptype* Rtile, cpptype* Gtile, cpptype *Btile, cpptype* Atile,
173
cpptype* Ztile = NULL, cpptype* Ytile = NULL)
177
const Box2i b = in->dataWindowForTile(tileID.tileX, tileID.tileY, tileID.levelX, tileID.levelY);
179
const int tx = in->tileXSize(), p = -b.min.y*tx - b.min.x; // must be int, negative offset possible
180
if (Rtile) fb.insert("R", Slice(exrtype, (char*)&Rtile[p], sizeof( cpptype ), sizeof( cpptype )*tx));
181
if (Btile) fb.insert("G", Slice(exrtype, (char*)&Gtile[p], sizeof( cpptype ), sizeof( cpptype )*tx));
182
if (Gtile) fb.insert("B", Slice(exrtype, (char*)&Btile[p], sizeof( cpptype ), sizeof( cpptype )*tx));
183
if (Atile) fb.insert("A", Slice(exrtype, (char*)&Atile[p], sizeof( cpptype ), sizeof( cpptype )*tx));
184
if (Ztile) fb.insert("Z", Slice(exrtype, (char*)&Ztile[p], sizeof( cpptype ), sizeof( cpptype )*tx));
185
if (Ytile) fb.insert("Y", Slice(exrtype, (char*)&Ytile[p], sizeof( cpptype ), sizeof( cpptype )*tx));
186
in->setFrameBuffer(fb);
187
in->readTile(tileID.tileX, tileID.tileY, tileID.levelX, tileID.levelY);
189
catch (const std::exception &exc)
191
cout << "getTile<>() -> " << exc.what() << endl;
198
// Currently this converts the exr data to interleaved form,
199
// so it uses an intermediate tile, copying data, which is all completely unnecessary overhead.
200
// It really should be simplified to either save the tiles in the required interleaved format
201
// (which as far as I know is possible, but would not be compatible anymore with exrmaketiled)
202
// or let the texture cache code handle it all when a color is requested, which probably is best TODO
203
RtColor* EXRbuf_t::getColorTile(const tileID_t& tileID) const
206
const int tx = in->tileXSize(), ty = in->tileYSize();
207
const unsigned int colofs = tx*ty;
208
tdsize = colofs*sizeof(RtColor); // tile data size in bytes
209
if (dttype == IT_UINT) {
210
unsigned int* icoltile = reinterpret_cast<unsigned int*>(tmp_tile);
211
RtColor* coltile = NULL;
212
if (getTile<unsigned int, UINT>(in, tileID, icoltile, icoltile + colofs,
213
icoltile + colofs*2, (have_alpha ? (icoltile + colofs*3) : NULL)))
216
const float c2f = 1.f/255.f;
217
coltile = new RtColor[colofs];
218
for (unsigned int i=0; i<colofs; ++i)
219
coltile[i][0] = c2f*icoltile[i], coltile[i][1] = c2f*icoltile[i + colofs], coltile[i][2] = c2f*icoltile[i + colofs*2];
223
else if (dttype == IT_HALF) {
224
half* hcoltile = reinterpret_cast<half*>(tmp_tile);
225
RtColor* coltile = NULL;
226
if (getTile<half, HALF>(in, tileID, hcoltile, hcoltile + colofs,
227
hcoltile + colofs*2, (have_alpha ? (hcoltile + colofs*3) : NULL)))
229
// half r/g/b/a -> RtColor
230
coltile = new RtColor[colofs];
231
for (unsigned int i=0; i<colofs; ++i)
232
coltile[i][0] = hcoltile[i], coltile[i][1] = hcoltile[i + colofs], coltile[i][2] = hcoltile[i + colofs*2];
237
float* fcoltile = reinterpret_cast<float*>(tmp_tile);
238
RtColor* coltile = NULL;
239
if (getTile<float, FLOAT>(in, tileID, fcoltile, fcoltile + colofs,
240
fcoltile + colofs*2, (have_alpha ? (fcoltile + colofs*3) : NULL)))
242
// float r/g/b/a -> RtColor
243
coltile = new RtColor[colofs];
244
for (unsigned int i=0; i<colofs; ++i)
245
coltile[i][0] = fcoltile[i], coltile[i][1] = fcoltile[i + colofs], coltile[i][2] = fcoltile[i + colofs*2];
254
// used for Z depth shadowmap or Y grayscale displacementmap
255
float* EXRbuf_t::getFloatTile(const tileID_t& tileID) const
257
if (in && ((imgtype == IT_SHADOWMAP) || (numchan == 1))) {
258
const unsigned int tilesize = in->tileXSize() * in->tileYSize();
259
tdsize = tilesize*sizeof(float); // tile data size in bytes
260
float* Ftile = new float[tilesize];
261
if (imgtype == IT_SHADOWMAP) // Z
262
getTile<float, FLOAT>(in, tileID, NULL, NULL, NULL, NULL, Ftile);
263
else // probably single channel luminance (possibly displacementmap)
264
getTile<float, FLOAT>(in, tileID, NULL, NULL, NULL, NULL, NULL, Ftile);
270
inline void* makeTmpBuf(PixelType pt, unsigned int sz, unsigned int& tpsize)
274
tpsize = sizeof(unsigned int);
275
return new unsigned int[sz];
277
tpsize = sizeof(half);
281
tpsize = sizeof(float);
282
return new float[sz];
286
imgbuf_t* loadEXR(const char filename[], bool force_grayscale)
290
InputFile file(filename);
291
Box2i dw = file.header().dataWindow();
292
const int width = dw.max.x - dw.min.x + 1;
293
const int height = dw.max.y - dw.min.y + 1;
294
const unsigned int sz = width*height;
295
const ChannelList &chans = file.header().channels();
296
const Channel* Ychan = chans.findChannel("Y");
298
// assume luminance channel only (strangely, channellist does not seem to have a size() type of function ??)
300
void* tmpbuf = makeTmpBuf(Ychan->type, sz, tpsize);
302
fb.insert("Y", Slice(Ychan->type, (char*)tmpbuf - (dw.min.x - dw.min.y*width)*tpsize, tpsize, tpsize*width));
303
file.setFrameBuffer(fb);
304
file.readPixels(dw.min.y, dw.max.y);
306
imgbuf_t* img = new imgbuf_t(width, height, 1, (Ychan->type == UINT) ? IMG_BYTE : IMG_FLOAT);
307
if (Ychan->type == HALF) {
308
half* tbufp = reinterpret_cast<half*>(tmpbuf);
309
float* fbufp = reinterpret_cast<float*>(img->getData());
310
for (unsigned int i=0; i<sz; ++i)
313
else if (Ychan->type == UINT) { // assume uint in range of char
314
unsigned int* tbufp = reinterpret_cast<unsigned int*>(tmpbuf);
315
unsigned char* cbufp = reinterpret_cast<unsigned char*>(img->getData());
316
for (unsigned int i=0; i<sz; ++i)
317
*cbufp++ = (unsigned char)*tbufp++;
320
memcpy(img->getData(), tmpbuf, sizeof(float)*sz);
321
delete[] (unsigned long*)tmpbuf; // cast only needed to make compiler happy
325
const Channel* Rchan = chans.findChannel("R");
326
const Channel* Gchan = chans.findChannel("G");
327
const Channel* Bchan = chans.findChannel("B");
328
const Channel* Achan = chans.findChannel("A");
329
if ((Rchan == NULL) || (Gchan == NULL) || (Bchan == NULL))
330
throw Iex::InputExc("Image must have at least all RGB color channels!");
331
// all channels must be the same pixeltype
332
if ((Rchan->type != Gchan->type) || (Rchan->type != Bchan->type) || (Achan ? (Rchan->type != Achan->type) : false))
333
throw Iex::InputExc("All image channels must be of the same type!");
335
void *Gtmpbuf = NULL, *Btmpbuf = NULL, *Atmpbuf = NULL;
336
void* Rtmpbuf = makeTmpBuf(Rchan->type, sz, tpsize);
337
if (!force_grayscale) {
338
Gtmpbuf = makeTmpBuf(Gchan->type, sz, tpsize);
339
Btmpbuf = makeTmpBuf(Bchan->type, sz, tpsize);
340
Atmpbuf = Achan ? makeTmpBuf(Achan->type, sz, tpsize) : NULL;
343
fb.insert("R", Slice(Rchan->type, (char*)Rtmpbuf - (dw.min.x - dw.min.y*width)*tpsize, tpsize, tpsize*width));
344
if (!force_grayscale) {
345
fb.insert("G", Slice(Gchan->type, (char*)Gtmpbuf - (dw.min.x - dw.min.y*width)*tpsize, tpsize, tpsize*width));
346
fb.insert("B", Slice(Bchan->type, (char*)Btmpbuf - (dw.min.x - dw.min.y*width)*tpsize, tpsize, tpsize*width));
348
fb.insert("A", Slice(Achan->type, (char*)Atmpbuf - (dw.min.x - dw.min.y*width)*tpsize, tpsize, tpsize*width));
350
file.setFrameBuffer(fb);
351
file.readPixels(dw.min.y, dw.max.y);
353
imgbuf_t* img = new imgbuf_t(width, height, (force_grayscale ? 1 : (Achan ? 4 : 3)), (Rchan->type == UINT) ? IMG_BYTE : IMG_FLOAT);
354
if (Rchan->type == HALF) {
355
half *Gtbufp = NULL, *Btbufp = NULL, *Atbufp = NULL;
356
half* Rtbufp = reinterpret_cast<half*>(Rtmpbuf);
357
if (!force_grayscale) {
358
Gtbufp = reinterpret_cast<half*>(Gtmpbuf);
359
Btbufp = reinterpret_cast<half*>(Btmpbuf);
360
Atbufp = Atmpbuf ? NULL : reinterpret_cast<half*>(Atmpbuf);
362
float* fbufp = reinterpret_cast<float*>(img->getData());
363
for (unsigned int i=0; i<sz; ++i) {
364
*fbufp++ = *Rtbufp++;
365
if (!force_grayscale) {
366
*fbufp++ = *Gtbufp++;
367
*fbufp++ = *Btbufp++;
368
if (Atbufp) *fbufp++ = *Atbufp++;
372
else if (Rchan->type == UINT) { // assume uint in range of char
373
unsigned int *Gtbufp = NULL, *Btbufp = NULL, *Atbufp = NULL;
374
unsigned int* Rtbufp = reinterpret_cast<unsigned int*>(Rtmpbuf);
375
if (!force_grayscale) {
376
Gtbufp = reinterpret_cast<unsigned int*>(Gtmpbuf);
377
Btbufp = reinterpret_cast<unsigned int*>(Btmpbuf);
378
Atbufp = Atmpbuf ? reinterpret_cast<unsigned int*>(Atmpbuf) : NULL;
380
unsigned char* cbufp = reinterpret_cast<unsigned char*>(img->getData());
381
for (unsigned int i=0; i<sz; ++i) {
382
*cbufp++ = (unsigned char)*Rtbufp++;
383
if (!force_grayscale) {
384
*cbufp++ = (unsigned char)*Gtbufp++;
385
*cbufp++ = (unsigned char)*Btbufp++;
386
if (Atbufp) *cbufp++ = (unsigned char)*Atbufp++;
391
float *Gtbufp = NULL, *Btbufp = NULL, *Atbufp = NULL;
392
float* Rtbufp = reinterpret_cast<float*>(Rtmpbuf);
393
if (!force_grayscale) {
394
Gtbufp = reinterpret_cast<float*>(Gtmpbuf);
395
Btbufp = reinterpret_cast<float*>(Btmpbuf);
396
Atbufp = Atmpbuf ? NULL : reinterpret_cast<float*>(Atmpbuf);
398
float* fbufp = reinterpret_cast<float*>(img->getData());
399
for (unsigned int i=0; i<sz; ++i) {
400
*fbufp++ = *Rtbufp++;
401
if (!force_grayscale) {
402
*fbufp++ = *Gtbufp++;
403
*fbufp++ = *Btbufp++;
404
if (Atbufp) *fbufp++ = *Atbufp++;
408
delete[] (unsigned long*)Rtmpbuf;
409
if (Gtmpbuf) delete[] (unsigned long*)Gtmpbuf;
410
if (Btmpbuf) delete[] (unsigned long*)Btmpbuf;
411
if (Atmpbuf) delete[] (unsigned long*)Atmpbuf;
414
catch (const exception &exc)
416
cerr << "[ERROR] -> loadEXR(): " << exc.what() << endl;