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 <ImfDeepImageStateAttribute.h>
53 #include <ImfDoubleAttribute.h>
54 #include <ImfDwaCompressor.h>
55 #include <ImfEnvmapAttribute.h>
56 #include <ImfFloatAttribute.h>
57 #include <ImfFloatVectorAttribute.h>
58 #include <ImfIntAttribute.h>
59 #include <ImfKeyCodeAttribute.h>
60 #include <ImfLineOrderAttribute.h>
61 #include <ImfMatrixAttribute.h>
62 #include <ImfOpaqueAttribute.h>
63 #include <ImfPreviewImageAttribute.h>
64 #include <ImfRationalAttribute.h>
65 #include <ImfStringAttribute.h>
66 #include <ImfStringVectorAttribute.h>
67 #include <ImfTileDescriptionAttribute.h>
68 #include <ImfTimeCodeAttribute.h>
69 #include <ImfVecAttribute.h>
70 #include <ImfPartType.h>
71 #include "IlmThreadMutex.h"
72 #include "Iex.h"
73 #include <sstream>
74 #include <stdlib.h>
75 #include <time.h>
76 
77 #include "ImfNamespace.h"
78 
79 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
80 
81 using namespace std;
82 using IMATH_NAMESPACE::Box2i;
83 using IMATH_NAMESPACE::V2i;
84 using IMATH_NAMESPACE::V2f;
85 using ILMTHREAD_NAMESPACE::Mutex;
86 using ILMTHREAD_NAMESPACE::Lock;
87 
88 
89 namespace {
90 
91 int maxImageWidth = 0;
92 int maxImageHeight = 0;
93 int maxTileWidth = 0;
94 int maxTileHeight = 0;
95 
96 
97 void
initialize(Header & header,const Box2i & displayWindow,const Box2i & dataWindow,float pixelAspectRatio,const V2f & screenWindowCenter,float screenWindowWidth,LineOrder lineOrder,Compression compression)98 initialize (Header &header,
99 	    const Box2i &displayWindow,
100 	    const Box2i &dataWindow,
101 	    float pixelAspectRatio,
102 	    const V2f &screenWindowCenter,
103 	    float screenWindowWidth,
104 	    LineOrder lineOrder,
105 	    Compression compression)
106 {
107     header.insert ("displayWindow", Box2iAttribute (displayWindow));
108     header.insert ("dataWindow", Box2iAttribute (dataWindow));
109     header.insert ("pixelAspectRatio", FloatAttribute (pixelAspectRatio));
110     header.insert ("screenWindowCenter", V2fAttribute (screenWindowCenter));
111     header.insert ("screenWindowWidth", FloatAttribute (screenWindowWidth));
112     header.insert ("lineOrder", LineOrderAttribute (lineOrder));
113     header.insert ("compression", CompressionAttribute (compression));
114     header.insert ("channels", ChannelListAttribute ());
115 }
116 
117 template <size_t N>
checkIsNullTerminated(const char (& str)[N],const char * what)118 void checkIsNullTerminated (const char (&str)[N], const char *what)
119 {
120 	for (size_t i = 0; i < N; ++i) {
121 		if (str[i] == '\0')
122 			return;
123 	}
124 	std::stringstream s;
125 	s << "Invalid " << what << ": it is more than " << (N - 1)
126 		<< " characters long.";
127 	throw IEX_NAMESPACE::InputExc(s);
128 }
129 
130 } // namespace
131 
132 
Header(int width,int height,float pixelAspectRatio,const V2f & screenWindowCenter,float screenWindowWidth,LineOrder lineOrder,Compression compression)133 Header::Header (int width,
134 		int height,
135 		float pixelAspectRatio,
136 		const V2f &screenWindowCenter,
137 		float screenWindowWidth,
138 		LineOrder lineOrder,
139 		Compression compression)
140 :
141     _map()
142 {
143     staticInitialize();
144 
145     Box2i displayWindow (V2i (0, 0), V2i (width - 1, height - 1));
146 
147     initialize (*this,
148 		displayWindow,
149 		displayWindow,
150 		pixelAspectRatio,
151 		screenWindowCenter,
152 		screenWindowWidth,
153 		lineOrder,
154 		compression);
155 }
156 
157 
Header(int width,int height,const Box2i & dataWindow,float pixelAspectRatio,const V2f & screenWindowCenter,float screenWindowWidth,LineOrder lineOrder,Compression compression)158 Header::Header (int width,
159 		int height,
160 		const Box2i &dataWindow,
161 		float pixelAspectRatio,
162 		const V2f &screenWindowCenter,
163 		float screenWindowWidth,
164 		LineOrder lineOrder,
165 		Compression compression)
166 :
167     _map()
168 {
169     staticInitialize();
170 
171     Box2i displayWindow (V2i (0, 0), V2i (width - 1, height - 1));
172 
173     initialize (*this,
174 		displayWindow,
175 		dataWindow,
176 		pixelAspectRatio,
177 		screenWindowCenter,
178 		screenWindowWidth,
179 		lineOrder,
180 		compression);
181 }
182 
183 
Header(const Box2i & displayWindow,const Box2i & dataWindow,float pixelAspectRatio,const V2f & screenWindowCenter,float screenWindowWidth,LineOrder lineOrder,Compression compression)184 Header::Header (const Box2i &displayWindow,
185 		const Box2i &dataWindow,
186 		float pixelAspectRatio,
187 		const V2f &screenWindowCenter,
188 		float screenWindowWidth,
189 		LineOrder lineOrder,
190 		Compression compression)
191 :
192     _map()
193 {
194     staticInitialize();
195 
196     initialize (*this,
197 		displayWindow,
198 		dataWindow,
199 		pixelAspectRatio,
200 		screenWindowCenter,
201 		screenWindowWidth,
202 		lineOrder,
203 		compression);
204 }
205 
206 
Header(const Header & other)207 Header::Header (const Header &other): _map()
208 {
209     for (AttributeMap::const_iterator i = other._map.begin();
210 	 i != other._map.end();
211 	 ++i)
212     {
213 	insert (*i->first, *i->second);
214     }
215 }
216 
217 
~Header()218 Header::~Header ()
219 {
220     for (AttributeMap::iterator i = _map.begin();
221 	 i != _map.end();
222 	 ++i)
223     {
224 	 delete i->second;
225     }
226 }
227 
228 
229 Header &
operator =(const Header & other)230 Header::operator = (const Header &other)
231 {
232     if (this != &other)
233     {
234 	for (AttributeMap::iterator i = _map.begin();
235 	     i != _map.end();
236 	     ++i)
237 	{
238 	     delete i->second;
239 	}
240 
241 	_map.erase (_map.begin(), _map.end());
242 
243 	for (AttributeMap::const_iterator i = other._map.begin();
244 	     i != other._map.end();
245 	     ++i)
246 	{
247 	    insert (*i->first, *i->second);
248 	}
249     }
250 
251     return *this;
252 }
253 
254 
255 void
erase(const char name[])256 Header::erase (const char name[])
257 {
258     if (name[0] == 0)
259         THROW (IEX_NAMESPACE::ArgExc, "Image attribute name cannot be an empty string.");
260 
261 
262     AttributeMap::iterator i = _map.find (name);
263     if (i != _map.end())
264         _map.erase (i);
265 
266 }
267 
268 
269 void
erase(const string & name)270 Header::erase (const string &name)
271 {
272     erase (name.c_str());
273 }
274 
275 
276 void
insert(const char name[],const Attribute & attribute)277 Header::insert (const char name[], const Attribute &attribute)
278 {
279     if (name[0] == 0)
280 	THROW (IEX_NAMESPACE::ArgExc, "Image attribute name cannot be an empty string.");
281 
282     AttributeMap::iterator i = _map.find (name);
283 
284     if (i == _map.end())
285     {
286 	Attribute *tmp = attribute.copy();
287 
288 	try
289 	{
290 	    _map[name] = tmp;
291 	}
292 	catch (...)
293 	{
294 	    delete tmp;
295 	    throw;
296 	}
297     }
298     else
299     {
300 	if (strcmp (i->second->typeName(), attribute.typeName()))
301 	    THROW (IEX_NAMESPACE::TypeExc, "Cannot assign a value of "
302 				 "type \"" << attribute.typeName() << "\" "
303 				 "to image attribute \"" << name << "\" of "
304 				 "type \"" << i->second->typeName() << "\".");
305 
306 	Attribute *tmp = attribute.copy();
307 	delete i->second;
308 	i->second = tmp;
309     }
310 }
311 
312 
313 void
insert(const string & name,const Attribute & attribute)314 Header::insert (const string &name, const Attribute &attribute)
315 {
316     insert (name.c_str(), attribute);
317 }
318 
319 
320 Attribute &
operator [](const char name[])321 Header::operator [] (const char name[])
322 {
323     AttributeMap::iterator i = _map.find (name);
324 
325     if (i == _map.end())
326 	THROW (IEX_NAMESPACE::ArgExc, "Cannot find image attribute \"" << name << "\".");
327 
328     return *i->second;
329 }
330 
331 
332 const Attribute &
operator [](const char name[]) const333 Header::operator [] (const char name[]) const
334 {
335     AttributeMap::const_iterator i = _map.find (name);
336 
337     if (i == _map.end())
338 	THROW (IEX_NAMESPACE::ArgExc, "Cannot find image attribute \"" << name << "\".");
339 
340     return *i->second;
341 }
342 
343 
344 Attribute &
operator [](const string & name)345 Header::operator [] (const string &name)
346 {
347     return this->operator[] (name.c_str());
348 }
349 
350 
351 const Attribute &
operator [](const string & name) const352 Header::operator [] (const string &name) const
353 {
354     return this->operator[] (name.c_str());
355 }
356 
357 
358 Header::Iterator
begin()359 Header::begin ()
360 {
361     return _map.begin();
362 }
363 
364 
365 Header::ConstIterator
begin() const366 Header::begin () const
367 {
368     return _map.begin();
369 }
370 
371 
372 Header::Iterator
end()373 Header::end ()
374 {
375     return _map.end();
376 }
377 
378 
379 Header::ConstIterator
end() const380 Header::end () const
381 {
382     return _map.end();
383 }
384 
385 
386 Header::Iterator
find(const char name[])387 Header::find (const char name[])
388 {
389     return _map.find (name);
390 }
391 
392 
393 Header::ConstIterator
find(const char name[]) const394 Header::find (const char name[]) const
395 {
396     return _map.find (name);
397 }
398 
399 
400 Header::Iterator
find(const string & name)401 Header::find (const string &name)
402 {
403     return find (name.c_str());
404 }
405 
406 
407 Header::ConstIterator
find(const string & name) const408 Header::find (const string &name) const
409 {
410     return find (name.c_str());
411 }
412 
413 
414 IMATH_NAMESPACE::Box2i &
displayWindow()415 Header::displayWindow ()
416 {
417     return static_cast <Box2iAttribute &>
418 	((*this)["displayWindow"]).value();
419 }
420 
421 
422 const IMATH_NAMESPACE::Box2i &
displayWindow() const423 Header::displayWindow () const
424 {
425     return static_cast <const Box2iAttribute &>
426 	((*this)["displayWindow"]).value();
427 }
428 
429 
430 IMATH_NAMESPACE::Box2i &
dataWindow()431 Header::dataWindow ()
432 {
433     return static_cast <Box2iAttribute &>
434 	((*this)["dataWindow"]).value();
435 }
436 
437 
438 const IMATH_NAMESPACE::Box2i &
dataWindow() const439 Header::dataWindow () const
440 {
441     return static_cast <const Box2iAttribute &>
442 	((*this)["dataWindow"]).value();
443 }
444 
445 
446 float &
pixelAspectRatio()447 Header::pixelAspectRatio ()
448 {
449     return static_cast <FloatAttribute &>
450 	((*this)["pixelAspectRatio"]).value();
451 }
452 
453 
454 const float &
pixelAspectRatio() const455 Header::pixelAspectRatio () const
456 {
457     return static_cast <const FloatAttribute &>
458 	((*this)["pixelAspectRatio"]).value();
459 }
460 
461 
462 IMATH_NAMESPACE::V2f &
screenWindowCenter()463 Header::screenWindowCenter ()
464 {
465     return static_cast <V2fAttribute &>
466 	((*this)["screenWindowCenter"]).value();
467 }
468 
469 
470 const IMATH_NAMESPACE::V2f &
screenWindowCenter() const471 Header::screenWindowCenter () const
472 {
473     return static_cast <const V2fAttribute &>
474 	((*this)["screenWindowCenter"]).value();
475 }
476 
477 
478 float &
screenWindowWidth()479 Header::screenWindowWidth ()
480 {
481     return static_cast <FloatAttribute &>
482 	((*this)["screenWindowWidth"]).value();
483 }
484 
485 
486 const float &
screenWindowWidth() const487 Header::screenWindowWidth () const
488 {
489     return static_cast <const FloatAttribute &>
490 	((*this)["screenWindowWidth"]).value();
491 }
492 
493 
494 ChannelList &
channels()495 Header::channels ()
496 {
497     return static_cast <ChannelListAttribute &>
498 	((*this)["channels"]).value();
499 }
500 
501 
502 const ChannelList &
channels() const503 Header::channels () const
504 {
505     return static_cast <const ChannelListAttribute &>
506 	((*this)["channels"]).value();
507 }
508 
509 
510 LineOrder &
lineOrder()511 Header::lineOrder ()
512 {
513     return static_cast <LineOrderAttribute &>
514 	((*this)["lineOrder"]).value();
515 }
516 
517 
518 const LineOrder &
lineOrder() const519 Header::lineOrder () const
520 {
521     return static_cast <const LineOrderAttribute &>
522 	((*this)["lineOrder"]).value();
523 }
524 
525 
526 Compression &
compression()527 Header::compression ()
528 {
529     return static_cast <CompressionAttribute &>
530 	((*this)["compression"]).value();
531 }
532 
533 
534 const Compression &
compression() const535 Header::compression () const
536 {
537     return static_cast <const CompressionAttribute &>
538 	((*this)["compression"]).value();
539 }
540 
541 
542 void
setName(const string & name)543 Header::setName(const string& name)
544 {
545     insert ("name", StringAttribute (name));
546 }
547 
548 
549 bool
hasName() const550 Header::hasName() const
551 {
552     return findTypedAttribute <StringAttribute> ("name") != 0;
553 }
554 
555 
556 string &
name()557 Header::name()
558 {
559     return typedAttribute <StringAttribute> ("name").value();
560 }
561 
562 
563 const string &
name() const564 Header::name() const
565 {
566     return typedAttribute <StringAttribute> ("name").value();
567 }
568 
569 
570 void
setType(const string & type)571 Header::setType(const string& type)
572 {
573     if (isSupportedType(type) == false)
574     {
575         throw IEX_NAMESPACE::ArgExc (type + "is not a supported image type." +
576                            "The following are supported: " +
577                            SCANLINEIMAGE + ", " +
578                            TILEDIMAGE + ", " +
579                            DEEPSCANLINE + " or " +
580                            DEEPTILE + ".");
581     }
582 
583     insert ("type", StringAttribute (type));
584 
585     // (TODO) Should we do it here?
586     if (isDeepData(type) && hasVersion() == false)
587     {
588         setVersion(1);
589     }
590 }
591 
592 
593 bool
hasType() const594 Header::hasType() const
595 {
596     return findTypedAttribute <StringAttribute> ("type") != 0;
597 }
598 
599 
600 string &
type()601 Header::type()
602 {
603     return typedAttribute <StringAttribute> ("type").value();
604 }
605 
606 
607 const string &
type() const608 Header::type() const
609 {
610     return typedAttribute <StringAttribute> ("type").value();
611 }
612 
613 
614 void
setView(const string & view)615 Header::setView(const string& view)
616 {
617     insert ("view", StringAttribute (view));
618 }
619 
620 
621 bool
hasView() const622 Header::hasView() const
623 {
624     return findTypedAttribute <StringAttribute> ("view") != 0;
625 }
626 
627 
628 string &
view()629 Header::view()
630 {
631     return typedAttribute <StringAttribute> ("view").value();
632 }
633 
634 
635 const string &
view() const636 Header::view() const
637 {
638     return typedAttribute <StringAttribute> ("view").value();
639 }
640 
641 
642 void
setVersion(const int version)643 Header::setVersion(const int version)
644 {
645     if (version != 1)
646     {
647         throw IEX_NAMESPACE::ArgExc ("We can only process version 1");
648     }
649 
650     insert ("version", IntAttribute (version));
651 }
652 
653 
654 bool
hasVersion() const655 Header::hasVersion() const
656 {
657     return findTypedAttribute <IntAttribute> ("version") != 0;
658 }
659 
660 
661 int &
version()662 Header::version()
663 {
664     return typedAttribute <IntAttribute> ("version").value();
665 }
666 
667 
668 const int &
version() const669 Header::version() const
670 {
671     return typedAttribute <IntAttribute> ("version").value();
672 }
673 
674 void
setChunkCount(int chunks)675 Header::setChunkCount(int chunks)
676 {
677     insert("chunkCount",IntAttribute(chunks));
678 }
679 
680 bool
hasChunkCount() const681 Header::hasChunkCount() const
682 {
683    return findTypedAttribute<IntAttribute>("chunkCount") != 0;
684 }
685 
686 int&
chunkCount()687 Header::chunkCount()
688 {
689     return typedAttribute <IntAttribute> ("chunkCount").value();
690 }
691 
692 const int&
chunkCount() const693 Header::chunkCount() const
694 {
695     return typedAttribute <IntAttribute> ("chunkCount").value();
696 }
697 
698 void
setTileDescription(const TileDescription & td)699 Header::setTileDescription(const TileDescription& td)
700 {
701     insert ("tiles", TileDescriptionAttribute (td));
702 }
703 
704 
705 bool
hasTileDescription() const706 Header::hasTileDescription() const
707 {
708     return findTypedAttribute <TileDescriptionAttribute> ("tiles") != 0;
709 }
710 
711 
712 TileDescription &
tileDescription()713 Header::tileDescription ()
714 {
715     return typedAttribute <TileDescriptionAttribute> ("tiles").value();
716 }
717 
718 
719 const TileDescription &
tileDescription() const720 Header::tileDescription () const
721 {
722     return typedAttribute <TileDescriptionAttribute> ("tiles").value();
723 }
724 
725 void
setPreviewImage(const PreviewImage & pi)726 Header::setPreviewImage (const PreviewImage &pi)
727 {
728     insert ("preview", PreviewImageAttribute (pi));
729 }
730 
731 
732 PreviewImage &
previewImage()733 Header::previewImage ()
734 {
735     return typedAttribute <PreviewImageAttribute> ("preview").value();
736 }
737 
738 
739 const PreviewImage &
previewImage() const740 Header::previewImage () const
741 {
742     return typedAttribute <PreviewImageAttribute> ("preview").value();
743 }
744 
745 
746 bool
hasPreviewImage() const747 Header::hasPreviewImage () const
748 {
749     return findTypedAttribute <PreviewImageAttribute> ("preview") != 0;
750 }
751 
752 
753 void
sanityCheck(bool isTiled,bool isMultipartFile) const754 Header::sanityCheck (bool isTiled, bool isMultipartFile) const
755 {
756     //
757     // The display window and the data window must each
758     // contain at least one pixel.  In addition, the
759     // coordinates of the window corners must be small
760     // enough to keep expressions like max-min+1 or
761     // max+min from overflowing.
762     //
763 
764     const Box2i &displayWindow = this->displayWindow();
765 
766     if (displayWindow.min.x > displayWindow.max.x ||
767 	displayWindow.min.y > displayWindow.max.y ||
768 	displayWindow.min.x <= -(INT_MAX / 2) ||
769 	displayWindow.min.y <= -(INT_MAX / 2) ||
770 	displayWindow.max.x >=  (INT_MAX / 2) ||
771 	displayWindow.max.y >=  (INT_MAX / 2))
772     {
773 	throw IEX_NAMESPACE::ArgExc ("Invalid display window in image header.");
774     }
775 
776     const Box2i &dataWindow = this->dataWindow();
777 
778     if (dataWindow.min.x > dataWindow.max.x ||
779 	dataWindow.min.y > dataWindow.max.y ||
780 	dataWindow.min.x <= -(INT_MAX / 2) ||
781 	dataWindow.min.y <= -(INT_MAX / 2) ||
782 	dataWindow.max.x >=  (INT_MAX / 2) ||
783 	dataWindow.max.y >=  (INT_MAX / 2))
784     {
785 	throw IEX_NAMESPACE::ArgExc ("Invalid data window in image header.");
786     }
787 
788     if (maxImageWidth > 0 &&
789         maxImageWidth < (dataWindow.max.x - dataWindow.min.x + 1))
790     {
791 	THROW (IEX_NAMESPACE::ArgExc, "The width of the data window exceeds the "
792 			    "maximum width of " << maxImageWidth << "pixels.");
793     }
794 
795     if (maxImageHeight > 0 &&
796 	maxImageHeight < dataWindow.max.y - dataWindow.min.y + 1)
797     {
798 	THROW (IEX_NAMESPACE::ArgExc, "The width of the data window exceeds the "
799 			    "maximum width of " << maxImageHeight << "pixels.");
800     }
801 
802    // chunk table must be smaller than the maximum image area
803    // (only reachable for unknown types or damaged files: will have thrown earlier
804    //  for regular image types)
805    if( maxImageHeight>0 && maxImageWidth>0 &&
806        hasChunkCount() && chunkCount()>Int64(maxImageWidth)*Int64(maxImageHeight))
807    {
808        THROW (IEX_NAMESPACE::ArgExc, "chunkCount exceeds maximum area of "
809        << Int64(maxImageWidth)*Int64(maxImageHeight) << " pixels." );
810 
811    }
812 
813 
814     //
815     // The pixel aspect ratio must be greater than 0.
816     // In applications, numbers like the the display or
817     // data window dimensions are likely to be multiplied
818     // or divided by the pixel aspect ratio; to avoid
819     // arithmetic exceptions, we limit the pixel aspect
820     // ratio to a range that is smaller than theoretically
821     // possible (real aspect ratios are likely to be close
822     // to 1.0 anyway).
823     //
824 
825     float pixelAspectRatio = this->pixelAspectRatio();
826 
827     const float MIN_PIXEL_ASPECT_RATIO = 1e-6f;
828     const float MAX_PIXEL_ASPECT_RATIO = 1e+6f;
829 
830     if (pixelAspectRatio < MIN_PIXEL_ASPECT_RATIO ||
831 	pixelAspectRatio > MAX_PIXEL_ASPECT_RATIO)
832     {
833 	throw IEX_NAMESPACE::ArgExc ("Invalid pixel aspect ratio in image header.");
834     }
835 
836     //
837     // The screen window width must not be less than 0.
838     // The size of the screen window can vary over a wide
839     // range (fish-eye lens to astronomical telescope),
840     // so we can't limit the screen window width to a
841     // small range.
842     //
843 
844     float screenWindowWidth = this->screenWindowWidth();
845 
846     if (screenWindowWidth < 0)
847 	throw IEX_NAMESPACE::ArgExc ("Invalid screen window width in image header.");
848 
849     //
850     // If the file has multiple parts, verify that each header has attribute
851     // name and type.
852     // (TODO) We may want to check more stuff here.
853     //
854 
855     if (isMultipartFile)
856     {
857         if (!hasName())
858         {
859             throw IEX_NAMESPACE::ArgExc ("Headers in a multipart file should"
860                                " have name attribute.");
861         }
862 
863         if (!hasType())
864         {
865             throw IEX_NAMESPACE::ArgExc ("Headers in a multipart file should"
866                                " have type attribute.");
867         }
868 
869     }
870 
871     const std::string & part_type=hasType() ? type() : "";
872 
873     if(part_type!="" && !isSupportedType(part_type))
874     {
875         //
876         // skip remaining sanity checks with unsupported types - they may not hold
877         //
878         return;
879     }
880 
881 
882     //
883     // If the file is tiled, verify that the tile description has reasonable
884     // values and check to see if the lineOrder is one of the predefined 3.
885     // If the file is not tiled, then the lineOrder can only be INCREASING_Y
886     // or DECREASING_Y.
887     //
888 
889     LineOrder lineOrder = this->lineOrder();
890 
891     if (isTiled)
892     {
893 	if (!hasTileDescription())
894 	{
895 	    throw IEX_NAMESPACE::ArgExc ("Tiled image has no tile "
896 			       "description attribute.");
897 	}
898 
899 	const TileDescription &tileDesc = tileDescription();
900 
901 	if (tileDesc.xSize <= 0 || tileDesc.ySize <= 0)
902 	    throw IEX_NAMESPACE::ArgExc ("Invalid tile size in image header.");
903 
904 	if (maxTileWidth > 0 &&
905 	    maxTileWidth < int(tileDesc.xSize))
906 	{
907 	    THROW (IEX_NAMESPACE::ArgExc, "The width of the tiles exceeds the maximum "
908 				"width of " << maxTileWidth << "pixels.");
909 	}
910 
911 	if (maxTileHeight > 0 &&
912 	    maxTileHeight < int(tileDesc.ySize))
913 	{
914 	    THROW (IEX_NAMESPACE::ArgExc, "The width of the tiles exceeds the maximum "
915 				"width of " << maxTileHeight << "pixels.");
916 	}
917 
918 	if (tileDesc.mode != ONE_LEVEL &&
919 	    tileDesc.mode != MIPMAP_LEVELS &&
920 	    tileDesc.mode != RIPMAP_LEVELS)
921 	    throw IEX_NAMESPACE::ArgExc ("Invalid level mode in image header.");
922 
923 	if (tileDesc.roundingMode != ROUND_UP &&
924 	    tileDesc.roundingMode != ROUND_DOWN)
925 	    throw IEX_NAMESPACE::ArgExc ("Invalid level rounding mode in image header.");
926 
927 	if (lineOrder != INCREASING_Y &&
928 	    lineOrder != DECREASING_Y &&
929 	    lineOrder != RANDOM_Y)
930 	    throw IEX_NAMESPACE::ArgExc ("Invalid line order in image header.");
931     }
932     else
933     {
934         if (lineOrder != INCREASING_Y &&
935             lineOrder != DECREASING_Y)
936             throw IEX_NAMESPACE::ArgExc ("Invalid line order in image header.");
937 
938 
939     }
940 
941     //
942     // The compression method must be one of the predefined values.
943     //
944 
945     if (!isValidCompression (this->compression()))
946   	throw IEX_NAMESPACE::ArgExc ("Unknown compression type in image header.");
947 
948     if(isDeepData(part_type))
949     {
950         if (!isValidDeepCompression (this->compression()))
951             throw IEX_NAMESPACE::ArgExc ("Compression type in header not valid for deep data");
952     }
953 
954     //
955     // Check the channel list:
956     //
957     // If the file is tiled then for each channel, the type must be one of the
958     // predefined values, and the x and y sampling must both be 1.
959     //
960     // If the file is not tiled then for each channel, the type must be one
961     // of the predefined values, the x and y coordinates of the data window's
962     // upper left corner must be divisible by the x and y subsampling factors,
963     // and the width and height of the data window must be divisible by the
964     // x and y subsampling factors.
965     //
966 
967     const ChannelList &channels = this->channels();
968 
969     if (isTiled)
970     {
971 	for (ChannelList::ConstIterator i = channels.begin();
972 	     i != channels.end();
973 	     ++i)
974 	{
975 	    if (i.channel().type != OPENEXR_IMF_INTERNAL_NAMESPACE::UINT &&
976 		    i.channel().type != OPENEXR_IMF_INTERNAL_NAMESPACE::HALF &&
977 		    i.channel().type != OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT)
978 	    {
979 		THROW (IEX_NAMESPACE::ArgExc, "Pixel type of \"" << i.name() << "\" "
980 			            "image channel is invalid.");
981 	    }
982 
983 	    if (i.channel().xSampling != 1)
984 	    {
985 		THROW (IEX_NAMESPACE::ArgExc, "The x subsampling factor for the "
986 				    "\"" << i.name() << "\" channel "
987 				    "is not 1.");
988 	    }
989 
990 	    if (i.channel().ySampling != 1)
991 	    {
992 		THROW (IEX_NAMESPACE::ArgExc, "The y subsampling factor for the "
993 				    "\"" << i.name() << "\" channel "
994 				    "is not 1.");
995 	    }
996 	}
997     }
998     else
999     {
1000 	for (ChannelList::ConstIterator i = channels.begin();
1001 	     i != channels.end();
1002 	     ++i)
1003 	{
1004 	    if (i.channel().type != OPENEXR_IMF_INTERNAL_NAMESPACE::UINT &&
1005 		    i.channel().type != OPENEXR_IMF_INTERNAL_NAMESPACE::HALF &&
1006 		    i.channel().type != OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT)
1007 	    {
1008 		THROW (IEX_NAMESPACE::ArgExc, "Pixel type of \"" << i.name() << "\" "
1009 			            "image channel is invalid.");
1010 	    }
1011 
1012 	    if (i.channel().xSampling < 1)
1013 	    {
1014 		THROW (IEX_NAMESPACE::ArgExc, "The x subsampling factor for the "
1015 				    "\"" << i.name() << "\" channel "
1016 				    "is invalid.");
1017 	    }
1018 
1019 	    if (i.channel().ySampling < 1)
1020 	    {
1021 		THROW (IEX_NAMESPACE::ArgExc, "The y subsampling factor for the "
1022 				    "\"" << i.name() << "\" channel "
1023 				    "is invalid.");
1024 	    }
1025 
1026 	    if (dataWindow.min.x % i.channel().xSampling)
1027 	    {
1028 		THROW (IEX_NAMESPACE::ArgExc, "The minimum x coordinate of the "
1029 				    "image's data window is not a multiple "
1030 				    "of the x subsampling factor of "
1031 				    "the \"" << i.name() << "\" channel.");
1032 	    }
1033 
1034 	    if (dataWindow.min.y % i.channel().ySampling)
1035 	    {
1036 		THROW (IEX_NAMESPACE::ArgExc, "The minimum y coordinate of the "
1037 				    "image's data window is not a multiple "
1038 				    "of the y subsampling factor of "
1039 				    "the \"" << i.name() << "\" channel.");
1040 	    }
1041 
1042 	    if ((dataWindow.max.x - dataWindow.min.x + 1) %
1043 		    i.channel().xSampling)
1044 	    {
1045 		THROW (IEX_NAMESPACE::ArgExc, "Number of pixels per row in the "
1046 				    "image's data window is not a multiple "
1047 				    "of the x subsampling factor of "
1048 				    "the \"" << i.name() << "\" channel.");
1049 	    }
1050 
1051 	    if ((dataWindow.max.y - dataWindow.min.y + 1) %
1052 		    i.channel().ySampling)
1053 	    {
1054 		THROW (IEX_NAMESPACE::ArgExc, "Number of pixels per column in the "
1055 				    "image's data window is not a multiple "
1056 				    "of the y subsampling factor of "
1057 				    "the \"" << i.name() << "\" channel.");
1058 	    }
1059 	}
1060     }
1061 }
1062 
1063 
1064 void
setMaxImageSize(int maxWidth,int maxHeight)1065 Header::setMaxImageSize (int maxWidth, int maxHeight)
1066 {
1067     maxImageWidth = maxWidth;
1068     maxImageHeight = maxHeight;
1069 }
1070 
1071 
1072 void
setMaxTileSize(int maxWidth,int maxHeight)1073 Header::setMaxTileSize (int maxWidth, int maxHeight)
1074 {
1075     maxTileWidth = maxWidth;
1076     maxTileHeight = maxHeight;
1077 }
1078 
1079 
1080 bool
readsNothing()1081 Header::readsNothing()
1082 {
1083     return _readsNothing;
1084 }
1085 
1086 
1087 Int64
writeTo(OPENEXR_IMF_INTERNAL_NAMESPACE::OStream & os,bool isTiled) const1088 Header::writeTo (OPENEXR_IMF_INTERNAL_NAMESPACE::OStream &os, bool isTiled) const
1089 {
1090     //
1091     // Write a "magic number" to identify the file as an image file.
1092     // Write the current file format version number.
1093     //
1094 
1095     int version = EXR_VERSION;
1096 
1097     //
1098     // Write all attributes.  If we have a preview image attribute,
1099     // keep track of its position in the file.
1100     //
1101 
1102     Int64 previewPosition = 0;
1103 
1104     const Attribute *preview =
1105 	    findTypedAttribute <PreviewImageAttribute> ("preview");
1106 
1107     for (ConstIterator i = begin(); i != end(); ++i)
1108     {
1109 	//
1110 	// Write the attribute's name and type.
1111 	//
1112 
1113 	OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (os, i.name());
1114 	OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (os, i.attribute().typeName());
1115 
1116 	//
1117 	// Write the size of the attribute value,
1118 	// and the value itself.
1119 	//
1120 
1121 	StdOSStream oss;
1122 	i.attribute().writeValueTo (oss, version);
1123 
1124 	std::string s = oss.str();
1125 	OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (os, (int) s.length());
1126 
1127 	if (&i.attribute() == preview)
1128 	    previewPosition = os.tellp();
1129 
1130 	os.write (s.data(), int(s.length()));
1131     }
1132 
1133     //
1134     // Write zero-length attribute name to mark the end of the header.
1135     //
1136 
1137     OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (os, "");
1138 
1139     return previewPosition;
1140 }
1141 
1142 
1143 void
readFrom(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream & is,int & version)1144 Header::readFrom (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is, int &version)
1145 {
1146     //
1147     // Read all attributes.
1148     //
1149 
1150     int attrCount = 0;
1151 
1152     while (true)
1153     {
1154 	//
1155 	// Read the name of the attribute.
1156 	// A zero-length attribute name indicates the end of the header.
1157 	//
1158 
1159 	char name[Name::SIZE];
1160 	OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, Name::MAX_LENGTH, name);
1161 
1162 	if (name[0] == 0)
1163 	{
1164 	    if (attrCount == 0) _readsNothing = true;
1165 	    else                _readsNothing = false;
1166 	    break;
1167 	}
1168 
1169 	attrCount++;
1170 
1171 	checkIsNullTerminated (name, "attribute name");
1172 
1173 	//
1174 	// Read the attribute type and the size of the attribute value.
1175 	//
1176 
1177 	char typeName[Name::SIZE];
1178 	int size;
1179 
1180 	OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, Name::MAX_LENGTH, typeName);
1181 	checkIsNullTerminated (typeName, "attribute type name");
1182 	OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, size);
1183 
1184 	AttributeMap::iterator i = _map.find (name);
1185 
1186 	if (i != _map.end())
1187 	{
1188 	    //
1189 	    // The attribute already exists (for example,
1190 	    // because it is a predefined attribute).
1191 	    // Read the attribute's new value from the file.
1192 	    //
1193 
1194 	    if (strncmp (i->second->typeName(), typeName, sizeof (typeName)))
1195 		THROW (IEX_NAMESPACE::InputExc, "Unexpected type for image attribute "
1196 				      "\"" << name << "\".");
1197 
1198 	    i->second->readValueFrom (is, size, version);
1199 	}
1200 	else
1201 	{
1202 	    //
1203 	    // The new attribute does not exist yet.
1204 	    // If the attribute type is of a known type,
1205 	    // read the attribute value.  If the attribute
1206 	    // is of an unknown type, read its value and
1207 	    // store it as an OpaqueAttribute.
1208 	    //
1209 
1210 	    Attribute *attr;
1211 
1212 	    if (Attribute::knownType (typeName))
1213 		attr = Attribute::newAttribute (typeName);
1214 	    else
1215 		attr = new OpaqueAttribute (typeName);
1216 
1217 	    try
1218 	    {
1219 		attr->readValueFrom (is, size, version);
1220 		_map[name] = attr;
1221 	    }
1222 	    catch (...)
1223 	    {
1224 		delete attr;
1225 		throw;
1226 	    }
1227 	}
1228     }
1229 }
1230 
1231 
1232 void
staticInitialize()1233 staticInitialize ()
1234 {
1235     static Mutex criticalSection;
1236     Lock lock (criticalSection);
1237 
1238     static bool initialized = false;
1239 
1240     if (!initialized)
1241     {
1242 	//
1243 	// One-time initialization -- register
1244 	// some predefined attribute types.
1245 	//
1246 
1247 	Box2fAttribute::registerAttributeType();
1248 	Box2iAttribute::registerAttributeType();
1249 	ChannelListAttribute::registerAttributeType();
1250 	CompressionAttribute::registerAttributeType();
1251 	ChromaticitiesAttribute::registerAttributeType();
1252 	DeepImageStateAttribute::registerAttributeType();
1253 	DoubleAttribute::registerAttributeType();
1254 	EnvmapAttribute::registerAttributeType();
1255 	FloatAttribute::registerAttributeType();
1256 	FloatVectorAttribute::registerAttributeType();
1257 	IntAttribute::registerAttributeType();
1258 	KeyCodeAttribute::registerAttributeType();
1259 	LineOrderAttribute::registerAttributeType();
1260 	M33dAttribute::registerAttributeType();
1261 	M33fAttribute::registerAttributeType();
1262 	M44dAttribute::registerAttributeType();
1263 	M44fAttribute::registerAttributeType();
1264 	PreviewImageAttribute::registerAttributeType();
1265 	RationalAttribute::registerAttributeType();
1266 	StringAttribute::registerAttributeType();
1267         StringVectorAttribute::registerAttributeType();
1268 	TileDescriptionAttribute::registerAttributeType();
1269 	TimeCodeAttribute::registerAttributeType();
1270 	V2dAttribute::registerAttributeType();
1271 	V2fAttribute::registerAttributeType();
1272 	V2iAttribute::registerAttributeType();
1273 	V3dAttribute::registerAttributeType();
1274 	V3fAttribute::registerAttributeType();
1275 	V3iAttribute::registerAttributeType();
1276 	DwaCompressor::initializeFuncs();
1277 
1278 	initialized = true;
1279     }
1280 }
1281 
1282 
1283 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT
1284