123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519 |
- ///////////////////////////////////////////////////////////////////////////
- //
- // Copyright (c) 2011, 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.
- //
- ///////////////////////////////////////////////////////////////////////////
- #include "ImfMultiPartOutputFile.h"
- #include "ImfBoxAttribute.h"
- #include "ImfFloatAttribute.h"
- #include "ImfTimeCodeAttribute.h"
- #include "ImfChromaticitiesAttribute.h"
- #include "ImfOutputPartData.h"
- #include "ImfPartType.h"
- #include "ImfOutputFile.h"
- #include "ImfTiledOutputFile.h"
- #include "ImfThreading.h"
- #include "IlmThreadMutex.h"
- #include "ImfMisc.h"
- #include "ImfStdIO.h"
- #include "ImfDeepScanLineOutputFile.h"
- #include "ImfDeepTiledOutputFile.h"
- #include "ImfOutputStreamMutex.h"
- #include "ImfNamespace.h"
- #include <Iex.h>
- #include <set>
- OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
- using IMATH_NAMESPACE::Box2i;
- using ILMTHREAD_NAMESPACE::Lock;
-
- using std::vector;
- using std::map;
- using std::set;
- struct MultiPartOutputFile::Data: public OutputStreamMutex
- {
- vector<OutputPartData*> parts; // Contains data to initialize Output files.
- bool deleteStream; // If we should delete the stream when destruction.
- int numThreads; // The number of threads.
- std::map<int, GenericOutputFile*> _outputFiles;
- std::vector<Header> _headers;
-
-
- void headerNameUniquenessCheck (const std::vector<Header> &headers);
-
- void writeHeadersToFile (const std::vector<Header> &headers);
-
- void writeChunkTableOffsets (std::vector<OutputPartData*> &parts);
-
-
- //-------------------------------------
- // ensure that _headers is valid: called by constructors
- //-------------------------------------
- void do_header_sanity_checks(bool overrideSharedAttributes);
-
- // ------------------------------------------------
- // Given a source header, we copy over all the 'shared attributes' to
- // the destination header and remove any conflicting ones.
- // ------------------------------------------------
- void overrideSharedAttributesValues (const Header & src,
- Header & dst);
-
- // ------------------------------------------------
- // Given a source header, we check the destination header for any
- // attributes that are part of the shared attribute set. For attributes
- // present in both we check the values. For attribute present in
- // destination but absent in source we return false.
- // For attributes present in src but missing from dst we return false
- // and add the attribute to dst.
- // We return false for all other cases.
- // If we return true then we also populate the conflictingAttributes
- // vector with the names of the attributes that failed the above.
- // ------------------------------------------------
- bool checkSharedAttributesValues (const Header & src,
- const Header & dst,
- std::vector<std::string> & conflictingAttributes) const;
- Data (bool deleteStream, int numThreads):
- OutputStreamMutex(),
- deleteStream (deleteStream),
- numThreads (numThreads)
- {
- }
-
- ~Data()
- {
- if (deleteStream) delete os;
- for (size_t i = 0; i < parts.size(); i++)
- delete parts[i];
- }
- };
- void
- MultiPartOutputFile::Data::do_header_sanity_checks(bool overrideSharedAttributes)
- {
- size_t parts = _headers.size();
- if (parts == 0)
- throw IEX_NAMESPACE::ArgExc ("Empty header list.");
-
- bool isMultiPart = (parts > 1);
-
- //
- // Do part 0 checks first.
- //
-
- _headers[0].sanityCheck (_headers[0].hasTileDescription(), isMultiPart);
-
-
- if (isMultiPart)
- {
- // multipart files must contain a chunkCount attribute
- _headers[0].setChunkCount(getChunkOffsetTableSize(_headers[0],true));
-
- for (size_t i = 1; i < parts; i++)
- {
- if (_headers[i].hasType() == false)
- throw IEX_NAMESPACE::ArgExc ("Every header in a multipart file should have a type");
-
-
- _headers[i].setChunkCount(getChunkOffsetTableSize(_headers[i],true));
- _headers[i].sanityCheck (_headers[i].hasTileDescription(), isMultiPart);
-
-
- if (overrideSharedAttributes)
- overrideSharedAttributesValues(_headers[0],_headers[i]);
- else
- {
- std::vector<std::string> conflictingAttributes;
- bool valid =checkSharedAttributesValues (_headers[0],
- _headers[i],
- conflictingAttributes);
- if (valid)
- {
- string excMsg("Conflicting attributes found for header :: ");
- excMsg += _headers[i].name();
- for (size_t i=0; i<conflictingAttributes.size(); i++)
- excMsg += " '" + conflictingAttributes[i] + "' ";
-
- THROW (IEX_NAMESPACE::ArgExc, excMsg);
- }
- }
- }
-
- headerNameUniquenessCheck(_headers);
-
- }else{
-
- // add chunk count offset to single part data (if not an image)
-
- if (_headers[0].hasType() && isImage(_headers[0].type()) == false)
- {
- _headers[0].setChunkCount(getChunkOffsetTableSize(_headers[0],true));
- }
-
- }
- }
-
- MultiPartOutputFile::MultiPartOutputFile (const char fileName[],
- const Header * headers,
- int parts,
- bool overrideSharedAttributes,
- int numThreads)
- :
- _data (new Data (true, numThreads))
- {
- // grab headers
- _data->_headers.resize(parts);
-
- for(int i=0;i<parts;i++)
- {
- _data->_headers[i]=headers[i];
- }
- try
- {
-
- _data->do_header_sanity_checks(overrideSharedAttributes);
- //
- // Build parts and write headers and offset tables to file.
- //
- _data->os = new StdOFStream (fileName);
- for (size_t i = 0; i < _data->_headers.size(); i++)
- _data->parts.push_back( new OutputPartData(_data, _data->_headers[i], i, numThreads, parts>1 ) );
- writeMagicNumberAndVersionField(*_data->os, &_data->_headers[0],_data->_headers.size());
- _data->writeHeadersToFile(_data->_headers);
- _data->writeChunkTableOffsets(_data->parts);
- }
- catch (IEX_NAMESPACE::BaseExc &e)
- {
- delete _data;
- REPLACE_EXC (e, "Cannot open image file "
- "\"" << fileName << "\". " << e.what());
- throw;
- }
- catch (...)
- {
- delete _data;
- throw;
- }
- }
- MultiPartOutputFile::MultiPartOutputFile(OStream& os,
- const Header* headers,
- int parts,
- bool overrideSharedAttributes,
- int numThreads):
- _data(new Data(false,numThreads))
- {
- // grab headers
- _data->_headers.resize(parts);
- _data->os=&os;
-
- for(int i=0;i<parts;i++)
- {
- _data->_headers[i]=headers[i];
- }
- try
- {
-
- _data->do_header_sanity_checks(overrideSharedAttributes);
-
- //
- // Build parts and write headers and offset tables to file.
- //
-
- for (size_t i = 0; i < _data->_headers.size(); i++)
- _data->parts.push_back( new OutputPartData(_data, _data->_headers[i], i, numThreads, parts>1 ) );
-
- writeMagicNumberAndVersionField(*_data->os, &_data->_headers[0],_data->_headers.size());
- _data->writeHeadersToFile(_data->_headers);
- _data->writeChunkTableOffsets(_data->parts);
- }
- catch (IEX_NAMESPACE::BaseExc &e)
- {
- delete _data;
-
- REPLACE_EXC (e, "Cannot open image stream "
- "\"" << os.fileName() << "\". " << e.what());
- throw;
- }
- catch (...)
- {
- delete _data;
- throw;
- }
- }
- const Header &
- MultiPartOutputFile::header(int n) const
- {
- if(n<0 || n>int(_data->_headers.size()))
- {
- throw IEX_NAMESPACE::ArgExc("MultiPartOutputFile::header called with invalid part number");
- }
- return _data->_headers[n];
- }
- int
- MultiPartOutputFile::parts() const
- {
- return _data->_headers.size();
- }
- MultiPartOutputFile::~MultiPartOutputFile ()
- {
- for (map<int, GenericOutputFile*>::iterator it = _data->_outputFiles.begin();
- it != _data->_outputFiles.end(); it++)
- {
- delete it->second;
- }
- delete _data;
- }
- template <class T>
- T*
- MultiPartOutputFile::getOutputPart(int partNumber)
- {
- Lock lock(*_data);
- if (_data->_outputFiles.find(partNumber) == _data->_outputFiles.end())
- {
- T* file = new T(_data->parts[partNumber]);
- _data->_outputFiles.insert(std::make_pair(partNumber, (GenericOutputFile*) file));
- return file;
- }
- else return (T*) _data->_outputFiles[partNumber];
- }
- // instance above function for all four types
- template OutputFile* MultiPartOutputFile::getOutputPart<OutputFile>(int);
- template TiledOutputFile * MultiPartOutputFile::getOutputPart<TiledOutputFile>(int);
- template DeepScanLineOutputFile * MultiPartOutputFile::getOutputPart<DeepScanLineOutputFile> (int);
- template DeepTiledOutputFile * MultiPartOutputFile::getOutputPart<DeepTiledOutputFile> (int);
- void
- MultiPartOutputFile::Data::overrideSharedAttributesValues(const Header & src, Header & dst)
- {
- //
- // Display Window
- //
- const Box2iAttribute * displayWindow =
- src.findTypedAttribute<Box2iAttribute> ("displayWindow");
-
- if (displayWindow)
- dst.insert ("displayWindow", *displayWindow);
- else
- dst.erase ("displayWindow");
-
-
- //
- // Pixel Aspect Ratio
- //
- const FloatAttribute * pixelAspectRatio =
- src.findTypedAttribute<FloatAttribute> ("pixelAspectRatio");
-
- if (pixelAspectRatio)
- dst.insert ("pixelAspectRatio", *pixelAspectRatio);
- else
- dst.erase ("pixelAspectRatio");
-
-
- //
- // Timecode
- //
- const TimeCodeAttribute * timeCode =
- src.findTypedAttribute<TimeCodeAttribute> ("timecode");
-
- if (timeCode)
- dst.insert ("timecode", *timeCode);
- else
- dst.erase ("timecode");
-
-
- //
- // Chromaticities
- //
- const ChromaticitiesAttribute * chromaticities =
- src.findTypedAttribute<ChromaticitiesAttribute> ("chromaticities");
-
- if (chromaticities)
- dst.insert ("chromaticities", *chromaticities);
- else
- dst.erase ("chromaticities");
-
- }
- bool
- MultiPartOutputFile::Data::checkSharedAttributesValues(const Header & src,
- const Header & dst,
- vector<string> & conflictingAttributes) const
- {
- bool conflict = false;
- //
- // Display Window
- //
- if (src.displayWindow() != dst.displayWindow())
- {
- conflict = true;
- conflictingAttributes.push_back ("displayWindow");
- }
- //
- // Pixel Aspect Ratio
- //
- if (src.pixelAspectRatio() != dst.pixelAspectRatio())
- {
- conflict = true;
- conflictingAttributes.push_back ("pixelAspectRatio");
- }
- //
- // Timecode
- //
- const TimeCodeAttribute * srcTimeCode = src.findTypedAttribute<
- TimeCodeAttribute> (TimeCodeAttribute::staticTypeName());
- const TimeCodeAttribute * dstTimeCode = dst.findTypedAttribute<
- TimeCodeAttribute> (TimeCodeAttribute::staticTypeName());
- if (dstTimeCode)
- {
- if ((srcTimeCode && (srcTimeCode->value() != dstTimeCode->value())) ||
- (!srcTimeCode))
- {
- conflict = true;
- conflictingAttributes.push_back (TimeCodeAttribute::staticTypeName());
- }
- }
- //
- // Chromaticities
- //
- const ChromaticitiesAttribute * srcChrom = src.findTypedAttribute<
- ChromaticitiesAttribute> (ChromaticitiesAttribute::staticTypeName());
- const ChromaticitiesAttribute * dstChrom = dst.findTypedAttribute<
- ChromaticitiesAttribute> (ChromaticitiesAttribute::staticTypeName());
- if (dstChrom)
- {
- if ( (srcChrom && (srcChrom->value() != dstChrom->value())) ||
- (!srcChrom))
- {
- conflict = true;
- conflictingAttributes.push_back (ChromaticitiesAttribute::staticTypeName());
- }
- }
- return conflict;
- }
-
- void
- MultiPartOutputFile::Data::headerNameUniquenessCheck (const vector<Header> &headers)
- {
- set<string> names;
- for (size_t i = 0; i < headers.size(); i++)
- {
- if (names.find(headers[i].name()) != names.end())
- throw IEX_NAMESPACE::ArgExc ("Each part should have a unique name.");
- names.insert(headers[i].name());
- }
- }
- void
- MultiPartOutputFile::Data::writeHeadersToFile (const vector<Header> &headers)
- {
- for (size_t i = 0; i < headers.size(); i++)
- {
- // (TODO) consider deep files' preview images here.
- if (headers[i].type() == TILEDIMAGE)
- parts[i]->previewPosition = headers[i].writeTo(*os, true);
- else
- parts[i]->previewPosition = headers[i].writeTo(*os, false);
- }
- //
- // If a multipart file, write zero-length attribute name to mark the end of all headers.
- //
- if (headers.size() !=1)
- OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*os, "");
- }
- void
- MultiPartOutputFile::Data::writeChunkTableOffsets (vector<OutputPartData*> &parts)
- {
- for (size_t i = 0; i < parts.size(); i++)
- {
- int chunkTableSize = getChunkOffsetTableSize(parts[i]->header,false);
- Int64 pos = os->tellp();
- if (pos == -1)
- IEX_NAMESPACE::throwErrnoExc ("Cannot determine current file position (%T).");
- parts[i]->chunkOffsetTablePosition = os->tellp();
- //
- // Fill in empty data for now. We'll write actual offsets during destruction.
- //
- for (int j = 0; j < chunkTableSize; j++)
- {
- Int64 empty = 0;
- OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*os, empty);
- }
- }
- }
- OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT
|