Coverage Report

Created: 2024-09-03 06:26

/src/freeimage-svn/FreeImage/trunk/Source/FreeImage/PluginEXR.cpp
Line
Count
Source (jump to first uncovered line)
1
// ==========================================================
2
// EXR Loader and writer
3
//
4
// Design and implementation by 
5
// - Hervé Drolon (drolon@infonie.fr)
6
// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
7
//
8
// This file is part of FreeImage 3
9
//
10
// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
11
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
12
// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
13
// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
14
// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
15
// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
16
// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
17
// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
18
// THIS DISCLAIMER.
19
//
20
// Use at your own risk!
21
// ==========================================================
22
23
#include "FreeImage.h"
24
#include "Utilities.h"
25
26
#ifdef _MSC_VER
27
// OpenEXR has many problems with MSVC warnings (why not just correct them ?), just ignore one of them
28
#pragma warning (disable : 4800) // ImfVersion.h - 'const int' : forcing value to bool 'true' or 'false' (performance warning)
29
#endif 
30
31
#include "../OpenEXR/IlmImf/ImfIO.h"
32
#include "../OpenEXR/Iex/Iex.h"
33
#include "../OpenEXR/IlmImf/ImfOutputFile.h"
34
#include "../OpenEXR/IlmImf/ImfInputFile.h"
35
#include "../OpenEXR/IlmImf/ImfRgbaFile.h"
36
#include "../OpenEXR/IlmImf/ImfChannelList.h"
37
#include "../OpenEXR/IlmImf/ImfRgba.h"
38
#include "../OpenEXR/IlmImf/ImfArray.h"
39
#include "../OpenEXR/IlmImf/ImfPreviewImage.h"
40
#include "../OpenEXR/Half/half.h"
41
42
43
// ==========================================================
44
// Plugin Interface
45
// ==========================================================
46
47
static int s_format_id;
48
49
// ----------------------------------------------------------
50
51
/**
52
FreeImage input stream wrapper
53
@see Imf_2_2::IStream
54
*/
55
class C_IStream : public Imf::IStream {
56
private:
57
    FreeImageIO *_io;
58
  fi_handle _handle;
59
60
public:
61
  C_IStream (FreeImageIO *io, fi_handle handle) : 
62
0
    Imf::IStream(""), _io (io), _handle(handle) {
63
0
  }
64
65
0
  virtual bool read (char c[/*n*/], int n) {
66
0
    return ((unsigned)n != _io->read_proc(c, 1, n, _handle));
67
0
  }
68
69
0
  virtual Imath::Int64 tellg() {
70
0
    return _io->tell_proc(_handle);
71
0
  }
72
73
0
  virtual void seekg(Imath::Int64 pos) {
74
0
    _io->seek_proc(_handle, (unsigned)pos, SEEK_SET);
75
0
  }
76
77
0
  virtual void clear() {
78
0
  }
79
};
80
81
// ----------------------------------------------------------
82
83
/**
84
FreeImage output stream wrapper
85
@see Imf_2_2::OStream
86
*/
87
class C_OStream : public Imf::OStream {
88
private:
89
    FreeImageIO *_io;
90
  fi_handle _handle;
91
92
public:
93
  C_OStream (FreeImageIO *io, fi_handle handle) : 
94
0
    Imf::OStream(""), _io (io), _handle(handle) {
95
0
  }
96
97
0
  virtual void write(const char c[/*n*/], int n) {
98
0
    if((unsigned)n != _io->write_proc((void*)&c[0], 1, n, _handle)) {
99
0
      Iex::throwErrnoExc();
100
0
    }
101
0
  }
102
103
0
  virtual Imath::Int64 tellp() {
104
0
    return _io->tell_proc(_handle);
105
0
  }
106
107
0
  virtual void seekp(Imath::Int64 pos) {
108
0
    _io->seek_proc(_handle, (unsigned)pos, SEEK_SET);
109
0
  }
110
};
111
112
// ----------------------------------------------------------
113
114
115
// ==========================================================
116
// Plugin Implementation
117
// ==========================================================
118
119
static const char * DLL_CALLCONV
120
2
Format() {
121
2
  return "EXR";
122
2
}
123
124
static const char * DLL_CALLCONV
125
0
Description() {
126
0
  return "ILM OpenEXR";
127
0
}
128
129
static const char * DLL_CALLCONV
130
0
Extension() {
131
0
  return "exr";
132
0
}
133
134
static const char * DLL_CALLCONV
135
0
RegExpr() {
136
0
  return NULL;
137
0
}
138
139
static const char * DLL_CALLCONV
140
0
MimeType() {
141
0
  return "image/x-exr";
142
0
}
143
144
static BOOL DLL_CALLCONV
145
22.8k
Validate(FreeImageIO *io, fi_handle handle) {
146
22.8k
  BYTE exr_signature[] = { 0x76, 0x2F, 0x31, 0x01 };
147
22.8k
  BYTE signature[] = { 0, 0, 0, 0 };
148
149
22.8k
  io->read_proc(signature, 1, 4, handle);
150
22.8k
  return (memcmp(exr_signature, signature, 4) == 0);
151
22.8k
}
152
153
static BOOL DLL_CALLCONV
154
0
SupportsExportDepth(int depth) {
155
0
  return FALSE;
156
0
}
157
158
static BOOL DLL_CALLCONV 
159
0
SupportsExportType(FREE_IMAGE_TYPE type) {
160
0
  return (
161
0
    (type == FIT_FLOAT) ||
162
0
    (type == FIT_RGBF)  ||
163
0
    (type == FIT_RGBAF)
164
0
  );
165
0
}
166
167
static BOOL DLL_CALLCONV
168
0
SupportsNoPixels() {
169
0
  return TRUE;
170
0
}
171
172
// --------------------------------------------------------------------------
173
174
static FIBITMAP * DLL_CALLCONV
175
0
Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
176
0
  bool bUseRgbaInterface = false;
177
0
  FIBITMAP *dib = NULL; 
178
179
0
  if(!handle) {
180
0
    return NULL;
181
0
  }
182
183
0
  try {
184
0
    BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
185
186
    // save the stream starting point
187
0
    const long stream_start = io->tell_proc(handle);
188
189
    // wrap the FreeImage IO stream
190
0
    C_IStream istream(io, handle);
191
192
    // open the file
193
0
    Imf::InputFile file(istream);
194
195
    // get file info      
196
0
    const Imath::Box2i &dataWindow = file.header().dataWindow();
197
0
    int width  = dataWindow.max.x - dataWindow.min.x + 1;
198
0
    int height = dataWindow.max.y - dataWindow.min.y + 1;
199
200
    //const Imf::Compression &compression = file.header().compression();
201
202
0
    const Imf::ChannelList &channels = file.header().channels();
203
204
    // check the number of components and check for a coherent format
205
206
0
    std::string exr_color_model;
207
0
    Imf::PixelType pixel_type = Imf::HALF;
208
0
    FREE_IMAGE_TYPE image_type = FIT_UNKNOWN;
209
0
    int components = 0;
210
0
    bool bMixedComponents = false;
211
212
0
    for (Imf::ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
213
0
      components++;
214
0
      if(components == 1) {
215
0
        exr_color_model += i.name();
216
0
        pixel_type = i.channel().type;
217
0
      } else {
218
0
        exr_color_model += "/";
219
0
        exr_color_model += i.name();
220
0
        if (i.channel().type != pixel_type) {
221
0
          bMixedComponents = true;
222
0
        }
223
0
      }
224
0
    }
225
226
0
    if(bMixedComponents) {
227
0
      bool bHandled = false;
228
      // we may have a RGBZ or RGBAZ image ... 
229
0
      if(components > 4) {
230
0
        if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B") && channels.findChannel("A")) {
231
0
          std::string msg = "Warning: converting color model " + exr_color_model + " to RGBA color model";
232
0
          FreeImage_OutputMessageProc(s_format_id, msg.c_str());
233
0
          bHandled = true;
234
0
        }
235
0
      }
236
0
      else if(components > 3) {
237
0
        if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B")) {
238
0
          std::string msg = "Warning: converting color model " + exr_color_model + " to RGB color model";
239
0
          FreeImage_OutputMessageProc(s_format_id, msg.c_str());
240
0
          bHandled = true;
241
0
        }
242
0
      }
243
0
      if(!bHandled) {
244
0
        THROW (Iex::InputExc, "Unable to handle mixed component types (color model = " << exr_color_model << ")");
245
0
      } 
246
0
    }
247
248
0
    switch(pixel_type) {
249
0
      case Imf::UINT:
250
0
        THROW (Iex::InputExc, "Unsupported format: UINT");
251
0
        break;
252
0
      case Imf::HALF:
253
0
      case Imf::FLOAT:
254
0
      default:
255
0
        break;
256
0
    }
257
258
    // check for supported image color models
259
    // --------------------------------------------------------------
260
261
0
    if((components == 1) || (components == 2)) {       
262
      // if the image is gray-alpha (YA), ignore the alpha channel
263
0
      if((components == 1) && channels.findChannel("Y")) {
264
0
        image_type = FIT_FLOAT;
265
0
        components = 1;
266
0
      } else {
267
0
        std::string msg = "Warning: loading color model " + exr_color_model + " as Y color model";
268
0
        FreeImage_OutputMessageProc(s_format_id, msg.c_str());
269
0
        image_type = FIT_FLOAT;
270
        // ignore the other channel
271
0
        components = 1;
272
0
      }
273
0
    } else if(components == 3) {
274
0
      if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B")) {
275
0
        image_type = FIT_RGBF;
276
0
      }
277
0
      else if(channels.findChannel("BY") && channels.findChannel("RY") && channels.findChannel("Y")) {
278
0
        image_type = FIT_RGBF;
279
0
        bUseRgbaInterface = true;
280
0
      }
281
0
    } else if(components >= 4) {
282
0
      if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B")) {
283
0
        if(channels.findChannel("A")) {
284
0
          if(components > 4) {
285
0
            std::string msg = "Warning: converting color model " + exr_color_model + " to RGBA color model";
286
0
            FreeImage_OutputMessageProc(s_format_id, msg.c_str());
287
0
          }
288
0
          image_type = FIT_RGBAF;
289
          // ignore other layers if there is more than one alpha layer
290
0
          components = 4;
291
0
        } else {
292
0
          std::string msg = "Warning: converting color model " + exr_color_model + " to RGB color model";
293
0
          FreeImage_OutputMessageProc(s_format_id, msg.c_str());
294
295
0
          image_type = FIT_RGBF;
296
          // ignore other channels
297
0
          components = 3;         
298
0
        }
299
0
      }
300
0
    }
301
302
0
    if(image_type == FIT_UNKNOWN) {
303
0
      THROW (Iex::InputExc, "Unsupported color model: " << exr_color_model);
304
0
    }
305
306
    // allocate a new dib
307
0
    dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, 0);
308
0
    if(!dib) THROW (Iex::NullExc, FI_MSG_ERROR_MEMORY);
309
310
    // try to load the preview image
311
    // --------------------------------------------------------------
312
313
0
    if(file.header().hasPreviewImage()) {
314
0
      const Imf::PreviewImage& preview = file.header().previewImage();
315
0
      const unsigned thWidth = preview.width();
316
0
      const unsigned thHeight = preview.height();
317
      
318
0
      FIBITMAP* thumbnail = FreeImage_Allocate(thWidth, thHeight, 32);
319
0
      if(thumbnail) {
320
0
        const Imf::PreviewRgba *src_line = preview.pixels();
321
0
        BYTE *dst_line = FreeImage_GetScanLine(thumbnail, thHeight - 1);
322
0
        const unsigned dstPitch = FreeImage_GetPitch(thumbnail);
323
        
324
0
        for (unsigned y = 0; y < thHeight; ++y) {
325
0
          const Imf::PreviewRgba *src_pixel = src_line;
326
0
          RGBQUAD* dst_pixel = (RGBQUAD*)dst_line;
327
          
328
0
          for(unsigned x = 0; x < thWidth; ++x) {
329
0
            dst_pixel->rgbRed = src_pixel->r;
330
0
            dst_pixel->rgbGreen = src_pixel->g;
331
0
            dst_pixel->rgbBlue = src_pixel->b;
332
0
            dst_pixel->rgbReserved = src_pixel->a;        
333
0
            src_pixel++;
334
0
            dst_pixel++;
335
0
          }
336
0
          src_line += thWidth;
337
0
          dst_line -= dstPitch;
338
0
        }
339
0
        FreeImage_SetThumbnail(dib, thumbnail);
340
0
        FreeImage_Unload(thumbnail);
341
0
      }
342
0
    }
343
344
0
    if(header_only) {
345
      // header only mode
346
0
      return dib;
347
0
    }
348
349
    // load pixels
350
    // --------------------------------------------------------------
351
352
0
    const BYTE *bits = FreeImage_GetBits(dib);      // pointer to our pixel buffer
353
0
    const size_t bytespp = sizeof(float) * components;  // size of our pixel in bytes
354
0
    const unsigned pitch = FreeImage_GetPitch(dib);   // size of our yStride in bytes
355
356
0
    Imf::PixelType pixelType = Imf::FLOAT;  // load as float data type;
357
    
358
0
    if(bUseRgbaInterface) {
359
      // use the RGBA interface (used when loading RY BY Y images )
360
361
0
      const int chunk_size = 16;
362
363
0
      BYTE *scanline = (BYTE*)bits;
364
365
      // re-open using the RGBA interface
366
0
      io->seek_proc(handle, stream_start, SEEK_SET);
367
0
      Imf::RgbaInputFile rgbaFile(istream);
368
369
      // read the file in chunks
370
0
      Imath::Box2i dw = dataWindow;
371
0
      Imf::Array2D<Imf::Rgba> chunk(chunk_size, width);
372
0
      while (dw.min.y <= dw.max.y) {
373
        // read a chunk
374
0
        rgbaFile.setFrameBuffer (&chunk[0][0] - dw.min.x - dw.min.y * width, 1, width);
375
0
        rgbaFile.readPixels (dw.min.y, MIN(dw.min.y + chunk_size - 1, dw.max.y));
376
        // fill the dib
377
0
        const int y_max = ((dw.max.y - dw.min.y) <= chunk_size) ? (dw.max.y - dw.min.y) : chunk_size;
378
0
        for(int y = 0; y < y_max; y++) {
379
0
          FIRGBF *pixel = (FIRGBF*)scanline;
380
0
          const Imf::Rgba *half_rgba = chunk[y];
381
0
          for(int x = 0; x < width; x++) {
382
            // convert from half to float
383
0
            pixel[x].red = half_rgba[x].r;
384
0
            pixel[x].green = half_rgba[x].g;
385
0
            pixel[x].blue = half_rgba[x].b;
386
0
          }
387
          // next line
388
0
          scanline += pitch;
389
0
        }
390
        // next chunk
391
0
        dw.min.y += chunk_size;
392
0
      }
393
394
0
    } else {
395
      // use the low level interface
396
397
      // build a frame buffer (i.e. what we want on output)
398
0
      Imf::FrameBuffer frameBuffer;
399
400
      // allow dataWindow with minimal bounds different form zero
401
0
      size_t offset = - dataWindow.min.x * bytespp - dataWindow.min.y * pitch;
402
403
0
      if(components == 1) {
404
0
        frameBuffer.insert ("Y",  // name
405
0
          Imf::Slice (pixelType,  // type
406
0
          (char*)(bits + offset), // base
407
0
          bytespp,        // xStride
408
0
          pitch,          // yStride
409
0
          1, 1,         // x/y sampling
410
0
          0.0));          // fillValue
411
0
      } else if((components == 3) || (components == 4)) {
412
0
        const char *channel_name[4] = { "R", "G", "B", "A" };
413
414
0
        for(int c = 0; c < components; c++) {
415
0
          frameBuffer.insert (
416
0
            channel_name[c],          // name
417
0
            Imf::Slice (pixelType,        // type
418
0
            (char*)(bits + c * sizeof(float) + offset), // base
419
0
            bytespp,              // xStride
420
0
            pitch,                // yStride
421
0
            1, 1,               // x/y sampling
422
0
            0.0));                // fillValue
423
0
        }
424
0
      }
425
426
      // read the file
427
0
      file.setFrameBuffer(frameBuffer);
428
0
      file.readPixels(dataWindow.min.y, dataWindow.max.y);
429
0
    }
430
431
    // lastly, flip dib lines
432
0
    FreeImage_FlipVertical(dib);
433
434
0
  }
435
0
  catch(Iex::BaseExc & e) {
436
0
    if(dib != NULL) {
437
0
      FreeImage_Unload(dib);
438
0
    }
439
0
    FreeImage_OutputMessageProc(s_format_id, e.what());
440
0
    return NULL;
441
0
  }
442
443
0
  return dib;
444
0
}
445
446
/**
447
Set the preview image using the dib embedded thumbnail
448
*/
449
static BOOL
450
0
SetPreviewImage(FIBITMAP *dib, Imf::Header& header) {
451
0
  if(!FreeImage_GetThumbnail(dib)) {
452
0
    return FALSE;
453
0
  }
454
0
  FIBITMAP* thumbnail = FreeImage_GetThumbnail(dib);
455
456
0
  if((FreeImage_GetImageType(thumbnail) != FIT_BITMAP) || (FreeImage_GetBPP(thumbnail) != 32)) {
457
    // invalid thumbnail - ignore it
458
0
    FreeImage_OutputMessageProc(s_format_id, FI_MSG_WARNING_INVALID_THUMBNAIL);
459
0
  } else {
460
0
    const unsigned thWidth = FreeImage_GetWidth(thumbnail);
461
0
    const unsigned thHeight = FreeImage_GetHeight(thumbnail);
462
    
463
0
    Imf::PreviewImage preview(thWidth, thHeight);
464
465
    // copy thumbnail to 32-bit RGBA preview image
466
    
467
0
    const BYTE* src_line = FreeImage_GetScanLine(thumbnail, thHeight - 1);
468
0
    Imf::PreviewRgba* dst_line = preview.pixels();
469
0
    const unsigned srcPitch = FreeImage_GetPitch(thumbnail);
470
    
471
0
    for (unsigned y = 0; y < thHeight; y++) {
472
0
      const RGBQUAD* src_pixel = (RGBQUAD*)src_line;
473
0
      Imf::PreviewRgba* dst_pixel = dst_line;
474
      
475
0
      for(unsigned x = 0; x < thWidth; x++) {
476
0
        dst_pixel->r = src_pixel->rgbRed;
477
0
        dst_pixel->g = src_pixel->rgbGreen;
478
0
        dst_pixel->b = src_pixel->rgbBlue;
479
0
        dst_pixel->a = src_pixel->rgbReserved;
480
        
481
0
        src_pixel++;
482
0
        dst_pixel++;
483
0
      }
484
      
485
0
      src_line -= srcPitch;
486
0
      dst_line += thWidth;
487
0
    }
488
    
489
0
    header.setPreviewImage(preview);
490
0
  }
491
492
0
  return TRUE;
493
0
}
494
495
/**
496
Save using EXR_LC compression (works only with RGB[A]F images)
497
*/
498
static BOOL 
499
0
SaveAsEXR_LC(C_OStream& ostream, FIBITMAP *dib, Imf::Header& header, int width, int height) {
500
0
  int x, y;
501
0
  Imf::RgbaChannels rgbaChannels;
502
503
0
  try {
504
505
0
    FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
506
507
    // convert from float to half
508
0
    Imf::Array2D<Imf::Rgba> pixels(height, width);
509
0
    switch(image_type) {
510
0
      case FIT_RGBF:
511
0
        rgbaChannels = Imf::WRITE_YC;
512
0
        for(y = 0; y < height; y++) {
513
0
          FIRGBF *src_bits = (FIRGBF*)FreeImage_GetScanLine(dib, height - 1 - y);
514
0
          for(x = 0; x < width; x++) {
515
0
            Imf::Rgba &dst_bits = pixels[y][x];
516
0
            dst_bits.r = src_bits[x].red;
517
0
            dst_bits.g = src_bits[x].green;
518
0
            dst_bits.b = src_bits[x].blue;
519
0
          }
520
0
        }
521
0
        break;
522
0
      case FIT_RGBAF:
523
0
        rgbaChannels = Imf::WRITE_YCA;
524
0
        for(y = 0; y < height; y++) {
525
0
          FIRGBAF *src_bits = (FIRGBAF*)FreeImage_GetScanLine(dib, height - 1 - y);
526
0
          for(x = 0; x < width; x++) {
527
0
            Imf::Rgba &dst_bits = pixels[y][x];
528
0
            dst_bits.r = src_bits[x].red;
529
0
            dst_bits.g = src_bits[x].green;
530
0
            dst_bits.b = src_bits[x].blue;
531
0
            dst_bits.a = src_bits[x].alpha;
532
0
          }
533
0
        }
534
0
        break;
535
0
      default:
536
0
        THROW (Iex::IoExc, "Bad image type");
537
0
        break;
538
0
    }
539
540
    // write the data
541
0
    Imf::RgbaOutputFile file(ostream, header, rgbaChannels);
542
0
    file.setFrameBuffer (&pixels[0][0], 1, width);
543
0
    file.writePixels (height);
544
545
0
    return TRUE;
546
547
0
  } catch(Iex::BaseExc & e) {
548
0
    FreeImage_OutputMessageProc(s_format_id, e.what());
549
550
0
    return FALSE;
551
0
  }
552
553
0
}
554
555
static BOOL DLL_CALLCONV
556
0
Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
557
0
  const char *channel_name[4] = { "R", "G", "B", "A" };
558
0
  BOOL bIsFlipped = FALSE;
559
0
  half *halfData = NULL;
560
561
0
  if(!dib || !handle) return FALSE;
562
563
0
  try {
564
    // check for EXR_LC compression and verify that the format is RGB
565
0
    if((flags & EXR_LC) == EXR_LC) {
566
0
      FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
567
0
      if(((image_type != FIT_RGBF) && (image_type != FIT_RGBAF)) || ((flags & EXR_FLOAT) == EXR_FLOAT)) {
568
0
        THROW (Iex::IoExc, "EXR_LC compression is only available with RGB[A]F images");
569
0
      }
570
0
      if((FreeImage_GetWidth(dib) % 2) || (FreeImage_GetHeight(dib) % 2)) {
571
0
        THROW (Iex::IoExc, "EXR_LC compression only works when the width and height are a multiple of 2");
572
0
      }
573
0
    }
574
575
    // wrap the FreeImage IO stream
576
0
    C_OStream ostream(io, handle);
577
578
    // compression
579
0
    Imf::Compression compress;
580
0
    if((flags & EXR_NONE) == EXR_NONE) {
581
      // no compression
582
0
      compress = Imf::NO_COMPRESSION;
583
0
    } else if((flags & EXR_ZIP) == EXR_ZIP) {
584
      // zlib compression, in blocks of 16 scan lines
585
0
      compress = Imf::ZIP_COMPRESSION;
586
0
    } else if((flags & EXR_PIZ) == EXR_PIZ) {
587
      // piz-based wavelet compression
588
0
      compress = Imf::PIZ_COMPRESSION;
589
0
    } else if((flags & EXR_PXR24) == EXR_PXR24) {
590
      // lossy 24-bit float compression
591
0
      compress = Imf::PXR24_COMPRESSION;
592
0
    } else if((flags & EXR_B44) == EXR_B44) {
593
      // lossy 44% float compression
594
0
      compress = Imf::B44_COMPRESSION;
595
0
    } else {
596
      // default value
597
0
      compress = Imf::PIZ_COMPRESSION;
598
0
    }
599
600
    // create the header
601
0
    int width  = FreeImage_GetWidth(dib);
602
0
    int height = FreeImage_GetHeight(dib);
603
0
    int dx = 0, dy = 0;
604
605
0
    Imath::Box2i dataWindow (Imath::V2i (0, 0), Imath::V2i (width - 1, height - 1));
606
0
    Imath::Box2i displayWindow (Imath::V2i (-dx, -dy), Imath::V2i (width - dx - 1, height - dy - 1));
607
608
0
    Imf::Header header = Imf::Header(displayWindow, dataWindow, 1, 
609
0
      Imath::V2f(0,0), 1, 
610
0
      Imf::INCREASING_Y, compress);           
611
612
    // handle thumbnail
613
0
    SetPreviewImage(dib, header);
614
    
615
    // check for EXR_LC compression
616
0
    if((flags & EXR_LC) == EXR_LC) {
617
0
      return SaveAsEXR_LC(ostream, dib, header, width, height);
618
0
    }
619
620
    // output pixel type
621
0
    Imf::PixelType pixelType;
622
0
    if((flags & EXR_FLOAT) == EXR_FLOAT) {
623
0
      pixelType = Imf::FLOAT; // save as float data type
624
0
    } else {
625
      // default value
626
0
      pixelType = Imf::HALF;  // save as half data type
627
0
    }
628
629
    // check the data type and number of channels
630
0
    int components = 0;
631
0
    FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
632
0
    switch(image_type) {
633
0
      case FIT_FLOAT:
634
0
        components = 1;
635
        // insert luminance channel
636
0
        header.channels().insert ("Y", Imf::Channel(pixelType));
637
0
        break;
638
0
      case FIT_RGBF:
639
0
        components = 3;
640
0
        for(int c = 0; c < components; c++) {
641
          // insert R, G and B channels
642
0
          header.channels().insert (channel_name[c], Imf::Channel(pixelType));
643
0
        }
644
0
        break;
645
0
      case FIT_RGBAF:
646
0
        components = 4;
647
0
        for(int c = 0; c < components; c++) {
648
          // insert R, G, B and A channels
649
0
          header.channels().insert (channel_name[c], Imf::Channel(pixelType));
650
0
        }
651
0
        break;
652
0
      default:
653
0
        THROW (Iex::ArgExc, "Cannot save: invalid data type.\nConvert the image to float before saving as OpenEXR.");
654
0
    }
655
656
    // build a frame buffer (i.e. what we have on input)
657
0
    Imf::FrameBuffer frameBuffer;
658
659
0
    BYTE *bits = NULL;  // pointer to our pixel buffer
660
0
    size_t bytespp = 0; // size of our pixel in bytes
661
0
    size_t bytespc = 0; // size of our pixel component in bytes
662
0
    unsigned pitch = 0; // size of our yStride in bytes
663
664
665
0
    if(pixelType == Imf::HALF) {
666
      // convert from float to half
667
0
      halfData = new(std::nothrow) half[width * height * components];
668
0
      if(!halfData) {
669
0
        THROW (Iex::NullExc, FI_MSG_ERROR_MEMORY);
670
0
      }
671
672
0
      for(int y = 0; y < height; y++) {
673
0
        float *src_bits = (float*)FreeImage_GetScanLine(dib, height - 1 - y);
674
0
        half *dst_bits = halfData + y * width * components;
675
0
        for(int x = 0; x < width; x++) {
676
0
          for(int c = 0; c < components; c++) {
677
0
            dst_bits[c] = src_bits[c];
678
0
          }
679
0
          src_bits += components;
680
0
          dst_bits += components;
681
0
        }
682
0
      }
683
0
      bits = (BYTE*)halfData;
684
0
      bytespc = sizeof(half);
685
0
      bytespp = sizeof(half) * components;
686
0
      pitch = sizeof(half) * width * components;
687
0
    } else if(pixelType == Imf::FLOAT) {
688
      // invert dib scanlines
689
0
      bIsFlipped = FreeImage_FlipVertical(dib);
690
    
691
0
      bits = FreeImage_GetBits(dib);
692
0
      bytespc = sizeof(float);
693
0
      bytespp = sizeof(float) * components;
694
0
      pitch = FreeImage_GetPitch(dib);
695
0
    }
696
697
0
    if(image_type == FIT_FLOAT) {
698
0
      frameBuffer.insert ("Y",  // name
699
0
        Imf::Slice (pixelType,  // type
700
0
        (char*)(bits),      // base
701
0
        bytespp,        // xStride
702
0
        pitch));        // yStride
703
0
    } else if((image_type == FIT_RGBF) || (image_type == FIT_RGBAF)) {     
704
0
      for(int c = 0; c < components; c++) {
705
0
        char *channel_base = (char*)(bits) + c*bytespc;
706
0
        frameBuffer.insert (channel_name[c],// name
707
0
          Imf::Slice (pixelType,      // type
708
0
          channel_base,         // base
709
0
          bytespp,  // xStride
710
0
          pitch));  // yStride
711
0
      }
712
0
    }
713
714
    // write the data
715
0
    Imf::OutputFile file (ostream, header);
716
0
    file.setFrameBuffer (frameBuffer);
717
0
    file.writePixels (height);
718
719
0
    if(halfData != NULL) {
720
0
      delete[] halfData;
721
0
    }
722
0
    if(bIsFlipped) {
723
      // invert dib scanlines
724
0
      FreeImage_FlipVertical(dib);
725
0
    }
726
727
0
    return TRUE;
728
729
0
  } catch(Iex::BaseExc & e) {
730
0
    if(halfData != NULL) {
731
0
      delete[] halfData;
732
0
    }
733
0
    if(bIsFlipped) {
734
      // invert dib scanlines
735
0
      FreeImage_FlipVertical(dib);
736
0
    }
737
738
0
    FreeImage_OutputMessageProc(s_format_id, e.what());
739
740
0
    return FALSE;
741
0
  } 
742
0
}
743
744
// ==========================================================
745
//   Init
746
// ==========================================================
747
748
void DLL_CALLCONV
749
2
InitEXR(Plugin *plugin, int format_id) {
750
2
  s_format_id = format_id;
751
752
  // initialize the OpenEXR library
753
  // note that this OpenEXR function produce so called "false memory leaks"
754
  // see http://lists.nongnu.org/archive/html/openexr-devel/2013-11/msg00000.html
755
2
  Imf::staticInitialize();
756
757
2
  plugin->format_proc = Format;
758
2
  plugin->description_proc = Description;
759
2
  plugin->extension_proc = Extension;
760
2
  plugin->regexpr_proc = RegExpr;
761
2
  plugin->open_proc = NULL;
762
2
  plugin->close_proc = NULL;
763
2
  plugin->pagecount_proc = NULL;
764
2
  plugin->pagecapability_proc = NULL;
765
2
  plugin->load_proc = Load;
766
2
  plugin->save_proc = Save;
767
2
  plugin->validate_proc = Validate;
768
2
  plugin->mime_proc = MimeType;
769
2
  plugin->supports_export_bpp_proc = SupportsExportDepth;
770
2
  plugin->supports_export_type_proc = SupportsExportType;
771
2
  plugin->supports_icc_profiles_proc = NULL;
772
2
  plugin->supports_no_pixels_proc = SupportsNoPixels;
773
2
}