PageRenderTime 85ms CodeModel.GetById 16ms app.highlight 62ms RepoModel.GetById 1ms app.codeStats 0ms

/src/FreeImage/Source/OpenEXR/IlmImf/ImfHeader.cpp

https://bitbucket.org/cabalistic/ogredeps/
C++ | 1106 lines | 749 code | 215 blank | 142 comment | 100 complexity | 82185b86a8a374a82711b5c49eeab256 MD5 | raw file
   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
  74namespace Imf {
  75
  76using namespace std;
  77using Imath::Box2i;
  78using Imath::V2i;
  79using Imath::V2f;
  80using IlmThread::Mutex;
  81using IlmThread::Lock;
  82
  83
  84namespace {
  85
  86int maxImageWidth = 0;
  87int maxImageHeight = 0;
  88int maxTileWidth = 0;
  89int maxTileHeight = 0;
  90
  91
  92void
  93initialize (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
 113bool
 114usesLongNames (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
 149template <size_t N>
 150void 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
 165Header::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
 190Header::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
 216Header::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
 239Header::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
 250Header::~Header ()
 251{
 252    for (AttributeMap::iterator i = _map.begin();
 253	 i != _map.end();
 254	 ++i)
 255    {
 256	 delete i->second;
 257    }
 258}
 259
 260
 261Header &		
 262Header::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
 287void
 288Header::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
 324void
 325Header::insert (const string &name, const Attribute &attribute)
 326{
 327    insert (name.c_str(), attribute);
 328}
 329
 330
 331Attribute &		
 332Header::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
 343const Attribute &	
 344Header::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
 355Attribute &		
 356Header::operator [] (const string &name)
 357{
 358    return this->operator[] (name.c_str());
 359}
 360
 361
 362const Attribute &	
 363Header::operator [] (const string &name) const
 364{
 365    return this->operator[] (name.c_str());
 366}
 367
 368
 369Header::Iterator
 370Header::begin ()
 371{
 372    return _map.begin();
 373}
 374
 375
 376Header::ConstIterator
 377Header::begin () const
 378{
 379    return _map.begin();
 380}
 381
 382
 383Header::Iterator
 384Header::end ()
 385{
 386    return _map.end();
 387}
 388
 389
 390Header::ConstIterator
 391Header::end () const
 392{
 393    return _map.end();
 394}
 395
 396
 397Header::Iterator
 398Header::find (const char name[])
 399{
 400    return _map.find (name);
 401}
 402
 403
 404Header::ConstIterator
 405Header::find (const char name[]) const
 406{
 407    return _map.find (name);
 408}
 409
 410
 411Header::Iterator
 412Header::find (const string &name)
 413{
 414    return find (name.c_str());
 415}
 416
 417
 418Header::ConstIterator
 419Header::find (const string &name) const
 420{
 421    return find (name.c_str());
 422}
 423
 424
 425Imath::Box2i &	
 426Header::displayWindow ()
 427{
 428    return static_cast <Box2iAttribute &>
 429	((*this)["displayWindow"]).value();
 430}
 431
 432
 433const Imath::Box2i &
 434Header::displayWindow () const
 435{
 436    return static_cast <const Box2iAttribute &>
 437	((*this)["displayWindow"]).value();
 438}
 439
 440
 441Imath::Box2i &	
 442Header::dataWindow ()
 443{
 444    return static_cast <Box2iAttribute &>
 445	((*this)["dataWindow"]).value();
 446}
 447
 448
 449const Imath::Box2i &
 450Header::dataWindow () const
 451{
 452    return static_cast <const Box2iAttribute &>
 453	((*this)["dataWindow"]).value();
 454}
 455
 456
 457float &		
 458Header::pixelAspectRatio ()
 459{
 460    return static_cast <FloatAttribute &>
 461	((*this)["pixelAspectRatio"]).value();
 462}
 463
 464
 465const float &	
 466Header::pixelAspectRatio () const
 467{
 468    return static_cast <const FloatAttribute &>
 469	((*this)["pixelAspectRatio"]).value();
 470}
 471
 472
 473Imath::V2f &	
 474Header::screenWindowCenter ()
 475{
 476    return static_cast <V2fAttribute &>
 477	((*this)["screenWindowCenter"]).value();
 478}
 479
 480
 481const Imath::V2f &	
 482Header::screenWindowCenter () const
 483{
 484    return static_cast <const V2fAttribute &>
 485	((*this)["screenWindowCenter"]).value();
 486}
 487
 488
 489float &		
 490Header::screenWindowWidth ()
 491{
 492    return static_cast <FloatAttribute &>
 493	((*this)["screenWindowWidth"]).value();
 494}
 495
 496
 497const float &	
 498Header::screenWindowWidth () const
 499{
 500    return static_cast <const FloatAttribute &>
 501	((*this)["screenWindowWidth"]).value();
 502}
 503
 504
 505ChannelList &	
 506Header::channels ()
 507{
 508    return static_cast <ChannelListAttribute &>
 509	((*this)["channels"]).value();
 510}
 511
 512
 513const ChannelList &	
 514Header::channels () const
 515{
 516    return static_cast <const ChannelListAttribute &>
 517	((*this)["channels"]).value();
 518}
 519
 520
 521LineOrder &
 522Header::lineOrder ()
 523{
 524    return static_cast <LineOrderAttribute &>
 525	((*this)["lineOrder"]).value();
 526}
 527
 528
 529const LineOrder &
 530Header::lineOrder () const
 531{
 532    return static_cast <const LineOrderAttribute &>
 533	((*this)["lineOrder"]).value();
 534}
 535
 536
 537Compression &
 538Header::compression ()
 539{
 540    return static_cast <CompressionAttribute &>
 541	((*this)["compression"]).value();
 542}
 543
 544
 545const Compression &
 546Header::compression () const
 547{
 548    return static_cast <const CompressionAttribute &>
 549	((*this)["compression"]).value();
 550}
 551
 552
 553void
 554Header::setTileDescription(const TileDescription& td)
 555{
 556    insert ("tiles", TileDescriptionAttribute (td));
 557}
 558
 559
 560bool
 561Header::hasTileDescription() const
 562{
 563    return findTypedAttribute <TileDescriptionAttribute> ("tiles") != 0;
 564}
 565
 566
 567TileDescription &
 568Header::tileDescription ()
 569{
 570    return typedAttribute <TileDescriptionAttribute> ("tiles").value();
 571}
 572
 573
 574const TileDescription &
 575Header::tileDescription () const
 576{
 577    return typedAttribute <TileDescriptionAttribute> ("tiles").value();
 578}
 579
 580void		
 581Header::setPreviewImage (const PreviewImage &pi)
 582{
 583    insert ("preview", PreviewImageAttribute (pi));
 584}
 585
 586
 587PreviewImage &
 588Header::previewImage ()
 589{
 590    return typedAttribute <PreviewImageAttribute> ("preview").value();
 591}
 592
 593
 594const PreviewImage &
 595Header::previewImage () const
 596{
 597    return typedAttribute <PreviewImageAttribute> ("preview").value();
 598}
 599
 600
 601bool		
 602Header::hasPreviewImage () const
 603{
 604    return findTypedAttribute <PreviewImageAttribute> ("preview") != 0;
 605}
 606
 607
 608void		
 609Header::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
 866void		
 867Header::setMaxImageSize (int maxWidth, int maxHeight)
 868{
 869    maxImageWidth = maxWidth;
 870    maxImageHeight = maxHeight;
 871}
 872
 873
 874void		
 875Header::setMaxTileSize (int maxWidth, int maxHeight)
 876{
 877    maxTileWidth = maxWidth;
 878    maxTileHeight = maxHeight;
 879}
 880
 881
 882Int64
 883Header::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
 948void
 949Header::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
1058void
1059staticInitialize ()
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