1 //-----------------------------------------------------------------------------
2 //
3 // ImageLib Sources
4 // Copyright (C) 2000-2009 by Denton Woods
5 // Last modified: 03/07/2009
6 //
7 // Filename: src-IL/src/il_exr.cpp
8 //
9 // Description: Reads from an OpenEXR (.exr) file using the OpenEXR library.
10 //
11 //-----------------------------------------------------------------------------
12
13 #include <machine/endian.h>
14 #include "il_internal.h"
15 #ifndef IL_NO_EXR
16
17 #ifndef HAVE_CONFIG_H // We are probably on a Windows box .
18 //#define OPENEXR_DLL
19 #define HALF_EXPORTS
20 #endif //HAVE_CONFIG_H
21
22 #include "il_exr.h"
23 #include <ImfRgba.h>
24 #include <ImfArray.h>
25 #include <ImfRgbaFile.h>
26 //#include <ImfTiledRgbaFile.h>
27 //#include <ImfInputFile.h>
28 //#include <ImfTiledInputFile.h>
29 //#include <ImfPreviewImage.h>
30 //#include <ImfChannelList.h>
31
32
33
34 #if (defined(_WIN32) || defined(_WIN64)) && defined(IL_USE_PRAGMA_LIBS)
35 #if defined(_MSC_VER) || defined(__BORLANDC__)
36 #ifndef _DEBUG
37 #pragma comment(lib, "openexr.lib")
38 #else
39 #pragma comment(lib, "openexr-d.lib")
40 #endif
41 #endif
42 #endif
43
44
45 //! Checks if the file specified in FileName is a valid EXR file.
ilIsValidExr(ILconst_string FileName)46 ILboolean ilIsValidExr(ILconst_string FileName)
47 {
48 ILHANDLE ExrFile;
49 ILboolean bExr = IL_FALSE;
50
51 if (!iCheckExtension(FileName, IL_TEXT("exr"))) {
52 ilSetError(IL_INVALID_EXTENSION);
53 return bExr;
54 }
55
56 ExrFile = iopenr(FileName);
57 if (ExrFile == NULL) {
58 ilSetError(IL_COULD_NOT_OPEN_FILE);
59 return bExr;
60 }
61
62 bExr = ilIsValidExrF(ExrFile);
63 icloser(ExrFile);
64
65 return bExr;
66 }
67
68
69 //! Checks if the ILHANDLE contains a valid EXR file at the current position.
ilIsValidExrF(ILHANDLE File)70 ILboolean ilIsValidExrF(ILHANDLE File)
71 {
72 ILuint FirstPos;
73 ILboolean bRet;
74
75 iSetInputFile(File);
76 FirstPos = itell();
77 bRet = iIsValidExr();
78 iseek(FirstPos, IL_SEEK_SET);
79
80 return bRet;
81 }
82
83
84 //! Checks if Lump is a valid EXR lump.
ilIsValidExrL(const void * Lump,ILuint Size)85 ILboolean ilIsValidExrL(const void *Lump, ILuint Size)
86 {
87 iSetInputLump(Lump, Size);
88 return iIsValidExr();
89 }
90
91
92 // Internal function used to get the EXR header from the current file.
iGetExrHead(EXRHEAD * Header)93 ILboolean iGetExrHead(EXRHEAD *Header)
94 {
95 Header->MagicNumber = GetLittleUInt();
96 Header->Version = GetLittleUInt();
97
98 return IL_TRUE;
99 }
100
101
102 // Internal function to get the header and check it.
iIsValidExr()103 ILboolean iIsValidExr()
104 {
105 EXRHEAD Head;
106
107 if (!iGetExrHead(&Head))
108 return IL_FALSE;
109 iseek(-8, IL_SEEK_CUR);
110
111 return iCheckExr(&Head);
112 }
113
114
115 // Internal function used to check if the HEADER is a valid EXR header.
iCheckExr(EXRHEAD * Header)116 ILboolean iCheckExr(EXRHEAD *Header)
117 {
118 // The file magic number (signature) is 0x76, 0x2f, 0x31, 0x01
119 if (Header->MagicNumber != 0x01312F76)
120 return IL_FALSE;
121 // The only valid version so far is version 2. The upper value has
122 // to do with tiling.
123 if (Header->Version != 0x002 && Header->Version != 0x202)
124 return IL_FALSE;
125
126 return IL_TRUE;
127 }
128
129
130 // Nothing to do here in the constructor.
ilIStream()131 ilIStream::ilIStream() : Imf::IStream("N/A")
132 {
133 return;
134 }
135
136
read(char c[],int n)137 bool ilIStream::read(char c[], int n)
138 {
139 if (iread(c, 1, n) != n)
140 return false;
141 return true;
142 }
143
144
145 //@TODO: Make this work with 64-bit values.
tellg()146 uint64_t ilIStream::tellg()
147 {
148 uint64_t Pos;
149
150 // itell only returns a 32-bit value!
151 Pos = itell();
152
153 return Pos;
154 }
155
156
157 // Note that there is no return value here, even though there probably should be.
158 //@TODO: Make this work with 64-bit values.
seekg(uint64_t Pos)159 void ilIStream::seekg(uint64_t Pos)
160 {
161 // iseek only uses a 32-bit value!
162 iseek((ILint)Pos, IL_SEEK_SET); // I am assuming this is seeking from the beginning.
163 return;
164 }
165
166
clear()167 void ilIStream::clear()
168 {
169 return;
170 }
171
172
173 //! Reads an .exr file.
ilLoadExr(ILconst_string FileName)174 ILboolean ilLoadExr(ILconst_string FileName)
175 {
176 ILHANDLE ExrFile;
177 ILboolean bExr = IL_FALSE;
178
179 ExrFile = iopenr(FileName);
180 if (ExrFile == NULL) {
181 ilSetError(IL_COULD_NOT_OPEN_FILE);
182 return bExr;
183 }
184
185 bExr = ilLoadExrF(ExrFile);
186 icloser(ExrFile);
187
188 return bExr;
189 }
190
191
192 //! Reads an already-opened .exr file
ilLoadExrF(ILHANDLE File)193 ILboolean ilLoadExrF(ILHANDLE File)
194 {
195 ILuint FirstPos;
196 ILboolean bRet;
197
198 iSetInputFile(File);
199 FirstPos = itell();
200 bRet = iLoadExrInternal();
201 iseek(FirstPos, IL_SEEK_SET);
202
203 return bRet;
204 }
205
206
207 //! Reads from a memory "lump" that contains an .exr
ilLoadExrL(const void * Lump,ILuint Size)208 ILboolean ilLoadExrL(const void *Lump, ILuint Size)
209 {
210 iSetInputLump(Lump, Size);
211 return iLoadExrInternal();
212 }
213
214
215 using namespace Imath;
216 using namespace Imf;
217 using namespace std;
218
219
iLoadExrInternal()220 ILboolean iLoadExrInternal()
221 {
222 Array<Rgba> pixels;
223 Box2i dataWindow;
224 float pixelAspectRatio;
225 ILfloat *FloatData;
226
227 ilIStream File;
228 RgbaInputFile in(File);
229
230 Rgba a;
231 dataWindow = in.dataWindow();
232 pixelAspectRatio = in.pixelAspectRatio();
233
234 int dw, dh, dx, dy;
235
236 dw = dataWindow.max.x - dataWindow.min.x + 1;
237 dh = dataWindow.max.y - dataWindow.min.y + 1;
238 dx = dataWindow.min.x;
239 dy = dataWindow.min.y;
240
241 pixels.resizeErase (dw * dh);
242 in.setFrameBuffer (pixels - dx - dy * dw, 1, dw);
243
244 try
245 {
246 in.readPixels (dataWindow.min.y, dataWindow.max.y);
247 }
248 catch (const exception)
249 {
250 // If some of the pixels in the file cannot be read,
251 // print an error message, and return a partial image
252 // to the caller.
253 ilSetError(IL_LIB_EXR_ERROR); // Could I use something a bit more descriptive based on e?
254 return IL_FALSE;
255 }
256
257 //if (ilTexImage(dw, dh, 1, 4, IL_RGBA, IL_UNSIGNED_BYTE, NULL) == IL_FALSE)
258 //if (ilTexImage(dw, dh, 1, 4, IL_RGBA, IL_UNSIGNED_SHORT, NULL) == IL_FALSE)
259 if (ilTexImage(dw, dh, 1, 4, IL_RGBA, IL_FLOAT, NULL) == IL_FALSE)
260 return IL_FALSE;
261
262 // Determine where the origin is in the original file.
263 if (in.lineOrder() == INCREASING_Y)
264 iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
265 else
266 iCurImage->Origin = IL_ORIGIN_LOWER_LEFT;
267
268 // Better to access FloatData instead of recasting everytime.
269 FloatData = (ILfloat*)iCurImage->Data;
270
271 for (int i = 0; i < dw * dh; i++)
272 {
273 // Too much data lost
274 //iCurImage->Data[i * 4 + 0] = (ILubyte)(pixels[i].r.bits() >> 8);
275 //iCurImage->Data[i * 4 + 1] = (ILubyte)(pixels[i].g.bits() >> 8);
276 //iCurImage->Data[i * 4 + 2] = (ILubyte)(pixels[i].b.bits() >> 8);
277 //iCurImage->Data[i * 4 + 3] = (ILubyte)(pixels[i].a.bits() >> 8);
278
279 // The images look kind of washed out with this.
280 //((ILshort*)(iCurImage->Data))[i * 4 + 0] = pixels[i].r.bits();
281 //((ILshort*)(iCurImage->Data))[i * 4 + 1] = pixels[i].g.bits();
282 //((ILshort*)(iCurImage->Data))[i * 4 + 2] = pixels[i].b.bits();
283 //((ILshort*)(iCurImage->Data))[i * 4 + 3] = pixels[i].a.bits();
284
285 // This gives the best results, since no data is lost.
286 FloatData[i * 4] = pixels[i].r;
287 FloatData[i * 4 + 1] = pixels[i].g;
288 FloatData[i * 4 + 2] = pixels[i].b;
289 FloatData[i * 4 + 3] = pixels[i].a;
290 }
291
292 // Converts the image to predefined type, format and/or origin if needed.
293 return ilFixImage();
294 }
295
296
297
298 // Nothing to do here in the constructor.
ilOStream()299 ilOStream::ilOStream() : Imf::OStream("N/A")
300 {
301 return;
302 }
303
write(const char c[],int n)304 void ilOStream::write(const char c[], int n)
305 {
306 iwrite(c, 1, n); //@TODO: Throw an exception here.
307 return;
308 }
309
310 //@TODO: Make this work with 64-bit values.
tellp()311 uint64_t ilOStream::tellp()
312 {
313 uint64_t Pos;
314
315 // itellw only returns a 32-bit value!
316 Pos = itellw();
317
318 return Pos;
319 }
320
321 // Note that there is no return value here, even though there probably should be.
322 //@TODO: Make this work with 64-bit values.
seekp(uint64_t Pos)323 void ilOStream::seekp(uint64_t Pos)
324 {
325 // iseekw only uses a 32-bit value!
326 iseekw((ILint)Pos, IL_SEEK_SET); // I am assuming this is seeking from the beginning.
327 return;
328 }
329
330
331 //! Writes a Exr file
ilSaveExr(const ILstring FileName)332 ILboolean ilSaveExr(const ILstring FileName)
333 {
334 ILHANDLE ExrFile;
335 ILuint ExrSize;
336
337 if (ilGetBoolean(IL_FILE_MODE) == IL_FALSE) {
338 if (iFileExists(FileName)) {
339 ilSetError(IL_FILE_ALREADY_EXISTS);
340 return IL_FALSE;
341 }
342 }
343
344 ExrFile = iopenw(FileName);
345 if (ExrFile == NULL) {
346 ilSetError(IL_COULD_NOT_OPEN_FILE);
347 return IL_FALSE;
348 }
349
350 ExrSize = ilSaveExrF(ExrFile);
351 iclosew(ExrFile);
352
353 if (ExrSize == 0)
354 return IL_FALSE;
355 return IL_TRUE;
356 }
357
358
359 //! Writes a Exr to an already-opened file
ilSaveExrF(ILHANDLE File)360 ILuint ilSaveExrF(ILHANDLE File)
361 {
362 ILuint Pos;
363 iSetOutputFile(File);
364 Pos = itellw();
365 if (iSaveExrInternal() == IL_FALSE)
366 return 0; // Error occurred
367 return itellw() - Pos; // Return the number of bytes written.
368 }
369
370
371 //! Writes a Exr to a memory "lump"
ilSaveExrL(void * Lump,ILuint Size)372 ILuint ilSaveExrL(void *Lump, ILuint Size)
373 {
374 ILuint Pos = itellw();
375 iSetOutputLump(Lump, Size);
376 if (iSaveExrInternal() == IL_FALSE)
377 return 0; // Error occurred
378 return itellw() - Pos; // Return the number of bytes written.
379 }
380
381
iSaveExrInternal()382 ILboolean iSaveExrInternal()
383 {
384 Imath::Box2i DataWindow(Imath::V2i(0, 0), Imath::V2i(iCurImage->Width-1, iCurImage->Height-1));
385 Imf::LineOrder Order;
386 if (iCurImage->Origin == IL_ORIGIN_LOWER_LEFT)
387 Order = DECREASING_Y;
388 else
389 Order = INCREASING_Y;
390 Imf::Header Head(iCurImage->Width, iCurImage->Height, DataWindow, 1, Imath::V2f (0, 0), 1, Order);
391
392 ilOStream File;
393 Imf::RgbaOutputFile Out(File, Head);
394 ILimage *TempImage = iCurImage;
395
396 //@TODO: Can we always assume that Rgba is packed the same?
397 Rgba *HalfData = (Rgba*)ialloc(TempImage->Width * TempImage->Height * sizeof(Rgba));
398 if (HalfData == NULL)
399 return IL_FALSE;
400
401 if (iCurImage->Format != IL_RGBA || iCurImage->Type != IL_FLOAT) {
402 TempImage = iConvertImage(iCurImage, IL_RGBA, IL_FLOAT);
403 if (TempImage == NULL) {
404 ifree(HalfData);
405 return IL_FALSE;
406 }
407 }
408
409 ILuint Offset = 0;
410 ILfloat *FloatPtr = (ILfloat*)TempImage->Data;
411 for (unsigned int y = 0; y < TempImage->Height; y++) {
412 for (unsigned int x = 0; x < TempImage->Width; x++) {
413 HalfData[y * TempImage->Width + x].r = FloatPtr[Offset];
414 HalfData[y * TempImage->Width + x].g = FloatPtr[Offset + 1];
415 HalfData[y * TempImage->Width + x].b = FloatPtr[Offset + 2];
416 HalfData[y * TempImage->Width + x].a = FloatPtr[Offset + 3];
417 Offset += 4; // 4 floats
418 }
419 }
420
421 Out.setFrameBuffer(HalfData, 1, TempImage->Width);
422 Out.writePixels(TempImage->Height); //@TODO: Do each scanline separately to keep from using so much memory.
423
424 // Free our half data.
425 ifree(HalfData);
426 // Destroy our temporary image if we used one.
427 if (TempImage != iCurImage)
428 ilCloseImage(TempImage);
429
430 return IL_TRUE;
431 }
432
433
434 #endif //IL_NO_EXR
435