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