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