Coverage Report

Created: 2025-08-28 06:33

/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
}