Coverage Report

Created: 2023-12-08 06:53

/src/freeimage-svn/FreeImage/trunk/Source/OpenEXR/IlmImf/ImfHeader.cpp
Line
Count
Source (jump to first uncovered line)
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
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
0
{
107
0
    header.insert ("displayWindow", Box2iAttribute (displayWindow));
108
0
    header.insert ("dataWindow", Box2iAttribute (dataWindow));
109
0
    header.insert ("pixelAspectRatio", FloatAttribute (pixelAspectRatio));
110
0
    header.insert ("screenWindowCenter", V2fAttribute (screenWindowCenter));
111
0
    header.insert ("screenWindowWidth", FloatAttribute (screenWindowWidth));
112
0
    header.insert ("lineOrder", LineOrderAttribute (lineOrder));
113
0
    header.insert ("compression", CompressionAttribute (compression));
114
0
    header.insert ("channels", ChannelListAttribute ());
115
0
}
116
117
template <size_t N>
118
void checkIsNullTerminated (const char (&str)[N], const char *what)
119
0
{
120
0
  for (size_t i = 0; i < N; ++i) {
121
0
    if (str[i] == '\0')
122
0
      return;
123
0
  }
124
0
  std::stringstream s;
125
0
  s << "Invalid " << what << ": it is more than " << (N - 1) 
126
0
    << " characters long.";
127
0
  throw IEX_NAMESPACE::InputExc(s);
128
0
}
129
130
} // namespace
131
132
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
0
{
143
0
    staticInitialize();
144
145
0
    Box2i displayWindow (V2i (0, 0), V2i (width - 1, height - 1));
146
147
0
    initialize (*this,
148
0
    displayWindow,
149
0
    displayWindow,
150
0
    pixelAspectRatio,
151
0
    screenWindowCenter,
152
0
    screenWindowWidth,
153
0
    lineOrder,
154
0
    compression);
155
0
}
156
157
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
0
{
169
0
    staticInitialize();
170
171
0
    Box2i displayWindow (V2i (0, 0), V2i (width - 1, height - 1));
172
173
0
    initialize (*this,
174
0
    displayWindow,
175
0
    dataWindow,
176
0
    pixelAspectRatio,
177
0
    screenWindowCenter,
178
0
    screenWindowWidth,
179
0
    lineOrder,
180
0
    compression);
181
0
}
182
183
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
0
{
194
0
    staticInitialize();
195
196
0
    initialize (*this,
197
0
    displayWindow,
198
0
    dataWindow,
199
0
    pixelAspectRatio,
200
0
    screenWindowCenter,
201
0
    screenWindowWidth,
202
0
    lineOrder,
203
0
    compression);
204
0
}
205
206
207
Header::Header (const Header &other): _map()
208
0
{
209
0
    for (AttributeMap::const_iterator i = other._map.begin();
210
0
   i != other._map.end();
211
0
   ++i)
212
0
    {
213
0
  insert (*i->first, *i->second);
214
0
    }
215
0
}
216
217
218
Header::~Header ()
219
0
{
220
0
    for (AttributeMap::iterator i = _map.begin();
221
0
   i != _map.end();
222
0
   ++i)
223
0
    {
224
0
   delete i->second;
225
0
    }
226
0
}
227
228
229
Header &    
230
Header::operator = (const Header &other)
231
0
{
232
0
    if (this != &other)
233
0
    {
234
0
  for (AttributeMap::iterator i = _map.begin();
235
0
       i != _map.end();
236
0
       ++i)
237
0
  {
238
0
       delete i->second;
239
0
  }
240
241
0
  _map.erase (_map.begin(), _map.end());
242
243
0
  for (AttributeMap::const_iterator i = other._map.begin();
244
0
       i != other._map.end();
245
0
       ++i)
246
0
  {
247
0
      insert (*i->first, *i->second);
248
0
  }
249
0
    }
250
251
0
    return *this;
252
0
}
253
254
255
void
256
Header::erase (const char name[])
257
0
{
258
0
    if (name[0] == 0)
259
0
        THROW (IEX_NAMESPACE::ArgExc, "Image attribute name cannot be an empty string.");
260
    
261
    
262
0
    AttributeMap::iterator i = _map.find (name);
263
0
    if (i != _map.end())
264
0
        _map.erase (i);
265
266
0
}
267
268
269
void
270
Header::erase (const string &name)
271
0
{
272
0
    erase (name.c_str());
273
0
}
274
    
275
    
276
void
277
Header::insert (const char name[], const Attribute &attribute)
278
0
{
279
0
    if (name[0] == 0)
280
0
  THROW (IEX_NAMESPACE::ArgExc, "Image attribute name cannot be an empty string.");
281
282
0
    AttributeMap::iterator i = _map.find (name);
283
284
0
    if (i == _map.end())
285
0
    {
286
0
  Attribute *tmp = attribute.copy();
287
288
0
  try
289
0
  {
290
0
      _map[name] = tmp;
291
0
  }
292
0
  catch (...)
293
0
  {
294
0
      delete tmp;
295
0
      throw;
296
0
  }
297
0
    }
298
0
    else
299
0
    {
300
0
  if (strcmp (i->second->typeName(), attribute.typeName()))
301
0
      THROW (IEX_NAMESPACE::TypeExc, "Cannot assign a value of "
302
0
         "type \"" << attribute.typeName() << "\" "
303
0
         "to image attribute \"" << name << "\" of "
304
0
         "type \"" << i->second->typeName() << "\".");
305
306
0
  Attribute *tmp = attribute.copy();
307
0
  delete i->second;
308
0
  i->second = tmp;
309
0
    }
310
0
}
311
312
313
void
314
Header::insert (const string &name, const Attribute &attribute)
315
0
{
316
0
    insert (name.c_str(), attribute);
317
0
}
318
319
320
Attribute &   
321
Header::operator [] (const char name[])
322
0
{
323
0
    AttributeMap::iterator i = _map.find (name);
324
325
0
    if (i == _map.end())
326
0
  THROW (IEX_NAMESPACE::ArgExc, "Cannot find image attribute \"" << name << "\".");
327
328
0
    return *i->second;
329
0
}
330
331
332
const Attribute & 
333
Header::operator [] (const char name[]) const
334
0
{
335
0
    AttributeMap::const_iterator i = _map.find (name);
336
337
0
    if (i == _map.end())
338
0
  THROW (IEX_NAMESPACE::ArgExc, "Cannot find image attribute \"" << name << "\".");
339
340
0
    return *i->second;
341
0
}
342
343
344
Attribute &   
345
Header::operator [] (const string &name)
346
0
{
347
0
    return this->operator[] (name.c_str());
348
0
}
349
350
351
const Attribute & 
352
Header::operator [] (const string &name) const
353
0
{
354
0
    return this->operator[] (name.c_str());
355
0
}
356
357
358
Header::Iterator
359
Header::begin ()
360
0
{
361
0
    return _map.begin();
362
0
}
363
364
365
Header::ConstIterator
366
Header::begin () const
367
0
{
368
0
    return _map.begin();
369
0
}
370
371
372
Header::Iterator
373
Header::end ()
374
0
{
375
0
    return _map.end();
376
0
}
377
378
379
Header::ConstIterator
380
Header::end () const
381
0
{
382
0
    return _map.end();
383
0
}
384
385
386
Header::Iterator
387
Header::find (const char name[])
388
0
{
389
0
    return _map.find (name);
390
0
}
391
392
393
Header::ConstIterator
394
Header::find (const char name[]) const
395
0
{
396
0
    return _map.find (name);
397
0
}
398
399
400
Header::Iterator
401
Header::find (const string &name)
402
0
{
403
0
    return find (name.c_str());
404
0
}
405
406
407
Header::ConstIterator
408
Header::find (const string &name) const
409
0
{
410
0
    return find (name.c_str());
411
0
}
412
413
414
IMATH_NAMESPACE::Box2i &  
415
Header::displayWindow ()
416
0
{
417
0
    return static_cast <Box2iAttribute &>
418
0
  ((*this)["displayWindow"]).value();
419
0
}
420
421
422
const IMATH_NAMESPACE::Box2i &
423
Header::displayWindow () const
424
0
{
425
0
    return static_cast <const Box2iAttribute &>
426
0
  ((*this)["displayWindow"]).value();
427
0
}
428
429
430
IMATH_NAMESPACE::Box2i &  
431
Header::dataWindow ()
432
0
{
433
0
    return static_cast <Box2iAttribute &>
434
0
  ((*this)["dataWindow"]).value();
435
0
}
436
437
438
const IMATH_NAMESPACE::Box2i &
439
Header::dataWindow () const
440
0
{
441
0
    return static_cast <const Box2iAttribute &>
442
0
  ((*this)["dataWindow"]).value();
443
0
}
444
445
446
float &   
447
Header::pixelAspectRatio ()
448
0
{
449
0
    return static_cast <FloatAttribute &>
450
0
  ((*this)["pixelAspectRatio"]).value();
451
0
}
452
453
454
const float & 
455
Header::pixelAspectRatio () const
456
0
{
457
0
    return static_cast <const FloatAttribute &>
458
0
  ((*this)["pixelAspectRatio"]).value();
459
0
}
460
461
462
IMATH_NAMESPACE::V2f &  
463
Header::screenWindowCenter ()
464
0
{
465
0
    return static_cast <V2fAttribute &>
466
0
  ((*this)["screenWindowCenter"]).value();
467
0
}
468
469
470
const IMATH_NAMESPACE::V2f &  
471
Header::screenWindowCenter () const
472
0
{
473
0
    return static_cast <const V2fAttribute &>
474
0
  ((*this)["screenWindowCenter"]).value();
475
0
}
476
477
478
float &   
479
Header::screenWindowWidth ()
480
0
{
481
0
    return static_cast <FloatAttribute &>
482
0
  ((*this)["screenWindowWidth"]).value();
483
0
}
484
485
486
const float & 
487
Header::screenWindowWidth () const
488
0
{
489
0
    return static_cast <const FloatAttribute &>
490
0
  ((*this)["screenWindowWidth"]).value();
491
0
}
492
493
494
ChannelList & 
495
Header::channels ()
496
0
{
497
0
    return static_cast <ChannelListAttribute &>
498
0
  ((*this)["channels"]).value();
499
0
}
500
501
502
const ChannelList & 
503
Header::channels () const
504
0
{
505
0
    return static_cast <const ChannelListAttribute &>
506
0
  ((*this)["channels"]).value();
507
0
}
508
509
510
LineOrder &
511
Header::lineOrder ()
512
0
{
513
0
    return static_cast <LineOrderAttribute &>
514
0
  ((*this)["lineOrder"]).value();
515
0
}
516
517
518
const LineOrder &
519
Header::lineOrder () const
520
0
{
521
0
    return static_cast <const LineOrderAttribute &>
522
0
  ((*this)["lineOrder"]).value();
523
0
}
524
525
526
Compression &
527
Header::compression ()
528
0
{
529
0
    return static_cast <CompressionAttribute &>
530
0
  ((*this)["compression"]).value();
531
0
}
532
533
534
const Compression &
535
Header::compression () const
536
0
{
537
0
    return static_cast <const CompressionAttribute &>
538
0
  ((*this)["compression"]).value();
539
0
}
540
541
542
void
543
Header::setName(const string& name)
544
0
{
545
0
    insert ("name", StringAttribute (name));
546
0
}
547
548
549
bool
550
Header::hasName() const
551
0
{
552
0
    return findTypedAttribute <StringAttribute> ("name") != 0;
553
0
}
554
555
556
string &
557
Header::name()
558
0
{
559
0
    return typedAttribute <StringAttribute> ("name").value();
560
0
}
561
562
563
const string &
564
Header::name() const
565
0
{
566
0
    return typedAttribute <StringAttribute> ("name").value();
567
0
}
568
569
570
void
571
Header::setType(const string& type)
572
0
{
573
0
    if (isSupportedType(type) == false)
574
0
    {
575
0
        throw IEX_NAMESPACE::ArgExc (type + "is not a supported image type." +
576
0
                           "The following are supported: " +
577
0
                           SCANLINEIMAGE + ", " +
578
0
                           TILEDIMAGE + ", " +
579
0
                           DEEPSCANLINE + " or " +
580
0
                           DEEPTILE + ".");
581
0
    }
582
583
0
    insert ("type", StringAttribute (type));
584
585
    // (TODO) Should we do it here?
586
0
    if (isDeepData(type) && hasVersion() == false)
587
0
    {
588
0
        setVersion(1);
589
0
    }
590
0
}
591
592
593
bool
594
Header::hasType() const
595
0
{
596
0
    return findTypedAttribute <StringAttribute> ("type") != 0;
597
0
}
598
599
600
string &
601
Header::type()
602
0
{
603
0
    return typedAttribute <StringAttribute> ("type").value();
604
0
}
605
606
607
const string &
608
Header::type() const
609
0
{
610
0
    return typedAttribute <StringAttribute> ("type").value();
611
0
}
612
613
614
void
615
Header::setView(const string& view)
616
0
{
617
0
    insert ("view", StringAttribute (view));
618
0
}
619
620
621
bool
622
Header::hasView() const
623
0
{
624
0
    return findTypedAttribute <StringAttribute> ("view") != 0;
625
0
}
626
627
628
string &
629
Header::view()
630
0
{
631
0
    return typedAttribute <StringAttribute> ("view").value();
632
0
}
633
634
635
const string &
636
Header::view() const
637
0
{
638
0
    return typedAttribute <StringAttribute> ("view").value();
639
0
}
640
641
642
void
643
Header::setVersion(const int version)
644
0
{
645
0
    if (version != 1)
646
0
    {
647
0
        throw IEX_NAMESPACE::ArgExc ("We can only process version 1");
648
0
    }
649
650
0
    insert ("version", IntAttribute (version));
651
0
}
652
653
654
bool
655
Header::hasVersion() const
656
0
{
657
0
    return findTypedAttribute <IntAttribute> ("version") != 0;
658
0
}
659
660
661
int &
662
Header::version()
663
0
{
664
0
    return typedAttribute <IntAttribute> ("version").value();
665
0
}
666
667
668
const int &
669
Header::version() const
670
0
{
671
0
    return typedAttribute <IntAttribute> ("version").value();
672
0
}
673
674
void 
675
Header::setChunkCount(int chunks)
676
0
{
677
0
    insert("chunkCount",IntAttribute(chunks));
678
0
}
679
680
bool 
681
Header::hasChunkCount() const
682
0
{
683
0
   return findTypedAttribute<IntAttribute>("chunkCount") != 0;
684
0
}
685
686
int& 
687
Header::chunkCount()
688
0
{
689
0
    return typedAttribute <IntAttribute> ("chunkCount").value();
690
0
}
691
692
const int& 
693
Header::chunkCount() const
694
0
{
695
0
    return typedAttribute <IntAttribute> ("chunkCount").value();
696
0
}
697
698
void
699
Header::setTileDescription(const TileDescription& td)
700
0
{
701
0
    insert ("tiles", TileDescriptionAttribute (td));
702
0
}
703
704
705
bool
706
Header::hasTileDescription() const
707
0
{
708
0
    return findTypedAttribute <TileDescriptionAttribute> ("tiles") != 0;
709
0
}
710
711
712
TileDescription &
713
Header::tileDescription ()
714
0
{
715
0
    return typedAttribute <TileDescriptionAttribute> ("tiles").value();
716
0
}
717
718
719
const TileDescription &
720
Header::tileDescription () const
721
0
{
722
0
    return typedAttribute <TileDescriptionAttribute> ("tiles").value();
723
0
}
724
725
void    
726
Header::setPreviewImage (const PreviewImage &pi)
727
0
{
728
0
    insert ("preview", PreviewImageAttribute (pi));
729
0
}
730
731
732
PreviewImage &
733
Header::previewImage ()
734
0
{
735
0
    return typedAttribute <PreviewImageAttribute> ("preview").value();
736
0
}
737
738
739
const PreviewImage &
740
Header::previewImage () const
741
0
{
742
0
    return typedAttribute <PreviewImageAttribute> ("preview").value();
743
0
}
744
745
746
bool    
747
Header::hasPreviewImage () const
748
0
{
749
0
    return findTypedAttribute <PreviewImageAttribute> ("preview") != 0;
750
0
}
751
752
753
void    
754
Header::sanityCheck (bool isTiled, bool isMultipartFile) const
755
0
{
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
0
    const Box2i &displayWindow = this->displayWindow();
765
766
0
    if (displayWindow.min.x > displayWindow.max.x ||
767
0
  displayWindow.min.y > displayWindow.max.y ||
768
0
  displayWindow.min.x <= -(INT_MAX / 2) ||
769
0
  displayWindow.min.y <= -(INT_MAX / 2) ||
770
0
  displayWindow.max.x >=  (INT_MAX / 2) ||
771
0
  displayWindow.max.y >=  (INT_MAX / 2))
772
0
    {
773
0
  throw IEX_NAMESPACE::ArgExc ("Invalid display window in image header.");
774
0
    }
775
776
0
    const Box2i &dataWindow = this->dataWindow();
777
778
0
    if (dataWindow.min.x > dataWindow.max.x ||
779
0
  dataWindow.min.y > dataWindow.max.y ||
780
0
  dataWindow.min.x <= -(INT_MAX / 2) ||
781
0
  dataWindow.min.y <= -(INT_MAX / 2) ||
782
0
  dataWindow.max.x >=  (INT_MAX / 2) ||
783
0
  dataWindow.max.y >=  (INT_MAX / 2))
784
0
    {
785
0
  throw IEX_NAMESPACE::ArgExc ("Invalid data window in image header.");
786
0
    }
787
788
0
    if (maxImageWidth > 0 &&
789
0
        maxImageWidth < (dataWindow.max.x - dataWindow.min.x + 1))
790
0
    {
791
0
  THROW (IEX_NAMESPACE::ArgExc, "The width of the data window exceeds the "
792
0
          "maximum width of " << maxImageWidth << "pixels.");
793
0
    }
794
795
0
    if (maxImageHeight > 0 &&
796
0
  maxImageHeight < dataWindow.max.y - dataWindow.min.y + 1)
797
0
    {
798
0
  THROW (IEX_NAMESPACE::ArgExc, "The width of the data window exceeds the "
799
0
          "maximum width of " << maxImageHeight << "pixels.");
800
0
    }
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
0
   if( maxImageHeight>0 && maxImageWidth>0 && 
806
0
       hasChunkCount() && chunkCount()>Int64(maxImageWidth)*Int64(maxImageHeight))
807
0
   {
808
0
       THROW (IEX_NAMESPACE::ArgExc, "chunkCount exceeds maximum area of "
809
0
       << Int64(maxImageWidth)*Int64(maxImageHeight) << " pixels." );
810
       
811
0
   }
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
0
    float pixelAspectRatio = this->pixelAspectRatio();
826
827
0
    const float MIN_PIXEL_ASPECT_RATIO = 1e-6f;
828
0
    const float MAX_PIXEL_ASPECT_RATIO = 1e+6f;
829
830
0
    if (pixelAspectRatio < MIN_PIXEL_ASPECT_RATIO ||
831
0
  pixelAspectRatio > MAX_PIXEL_ASPECT_RATIO)
832
0
    {
833
0
  throw IEX_NAMESPACE::ArgExc ("Invalid pixel aspect ratio in image header.");
834
0
    }
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
0
    float screenWindowWidth = this->screenWindowWidth();
845
846
0
    if (screenWindowWidth < 0)
847
0
  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
0
    if (isMultipartFile)
856
0
    {
857
0
        if (!hasName())
858
0
        {
859
0
            throw IEX_NAMESPACE::ArgExc ("Headers in a multipart file should"
860
0
                               " have name attribute.");
861
0
        }
862
863
0
        if (!hasType())
864
0
        {
865
0
            throw IEX_NAMESPACE::ArgExc ("Headers in a multipart file should"
866
0
                               " have type attribute.");
867
0
        }
868
869
0
    }
870
    
871
0
    const std::string & part_type=hasType() ? type() : "";
872
    
873
0
    if(part_type!="" && !isSupportedType(part_type))
874
0
    {
875
        //
876
        // skip remaining sanity checks with unsupported types - they may not hold
877
        //
878
0
        return;
879
0
    }
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
0
    LineOrder lineOrder = this->lineOrder();
890
891
0
    if (isTiled)
892
0
    {
893
0
  if (!hasTileDescription())
894
0
  {
895
0
      throw IEX_NAMESPACE::ArgExc ("Tiled image has no tile "
896
0
             "description attribute.");
897
0
  }
898
899
0
  const TileDescription &tileDesc = tileDescription();
900
901
0
  if (tileDesc.xSize <= 0 || tileDesc.ySize <= 0)
902
0
      throw IEX_NAMESPACE::ArgExc ("Invalid tile size in image header.");
903
904
0
  if (maxTileWidth > 0 &&
905
0
      maxTileWidth < int(tileDesc.xSize))
906
0
  {
907
0
      THROW (IEX_NAMESPACE::ArgExc, "The width of the tiles exceeds the maximum "
908
0
        "width of " << maxTileWidth << "pixels.");
909
0
  }
910
911
0
  if (maxTileHeight > 0 &&
912
0
      maxTileHeight < int(tileDesc.ySize))
913
0
  {
914
0
      THROW (IEX_NAMESPACE::ArgExc, "The width of the tiles exceeds the maximum "
915
0
        "width of " << maxTileHeight << "pixels.");
916
0
  }
917
918
0
  if (tileDesc.mode != ONE_LEVEL &&
919
0
      tileDesc.mode != MIPMAP_LEVELS &&
920
0
      tileDesc.mode != RIPMAP_LEVELS)
921
0
      throw IEX_NAMESPACE::ArgExc ("Invalid level mode in image header.");
922
923
0
  if (tileDesc.roundingMode != ROUND_UP &&
924
0
      tileDesc.roundingMode != ROUND_DOWN)
925
0
      throw IEX_NAMESPACE::ArgExc ("Invalid level rounding mode in image header.");
926
927
0
  if (lineOrder != INCREASING_Y &&
928
0
      lineOrder != DECREASING_Y &&
929
0
      lineOrder != RANDOM_Y)
930
0
      throw IEX_NAMESPACE::ArgExc ("Invalid line order in image header.");
931
0
    }
932
0
    else
933
0
    {
934
0
        if (lineOrder != INCREASING_Y &&
935
0
            lineOrder != DECREASING_Y)
936
0
            throw IEX_NAMESPACE::ArgExc ("Invalid line order in image header.");
937
        
938
        
939
0
    }
940
941
    //
942
    // The compression method must be one of the predefined values.
943
    //
944
945
0
    if (!isValidCompression (this->compression()))
946
0
    throw IEX_NAMESPACE::ArgExc ("Unknown compression type in image header.");
947
    
948
0
    if(isDeepData(part_type))
949
0
    {
950
0
        if (!isValidDeepCompression (this->compression()))
951
0
            throw IEX_NAMESPACE::ArgExc ("Compression type in header not valid for deep data");
952
0
    }
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
0
    const ChannelList &channels = this->channels();
968
    
969
0
    if (isTiled)
970
0
    {
971
0
  for (ChannelList::ConstIterator i = channels.begin();
972
0
       i != channels.end();
973
0
       ++i)
974
0
  {
975
0
      if (i.channel().type != OPENEXR_IMF_INTERNAL_NAMESPACE::UINT &&
976
0
        i.channel().type != OPENEXR_IMF_INTERNAL_NAMESPACE::HALF &&
977
0
        i.channel().type != OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT)
978
0
      {
979
0
    THROW (IEX_NAMESPACE::ArgExc, "Pixel type of \"" << i.name() << "\" "
980
0
                  "image channel is invalid.");
981
0
      }
982
983
0
      if (i.channel().xSampling != 1)
984
0
      {
985
0
    THROW (IEX_NAMESPACE::ArgExc, "The x subsampling factor for the "
986
0
            "\"" << i.name() << "\" channel "
987
0
            "is not 1.");
988
0
      } 
989
990
0
      if (i.channel().ySampling != 1)
991
0
      {
992
0
    THROW (IEX_NAMESPACE::ArgExc, "The y subsampling factor for the "
993
0
            "\"" << i.name() << "\" channel "
994
0
            "is not 1.");
995
0
      } 
996
0
  }
997
0
    }
998
0
    else
999
0
    {
1000
0
  for (ChannelList::ConstIterator i = channels.begin();
1001
0
       i != channels.end();
1002
0
       ++i)
1003
0
  {
1004
0
      if (i.channel().type != OPENEXR_IMF_INTERNAL_NAMESPACE::UINT &&
1005
0
        i.channel().type != OPENEXR_IMF_INTERNAL_NAMESPACE::HALF &&
1006
0
        i.channel().type != OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT)
1007
0
      {
1008
0
    THROW (IEX_NAMESPACE::ArgExc, "Pixel type of \"" << i.name() << "\" "
1009
0
                  "image channel is invalid.");
1010
0
      }
1011
1012
0
      if (i.channel().xSampling < 1)
1013
0
      {
1014
0
    THROW (IEX_NAMESPACE::ArgExc, "The x subsampling factor for the "
1015
0
            "\"" << i.name() << "\" channel "
1016
0
            "is invalid.");
1017
0
      }
1018
1019
0
      if (i.channel().ySampling < 1)
1020
0
      {
1021
0
    THROW (IEX_NAMESPACE::ArgExc, "The y subsampling factor for the "
1022
0
            "\"" << i.name() << "\" channel "
1023
0
            "is invalid.");
1024
0
      }
1025
1026
0
      if (dataWindow.min.x % i.channel().xSampling)
1027
0
      {
1028
0
    THROW (IEX_NAMESPACE::ArgExc, "The minimum x coordinate of the "
1029
0
            "image's data window is not a multiple "
1030
0
            "of the x subsampling factor of "
1031
0
            "the \"" << i.name() << "\" channel.");
1032
0
      }
1033
1034
0
      if (dataWindow.min.y % i.channel().ySampling)
1035
0
      {
1036
0
    THROW (IEX_NAMESPACE::ArgExc, "The minimum y coordinate of the "
1037
0
            "image's data window is not a multiple "
1038
0
            "of the y subsampling factor of "
1039
0
            "the \"" << i.name() << "\" channel.");
1040
0
      }
1041
1042
0
      if ((dataWindow.max.x - dataWindow.min.x + 1) %
1043
0
        i.channel().xSampling)
1044
0
      {
1045
0
    THROW (IEX_NAMESPACE::ArgExc, "Number of pixels per row in the "
1046
0
            "image's data window is not a multiple "
1047
0
            "of the x subsampling factor of "
1048
0
            "the \"" << i.name() << "\" channel.");
1049
0
      }
1050
1051
0
      if ((dataWindow.max.y - dataWindow.min.y + 1) %
1052
0
        i.channel().ySampling)
1053
0
      {
1054
0
    THROW (IEX_NAMESPACE::ArgExc, "Number of pixels per column in the "
1055
0
            "image's data window is not a multiple "
1056
0
            "of the y subsampling factor of "
1057
0
            "the \"" << i.name() << "\" channel.");
1058
0
      }
1059
0
  }
1060
0
    }
1061
0
}
1062
1063
1064
void    
1065
Header::setMaxImageSize (int maxWidth, int maxHeight)
1066
0
{
1067
0
    maxImageWidth = maxWidth;
1068
0
    maxImageHeight = maxHeight;
1069
0
}
1070
1071
1072
void    
1073
Header::setMaxTileSize (int maxWidth, int maxHeight)
1074
0
{
1075
0
    maxTileWidth = maxWidth;
1076
0
    maxTileHeight = maxHeight;
1077
0
}
1078
1079
1080
bool
1081
Header::readsNothing()
1082
0
{
1083
0
    return _readsNothing;
1084
0
}
1085
1086
1087
Int64
1088
Header::writeTo (OPENEXR_IMF_INTERNAL_NAMESPACE::OStream &os, bool isTiled) const
1089
0
{
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
0
    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
0
    Int64 previewPosition = 0;
1103
1104
0
    const Attribute *preview =
1105
0
      findTypedAttribute <PreviewImageAttribute> ("preview");
1106
1107
0
    for (ConstIterator i = begin(); i != end(); ++i)
1108
0
    {
1109
  //
1110
  // Write the attribute's name and type.
1111
  //
1112
1113
0
  OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (os, i.name());
1114
0
  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
0
  StdOSStream oss;
1122
0
  i.attribute().writeValueTo (oss, version);
1123
1124
0
  std::string s = oss.str();
1125
0
  OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (os, (int) s.length());
1126
1127
0
  if (&i.attribute() == preview)
1128
0
      previewPosition = os.tellp();
1129
1130
0
  os.write (s.data(), int(s.length()));
1131
0
    }
1132
1133
    //
1134
    // Write zero-length attribute name to mark the end of the header.
1135
    //
1136
1137
0
    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (os, "");
1138
1139
0
    return previewPosition;
1140
0
}
1141
1142
1143
void
1144
Header::readFrom (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is, int &version)
1145
0
{
1146
    //
1147
    // Read all attributes.
1148
    //
1149
1150
0
    int attrCount = 0;
1151
1152
0
    while (true)
1153
0
    {
1154
  //
1155
  // Read the name of the attribute.
1156
  // A zero-length attribute name indicates the end of the header.
1157
  //
1158
1159
0
  char name[Name::SIZE];
1160
0
  OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, Name::MAX_LENGTH, name);
1161
1162
0
  if (name[0] == 0)
1163
0
  {
1164
0
      if (attrCount == 0) _readsNothing = true;
1165
0
      else                _readsNothing = false;
1166
0
      break;
1167
0
  }
1168
1169
0
  attrCount++;
1170
1171
0
  checkIsNullTerminated (name, "attribute name");
1172
1173
  //
1174
  // Read the attribute type and the size of the attribute value.
1175
  //
1176
1177
0
  char typeName[Name::SIZE];
1178
0
  int size;
1179
1180
0
  OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, Name::MAX_LENGTH, typeName);
1181
0
  checkIsNullTerminated (typeName, "attribute type name");
1182
0
  OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, size);
1183
1184
0
  AttributeMap::iterator i = _map.find (name);
1185
1186
0
  if (i != _map.end())
1187
0
  {
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
0
      if (strncmp (i->second->typeName(), typeName, sizeof (typeName)))
1195
0
    THROW (IEX_NAMESPACE::InputExc, "Unexpected type for image attribute "
1196
0
              "\"" << name << "\".");
1197
1198
0
      i->second->readValueFrom (is, size, version);
1199
0
  }
1200
0
  else
1201
0
  {
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
0
      Attribute *attr;
1211
1212
0
      if (Attribute::knownType (typeName))
1213
0
    attr = Attribute::newAttribute (typeName);
1214
0
      else
1215
0
    attr = new OpaqueAttribute (typeName);
1216
1217
0
      try
1218
0
      {
1219
0
    attr->readValueFrom (is, size, version);
1220
0
    _map[name] = attr;
1221
0
      }
1222
0
      catch (...)
1223
0
      {
1224
0
    delete attr;
1225
0
    throw;
1226
0
      }
1227
0
  }
1228
0
    }
1229
0
}
1230
1231
1232
void
1233
staticInitialize ()
1234
2
{
1235
2
    static Mutex criticalSection;
1236
2
    Lock lock (criticalSection);
1237
1238
2
    static bool initialized = false;
1239
1240
2
    if (!initialized)
1241
2
    {
1242
  //
1243
  // One-time initialization -- register
1244
  // some predefined attribute types.
1245
  //
1246
  
1247
2
  Box2fAttribute::registerAttributeType();
1248
2
  Box2iAttribute::registerAttributeType();
1249
2
  ChannelListAttribute::registerAttributeType();
1250
2
  CompressionAttribute::registerAttributeType();
1251
2
  ChromaticitiesAttribute::registerAttributeType();
1252
2
  DeepImageStateAttribute::registerAttributeType();
1253
2
  DoubleAttribute::registerAttributeType();
1254
2
  EnvmapAttribute::registerAttributeType();
1255
2
  FloatAttribute::registerAttributeType();
1256
2
  FloatVectorAttribute::registerAttributeType();
1257
2
  IntAttribute::registerAttributeType();
1258
2
  KeyCodeAttribute::registerAttributeType();
1259
2
  LineOrderAttribute::registerAttributeType();
1260
2
  M33dAttribute::registerAttributeType();
1261
2
  M33fAttribute::registerAttributeType();
1262
2
  M44dAttribute::registerAttributeType();
1263
2
  M44fAttribute::registerAttributeType();
1264
2
  PreviewImageAttribute::registerAttributeType();
1265
2
  RationalAttribute::registerAttributeType();
1266
2
  StringAttribute::registerAttributeType();
1267
2
        StringVectorAttribute::registerAttributeType();
1268
2
  TileDescriptionAttribute::registerAttributeType();
1269
2
  TimeCodeAttribute::registerAttributeType();
1270
2
  V2dAttribute::registerAttributeType();
1271
2
  V2fAttribute::registerAttributeType();
1272
2
  V2iAttribute::registerAttributeType();
1273
2
  V3dAttribute::registerAttributeType();
1274
2
  V3fAttribute::registerAttributeType();
1275
2
  V3iAttribute::registerAttributeType();
1276
2
  DwaCompressor::initializeFuncs();
1277
1278
2
  initialized = true;
1279
2
    }
1280
2
}
1281
1282
1283
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT