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