| /////////////////////////////////////////////////////////////////////////// |
| // |
| // Copyright (c) 2004, Industrial Light & Magic, a division of Lucas |
| // Digital Ltd. LLC |
| // |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Industrial Light & Magic nor the names of |
| // its contributors may be used to endorse or promote products derived |
| // from this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| // |
| /////////////////////////////////////////////////////////////////////////// |
| |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // class Header |
| // |
| //----------------------------------------------------------------------------- |
| |
| #include <ImfHeader.h> |
| #include <ImfStdIO.h> |
| #include <ImfVersion.h> |
| #include <ImfCompressor.h> |
| #include <ImfMisc.h> |
| #include <ImfBoxAttribute.h> |
| #include <ImfChannelListAttribute.h> |
| #include <ImfChromaticitiesAttribute.h> |
| #include <ImfCompressionAttribute.h> |
| #include <ImfDoubleAttribute.h> |
| #include <ImfEnvmapAttribute.h> |
| #include <ImfFloatAttribute.h> |
| #include <ImfIntAttribute.h> |
| #include <ImfKeyCodeAttribute.h> |
| #include <ImfLineOrderAttribute.h> |
| #include <ImfMatrixAttribute.h> |
| #include <ImfOpaqueAttribute.h> |
| #include <ImfPreviewImageAttribute.h> |
| #include <ImfRationalAttribute.h> |
| #include <ImfStringAttribute.h> |
| #include <ImfTileDescriptionAttribute.h> |
| #include <ImfTimeCodeAttribute.h> |
| #include <ImfVecAttribute.h> |
| #include "IlmThreadMutex.h" |
| #include "Iex.h" |
| #include <sstream> |
| #include <stdlib.h> |
| #include <time.h> |
| |
| |
| namespace Imf { |
| |
| using Imath::Box2i; |
| using Imath::V2i; |
| using Imath::V2f; |
| using IlmThread::Mutex; |
| using IlmThread::Lock; |
| |
| |
| namespace { |
| |
| int maxImageWidth = 0; |
| int maxImageHeight = 0; |
| int maxTileWidth = 0; |
| int maxTileHeight = 0; |
| |
| |
| void |
| initialize (Header &header, |
| const Box2i &displayWindow, |
| const Box2i &dataWindow, |
| float pixelAspectRatio, |
| const V2f &screenWindowCenter, |
| float screenWindowWidth, |
| LineOrder lineOrder, |
| Compression compression) |
| { |
| header.insert ("displayWindow", Box2iAttribute (displayWindow)); |
| header.insert ("dataWindow", Box2iAttribute (dataWindow)); |
| header.insert ("pixelAspectRatio", FloatAttribute (pixelAspectRatio)); |
| header.insert ("screenWindowCenter", V2fAttribute (screenWindowCenter)); |
| header.insert ("screenWindowWidth", FloatAttribute (screenWindowWidth)); |
| header.insert ("lineOrder", LineOrderAttribute (lineOrder)); |
| header.insert ("compression", CompressionAttribute (compression)); |
| header.insert ("channels", ChannelListAttribute ()); |
| } |
| |
| } // namespace |
| |
| |
| Header::Header (int width, |
| int height, |
| float pixelAspectRatio, |
| const V2f &screenWindowCenter, |
| float screenWindowWidth, |
| LineOrder lineOrder, |
| Compression compression) |
| : |
| _map() |
| { |
| staticInitialize(); |
| |
| Box2i displayWindow (V2i (0, 0), V2i (width - 1, height - 1)); |
| |
| initialize (*this, |
| displayWindow, |
| displayWindow, |
| pixelAspectRatio, |
| screenWindowCenter, |
| screenWindowWidth, |
| lineOrder, |
| compression); |
| } |
| |
| |
| Header::Header (int width, |
| int height, |
| const Box2i &dataWindow, |
| float pixelAspectRatio, |
| const V2f &screenWindowCenter, |
| float screenWindowWidth, |
| LineOrder lineOrder, |
| Compression compression) |
| : |
| _map() |
| { |
| staticInitialize(); |
| |
| Box2i displayWindow (V2i (0, 0), V2i (width - 1, height - 1)); |
| |
| initialize (*this, |
| displayWindow, |
| dataWindow, |
| pixelAspectRatio, |
| screenWindowCenter, |
| screenWindowWidth, |
| lineOrder, |
| compression); |
| } |
| |
| |
| Header::Header (const Box2i &displayWindow, |
| const Box2i &dataWindow, |
| float pixelAspectRatio, |
| const V2f &screenWindowCenter, |
| float screenWindowWidth, |
| LineOrder lineOrder, |
| Compression compression) |
| : |
| _map() |
| { |
| staticInitialize(); |
| |
| initialize (*this, |
| displayWindow, |
| dataWindow, |
| pixelAspectRatio, |
| screenWindowCenter, |
| screenWindowWidth, |
| lineOrder, |
| compression); |
| } |
| |
| |
| Header::Header (const Header &other): _map() |
| { |
| for (AttributeMap::const_iterator i = other._map.begin(); |
| i != other._map.end(); |
| ++i) |
| { |
| insert (*i->first, *i->second); |
| } |
| } |
| |
| |
| Header::~Header () |
| { |
| for (AttributeMap::iterator i = _map.begin(); |
| i != _map.end(); |
| ++i) |
| { |
| delete i->second; |
| } |
| } |
| |
| |
| Header & |
| Header::operator = (const Header &other) |
| { |
| if (this != &other) |
| { |
| for (AttributeMap::iterator i = _map.begin(); |
| i != _map.end(); |
| ++i) |
| { |
| delete i->second; |
| } |
| |
| _map.erase (_map.begin(), _map.end()); |
| |
| for (AttributeMap::const_iterator i = other._map.begin(); |
| i != other._map.end(); |
| ++i) |
| { |
| insert (*i->first, *i->second); |
| } |
| } |
| |
| return *this; |
| } |
| |
| |
| void |
| Header::insert (const char name[], const Attribute &attribute) |
| { |
| if (name[0] == 0) |
| THROW (Iex::ArgExc, "Image attribute name cannot be an empty string."); |
| |
| AttributeMap::iterator i = _map.find (name); |
| |
| if (i == _map.end()) |
| { |
| Attribute *tmp = attribute.copy(); |
| |
| try |
| { |
| _map[name] = tmp; |
| } |
| catch (...) |
| { |
| delete tmp; |
| throw; |
| } |
| } |
| else |
| { |
| if (strcmp (i->second->typeName(), attribute.typeName())) |
| THROW (Iex::TypeExc, "Cannot assign a value of " |
| "type \"" << attribute.typeName() << "\" " |
| "to image attribute \"" << name << "\" of " |
| "type \"" << i->second->typeName() << "\"."); |
| |
| Attribute *tmp = attribute.copy(); |
| delete i->second; |
| i->second = tmp; |
| } |
| } |
| |
| |
| Attribute & |
| Header::operator [] (const char name[]) |
| { |
| AttributeMap::iterator i = _map.find (name); |
| |
| if (i == _map.end()) |
| THROW (Iex::ArgExc, "Cannot find image attribute \"" << name << "\"."); |
| |
| return *i->second; |
| } |
| |
| |
| const Attribute & |
| Header::operator [] (const char name[]) const |
| { |
| AttributeMap::const_iterator i = _map.find (name); |
| |
| if (i == _map.end()) |
| THROW (Iex::ArgExc, "Cannot find image attribute \"" << name << "\"."); |
| |
| return *i->second; |
| } |
| |
| |
| Header::Iterator |
| Header::begin () |
| { |
| return _map.begin(); |
| } |
| |
| |
| Header::ConstIterator |
| Header::begin () const |
| { |
| return _map.begin(); |
| } |
| |
| |
| Header::Iterator |
| Header::end () |
| { |
| return _map.end(); |
| } |
| |
| |
| Header::ConstIterator |
| Header::end () const |
| { |
| return _map.end(); |
| } |
| |
| |
| Header::Iterator |
| Header::find (const char name[]) |
| { |
| return _map.find (name); |
| } |
| |
| |
| Header::ConstIterator |
| Header::find (const char name[]) const |
| { |
| return _map.find (name); |
| } |
| |
| |
| Imath::Box2i & |
| Header::displayWindow () |
| { |
| return static_cast <Box2iAttribute &> |
| ((*this)["displayWindow"]).value(); |
| } |
| |
| |
| const Imath::Box2i & |
| Header::displayWindow () const |
| { |
| return static_cast <const Box2iAttribute &> |
| ((*this)["displayWindow"]).value(); |
| } |
| |
| Imath::Box2i & |
| Header::dataWindow () |
| { |
| return static_cast <Box2iAttribute &> |
| ((*this)["dataWindow"]).value(); |
| } |
| |
| |
| const Imath::Box2i & |
| Header::dataWindow () const |
| { |
| return static_cast <const Box2iAttribute &> |
| ((*this)["dataWindow"]).value(); |
| } |
| |
| |
| float & |
| Header::pixelAspectRatio () |
| { |
| return static_cast <FloatAttribute &> |
| ((*this)["pixelAspectRatio"]).value(); |
| } |
| |
| |
| const float & |
| Header::pixelAspectRatio () const |
| { |
| return static_cast <const FloatAttribute &> |
| ((*this)["pixelAspectRatio"]).value(); |
| } |
| |
| |
| Imath::V2f & |
| Header::screenWindowCenter () |
| { |
| return static_cast <V2fAttribute &> |
| ((*this)["screenWindowCenter"]).value(); |
| } |
| |
| |
| const Imath::V2f & |
| Header::screenWindowCenter () const |
| { |
| return static_cast <const V2fAttribute &> |
| ((*this)["screenWindowCenter"]).value(); |
| } |
| |
| |
| float & |
| Header::screenWindowWidth () |
| { |
| return static_cast <FloatAttribute &> |
| ((*this)["screenWindowWidth"]).value(); |
| } |
| |
| |
| const float & |
| Header::screenWindowWidth () const |
| { |
| return static_cast <const FloatAttribute &> |
| ((*this)["screenWindowWidth"]).value(); |
| } |
| |
| |
| ChannelList & |
| Header::channels () |
| { |
| return static_cast <ChannelListAttribute &> |
| ((*this)["channels"]).value(); |
| } |
| |
| |
| const ChannelList & |
| Header::channels () const |
| { |
| return static_cast <const ChannelListAttribute &> |
| ((*this)["channels"]).value(); |
| } |
| |
| |
| LineOrder & |
| Header::lineOrder () |
| { |
| return static_cast <LineOrderAttribute &> |
| ((*this)["lineOrder"]).value(); |
| } |
| |
| |
| const LineOrder & |
| Header::lineOrder () const |
| { |
| return static_cast <const LineOrderAttribute &> |
| ((*this)["lineOrder"]).value(); |
| } |
| |
| |
| Compression & |
| Header::compression () |
| { |
| return static_cast <CompressionAttribute &> |
| ((*this)["compression"]).value(); |
| } |
| |
| |
| const Compression & |
| Header::compression () const |
| { |
| return static_cast <const CompressionAttribute &> |
| ((*this)["compression"]).value(); |
| } |
| |
| |
| void |
| Header::setTileDescription(const TileDescription& td) |
| { |
| insert ("tiles", TileDescriptionAttribute (td)); |
| } |
| |
| |
| bool |
| Header::hasTileDescription() const |
| { |
| return findTypedAttribute <TileDescriptionAttribute> ("tiles") != 0; |
| } |
| |
| |
| TileDescription & |
| Header::tileDescription () |
| { |
| return typedAttribute <TileDescriptionAttribute> ("tiles").value(); |
| } |
| |
| |
| const TileDescription & |
| Header::tileDescription () const |
| { |
| return typedAttribute <TileDescriptionAttribute> ("tiles").value(); |
| } |
| |
| void |
| Header::setPreviewImage (const PreviewImage &pi) |
| { |
| insert ("preview", PreviewImageAttribute (pi)); |
| } |
| |
| |
| PreviewImage & |
| Header::previewImage () |
| { |
| return typedAttribute <PreviewImageAttribute> ("preview").value(); |
| } |
| |
| |
| const PreviewImage & |
| Header::previewImage () const |
| { |
| return typedAttribute <PreviewImageAttribute> ("preview").value(); |
| } |
| |
| |
| bool |
| Header::hasPreviewImage () const |
| { |
| return findTypedAttribute <PreviewImageAttribute> ("preview") != 0; |
| } |
| |
| |
| void |
| Header::sanityCheck (bool isTiled) const |
| { |
| // |
| // The display window and the data window must each |
| // contain at least one pixel. In addition, the |
| // coordinates of the window corners must be small |
| // enough to keep expressions like max-min+1 or |
| // max+min from overflowing. |
| // |
| |
| const Box2i &displayWindow = this->displayWindow(); |
| |
| if (displayWindow.min.x > displayWindow.max.x || |
| displayWindow.min.y > displayWindow.max.y || |
| displayWindow.min.x <= -(INT_MAX / 2) || |
| displayWindow.min.y <= -(INT_MAX / 2) || |
| displayWindow.max.x >= (INT_MAX / 2) || |
| displayWindow.max.y >= (INT_MAX / 2)) |
| { |
| throw Iex::ArgExc ("Invalid display window in image header."); |
| } |
| |
| const Box2i &dataWindow = this->dataWindow(); |
| |
| if (dataWindow.min.x > dataWindow.max.x || |
| dataWindow.min.y > dataWindow.max.y || |
| dataWindow.min.x <= -(INT_MAX / 2) || |
| dataWindow.min.y <= -(INT_MAX / 2) || |
| dataWindow.max.x >= (INT_MAX / 2) || |
| dataWindow.max.y >= (INT_MAX / 2)) |
| { |
| throw Iex::ArgExc ("Invalid data window in image header."); |
| } |
| |
| if (maxImageWidth > 0 && |
| maxImageWidth < dataWindow.max.x - dataWindow.min.x + 1) |
| { |
| THROW (Iex::ArgExc, "The width of the data window exceeds the " |
| "maximum width of " << maxImageWidth << "pixels."); |
| } |
| |
| if (maxImageHeight > 0 && |
| maxImageHeight < dataWindow.max.y - dataWindow.min.y + 1) |
| { |
| THROW (Iex::ArgExc, "The width of the data window exceeds the " |
| "maximum width of " << maxImageHeight << "pixels."); |
| } |
| |
| // |
| // The pixel aspect ratio must be greater than 0. |
| // In applications, numbers like the the display or |
| // data window dimensions are likely to be multiplied |
| // or divided by the pixel aspect ratio; to avoid |
| // arithmetic exceptions, we limit the pixel aspect |
| // ratio to a range that is smaller than theoretically |
| // possible (real aspect ratios are likely to be close |
| // to 1.0 anyway). |
| // |
| |
| float pixelAspectRatio = this->pixelAspectRatio(); |
| |
| const float MIN_PIXEL_ASPECT_RATIO = 1e-6; |
| const float MAX_PIXEL_ASPECT_RATIO = 1e+6; |
| |
| if (pixelAspectRatio < MIN_PIXEL_ASPECT_RATIO || |
| pixelAspectRatio > MAX_PIXEL_ASPECT_RATIO) |
| { |
| throw Iex::ArgExc ("Invalid pixel aspect ratio in image header."); |
| } |
| |
| // |
| // The screen window width must not be less than 0. |
| // The size of the screen window can vary over a wide |
| // range (fish-eye lens to astronomical telescope), |
| // so we can't limit the screen window width to a |
| // small range. |
| // |
| |
| float screenWindowWidth = this->screenWindowWidth(); |
| |
| if (screenWindowWidth < 0) |
| throw Iex::ArgExc ("Invalid screen window width in image header."); |
| |
| // |
| // If the file is tiled, verify that the tile description has resonable |
| // values and check to see if the lineOrder is one of the predefined 3. |
| // If the file is not tiled, then the lineOrder can only be INCREASING_Y |
| // or DECREASING_Y. |
| // |
| |
| LineOrder lineOrder = this->lineOrder(); |
| |
| if (isTiled) |
| { |
| if (!hasTileDescription()) |
| { |
| throw Iex::ArgExc ("Tiled image has no tile " |
| "description attribute."); |
| } |
| |
| const TileDescription &tileDesc = tileDescription(); |
| |
| if (tileDesc.xSize <= 0 || tileDesc.ySize <= 0) |
| throw Iex::ArgExc ("Invalid tile size in image header."); |
| |
| if (maxTileWidth > 0 && |
| maxTileWidth < tileDesc.xSize) |
| { |
| THROW (Iex::ArgExc, "The width of the tiles exceeds the maximum " |
| "width of " << maxTileWidth << "pixels."); |
| } |
| |
| if (maxTileHeight > 0 && |
| maxTileHeight < tileDesc.ySize) |
| { |
| THROW (Iex::ArgExc, "The width of the tiles exceeds the maximum " |
| "width of " << maxTileHeight << "pixels."); |
| } |
| |
| if (tileDesc.mode != ONE_LEVEL && |
| tileDesc.mode != MIPMAP_LEVELS && |
| tileDesc.mode != RIPMAP_LEVELS) |
| throw Iex::ArgExc ("Invalid level mode in image header."); |
| |
| if (tileDesc.roundingMode != ROUND_UP && |
| tileDesc.roundingMode != ROUND_DOWN) |
| throw Iex::ArgExc ("Invalid level rounding mode in image header."); |
| |
| if (lineOrder != INCREASING_Y && |
| lineOrder != DECREASING_Y && |
| lineOrder != RANDOM_Y) |
| throw Iex::ArgExc ("Invalid line order in image header."); |
| } |
| else |
| { |
| if (lineOrder != INCREASING_Y && |
| lineOrder != DECREASING_Y) |
| throw Iex::ArgExc ("Invalid line order in image header."); |
| } |
| |
| // |
| // The compression method must be one of the predefined values. |
| // |
| |
| if (!isValidCompression (this->compression())) |
| throw Iex::ArgExc ("Unknown compression type in image header."); |
| |
| // |
| // Check the channel list: |
| // |
| // If the file is tiled then for each channel, the type must be one of the |
| // predefined values, and the x and y sampling must both be 1. |
| // |
| // If the file is not tiled then for each channel, the type must be one |
| // of the predefined values, the x and y coordinates of the data window's |
| // upper left corner must be divisible by the x and y subsampling factors, |
| // and the width and height of the data window must be divisible by the |
| // x and y subsampling factors. |
| // |
| |
| const ChannelList &channels = this->channels(); |
| |
| if (isTiled) |
| { |
| for (ChannelList::ConstIterator i = channels.begin(); |
| i != channels.end(); |
| ++i) |
| { |
| if (i.channel().type != UINT && |
| i.channel().type != HALF && |
| i.channel().type != FLOAT) |
| { |
| THROW (Iex::ArgExc, "Pixel type of \"" << i.name() << "\" " |
| "image channel is invalid."); |
| } |
| |
| if (i.channel().xSampling != 1) |
| { |
| THROW (Iex::ArgExc, "The x subsampling factor for the " |
| "\"" << i.name() << "\" channel " |
| "is not 1."); |
| } |
| |
| if (i.channel().ySampling != 1) |
| { |
| THROW (Iex::ArgExc, "The y subsampling factor for the " |
| "\"" << i.name() << "\" channel " |
| "is not 1."); |
| } |
| } |
| } |
| else |
| { |
| for (ChannelList::ConstIterator i = channels.begin(); |
| i != channels.end(); |
| ++i) |
| { |
| if (i.channel().type != UINT && |
| i.channel().type != HALF && |
| i.channel().type != FLOAT) |
| { |
| THROW (Iex::ArgExc, "Pixel type of \"" << i.name() << "\" " |
| "image channel is invalid."); |
| } |
| |
| if (i.channel().xSampling < 1) |
| { |
| THROW (Iex::ArgExc, "The x subsampling factor for the " |
| "\"" << i.name() << "\" channel " |
| "is invalid."); |
| } |
| |
| if (i.channel().ySampling < 1) |
| { |
| THROW (Iex::ArgExc, "The y subsampling factor for the " |
| "\"" << i.name() << "\" channel " |
| "is invalid."); |
| } |
| |
| if (dataWindow.min.x % i.channel().xSampling) |
| { |
| THROW (Iex::ArgExc, "The minimum x coordinate of the " |
| "image's data window is not a multiple " |
| "of the x subsampling factor of " |
| "the \"" << i.name() << "\" channel."); |
| } |
| |
| if (dataWindow.min.y % i.channel().ySampling) |
| { |
| THROW (Iex::ArgExc, "The minimum y coordinate of the " |
| "image's data window is not a multiple " |
| "of the y subsampling factor of " |
| "the \"" << i.name() << "\" channel."); |
| } |
| |
| if ((dataWindow.max.x - dataWindow.min.x + 1) % |
| i.channel().xSampling) |
| { |
| THROW (Iex::ArgExc, "Number of pixels per row in the " |
| "image's data window is not a multiple " |
| "of the x subsampling factor of " |
| "the \"" << i.name() << "\" channel."); |
| } |
| |
| if ((dataWindow.max.y - dataWindow.min.y + 1) % |
| i.channel().ySampling) |
| { |
| THROW (Iex::ArgExc, "Number of pixels per column in the " |
| "image's data window is not a multiple " |
| "of the y subsampling factor of " |
| "the \"" << i.name() << "\" channel."); |
| } |
| } |
| } |
| } |
| |
| |
| void |
| Header::setMaxImageSize (int maxWidth, int maxHeight) |
| { |
| maxImageWidth = maxWidth; |
| maxImageHeight = maxHeight; |
| } |
| |
| |
| void |
| Header::setMaxTileSize (int maxWidth, int maxHeight) |
| { |
| maxTileWidth = maxWidth; |
| maxTileHeight = maxHeight; |
| } |
| |
| |
| Int64 |
| Header::writeTo (OStream &os, bool isTiled) const |
| { |
| // |
| // Write a "magic number" to identify the file as an image file. |
| // Write the current file format version number. |
| // |
| |
| Xdr::write <StreamIO> (os, MAGIC); |
| |
| int version = isTiled ? makeTiled (EXR_VERSION) : EXR_VERSION; |
| Xdr::write <StreamIO> (os, version); |
| |
| // |
| // Write all attributes. If we have a preview image attribute, |
| // keep track of its position in the file. |
| // |
| |
| Int64 previewPosition = 0; |
| |
| const Attribute *preview = |
| findTypedAttribute <PreviewImageAttribute> ("preview"); |
| |
| for (ConstIterator i = begin(); i != end(); ++i) |
| { |
| // |
| // Write the attribute's name and type. |
| // |
| |
| Xdr::write <StreamIO> (os, i.name()); |
| Xdr::write <StreamIO> (os, i.attribute().typeName()); |
| |
| // |
| // Write the size of the attribute value, |
| // and the value itself. |
| // |
| |
| StdOSStream oss; |
| i.attribute().writeValueTo (oss, version); |
| |
| std::string s = oss.str(); |
| Xdr::write <StreamIO> (os, (int) s.length()); |
| |
| if (&i.attribute() == preview) |
| previewPosition = os.tellp(); |
| |
| os.write (s.data(), s.length()); |
| } |
| |
| // |
| // Write zero-length attribute name to mark the end of the header. |
| // |
| |
| Xdr::write <StreamIO> (os, ""); |
| |
| return previewPosition; |
| } |
| |
| |
| void |
| Header::readFrom (IStream &is, int &version) |
| { |
| // |
| // Read the magic number and the file format version number. |
| // Then check if we can read the rest of this file. |
| // |
| |
| int magic; |
| |
| Xdr::read <StreamIO> (is, magic); |
| Xdr::read <StreamIO> (is, version); |
| |
| if (magic != MAGIC) |
| { |
| throw Iex::InputExc ("File is not an image file."); |
| } |
| |
| if (getVersion (version) != EXR_VERSION) |
| { |
| THROW (Iex::InputExc, "Cannot read " |
| "version " << getVersion (version) << " " |
| "image files. Current file format version " |
| "is " << EXR_VERSION << "."); |
| } |
| |
| if (!supportsFlags (getFlags (version))) |
| { |
| THROW (Iex::InputExc, "The file format version number's flag field " |
| "contains unrecognized flags."); |
| } |
| |
| // |
| // Read all attributes. |
| // |
| |
| while (true) |
| { |
| // |
| // Read the name of the attribute. |
| // A zero-length attribute name indicates the end of the header. |
| // |
| |
| char name[100]; |
| Xdr::read <StreamIO> (is, sizeof (name), name); |
| |
| if (name[0] == 0) |
| break; |
| |
| // |
| // Read the attribute type and the size of the attribute value. |
| // |
| |
| char typeName[100]; |
| int size; |
| |
| Xdr::read <StreamIO> (is, sizeof (typeName), typeName); |
| Xdr::read <StreamIO> (is, size); |
| |
| AttributeMap::iterator i = _map.find (name); |
| |
| if (i != _map.end()) |
| { |
| // |
| // The attribute already exists (for example, |
| // because it is a predefined attribute). |
| // Read the attribute's new value from the file. |
| // |
| |
| if (strncmp (i->second->typeName(), typeName, sizeof (typeName))) |
| THROW (Iex::InputExc, "Unexpected type for image attribute " |
| "\"" << name << "\"."); |
| |
| i->second->readValueFrom (is, size, version); |
| } |
| else |
| { |
| // |
| // The new attribute does not exist yet. |
| // If the attribute type is of a known type, |
| // read the attribute value. If the attribute |
| // is of an unknown type, read its value and |
| // store it as an OpaqueAttribute. |
| // |
| |
| Attribute *attr; |
| |
| if (Attribute::knownType (typeName)) |
| attr = Attribute::newAttribute (typeName); |
| else |
| attr = new OpaqueAttribute (typeName); |
| |
| try |
| { |
| attr->readValueFrom (is, size, version); |
| _map[name] = attr; |
| } |
| catch (...) |
| { |
| delete attr; |
| throw; |
| } |
| } |
| } |
| } |
| |
| |
| void |
| staticInitialize () |
| { |
| static Mutex criticalSection; |
| Lock lock (criticalSection); |
| |
| static bool initialized = false; |
| |
| if (!initialized) |
| { |
| // |
| // One-time initialization -- register |
| // some predefined attribute types. |
| // |
| |
| Box2fAttribute::registerAttributeType(); |
| Box2iAttribute::registerAttributeType(); |
| ChannelListAttribute::registerAttributeType(); |
| CompressionAttribute::registerAttributeType(); |
| ChromaticitiesAttribute::registerAttributeType(); |
| DoubleAttribute::registerAttributeType(); |
| EnvmapAttribute::registerAttributeType(); |
| FloatAttribute::registerAttributeType(); |
| IntAttribute::registerAttributeType(); |
| KeyCodeAttribute::registerAttributeType(); |
| LineOrderAttribute::registerAttributeType(); |
| M33fAttribute::registerAttributeType(); |
| M44fAttribute::registerAttributeType(); |
| PreviewImageAttribute::registerAttributeType(); |
| RationalAttribute::registerAttributeType(); |
| StringAttribute::registerAttributeType(); |
| TileDescriptionAttribute::registerAttributeType(); |
| TimeCodeAttribute::registerAttributeType(); |
| V2fAttribute::registerAttributeType(); |
| V2iAttribute::registerAttributeType(); |
| V3fAttribute::registerAttributeType(); |
| V3iAttribute::registerAttributeType(); |
| |
| initialized = true; |
| } |
| } |
| |
| |
| } // namespace Imf |