Home | History | Annotate | Download | only in IlmImf
      1 ///////////////////////////////////////////////////////////////////////////
      2 //
      3 // Copyright (c) 2004, Industrial Light & Magic, a division of Lucas
      4 // Digital Ltd. LLC
      5 //
      6 // All rights reserved.
      7 //
      8 // Redistribution and use in source and binary forms, with or without
      9 // modification, are permitted provided that the following conditions are
     10 // met:
     11 // *       Redistributions of source code must retain the above copyright
     12 // notice, this list of conditions and the following disclaimer.
     13 // *       Redistributions in binary form must reproduce the above
     14 // copyright notice, this list of conditions and the following disclaimer
     15 // in the documentation and/or other materials provided with the
     16 // distribution.
     17 // *       Neither the name of Industrial Light & Magic nor the names of
     18 // its contributors may be used to endorse or promote products derived
     19 // from this software without specific prior written permission.
     20 //
     21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     32 //
     33 ///////////////////////////////////////////////////////////////////////////
     34 
     35 
     36 
     37 //-----------------------------------------------------------------------------
     38 //
     39 //	class Header
     40 //
     41 //-----------------------------------------------------------------------------
     42 
     43 #include <ImfHeader.h>
     44 #include <ImfStdIO.h>
     45 #include <ImfVersion.h>
     46 #include <ImfCompressor.h>
     47 #include <ImfMisc.h>
     48 #include <ImfBoxAttribute.h>
     49 #include <ImfChannelListAttribute.h>
     50 #include <ImfChromaticitiesAttribute.h>
     51 #include <ImfCompressionAttribute.h>
     52 #include <ImfDoubleAttribute.h>
     53 #include <ImfEnvmapAttribute.h>
     54 #include <ImfFloatAttribute.h>
     55 #include <ImfIntAttribute.h>
     56 #include <ImfKeyCodeAttribute.h>
     57 #include <ImfLineOrderAttribute.h>
     58 #include <ImfMatrixAttribute.h>
     59 #include <ImfOpaqueAttribute.h>
     60 #include <ImfPreviewImageAttribute.h>
     61 #include <ImfRationalAttribute.h>
     62 #include <ImfStringAttribute.h>
     63 #include <ImfStringVectorAttribute.h>
     64 #include <ImfTileDescriptionAttribute.h>
     65 #include <ImfTimeCodeAttribute.h>
     66 #include <ImfVecAttribute.h>
     67 #include "IlmThreadMutex.h"
     68 #include "Iex.h"
     69 #include <sstream>
     70 #include <stdlib.h>
     71 #include <time.h>
     72 
     73 
     74 namespace Imf {
     75 
     76 using namespace std;
     77 using Imath::Box2i;
     78 using Imath::V2i;
     79 using Imath::V2f;
     80 using IlmThread::Mutex;
     81 using IlmThread::Lock;
     82 
     83 
     84 namespace {
     85 
     86 int maxImageWidth = 0;
     87 int maxImageHeight = 0;
     88 int maxTileWidth = 0;
     89 int maxTileHeight = 0;
     90 
     91 
     92 void
     93 initialize (Header &header,
     94         const Box2i &displayWindow,
     95         const Box2i &dataWindow,
     96         float pixelAspectRatio,
     97         const V2f &screenWindowCenter,
     98         float screenWindowWidth,
     99         LineOrder lineOrder,
    100         Compression compression)
    101 {
    102     header.insert ("displayWindow", Box2iAttribute (displayWindow));
    103     header.insert ("dataWindow", Box2iAttribute (dataWindow));
    104     header.insert ("pixelAspectRatio", FloatAttribute (pixelAspectRatio));
    105     header.insert ("screenWindowCenter", V2fAttribute (screenWindowCenter));
    106     header.insert ("screenWindowWidth", FloatAttribute (screenWindowWidth));
    107     header.insert ("lineOrder", LineOrderAttribute (lineOrder));
    108     header.insert ("compression", CompressionAttribute (compression));
    109     header.insert ("channels", ChannelListAttribute ());
    110 }
    111 
    112 
    113 bool
    114 usesLongNames (const Header &header)
    115 {
    116     //
    117     // If an OpenEXR file contains any attribute names, attribute type names
    118     // or channel names longer than 31 characters, then the file cannot be
    119     // read by older versions of the IlmImf library (up to OpenEXR 1.6.1).
    120     // Before writing the file header, we check if the header contains
    121     // any names longer than 31 characters; if it does, then we set the
    122     // LONG_NAMES_FLAG in the file version number.  Older versions of the
    123     // IlmImf library will refuse to read files that have the LONG_NAMES_FLAG
    124     // set.  Without the flag, older versions of the library would mis-
    125     // interpret the file as broken.
    126     //
    127 
    128     for (Header::ConstIterator i = header.begin();
    129          i != header.end();
    130          ++i)
    131     {
    132         if (strlen (i.name()) >= 32 || strlen (i.attribute().typeName()) >= 32)
    133             return true;
    134     }
    135 
    136     const ChannelList &channels = header.channels();
    137 
    138     for (ChannelList::ConstIterator i = channels.begin();
    139          i != channels.end();
    140          ++i)
    141     {
    142         if (strlen (i.name()) >= 32)
    143             return true;
    144     }
    145 
    146     return false;
    147 }
    148 
    149 template <size_t N>
    150 void checkIsNullTerminated (const char (&str)[N], const char *what)
    151 {
    152     for (int i = 0; i < N; ++i) {
    153         if (str[i] == '\0')
    154             return;
    155     }
    156     std::stringstream s;
    157     s << "Invalid " << what << ": it is more than " << (N - 1)
    158         << " characters long.";
    159     throw Iex::InputExc(s);
    160 }
    161 
    162 } // namespace
    163 
    164 
    165 Header::Header (int width,
    166         int height,
    167         float pixelAspectRatio,
    168         const V2f &screenWindowCenter,
    169         float screenWindowWidth,
    170         LineOrder lineOrder,
    171         Compression compression)
    172 :
    173     _map()
    174 {
    175     staticInitialize();
    176 
    177     Box2i displayWindow (V2i (0, 0), V2i (width - 1, height - 1));
    178 
    179     initialize (*this,
    180         displayWindow,
    181         displayWindow,
    182         pixelAspectRatio,
    183         screenWindowCenter,
    184         screenWindowWidth,
    185         lineOrder,
    186         compression);
    187 }
    188 
    189 
    190 Header::Header (int width,
    191         int height,
    192         const Box2i &dataWindow,
    193         float pixelAspectRatio,
    194         const V2f &screenWindowCenter,
    195         float screenWindowWidth,
    196         LineOrder lineOrder,
    197         Compression compression)
    198 :
    199     _map()
    200 {
    201     staticInitialize();
    202 
    203     Box2i displayWindow (V2i (0, 0), V2i (width - 1, height - 1));
    204 
    205     initialize (*this,
    206         displayWindow,
    207         dataWindow,
    208         pixelAspectRatio,
    209         screenWindowCenter,
    210         screenWindowWidth,
    211         lineOrder,
    212         compression);
    213 }
    214 
    215 
    216 Header::Header (const Box2i &displayWindow,
    217         const Box2i &dataWindow,
    218         float pixelAspectRatio,
    219         const V2f &screenWindowCenter,
    220         float screenWindowWidth,
    221         LineOrder lineOrder,
    222         Compression compression)
    223 :
    224     _map()
    225 {
    226     staticInitialize();
    227 
    228     initialize (*this,
    229         displayWindow,
    230         dataWindow,
    231         pixelAspectRatio,
    232         screenWindowCenter,
    233         screenWindowWidth,
    234         lineOrder,
    235         compression);
    236 }
    237 
    238 
    239 Header::Header (const Header &other): _map()
    240 {
    241     for (AttributeMap::const_iterator i = other._map.begin();
    242      i != other._map.end();
    243      ++i)
    244     {
    245     insert (*i->first, *i->second);
    246     }
    247 }
    248 
    249 
    250 Header::~Header ()
    251 {
    252     for (AttributeMap::iterator i = _map.begin();
    253      i != _map.end();
    254      ++i)
    255     {
    256      delete i->second;
    257     }
    258 }
    259 
    260 
    261 Header &
    262 Header::operator = (const Header &other)
    263 {
    264     if (this != &other)
    265     {
    266     for (AttributeMap::iterator i = _map.begin();
    267          i != _map.end();
    268          ++i)
    269     {
    270          delete i->second;
    271     }
    272 
    273     _map.erase (_map.begin(), _map.end());
    274 
    275     for (AttributeMap::const_iterator i = other._map.begin();
    276          i != other._map.end();
    277          ++i)
    278     {
    279         insert (*i->first, *i->second);
    280     }
    281     }
    282 
    283     return *this;
    284 }
    285 
    286 
    287 void
    288 Header::insert (const char name[], const Attribute &attribute)
    289 {
    290     if (name[0] == 0)
    291     THROW (Iex::ArgExc, "Image attribute name cannot be an empty string.");
    292 
    293     AttributeMap::iterator i = _map.find (name);
    294 
    295     if (i == _map.end())
    296     {
    297     Attribute *tmp = attribute.copy();
    298 
    299     try
    300     {
    301         _map[name] = tmp;
    302     }
    303     catch (...)
    304     {
    305         delete tmp;
    306         throw;
    307     }
    308     }
    309     else
    310     {
    311     if (strcmp (i->second->typeName(), attribute.typeName()))
    312         THROW (Iex::TypeExc, "Cannot assign a value of "
    313                  "type \"" << attribute.typeName() << "\" "
    314                  "to image attribute \"" << name << "\" of "
    315                  "type \"" << i->second->typeName() << "\".");
    316 
    317     Attribute *tmp = attribute.copy();
    318     delete i->second;
    319     i->second = tmp;
    320     }
    321 }
    322 
    323 
    324 void
    325 Header::insert (const string &name, const Attribute &attribute)
    326 {
    327     insert (name.c_str(), attribute);
    328 }
    329 
    330 
    331 Attribute &
    332 Header::operator [] (const char name[])
    333 {
    334     AttributeMap::iterator i = _map.find (name);
    335 
    336     if (i == _map.end())
    337     THROW (Iex::ArgExc, "Cannot find image attribute \"" << name << "\".");
    338 
    339     return *i->second;
    340 }
    341 
    342 
    343 const Attribute &
    344 Header::operator [] (const char name[]) const
    345 {
    346     AttributeMap::const_iterator i = _map.find (name);
    347 
    348     if (i == _map.end())
    349     THROW (Iex::ArgExc, "Cannot find image attribute \"" << name << "\".");
    350 
    351     return *i->second;
    352 }
    353 
    354 
    355 Attribute &
    356 Header::operator [] (const string &name)
    357 {
    358     return this->operator[] (name.c_str());
    359 }
    360 
    361 
    362 const Attribute &
    363 Header::operator [] (const string &name) const
    364 {
    365     return this->operator[] (name.c_str());
    366 }
    367 
    368 
    369 Header::Iterator
    370 Header::begin ()
    371 {
    372     return _map.begin();
    373 }
    374 
    375 
    376 Header::ConstIterator
    377 Header::begin () const
    378 {
    379     return _map.begin();
    380 }
    381 
    382 
    383 Header::Iterator
    384 Header::end ()
    385 {
    386     return _map.end();
    387 }
    388 
    389 
    390 Header::ConstIterator
    391 Header::end () const
    392 {
    393     return _map.end();
    394 }
    395 
    396 
    397 Header::Iterator
    398 Header::find (const char name[])
    399 {
    400     return _map.find (name);
    401 }
    402 
    403 
    404 Header::ConstIterator
    405 Header::find (const char name[]) const
    406 {
    407     return _map.find (name);
    408 }
    409 
    410 
    411 Header::Iterator
    412 Header::find (const string &name)
    413 {
    414     return find (name.c_str());
    415 }
    416 
    417 
    418 Header::ConstIterator
    419 Header::find (const string &name) const
    420 {
    421     return find (name.c_str());
    422 }
    423 
    424 
    425 Imath::Box2i &
    426 Header::displayWindow ()
    427 {
    428     return static_cast <Box2iAttribute &>
    429     ((*this)["displayWindow"]).value();
    430 }
    431 
    432 
    433 const Imath::Box2i &
    434 Header::displayWindow () const
    435 {
    436     return static_cast <const Box2iAttribute &>
    437     ((*this)["displayWindow"]).value();
    438 }
    439 
    440 
    441 Imath::Box2i &
    442 Header::dataWindow ()
    443 {
    444     return static_cast <Box2iAttribute &>
    445     ((*this)["dataWindow"]).value();
    446 }
    447 
    448 
    449 const Imath::Box2i &
    450 Header::dataWindow () const
    451 {
    452     return static_cast <const Box2iAttribute &>
    453     ((*this)["dataWindow"]).value();
    454 }
    455 
    456 
    457 float &
    458 Header::pixelAspectRatio ()
    459 {
    460     return static_cast <FloatAttribute &>
    461     ((*this)["pixelAspectRatio"]).value();
    462 }
    463 
    464 
    465 const float &
    466 Header::pixelAspectRatio () const
    467 {
    468     return static_cast <const FloatAttribute &>
    469     ((*this)["pixelAspectRatio"]).value();
    470 }
    471 
    472 
    473 Imath::V2f &
    474 Header::screenWindowCenter ()
    475 {
    476     return static_cast <V2fAttribute &>
    477     ((*this)["screenWindowCenter"]).value();
    478 }
    479 
    480 
    481 const Imath::V2f &
    482 Header::screenWindowCenter () const
    483 {
    484     return static_cast <const V2fAttribute &>
    485     ((*this)["screenWindowCenter"]).value();
    486 }
    487 
    488 
    489 float &
    490 Header::screenWindowWidth ()
    491 {
    492     return static_cast <FloatAttribute &>
    493     ((*this)["screenWindowWidth"]).value();
    494 }
    495 
    496 
    497 const float &
    498 Header::screenWindowWidth () const
    499 {
    500     return static_cast <const FloatAttribute &>
    501     ((*this)["screenWindowWidth"]).value();
    502 }
    503 
    504 
    505 ChannelList &
    506 Header::channels ()
    507 {
    508     return static_cast <ChannelListAttribute &>
    509     ((*this)["channels"]).value();
    510 }
    511 
    512 
    513 const ChannelList &
    514 Header::channels () const
    515 {
    516     return static_cast <const ChannelListAttribute &>
    517     ((*this)["channels"]).value();
    518 }
    519 
    520 
    521 LineOrder &
    522 Header::lineOrder ()
    523 {
    524     return static_cast <LineOrderAttribute &>
    525     ((*this)["lineOrder"]).value();
    526 }
    527 
    528 
    529 const LineOrder &
    530 Header::lineOrder () const
    531 {
    532     return static_cast <const LineOrderAttribute &>
    533     ((*this)["lineOrder"]).value();
    534 }
    535 
    536 
    537 Compression &
    538 Header::compression ()
    539 {
    540     return static_cast <CompressionAttribute &>
    541     ((*this)["compression"]).value();
    542 }
    543 
    544 
    545 const Compression &
    546 Header::compression () const
    547 {
    548     return static_cast <const CompressionAttribute &>
    549     ((*this)["compression"]).value();
    550 }
    551 
    552 
    553 void
    554 Header::setTileDescription(const TileDescription& td)
    555 {
    556     insert ("tiles", TileDescriptionAttribute (td));
    557 }
    558 
    559 
    560 bool
    561 Header::hasTileDescription() const
    562 {
    563     return findTypedAttribute <TileDescriptionAttribute> ("tiles") != 0;
    564 }
    565 
    566 
    567 TileDescription &
    568 Header::tileDescription ()
    569 {
    570     return typedAttribute <TileDescriptionAttribute> ("tiles").value();
    571 }
    572 
    573 
    574 const TileDescription &
    575 Header::tileDescription () const
    576 {
    577     return typedAttribute <TileDescriptionAttribute> ("tiles").value();
    578 }
    579 
    580 void
    581 Header::setPreviewImage (const PreviewImage &pi)
    582 {
    583     insert ("preview", PreviewImageAttribute (pi));
    584 }
    585 
    586 
    587 PreviewImage &
    588 Header::previewImage ()
    589 {
    590     return typedAttribute <PreviewImageAttribute> ("preview").value();
    591 }
    592 
    593 
    594 const PreviewImage &
    595 Header::previewImage () const
    596 {
    597     return typedAttribute <PreviewImageAttribute> ("preview").value();
    598 }
    599 
    600 
    601 bool
    602 Header::hasPreviewImage () const
    603 {
    604     return findTypedAttribute <PreviewImageAttribute> ("preview") != 0;
    605 }
    606 
    607 
    608 void
    609 Header::sanityCheck (bool isTiled) const
    610 {
    611     //
    612     // The display window and the data window must each
    613     // contain at least one pixel.  In addition, the
    614     // coordinates of the window corners must be small
    615     // enough to keep expressions like max-min+1 or
    616     // max+min from overflowing.
    617     //
    618 
    619     const Box2i &displayWindow = this->displayWindow();
    620 
    621     if (displayWindow.min.x > displayWindow.max.x ||
    622     displayWindow.min.y > displayWindow.max.y ||
    623     displayWindow.min.x <= -(INT_MAX / 2) ||
    624     displayWindow.min.y <= -(INT_MAX / 2) ||
    625     displayWindow.max.x >=  (INT_MAX / 2) ||
    626     displayWindow.max.y >=  (INT_MAX / 2))
    627     {
    628     throw Iex::ArgExc ("Invalid display window in image header.");
    629     }
    630 
    631     const Box2i &dataWindow = this->dataWindow();
    632 
    633     if (dataWindow.min.x > dataWindow.max.x ||
    634     dataWindow.min.y > dataWindow.max.y ||
    635     dataWindow.min.x <= -(INT_MAX / 2) ||
    636     dataWindow.min.y <= -(INT_MAX / 2) ||
    637     dataWindow.max.x >=  (INT_MAX / 2) ||
    638     dataWindow.max.y >=  (INT_MAX / 2))
    639     {
    640     throw Iex::ArgExc ("Invalid data window in image header.");
    641     }
    642 
    643     if (maxImageWidth > 0 &&
    644     maxImageWidth < dataWindow.max.x - dataWindow.min.x + 1)
    645     {
    646     THROW (Iex::ArgExc, "The width of the data window exceeds the "
    647                 "maximum width of " << maxImageWidth << "pixels.");
    648     }
    649 
    650     if (maxImageHeight > 0 &&
    651     maxImageHeight < dataWindow.max.y - dataWindow.min.y + 1)
    652     {
    653     THROW (Iex::ArgExc, "The width of the data window exceeds the "
    654                 "maximum width of " << maxImageHeight << "pixels.");
    655     }
    656 
    657     //
    658     // The pixel aspect ratio must be greater than 0.
    659     // In applications, numbers like the the display or
    660     // data window dimensions are likely to be multiplied
    661     // or divided by the pixel aspect ratio; to avoid
    662     // arithmetic exceptions, we limit the pixel aspect
    663     // ratio to a range that is smaller than theoretically
    664     // possible (real aspect ratios are likely to be close
    665     // to 1.0 anyway).
    666     //
    667 
    668     float pixelAspectRatio = this->pixelAspectRatio();
    669 
    670     const float MIN_PIXEL_ASPECT_RATIO = 1e-6f;
    671     const float MAX_PIXEL_ASPECT_RATIO = 1e+6f;
    672 
    673     if (pixelAspectRatio < MIN_PIXEL_ASPECT_RATIO ||
    674     pixelAspectRatio > MAX_PIXEL_ASPECT_RATIO)
    675     {
    676     throw Iex::ArgExc ("Invalid pixel aspect ratio in image header.");
    677     }
    678 
    679     //
    680     // The screen window width must not be less than 0.
    681     // The size of the screen window can vary over a wide
    682     // range (fish-eye lens to astronomical telescope),
    683     // so we can't limit the screen window width to a
    684     // small range.
    685     //
    686 
    687     float screenWindowWidth = this->screenWindowWidth();
    688 
    689     if (screenWindowWidth < 0)
    690     throw Iex::ArgExc ("Invalid screen window width in image header.");
    691 
    692     //
    693     // If the file is tiled, verify that the tile description has resonable
    694     // values and check to see if the lineOrder is one of the predefined 3.
    695     // If the file is not tiled, then the lineOrder can only be INCREASING_Y
    696     // or DECREASING_Y.
    697     //
    698 
    699     LineOrder lineOrder = this->lineOrder();
    700 
    701     if (isTiled)
    702     {
    703     if (!hasTileDescription())
    704     {
    705         throw Iex::ArgExc ("Tiled image has no tile "
    706                    "description attribute.");
    707     }
    708 
    709     const TileDescription &tileDesc = tileDescription();
    710 
    711     if (tileDesc.xSize <= 0 || tileDesc.ySize <= 0)
    712         throw Iex::ArgExc ("Invalid tile size in image header.");
    713 
    714     if (maxTileWidth > 0 &&
    715         maxTileWidth < tileDesc.xSize)
    716     {
    717         THROW (Iex::ArgExc, "The width of the tiles exceeds the maximum "
    718                 "width of " << maxTileWidth << "pixels.");
    719     }
    720 
    721     if (maxTileHeight > 0 &&
    722         maxTileHeight < tileDesc.ySize)
    723     {
    724         THROW (Iex::ArgExc, "The width of the tiles exceeds the maximum "
    725                 "width of " << maxTileHeight << "pixels.");
    726     }
    727 
    728     if (tileDesc.mode != ONE_LEVEL &&
    729         tileDesc.mode != MIPMAP_LEVELS &&
    730         tileDesc.mode != RIPMAP_LEVELS)
    731         throw Iex::ArgExc ("Invalid level mode in image header.");
    732 
    733     if (tileDesc.roundingMode != ROUND_UP &&
    734         tileDesc.roundingMode != ROUND_DOWN)
    735         throw Iex::ArgExc ("Invalid level rounding mode in image header.");
    736 
    737     if (lineOrder != INCREASING_Y &&
    738         lineOrder != DECREASING_Y &&
    739         lineOrder != RANDOM_Y)
    740         throw Iex::ArgExc ("Invalid line order in image header.");
    741     }
    742     else
    743     {
    744     if (lineOrder != INCREASING_Y &&
    745         lineOrder != DECREASING_Y)
    746         throw Iex::ArgExc ("Invalid line order in image header.");
    747     }
    748 
    749     //
    750     // The compression method must be one of the predefined values.
    751     //
    752 
    753     if (!isValidCompression (this->compression()))
    754     throw Iex::ArgExc ("Unknown compression type in image header.");
    755 
    756     //
    757     // Check the channel list:
    758     //
    759     // If the file is tiled then for each channel, the type must be one of the
    760     // predefined values, and the x and y sampling must both be 1.
    761     //
    762     // If the file is not tiled then for each channel, the type must be one
    763     // of the predefined values, the x and y coordinates of the data window's
    764     // upper left corner must be divisible by the x and y subsampling factors,
    765     // and the width and height of the data window must be divisible by the
    766     // x and y subsampling factors.
    767     //
    768 
    769     const ChannelList &channels = this->channels();
    770 
    771     if (isTiled)
    772     {
    773     for (ChannelList::ConstIterator i = channels.begin();
    774          i != channels.end();
    775          ++i)
    776     {
    777         if (i.channel().type != UINT &&
    778         i.channel().type != HALF &&
    779         i.channel().type != FLOAT)
    780         {
    781         THROW (Iex::ArgExc, "Pixel type of \"" << i.name() << "\" "
    782                         "image channel is invalid.");
    783         }
    784 
    785         if (i.channel().xSampling != 1)
    786         {
    787         THROW (Iex::ArgExc, "The x subsampling factor for the "
    788                     "\"" << i.name() << "\" channel "
    789                     "is not 1.");
    790         }
    791 
    792         if (i.channel().ySampling != 1)
    793         {
    794         THROW (Iex::ArgExc, "The y subsampling factor for the "
    795                     "\"" << i.name() << "\" channel "
    796                     "is not 1.");
    797         }
    798     }
    799     }
    800     else
    801     {
    802     for (ChannelList::ConstIterator i = channels.begin();
    803          i != channels.end();
    804          ++i)
    805     {
    806         if (i.channel().type != UINT &&
    807         i.channel().type != HALF &&
    808         i.channel().type != FLOAT)
    809         {
    810         THROW (Iex::ArgExc, "Pixel type of \"" << i.name() << "\" "
    811                         "image channel is invalid.");
    812         }
    813 
    814         if (i.channel().xSampling < 1)
    815         {
    816         THROW (Iex::ArgExc, "The x subsampling factor for the "
    817                     "\"" << i.name() << "\" channel "
    818                     "is invalid.");
    819         }
    820 
    821         if (i.channel().ySampling < 1)
    822         {
    823         THROW (Iex::ArgExc, "The y subsampling factor for the "
    824                     "\"" << i.name() << "\" channel "
    825                     "is invalid.");
    826         }
    827 
    828         if (dataWindow.min.x % i.channel().xSampling)
    829         {
    830         THROW (Iex::ArgExc, "The minimum x coordinate of the "
    831                     "image's data window is not a multiple "
    832                     "of the x subsampling factor of "
    833                     "the \"" << i.name() << "\" channel.");
    834         }
    835 
    836         if (dataWindow.min.y % i.channel().ySampling)
    837         {
    838         THROW (Iex::ArgExc, "The minimum y coordinate of the "
    839                     "image's data window is not a multiple "
    840                     "of the y subsampling factor of "
    841                     "the \"" << i.name() << "\" channel.");
    842         }
    843 
    844         if ((dataWindow.max.x - dataWindow.min.x + 1) %
    845             i.channel().xSampling)
    846         {
    847         THROW (Iex::ArgExc, "Number of pixels per row in the "
    848                     "image's data window is not a multiple "
    849                     "of the x subsampling factor of "
    850                     "the \"" << i.name() << "\" channel.");
    851         }
    852 
    853         if ((dataWindow.max.y - dataWindow.min.y + 1) %
    854             i.channel().ySampling)
    855         {
    856         THROW (Iex::ArgExc, "Number of pixels per column in the "
    857                     "image's data window is not a multiple "
    858                     "of the y subsampling factor of "
    859                     "the \"" << i.name() << "\" channel.");
    860         }
    861     }
    862     }
    863 }
    864 
    865 
    866 void
    867 Header::setMaxImageSize (int maxWidth, int maxHeight)
    868 {
    869     maxImageWidth = maxWidth;
    870     maxImageHeight = maxHeight;
    871 }
    872 
    873 
    874 void
    875 Header::setMaxTileSize (int maxWidth, int maxHeight)
    876 {
    877     maxTileWidth = maxWidth;
    878     maxTileHeight = maxHeight;
    879 }
    880 
    881 
    882 Int64
    883 Header::writeTo (OStream &os, bool isTiled) const
    884 {
    885     //
    886     // Write a "magic number" to identify the file as an image file.
    887     // Write the current file format version number.
    888     //
    889 
    890     Xdr::write <StreamIO> (os, MAGIC);
    891 
    892     int version = EXR_VERSION;
    893 
    894     if (isTiled)
    895         version |= TILED_FLAG;
    896 
    897     if (usesLongNames (*this))
    898         version |= LONG_NAMES_FLAG;
    899 
    900     Xdr::write <StreamIO> (os, version);
    901 
    902     //
    903     // Write all attributes.  If we have a preview image attribute,
    904     // keep track of its position in the file.
    905     //
    906 
    907     Int64 previewPosition = 0;
    908 
    909     const Attribute *preview =
    910         findTypedAttribute <PreviewImageAttribute> ("preview");
    911 
    912     for (ConstIterator i = begin(); i != end(); ++i)
    913     {
    914     //
    915     // Write the attribute's name and type.
    916     //
    917 
    918     Xdr::write <StreamIO> (os, i.name());
    919     Xdr::write <StreamIO> (os, i.attribute().typeName());
    920 
    921     //
    922     // Write the size of the attribute value,
    923     // and the value itself.
    924     //
    925 
    926     StdOSStream oss;
    927     i.attribute().writeValueTo (oss, version);
    928 
    929     std::string s = oss.str();
    930     Xdr::write <StreamIO> (os, (int) s.length());
    931 
    932     if (&i.attribute() == preview)
    933         previewPosition = os.tellp();
    934 
    935     os.write (s.data(), s.length());
    936     }
    937 
    938     //
    939     // Write zero-length attribute name to mark the end of the header.
    940     //
    941 
    942     Xdr::write <StreamIO> (os, "");
    943 
    944     return previewPosition;
    945 }
    946 
    947 
    948 void
    949 Header::readFrom (IStream &is, int &version)
    950 {
    951     //
    952     // Read the magic number and the file format version number.
    953     // Then check if we can read the rest of this file.
    954     //
    955 
    956     int magic;
    957 
    958     Xdr::read <StreamIO> (is, magic);
    959     Xdr::read <StreamIO> (is, version);
    960 
    961     if (magic != MAGIC)
    962     {
    963     throw Iex::InputExc ("File is not an image file.");
    964     }
    965 
    966     if (getVersion (version) != EXR_VERSION)
    967     {
    968     THROW (Iex::InputExc, "Cannot read "
    969                   "version " << getVersion (version) << " "
    970                   "image files.  Current file format version "
    971                   "is " << EXR_VERSION << ".");
    972     }
    973 
    974     if (!supportsFlags (getFlags (version)))
    975     {
    976     THROW (Iex::InputExc, "The file format version number's flag field "
    977                   "contains unrecognized flags.");
    978     }
    979 
    980     //
    981     // Read all attributes.
    982     //
    983 
    984     while (true)
    985     {
    986     //
    987     // Read the name of the attribute.
    988     // A zero-length attribute name indicates the end of the header.
    989     //
    990 
    991     char name[Name::SIZE];
    992     Xdr::read <StreamIO> (is, Name::MAX_LENGTH, name);
    993 
    994     if (name[0] == 0)
    995         break;
    996 
    997     checkIsNullTerminated (name, "attribute name");
    998 
    999     //
   1000     // Read the attribute type and the size of the attribute value.
   1001     //
   1002 
   1003     char typeName[Name::SIZE];
   1004     int size;
   1005 
   1006     Xdr::read <StreamIO> (is, Name::MAX_LENGTH, typeName);
   1007     checkIsNullTerminated (typeName, "attribute type name");
   1008     Xdr::read <StreamIO> (is, size);
   1009 
   1010     AttributeMap::iterator i = _map.find (name);
   1011 
   1012     if (i != _map.end())
   1013     {
   1014         //
   1015         // The attribute already exists (for example,
   1016         // because it is a predefined attribute).
   1017         // Read the attribute's new value from the file.
   1018         //
   1019 
   1020         if (strncmp (i->second->typeName(), typeName, sizeof (typeName)))
   1021         THROW (Iex::InputExc, "Unexpected type for image attribute "
   1022                       "\"" << name << "\".");
   1023 
   1024         i->second->readValueFrom (is, size, version);
   1025     }
   1026     else
   1027     {
   1028         //
   1029         // The new attribute does not exist yet.
   1030         // If the attribute type is of a known type,
   1031         // read the attribute value.  If the attribute
   1032         // is of an unknown type, read its value and
   1033         // store it as an OpaqueAttribute.
   1034         //
   1035 
   1036         Attribute *attr;
   1037 
   1038         if (Attribute::knownType (typeName))
   1039         attr = Attribute::newAttribute (typeName);
   1040         else
   1041         attr = new OpaqueAttribute (typeName);
   1042 
   1043         try
   1044         {
   1045         attr->readValueFrom (is, size, version);
   1046         _map[name] = attr;
   1047         }
   1048         catch (...)
   1049         {
   1050         delete attr;
   1051         throw;
   1052         }
   1053     }
   1054     }
   1055 }
   1056 
   1057 
   1058 void
   1059 staticInitialize ()
   1060 {
   1061     static Mutex criticalSection;
   1062     Lock lock (criticalSection);
   1063 
   1064     static bool initialized = false;
   1065 
   1066     if (!initialized)
   1067     {
   1068     //
   1069     // One-time initialization -- register
   1070     // some predefined attribute types.
   1071     //
   1072 
   1073     Box2fAttribute::registerAttributeType();
   1074     Box2iAttribute::registerAttributeType();
   1075     ChannelListAttribute::registerAttributeType();
   1076     CompressionAttribute::registerAttributeType();
   1077     ChromaticitiesAttribute::registerAttributeType();
   1078     DoubleAttribute::registerAttributeType();
   1079     EnvmapAttribute::registerAttributeType();
   1080     FloatAttribute::registerAttributeType();
   1081     IntAttribute::registerAttributeType();
   1082     KeyCodeAttribute::registerAttributeType();
   1083     LineOrderAttribute::registerAttributeType();
   1084     M33dAttribute::registerAttributeType();
   1085     M33fAttribute::registerAttributeType();
   1086     M44dAttribute::registerAttributeType();
   1087     M44fAttribute::registerAttributeType();
   1088     PreviewImageAttribute::registerAttributeType();
   1089     RationalAttribute::registerAttributeType();
   1090     StringAttribute::registerAttributeType();
   1091         StringVectorAttribute::registerAttributeType();
   1092     TileDescriptionAttribute::registerAttributeType();
   1093     TimeCodeAttribute::registerAttributeType();
   1094     V2dAttribute::registerAttributeType();
   1095     V2fAttribute::registerAttributeType();
   1096     V2iAttribute::registerAttributeType();
   1097     V3dAttribute::registerAttributeType();
   1098     V3fAttribute::registerAttributeType();
   1099     V3iAttribute::registerAttributeType();
   1100 
   1101     initialized = true;
   1102     }
   1103 }
   1104 
   1105 
   1106 } // namespace Imf
   1107