PageRenderTime 35ms CodeModel.GetById 8ms app.highlight 23ms RepoModel.GetById 1ms app.codeStats 0ms

/src/FreeImage/Source/FreeImage/PluginPFM.cpp

https://bitbucket.org/cabalistic/ogredeps/
C++ | 402 lines | 258 code | 81 blank | 63 comment | 78 complexity | 38dc9573ee3732b4f4a0acd8dd777887 MD5 | raw file
  1// ==========================================================
  2// PFM 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// Internal functions
 27// ==========================================================
 28
 29/** maximum size of a line in the header */
 30#define PFM_MAXLINE	256
 31
 32/** Big endian / Little endian float conversion */
 33#define REVERSEBYTES(source, dest)		\
 34{										\
 35	char *j = (char *) source;			\
 36	char *dj = (char *) dest;			\
 37	dj[0] = j[3];						\
 38	dj[1] = j[2];						\
 39	dj[2] = j[1];						\
 40	dj[3] = j[0];						\
 41}
 42
 43/**
 44Get a line from a ASCII io stream
 45*/
 46static BOOL 
 47pfm_get_line(FreeImageIO *io, fi_handle handle, char *buffer, int length) {
 48	int i;
 49	memset(buffer, 0, length);
 50	for(i = 0; i < length; i++) {
 51		if(!io->read_proc(&buffer[i], 1, 1, handle))
 52			return FALSE;
 53		if(buffer[i] == 0x0A)
 54			break;
 55	}
 56	
 57	return (i < length) ? TRUE : FALSE;
 58}
 59
 60/**
 61Get an integer value from the actual position pointed by handle
 62*/
 63static int
 64pfm_get_int(FreeImageIO *io, fi_handle handle) {
 65    char c = 0;
 66	BOOL firstchar;
 67
 68    // skip forward to start of next number
 69
 70    if(!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING;
 71
 72    while (1) {
 73        // eat comments
 74
 75        if (c == '#') {
 76			// if we're at a comment, read to end of line
 77
 78            firstchar = TRUE;
 79
 80            while (1) {
 81				if(!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING;
 82
 83				if (firstchar && c == ' ') {
 84					// loop off 1 sp after #
 85
 86					firstchar = FALSE;
 87				} else if (c == '\n') {
 88					break;
 89				}
 90			}
 91		}
 92
 93        if (c >= '0' && c <='9') {
 94			// we've found what we were looking for
 95
 96            break;
 97		}
 98
 99        if(!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING;
100    }
101
102    // we're at the start of a number, continue until we hit a non-number
103
104    int i = 0;
105
106    while (1) {
107        i = (i * 10) + (c - '0');
108
109        if(!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING;
110
111        if (c < '0' || c > '9')
112            break;
113    }
114
115    return i;
116}
117
118// ==========================================================
119// Plugin Interface
120// ==========================================================
121
122static int s_format_id;
123
124// ==========================================================
125// Plugin Implementation
126// ==========================================================
127
128static const char * DLL_CALLCONV
129Format() {
130	return "PFM";
131}
132
133static const char * DLL_CALLCONV
134Description() {
135	return "Portable floatmap";
136}
137
138static const char * DLL_CALLCONV
139Extension() {
140	return "pfm";
141}
142
143static const char * DLL_CALLCONV
144RegExpr() {
145	return NULL;
146}
147
148static const char * DLL_CALLCONV
149MimeType() {
150	return "image/x-portable-floatmap";
151}
152
153static BOOL DLL_CALLCONV
154Validate(FreeImageIO *io, fi_handle handle) {
155	BYTE pfm_id1[] = { 0x50, 0x46 };
156	BYTE pfm_id2[] = { 0x50, 0x66 };
157	BYTE signature[2] = { 0, 0 };
158
159	io->read_proc(signature, 1, sizeof(pfm_id1), handle);
160
161	if (memcmp(pfm_id1, signature, sizeof(pfm_id1)) == 0)
162		return TRUE;
163
164	if (memcmp(pfm_id2, signature, sizeof(pfm_id2)) == 0)
165		return TRUE;
166
167	return FALSE;
168}
169
170static BOOL DLL_CALLCONV
171SupportsExportDepth(int depth) {
172	return FALSE;
173}
174
175static BOOL DLL_CALLCONV 
176SupportsExportType(FREE_IMAGE_TYPE type) {
177	return (
178		(type == FIT_FLOAT) ||
179		(type == FIT_RGBF)
180	);
181}
182
183static BOOL DLL_CALLCONV
184SupportsNoPixels() {
185	return TRUE;
186}
187
188// ----------------------------------------------------------
189
190static FIBITMAP * DLL_CALLCONV
191Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
192	char line_buffer[PFM_MAXLINE];
193	char id_one = 0, id_two = 0;
194	FIBITMAP *dib = NULL;
195	float *lineBuffer = NULL;
196
197	if (!handle) {
198		return NULL;
199	}
200
201	BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
202
203	try {
204		FREE_IMAGE_TYPE image_type = FIT_UNKNOWN;
205
206		// Read the first two bytes of the file to determine the file format
207		// "PF" = color image
208		// "Pf" = greyscale image
209
210		io->read_proc(&id_one, 1, 1, handle);
211		io->read_proc(&id_two, 1, 1, handle);
212
213		if(id_one == 'P') {
214			if(id_two == 'F') {
215				image_type = FIT_RGBF;
216			} else if(id_two == 'f') {
217				image_type = FIT_FLOAT;
218			}
219		}
220		if(image_type == FIT_UNKNOWN) {
221			// signature error
222			throw FI_MSG_ERROR_MAGIC_NUMBER;
223		}
224
225		// Read the header information: width, height and the scale value
226		unsigned width  = (unsigned) pfm_get_int(io, handle);
227		unsigned height = (unsigned) pfm_get_int(io, handle);
228		float scalefactor = 1;
229
230		BOOL bResult = pfm_get_line(io, handle, line_buffer, PFM_MAXLINE);
231		if(bResult) {
232			bResult = (sscanf(line_buffer, "%f", &scalefactor) == 1) ? TRUE : FALSE;
233		}
234		if(!bResult) {
235			throw "Read error: invalid PFM header";
236		}
237
238		// Create a new DIB
239		dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height);
240		if (dib == NULL) {
241			throw FI_MSG_ERROR_DIB_MEMORY;
242		}
243
244		if(header_only) {
245			// header only mode
246			return dib;
247		}
248
249		// Read the image...
250
251		if(image_type == FIT_RGBF) {
252			const unsigned lineWidth = 3 * width;
253			lineBuffer = (float*)malloc(lineWidth * sizeof(float));
254			if(!lineBuffer) {
255				throw FI_MSG_ERROR_MEMORY;
256			}
257
258			for (unsigned y = 0; y < height; y++) {	
259				FIRGBF *bits = (FIRGBF*)FreeImage_GetScanLine(dib, height - 1 - y);
260
261				if(io->read_proc(lineBuffer, sizeof(float), lineWidth, handle) != lineWidth) {
262					throw "Read error";
263				}
264				float *channel = lineBuffer;
265				if(scalefactor > 0) {
266					// MSB
267					for (unsigned x = 0; x < width; x++) {
268						REVERSEBYTES(channel++, &bits[x].red);
269						REVERSEBYTES(channel++, &bits[x].green);
270						REVERSEBYTES(channel++, &bits[x].blue);
271					}
272				} else {
273					// LSB					
274					for (unsigned x = 0; x < width; x++) {
275						bits[x].red		= *channel++;
276						bits[x].green	= *channel++;
277						bits[x].blue	= *channel++;
278					}
279				}
280			}
281
282			free(lineBuffer);
283			lineBuffer = NULL;
284
285		} else if(image_type == FIT_FLOAT) {
286			const unsigned lineWidth = width;
287			lineBuffer = (float*)malloc(lineWidth * sizeof(float));
288			if(!lineBuffer) {
289				throw FI_MSG_ERROR_MEMORY;
290			}
291
292			for (unsigned y = 0; y < height; y++) {	
293				float *bits = (float*)FreeImage_GetScanLine(dib, height - 1 - y);
294
295				if(io->read_proc(lineBuffer, sizeof(float), lineWidth, handle) != lineWidth) {
296					throw "Read error";
297				}
298				float *channel = lineBuffer;
299				if(scalefactor > 0) {
300					// MSB - File is Big endian
301					for (unsigned x = 0; x < width; x++) {
302						REVERSEBYTES(channel++, &bits[x]);
303					}
304				} else {
305					// LSB - File is Little Endian
306					for (unsigned x = 0; x < width; x++) {
307						bits[x] = *channel++;
308					}
309				}
310			}
311
312			free(lineBuffer);
313			lineBuffer = NULL;
314		}
315		
316		return dib;
317
318	} catch (const char *text)  {
319		if(lineBuffer) free(lineBuffer);
320		if(dib) FreeImage_Unload(dib);
321
322		if(NULL != text) {
323			FreeImage_OutputMessageProc(s_format_id, text);
324		}
325
326		return NULL;
327	}
328
329}
330
331static BOOL DLL_CALLCONV
332Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
333	if(!dib || !handle) return FALSE;
334
335	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
336	if((image_type != FIT_RGBF) && (image_type != FIT_FLOAT)) {
337		return FALSE;
338	}
339
340	unsigned width  = FreeImage_GetWidth(dib);
341	unsigned height = FreeImage_GetHeight(dib);
342	unsigned lineWidth = FreeImage_GetLine(dib);
343	
344	// save image as Little Endian
345	const float scalefactor = -1.0F;
346
347	char buffer[PFM_MAXLINE];	// temporary buffer whose size should be enough for what we need
348
349	// Find the appropriate magic number for this file type
350
351	char magic = 0;
352
353	switch(image_type) {
354		case FIT_RGBF:
355			magic = 'F';	// RGBF
356			break;	
357		case FIT_FLOAT:
358			magic = 'f';	// float greyscale
359			break;
360		default:
361			return FALSE;
362	}
363
364	// Write the header info
365
366	sprintf(buffer, "P%c\n%d %d\n%f\n", magic, width, height, scalefactor);
367	io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
368
369	// Write the image data
370	for (unsigned y = 0; y < height; y++) {	
371		BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
372		io->write_proc(bits, 1, lineWidth, handle);
373	}
374
375	return TRUE;
376}
377
378// ==========================================================
379//   Init
380// ==========================================================
381
382void DLL_CALLCONV
383InitPFM(Plugin *plugin, int format_id) {
384	s_format_id = format_id;
385
386	plugin->format_proc = Format;
387	plugin->description_proc = Description;
388	plugin->extension_proc = Extension;
389	plugin->regexpr_proc = RegExpr;
390	plugin->open_proc = NULL;
391	plugin->close_proc = NULL;
392	plugin->pagecount_proc = NULL;
393	plugin->pagecapability_proc = NULL;
394	plugin->load_proc = Load;
395	plugin->save_proc = Save;
396	plugin->validate_proc = Validate;
397	plugin->mime_proc = MimeType;
398	plugin->supports_export_bpp_proc = SupportsExportDepth;
399	plugin->supports_export_type_proc = SupportsExportType;
400	plugin->supports_icc_profiles_proc = NULL;
401	plugin->supports_no_pixels_proc = SupportsNoPixels;
402}