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