PageRenderTime 115ms CodeModel.GetById 27ms app.highlight 82ms RepoModel.GetById 1ms app.codeStats 0ms

/src/FreeImage/Source/FreeImage/PluginHDR.cpp

https://bitbucket.org/cabalistic/ogredeps/
C++ | 719 lines | 510 code | 98 blank | 111 comment | 124 complexity | 4c308290944fb12d8d53a5a0569e275f MD5 | raw file
  1// ==========================================================
  2// HDR Loader and writer
  3//
  4// Design and implementation by 
  5// - Hervé Drolon (drolon@infonie.fr)
  6//
  7// This file is part of FreeImage 3
  8//
  9// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
 10// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
 11// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
 12// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
 13// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
 14// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
 15// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
 16// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
 17// THIS DISCLAIMER.
 18//
 19// Use at your own risk!
 20// ==========================================================
 21
 22#include "FreeImage.h"
 23#include "Utilities.h"
 24
 25// ==========================================================
 26// Plugin Interface
 27// ==========================================================
 28
 29static int s_format_id;
 30
 31// ==========================================================
 32// RGBE library
 33// ==========================================================
 34
 35// ----------------------------------------------------------
 36
 37// maximum size of a line in the header
 38#define HDR_MAXLINE	256
 39
 40// flags indicating which fields in an rgbeHeaderInfo are valid
 41#define RGBE_VALID_PROGRAMTYPE	0x01
 42#define RGBE_VALID_COMMENT		0x02
 43#define RGBE_VALID_GAMMA		0x04
 44#define RGBE_VALID_EXPOSURE		0x08
 45
 46// offsets to red, green, and blue components in a data (float) pixel
 47#define RGBE_DATA_RED    0
 48#define RGBE_DATA_GREEN  1
 49#define RGBE_DATA_BLUE   2
 50
 51// ----------------------------------------------------------
 52#ifdef _WIN32
 53#pragma pack(push, 1)
 54#else
 55#pragma pack(1)
 56#endif
 57
 58typedef struct tagHeaderInfo {
 59	int valid;					// indicate which fields are valid
 60	char programtype[16];		// listed at beginning of file to identify it after "#?". defaults to "RGBE"
 61	char comment[HDR_MAXLINE];	// comment beginning with "# " 
 62	float gamma;				// image has already been gamma corrected with given gamma. defaults to 1.0 (no correction)
 63	float exposure;				// a value of 1.0 in an image corresponds to <exposure> watts/steradian/m^2. defaults to 1.0
 64} rgbeHeaderInfo;
 65
 66#ifdef _WIN32
 67#pragma pack(pop)
 68#else
 69#pragma pack()
 70#endif
 71
 72typedef enum {
 73	rgbe_read_error,
 74	rgbe_write_error,
 75	rgbe_format_error,
 76	rgbe_memory_error,
 77} rgbe_error_code;
 78
 79// ----------------------------------------------------------
 80// Prototypes
 81// ----------------------------------------------------------
 82
 83static BOOL rgbe_Error(rgbe_error_code error_code, const char *msg);
 84static BOOL rgbe_GetLine(FreeImageIO *io, fi_handle handle, char *buffer, int length);
 85static inline void rgbe_FloatToRGBE(BYTE rgbe[4], FIRGBF *rgbf);
 86static inline void rgbe_RGBEToFloat(FIRGBF *rgbf, BYTE rgbe[4]);
 87static BOOL rgbe_ReadHeader(FreeImageIO *io, fi_handle handle, unsigned *width, unsigned *height, rgbeHeaderInfo *header_info);
 88static BOOL rgbe_WriteHeader(FreeImageIO *io, fi_handle handle, unsigned width, unsigned height, rgbeHeaderInfo *info);
 89static BOOL rgbe_ReadPixels(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned numpixels);
 90static BOOL rgbe_WritePixels(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned numpixels);
 91static BOOL rgbe_ReadPixels_RLE(FreeImageIO *io, fi_handle handle, FIRGBF *data, int scanline_width, unsigned num_scanlines);
 92static BOOL rgbe_WriteBytes_RLE(FreeImageIO *io, fi_handle handle, BYTE *data, int numbytes);
 93static BOOL rgbe_WritePixels_RLE(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned scanline_width, unsigned num_scanlines);
 94static BOOL rgbe_ReadMetadata(FIBITMAP *dib, rgbeHeaderInfo *header_info);
 95static BOOL rgbe_WriteMetadata(FIBITMAP *dib, rgbeHeaderInfo *header_info);
 96
 97// ----------------------------------------------------------
 98
 99/**
100Default error routine.  change this to change error handling 
101*/
102static BOOL  
103rgbe_Error(rgbe_error_code error_code, const char *msg) {
104	switch (error_code) {
105		case rgbe_read_error:
106			FreeImage_OutputMessageProc(s_format_id, "RGBE read error");
107			break;
108		case rgbe_write_error:
109			FreeImage_OutputMessageProc(s_format_id, "RGBE write error");
110			break;
111		case rgbe_format_error:
112			FreeImage_OutputMessageProc(s_format_id, "RGBE bad file format: %s\n", msg);
113			break;
114		default:
115		case rgbe_memory_error:
116			FreeImage_OutputMessageProc(s_format_id, "RGBE error: %s\n",msg);
117	}
118
119	return FALSE;
120}
121
122/**
123Get a line from a ASCII io stream
124*/
125static BOOL 
126rgbe_GetLine(FreeImageIO *io, fi_handle handle, char *buffer, int length) {
127	int i;
128	memset(buffer, 0, length);
129	for(i = 0; i < length; i++) {
130		if(!io->read_proc(&buffer[i], 1, 1, handle))
131			return FALSE;
132		if(buffer[i] == 0x0A)
133			break;
134	}
135	
136	return (i < length) ? TRUE : FALSE;
137}
138
139/**
140Standard conversion from float pixels to rgbe pixels. 
141Note: you can remove the "inline"s if your compiler complains about it 
142*/
143static inline void 
144rgbe_FloatToRGBE(BYTE rgbe[4], FIRGBF *rgbf) {
145	float v;
146	int e;
147
148	v = rgbf->red;
149	if (rgbf->green > v) v = rgbf->green;
150	if (rgbf->blue > v) v = rgbf->blue;
151	if (v < 1e-32) {
152		rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
153	}
154	else {
155		v = (float)(frexp(v, &e) * 256.0 / v);
156		rgbe[0] = (BYTE) (rgbf->red * v);
157		rgbe[1] = (BYTE) (rgbf->green * v);
158		rgbe[2] = (BYTE) (rgbf->blue * v);
159		rgbe[3] = (BYTE) (e + 128);
160	}
161}
162
163/**
164Standard conversion from rgbe to float pixels. 
165Note: Ward uses ldexp(col+0.5,exp-(128+8)). 
166However we wanted pixels in the range [0,1] to map back into the range [0,1].
167*/
168static inline void 
169rgbe_RGBEToFloat(FIRGBF *rgbf, BYTE rgbe[4]) {
170	if (rgbe[3]) {   // nonzero pixel
171		float f = (float)(ldexp(1.0, rgbe[3] - (int)(128+8)));
172		rgbf->red   = rgbe[0] * f;
173		rgbf->green = rgbe[1] * f;
174		rgbf->blue  = rgbe[2] * f;
175
176	}
177	else {
178		rgbf->red = rgbf->green = rgbf->blue = 0;
179	}
180}
181
182/**
183Minimal header reading. Modify if you want to parse more information 
184*/
185static BOOL 
186rgbe_ReadHeader(FreeImageIO *io, fi_handle handle, unsigned *width, unsigned *height, rgbeHeaderInfo *header_info) {
187	char buf[HDR_MAXLINE];
188	float tempf;
189	int i;
190	BOOL bFormatFound = FALSE;
191	BOOL bHeaderFound = FALSE;
192
193	header_info->valid = 0;
194	header_info->programtype[0] = 0;
195	header_info->gamma = 1.0;
196	header_info->exposure = 1.0;
197
198	// get the first line
199	if(!rgbe_GetLine(io, handle, buf, HDR_MAXLINE))
200		return rgbe_Error(rgbe_read_error, NULL);
201
202	// check the signature
203
204	if ((buf[0] != '#')||(buf[1] != '?')) {
205		// if you don't want to require the magic token then comment the next line
206		return rgbe_Error(rgbe_format_error,"bad initial token");
207	}
208	else {
209		header_info->valid |= RGBE_VALID_PROGRAMTYPE;
210		for(i = 0; i < sizeof(header_info->programtype) - 1; i++) {
211			if((buf[i+2] == 0) || isspace(buf[i+2]))
212				break;
213			header_info->programtype[i] = buf[i+2];
214		}
215		header_info->programtype[i] = 0;
216	}
217
218	for(;;) {
219		// get next line
220		if(!rgbe_GetLine(io, handle, buf, HDR_MAXLINE))
221			return rgbe_Error(rgbe_read_error, NULL);
222
223		if((buf[0] == 0) || (buf[0] == '\n')) {
224			// end of header so break out of loop
225			bHeaderFound = TRUE;
226			break;
227		}
228		else if(strcmp(buf,"FORMAT=32-bit_rle_rgbe\n") == 0) {
229			bFormatFound = TRUE;
230		}
231		else if(sscanf(buf, "GAMMA=%g", &tempf) == 1) {
232			header_info->gamma = tempf;
233			header_info->valid |= RGBE_VALID_GAMMA;
234		}
235		else if(sscanf(buf,"EXPOSURE=%g",&tempf) == 1) {
236			header_info->exposure = tempf;
237			header_info->valid |= RGBE_VALID_EXPOSURE;
238		}
239		else if((buf[0] == '#') && (buf[1] == 0x20)) {
240			header_info->valid |= RGBE_VALID_COMMENT;
241			strcpy(header_info->comment, buf);
242		}
243	}
244	if(!bHeaderFound || !bFormatFound) {
245		return rgbe_Error(rgbe_format_error, "invalid header");
246	}
247
248	// get next line
249	if(!rgbe_GetLine(io, handle, buf, HDR_MAXLINE))
250		return rgbe_Error(rgbe_read_error, NULL);
251
252	// get the image width & height
253	if(sscanf(buf,"-Y %d +X %d", height, width) < 2) {
254		if(sscanf(buf,"+X %d +Y %d", height, width) < 2) {
255			return rgbe_Error(rgbe_format_error, "missing image size specifier");
256		}
257	}
258
259	return TRUE;
260}
261
262/**
263 default minimal header. modify if you want more information in header 
264*/
265static BOOL 
266rgbe_WriteHeader(FreeImageIO *io, fi_handle handle, unsigned width, unsigned height, rgbeHeaderInfo *info) {
267	char buffer[HDR_MAXLINE];
268
269	const char *programtype = "RADIANCE";
270
271	if(info && (info->valid & RGBE_VALID_PROGRAMTYPE)) {
272		programtype = info->programtype;
273	}
274	// The #? is to identify file type, the programtype is optional
275	sprintf(buffer, "#?%s\n", programtype);
276	if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
277		return rgbe_Error(rgbe_write_error, NULL);
278	sprintf(buffer, "%s\n", info->comment);
279	if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
280		return rgbe_Error(rgbe_write_error, NULL);
281	sprintf(buffer, "FORMAT=32-bit_rle_rgbe\n");
282	if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
283		return rgbe_Error(rgbe_write_error, NULL);
284	if(info && (info->valid & RGBE_VALID_GAMMA)) {
285		sprintf(buffer, "GAMMA=%g\n", info->gamma);
286		if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
287			return rgbe_Error(rgbe_write_error, NULL);
288	}
289	if(info && (info->valid & RGBE_VALID_EXPOSURE)) {
290		sprintf(buffer,"EXPOSURE=%g\n", info->exposure);
291		if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
292			return rgbe_Error(rgbe_write_error, NULL);
293	}
294	sprintf(buffer, "\n-Y %d +X %d\n", height, width);
295	if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
296		return rgbe_Error(rgbe_write_error, NULL);
297
298	return TRUE;
299}
300
301static BOOL 
302rgbe_ReadMetadata(FIBITMAP *dib, rgbeHeaderInfo *header_info) {
303	return TRUE;
304}
305static BOOL 
306rgbe_WriteMetadata(FIBITMAP *dib, rgbeHeaderInfo *header_info) {
307	header_info->gamma = 1;
308	header_info->valid |= RGBE_VALID_GAMMA;
309	header_info->exposure = 0;
310	header_info->valid |= RGBE_VALID_EXPOSURE;
311
312	return TRUE;
313}
314
315/** 
316Simple read routine. Will not correctly handle run length encoding 
317*/
318static BOOL 
319rgbe_ReadPixels(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned numpixels) {
320  BYTE rgbe[4];
321
322  for(unsigned x = 0; x < numpixels; x++) {
323	if(io->read_proc(rgbe, 1, sizeof(rgbe), handle) < 1) {
324		return rgbe_Error(rgbe_read_error, NULL);
325	}
326	rgbe_RGBEToFloat(&data[x], rgbe);
327  }
328
329  return TRUE;
330}
331
332/**
333 Simple write routine that does not use run length encoding. 
334 These routines can be made faster by allocating a larger buffer and
335 fread-ing and fwrite-ing the data in larger chunks.
336*/
337static BOOL 
338rgbe_WritePixels(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned numpixels) {
339  BYTE rgbe[4];
340
341  for(unsigned x = 0; x < numpixels; x++) {
342	  rgbe_FloatToRGBE(rgbe, &data[x]);
343	  if(io->write_proc(rgbe, sizeof(rgbe), 1, handle) < 1)
344		  return rgbe_Error(rgbe_write_error, NULL);
345  }
346
347  return TRUE;
348}
349
350static BOOL 
351rgbe_ReadPixels_RLE(FreeImageIO *io, fi_handle handle, FIRGBF *data, int scanline_width, unsigned num_scanlines) {
352	BYTE rgbe[4], *scanline_buffer, *ptr, *ptr_end;
353	int i, count;
354	BYTE buf[2];
355	
356	if ((scanline_width < 8)||(scanline_width > 0x7fff)) {
357		// run length encoding is not allowed so read flat
358		return rgbe_ReadPixels(io, handle, data, scanline_width * num_scanlines);
359	}
360	scanline_buffer = NULL;
361	// read in each successive scanline 
362	while(num_scanlines > 0) {
363		if(io->read_proc(rgbe, 1, sizeof(rgbe), handle) < 1) {
364			free(scanline_buffer);
365			return rgbe_Error(rgbe_read_error,NULL);
366		}
367		if((rgbe[0] != 2) || (rgbe[1] != 2) || (rgbe[2] & 0x80)) {
368			// this file is not run length encoded
369			rgbe_RGBEToFloat(data, rgbe);
370			data ++;
371			free(scanline_buffer);
372			return rgbe_ReadPixels(io, handle, data, scanline_width * num_scanlines - 1);
373		}
374		if((((int)rgbe[2]) << 8 | rgbe[3]) != scanline_width) {
375			free(scanline_buffer);
376			return rgbe_Error(rgbe_format_error,"wrong scanline width");
377		}
378		if(scanline_buffer == NULL) {
379			scanline_buffer = (BYTE*)malloc(sizeof(BYTE) * 4 * scanline_width);
380			if(scanline_buffer == NULL) {
381				return rgbe_Error(rgbe_memory_error, "unable to allocate buffer space");
382			}
383		}
384		
385		ptr = &scanline_buffer[0];
386		// read each of the four channels for the scanline into the buffer
387		for(i = 0; i < 4; i++) {
388			ptr_end = &scanline_buffer[(i+1)*scanline_width];
389			while(ptr < ptr_end) {
390				if(io->read_proc(buf, 1, 2 * sizeof(BYTE), handle) < 1) {
391					free(scanline_buffer);
392					return rgbe_Error(rgbe_read_error, NULL);
393				}
394				if(buf[0] > 128) {
395					// a run of the same value
396					count = buf[0] - 128;
397					if((count == 0) || (count > ptr_end - ptr)) {
398						free(scanline_buffer);
399						return rgbe_Error(rgbe_format_error, "bad scanline data");
400					}
401					while(count-- > 0)
402						*ptr++ = buf[1];
403				}
404				else {
405					// a non-run
406					count = buf[0];
407					if((count == 0) || (count > ptr_end - ptr)) {
408						free(scanline_buffer);
409						return rgbe_Error(rgbe_format_error, "bad scanline data");
410					}
411					*ptr++ = buf[1];
412					if(--count > 0) {
413						if(io->read_proc(ptr, 1, sizeof(BYTE) * count, handle) < 1) {
414							free(scanline_buffer);
415							return rgbe_Error(rgbe_read_error, NULL);
416						}
417						ptr += count;
418					}
419				}
420			}
421		}
422		// now convert data from buffer into floats
423		for(i = 0; i < scanline_width; i++) {
424			rgbe[0] = scanline_buffer[i];
425			rgbe[1] = scanline_buffer[i+scanline_width];
426			rgbe[2] = scanline_buffer[i+2*scanline_width];
427			rgbe[3] = scanline_buffer[i+3*scanline_width];
428			rgbe_RGBEToFloat(data, rgbe);
429			data ++;
430		}
431
432		num_scanlines--;
433	}
434
435	free(scanline_buffer);
436	
437	return TRUE;
438}
439
440/**
441 The code below is only needed for the run-length encoded files.
442 Run length encoding adds considerable complexity but does 
443 save some space.  For each scanline, each channel (r,g,b,e) is 
444 encoded separately for better compression. 
445 @return Returns TRUE if successful, returns FALSE otherwise
446*/
447static BOOL 
448rgbe_WriteBytes_RLE(FreeImageIO *io, fi_handle handle, BYTE *data, int numbytes) {
449	static const int MINRUNLENGTH = 4;
450	int cur, beg_run, run_count, old_run_count, nonrun_count;
451	BYTE buf[2];
452	
453	cur = 0;
454	while(cur < numbytes) {
455		beg_run = cur;
456		// find next run of length at least 4 if one exists 
457		run_count = old_run_count = 0;
458		while((run_count < MINRUNLENGTH) && (beg_run < numbytes)) {
459			beg_run += run_count;
460			old_run_count = run_count;
461			run_count = 1;
462			while((data[beg_run] == data[beg_run + run_count]) && (beg_run + run_count < numbytes) && (run_count < 127))
463				run_count++;
464		}
465		// if data before next big run is a short run then write it as such 
466		if ((old_run_count > 1)&&(old_run_count == beg_run - cur)) {
467			buf[0] = (BYTE)(128 + old_run_count);   // write short run
468			buf[1] = data[cur];
469			if(io->write_proc(buf, 2 * sizeof(BYTE), 1, handle) < 1)
470				return rgbe_Error(rgbe_write_error, NULL);
471			cur = beg_run;
472		}
473		// write out bytes until we reach the start of the next run 
474		while(cur < beg_run) {
475			nonrun_count = beg_run - cur;
476			if (nonrun_count > 128) 
477				nonrun_count = 128;
478			buf[0] = (BYTE)nonrun_count;
479			if(io->write_proc(buf, sizeof(buf[0]), 1, handle) < 1)
480				return rgbe_Error(rgbe_write_error,NULL);
481			if(io->write_proc(&data[cur], sizeof(data[0]) * nonrun_count, 1, handle) < 1)
482				return rgbe_Error(rgbe_write_error,NULL);
483			cur += nonrun_count;
484		}
485		// write out next run if one was found 
486		if (run_count >= MINRUNLENGTH) {
487			buf[0] = (BYTE)(128 + run_count);
488			buf[1] = data[beg_run];
489			if(io->write_proc(buf, sizeof(buf[0]) * 2, 1, handle) < 1)
490				return rgbe_Error(rgbe_write_error,NULL);
491			cur += run_count;
492		}
493	}
494	
495	return TRUE;
496}
497
498static BOOL 
499rgbe_WritePixels_RLE(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned scanline_width, unsigned num_scanlines) {
500	BYTE rgbe[4];
501	BYTE *buffer;
502	
503	if ((scanline_width < 8)||(scanline_width > 0x7fff)) {
504		// run length encoding is not allowed so write flat
505		return rgbe_WritePixels(io, handle, data, scanline_width * num_scanlines);
506	}
507	buffer = (BYTE*)malloc(sizeof(BYTE) * 4 * scanline_width);
508	if (buffer == NULL) {
509		// no buffer space so write flat 
510		return rgbe_WritePixels(io, handle, data, scanline_width * num_scanlines);
511	}
512	while(num_scanlines-- > 0) {
513		rgbe[0] = (BYTE)2;
514		rgbe[1] = (BYTE)2;
515		rgbe[2] = (BYTE)(scanline_width >> 8);
516		rgbe[3] = (BYTE)(scanline_width & 0xFF);
517		if(io->write_proc(rgbe, sizeof(rgbe), 1, handle) < 1) {
518			free(buffer);
519			return rgbe_Error(rgbe_write_error, NULL);
520		}
521		for(unsigned x = 0; x < scanline_width; x++) {
522			rgbe_FloatToRGBE(rgbe, data);
523			buffer[x] = rgbe[0];
524			buffer[x+scanline_width] = rgbe[1];
525			buffer[x+2*scanline_width] = rgbe[2];
526			buffer[x+3*scanline_width] = rgbe[3];
527			data ++;
528		}
529		// write out each of the four channels separately run length encoded
530		// first red, then green, then blue, then exponent
531		for(int i = 0; i < 4; i++) {
532			BOOL bOK = rgbe_WriteBytes_RLE(io, handle, &buffer[i*scanline_width], scanline_width);
533			if(!bOK) {
534				free(buffer);
535				return bOK;
536			}
537		}
538	}
539	free(buffer);
540	
541	return TRUE;
542}
543
544
545// ----------------------------------------------------------
546
547
548
549// ==========================================================
550// Plugin Implementation
551// ==========================================================
552
553static const char * DLL_CALLCONV
554Format() {
555	return "HDR";
556}
557
558static const char * DLL_CALLCONV
559Description() {
560	return "High Dynamic Range Image";
561}
562
563static const char * DLL_CALLCONV
564Extension() {
565	return "hdr";
566}
567
568static const char * DLL_CALLCONV
569RegExpr() {
570	return NULL;
571}
572
573static const char * DLL_CALLCONV
574MimeType() {
575	return "image/vnd.radiance";
576}
577
578static BOOL DLL_CALLCONV
579Validate(FreeImageIO *io, fi_handle handle) {
580	BYTE hdr_signature[] = { '#', '?' };
581	BYTE signature[] = { 0, 0 };
582
583	io->read_proc(signature, 1, 2, handle);
584
585	return (memcmp(hdr_signature, signature, 2) == 0);
586}
587
588static BOOL DLL_CALLCONV
589SupportsExportDepth(int depth) {
590	return FALSE;
591}
592
593static BOOL DLL_CALLCONV 
594SupportsExportType(FREE_IMAGE_TYPE type) {
595	return (type == FIT_RGBF) ? TRUE : FALSE;
596}
597
598static BOOL DLL_CALLCONV
599SupportsNoPixels() {
600	return TRUE;
601}
602
603// --------------------------------------------------------------------------
604
605static FIBITMAP * DLL_CALLCONV
606Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
607	FIBITMAP *dib = NULL;
608
609	if(!handle) {
610		return NULL;
611	}
612
613	BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
614
615	try {
616
617		rgbeHeaderInfo header_info;
618		unsigned width, height;
619
620		// Read the header
621		if(rgbe_ReadHeader(io, handle, &width, &height, &header_info) == FALSE) {
622			return NULL;
623		}
624
625		// allocate a RGBF image
626		dib = FreeImage_AllocateHeaderT(header_only, FIT_RGBF, width, height);
627		if(!dib) {
628			throw FI_MSG_ERROR_MEMORY;
629		}
630
631		// set the metadata as comments
632		rgbe_ReadMetadata(dib, &header_info);
633
634		if(header_only) {
635			// header only mode
636			return dib;
637		}
638
639		// read the image pixels and fill the dib
640		
641		for(unsigned y = 0; y < height; y++) {
642			FIRGBF *scanline = (FIRGBF*)FreeImage_GetScanLine(dib, height - 1 - y);
643			if(!rgbe_ReadPixels_RLE(io, handle, scanline, width, 1)) {
644				FreeImage_Unload(dib);
645				return NULL;
646			}
647		}
648
649	}
650	catch(const char *text) {
651		if(dib != NULL) {
652			FreeImage_Unload(dib);
653		}
654		FreeImage_OutputMessageProc(s_format_id, text);
655	}
656
657	return dib;
658}
659
660static BOOL DLL_CALLCONV
661Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
662	if(!dib) return FALSE;
663
664	if(FreeImage_GetImageType(dib) != FIT_RGBF) {
665		return FALSE;
666	}
667
668	unsigned width  = FreeImage_GetWidth(dib);
669	unsigned height = FreeImage_GetHeight(dib);
670
671	// write the header
672
673	rgbeHeaderInfo header_info;
674	memset(&header_info, 0, sizeof(rgbeHeaderInfo));
675	// fill the header with correct gamma and exposure
676	rgbe_WriteMetadata(dib, &header_info);
677	// fill a comment
678	sprintf(header_info.comment, "# Made with FreeImage %s", FreeImage_GetVersion());
679	if(!rgbe_WriteHeader(io, handle, width, height, &header_info)) {
680		return FALSE;
681	}
682
683	// write each scanline
684
685	for(unsigned y = 0; y < height; y++) {
686		FIRGBF *scanline = (FIRGBF*)FreeImage_GetScanLine(dib, height - 1 - y);
687		if(!rgbe_WritePixels_RLE(io, handle, scanline, width, 1)) {
688			return FALSE;
689		}
690	}
691
692	return TRUE;
693}
694
695// ==========================================================
696//   Init
697// ==========================================================
698
699void DLL_CALLCONV
700InitHDR(Plugin *plugin, int format_id) {
701	s_format_id = format_id;
702
703	plugin->format_proc = Format;
704	plugin->description_proc = Description;
705	plugin->extension_proc = Extension;
706	plugin->regexpr_proc = RegExpr;
707	plugin->open_proc = NULL;
708	plugin->close_proc = NULL;
709	plugin->pagecount_proc = NULL;
710	plugin->pagecapability_proc = NULL;
711	plugin->load_proc = Load;
712	plugin->save_proc = Save;
713	plugin->validate_proc = Validate;
714	plugin->mime_proc = MimeType;
715	plugin->supports_export_bpp_proc = SupportsExportDepth;
716	plugin->supports_export_type_proc = SupportsExportType;
717	plugin->supports_icc_profiles_proc = NULL;
718	plugin->supports_no_pixels_proc = SupportsNoPixels;
719}