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