1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 // By downloading, copying, installing or using the software you agree to this license.
6 // If you do not agree to this license, do not download, install,
7 // copy or use the software.
8 //
9 //
10 // License Agreement
11 // For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
14 // Copyright (C) 2009, Willow Garage Inc., all rights reserved.
15 // Third party copyrights are property of their respective owners.
16 //
17 // Redistribution and use in source and binary forms, with or without modification,
18 // are permitted provided that the following conditions are met:
19 //
20 // * Redistribution's of source code must retain the above copyright notice,
21 // this list of conditions and the following disclaimer.
22 //
23 // * Redistribution's in binary form must reproduce the above copyright notice,
24 // this list of conditions and the following disclaimer in the documentation
25 // and/or other materials provided with the distribution.
26 //
27 // * The name of the copyright holders may not be used to endorse or promote products
28 // derived from this software without specific prior written permission.
29 //
30 // This software is provided by the copyright holders and contributors "as is" and
31 // any express or implied warranties, including, but not limited to, the implied
32 // warranties of merchantability and fitness for a particular purpose are disclaimed.
33 // In no event shall the Intel Corporation or contributors be liable for any direct,
34 // indirect, incidental, special, exemplary, or consequential damages
35 // (including, but not limited to, procurement of substitute goods or services;
36 // loss of use, data, or profits; or business interruption) however caused
37 // and on any theory of liability, whether in contract, strict liability,
38 // or tort (including negligence or otherwise) arising in any way out of
39 // the use of this software, even if advised of the possibility of such damage.
40 //
41 //M*/
42
43 #include "precomp.hpp"
44
45 #ifdef HAVE_OPENEXR
46
47 #if defined _MSC_VER && _MSC_VER >= 1200
48 # pragma warning( disable: 4100 4244 4267 )
49 #endif
50
51 #if defined __GNUC__ && defined __APPLE__
52 # pragma GCC diagnostic ignored "-Wshadow"
53 #endif
54
55 #include <ImfHeader.h>
56 #include <ImfInputFile.h>
57 #include <ImfOutputFile.h>
58 #include <ImfChannelList.h>
59 #include <ImfStandardAttributes.h>
60 #include <half.h>
61 #include "grfmt_exr.hpp"
62
63 #if defined _WIN32
64
65 #undef UINT
66 #define UINT ((Imf::PixelType)0)
67 #undef HALF
68 #define HALF ((Imf::PixelType)1)
69 #undef FLOAT
70 #define FLOAT ((Imf::PixelType)2)
71
72 #endif
73
74 namespace cv
75 {
76
77 /////////////////////// ExrDecoder ///////////////////
78
ExrDecoder()79 ExrDecoder::ExrDecoder()
80 {
81 m_signature = "\x76\x2f\x31\x01";
82 m_file = 0;
83 m_red = m_green = m_blue = 0;
84 }
85
86
~ExrDecoder()87 ExrDecoder::~ExrDecoder()
88 {
89 close();
90 }
91
92
close()93 void ExrDecoder::close()
94 {
95 if( m_file )
96 {
97 delete m_file;
98 m_file = 0;
99 }
100 }
101
102
type() const103 int ExrDecoder::type() const
104 {
105 return CV_MAKETYPE((m_isfloat ? CV_32F : CV_32S), m_iscolor ? 3 : 1);
106 }
107
108
readHeader()109 bool ExrDecoder::readHeader()
110 {
111 bool result = false;
112
113 m_file = new InputFile( m_filename.c_str() );
114
115 if( !m_file ) // probably paranoid
116 return false;
117
118 m_datawindow = m_file->header().dataWindow();
119 m_width = m_datawindow.max.x - m_datawindow.min.x + 1;
120 m_height = m_datawindow.max.y - m_datawindow.min.y + 1;
121
122 // the type HALF is converted to 32 bit float
123 // and the other types supported by OpenEXR are 32 bit anyway
124 m_bit_depth = 32;
125
126 if( hasChromaticities( m_file->header() ))
127 m_chroma = chromaticities( m_file->header() );
128
129 const ChannelList &channels = m_file->header().channels();
130 m_red = channels.findChannel( "R" );
131 m_green = channels.findChannel( "G" );
132 m_blue = channels.findChannel( "B" );
133 if( m_red || m_green || m_blue )
134 {
135 m_iscolor = true;
136 m_ischroma = false;
137 result = true;
138 }
139 else
140 {
141 m_green = channels.findChannel( "Y" );
142 if( m_green )
143 {
144 m_ischroma = true;
145 m_red = channels.findChannel( "RY" );
146 m_blue = channels.findChannel( "BY" );
147 m_iscolor = (m_blue || m_red);
148 result = true;
149 }
150 else
151 result = false;
152 }
153
154 if( result )
155 {
156 int uintcnt = 0;
157 int chcnt = 0;
158 if( m_red )
159 {
160 chcnt++;
161 uintcnt += ( m_red->type == UINT );
162 }
163 if( m_green )
164 {
165 chcnt++;
166 uintcnt += ( m_green->type == UINT );
167 }
168 if( m_blue )
169 {
170 chcnt++;
171 uintcnt += ( m_blue->type == UINT );
172 }
173 m_type = (chcnt == uintcnt) ? UINT : FLOAT;
174
175 m_isfloat = (m_type == FLOAT);
176 }
177
178 if( !result )
179 close();
180
181 return result;
182 }
183
184
readData(Mat & img)185 bool ExrDecoder::readData( Mat& img )
186 {
187 m_native_depth = CV_MAT_DEPTH(type()) == img.depth();
188 bool color = img.channels() > 1;
189
190 uchar* data = img.ptr();
191 int step = img.step;
192 bool justcopy = m_native_depth;
193 bool chromatorgb = false;
194 bool rgbtogray = false;
195 bool result = true;
196 FrameBuffer frame;
197 int xsample[3] = {1, 1, 1};
198 char *buffer;
199 int xstep;
200 int ystep;
201
202 xstep = m_native_depth ? 4 : 1;
203
204 if( !m_native_depth || (!color && m_iscolor ))
205 {
206 buffer = (char *)new float[ m_width * 3 ];
207 ystep = 0;
208 }
209 else
210 {
211 buffer = (char *)data;
212 ystep = step;
213 }
214
215 if( m_ischroma )
216 {
217 if( color )
218 {
219 if( m_iscolor )
220 {
221 if( m_blue )
222 {
223 frame.insert( "BY", Slice( m_type,
224 buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep,
225 12, ystep, m_blue->xSampling, m_blue->ySampling, 0.0 ));
226 xsample[0] = m_blue->ySampling;
227 }
228 if( m_green )
229 {
230 frame.insert( "Y", Slice( m_type,
231 buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 4,
232 12, ystep, m_green->xSampling, m_green->ySampling, 0.0 ));
233 xsample[1] = m_green->ySampling;
234 }
235 if( m_red )
236 {
237 frame.insert( "RY", Slice( m_type,
238 buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 8,
239 12, ystep, m_red->xSampling, m_red->ySampling, 0.0 ));
240 xsample[2] = m_red->ySampling;
241 }
242 chromatorgb = true;
243 }
244 else
245 {
246 frame.insert( "Y", Slice( m_type,
247 buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep,
248 12, ystep, m_green->xSampling, m_green->ySampling, 0.0 ));
249 frame.insert( "Y", Slice( m_type,
250 buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 4,
251 12, ystep, m_green->xSampling, m_green->ySampling, 0.0 ));
252 frame.insert( "Y", Slice( m_type,
253 buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 8,
254 12, ystep, m_green->xSampling, m_green->ySampling, 0.0 ));
255 xsample[0] = m_green->ySampling;
256 xsample[1] = m_green->ySampling;
257 xsample[2] = m_green->ySampling;
258 }
259 }
260 else
261 {
262 frame.insert( "Y", Slice( m_type,
263 buffer - m_datawindow.min.x * 4 - m_datawindow.min.y * ystep,
264 4, ystep, m_green->xSampling, m_green->ySampling, 0.0 ));
265 xsample[0] = m_green->ySampling;
266 }
267 }
268 else
269 {
270 if( m_blue )
271 {
272 frame.insert( "B", Slice( m_type,
273 buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep,
274 12, ystep, m_blue->xSampling, m_blue->ySampling, 0.0 ));
275 xsample[0] = m_blue->ySampling;
276 }
277 if( m_green )
278 {
279 frame.insert( "G", Slice( m_type,
280 buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 4,
281 12, ystep, m_green->xSampling, m_green->ySampling, 0.0 ));
282 xsample[1] = m_green->ySampling;
283 }
284 if( m_red )
285 {
286 frame.insert( "R", Slice( m_type,
287 buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 8,
288 12, ystep, m_red->xSampling, m_red->ySampling, 0.0 ));
289 xsample[2] = m_red->ySampling;
290 }
291 if(color == 0)
292 {
293 rgbtogray = true;
294 justcopy = false;
295 }
296 }
297
298 m_file->setFrameBuffer( frame );
299 if( justcopy )
300 {
301 m_file->readPixels( m_datawindow.min.y, m_datawindow.max.y );
302
303 if( color )
304 {
305 if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) )
306 UpSample( data, 3, step / xstep, xsample[0], m_blue->ySampling );
307 if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) )
308 UpSample( data + xstep, 3, step / xstep, xsample[1], m_green->ySampling );
309 if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) )
310 UpSample( data + 2 * xstep, 3, step / xstep, xsample[2], m_red->ySampling );
311 }
312 else if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) )
313 UpSample( data, 1, step / xstep, xsample[0], m_green->ySampling );
314 }
315 else
316 {
317 uchar *out = data;
318 int x, y;
319 for( y = m_datawindow.min.y; y <= m_datawindow.max.y; y++ )
320 {
321 m_file->readPixels( y, y );
322
323 if( rgbtogray )
324 {
325 if( xsample[0] != 1 )
326 UpSampleX( (float *)buffer, 3, xsample[0] );
327 if( xsample[1] != 1 )
328 UpSampleX( (float *)buffer + 4, 3, xsample[1] );
329 if( xsample[2] != 1 )
330 UpSampleX( (float *)buffer + 8, 3, xsample[2] );
331
332 RGBToGray( (float *)buffer, (float *)out );
333 }
334 else
335 {
336 if( xsample[0] != 1 )
337 UpSampleX( (float *)buffer, 3, xsample[0] );
338 if( xsample[1] != 1 )
339 UpSampleX( (float *)(buffer + 4), 3, xsample[1] );
340 if( xsample[2] != 1 )
341 UpSampleX( (float *)(buffer + 8), 3, xsample[2] );
342
343 if( chromatorgb )
344 ChromaToBGR( (float *)buffer, 1, step );
345
346 if( m_type == FLOAT )
347 {
348 float *fi = (float *)buffer;
349 for( x = 0; x < m_width * 3; x++)
350 {
351 out[x] = cv::saturate_cast<uchar>(fi[x]*5);
352 }
353 }
354 else
355 {
356 unsigned *ui = (unsigned *)buffer;
357 for( x = 0; x < m_width * 3; x++)
358 {
359 out[x] = cv::saturate_cast<uchar>(ui[x]);
360 }
361 }
362 }
363
364 out += step;
365 }
366 if( color )
367 {
368 if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) )
369 UpSampleY( data, 3, step / xstep, m_blue->ySampling );
370 if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) )
371 UpSampleY( data + xstep, 3, step / xstep, m_green->ySampling );
372 if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) )
373 UpSampleY( data + 2 * xstep, 3, step / xstep, m_red->ySampling );
374 }
375 else if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) )
376 UpSampleY( data, 1, step / xstep, m_green->ySampling );
377 }
378
379 if( chromatorgb )
380 ChromaToBGR( (float *)data, m_height, step / xstep );
381
382 close();
383
384 return result;
385 }
386
387 /**
388 // on entry pixel values are stored packed in the upper left corner of the image
389 // this functions expands them by duplication to cover the whole image
390 */
UpSample(uchar * data,int xstep,int ystep,int xsample,int ysample)391 void ExrDecoder::UpSample( uchar *data, int xstep, int ystep, int xsample, int ysample )
392 {
393 for( int y = (m_height - 1) / ysample, yre = m_height - ysample; y >= 0; y--, yre -= ysample )
394 {
395 for( int x = (m_width - 1) / xsample, xre = m_width - xsample; x >= 0; x--, xre -= xsample )
396 {
397 for( int i = 0; i < ysample; i++ )
398 {
399 for( int n = 0; n < xsample; n++ )
400 {
401 if( !m_native_depth )
402 data[(yre + i) * ystep + (xre + n) * xstep] = data[y * ystep + x * xstep];
403 else if( m_type == FLOAT )
404 ((float *)data)[(yre + i) * ystep + (xre + n) * xstep] = ((float *)data)[y * ystep + x * xstep];
405 else
406 ((unsigned *)data)[(yre + i) * ystep + (xre + n) * xstep] = ((unsigned *)data)[y * ystep + x * xstep];
407 }
408 }
409 }
410 }
411 }
412
413 /**
414 // on entry pixel values are stored packed in the upper left corner of the image
415 // this functions expands them by duplication to cover the whole image
416 */
UpSampleX(float * data,int xstep,int xsample)417 void ExrDecoder::UpSampleX( float *data, int xstep, int xsample )
418 {
419 for( int x = (m_width - 1) / xsample, xre = m_width - xsample; x >= 0; x--, xre -= xsample )
420 {
421 for( int n = 0; n < xsample; n++ )
422 {
423 if( m_type == FLOAT )
424 ((float *)data)[(xre + n) * xstep] = ((float *)data)[x * xstep];
425 else
426 ((unsigned *)data)[(xre + n) * xstep] = ((unsigned *)data)[x * xstep];
427 }
428 }
429 }
430
431 /**
432 // on entry pixel values are stored packed in the upper left corner of the image
433 // this functions expands them by duplication to cover the whole image
434 */
UpSampleY(uchar * data,int xstep,int ystep,int ysample)435 void ExrDecoder::UpSampleY( uchar *data, int xstep, int ystep, int ysample )
436 {
437 for( int y = m_height - ysample, yre = m_height - ysample; y >= 0; y -= ysample, yre -= ysample )
438 {
439 for( int x = 0; x < m_width; x++ )
440 {
441 for( int i = 1; i < ysample; i++ )
442 {
443 if( !m_native_depth )
444 data[(yre + i) * ystep + x * xstep] = data[y * ystep + x * xstep];
445 else if( m_type == FLOAT )
446 ((float *)data)[(yre + i) * ystep + x * xstep] = ((float *)data)[y * ystep + x * xstep];
447 else
448 ((unsigned *)data)[(yre + i) * ystep + x * xstep] = ((unsigned *)data)[y * ystep + x * xstep];
449 }
450 }
451 }
452 }
453
454 /**
455 // algorithm from ImfRgbaYca.cpp
456 */
ChromaToBGR(float * data,int numlines,int step)457 void ExrDecoder::ChromaToBGR( float *data, int numlines, int step )
458 {
459 for( int y = 0; y < numlines; y++ )
460 {
461 for( int x = 0; x < m_width; x++ )
462 {
463 double b, Y, r;
464 if( !m_native_depth )
465 {
466 b = ((uchar *)data)[y * step + x * 3];
467 Y = ((uchar *)data)[y * step + x * 3 + 1];
468 r = ((uchar *)data)[y * step + x * 3 + 2];
469 }
470 else if( m_type == FLOAT )
471 {
472 b = data[y * step + x * 3];
473 Y = data[y * step + x * 3 + 1];
474 r = data[y * step + x * 3 + 2];
475 }
476 else
477 {
478 b = ((unsigned *)data)[y * step + x * 3];
479 Y = ((unsigned *)data)[y * step + x * 3 + 1];
480 r = ((unsigned *)data)[y * step + x * 3 + 2];
481 }
482 r = (r + 1) * Y;
483 b = (b + 1) * Y;
484 Y = (Y - b * m_chroma.blue[1] - r * m_chroma.red[1]) / m_chroma.green[1];
485
486 if( !m_native_depth )
487 {
488 ((uchar *)data)[y * step + x * 3 + 0] = cv::saturate_cast<uchar>(b);
489 ((uchar *)data)[y * step + x * 3 + 1] = cv::saturate_cast<uchar>(Y);
490 ((uchar *)data)[y * step + x * 3 + 2] = cv::saturate_cast<uchar>(r);
491 }
492 else if( m_type == FLOAT )
493 {
494 data[y * step + x * 3] = (float)b;
495 data[y * step + x * 3 + 1] = (float)Y;
496 data[y * step + x * 3 + 2] = (float)r;
497 }
498 else
499 {
500 int t = cvRound(b);
501 ((unsigned *)data)[y * step + x * 3 + 0] = (unsigned)MAX(t, 0);
502 t = cvRound(Y);
503 ((unsigned *)data)[y * step + x * 3 + 1] = (unsigned)MAX(t, 0);
504 t = cvRound(r);
505 ((unsigned *)data)[y * step + x * 3 + 2] = (unsigned)MAX(t, 0);
506 }
507 }
508 }
509 }
510
511
512 /**
513 // convert one row to gray
514 */
RGBToGray(float * in,float * out)515 void ExrDecoder::RGBToGray( float *in, float *out )
516 {
517 if( m_type == FLOAT )
518 {
519 if( m_native_depth )
520 {
521 for( int i = 0, n = 0; i < m_width; i++, n += 3 )
522 out[i] = in[n] * m_chroma.blue[0] + in[n + 1] * m_chroma.green[0] + in[n + 2] * m_chroma.red[0];
523 }
524 else
525 {
526 uchar *o = (uchar *)out;
527 for( int i = 0, n = 0; i < m_width; i++, n += 3 )
528 o[i] = (uchar) (in[n] * m_chroma.blue[0] + in[n + 1] * m_chroma.green[0] + in[n + 2] * m_chroma.red[0]);
529 }
530 }
531 else // UINT
532 {
533 if( m_native_depth )
534 {
535 unsigned *ui = (unsigned *)in;
536 for( int i = 0; i < m_width * 3; i++ )
537 ui[i] -= 0x80000000;
538 int *si = (int *)in;
539 for( int i = 0, n = 0; i < m_width; i++, n += 3 )
540 ((int *)out)[i] = int(si[n] * m_chroma.blue[0] + si[n + 1] * m_chroma.green[0] + si[n + 2] * m_chroma.red[0]);
541 }
542 else // how to best convert float to uchar?
543 {
544 unsigned *ui = (unsigned *)in;
545 for( int i = 0, n = 0; i < m_width; i++, n += 3 )
546 ((uchar *)out)[i] = uchar((ui[n] * m_chroma.blue[0] + ui[n + 1] * m_chroma.green[0] + ui[n + 2] * m_chroma.red[0]) * (256.0 / 4294967296.0));
547 }
548 }
549 }
550
551
newDecoder() const552 ImageDecoder ExrDecoder::newDecoder() const
553 {
554 return makePtr<ExrDecoder>();
555 }
556
557 /////////////////////// ExrEncoder ///////////////////
558
559
ExrEncoder()560 ExrEncoder::ExrEncoder()
561 {
562 m_description = "OpenEXR Image files (*.exr)";
563 }
564
565
~ExrEncoder()566 ExrEncoder::~ExrEncoder()
567 {
568 }
569
570
isFormatSupported(int depth) const571 bool ExrEncoder::isFormatSupported( int depth ) const
572 {
573 return CV_MAT_DEPTH(depth) >= CV_8U && CV_MAT_DEPTH(depth) < CV_64F;
574 }
575
576
577 // TODO scale appropriately
write(const Mat & img,const std::vector<int> &)578 bool ExrEncoder::write( const Mat& img, const std::vector<int>& )
579 {
580 int width = img.cols, height = img.rows;
581 int depth = img.depth(), channels = img.channels();
582 bool result = false;
583 bool issigned = depth == CV_8S || depth == CV_16S || depth == CV_32S;
584 bool isfloat = depth == CV_32F || depth == CV_64F;
585 depth = CV_ELEM_SIZE1(depth)*8;
586 const int step = img.step;
587
588 Header header( width, height );
589 Imf::PixelType type;
590
591 if(depth == 8)
592 type = HALF;
593 else if(isfloat)
594 type = FLOAT;
595 else
596 type = UINT;
597
598 if( channels == 3 )
599 {
600 header.channels().insert( "R", Channel( type ));
601 header.channels().insert( "G", Channel( type ));
602 header.channels().insert( "B", Channel( type ));
603 //printf("bunt\n");
604 }
605 else
606 {
607 header.channels().insert( "Y", Channel( type ));
608 //printf("gray\n");
609 }
610
611 OutputFile file( m_filename.c_str(), header );
612
613 FrameBuffer frame;
614
615 char *buffer;
616 int bufferstep;
617 int size;
618 if( type == FLOAT && depth == 32 )
619 {
620 buffer = (char *)const_cast<uchar *>(img.ptr());
621 bufferstep = step;
622 size = 4;
623 }
624 else if( depth > 16 || type == UINT )
625 {
626 buffer = (char *)new unsigned[width * channels];
627 bufferstep = 0;
628 size = 4;
629 }
630 else
631 {
632 buffer = (char *)new half[width * channels];
633 bufferstep = 0;
634 size = 2;
635 }
636
637 //printf("depth %d %s\n", depth, types[type]);
638
639 if( channels == 3 )
640 {
641 frame.insert( "B", Slice( type, buffer, size * 3, bufferstep ));
642 frame.insert( "G", Slice( type, buffer + size, size * 3, bufferstep ));
643 frame.insert( "R", Slice( type, buffer + size * 2, size * 3, bufferstep ));
644 }
645 else
646 frame.insert( "Y", Slice( type, buffer, size, bufferstep ));
647
648 file.setFrameBuffer( frame );
649
650 int offset = issigned ? 1 << (depth - 1) : 0;
651
652 result = true;
653 if( type == FLOAT && depth == 32 )
654 {
655 try
656 {
657 file.writePixels( height );
658 }
659 catch(...)
660 {
661 result = false;
662 }
663 }
664 else
665 {
666 // int scale = 1 << (32 - depth);
667 // printf("scale %d\n", scale);
668 for(int line = 0; line < height; line++)
669 {
670 if(type == UINT)
671 {
672 unsigned *buf = (unsigned*)buffer; // FIXME 64-bit problems
673
674 if( depth <= 8 )
675 {
676 const uchar* sd = img.ptr(line);
677 for(int i = 0; i < width * channels; i++)
678 buf[i] = sd[i] + offset;
679 }
680 else if( depth <= 16 )
681 {
682 const unsigned short *sd = img.ptr<unsigned short>(line);
683 for(int i = 0; i < width * channels; i++)
684 buf[i] = sd[i] + offset;
685 }
686 else
687 {
688 const int *sd = img.ptr<int>(line); // FIXME 64-bit problems
689 for(int i = 0; i < width * channels; i++)
690 buf[i] = (unsigned) sd[i] + offset;
691 }
692 }
693 else
694 {
695 half *buf = (half *)buffer;
696
697 if( depth <= 8 )
698 {
699 const uchar* sd = img.ptr(line);
700 for(int i = 0; i < width * channels; i++)
701 buf[i] = sd[i];
702 }
703 else if( depth <= 16 )
704 {
705 const unsigned short *sd = img.ptr<unsigned short>(line);
706 for(int i = 0; i < width * channels; i++)
707 buf[i] = sd[i];
708 }
709 }
710 try
711 {
712 file.writePixels( 1 );
713 }
714 catch(...)
715 {
716 result = false;
717 break;
718 }
719 }
720 delete[] buffer;
721 }
722
723 return result;
724 }
725
726
newEncoder() const727 ImageEncoder ExrEncoder::newEncoder() const
728 {
729 return makePtr<ExrEncoder>();
730 }
731
732 }
733
734 #endif
735
736 /* End of file. */
737