Coverage Report

Created: 2026-07-25 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openexr/src/lib/OpenEXR/ImfHeader.cpp
Line
Count
Source
1
//
2
// SPDX-License-Identifier: BSD-3-Clause
3
// Copyright (c) Contributors to the OpenEXR Project.
4
//
5
6
//-----------------------------------------------------------------------------
7
//
8
//  class Header
9
//
10
//-----------------------------------------------------------------------------
11
12
#include "Iex.h"
13
#include <IlmThreadConfig.h>
14
#include <ImfBoxAttribute.h>
15
#include <ImfChannelListAttribute.h>
16
#include <ImfChromaticitiesAttribute.h>
17
#include <ImfCompressionAttribute.h>
18
#include <ImfCompressor.h>
19
#include <ImfDeepImageStateAttribute.h>
20
#include <ImfDoubleAttribute.h>
21
#include <ImfDwaCompressor.h>
22
#include <ImfEnvmapAttribute.h>
23
#include <ImfFloatAttribute.h>
24
#include <ImfFloatVectorAttribute.h>
25
#include <ImfHeader.h>
26
#include <ImfIDManifestAttribute.h>
27
#include <ImfIntAttribute.h>
28
#include <ImfKeyCodeAttribute.h>
29
#include <ImfLineOrderAttribute.h>
30
#include <ImfMatrixAttribute.h>
31
#include <ImfMisc.h>
32
#include <ImfOpaqueAttribute.h>
33
#include <ImfPartType.h>
34
#include <ImfPreviewImageAttribute.h>
35
#include <ImfRationalAttribute.h>
36
#include <ImfStdIO.h>
37
#include <ImfStringAttribute.h>
38
#include <ImfStringVectorAttribute.h>
39
#include <ImfTileDescriptionAttribute.h>
40
#include <ImfTimeCodeAttribute.h>
41
#include <ImfVecAttribute.h>
42
#include <ImfVersion.h>
43
#include <atomic>
44
#include <cmath>
45
#include <sstream>
46
#include <stdlib.h>
47
#include <time.h>
48
#include <openexr_base.h>
49
50
#include "ImfNamespace.h"
51
#include "ImfTiledMisc.h"
52
53
#if ILMTHREAD_THREADING_ENABLED
54
#    include <mutex>
55
#endif
56
57
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
58
59
using namespace std;
60
using IMATH_NAMESPACE::Box2i;
61
using IMATH_NAMESPACE::V2f;
62
using IMATH_NAMESPACE::V2i;
63
64
namespace
65
{
66
67
struct CompressionRecord
68
{
69
    CompressionRecord ()
70
6.48k
    {
71
6.48k
        exr_get_default_zip_compression_level (&zip_level);
72
6.48k
        exr_get_default_dwa_compression_quality (&dwa_level);
73
6.48k
    }
74
    int   zip_level;
75
    float dwa_level;
76
};
77
// NB: This is extra complicated than one would normally write to
78
// handle scenario that seems to happen on MacOS/Windows (probably
79
// linux too, but unobserved) with a static library, where a
80
// (static/global) Header is being destroyed after the shutdown of
81
// this translation unit happens, causing use after destroy.
82
//
83
// but if we just use the once_flag / call_once mechanism, windows
84
// then starts crashing on exit in a different way.
85
86
struct CompressionStash;
87
// assignments to here happen in static singleton ctor which is
88
// guaranteed construction safe.
89
//
90
// we could potentially solve with using an atomic shared ptr instead
91
// of needing the ctor/dtor and getstash thing, but gcc 4.8 does not
92
// include proper support for those.
93
static std::atomic<CompressionStash*> s_stash{nullptr};
94
95
struct CompressionStash
96
{
97
1
    CompressionStash () { s_stash.store (this); }
98
    ~CompressionStash ()
99
1
    {
100
        // technically not safe in that if there are multiple threads
101
        // running at object destruction time, another thread may have
102
        // retrieved the pointer, but not yet entered the mutex, but
103
        // then we run, destroying the mutex, and then they crash
104
        // against a destroyed mutex. But this code only happens at
105
        // static object destruction time, and only has
106
        // non-deterministic behavior when compiled statically.
107
        // so. this is about all we can do to add this feature without
108
        // changing the abi other than say don't have static/global
109
        // Header objects?
110
1
        s_stash.store (nullptr);
111
1
#if ILMTHREAD_THREADING_ENABLED
112
        // let's explicitly grab the lock and clear the map in case
113
        // there is someone waiting on a lock concurrently at static
114
        // destruction time, just to be pedantic
115
1
        _mutex.lock ();
116
1
        _store.clear ();
117
1
        _mutex.unlock ();
118
1
#endif
119
1
    }
120
#if ILMTHREAD_THREADING_ENABLED
121
    std::mutex _mutex;
122
#endif
123
    std::map<const void*, CompressionRecord> _store;
124
};
125
126
static CompressionStash*
127
getStash ()
128
66.3k
{
129
66.3k
    static CompressionStash stash_impl;
130
66.3k
    return s_stash.load ();
131
66.3k
}
132
133
static void
134
clearCompressionRecord (Header* hdr)
135
42.5k
{
136
42.5k
    CompressionStash* s = getStash ();
137
42.5k
    if (s)
138
42.5k
    {
139
42.5k
#if ILMTHREAD_THREADING_ENABLED
140
42.5k
        std::lock_guard<std::mutex> lk (s->_mutex);
141
42.5k
#endif
142
42.5k
        auto i = s->_store.find (hdr);
143
42.5k
        if (i != s->_store.end ()) s->_store.erase (i);
144
42.5k
    }
145
42.5k
}
146
147
static CompressionRecord
148
retrieveCompressionRecord (const Header* hdr)
149
0
{
150
0
    CompressionRecord retval;
151
152
0
    CompressionStash* s = getStash ();
153
0
    if (s)
154
0
    {
155
0
#if ILMTHREAD_THREADING_ENABLED
156
0
        std::lock_guard<std::mutex> lk (s->_mutex);
157
0
#endif
158
0
        auto i = s->_store.find (hdr);
159
0
        if (i != s->_store.end ()) retval = i->second;
160
0
    }
161
0
    return retval;
162
0
}
163
164
static CompressionRecord&
165
retrieveCompressionRecord (Header* hdr)
166
3.24k
{
167
3.24k
    CompressionStash* s = getStash ();
168
3.24k
    if (s)
169
3.24k
    {
170
3.24k
#if ILMTHREAD_THREADING_ENABLED
171
3.24k
        std::lock_guard<std::mutex> lk (s->_mutex);
172
3.24k
#endif
173
3.24k
        return s->_store[hdr];
174
3.24k
    }
175
    // this will only happen at app shutdown, so it'd be an invalid
176
    // store anyway, but just return something to avoid a crash
177
0
    static CompressionRecord defrec;
178
0
    return defrec;
179
3.24k
}
180
181
static void
182
copyCompressionRecord (Header* dst, const Header* src)
183
20.5k
{
184
20.5k
    CompressionStash* s = getStash ();
185
20.5k
    if (s)
186
20.5k
    {
187
20.5k
#if ILMTHREAD_THREADING_ENABLED
188
20.5k
        std::lock_guard<std::mutex> lk (s->_mutex);
189
20.5k
#endif
190
20.5k
        auto i = s->_store.find (src);
191
20.5k
        if (i != s->_store.end ()) { s->_store[dst] = i->second; }
192
17.3k
        else
193
17.3k
        {
194
17.3k
            i = s->_store.find (dst);
195
17.3k
            if (i != s->_store.end ()) s->_store.erase (i);
196
17.3k
        }
197
20.5k
    }
198
20.5k
};
199
200
void
201
initialize (
202
    Header&      header,
203
    const Box2i& displayWindow,
204
    const Box2i& dataWindow,
205
    float        pixelAspectRatio,
206
    const V2f&   screenWindowCenter,
207
    float        screenWindowWidth,
208
    LineOrder    lineOrder,
209
    Compression  compression)
210
42.5k
{
211
42.5k
    header.insert ("displayWindow", Box2iAttribute (displayWindow));
212
42.5k
    header.insert ("dataWindow", Box2iAttribute (dataWindow));
213
42.5k
    if (!std::isnormal (pixelAspectRatio) || pixelAspectRatio < 0.f)
214
0
        THROW (IEX_NAMESPACE::ArgExc, "Invalid pixel aspect ratio");
215
42.5k
    header.insert ("pixelAspectRatio", FloatAttribute (pixelAspectRatio));
216
42.5k
    header.insert ("screenWindowCenter", V2fAttribute (screenWindowCenter));
217
42.5k
    header.insert ("screenWindowWidth", FloatAttribute (screenWindowWidth));
218
42.5k
    header.insert ("lineOrder", LineOrderAttribute (lineOrder));
219
42.5k
    header.insert ("compression", CompressionAttribute (compression));
220
42.5k
    header.insert ("channels", ChannelListAttribute ());
221
42.5k
}
222
223
template <size_t N>
224
void
225
checkIsNullTerminated (const char (&str)[N], const char* what)
226
0
{
227
0
    for (size_t i = 0; i < N; ++i)
228
0
    {
229
0
        if (str[i] == '\0') return;
230
0
    }
231
0
    std::stringstream s;
232
0
    s << "Invalid " << what << ": it is more than " << (N - 1)
233
0
      << " characters long.";
234
0
    throw IEX_NAMESPACE::InputExc (s);
235
0
}
236
237
void
238
sanityCheckDisplayWindow (int width, int height)
239
42.5k
{
240
    //
241
    // Ensure a valid displayWindow.  All values for which width-1 < 0
242
    // are invalid, but in particular, note that if width=-INT_MAX,
243
    // width-1 will overflow.
244
    //
245
246
42.5k
    if (width < 1 || height < 1)
247
0
        throw IEX_NAMESPACE::ArgExc ("Invalid display window in image header.");
248
42.5k
}
249
250
} // namespace
251
252
void
253
setDefaultZipCompressionLevel (int level)
254
0
{
255
0
    exr_set_default_zip_compression_level (level);
256
0
}
257
258
void
259
setDefaultDwaCompressionLevel (float level)
260
0
{
261
0
    exr_set_default_dwa_compression_quality (level);
262
0
}
263
264
Header::Header (
265
    int         width,
266
    int         height,
267
    float       pixelAspectRatio,
268
    const V2f&  screenWindowCenter,
269
    float       screenWindowWidth,
270
    LineOrder   lineOrder,
271
    Compression compression)
272
42.5k
    : _map (), _readsNothing (false)
273
42.5k
{
274
42.5k
    sanityCheckDisplayWindow (width, height);
275
276
42.5k
    staticInitialize ();
277
278
42.5k
    Box2i displayWindow (V2i (0, 0), V2i (width - 1, height - 1));
279
280
42.5k
    initialize (
281
42.5k
        *this,
282
42.5k
        displayWindow,
283
42.5k
        displayWindow,
284
42.5k
        pixelAspectRatio,
285
42.5k
        screenWindowCenter,
286
42.5k
        screenWindowWidth,
287
42.5k
        lineOrder,
288
42.5k
        compression);
289
42.5k
}
290
291
Header::Header (
292
    int          width,
293
    int          height,
294
    const Box2i& dataWindow,
295
    float        pixelAspectRatio,
296
    const V2f&   screenWindowCenter,
297
    float        screenWindowWidth,
298
    LineOrder    lineOrder,
299
    Compression  compression)
300
0
    : _map (), _readsNothing (false)
301
0
{
302
0
    sanityCheckDisplayWindow (width, height);
303
304
0
    staticInitialize ();
305
306
0
    Box2i displayWindow (V2i (0, 0), V2i (width - 1, height - 1));
307
308
0
    initialize (
309
0
        *this,
310
0
        displayWindow,
311
0
        dataWindow,
312
0
        pixelAspectRatio,
313
0
        screenWindowCenter,
314
0
        screenWindowWidth,
315
0
        lineOrder,
316
0
        compression);
317
0
}
318
319
Header::Header (
320
    const Box2i& displayWindow,
321
    const Box2i& dataWindow,
322
    float        pixelAspectRatio,
323
    const V2f&   screenWindowCenter,
324
    float        screenWindowWidth,
325
    LineOrder    lineOrder,
326
    Compression  compression)
327
0
    : _map (), _readsNothing (false)
328
0
{
329
0
    staticInitialize ();
330
331
0
    initialize (
332
0
        *this,
333
0
        displayWindow,
334
0
        dataWindow,
335
0
        pixelAspectRatio,
336
0
        screenWindowCenter,
337
0
        screenWindowWidth,
338
0
        lineOrder,
339
0
        compression);
340
0
}
341
342
Header::Header (const Header& other)
343
0
    : _map (), _readsNothing (other._readsNothing)
344
0
{
345
0
    for (AttributeMap::const_iterator i = other._map.begin ();
346
0
         i != other._map.end ();
347
0
         ++i)
348
0
    {
349
0
        insert (*i->first, *i->second);
350
0
    }
351
0
    copyCompressionRecord (this, &other);
352
0
}
353
354
Header::Header (Header&& other)
355
0
    : _map (std::move (other._map)), _readsNothing (other._readsNothing)
356
0
{
357
0
    copyCompressionRecord (this, &other);
358
0
}
359
360
Header::~Header ()
361
42.5k
{
362
553k
    for (AttributeMap::iterator i = _map.begin (); i != _map.end (); ++i)
363
510k
    {
364
510k
        delete i->second;
365
510k
    }
366
42.5k
    clearCompressionRecord (this);
367
42.5k
}
368
369
Header&
370
Header::operator= (const Header& other)
371
0
{
372
0
    if (this != &other)
373
0
    {
374
0
        for (AttributeMap::iterator i = _map.begin (); i != _map.end (); ++i)
375
0
        {
376
0
            delete i->second;
377
0
        }
378
379
0
        _map.clear ();
380
381
0
        for (AttributeMap::const_iterator i = other._map.begin ();
382
0
             i != other._map.end ();
383
0
             ++i)
384
0
        {
385
0
            insert (*i->first, *i->second);
386
0
        }
387
0
        copyCompressionRecord (this, &other);
388
0
        _readsNothing = other._readsNothing;
389
0
    }
390
391
0
    return *this;
392
0
}
393
394
Header&
395
Header::operator= (Header&& other)
396
20.5k
{
397
20.5k
    if (this != &other)
398
20.5k
    {
399
20.5k
        std::swap (_map, other._map);
400
        // don't have to move or anything as it's pod types
401
20.5k
        copyCompressionRecord (this, &other);
402
20.5k
        _readsNothing = other._readsNothing;
403
20.5k
    }
404
405
20.5k
    return *this;
406
20.5k
}
407
408
void
409
Header::erase (const char name[])
410
0
{
411
0
    if (name[0] == 0)
412
0
        THROW (
413
0
            IEX_NAMESPACE::ArgExc,
414
0
            "Image attribute name cannot be an empty string.");
415
416
0
    AttributeMap::iterator i = _map.find (name);
417
0
    if (i != _map.end ())
418
0
    {
419
0
        delete i->second;
420
0
        _map.erase (i);
421
0
    }
422
0
}
423
424
void
425
Header::erase (const string& name)
426
0
{
427
0
    erase (name.c_str ());
428
0
}
429
430
void
431
Header::insert (const char name[], const Attribute& attribute)
432
654k
{
433
654k
    if (name[0] == 0)
434
0
        THROW (
435
654k
            IEX_NAMESPACE::ArgExc,
436
654k
            "Image attribute name cannot be an empty string.");
437
438
654k
    AttributeMap::iterator i = _map.find (name);
439
654k
    if (!strcmp (name, "dwaCompressionLevel") &&
440
3.77k
        !strcmp (attribute.typeName (), "float"))
441
3.24k
    {
442
3.24k
        const TypedAttribute<float>& dwaattr =
443
3.24k
            dynamic_cast<const TypedAttribute<float>&> (attribute);
444
3.24k
        dwaCompressionLevel () = dwaattr.value ();
445
3.24k
    }
446
447
654k
    if (i == _map.end ())
448
510k
    {
449
510k
        Attribute* tmp = attribute.copy ();
450
451
510k
        try
452
510k
        {
453
510k
            _map[name] = tmp;
454
510k
        }
455
510k
        catch (...)
456
510k
        {
457
0
            delete tmp;
458
0
            throw;
459
0
        }
460
510k
    }
461
143k
    else
462
143k
    {
463
143k
        if (strcmp (i->second->typeName (), attribute.typeName ()))
464
0
            THROW (
465
143k
                IEX_NAMESPACE::TypeExc,
466
143k
                "Cannot assign a value of "
467
143k
                "type \""
468
143k
                    << attribute.typeName ()
469
143k
                    << "\" "
470
143k
                       "to image attribute \""
471
143k
                    << name
472
143k
                    << "\" of "
473
143k
                       "type \""
474
143k
                    << i->second->typeName () << "\".");
475
476
143k
        Attribute* tmp = attribute.copy ();
477
143k
        delete i->second;
478
143k
        i->second = tmp;
479
143k
    }
480
654k
}
481
482
void
483
Header::insert (const string& name, const Attribute& attribute)
484
0
{
485
0
    insert (name.c_str (), attribute);
486
0
}
487
488
Attribute&
489
Header::operator[] (const char name[])
490
25.7k
{
491
25.7k
    AttributeMap::iterator i = _map.find (name);
492
493
25.7k
    if (i == _map.end ())
494
0
        THROW (
495
25.7k
            IEX_NAMESPACE::ArgExc,
496
25.7k
            "Cannot find image attribute \"" << name << "\".");
497
498
25.7k
    return *i->second;
499
25.7k
}
500
501
const Attribute&
502
Header::operator[] (const char name[]) const
503
16.5k
{
504
16.5k
    AttributeMap::const_iterator i = _map.find (name);
505
506
16.5k
    if (i == _map.end ())
507
0
        THROW (
508
16.5k
            IEX_NAMESPACE::ArgExc,
509
16.5k
            "Cannot find image attribute \"" << name << "\".");
510
511
16.5k
    return *i->second;
512
16.5k
}
513
514
Attribute&
515
Header::operator[] (const string& name)
516
0
{
517
0
    return this->operator[] (name.c_str ());
518
0
}
519
520
const Attribute&
521
Header::operator[] (const string& name) const
522
0
{
523
0
    return this->operator[] (name.c_str ());
524
0
}
525
526
Header::Iterator
527
Header::begin ()
528
0
{
529
0
    return _map.begin ();
530
0
}
531
532
Header::ConstIterator
533
Header::begin () const
534
0
{
535
0
    return _map.begin ();
536
0
}
537
538
Header::Iterator
539
Header::end ()
540
0
{
541
0
    return _map.end ();
542
0
}
543
544
Header::ConstIterator
545
Header::end () const
546
0
{
547
0
    return _map.end ();
548
0
}
549
550
Header::Iterator
551
Header::find (const char name[])
552
0
{
553
0
    return _map.find (name);
554
0
}
555
556
Header::ConstIterator
557
Header::find (const char name[]) const
558
0
{
559
0
    return _map.find (name);
560
0
}
561
562
Header::Iterator
563
Header::find (const string& name)
564
0
{
565
0
    return find (name.c_str ());
566
0
}
567
568
Header::ConstIterator
569
Header::find (const string& name) const
570
0
{
571
0
    return find (name.c_str ());
572
0
}
573
574
IMATH_NAMESPACE::Box2i&
575
Header::displayWindow ()
576
0
{
577
0
    return static_cast<Box2iAttribute&> ((*this)["displayWindow"]).value ();
578
0
}
579
580
const IMATH_NAMESPACE::Box2i&
581
Header::displayWindow () const
582
0
{
583
0
    return static_cast<const Box2iAttribute&> ((*this)["displayWindow"])
584
0
        .value ();
585
0
}
586
587
IMATH_NAMESPACE::Box2i&
588
Header::dataWindow ()
589
0
{
590
0
    return static_cast<Box2iAttribute&> ((*this)["dataWindow"]).value ();
591
0
}
592
593
const IMATH_NAMESPACE::Box2i&
594
Header::dataWindow () const
595
2.99k
{
596
2.99k
    return static_cast<const Box2iAttribute&> ((*this)["dataWindow"]).value ();
597
2.99k
}
598
599
float&
600
Header::pixelAspectRatio ()
601
0
{
602
0
    return static_cast<FloatAttribute&> ((*this)["pixelAspectRatio"]).value ();
603
0
}
604
605
const float&
606
Header::pixelAspectRatio () const
607
0
{
608
0
    return static_cast<const FloatAttribute&> ((*this)["pixelAspectRatio"])
609
0
        .value ();
610
0
}
611
612
IMATH_NAMESPACE::V2f&
613
Header::screenWindowCenter ()
614
0
{
615
0
    return static_cast<V2fAttribute&> ((*this)["screenWindowCenter"]).value ();
616
0
}
617
618
const IMATH_NAMESPACE::V2f&
619
Header::screenWindowCenter () const
620
0
{
621
0
    return static_cast<const V2fAttribute&> ((*this)["screenWindowCenter"])
622
0
        .value ();
623
0
}
624
625
float&
626
Header::screenWindowWidth ()
627
0
{
628
0
    return static_cast<FloatAttribute&> ((*this)["screenWindowWidth"]).value ();
629
0
}
630
631
const float&
632
Header::screenWindowWidth () const
633
0
{
634
0
    return static_cast<const FloatAttribute&> ((*this)["screenWindowWidth"])
635
0
        .value ();
636
0
}
637
638
ChannelList&
639
Header::channels ()
640
25.7k
{
641
25.7k
    return static_cast<ChannelListAttribute&> ((*this)["channels"]).value ();
642
25.7k
}
643
644
const ChannelList&
645
Header::channels () const
646
13.1k
{
647
13.1k
    return static_cast<const ChannelListAttribute&> ((*this)["channels"])
648
13.1k
        .value ();
649
13.1k
}
650
651
LineOrder&
652
Header::lineOrder ()
653
0
{
654
0
    return static_cast<LineOrderAttribute&> ((*this)["lineOrder"]).value ();
655
0
}
656
657
const LineOrder&
658
Header::lineOrder () const
659
250
{
660
250
    return static_cast<const LineOrderAttribute&> ((*this)["lineOrder"])
661
250
        .value ();
662
250
}
663
664
Compression&
665
Header::compression ()
666
0
{
667
0
    return static_cast<CompressionAttribute&> ((*this)["compression"]).value ();
668
0
}
669
670
const Compression&
671
Header::compression () const
672
0
{
673
0
    return static_cast<const CompressionAttribute&> ((*this)["compression"])
674
0
        .value ();
675
0
}
676
677
void
678
Header::resetDefaultCompressionLevels ()
679
0
{
680
0
    clearCompressionRecord (this);
681
0
}
682
683
int&
684
Header::zipCompressionLevel ()
685
0
{
686
0
    return retrieveCompressionRecord (this).zip_level;
687
0
}
688
689
int
690
Header::zipCompressionLevel () const
691
0
{
692
0
    return retrieveCompressionRecord (this).zip_level;
693
0
}
694
695
float&
696
Header::dwaCompressionLevel ()
697
3.24k
{
698
3.24k
    return retrieveCompressionRecord (this).dwa_level;
699
3.24k
}
700
701
float
702
Header::dwaCompressionLevel () const
703
0
{
704
0
    return retrieveCompressionRecord (this).dwa_level;
705
0
}
706
707
void
708
Header::setName (const string& name)
709
0
{
710
0
    insert ("name", StringAttribute (name));
711
0
}
712
713
bool
714
Header::hasName () const
715
0
{
716
0
    return findTypedAttribute<StringAttribute> ("name") != 0;
717
0
}
718
719
string&
720
Header::name ()
721
0
{
722
0
    return typedAttribute<StringAttribute> ("name").value ();
723
0
}
724
725
const string&
726
Header::name () const
727
0
{
728
0
    return typedAttribute<StringAttribute> ("name").value ();
729
0
}
730
731
void
732
Header::setType (const string& type)
733
651
{
734
651
    if (isSupportedType (type) == false)
735
0
    {
736
0
        throw IEX_NAMESPACE::ArgExc (
737
0
            type + "is not a supported image type." +
738
0
            "The following are supported: " + SCANLINEIMAGE + ", " +
739
0
            TILEDIMAGE + ", " + DEEPSCANLINE + " or " + DEEPTILE + ".");
740
0
    }
741
742
651
    insert ("type", StringAttribute (type));
743
744
    // (TODO) Should we do it here?
745
651
    if (isDeepData (type) && hasVersion () == false) { setVersion (1); }
746
651
}
747
748
bool
749
Header::hasType () const
750
20.2k
{
751
20.2k
    return findTypedAttribute<StringAttribute> ("type") != 0;
752
20.2k
}
753
754
string&
755
Header::type ()
756
0
{
757
0
    return typedAttribute<StringAttribute> ("type").value ();
758
0
}
759
760
const string&
761
Header::type () const
762
0
{
763
0
    return typedAttribute<StringAttribute> ("type").value ();
764
0
}
765
766
void
767
Header::setView (const string& view)
768
0
{
769
0
    insert ("view", StringAttribute (view));
770
0
}
771
772
bool
773
Header::hasView () const
774
0
{
775
0
    return findTypedAttribute<StringAttribute> ("view") != 0;
776
0
}
777
778
string&
779
Header::view ()
780
0
{
781
0
    return typedAttribute<StringAttribute> ("view").value ();
782
0
}
783
784
const string&
785
Header::view () const
786
0
{
787
0
    return typedAttribute<StringAttribute> ("view").value ();
788
0
}
789
790
void
791
Header::setVersion (const int version)
792
0
{
793
0
    if (version != 1)
794
0
    {
795
0
        throw IEX_NAMESPACE::ArgExc ("We can only process version 1");
796
0
    }
797
798
0
    insert ("version", IntAttribute (version));
799
0
}
800
801
bool
802
Header::hasVersion () const
803
0
{
804
0
    return findTypedAttribute<IntAttribute> ("version") != 0;
805
0
}
806
807
int&
808
Header::version ()
809
0
{
810
0
    return typedAttribute<IntAttribute> ("version").value ();
811
0
}
812
813
const int&
814
Header::version () const
815
0
{
816
0
    return typedAttribute<IntAttribute> ("version").value ();
817
0
}
818
819
void
820
Header::setChunkCount (int chunks)
821
0
{
822
0
    insert ("chunkCount", IntAttribute (chunks));
823
0
}
824
825
bool
826
Header::hasChunkCount () const
827
0
{
828
0
    return findTypedAttribute<IntAttribute> ("chunkCount") != 0;
829
0
}
830
831
int&
832
Header::chunkCount ()
833
0
{
834
0
    return typedAttribute<IntAttribute> ("chunkCount").value ();
835
0
}
836
837
const int&
838
Header::chunkCount () const
839
0
{
840
0
    return typedAttribute<IntAttribute> ("chunkCount").value ();
841
0
}
842
843
void
844
Header::setTileDescription (const TileDescription& td)
845
0
{
846
0
    insert ("tiles", TileDescriptionAttribute (td));
847
0
}
848
849
bool
850
Header::hasTileDescription () const
851
0
{
852
0
    return findTypedAttribute<TileDescriptionAttribute> ("tiles") != 0;
853
0
}
854
855
TileDescription&
856
Header::tileDescription ()
857
0
{
858
0
    return typedAttribute<TileDescriptionAttribute> ("tiles").value ();
859
0
}
860
861
const TileDescription&
862
Header::tileDescription () const
863
0
{
864
0
    return typedAttribute<TileDescriptionAttribute> ("tiles").value ();
865
0
}
866
867
void
868
Header::setPreviewImage (const PreviewImage& pi)
869
0
{
870
0
    insert ("preview", PreviewImageAttribute (pi));
871
0
}
872
873
PreviewImage&
874
Header::previewImage ()
875
0
{
876
0
    return typedAttribute<PreviewImageAttribute> ("preview").value ();
877
0
}
878
879
const PreviewImage&
880
Header::previewImage () const
881
0
{
882
0
    return typedAttribute<PreviewImageAttribute> ("preview").value ();
883
0
}
884
885
bool
886
Header::hasPreviewImage () const
887
0
{
888
0
    return findTypedAttribute<PreviewImageAttribute> ("preview") != 0;
889
0
}
890
891
void
892
Header::sanityCheck (bool isTiled, bool isMultipartFile) const
893
0
{
894
    //
895
    // The display window and the data window must each
896
    // contain at least one pixel.  In addition, the
897
    // coordinates of the window corners must be small
898
    // enough to keep expressions like max-min+1 or
899
    // max+min from overflowing.
900
    //
901
902
0
    const Box2i& displayWindow = this->displayWindow ();
903
904
0
    if (displayWindow.min.x > displayWindow.max.x ||
905
0
        displayWindow.min.y > displayWindow.max.y ||
906
0
        displayWindow.min.x <= -(INT_MAX / 2) ||
907
0
        displayWindow.min.y <= -(INT_MAX / 2) ||
908
0
        displayWindow.max.x >= (INT_MAX / 2) ||
909
0
        displayWindow.max.y >= (INT_MAX / 2))
910
0
    {
911
0
        throw IEX_NAMESPACE::ArgExc ("Invalid display window in image header.");
912
0
    }
913
914
0
    const Box2i& dataWindow = this->dataWindow ();
915
916
0
    if (dataWindow.min.x > dataWindow.max.x ||
917
0
        dataWindow.min.y > dataWindow.max.y ||
918
0
        dataWindow.min.x <= -(INT_MAX / 2) ||
919
0
        dataWindow.min.y <= -(INT_MAX / 2) ||
920
0
        dataWindow.max.x >= (INT_MAX / 2) || dataWindow.max.y >= (INT_MAX / 2))
921
0
    {
922
0
        throw IEX_NAMESPACE::ArgExc ("Invalid data window in image header.");
923
0
    }
924
925
0
    int w = (dataWindow.max.x - dataWindow.min.x + 1);
926
927
0
    int maxImageWidth = 0, maxImageHeight = 0;
928
    // TODO: this really should be accessed via the context but
929
    // we don't have that fully wired through just yet, so continue
930
    // to use the default as the older C++ code has done
931
0
    exr_get_default_maximum_image_size (&maxImageWidth, &maxImageHeight);
932
933
0
    if (maxImageWidth > 0 && maxImageWidth < w)
934
0
    {
935
0
        THROW (
936
0
            IEX_NAMESPACE::ArgExc,
937
0
            "The width of the data window exceeds the "
938
0
            "maximum width of "
939
0
                << maxImageWidth << "pixels.");
940
0
    }
941
942
0
    int h = (dataWindow.max.y - dataWindow.min.y + 1);
943
0
    if (maxImageHeight > 0 && maxImageHeight < h)
944
0
    {
945
0
        THROW (
946
0
            IEX_NAMESPACE::ArgExc,
947
0
            "The height of the data window exceeds the "
948
0
            "maximum height of "
949
0
                << maxImageHeight << "pixels.");
950
0
    }
951
952
    // chunk table must be smaller than the maximum image area
953
    // (only reachable for unknown types or damaged files: will have thrown earlier
954
    //  for regular image types)
955
0
    if (maxImageHeight > 0 && maxImageWidth > 0 && hasChunkCount () &&
956
0
        static_cast<uint64_t> (chunkCount ()) >
957
0
            uint64_t (maxImageWidth) * uint64_t (maxImageHeight))
958
0
    {
959
0
        THROW (
960
0
            IEX_NAMESPACE::ArgExc,
961
0
            "chunkCount exceeds maximum area of "
962
0
                << uint64_t (maxImageWidth) * uint64_t (maxImageHeight)
963
0
                << " pixels.");
964
0
    }
965
966
    //
967
    // The pixel aspect ratio must be greater than 0.
968
    // In applications, numbers like the display or the
969
    // data window dimensions are likely to be multiplied
970
    // or divided by the pixel aspect ratio; to avoid
971
    // arithmetic exceptions, we limit the pixel aspect
972
    // ratio to a range that is smaller than theoretically
973
    // possible (real aspect ratios are likely to be close
974
    // to 1.0 anyway).
975
    //
976
977
0
    float pixelAspectRatio = this->pixelAspectRatio ();
978
979
0
    const float MIN_PIXEL_ASPECT_RATIO = 1e-6f;
980
0
    const float MAX_PIXEL_ASPECT_RATIO = 1e+6f;
981
982
0
    if (!std::isnormal (pixelAspectRatio) ||
983
0
        pixelAspectRatio < MIN_PIXEL_ASPECT_RATIO ||
984
0
        pixelAspectRatio > MAX_PIXEL_ASPECT_RATIO)
985
0
    {
986
0
        throw IEX_NAMESPACE::ArgExc (
987
0
            "Invalid pixel aspect ratio in image header.");
988
0
    }
989
990
    //
991
    // The screen window width must not be less than 0.
992
    // The size of the screen window can vary over a wide
993
    // range (fish-eye lens to astronomical telescope),
994
    // so we can't limit the screen window width to a
995
    // small range.
996
    //
997
998
0
    float screenWindowWidth = this->screenWindowWidth ();
999
1000
0
    if (screenWindowWidth < 0)
1001
0
        throw IEX_NAMESPACE::ArgExc (
1002
0
            "Invalid screen window width in image header.");
1003
1004
    //
1005
    // If the file has multiple parts, verify that each header has attribute
1006
    // name and type.
1007
    // (TODO) We may want to check more stuff here.
1008
    //
1009
1010
0
    if (isMultipartFile)
1011
0
    {
1012
0
        if (!hasName ())
1013
0
        {
1014
0
            throw IEX_NAMESPACE::ArgExc ("Headers in a multipart file should"
1015
0
                                         " have name attribute.");
1016
0
        }
1017
1018
0
        if (!hasType ())
1019
0
        {
1020
0
            throw IEX_NAMESPACE::ArgExc ("Headers in a multipart file should"
1021
0
                                         " have type attribute.");
1022
0
        }
1023
0
    }
1024
1025
0
    const std::string& part_type = hasType () ? type () : "";
1026
1027
0
    if (part_type != "" && !isSupportedType (part_type))
1028
0
    {
1029
        //
1030
        // skip remaining sanity checks with unsupported types - they may not hold
1031
        //
1032
0
        return;
1033
0
    }
1034
1035
0
    bool isDeep = isDeepData (part_type);
1036
1037
    //
1038
    // If the file is tiled, verify that the tile description has reasonable
1039
    // values and check to see if the lineOrder is one of the predefined 3.
1040
    // If the file is not tiled, then the lineOrder can only be INCREASING_Y
1041
    // or DECREASING_Y.
1042
    //
1043
1044
0
    LineOrder lineOrder = this->lineOrder ();
1045
1046
0
    if (isTiled)
1047
0
    {
1048
0
        if (!hasTileDescription ())
1049
0
        {
1050
0
            throw IEX_NAMESPACE::ArgExc ("Tiled image has no tile "
1051
0
                                         "description attribute.");
1052
0
        }
1053
1054
0
        const TileDescription& tileDesc = tileDescription ();
1055
1056
0
        if (tileDesc.xSize <= 0 || tileDesc.ySize <= 0 ||
1057
0
            tileDesc.xSize > INT_MAX || tileDesc.ySize > INT_MAX)
1058
0
            throw IEX_NAMESPACE::ArgExc ("Invalid tile size in image header.");
1059
1060
0
        int maxTileWidth = 0, maxTileHeight = 0;
1061
        // TODO: this really should be accessed via the context but
1062
        // we don't have that fully wired through just yet, so continue
1063
        // to use the default as the older C++ code has done
1064
0
        exr_get_default_maximum_tile_size (&maxTileWidth, &maxTileHeight);
1065
1066
0
        if (maxTileWidth > 0 && maxTileWidth < int (tileDesc.xSize))
1067
0
        {
1068
0
            THROW (
1069
0
                IEX_NAMESPACE::ArgExc,
1070
0
                "The width of the tiles exceeds the maximum "
1071
0
                "width of "
1072
0
                    << maxTileWidth << "pixels.");
1073
0
        }
1074
1075
0
        if (maxTileHeight > 0 && maxTileHeight < int (tileDesc.ySize))
1076
0
        {
1077
0
            THROW (
1078
0
                IEX_NAMESPACE::ArgExc,
1079
0
                "The width of the tiles exceeds the maximum "
1080
0
                "width of "
1081
0
                    << maxTileHeight << "pixels.");
1082
0
        }
1083
1084
0
        if (tileDesc.mode != ONE_LEVEL && tileDesc.mode != MIPMAP_LEVELS &&
1085
0
            tileDesc.mode != RIPMAP_LEVELS)
1086
0
            throw IEX_NAMESPACE::ArgExc ("Invalid level mode in image header.");
1087
1088
0
        if (tileDesc.roundingMode != ROUND_UP &&
1089
0
            tileDesc.roundingMode != ROUND_DOWN)
1090
0
            throw IEX_NAMESPACE::ArgExc (
1091
0
                "Invalid level rounding mode in image header.");
1092
1093
0
        if (lineOrder != INCREASING_Y && lineOrder != DECREASING_Y &&
1094
0
            lineOrder != RANDOM_Y)
1095
0
            throw IEX_NAMESPACE::ArgExc ("Invalid line order in image header.");
1096
1097
        // computes size of chunk offset table. Throws an exception if this exceeds
1098
        // the maximum allowable size
1099
0
        getTiledChunkOffsetTableSize (*this);
1100
0
    }
1101
0
    else
1102
0
    {
1103
0
        if (lineOrder != INCREASING_Y && lineOrder != DECREASING_Y)
1104
0
            throw IEX_NAMESPACE::ArgExc ("Invalid line order in image header.");
1105
0
    }
1106
1107
    //
1108
    // The compression method must be one of the predefined values.
1109
    //
1110
1111
0
    if (!isValidCompression (this->compression ()))
1112
0
        throw IEX_NAMESPACE::ArgExc (
1113
0
            "Unknown compression type in image header.");
1114
1115
0
    if (isDeep)
1116
0
    {
1117
0
        if (!isValidDeepCompression (this->compression ()))
1118
0
            throw IEX_NAMESPACE::ArgExc (
1119
0
                "Compression type in header not valid for deep data");
1120
0
    }
1121
1122
    //
1123
    // Check the channel list:
1124
    //
1125
    // If the file is tiled then for each channel, the type must be one of the
1126
    // predefined values, and the x and y sampling must both be 1.
1127
    //
1128
    // x and y sampling must currently also be 1 for deep scanline images
1129
    //
1130
    // If the file is not tiled then for each channel, the type must be one
1131
    // of the predefined values, the x and y coordinates of the data window's
1132
    // upper left corner must be divisible by the x and y subsampling factors,
1133
    // and the width and height of the data window must be divisible by the
1134
    // x and y subsampling factors.
1135
    //
1136
1137
0
    const ChannelList& channels = this->channels ();
1138
1139
0
    if (channels.begin () == channels.end ())
1140
0
    {
1141
0
        THROW (
1142
0
            IEX_NAMESPACE::ArgExc, "Missing or empty channel list in header");
1143
0
    }
1144
1145
0
    if (isTiled || isDeep)
1146
0
    {
1147
0
        for (ChannelList::ConstIterator i = channels.begin ();
1148
0
             i != channels.end ();
1149
0
             ++i)
1150
0
        {
1151
0
            if (i.channel ().type != OPENEXR_IMF_INTERNAL_NAMESPACE::UINT &&
1152
0
                i.channel ().type != OPENEXR_IMF_INTERNAL_NAMESPACE::HALF &&
1153
0
                i.channel ().type != OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT)
1154
0
            {
1155
0
                THROW (
1156
0
                    IEX_NAMESPACE::ArgExc,
1157
0
                    "Pixel type of \"" << i.name ()
1158
0
                                       << "\" "
1159
0
                                          "image channel is invalid.");
1160
0
            }
1161
1162
0
            if (i.channel ().xSampling != 1)
1163
0
            {
1164
0
                THROW (
1165
0
                    IEX_NAMESPACE::ArgExc,
1166
0
                    "The x subsampling factor for the "
1167
0
                    "\"" << i.name ()
1168
0
                         << "\" channel "
1169
0
                            "is not 1.");
1170
0
            }
1171
1172
0
            if (i.channel ().ySampling != 1)
1173
0
            {
1174
0
                THROW (
1175
0
                    IEX_NAMESPACE::ArgExc,
1176
0
                    "The y subsampling factor for the "
1177
0
                    "\"" << i.name ()
1178
0
                         << "\" channel "
1179
0
                            "is not 1.");
1180
0
            }
1181
0
        }
1182
0
    }
1183
0
    else
1184
0
    {
1185
0
        for (ChannelList::ConstIterator i = channels.begin ();
1186
0
             i != channels.end ();
1187
0
             ++i)
1188
0
        {
1189
0
            if (i.channel ().type != OPENEXR_IMF_INTERNAL_NAMESPACE::UINT &&
1190
0
                i.channel ().type != OPENEXR_IMF_INTERNAL_NAMESPACE::HALF &&
1191
0
                i.channel ().type != OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT)
1192
0
            {
1193
0
                THROW (
1194
0
                    IEX_NAMESPACE::ArgExc,
1195
0
                    "Pixel type of \"" << i.name ()
1196
0
                                       << "\" "
1197
0
                                          "image channel is invalid.");
1198
0
            }
1199
1200
0
            if (i.channel ().xSampling < 1)
1201
0
            {
1202
0
                THROW (
1203
0
                    IEX_NAMESPACE::ArgExc,
1204
0
                    "The x subsampling factor for the "
1205
0
                    "\"" << i.name ()
1206
0
                         << "\" channel "
1207
0
                            "is invalid.");
1208
0
            }
1209
1210
0
            if (i.channel ().ySampling < 1)
1211
0
            {
1212
0
                THROW (
1213
0
                    IEX_NAMESPACE::ArgExc,
1214
0
                    "The y subsampling factor for the "
1215
0
                    "\"" << i.name ()
1216
0
                         << "\" channel "
1217
0
                            "is invalid.");
1218
0
            }
1219
1220
0
            if (dataWindow.min.x % i.channel ().xSampling)
1221
0
            {
1222
0
                THROW (
1223
0
                    IEX_NAMESPACE::ArgExc,
1224
0
                    "The minimum x coordinate of the "
1225
0
                    "image's data window is not a multiple "
1226
0
                    "of the x subsampling factor of "
1227
0
                    "the \""
1228
0
                        << i.name () << "\" channel.");
1229
0
            }
1230
1231
0
            if (dataWindow.min.y % i.channel ().ySampling)
1232
0
            {
1233
0
                THROW (
1234
0
                    IEX_NAMESPACE::ArgExc,
1235
0
                    "The minimum y coordinate of the "
1236
0
                    "image's data window is not a multiple "
1237
0
                    "of the y subsampling factor of "
1238
0
                    "the \""
1239
0
                        << i.name () << "\" channel.");
1240
0
            }
1241
1242
0
            if ((dataWindow.max.x - dataWindow.min.x + 1) %
1243
0
                i.channel ().xSampling)
1244
0
            {
1245
0
                THROW (
1246
0
                    IEX_NAMESPACE::ArgExc,
1247
0
                    "Number of pixels per row in the "
1248
0
                    "image's data window is not a multiple "
1249
0
                    "of the x subsampling factor of "
1250
0
                    "the \""
1251
0
                        << i.name () << "\" channel.");
1252
0
            }
1253
1254
0
            if ((dataWindow.max.y - dataWindow.min.y + 1) %
1255
0
                i.channel ().ySampling)
1256
0
            {
1257
0
                THROW (
1258
0
                    IEX_NAMESPACE::ArgExc,
1259
0
                    "Number of pixels per column in the "
1260
0
                    "image's data window is not a multiple "
1261
0
                    "of the y subsampling factor of "
1262
0
                    "the \""
1263
0
                        << i.name () << "\" channel.");
1264
0
            }
1265
0
        }
1266
0
    }
1267
0
}
1268
1269
void
1270
Header::setMaxImageSize (int maxWidth, int maxHeight)
1271
2.02k
{
1272
2.02k
    exr_set_default_maximum_image_size (maxWidth, maxHeight);
1273
2.02k
}
1274
1275
void
1276
Header::setMaxTileSize (int maxWidth, int maxHeight)
1277
0
{
1278
0
    exr_set_default_maximum_tile_size (maxWidth, maxHeight);
1279
0
}
1280
1281
void
1282
Header::getMaxImageSize (int& maxWidth, int& maxHeight)
1283
0
{
1284
0
    exr_get_default_maximum_image_size (&maxWidth, &maxHeight);
1285
0
}
1286
1287
void
1288
Header::getMaxTileSize (int& maxWidth, int& maxHeight)
1289
0
{
1290
0
    exr_get_default_maximum_tile_size (&maxWidth, &maxHeight);
1291
0
}
1292
1293
bool
1294
Header::readsNothing ()
1295
0
{
1296
0
    return _readsNothing;
1297
0
}
1298
1299
uint64_t
1300
Header::writeTo (
1301
    OPENEXR_IMF_INTERNAL_NAMESPACE::OStream& os, bool isTiled) const
1302
0
{
1303
    //
1304
    // Write a "magic number" to identify the file as an image file.
1305
    // Write the current file format version number.
1306
    //
1307
1308
0
    int version = EXR_VERSION;
1309
1310
    //
1311
    // Write all attributes.  If we have a preview image attribute,
1312
    // keep track of its position in the file.
1313
    //
1314
1315
0
    uint64_t previewPosition = 0;
1316
1317
0
    const Attribute* preview =
1318
0
        findTypedAttribute<PreviewImageAttribute> ("preview");
1319
1320
0
    for (ConstIterator i = begin (); i != end (); ++i)
1321
0
    {
1322
        //
1323
        // Write the attribute's name and type.
1324
        //
1325
1326
0
        OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write<
1327
0
            OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (os, i.name ());
1328
0
        OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write<
1329
0
            OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (
1330
0
            os, i.attribute ().typeName ());
1331
1332
        //
1333
        // Write the size of the attribute value,
1334
        // and the value itself.
1335
        //
1336
1337
0
        StdOSStream oss;
1338
0
        i.attribute ().writeValueTo (oss, version);
1339
1340
0
        std::string s = oss.str ();
1341
0
        OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write<
1342
0
            OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (os, (int) s.length ());
1343
1344
0
        if (&i.attribute () == preview) previewPosition = os.tellp ();
1345
1346
0
        os.write (s.data (), int (s.length ()));
1347
0
    }
1348
1349
    //
1350
    // Write zero-length attribute name to mark the end of the header.
1351
    //
1352
1353
0
    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write<
1354
0
        OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (os, "");
1355
1356
0
    return previewPosition;
1357
0
}
1358
1359
void
1360
Header::readFrom (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is, int& version)
1361
0
{
1362
    //
1363
    // Read all attributes.
1364
    //
1365
1366
0
    int attrCount = 0;
1367
1368
0
    while (true)
1369
0
    {
1370
        //
1371
        // Read the name of the attribute.
1372
        // A zero-length attribute name indicates the end of the header.
1373
        //
1374
1375
0
        char name[Name::SIZE];
1376
0
        OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read<
1377
0
            OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (
1378
0
            is, Name::MAX_LENGTH, name);
1379
1380
0
        if (name[0] == 0)
1381
0
        {
1382
0
            if (attrCount == 0)
1383
0
                _readsNothing = true;
1384
0
            else
1385
0
                _readsNothing = false;
1386
0
            break;
1387
0
        }
1388
1389
0
        attrCount++;
1390
1391
0
        checkIsNullTerminated (name, "attribute name");
1392
1393
        //
1394
        // Read the attribute type and the size of the attribute value.
1395
        //
1396
1397
0
        char typeName[Name::SIZE];
1398
0
        int  size;
1399
1400
0
        OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read<
1401
0
            OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (
1402
0
            is, Name::MAX_LENGTH, typeName);
1403
0
        checkIsNullTerminated (typeName, "attribute type name");
1404
0
        OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read<
1405
0
            OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, size);
1406
1407
0
        if (size < 0)
1408
0
        {
1409
0
            throw IEX_NAMESPACE::InputExc (
1410
0
                "Invalid size field in header attribute");
1411
0
        }
1412
1413
0
        AttributeMap::iterator i = _map.find (name);
1414
1415
0
        if (i != _map.end ())
1416
0
        {
1417
            //
1418
            // The attribute already exists (for example,
1419
            // because it is a predefined attribute).
1420
            // Read the attribute's new value from the file.
1421
            //
1422
1423
0
            if (strncmp (i->second->typeName (), typeName, sizeof (typeName)))
1424
0
                THROW (
1425
0
                    IEX_NAMESPACE::InputExc,
1426
0
                    "Unexpected type for image attribute "
1427
0
                    "\"" << name
1428
0
                         << "\".");
1429
1430
0
            i->second->readValueFrom (is, size, version);
1431
0
        }
1432
0
        else
1433
0
        {
1434
            //
1435
            // The new attribute does not exist yet.
1436
            // If the attribute type is of a known type,
1437
            // read the attribute value.  If the attribute
1438
            // is of an unknown type, read its value and
1439
            // store it as an OpaqueAttribute.
1440
            //
1441
1442
0
            Attribute* attr;
1443
1444
0
            if (Attribute::knownType (typeName))
1445
0
                attr = Attribute::newAttribute (typeName);
1446
0
            else
1447
0
                attr = new OpaqueAttribute (typeName);
1448
1449
0
            try
1450
0
            {
1451
0
                attr->readValueFrom (is, size, version);
1452
0
                _map[name] = attr;
1453
0
            }
1454
0
            catch (...)
1455
0
            {
1456
0
                delete attr;
1457
0
                throw;
1458
0
            }
1459
0
        }
1460
0
    }
1461
0
}
1462
1463
void
1464
staticInitialize ()
1465
42.5k
{
1466
42.5k
#if ILMTHREAD_THREADING_ENABLED
1467
42.5k
    static std::mutex           criticalSection;
1468
42.5k
    std::lock_guard<std::mutex> lock (criticalSection);
1469
42.5k
#endif
1470
42.5k
    static bool initialized = false;
1471
1472
42.5k
    if (!initialized)
1473
1
    {
1474
        //
1475
        // One-time initialization -- register
1476
        // some predefined attribute types.
1477
        //
1478
1479
1
        Box2fAttribute::registerAttributeType ();
1480
1
        Box2iAttribute::registerAttributeType ();
1481
1
        ChannelListAttribute::registerAttributeType ();
1482
1
        CompressionAttribute::registerAttributeType ();
1483
1
        ChromaticitiesAttribute::registerAttributeType ();
1484
1
        DeepImageStateAttribute::registerAttributeType ();
1485
1
        DoubleAttribute::registerAttributeType ();
1486
1
        EnvmapAttribute::registerAttributeType ();
1487
1
        FloatAttribute::registerAttributeType ();
1488
1
        FloatVectorAttribute::registerAttributeType ();
1489
1
        IntAttribute::registerAttributeType ();
1490
1
        KeyCodeAttribute::registerAttributeType ();
1491
1
        LineOrderAttribute::registerAttributeType ();
1492
1
        M33dAttribute::registerAttributeType ();
1493
1
        M33fAttribute::registerAttributeType ();
1494
1
        M44dAttribute::registerAttributeType ();
1495
1
        M44fAttribute::registerAttributeType ();
1496
1
        PreviewImageAttribute::registerAttributeType ();
1497
1
        RationalAttribute::registerAttributeType ();
1498
1
        StringAttribute::registerAttributeType ();
1499
1
        StringVectorAttribute::registerAttributeType ();
1500
1
        TileDescriptionAttribute::registerAttributeType ();
1501
1
        TimeCodeAttribute::registerAttributeType ();
1502
1
        V2dAttribute::registerAttributeType ();
1503
1
        V2fAttribute::registerAttributeType ();
1504
1
        V2iAttribute::registerAttributeType ();
1505
1
        V3dAttribute::registerAttributeType ();
1506
1
        V3fAttribute::registerAttributeType ();
1507
1
        V3iAttribute::registerAttributeType ();
1508
1
        IDManifestAttribute::registerAttributeType ();
1509
1510
        //
1511
        // Register functions, for example specialized functions
1512
        // for different CPU architectures.
1513
        //
1514
1515
1
        initialized = true;
1516
1
    }
1517
42.5k
}
1518
1519
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT