Coverage Report

Created: 2025-06-24 07:02

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