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 <ImfTileDescriptionAttribute.h>
64#include <ImfTimeCodeAttribute.h>
65#include <ImfVecAttribute.h>
66#include "IlmThreadMutex.h"
67#include "Iex.h"
68#include <sstream>
69#include <stdlib.h>
70#include <time.h>
71
72
73namespace Imf {
74
75using Imath::Box2i;
76using Imath::V2i;
77using Imath::V2f;
78using IlmThread::Mutex;
79using IlmThread::Lock;
80
81
82namespace {
83
84int maxImageWidth = 0;
85int maxImageHeight = 0;
86int maxTileWidth = 0;
87int maxTileHeight = 0;
88
89
90void
91initialize (Header &header,
92	    const Box2i &displayWindow,
93	    const Box2i &dataWindow,
94	    float pixelAspectRatio,
95	    const V2f &screenWindowCenter,
96	    float screenWindowWidth,
97	    LineOrder lineOrder,
98	    Compression compression)
99{
100    header.insert ("displayWindow", Box2iAttribute (displayWindow));
101    header.insert ("dataWindow", Box2iAttribute (dataWindow));
102    header.insert ("pixelAspectRatio", FloatAttribute (pixelAspectRatio));
103    header.insert ("screenWindowCenter", V2fAttribute (screenWindowCenter));
104    header.insert ("screenWindowWidth", FloatAttribute (screenWindowWidth));
105    header.insert ("lineOrder", LineOrderAttribute (lineOrder));
106    header.insert ("compression", CompressionAttribute (compression));
107    header.insert ("channels", ChannelListAttribute ());
108}
109
110} // namespace
111
112
113Header::Header (int width,
114		int height,
115		float pixelAspectRatio,
116		const V2f &screenWindowCenter,
117		float screenWindowWidth,
118		LineOrder lineOrder,
119		Compression compression)
120:
121    _map()
122{
123    staticInitialize();
124
125    Box2i displayWindow (V2i (0, 0), V2i (width - 1, height - 1));
126
127    initialize (*this,
128		displayWindow,
129		displayWindow,
130		pixelAspectRatio,
131		screenWindowCenter,
132		screenWindowWidth,
133		lineOrder,
134		compression);
135}
136
137
138Header::Header (int width,
139		int height,
140		const Box2i &dataWindow,
141		float pixelAspectRatio,
142		const V2f &screenWindowCenter,
143		float screenWindowWidth,
144		LineOrder lineOrder,
145		Compression compression)
146:
147    _map()
148{
149    staticInitialize();
150
151    Box2i displayWindow (V2i (0, 0), V2i (width - 1, height - 1));
152
153    initialize (*this,
154		displayWindow,
155		dataWindow,
156		pixelAspectRatio,
157		screenWindowCenter,
158		screenWindowWidth,
159		lineOrder,
160		compression);
161}
162
163
164Header::Header (const Box2i &displayWindow,
165		const Box2i &dataWindow,
166		float pixelAspectRatio,
167		const V2f &screenWindowCenter,
168		float screenWindowWidth,
169		LineOrder lineOrder,
170		Compression compression)
171:
172    _map()
173{
174    staticInitialize();
175
176    initialize (*this,
177		displayWindow,
178		dataWindow,
179		pixelAspectRatio,
180		screenWindowCenter,
181		screenWindowWidth,
182		lineOrder,
183		compression);
184}
185
186
187Header::Header (const Header &other): _map()
188{
189    for (AttributeMap::const_iterator i = other._map.begin();
190	 i != other._map.end();
191	 ++i)
192    {
193	insert (*i->first, *i->second);
194    }
195}
196
197
198Header::~Header ()
199{
200    for (AttributeMap::iterator i = _map.begin();
201	 i != _map.end();
202	 ++i)
203    {
204	 delete i->second;
205    }
206}
207
208
209Header &
210Header::operator = (const Header &other)
211{
212    if (this != &other)
213    {
214	for (AttributeMap::iterator i = _map.begin();
215	     i != _map.end();
216	     ++i)
217	{
218	     delete i->second;
219	}
220
221	_map.erase (_map.begin(), _map.end());
222
223	for (AttributeMap::const_iterator i = other._map.begin();
224	     i != other._map.end();
225	     ++i)
226	{
227	    insert (*i->first, *i->second);
228	}
229    }
230
231    return *this;
232}
233
234
235void
236Header::insert (const char name[], const Attribute &attribute)
237{
238    if (name[0] == 0)
239	THROW (Iex::ArgExc, "Image attribute name cannot be an empty string.");
240
241    AttributeMap::iterator i = _map.find (name);
242
243    if (i == _map.end())
244    {
245	Attribute *tmp = attribute.copy();
246
247	try
248	{
249	    _map[name] = tmp;
250	}
251	catch (...)
252	{
253	    delete tmp;
254	    throw;
255	}
256    }
257    else
258    {
259	if (strcmp (i->second->typeName(), attribute.typeName()))
260	    THROW (Iex::TypeExc, "Cannot assign a value of "
261				 "type \"" << attribute.typeName() << "\" "
262				 "to image attribute \"" << name << "\" of "
263				 "type \"" << i->second->typeName() << "\".");
264
265	Attribute *tmp = attribute.copy();
266	delete i->second;
267	i->second = tmp;
268    }
269}
270
271
272Attribute &
273Header::operator [] (const char name[])
274{
275    AttributeMap::iterator i = _map.find (name);
276
277    if (i == _map.end())
278	THROW (Iex::ArgExc, "Cannot find image attribute \"" << name << "\".");
279
280    return *i->second;
281}
282
283
284const Attribute &
285Header::operator [] (const char name[]) const
286{
287    AttributeMap::const_iterator i = _map.find (name);
288
289    if (i == _map.end())
290	THROW (Iex::ArgExc, "Cannot find image attribute \"" << name << "\".");
291
292    return *i->second;
293}
294
295
296Header::Iterator
297Header::begin ()
298{
299    return _map.begin();
300}
301
302
303Header::ConstIterator
304Header::begin () const
305{
306    return _map.begin();
307}
308
309
310Header::Iterator
311Header::end ()
312{
313    return _map.end();
314}
315
316
317Header::ConstIterator
318Header::end () const
319{
320    return _map.end();
321}
322
323
324Header::Iterator
325Header::find (const char name[])
326{
327    return _map.find (name);
328}
329
330
331Header::ConstIterator
332Header::find (const char name[]) const
333{
334    return _map.find (name);
335}
336
337
338Imath::Box2i &
339Header::displayWindow ()
340{
341    return static_cast <Box2iAttribute &>
342	((*this)["displayWindow"]).value();
343}
344
345
346const Imath::Box2i &
347Header::displayWindow () const
348{
349    return static_cast <const Box2iAttribute &>
350	((*this)["displayWindow"]).value();
351}
352
353Imath::Box2i &
354Header::dataWindow ()
355{
356    return static_cast <Box2iAttribute &>
357	((*this)["dataWindow"]).value();
358}
359
360
361const Imath::Box2i &
362Header::dataWindow () const
363{
364    return static_cast <const Box2iAttribute &>
365	((*this)["dataWindow"]).value();
366}
367
368
369float &
370Header::pixelAspectRatio ()
371{
372    return static_cast <FloatAttribute &>
373	((*this)["pixelAspectRatio"]).value();
374}
375
376
377const float &
378Header::pixelAspectRatio () const
379{
380    return static_cast <const FloatAttribute &>
381	((*this)["pixelAspectRatio"]).value();
382}
383
384
385Imath::V2f &
386Header::screenWindowCenter ()
387{
388    return static_cast <V2fAttribute &>
389	((*this)["screenWindowCenter"]).value();
390}
391
392
393const Imath::V2f &
394Header::screenWindowCenter () const
395{
396    return static_cast <const V2fAttribute &>
397	((*this)["screenWindowCenter"]).value();
398}
399
400
401float &
402Header::screenWindowWidth ()
403{
404    return static_cast <FloatAttribute &>
405	((*this)["screenWindowWidth"]).value();
406}
407
408
409const float &
410Header::screenWindowWidth () const
411{
412    return static_cast <const FloatAttribute &>
413	((*this)["screenWindowWidth"]).value();
414}
415
416
417ChannelList &
418Header::channels ()
419{
420    return static_cast <ChannelListAttribute &>
421	((*this)["channels"]).value();
422}
423
424
425const ChannelList &
426Header::channels () const
427{
428    return static_cast <const ChannelListAttribute &>
429	((*this)["channels"]).value();
430}
431
432
433LineOrder &
434Header::lineOrder ()
435{
436    return static_cast <LineOrderAttribute &>
437	((*this)["lineOrder"]).value();
438}
439
440
441const LineOrder &
442Header::lineOrder () const
443{
444    return static_cast <const LineOrderAttribute &>
445	((*this)["lineOrder"]).value();
446}
447
448
449Compression &
450Header::compression ()
451{
452    return static_cast <CompressionAttribute &>
453	((*this)["compression"]).value();
454}
455
456
457const Compression &
458Header::compression () const
459{
460    return static_cast <const CompressionAttribute &>
461	((*this)["compression"]).value();
462}
463
464
465void
466Header::setTileDescription(const TileDescription& td)
467{
468    insert ("tiles", TileDescriptionAttribute (td));
469}
470
471
472bool
473Header::hasTileDescription() const
474{
475    return findTypedAttribute <TileDescriptionAttribute> ("tiles") != 0;
476}
477
478
479TileDescription &
480Header::tileDescription ()
481{
482    return typedAttribute <TileDescriptionAttribute> ("tiles").value();
483}
484
485
486const TileDescription &
487Header::tileDescription () const
488{
489    return typedAttribute <TileDescriptionAttribute> ("tiles").value();
490}
491
492void
493Header::setPreviewImage (const PreviewImage &pi)
494{
495    insert ("preview", PreviewImageAttribute (pi));
496}
497
498
499PreviewImage &
500Header::previewImage ()
501{
502    return typedAttribute <PreviewImageAttribute> ("preview").value();
503}
504
505
506const PreviewImage &
507Header::previewImage () const
508{
509    return typedAttribute <PreviewImageAttribute> ("preview").value();
510}
511
512
513bool
514Header::hasPreviewImage () const
515{
516    return findTypedAttribute <PreviewImageAttribute> ("preview") != 0;
517}
518
519
520void
521Header::sanityCheck (bool isTiled) const
522{
523    //
524    // The display window and the data window must each
525    // contain at least one pixel.  In addition, the
526    // coordinates of the window corners must be small
527    // enough to keep expressions like max-min+1 or
528    // max+min from overflowing.
529    //
530
531    const Box2i &displayWindow = this->displayWindow();
532
533    if (displayWindow.min.x > displayWindow.max.x ||
534	displayWindow.min.y > displayWindow.max.y ||
535	displayWindow.min.x <= -(INT_MAX / 2) ||
536	displayWindow.min.y <= -(INT_MAX / 2) ||
537	displayWindow.max.x >=  (INT_MAX / 2) ||
538	displayWindow.max.y >=  (INT_MAX / 2))
539    {
540	throw Iex::ArgExc ("Invalid display window in image header.");
541    }
542
543    const Box2i &dataWindow = this->dataWindow();
544
545    if (dataWindow.min.x > dataWindow.max.x ||
546	dataWindow.min.y > dataWindow.max.y ||
547	dataWindow.min.x <= -(INT_MAX / 2) ||
548	dataWindow.min.y <= -(INT_MAX / 2) ||
549	dataWindow.max.x >=  (INT_MAX / 2) ||
550	dataWindow.max.y >=  (INT_MAX / 2))
551    {
552	throw Iex::ArgExc ("Invalid data window in image header.");
553    }
554
555    if (maxImageWidth > 0 &&
556	maxImageWidth < dataWindow.max.x - dataWindow.min.x + 1)
557    {
558	THROW (Iex::ArgExc, "The width of the data window exceeds the "
559			    "maximum width of " << maxImageWidth << "pixels.");
560    }
561
562    if (maxImageHeight > 0 &&
563	maxImageHeight < dataWindow.max.y - dataWindow.min.y + 1)
564    {
565	THROW (Iex::ArgExc, "The width of the data window exceeds the "
566			    "maximum width of " << maxImageHeight << "pixels.");
567    }
568
569    //
570    // The pixel aspect ratio must be greater than 0.
571    // In applications, numbers like the the display or
572    // data window dimensions are likely to be multiplied
573    // or divided by the pixel aspect ratio; to avoid
574    // arithmetic exceptions, we limit the pixel aspect
575    // ratio to a range that is smaller than theoretically
576    // possible (real aspect ratios are likely to be close
577    // to 1.0 anyway).
578    //
579
580    float pixelAspectRatio = this->pixelAspectRatio();
581
582    const float MIN_PIXEL_ASPECT_RATIO = 1e-6;
583    const float MAX_PIXEL_ASPECT_RATIO = 1e+6;
584
585    if (pixelAspectRatio < MIN_PIXEL_ASPECT_RATIO ||
586	pixelAspectRatio > MAX_PIXEL_ASPECT_RATIO)
587    {
588	throw Iex::ArgExc ("Invalid pixel aspect ratio in image header.");
589    }
590
591    //
592    // The screen window width must not be less than 0.
593    // The size of the screen window can vary over a wide
594    // range (fish-eye lens to astronomical telescope),
595    // so we can't limit the screen window width to a
596    // small range.
597    //
598
599    float screenWindowWidth = this->screenWindowWidth();
600
601    if (screenWindowWidth < 0)
602	throw Iex::ArgExc ("Invalid screen window width in image header.");
603
604    //
605    // If the file is tiled, verify that the tile description has resonable
606    // values and check to see if the lineOrder is one of the predefined 3.
607    // If the file is not tiled, then the lineOrder can only be INCREASING_Y
608    // or DECREASING_Y.
609    //
610
611    LineOrder lineOrder = this->lineOrder();
612
613    if (isTiled)
614    {
615	if (!hasTileDescription())
616	{
617	    throw Iex::ArgExc ("Tiled image has no tile "
618			       "description attribute.");
619	}
620
621	const TileDescription &tileDesc = tileDescription();
622
623	if (tileDesc.xSize <= 0 || tileDesc.ySize <= 0)
624	    throw Iex::ArgExc ("Invalid tile size in image header.");
625
626	if (maxTileWidth > 0 &&
627	    maxTileWidth < tileDesc.xSize)
628	{
629	    THROW (Iex::ArgExc, "The width of the tiles exceeds the maximum "
630				"width of " << maxTileWidth << "pixels.");
631	}
632
633	if (maxTileHeight > 0 &&
634	    maxTileHeight < tileDesc.ySize)
635	{
636	    THROW (Iex::ArgExc, "The width of the tiles exceeds the maximum "
637				"width of " << maxTileHeight << "pixels.");
638	}
639
640	if (tileDesc.mode != ONE_LEVEL &&
641	    tileDesc.mode != MIPMAP_LEVELS &&
642	    tileDesc.mode != RIPMAP_LEVELS)
643	    throw Iex::ArgExc ("Invalid level mode in image header.");
644
645	if (tileDesc.roundingMode != ROUND_UP &&
646	    tileDesc.roundingMode != ROUND_DOWN)
647	    throw Iex::ArgExc ("Invalid level rounding mode in image header.");
648
649	if (lineOrder != INCREASING_Y &&
650	    lineOrder != DECREASING_Y &&
651	    lineOrder != RANDOM_Y)
652	    throw Iex::ArgExc ("Invalid line order in image header.");
653    }
654    else
655    {
656	if (lineOrder != INCREASING_Y &&
657	    lineOrder != DECREASING_Y)
658	    throw Iex::ArgExc ("Invalid line order in image header.");
659    }
660
661    //
662    // The compression method must be one of the predefined values.
663    //
664
665    if (!isValidCompression (this->compression()))
666	throw Iex::ArgExc ("Unknown compression type in image header.");
667
668    //
669    // Check the channel list:
670    //
671    // If the file is tiled then for each channel, the type must be one of the
672    // predefined values, and the x and y sampling must both be 1.
673    //
674    // If the file is not tiled then for each channel, the type must be one
675    // of the predefined values, the x and y coordinates of the data window's
676    // upper left corner must be divisible by the x and y subsampling factors,
677    // and the width and height of the data window must be divisible by the
678    // x and y subsampling factors.
679    //
680
681    const ChannelList &channels = this->channels();
682
683    if (isTiled)
684    {
685	for (ChannelList::ConstIterator i = channels.begin();
686	     i != channels.end();
687	     ++i)
688	{
689	    if (i.channel().type != UINT &&
690		i.channel().type != HALF &&
691		i.channel().type != FLOAT)
692	    {
693		THROW (Iex::ArgExc, "Pixel type of \"" << i.name() << "\" "
694			            "image channel is invalid.");
695	    }
696
697	    if (i.channel().xSampling != 1)
698	    {
699		THROW (Iex::ArgExc, "The x subsampling factor for the "
700				    "\"" << i.name() << "\" channel "
701				    "is not 1.");
702	    }
703
704	    if (i.channel().ySampling != 1)
705	    {
706		THROW (Iex::ArgExc, "The y subsampling factor for the "
707				    "\"" << i.name() << "\" channel "
708				    "is not 1.");
709	    }
710	}
711    }
712    else
713    {
714	for (ChannelList::ConstIterator i = channels.begin();
715	     i != channels.end();
716	     ++i)
717	{
718	    if (i.channel().type != UINT &&
719		i.channel().type != HALF &&
720		i.channel().type != FLOAT)
721	    {
722		THROW (Iex::ArgExc, "Pixel type of \"" << i.name() << "\" "
723			            "image channel is invalid.");
724	    }
725
726	    if (i.channel().xSampling < 1)
727	    {
728		THROW (Iex::ArgExc, "The x subsampling factor for the "
729				    "\"" << i.name() << "\" channel "
730				    "is invalid.");
731	    }
732
733	    if (i.channel().ySampling < 1)
734	    {
735		THROW (Iex::ArgExc, "The y subsampling factor for the "
736				    "\"" << i.name() << "\" channel "
737				    "is invalid.");
738	    }
739
740	    if (dataWindow.min.x % i.channel().xSampling)
741	    {
742		THROW (Iex::ArgExc, "The minimum x coordinate of the "
743				    "image's data window is not a multiple "
744				    "of the x subsampling factor of "
745				    "the \"" << i.name() << "\" channel.");
746	    }
747
748	    if (dataWindow.min.y % i.channel().ySampling)
749	    {
750		THROW (Iex::ArgExc, "The minimum y coordinate of the "
751				    "image's data window is not a multiple "
752				    "of the y subsampling factor of "
753				    "the \"" << i.name() << "\" channel.");
754	    }
755
756	    if ((dataWindow.max.x - dataWindow.min.x + 1) %
757		    i.channel().xSampling)
758	    {
759		THROW (Iex::ArgExc, "Number of pixels per row in the "
760				    "image's data window is not a multiple "
761				    "of the x subsampling factor of "
762				    "the \"" << i.name() << "\" channel.");
763	    }
764
765	    if ((dataWindow.max.y - dataWindow.min.y + 1) %
766		    i.channel().ySampling)
767	    {
768		THROW (Iex::ArgExc, "Number of pixels per column in the "
769				    "image's data window is not a multiple "
770				    "of the y subsampling factor of "
771				    "the \"" << i.name() << "\" channel.");
772	    }
773	}
774    }
775}
776
777
778void
779Header::setMaxImageSize (int maxWidth, int maxHeight)
780{
781    maxImageWidth = maxWidth;
782    maxImageHeight = maxHeight;
783}
784
785
786void
787Header::setMaxTileSize (int maxWidth, int maxHeight)
788{
789    maxTileWidth = maxWidth;
790    maxTileHeight = maxHeight;
791}
792
793
794Int64
795Header::writeTo (OStream &os, bool isTiled) const
796{
797    //
798    // Write a "magic number" to identify the file as an image file.
799    // Write the current file format version number.
800    //
801
802    Xdr::write <StreamIO> (os, MAGIC);
803
804    int version = isTiled ? makeTiled (EXR_VERSION) : EXR_VERSION;
805    Xdr::write <StreamIO> (os, version);
806
807    //
808    // Write all attributes.  If we have a preview image attribute,
809    // keep track of its position in the file.
810    //
811
812    Int64 previewPosition = 0;
813
814    const Attribute *preview =
815	    findTypedAttribute <PreviewImageAttribute> ("preview");
816
817    for (ConstIterator i = begin(); i != end(); ++i)
818    {
819	//
820	// Write the attribute's name and type.
821	//
822
823	Xdr::write <StreamIO> (os, i.name());
824	Xdr::write <StreamIO> (os, i.attribute().typeName());
825
826	//
827	// Write the size of the attribute value,
828	// and the value itself.
829	//
830
831	StdOSStream oss;
832	i.attribute().writeValueTo (oss, version);
833
834	std::string s = oss.str();
835	Xdr::write <StreamIO> (os, (int) s.length());
836
837	if (&i.attribute() == preview)
838	    previewPosition = os.tellp();
839
840	os.write (s.data(), s.length());
841    }
842
843    //
844    // Write zero-length attribute name to mark the end of the header.
845    //
846
847    Xdr::write <StreamIO> (os, "");
848
849    return previewPosition;
850}
851
852
853void
854Header::readFrom (IStream &is, int &version)
855{
856    //
857    // Read the magic number and the file format version number.
858    // Then check if we can read the rest of this file.
859    //
860
861    int magic;
862
863    Xdr::read <StreamIO> (is, magic);
864    Xdr::read <StreamIO> (is, version);
865
866    if (magic != MAGIC)
867    {
868	throw Iex::InputExc ("File is not an image file.");
869    }
870
871    if (getVersion (version) != EXR_VERSION)
872    {
873	THROW (Iex::InputExc, "Cannot read "
874			      "version " << getVersion (version) << " "
875			      "image files.  Current file format version "
876			      "is " << EXR_VERSION << ".");
877    }
878
879    if (!supportsFlags (getFlags (version)))
880    {
881	THROW (Iex::InputExc, "The file format version number's flag field "
882			      "contains unrecognized flags.");
883    }
884
885    //
886    // Read all attributes.
887    //
888
889    while (true)
890    {
891	//
892	// Read the name of the attribute.
893	// A zero-length attribute name indicates the end of the header.
894	//
895
896	char name[100];
897	Xdr::read <StreamIO> (is, sizeof (name), name);
898
899	if (name[0] == 0)
900	    break;
901
902	//
903	// Read the attribute type and the size of the attribute value.
904	//
905
906	char typeName[100];
907	int size;
908
909	Xdr::read <StreamIO> (is, sizeof (typeName), typeName);
910	Xdr::read <StreamIO> (is, size);
911
912	AttributeMap::iterator i = _map.find (name);
913
914	if (i != _map.end())
915	{
916	    //
917	    // The attribute already exists (for example,
918	    // because it is a predefined attribute).
919	    // Read the attribute's new value from the file.
920	    //
921
922	    if (strncmp (i->second->typeName(), typeName, sizeof (typeName)))
923		THROW (Iex::InputExc, "Unexpected type for image attribute "
924				      "\"" << name << "\".");
925
926	    i->second->readValueFrom (is, size, version);
927	}
928	else
929	{
930	    //
931	    // The new attribute does not exist yet.
932	    // If the attribute type is of a known type,
933	    // read the attribute value.  If the attribute
934	    // is of an unknown type, read its value and
935	    // store it as an OpaqueAttribute.
936	    //
937
938	    Attribute *attr;
939
940	    if (Attribute::knownType (typeName))
941		attr = Attribute::newAttribute (typeName);
942	    else
943		attr = new OpaqueAttribute (typeName);
944
945	    try
946	    {
947		attr->readValueFrom (is, size, version);
948		_map[name] = attr;
949	    }
950	    catch (...)
951	    {
952		delete attr;
953		throw;
954	    }
955	}
956    }
957}
958
959
960void
961staticInitialize ()
962{
963    static Mutex criticalSection;
964    Lock lock (criticalSection);
965
966    static bool initialized = false;
967
968    if (!initialized)
969    {
970	//
971	// One-time initialization -- register
972	// some predefined attribute types.
973	//
974
975	Box2fAttribute::registerAttributeType();
976	Box2iAttribute::registerAttributeType();
977	ChannelListAttribute::registerAttributeType();
978	CompressionAttribute::registerAttributeType();
979	ChromaticitiesAttribute::registerAttributeType();
980	DoubleAttribute::registerAttributeType();
981	EnvmapAttribute::registerAttributeType();
982	FloatAttribute::registerAttributeType();
983	IntAttribute::registerAttributeType();
984	KeyCodeAttribute::registerAttributeType();
985	LineOrderAttribute::registerAttributeType();
986	M33fAttribute::registerAttributeType();
987	M44fAttribute::registerAttributeType();
988	PreviewImageAttribute::registerAttributeType();
989	RationalAttribute::registerAttributeType();
990	StringAttribute::registerAttributeType();
991	TileDescriptionAttribute::registerAttributeType();
992	TimeCodeAttribute::registerAttributeType();
993	V2fAttribute::registerAttributeType();
994	V2iAttribute::registerAttributeType();
995	V3fAttribute::registerAttributeType();
996	V3iAttribute::registerAttributeType();
997
998	initialized = true;
999    }
1000}
1001
1002
1003} // namespace Imf
1004