1 ///////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2004, Industrial Light & Magic, a division of Lucas
4 // Digital Ltd. LLC
5 //
6 // All rights reserved.
7 //
8 // Redistribution and use in source and binary forms, with or without
9 // modification, are permitted provided that the following conditions are
10 // met:
11 // * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 // * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 // * Neither the name of Industrial Light & Magic nor the names of
18 // its contributors may be used to endorse or promote products derived
19 // from this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 ///////////////////////////////////////////////////////////////////////////
34
35
36
37 //-----------------------------------------------------------------------------
38 //
39 // class Header
40 //
41 //-----------------------------------------------------------------------------
42
43 #include <ImfHeader.h>
44 #include <ImfStdIO.h>
45 #include <ImfVersion.h>
46 #include <ImfCompressor.h>
47 #include <ImfMisc.h>
48 #include <ImfBoxAttribute.h>
49 #include <ImfChannelListAttribute.h>
50 #include <ImfChromaticitiesAttribute.h>
51 #include <ImfCompressionAttribute.h>
52 #include <ImfDoubleAttribute.h>
53 #include <ImfEnvmapAttribute.h>
54 #include <ImfFloatAttribute.h>
55 #include <ImfIntAttribute.h>
56 #include <ImfKeyCodeAttribute.h>
57 #include <ImfLineOrderAttribute.h>
58 #include <ImfMatrixAttribute.h>
59 #include <ImfOpaqueAttribute.h>
60 #include <ImfPreviewImageAttribute.h>
61 #include <ImfRationalAttribute.h>
62 #include <ImfStringAttribute.h>
63 #include <ImfStringVectorAttribute.h>
64 #include <ImfTileDescriptionAttribute.h>
65 #include <ImfTimeCodeAttribute.h>
66 #include <ImfVecAttribute.h>
67 #include "IlmThreadMutex.h"
68 #include "Iex.h"
69 #include <sstream>
70 #include <stdlib.h>
71 #include <time.h>
72
73
74 namespace Imf {
75
76 using namespace std;
77 using Imath::Box2i;
78 using Imath::V2i;
79 using Imath::V2f;
80 using IlmThread::Mutex;
81 using IlmThread::Lock;
82
83
84 namespace {
85
86 int maxImageWidth = 0;
87 int maxImageHeight = 0;
88 int maxTileWidth = 0;
89 int maxTileHeight = 0;
90
91
92 void
initialize(Header & header,const Box2i & displayWindow,const Box2i & dataWindow,float pixelAspectRatio,const V2f & screenWindowCenter,float screenWindowWidth,LineOrder lineOrder,Compression compression)93 initialize (Header &header,
94 const Box2i &displayWindow,
95 const Box2i &dataWindow,
96 float pixelAspectRatio,
97 const V2f &screenWindowCenter,
98 float screenWindowWidth,
99 LineOrder lineOrder,
100 Compression compression)
101 {
102 header.insert ("displayWindow", Box2iAttribute (displayWindow));
103 header.insert ("dataWindow", Box2iAttribute (dataWindow));
104 header.insert ("pixelAspectRatio", FloatAttribute (pixelAspectRatio));
105 header.insert ("screenWindowCenter", V2fAttribute (screenWindowCenter));
106 header.insert ("screenWindowWidth", FloatAttribute (screenWindowWidth));
107 header.insert ("lineOrder", LineOrderAttribute (lineOrder));
108 header.insert ("compression", CompressionAttribute (compression));
109 header.insert ("channels", ChannelListAttribute ());
110 }
111
112
113 bool
usesLongNames(const Header & header)114 usesLongNames (const Header &header)
115 {
116 //
117 // If an OpenEXR file contains any attribute names, attribute type names
118 // or channel names longer than 31 characters, then the file cannot be
119 // read by older versions of the IlmImf library (up to OpenEXR 1.6.1).
120 // Before writing the file header, we check if the header contains
121 // any names longer than 31 characters; if it does, then we set the
122 // LONG_NAMES_FLAG in the file version number. Older versions of the
123 // IlmImf library will refuse to read files that have the LONG_NAMES_FLAG
124 // set. Without the flag, older versions of the library would mis-
125 // interpret the file as broken.
126 //
127
128 for (Header::ConstIterator i = header.begin();
129 i != header.end();
130 ++i)
131 {
132 if (strlen (i.name()) >= 32 || strlen (i.attribute().typeName()) >= 32)
133 return true;
134 }
135
136 const ChannelList &channels = header.channels();
137
138 for (ChannelList::ConstIterator i = channels.begin();
139 i != channels.end();
140 ++i)
141 {
142 if (strlen (i.name()) >= 32)
143 return true;
144 }
145
146 return false;
147 }
148
149 template <size_t N>
checkIsNullTerminated(const char (& str)[N],const char * what)150 void checkIsNullTerminated (const char (&str)[N], const char *what)
151 {
152 for (int i = 0; i < N; ++i) {
153 if (str[i] == '\0')
154 return;
155 }
156 std::stringstream s;
157 s << "Invalid " << what << ": it is more than " << (N - 1)
158 << " characters long.";
159 throw Iex::InputExc(s);
160 }
161
162 } // namespace
163
164
Header(int width,int height,float pixelAspectRatio,const V2f & screenWindowCenter,float screenWindowWidth,LineOrder lineOrder,Compression compression)165 Header::Header (int width,
166 int height,
167 float pixelAspectRatio,
168 const V2f &screenWindowCenter,
169 float screenWindowWidth,
170 LineOrder lineOrder,
171 Compression compression)
172 :
173 _map()
174 {
175 staticInitialize();
176
177 Box2i displayWindow (V2i (0, 0), V2i (width - 1, height - 1));
178
179 initialize (*this,
180 displayWindow,
181 displayWindow,
182 pixelAspectRatio,
183 screenWindowCenter,
184 screenWindowWidth,
185 lineOrder,
186 compression);
187 }
188
189
Header(int width,int height,const Box2i & dataWindow,float pixelAspectRatio,const V2f & screenWindowCenter,float screenWindowWidth,LineOrder lineOrder,Compression compression)190 Header::Header (int width,
191 int height,
192 const Box2i &dataWindow,
193 float pixelAspectRatio,
194 const V2f &screenWindowCenter,
195 float screenWindowWidth,
196 LineOrder lineOrder,
197 Compression compression)
198 :
199 _map()
200 {
201 staticInitialize();
202
203 Box2i displayWindow (V2i (0, 0), V2i (width - 1, height - 1));
204
205 initialize (*this,
206 displayWindow,
207 dataWindow,
208 pixelAspectRatio,
209 screenWindowCenter,
210 screenWindowWidth,
211 lineOrder,
212 compression);
213 }
214
215
Header(const Box2i & displayWindow,const Box2i & dataWindow,float pixelAspectRatio,const V2f & screenWindowCenter,float screenWindowWidth,LineOrder lineOrder,Compression compression)216 Header::Header (const Box2i &displayWindow,
217 const Box2i &dataWindow,
218 float pixelAspectRatio,
219 const V2f &screenWindowCenter,
220 float screenWindowWidth,
221 LineOrder lineOrder,
222 Compression compression)
223 :
224 _map()
225 {
226 staticInitialize();
227
228 initialize (*this,
229 displayWindow,
230 dataWindow,
231 pixelAspectRatio,
232 screenWindowCenter,
233 screenWindowWidth,
234 lineOrder,
235 compression);
236 }
237
238
Header(const Header & other)239 Header::Header (const Header &other): _map()
240 {
241 for (AttributeMap::const_iterator i = other._map.begin();
242 i != other._map.end();
243 ++i)
244 {
245 insert (*i->first, *i->second);
246 }
247 }
248
249
~Header()250 Header::~Header ()
251 {
252 for (AttributeMap::iterator i = _map.begin();
253 i != _map.end();
254 ++i)
255 {
256 delete i->second;
257 }
258 }
259
260
261 Header &
operator =(const Header & other)262 Header::operator = (const Header &other)
263 {
264 if (this != &other)
265 {
266 for (AttributeMap::iterator i = _map.begin();
267 i != _map.end();
268 ++i)
269 {
270 delete i->second;
271 }
272
273 _map.erase (_map.begin(), _map.end());
274
275 for (AttributeMap::const_iterator i = other._map.begin();
276 i != other._map.end();
277 ++i)
278 {
279 insert (*i->first, *i->second);
280 }
281 }
282
283 return *this;
284 }
285
286
287 void
insert(const char name[],const Attribute & attribute)288 Header::insert (const char name[], const Attribute &attribute)
289 {
290 if (name[0] == 0)
291 THROW (Iex::ArgExc, "Image attribute name cannot be an empty string.");
292
293 AttributeMap::iterator i = _map.find (name);
294
295 if (i == _map.end())
296 {
297 Attribute *tmp = attribute.copy();
298
299 try
300 {
301 _map[name] = tmp;
302 }
303 catch (...)
304 {
305 delete tmp;
306 throw;
307 }
308 }
309 else
310 {
311 if (strcmp (i->second->typeName(), attribute.typeName()))
312 THROW (Iex::TypeExc, "Cannot assign a value of "
313 "type \"" << attribute.typeName() << "\" "
314 "to image attribute \"" << name << "\" of "
315 "type \"" << i->second->typeName() << "\".");
316
317 Attribute *tmp = attribute.copy();
318 delete i->second;
319 i->second = tmp;
320 }
321 }
322
323
324 void
insert(const string & name,const Attribute & attribute)325 Header::insert (const string &name, const Attribute &attribute)
326 {
327 insert (name.c_str(), attribute);
328 }
329
330
331 Attribute &
operator [](const char name[])332 Header::operator [] (const char name[])
333 {
334 AttributeMap::iterator i = _map.find (name);
335
336 if (i == _map.end())
337 THROW (Iex::ArgExc, "Cannot find image attribute \"" << name << "\".");
338
339 return *i->second;
340 }
341
342
343 const Attribute &
operator [](const char name[]) const344 Header::operator [] (const char name[]) const
345 {
346 AttributeMap::const_iterator i = _map.find (name);
347
348 if (i == _map.end())
349 THROW (Iex::ArgExc, "Cannot find image attribute \"" << name << "\".");
350
351 return *i->second;
352 }
353
354
355 Attribute &
operator [](const string & name)356 Header::operator [] (const string &name)
357 {
358 return this->operator[] (name.c_str());
359 }
360
361
362 const Attribute &
operator [](const string & name) const363 Header::operator [] (const string &name) const
364 {
365 return this->operator[] (name.c_str());
366 }
367
368
369 Header::Iterator
begin()370 Header::begin ()
371 {
372 return _map.begin();
373 }
374
375
376 Header::ConstIterator
begin() const377 Header::begin () const
378 {
379 return _map.begin();
380 }
381
382
383 Header::Iterator
end()384 Header::end ()
385 {
386 return _map.end();
387 }
388
389
390 Header::ConstIterator
end() const391 Header::end () const
392 {
393 return _map.end();
394 }
395
396
397 Header::Iterator
find(const char name[])398 Header::find (const char name[])
399 {
400 return _map.find (name);
401 }
402
403
404 Header::ConstIterator
find(const char name[]) const405 Header::find (const char name[]) const
406 {
407 return _map.find (name);
408 }
409
410
411 Header::Iterator
find(const string & name)412 Header::find (const string &name)
413 {
414 return find (name.c_str());
415 }
416
417
418 Header::ConstIterator
find(const string & name) const419 Header::find (const string &name) const
420 {
421 return find (name.c_str());
422 }
423
424
425 Imath::Box2i &
displayWindow()426 Header::displayWindow ()
427 {
428 return static_cast <Box2iAttribute &>
429 ((*this)["displayWindow"]).value();
430 }
431
432
433 const Imath::Box2i &
displayWindow() const434 Header::displayWindow () const
435 {
436 return static_cast <const Box2iAttribute &>
437 ((*this)["displayWindow"]).value();
438 }
439
440
441 Imath::Box2i &
dataWindow()442 Header::dataWindow ()
443 {
444 return static_cast <Box2iAttribute &>
445 ((*this)["dataWindow"]).value();
446 }
447
448
449 const Imath::Box2i &
dataWindow() const450 Header::dataWindow () const
451 {
452 return static_cast <const Box2iAttribute &>
453 ((*this)["dataWindow"]).value();
454 }
455
456
457 float &
pixelAspectRatio()458 Header::pixelAspectRatio ()
459 {
460 return static_cast <FloatAttribute &>
461 ((*this)["pixelAspectRatio"]).value();
462 }
463
464
465 const float &
pixelAspectRatio() const466 Header::pixelAspectRatio () const
467 {
468 return static_cast <const FloatAttribute &>
469 ((*this)["pixelAspectRatio"]).value();
470 }
471
472
473 Imath::V2f &
screenWindowCenter()474 Header::screenWindowCenter ()
475 {
476 return static_cast <V2fAttribute &>
477 ((*this)["screenWindowCenter"]).value();
478 }
479
480
481 const Imath::V2f &
screenWindowCenter() const482 Header::screenWindowCenter () const
483 {
484 return static_cast <const V2fAttribute &>
485 ((*this)["screenWindowCenter"]).value();
486 }
487
488
489 float &
screenWindowWidth()490 Header::screenWindowWidth ()
491 {
492 return static_cast <FloatAttribute &>
493 ((*this)["screenWindowWidth"]).value();
494 }
495
496
497 const float &
screenWindowWidth() const498 Header::screenWindowWidth () const
499 {
500 return static_cast <const FloatAttribute &>
501 ((*this)["screenWindowWidth"]).value();
502 }
503
504
505 ChannelList &
channels()506 Header::channels ()
507 {
508 return static_cast <ChannelListAttribute &>
509 ((*this)["channels"]).value();
510 }
511
512
513 const ChannelList &
channels() const514 Header::channels () const
515 {
516 return static_cast <const ChannelListAttribute &>
517 ((*this)["channels"]).value();
518 }
519
520
521 LineOrder &
lineOrder()522 Header::lineOrder ()
523 {
524 return static_cast <LineOrderAttribute &>
525 ((*this)["lineOrder"]).value();
526 }
527
528
529 const LineOrder &
lineOrder() const530 Header::lineOrder () const
531 {
532 return static_cast <const LineOrderAttribute &>
533 ((*this)["lineOrder"]).value();
534 }
535
536
537 Compression &
compression()538 Header::compression ()
539 {
540 return static_cast <CompressionAttribute &>
541 ((*this)["compression"]).value();
542 }
543
544
545 const Compression &
compression() const546 Header::compression () const
547 {
548 return static_cast <const CompressionAttribute &>
549 ((*this)["compression"]).value();
550 }
551
552
553 void
setTileDescription(const TileDescription & td)554 Header::setTileDescription(const TileDescription& td)
555 {
556 insert ("tiles", TileDescriptionAttribute (td));
557 }
558
559
560 bool
hasTileDescription() const561 Header::hasTileDescription() const
562 {
563 return findTypedAttribute <TileDescriptionAttribute> ("tiles") != 0;
564 }
565
566
567 TileDescription &
tileDescription()568 Header::tileDescription ()
569 {
570 return typedAttribute <TileDescriptionAttribute> ("tiles").value();
571 }
572
573
574 const TileDescription &
tileDescription() const575 Header::tileDescription () const
576 {
577 return typedAttribute <TileDescriptionAttribute> ("tiles").value();
578 }
579
580 void
setPreviewImage(const PreviewImage & pi)581 Header::setPreviewImage (const PreviewImage &pi)
582 {
583 insert ("preview", PreviewImageAttribute (pi));
584 }
585
586
587 PreviewImage &
previewImage()588 Header::previewImage ()
589 {
590 return typedAttribute <PreviewImageAttribute> ("preview").value();
591 }
592
593
594 const PreviewImage &
previewImage() const595 Header::previewImage () const
596 {
597 return typedAttribute <PreviewImageAttribute> ("preview").value();
598 }
599
600
601 bool
hasPreviewImage() const602 Header::hasPreviewImage () const
603 {
604 return findTypedAttribute <PreviewImageAttribute> ("preview") != 0;
605 }
606
607
608 void
sanityCheck(bool isTiled) const609 Header::sanityCheck (bool isTiled) const
610 {
611 //
612 // The display window and the data window must each
613 // contain at least one pixel. In addition, the
614 // coordinates of the window corners must be small
615 // enough to keep expressions like max-min+1 or
616 // max+min from overflowing.
617 //
618
619 const Box2i &displayWindow = this->displayWindow();
620
621 if (displayWindow.min.x > displayWindow.max.x ||
622 displayWindow.min.y > displayWindow.max.y ||
623 displayWindow.min.x <= -(INT_MAX / 2) ||
624 displayWindow.min.y <= -(INT_MAX / 2) ||
625 displayWindow.max.x >= (INT_MAX / 2) ||
626 displayWindow.max.y >= (INT_MAX / 2))
627 {
628 throw Iex::ArgExc ("Invalid display window in image header.");
629 }
630
631 const Box2i &dataWindow = this->dataWindow();
632
633 if (dataWindow.min.x > dataWindow.max.x ||
634 dataWindow.min.y > dataWindow.max.y ||
635 dataWindow.min.x <= -(INT_MAX / 2) ||
636 dataWindow.min.y <= -(INT_MAX / 2) ||
637 dataWindow.max.x >= (INT_MAX / 2) ||
638 dataWindow.max.y >= (INT_MAX / 2))
639 {
640 throw Iex::ArgExc ("Invalid data window in image header.");
641 }
642
643 if (maxImageWidth > 0 &&
644 maxImageWidth < dataWindow.max.x - dataWindow.min.x + 1)
645 {
646 THROW (Iex::ArgExc, "The width of the data window exceeds the "
647 "maximum width of " << maxImageWidth << "pixels.");
648 }
649
650 if (maxImageHeight > 0 &&
651 maxImageHeight < dataWindow.max.y - dataWindow.min.y + 1)
652 {
653 THROW (Iex::ArgExc, "The width of the data window exceeds the "
654 "maximum width of " << maxImageHeight << "pixels.");
655 }
656
657 //
658 // The pixel aspect ratio must be greater than 0.
659 // In applications, numbers like the the display or
660 // data window dimensions are likely to be multiplied
661 // or divided by the pixel aspect ratio; to avoid
662 // arithmetic exceptions, we limit the pixel aspect
663 // ratio to a range that is smaller than theoretically
664 // possible (real aspect ratios are likely to be close
665 // to 1.0 anyway).
666 //
667
668 float pixelAspectRatio = this->pixelAspectRatio();
669
670 const float MIN_PIXEL_ASPECT_RATIO = 1e-6f;
671 const float MAX_PIXEL_ASPECT_RATIO = 1e+6f;
672
673 if (pixelAspectRatio < MIN_PIXEL_ASPECT_RATIO ||
674 pixelAspectRatio > MAX_PIXEL_ASPECT_RATIO)
675 {
676 throw Iex::ArgExc ("Invalid pixel aspect ratio in image header.");
677 }
678
679 //
680 // The screen window width must not be less than 0.
681 // The size of the screen window can vary over a wide
682 // range (fish-eye lens to astronomical telescope),
683 // so we can't limit the screen window width to a
684 // small range.
685 //
686
687 float screenWindowWidth = this->screenWindowWidth();
688
689 if (screenWindowWidth < 0)
690 throw Iex::ArgExc ("Invalid screen window width in image header.");
691
692 //
693 // If the file is tiled, verify that the tile description has resonable
694 // values and check to see if the lineOrder is one of the predefined 3.
695 // If the file is not tiled, then the lineOrder can only be INCREASING_Y
696 // or DECREASING_Y.
697 //
698
699 LineOrder lineOrder = this->lineOrder();
700
701 if (isTiled)
702 {
703 if (!hasTileDescription())
704 {
705 throw Iex::ArgExc ("Tiled image has no tile "
706 "description attribute.");
707 }
708
709 const TileDescription &tileDesc = tileDescription();
710
711 if (tileDesc.xSize <= 0 || tileDesc.ySize <= 0)
712 throw Iex::ArgExc ("Invalid tile size in image header.");
713
714 if (maxTileWidth > 0 &&
715 maxTileWidth < tileDesc.xSize)
716 {
717 THROW (Iex::ArgExc, "The width of the tiles exceeds the maximum "
718 "width of " << maxTileWidth << "pixels.");
719 }
720
721 if (maxTileHeight > 0 &&
722 maxTileHeight < tileDesc.ySize)
723 {
724 THROW (Iex::ArgExc, "The width of the tiles exceeds the maximum "
725 "width of " << maxTileHeight << "pixels.");
726 }
727
728 if (tileDesc.mode != ONE_LEVEL &&
729 tileDesc.mode != MIPMAP_LEVELS &&
730 tileDesc.mode != RIPMAP_LEVELS)
731 throw Iex::ArgExc ("Invalid level mode in image header.");
732
733 if (tileDesc.roundingMode != ROUND_UP &&
734 tileDesc.roundingMode != ROUND_DOWN)
735 throw Iex::ArgExc ("Invalid level rounding mode in image header.");
736
737 if (lineOrder != INCREASING_Y &&
738 lineOrder != DECREASING_Y &&
739 lineOrder != RANDOM_Y)
740 throw Iex::ArgExc ("Invalid line order in image header.");
741 }
742 else
743 {
744 if (lineOrder != INCREASING_Y &&
745 lineOrder != DECREASING_Y)
746 throw Iex::ArgExc ("Invalid line order in image header.");
747 }
748
749 //
750 // The compression method must be one of the predefined values.
751 //
752
753 if (!isValidCompression (this->compression()))
754 throw Iex::ArgExc ("Unknown compression type in image header.");
755
756 //
757 // Check the channel list:
758 //
759 // If the file is tiled then for each channel, the type must be one of the
760 // predefined values, and the x and y sampling must both be 1.
761 //
762 // If the file is not tiled then for each channel, the type must be one
763 // of the predefined values, the x and y coordinates of the data window's
764 // upper left corner must be divisible by the x and y subsampling factors,
765 // and the width and height of the data window must be divisible by the
766 // x and y subsampling factors.
767 //
768
769 const ChannelList &channels = this->channels();
770
771 if (isTiled)
772 {
773 for (ChannelList::ConstIterator i = channels.begin();
774 i != channels.end();
775 ++i)
776 {
777 if (i.channel().type != UINT &&
778 i.channel().type != HALF &&
779 i.channel().type != FLOAT)
780 {
781 THROW (Iex::ArgExc, "Pixel type of \"" << i.name() << "\" "
782 "image channel is invalid.");
783 }
784
785 if (i.channel().xSampling != 1)
786 {
787 THROW (Iex::ArgExc, "The x subsampling factor for the "
788 "\"" << i.name() << "\" channel "
789 "is not 1.");
790 }
791
792 if (i.channel().ySampling != 1)
793 {
794 THROW (Iex::ArgExc, "The y subsampling factor for the "
795 "\"" << i.name() << "\" channel "
796 "is not 1.");
797 }
798 }
799 }
800 else
801 {
802 for (ChannelList::ConstIterator i = channels.begin();
803 i != channels.end();
804 ++i)
805 {
806 if (i.channel().type != UINT &&
807 i.channel().type != HALF &&
808 i.channel().type != FLOAT)
809 {
810 THROW (Iex::ArgExc, "Pixel type of \"" << i.name() << "\" "
811 "image channel is invalid.");
812 }
813
814 if (i.channel().xSampling < 1)
815 {
816 THROW (Iex::ArgExc, "The x subsampling factor for the "
817 "\"" << i.name() << "\" channel "
818 "is invalid.");
819 }
820
821 if (i.channel().ySampling < 1)
822 {
823 THROW (Iex::ArgExc, "The y subsampling factor for the "
824 "\"" << i.name() << "\" channel "
825 "is invalid.");
826 }
827
828 if (dataWindow.min.x % i.channel().xSampling)
829 {
830 THROW (Iex::ArgExc, "The minimum x coordinate of the "
831 "image's data window is not a multiple "
832 "of the x subsampling factor of "
833 "the \"" << i.name() << "\" channel.");
834 }
835
836 if (dataWindow.min.y % i.channel().ySampling)
837 {
838 THROW (Iex::ArgExc, "The minimum y coordinate of the "
839 "image's data window is not a multiple "
840 "of the y subsampling factor of "
841 "the \"" << i.name() << "\" channel.");
842 }
843
844 if ((dataWindow.max.x - dataWindow.min.x + 1) %
845 i.channel().xSampling)
846 {
847 THROW (Iex::ArgExc, "Number of pixels per row in the "
848 "image's data window is not a multiple "
849 "of the x subsampling factor of "
850 "the \"" << i.name() << "\" channel.");
851 }
852
853 if ((dataWindow.max.y - dataWindow.min.y + 1) %
854 i.channel().ySampling)
855 {
856 THROW (Iex::ArgExc, "Number of pixels per column in the "
857 "image's data window is not a multiple "
858 "of the y subsampling factor of "
859 "the \"" << i.name() << "\" channel.");
860 }
861 }
862 }
863 }
864
865
866 void
setMaxImageSize(int maxWidth,int maxHeight)867 Header::setMaxImageSize (int maxWidth, int maxHeight)
868 {
869 maxImageWidth = maxWidth;
870 maxImageHeight = maxHeight;
871 }
872
873
874 void
setMaxTileSize(int maxWidth,int maxHeight)875 Header::setMaxTileSize (int maxWidth, int maxHeight)
876 {
877 maxTileWidth = maxWidth;
878 maxTileHeight = maxHeight;
879 }
880
881
882 Int64
writeTo(OStream & os,bool isTiled) const883 Header::writeTo (OStream &os, bool isTiled) const
884 {
885 //
886 // Write a "magic number" to identify the file as an image file.
887 // Write the current file format version number.
888 //
889
890 Xdr::write <StreamIO> (os, MAGIC);
891
892 int version = EXR_VERSION;
893
894 if (isTiled)
895 version |= TILED_FLAG;
896
897 if (usesLongNames (*this))
898 version |= LONG_NAMES_FLAG;
899
900 Xdr::write <StreamIO> (os, version);
901
902 //
903 // Write all attributes. If we have a preview image attribute,
904 // keep track of its position in the file.
905 //
906
907 Int64 previewPosition = 0;
908
909 const Attribute *preview =
910 findTypedAttribute <PreviewImageAttribute> ("preview");
911
912 for (ConstIterator i = begin(); i != end(); ++i)
913 {
914 //
915 // Write the attribute's name and type.
916 //
917
918 Xdr::write <StreamIO> (os, i.name());
919 Xdr::write <StreamIO> (os, i.attribute().typeName());
920
921 //
922 // Write the size of the attribute value,
923 // and the value itself.
924 //
925
926 StdOSStream oss;
927 i.attribute().writeValueTo (oss, version);
928
929 std::string s = oss.str();
930 Xdr::write <StreamIO> (os, (int) s.length());
931
932 if (&i.attribute() == preview)
933 previewPosition = os.tellp();
934
935 os.write (s.data(), s.length());
936 }
937
938 //
939 // Write zero-length attribute name to mark the end of the header.
940 //
941
942 Xdr::write <StreamIO> (os, "");
943
944 return previewPosition;
945 }
946
947
948 void
readFrom(IStream & is,int & version)949 Header::readFrom (IStream &is, int &version)
950 {
951 //
952 // Read the magic number and the file format version number.
953 // Then check if we can read the rest of this file.
954 //
955
956 int magic;
957
958 Xdr::read <StreamIO> (is, magic);
959 Xdr::read <StreamIO> (is, version);
960
961 if (magic != MAGIC)
962 {
963 throw Iex::InputExc ("File is not an image file.");
964 }
965
966 if (getVersion (version) != EXR_VERSION)
967 {
968 THROW (Iex::InputExc, "Cannot read "
969 "version " << getVersion (version) << " "
970 "image files. Current file format version "
971 "is " << EXR_VERSION << ".");
972 }
973
974 if (!supportsFlags (getFlags (version)))
975 {
976 THROW (Iex::InputExc, "The file format version number's flag field "
977 "contains unrecognized flags.");
978 }
979
980 //
981 // Read all attributes.
982 //
983
984 while (true)
985 {
986 //
987 // Read the name of the attribute.
988 // A zero-length attribute name indicates the end of the header.
989 //
990
991 char name[Name::SIZE];
992 Xdr::read <StreamIO> (is, Name::MAX_LENGTH, name);
993
994 if (name[0] == 0)
995 break;
996
997 checkIsNullTerminated (name, "attribute name");
998
999 //
1000 // Read the attribute type and the size of the attribute value.
1001 //
1002
1003 char typeName[Name::SIZE];
1004 int size;
1005
1006 Xdr::read <StreamIO> (is, Name::MAX_LENGTH, typeName);
1007 checkIsNullTerminated (typeName, "attribute type name");
1008 Xdr::read <StreamIO> (is, size);
1009
1010 AttributeMap::iterator i = _map.find (name);
1011
1012 if (i != _map.end())
1013 {
1014 //
1015 // The attribute already exists (for example,
1016 // because it is a predefined attribute).
1017 // Read the attribute's new value from the file.
1018 //
1019
1020 if (strncmp (i->second->typeName(), typeName, sizeof (typeName)))
1021 THROW (Iex::InputExc, "Unexpected type for image attribute "
1022 "\"" << name << "\".");
1023
1024 i->second->readValueFrom (is, size, version);
1025 }
1026 else
1027 {
1028 //
1029 // The new attribute does not exist yet.
1030 // If the attribute type is of a known type,
1031 // read the attribute value. If the attribute
1032 // is of an unknown type, read its value and
1033 // store it as an OpaqueAttribute.
1034 //
1035
1036 Attribute *attr;
1037
1038 if (Attribute::knownType (typeName))
1039 attr = Attribute::newAttribute (typeName);
1040 else
1041 attr = new OpaqueAttribute (typeName);
1042
1043 try
1044 {
1045 attr->readValueFrom (is, size, version);
1046 _map[name] = attr;
1047 }
1048 catch (...)
1049 {
1050 delete attr;
1051 throw;
1052 }
1053 }
1054 }
1055 }
1056
1057
1058 void
staticInitialize()1059 staticInitialize ()
1060 {
1061 static Mutex criticalSection;
1062 Lock lock (criticalSection);
1063
1064 static bool initialized = false;
1065
1066 if (!initialized)
1067 {
1068 //
1069 // One-time initialization -- register
1070 // some predefined attribute types.
1071 //
1072
1073 Box2fAttribute::registerAttributeType();
1074 Box2iAttribute::registerAttributeType();
1075 ChannelListAttribute::registerAttributeType();
1076 CompressionAttribute::registerAttributeType();
1077 ChromaticitiesAttribute::registerAttributeType();
1078 DoubleAttribute::registerAttributeType();
1079 EnvmapAttribute::registerAttributeType();
1080 FloatAttribute::registerAttributeType();
1081 IntAttribute::registerAttributeType();
1082 KeyCodeAttribute::registerAttributeType();
1083 LineOrderAttribute::registerAttributeType();
1084 M33dAttribute::registerAttributeType();
1085 M33fAttribute::registerAttributeType();
1086 M44dAttribute::registerAttributeType();
1087 M44fAttribute::registerAttributeType();
1088 PreviewImageAttribute::registerAttributeType();
1089 RationalAttribute::registerAttributeType();
1090 StringAttribute::registerAttributeType();
1091 StringVectorAttribute::registerAttributeType();
1092 TileDescriptionAttribute::registerAttributeType();
1093 TimeCodeAttribute::registerAttributeType();
1094 V2dAttribute::registerAttributeType();
1095 V2fAttribute::registerAttributeType();
1096 V2iAttribute::registerAttributeType();
1097 V3dAttribute::registerAttributeType();
1098 V3fAttribute::registerAttributeType();
1099 V3iAttribute::registerAttributeType();
1100
1101 initialized = true;
1102 }
1103 }
1104
1105
1106 } // namespace Imf
1107