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 <ImfTileDescriptionAttribute.h> 64#include <ImfTimeCodeAttribute.h> 65#include <ImfVecAttribute.h> 66#include "IlmThreadMutex.h" 67#include "Iex.h" 68#include <sstream> 69#include <stdlib.h> 70#include <time.h> 71 72 73namespace Imf { 74 75using Imath::Box2i; 76using Imath::V2i; 77using Imath::V2f; 78using IlmThread::Mutex; 79using IlmThread::Lock; 80 81 82namespace { 83 84int maxImageWidth = 0; 85int maxImageHeight = 0; 86int maxTileWidth = 0; 87int maxTileHeight = 0; 88 89 90void 91initialize (Header &header, 92 const Box2i &displayWindow, 93 const Box2i &dataWindow, 94 float pixelAspectRatio, 95 const V2f &screenWindowCenter, 96 float screenWindowWidth, 97 LineOrder lineOrder, 98 Compression compression) 99{ 100 header.insert ("displayWindow", Box2iAttribute (displayWindow)); 101 header.insert ("dataWindow", Box2iAttribute (dataWindow)); 102 header.insert ("pixelAspectRatio", FloatAttribute (pixelAspectRatio)); 103 header.insert ("screenWindowCenter", V2fAttribute (screenWindowCenter)); 104 header.insert ("screenWindowWidth", FloatAttribute (screenWindowWidth)); 105 header.insert ("lineOrder", LineOrderAttribute (lineOrder)); 106 header.insert ("compression", CompressionAttribute (compression)); 107 header.insert ("channels", ChannelListAttribute ()); 108} 109 110} // namespace 111 112 113Header::Header (int width, 114 int height, 115 float pixelAspectRatio, 116 const V2f &screenWindowCenter, 117 float screenWindowWidth, 118 LineOrder lineOrder, 119 Compression compression) 120: 121 _map() 122{ 123 staticInitialize(); 124 125 Box2i displayWindow (V2i (0, 0), V2i (width - 1, height - 1)); 126 127 initialize (*this, 128 displayWindow, 129 displayWindow, 130 pixelAspectRatio, 131 screenWindowCenter, 132 screenWindowWidth, 133 lineOrder, 134 compression); 135} 136 137 138Header::Header (int width, 139 int height, 140 const Box2i &dataWindow, 141 float pixelAspectRatio, 142 const V2f &screenWindowCenter, 143 float screenWindowWidth, 144 LineOrder lineOrder, 145 Compression compression) 146: 147 _map() 148{ 149 staticInitialize(); 150 151 Box2i displayWindow (V2i (0, 0), V2i (width - 1, height - 1)); 152 153 initialize (*this, 154 displayWindow, 155 dataWindow, 156 pixelAspectRatio, 157 screenWindowCenter, 158 screenWindowWidth, 159 lineOrder, 160 compression); 161} 162 163 164Header::Header (const Box2i &displayWindow, 165 const Box2i &dataWindow, 166 float pixelAspectRatio, 167 const V2f &screenWindowCenter, 168 float screenWindowWidth, 169 LineOrder lineOrder, 170 Compression compression) 171: 172 _map() 173{ 174 staticInitialize(); 175 176 initialize (*this, 177 displayWindow, 178 dataWindow, 179 pixelAspectRatio, 180 screenWindowCenter, 181 screenWindowWidth, 182 lineOrder, 183 compression); 184} 185 186 187Header::Header (const Header &other): _map() 188{ 189 for (AttributeMap::const_iterator i = other._map.begin(); 190 i != other._map.end(); 191 ++i) 192 { 193 insert (*i->first, *i->second); 194 } 195} 196 197 198Header::~Header () 199{ 200 for (AttributeMap::iterator i = _map.begin(); 201 i != _map.end(); 202 ++i) 203 { 204 delete i->second; 205 } 206} 207 208 209Header & 210Header::operator = (const Header &other) 211{ 212 if (this != &other) 213 { 214 for (AttributeMap::iterator i = _map.begin(); 215 i != _map.end(); 216 ++i) 217 { 218 delete i->second; 219 } 220 221 _map.erase (_map.begin(), _map.end()); 222 223 for (AttributeMap::const_iterator i = other._map.begin(); 224 i != other._map.end(); 225 ++i) 226 { 227 insert (*i->first, *i->second); 228 } 229 } 230 231 return *this; 232} 233 234 235void 236Header::insert (const char name[], const Attribute &attribute) 237{ 238 if (name[0] == 0) 239 THROW (Iex::ArgExc, "Image attribute name cannot be an empty string."); 240 241 AttributeMap::iterator i = _map.find (name); 242 243 if (i == _map.end()) 244 { 245 Attribute *tmp = attribute.copy(); 246 247 try 248 { 249 _map[name] = tmp; 250 } 251 catch (...) 252 { 253 delete tmp; 254 throw; 255 } 256 } 257 else 258 { 259 if (strcmp (i->second->typeName(), attribute.typeName())) 260 THROW (Iex::TypeExc, "Cannot assign a value of " 261 "type \"" << attribute.typeName() << "\" " 262 "to image attribute \"" << name << "\" of " 263 "type \"" << i->second->typeName() << "\"."); 264 265 Attribute *tmp = attribute.copy(); 266 delete i->second; 267 i->second = tmp; 268 } 269} 270 271 272Attribute & 273Header::operator [] (const char name[]) 274{ 275 AttributeMap::iterator i = _map.find (name); 276 277 if (i == _map.end()) 278 THROW (Iex::ArgExc, "Cannot find image attribute \"" << name << "\"."); 279 280 return *i->second; 281} 282 283 284const Attribute & 285Header::operator [] (const char name[]) const 286{ 287 AttributeMap::const_iterator i = _map.find (name); 288 289 if (i == _map.end()) 290 THROW (Iex::ArgExc, "Cannot find image attribute \"" << name << "\"."); 291 292 return *i->second; 293} 294 295 296Header::Iterator 297Header::begin () 298{ 299 return _map.begin(); 300} 301 302 303Header::ConstIterator 304Header::begin () const 305{ 306 return _map.begin(); 307} 308 309 310Header::Iterator 311Header::end () 312{ 313 return _map.end(); 314} 315 316 317Header::ConstIterator 318Header::end () const 319{ 320 return _map.end(); 321} 322 323 324Header::Iterator 325Header::find (const char name[]) 326{ 327 return _map.find (name); 328} 329 330 331Header::ConstIterator 332Header::find (const char name[]) const 333{ 334 return _map.find (name); 335} 336 337 338Imath::Box2i & 339Header::displayWindow () 340{ 341 return static_cast <Box2iAttribute &> 342 ((*this)["displayWindow"]).value(); 343} 344 345 346const Imath::Box2i & 347Header::displayWindow () const 348{ 349 return static_cast <const Box2iAttribute &> 350 ((*this)["displayWindow"]).value(); 351} 352 353Imath::Box2i & 354Header::dataWindow () 355{ 356 return static_cast <Box2iAttribute &> 357 ((*this)["dataWindow"]).value(); 358} 359 360 361const Imath::Box2i & 362Header::dataWindow () const 363{ 364 return static_cast <const Box2iAttribute &> 365 ((*this)["dataWindow"]).value(); 366} 367 368 369float & 370Header::pixelAspectRatio () 371{ 372 return static_cast <FloatAttribute &> 373 ((*this)["pixelAspectRatio"]).value(); 374} 375 376 377const float & 378Header::pixelAspectRatio () const 379{ 380 return static_cast <const FloatAttribute &> 381 ((*this)["pixelAspectRatio"]).value(); 382} 383 384 385Imath::V2f & 386Header::screenWindowCenter () 387{ 388 return static_cast <V2fAttribute &> 389 ((*this)["screenWindowCenter"]).value(); 390} 391 392 393const Imath::V2f & 394Header::screenWindowCenter () const 395{ 396 return static_cast <const V2fAttribute &> 397 ((*this)["screenWindowCenter"]).value(); 398} 399 400 401float & 402Header::screenWindowWidth () 403{ 404 return static_cast <FloatAttribute &> 405 ((*this)["screenWindowWidth"]).value(); 406} 407 408 409const float & 410Header::screenWindowWidth () const 411{ 412 return static_cast <const FloatAttribute &> 413 ((*this)["screenWindowWidth"]).value(); 414} 415 416 417ChannelList & 418Header::channels () 419{ 420 return static_cast <ChannelListAttribute &> 421 ((*this)["channels"]).value(); 422} 423 424 425const ChannelList & 426Header::channels () const 427{ 428 return static_cast <const ChannelListAttribute &> 429 ((*this)["channels"]).value(); 430} 431 432 433LineOrder & 434Header::lineOrder () 435{ 436 return static_cast <LineOrderAttribute &> 437 ((*this)["lineOrder"]).value(); 438} 439 440 441const LineOrder & 442Header::lineOrder () const 443{ 444 return static_cast <const LineOrderAttribute &> 445 ((*this)["lineOrder"]).value(); 446} 447 448 449Compression & 450Header::compression () 451{ 452 return static_cast <CompressionAttribute &> 453 ((*this)["compression"]).value(); 454} 455 456 457const Compression & 458Header::compression () const 459{ 460 return static_cast <const CompressionAttribute &> 461 ((*this)["compression"]).value(); 462} 463 464 465void 466Header::setTileDescription(const TileDescription& td) 467{ 468 insert ("tiles", TileDescriptionAttribute (td)); 469} 470 471 472bool 473Header::hasTileDescription() const 474{ 475 return findTypedAttribute <TileDescriptionAttribute> ("tiles") != 0; 476} 477 478 479TileDescription & 480Header::tileDescription () 481{ 482 return typedAttribute <TileDescriptionAttribute> ("tiles").value(); 483} 484 485 486const TileDescription & 487Header::tileDescription () const 488{ 489 return typedAttribute <TileDescriptionAttribute> ("tiles").value(); 490} 491 492void 493Header::setPreviewImage (const PreviewImage &pi) 494{ 495 insert ("preview", PreviewImageAttribute (pi)); 496} 497 498 499PreviewImage & 500Header::previewImage () 501{ 502 return typedAttribute <PreviewImageAttribute> ("preview").value(); 503} 504 505 506const PreviewImage & 507Header::previewImage () const 508{ 509 return typedAttribute <PreviewImageAttribute> ("preview").value(); 510} 511 512 513bool 514Header::hasPreviewImage () const 515{ 516 return findTypedAttribute <PreviewImageAttribute> ("preview") != 0; 517} 518 519 520void 521Header::sanityCheck (bool isTiled) const 522{ 523 // 524 // The display window and the data window must each 525 // contain at least one pixel. In addition, the 526 // coordinates of the window corners must be small 527 // enough to keep expressions like max-min+1 or 528 // max+min from overflowing. 529 // 530 531 const Box2i &displayWindow = this->displayWindow(); 532 533 if (displayWindow.min.x > displayWindow.max.x || 534 displayWindow.min.y > displayWindow.max.y || 535 displayWindow.min.x <= -(INT_MAX / 2) || 536 displayWindow.min.y <= -(INT_MAX / 2) || 537 displayWindow.max.x >= (INT_MAX / 2) || 538 displayWindow.max.y >= (INT_MAX / 2)) 539 { 540 throw Iex::ArgExc ("Invalid display window in image header."); 541 } 542 543 const Box2i &dataWindow = this->dataWindow(); 544 545 if (dataWindow.min.x > dataWindow.max.x || 546 dataWindow.min.y > dataWindow.max.y || 547 dataWindow.min.x <= -(INT_MAX / 2) || 548 dataWindow.min.y <= -(INT_MAX / 2) || 549 dataWindow.max.x >= (INT_MAX / 2) || 550 dataWindow.max.y >= (INT_MAX / 2)) 551 { 552 throw Iex::ArgExc ("Invalid data window in image header."); 553 } 554 555 if (maxImageWidth > 0 && 556 maxImageWidth < dataWindow.max.x - dataWindow.min.x + 1) 557 { 558 THROW (Iex::ArgExc, "The width of the data window exceeds the " 559 "maximum width of " << maxImageWidth << "pixels."); 560 } 561 562 if (maxImageHeight > 0 && 563 maxImageHeight < dataWindow.max.y - dataWindow.min.y + 1) 564 { 565 THROW (Iex::ArgExc, "The width of the data window exceeds the " 566 "maximum width of " << maxImageHeight << "pixels."); 567 } 568 569 // 570 // The pixel aspect ratio must be greater than 0. 571 // In applications, numbers like the the display or 572 // data window dimensions are likely to be multiplied 573 // or divided by the pixel aspect ratio; to avoid 574 // arithmetic exceptions, we limit the pixel aspect 575 // ratio to a range that is smaller than theoretically 576 // possible (real aspect ratios are likely to be close 577 // to 1.0 anyway). 578 // 579 580 float pixelAspectRatio = this->pixelAspectRatio(); 581 582 const float MIN_PIXEL_ASPECT_RATIO = 1e-6; 583 const float MAX_PIXEL_ASPECT_RATIO = 1e+6; 584 585 if (pixelAspectRatio < MIN_PIXEL_ASPECT_RATIO || 586 pixelAspectRatio > MAX_PIXEL_ASPECT_RATIO) 587 { 588 throw Iex::ArgExc ("Invalid pixel aspect ratio in image header."); 589 } 590 591 // 592 // The screen window width must not be less than 0. 593 // The size of the screen window can vary over a wide 594 // range (fish-eye lens to astronomical telescope), 595 // so we can't limit the screen window width to a 596 // small range. 597 // 598 599 float screenWindowWidth = this->screenWindowWidth(); 600 601 if (screenWindowWidth < 0) 602 throw Iex::ArgExc ("Invalid screen window width in image header."); 603 604 // 605 // If the file is tiled, verify that the tile description has resonable 606 // values and check to see if the lineOrder is one of the predefined 3. 607 // If the file is not tiled, then the lineOrder can only be INCREASING_Y 608 // or DECREASING_Y. 609 // 610 611 LineOrder lineOrder = this->lineOrder(); 612 613 if (isTiled) 614 { 615 if (!hasTileDescription()) 616 { 617 throw Iex::ArgExc ("Tiled image has no tile " 618 "description attribute."); 619 } 620 621 const TileDescription &tileDesc = tileDescription(); 622 623 if (tileDesc.xSize <= 0 || tileDesc.ySize <= 0) 624 throw Iex::ArgExc ("Invalid tile size in image header."); 625 626 if (maxTileWidth > 0 && 627 maxTileWidth < tileDesc.xSize) 628 { 629 THROW (Iex::ArgExc, "The width of the tiles exceeds the maximum " 630 "width of " << maxTileWidth << "pixels."); 631 } 632 633 if (maxTileHeight > 0 && 634 maxTileHeight < tileDesc.ySize) 635 { 636 THROW (Iex::ArgExc, "The width of the tiles exceeds the maximum " 637 "width of " << maxTileHeight << "pixels."); 638 } 639 640 if (tileDesc.mode != ONE_LEVEL && 641 tileDesc.mode != MIPMAP_LEVELS && 642 tileDesc.mode != RIPMAP_LEVELS) 643 throw Iex::ArgExc ("Invalid level mode in image header."); 644 645 if (tileDesc.roundingMode != ROUND_UP && 646 tileDesc.roundingMode != ROUND_DOWN) 647 throw Iex::ArgExc ("Invalid level rounding mode in image header."); 648 649 if (lineOrder != INCREASING_Y && 650 lineOrder != DECREASING_Y && 651 lineOrder != RANDOM_Y) 652 throw Iex::ArgExc ("Invalid line order in image header."); 653 } 654 else 655 { 656 if (lineOrder != INCREASING_Y && 657 lineOrder != DECREASING_Y) 658 throw Iex::ArgExc ("Invalid line order in image header."); 659 } 660 661 // 662 // The compression method must be one of the predefined values. 663 // 664 665 if (!isValidCompression (this->compression())) 666 throw Iex::ArgExc ("Unknown compression type in image header."); 667 668 // 669 // Check the channel list: 670 // 671 // If the file is tiled then for each channel, the type must be one of the 672 // predefined values, and the x and y sampling must both be 1. 673 // 674 // If the file is not tiled then for each channel, the type must be one 675 // of the predefined values, the x and y coordinates of the data window's 676 // upper left corner must be divisible by the x and y subsampling factors, 677 // and the width and height of the data window must be divisible by the 678 // x and y subsampling factors. 679 // 680 681 const ChannelList &channels = this->channels(); 682 683 if (isTiled) 684 { 685 for (ChannelList::ConstIterator i = channels.begin(); 686 i != channels.end(); 687 ++i) 688 { 689 if (i.channel().type != UINT && 690 i.channel().type != HALF && 691 i.channel().type != FLOAT) 692 { 693 THROW (Iex::ArgExc, "Pixel type of \"" << i.name() << "\" " 694 "image channel is invalid."); 695 } 696 697 if (i.channel().xSampling != 1) 698 { 699 THROW (Iex::ArgExc, "The x subsampling factor for the " 700 "\"" << i.name() << "\" channel " 701 "is not 1."); 702 } 703 704 if (i.channel().ySampling != 1) 705 { 706 THROW (Iex::ArgExc, "The y subsampling factor for the " 707 "\"" << i.name() << "\" channel " 708 "is not 1."); 709 } 710 } 711 } 712 else 713 { 714 for (ChannelList::ConstIterator i = channels.begin(); 715 i != channels.end(); 716 ++i) 717 { 718 if (i.channel().type != UINT && 719 i.channel().type != HALF && 720 i.channel().type != FLOAT) 721 { 722 THROW (Iex::ArgExc, "Pixel type of \"" << i.name() << "\" " 723 "image channel is invalid."); 724 } 725 726 if (i.channel().xSampling < 1) 727 { 728 THROW (Iex::ArgExc, "The x subsampling factor for the " 729 "\"" << i.name() << "\" channel " 730 "is invalid."); 731 } 732 733 if (i.channel().ySampling < 1) 734 { 735 THROW (Iex::ArgExc, "The y subsampling factor for the " 736 "\"" << i.name() << "\" channel " 737 "is invalid."); 738 } 739 740 if (dataWindow.min.x % i.channel().xSampling) 741 { 742 THROW (Iex::ArgExc, "The minimum x coordinate of the " 743 "image's data window is not a multiple " 744 "of the x subsampling factor of " 745 "the \"" << i.name() << "\" channel."); 746 } 747 748 if (dataWindow.min.y % i.channel().ySampling) 749 { 750 THROW (Iex::ArgExc, "The minimum y coordinate of the " 751 "image's data window is not a multiple " 752 "of the y subsampling factor of " 753 "the \"" << i.name() << "\" channel."); 754 } 755 756 if ((dataWindow.max.x - dataWindow.min.x + 1) % 757 i.channel().xSampling) 758 { 759 THROW (Iex::ArgExc, "Number of pixels per row in the " 760 "image's data window is not a multiple " 761 "of the x subsampling factor of " 762 "the \"" << i.name() << "\" channel."); 763 } 764 765 if ((dataWindow.max.y - dataWindow.min.y + 1) % 766 i.channel().ySampling) 767 { 768 THROW (Iex::ArgExc, "Number of pixels per column in the " 769 "image's data window is not a multiple " 770 "of the y subsampling factor of " 771 "the \"" << i.name() << "\" channel."); 772 } 773 } 774 } 775} 776 777 778void 779Header::setMaxImageSize (int maxWidth, int maxHeight) 780{ 781 maxImageWidth = maxWidth; 782 maxImageHeight = maxHeight; 783} 784 785 786void 787Header::setMaxTileSize (int maxWidth, int maxHeight) 788{ 789 maxTileWidth = maxWidth; 790 maxTileHeight = maxHeight; 791} 792 793 794Int64 795Header::writeTo (OStream &os, bool isTiled) const 796{ 797 // 798 // Write a "magic number" to identify the file as an image file. 799 // Write the current file format version number. 800 // 801 802 Xdr::write <StreamIO> (os, MAGIC); 803 804 int version = isTiled ? makeTiled (EXR_VERSION) : EXR_VERSION; 805 Xdr::write <StreamIO> (os, version); 806 807 // 808 // Write all attributes. If we have a preview image attribute, 809 // keep track of its position in the file. 810 // 811 812 Int64 previewPosition = 0; 813 814 const Attribute *preview = 815 findTypedAttribute <PreviewImageAttribute> ("preview"); 816 817 for (ConstIterator i = begin(); i != end(); ++i) 818 { 819 // 820 // Write the attribute's name and type. 821 // 822 823 Xdr::write <StreamIO> (os, i.name()); 824 Xdr::write <StreamIO> (os, i.attribute().typeName()); 825 826 // 827 // Write the size of the attribute value, 828 // and the value itself. 829 // 830 831 StdOSStream oss; 832 i.attribute().writeValueTo (oss, version); 833 834 std::string s = oss.str(); 835 Xdr::write <StreamIO> (os, (int) s.length()); 836 837 if (&i.attribute() == preview) 838 previewPosition = os.tellp(); 839 840 os.write (s.data(), s.length()); 841 } 842 843 // 844 // Write zero-length attribute name to mark the end of the header. 845 // 846 847 Xdr::write <StreamIO> (os, ""); 848 849 return previewPosition; 850} 851 852 853void 854Header::readFrom (IStream &is, int &version) 855{ 856 // 857 // Read the magic number and the file format version number. 858 // Then check if we can read the rest of this file. 859 // 860 861 int magic; 862 863 Xdr::read <StreamIO> (is, magic); 864 Xdr::read <StreamIO> (is, version); 865 866 if (magic != MAGIC) 867 { 868 throw Iex::InputExc ("File is not an image file."); 869 } 870 871 if (getVersion (version) != EXR_VERSION) 872 { 873 THROW (Iex::InputExc, "Cannot read " 874 "version " << getVersion (version) << " " 875 "image files. Current file format version " 876 "is " << EXR_VERSION << "."); 877 } 878 879 if (!supportsFlags (getFlags (version))) 880 { 881 THROW (Iex::InputExc, "The file format version number's flag field " 882 "contains unrecognized flags."); 883 } 884 885 // 886 // Read all attributes. 887 // 888 889 while (true) 890 { 891 // 892 // Read the name of the attribute. 893 // A zero-length attribute name indicates the end of the header. 894 // 895 896 char name[100]; 897 Xdr::read <StreamIO> (is, sizeof (name), name); 898 899 if (name[0] == 0) 900 break; 901 902 // 903 // Read the attribute type and the size of the attribute value. 904 // 905 906 char typeName[100]; 907 int size; 908 909 Xdr::read <StreamIO> (is, sizeof (typeName), typeName); 910 Xdr::read <StreamIO> (is, size); 911 912 AttributeMap::iterator i = _map.find (name); 913 914 if (i != _map.end()) 915 { 916 // 917 // The attribute already exists (for example, 918 // because it is a predefined attribute). 919 // Read the attribute's new value from the file. 920 // 921 922 if (strncmp (i->second->typeName(), typeName, sizeof (typeName))) 923 THROW (Iex::InputExc, "Unexpected type for image attribute " 924 "\"" << name << "\"."); 925 926 i->second->readValueFrom (is, size, version); 927 } 928 else 929 { 930 // 931 // The new attribute does not exist yet. 932 // If the attribute type is of a known type, 933 // read the attribute value. If the attribute 934 // is of an unknown type, read its value and 935 // store it as an OpaqueAttribute. 936 // 937 938 Attribute *attr; 939 940 if (Attribute::knownType (typeName)) 941 attr = Attribute::newAttribute (typeName); 942 else 943 attr = new OpaqueAttribute (typeName); 944 945 try 946 { 947 attr->readValueFrom (is, size, version); 948 _map[name] = attr; 949 } 950 catch (...) 951 { 952 delete attr; 953 throw; 954 } 955 } 956 } 957} 958 959 960void 961staticInitialize () 962{ 963 static Mutex criticalSection; 964 Lock lock (criticalSection); 965 966 static bool initialized = false; 967 968 if (!initialized) 969 { 970 // 971 // One-time initialization -- register 972 // some predefined attribute types. 973 // 974 975 Box2fAttribute::registerAttributeType(); 976 Box2iAttribute::registerAttributeType(); 977 ChannelListAttribute::registerAttributeType(); 978 CompressionAttribute::registerAttributeType(); 979 ChromaticitiesAttribute::registerAttributeType(); 980 DoubleAttribute::registerAttributeType(); 981 EnvmapAttribute::registerAttributeType(); 982 FloatAttribute::registerAttributeType(); 983 IntAttribute::registerAttributeType(); 984 KeyCodeAttribute::registerAttributeType(); 985 LineOrderAttribute::registerAttributeType(); 986 M33fAttribute::registerAttributeType(); 987 M44fAttribute::registerAttributeType(); 988 PreviewImageAttribute::registerAttributeType(); 989 RationalAttribute::registerAttributeType(); 990 StringAttribute::registerAttributeType(); 991 TileDescriptionAttribute::registerAttributeType(); 992 TimeCodeAttribute::registerAttributeType(); 993 V2fAttribute::registerAttributeType(); 994 V2iAttribute::registerAttributeType(); 995 V3fAttribute::registerAttributeType(); 996 V3iAttribute::registerAttributeType(); 997 998 initialized = true; 999 } 1000} 1001 1002 1003} // namespace Imf 1004