123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783 |
- ///////////////////////////////////////////////////////////////////////////
- //
- // 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 "ImfMultiPartInputFile.h"
- #include "ImfTimeCodeAttribute.h"
- #include "ImfChromaticitiesAttribute.h"
- #include "ImfBoxAttribute.h"
- #include "ImfFloatAttribute.h"
- #include "ImfStdIO.h"
- #include "ImfTileOffsets.h"
- #include "ImfMisc.h"
- #include "ImfTiledMisc.h"
- #include "ImfInputStreamMutex.h"
- #include "ImfInputPartData.h"
- #include "ImfPartType.h"
- #include "ImfInputFile.h"
- #include "ImfScanLineInputFile.h"
- #include "ImfTiledInputFile.h"
- #include "ImfDeepScanLineInputFile.h"
- #include "ImfDeepTiledInputFile.h"
- #include "ImfVersion.h"
- #include <OpenEXRConfig.h>
- #include <IlmThread.h>
- #include <IlmThreadMutex.h>
- #include <Iex.h>
- #include <map>
- #include <set>
- OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
- using ILMTHREAD_NAMESPACE::Mutex;
- using ILMTHREAD_NAMESPACE::Lock;
- using IMATH_NAMESPACE::Box2i;
- using std::vector;
- using std::map;
- using std::set;
- using std::string;
- namespace
- {
- // Controls whether we error out in the event of shared attribute
- // inconsistency in the input file
- static const bool strictSharedAttribute = true;
- }
- struct MultiPartInputFile::Data: public InputStreamMutex
- {
- int version; // Version of this file.
- bool deleteStream; // If we should delete the stream during destruction.
- vector<InputPartData*> parts; // Data to initialize Output files.
- int numThreads; // Number of threads
- bool reconstructChunkOffsetTable; // If we should reconstruct
- // the offset table if it's broken.
- std::map<int,GenericInputFile*> _inputFiles;
- std::vector<Header> _headers;
-
- void chunkOffsetReconstruction(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is, const std::vector<InputPartData*>& parts);
-
- void readChunkOffsetTables(bool reconstructChunkOffsetTable);
-
- bool checkSharedAttributesValues(const Header & src,
- const Header & dst,
- std::vector<std::string> & conflictingAttributes) const;
-
- TileOffsets* createTileOffsets(const Header& header);
-
- InputPartData* getPart(int partNumber);
-
- Data (bool deleteStream, int numThreads, bool reconstructChunkOffsetTable):
- InputStreamMutex(),
- deleteStream (deleteStream),
- numThreads (numThreads),
- reconstructChunkOffsetTable(reconstructChunkOffsetTable)
- {
- }
- ~Data()
- {
- if (deleteStream) delete is;
- for (size_t i = 0; i < parts.size(); i++)
- delete parts[i];
- }
-
- template <class T>
- T* createInputPartT(int partNumber)
- {
- }
- };
- MultiPartInputFile::MultiPartInputFile(const char fileName[],
- int numThreads,
- bool reconstructChunkOffsetTable):
- _data(new Data(true, numThreads, reconstructChunkOffsetTable))
- {
- try
- {
- _data->is = new StdIFStream (fileName);
- initialize();
- }
- catch (IEX_NAMESPACE::BaseExc &e)
- {
- delete _data;
- REPLACE_EXC (e, "Cannot read image file "
- "\"" << fileName << "\". " << e.what());
- throw;
- }
- catch (...)
- {
- delete _data;
- throw;
- }
- }
- MultiPartInputFile::MultiPartInputFile (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is,
- int numThreads,
- bool reconstructChunkOffsetTable):
- _data(new Data(false, numThreads, reconstructChunkOffsetTable))
- {
- try
- {
- _data->is = &is;
- initialize();
- }
- catch (IEX_NAMESPACE::BaseExc &e)
- {
- delete _data;
- REPLACE_EXC (e, "Cannot read image file "
- "\"" << is.fileName() << "\". " << e.what());
- throw;
- }
- catch (...)
- {
- delete _data;
- throw;
- }
- }
- template<class T>
- T*
- MultiPartInputFile::getInputPart(int partNumber)
- {
- Lock lock(*_data);
- if (_data->_inputFiles.find(partNumber) == _data->_inputFiles.end())
- {
- T* file = new T(_data->getPart(partNumber));
- _data->_inputFiles.insert(std::make_pair(partNumber, (GenericInputFile*) file));
- return file;
- }
- else return (T*) _data->_inputFiles[partNumber];
- }
- template InputFile* MultiPartInputFile::getInputPart<InputFile>(int);
- template TiledInputFile* MultiPartInputFile::getInputPart<TiledInputFile>(int);
- template DeepScanLineInputFile* MultiPartInputFile::getInputPart<DeepScanLineInputFile>(int);
- template DeepTiledInputFile* MultiPartInputFile::getInputPart<DeepTiledInputFile>(int);
- InputPartData*
- MultiPartInputFile::getPart(int partNumber)
- {
- return _data->getPart(partNumber);
- }
- const Header &
- MultiPartInputFile::header(int n) const
- {
- return _data->_headers[n];
- }
- MultiPartInputFile::~MultiPartInputFile()
- {
- for (map<int, GenericInputFile*>::iterator it = _data->_inputFiles.begin();
- it != _data->_inputFiles.end(); it++)
- {
- delete it->second;
- }
- delete _data;
- }
- bool
- MultiPartInputFile::Data::checkSharedAttributesValues(const Header & src,
- const Header & dst,
- vector<string> & conflictingAttributes) const
- {
- conflictingAttributes.clear();
- 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
- MultiPartInputFile::initialize()
- {
- readMagicNumberAndVersionField(*_data->is, _data->version);
-
- bool multipart = isMultiPart(_data->version);
- bool tiled = isTiled(_data->version);
- //
- // Multipart files don't have and shouldn't have the tiled bit set.
- //
- if (tiled && multipart)
- throw IEX_NAMESPACE::InputExc ("Multipart files cannot have the tiled bit set");
-
- int pos = 0;
- while (true)
- {
- Header header;
- header.readFrom(*_data->is, _data->version);
- //
- // If we read nothing then we stop reading.
- //
- if (header.readsNothing())
- {
- pos++;
- break;
- }
- _data->_headers.push_back(header);
-
- if(multipart == false)
- break;
- }
- //
- // Perform usual check on headers.
- //
- for (size_t i = 0; i < _data->_headers.size(); i++)
- {
- //
- // Silently invent a type if the file is a single part regular image.
- //
- if( _data->_headers[i].hasType() == false )
- {
- if(multipart)
- throw IEX_NAMESPACE::ArgExc ("Every header in a multipart file should have a type");
-
- _data->_headers[i].setType(tiled ? TILEDIMAGE : SCANLINEIMAGE);
- }
- else
- {
-
- //
- // Silently fix the header type if it's wrong
- // (happens when a regular Image file written by EXR_2.0 is rewritten by an older library,
- // so doesn't effect deep image types)
- //
- if(!multipart && !isNonImage(_data->version))
- {
- _data->_headers[i].setType(tiled ? TILEDIMAGE : SCANLINEIMAGE);
- }
- }
-
-
- if( _data->_headers[i].hasName() == false )
- {
- if(multipart)
- throw IEX_NAMESPACE::ArgExc ("Every header in a multipart file should have a name");
- }
-
- if (isTiled(_data->_headers[i].type()))
- _data->_headers[i].sanityCheck(true, multipart);
- else
- _data->_headers[i].sanityCheck(false, multipart);
- }
- //
- // Check name uniqueness.
- //
- if (multipart)
- {
- set<string> names;
- for (size_t i = 0; i < _data->_headers.size(); i++)
- {
-
- if (names.find(_data->_headers[i].name()) != names.end())
- {
- throw IEX_NAMESPACE::InputExc ("Header name " + _data->_headers[i].name() +
- " is not a unique name.");
- }
- names.insert(_data->_headers[i].name());
- }
- }
-
- //
- // Check shared attributes compliance.
- //
- if (multipart && strictSharedAttribute)
- {
- for (size_t i = 1; i < _data->_headers.size(); i++)
- {
- vector <string> attrs;
- if (_data->checkSharedAttributesValues (_data->_headers[0], _data->_headers[i], attrs))
- {
- string attrNames;
- for (size_t j=0; j<attrs.size(); j++)
- attrNames += " " + attrs[j];
- throw IEX_NAMESPACE::InputExc ("Header name " + _data->_headers[i].name() +
- " has non-conforming shared attributes: "+
- attrNames);
- }
- }
- }
- //
- // Create InputParts and read chunk offset tables.
- //
-
- for (size_t i = 0; i < _data->_headers.size(); i++)
- _data->parts.push_back(
- new InputPartData(_data, _data->_headers[i], i, _data->numThreads, _data->version));
- _data->readChunkOffsetTables(_data->reconstructChunkOffsetTable);
- }
- TileOffsets*
- MultiPartInputFile::Data::createTileOffsets(const Header& header)
- {
- //
- // Get the dataWindow information
- //
- const Box2i &dataWindow = header.dataWindow();
- int minX = dataWindow.min.x;
- int maxX = dataWindow.max.x;
- int minY = dataWindow.min.y;
- int maxY = dataWindow.max.y;
- //
- // Precompute level and tile information
- //
- int* numXTiles;
- int* numYTiles;
- int numXLevels, numYLevels;
- TileDescription tileDesc = header.tileDescription();
- precalculateTileInfo (tileDesc,
- minX, maxX,
- minY, maxY,
- numXTiles, numYTiles,
- numXLevels, numYLevels);
- TileOffsets* tileOffsets = new TileOffsets (tileDesc.mode,
- numXLevels,
- numYLevels,
- numXTiles,
- numYTiles);
- delete [] numXTiles;
- delete [] numYTiles;
- return tileOffsets;
- }
- void
- MultiPartInputFile::Data::chunkOffsetReconstruction(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is, const vector<InputPartData*>& parts)
- {
- //
- // Reconstruct broken chunk offset tables. Stop once we received any exception.
- //
- Int64 position = is.tellg();
-
- //
- // check we understand all the parts available: if not, we cannot continue
- // exceptions thrown here should trickle back up to the constructor
- //
-
- for (size_t i = 0; i < parts.size(); i++)
- {
- Header& header=parts[i]->header;
-
- //
- // do we have a valid type entry?
- // we only need them for true multipart files or single part non-image (deep) files
- //
- if(!header.hasType() && (isMultiPart(version) || isNonImage(version)))
- {
- throw IEX_NAMESPACE::ArgExc("cannot reconstruct incomplete file: part with missing type");
- }
- if(!isSupportedType(header.type()))
- {
- throw IEX_NAMESPACE::ArgExc("cannot reconstruct incomplete file: part with unknown type "+header.type());
- }
- }
-
-
- // how many chunks should we read? We should stop when we reach the end
- size_t total_chunks = 0;
-
- // for tiled-based parts, array of (pointers to) tileOffsets objects
- // to create mapping between tile coordinates and chunk table indices
-
-
- vector<TileOffsets*> tileOffsets(parts.size());
-
- // for scanline-based parts, number of scanlines in each part
- vector<int> rowsizes(parts.size());
-
- for(size_t i = 0 ; i < parts.size() ; i++)
- {
- total_chunks += parts[i]->chunkOffsets.size();
- if (isTiled(parts[i]->header.type()))
- {
- tileOffsets[i] = createTileOffsets(parts[i]->header);
- }else{
- tileOffsets[i] = NULL;
- // (TODO) fix this so that it doesn't need to be revised for future compression types.
- switch(parts[i]->header.compression())
- {
- case DWAB_COMPRESSION :
- rowsizes[i] = 256;
- break;
- case PIZ_COMPRESSION :
- case B44_COMPRESSION :
- case B44A_COMPRESSION :
- case DWAA_COMPRESSION :
- rowsizes[i]=32;
- break;
- case ZIP_COMPRESSION :
- case PXR24_COMPRESSION :
- rowsizes[i]=16;
- break;
- case ZIPS_COMPRESSION :
- case RLE_COMPRESSION :
- case NO_COMPRESSION :
- rowsizes[i]=1;
- break;
- default :
- throw(IEX_NAMESPACE::ArgExc("Unknown compression method in chunk offset reconstruction"));
- }
- }
- }
-
- try
- {
-
- //
- //
- //
-
- Int64 chunk_start = position;
- for (size_t i = 0; i < total_chunks ; i++)
- {
- //
- // do we have a part number?
- //
-
- int partNumber = 0;
- if(isMultiPart(version))
- {
- OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, partNumber);
- }
-
-
-
- if(partNumber<0 || partNumber>int(parts.size()))
- {
- // bail here - bad part number
- throw int();
- }
-
- Header& header = parts[partNumber]->header;
- // size of chunk NOT including multipart field
-
- Int64 size_of_chunk=0;
- if (isTiled(header.type()))
- {
- //
- //
- //
- int tilex,tiley,levelx,levely;
- OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, tilex);
- OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, tiley);
- OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, levelx);
- OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, levely);
-
- //std::cout << "chunk_start for " << tilex <<',' << tiley << ',' << levelx << ' ' << levely << ':' << chunk_start << std::endl;
-
-
- if(!tileOffsets[partNumber])
- {
- // this shouldn't actually happen - we should have allocated a valid
- // tileOffsets for any part which isTiled
- throw int();
-
- }
-
- if(!tileOffsets[partNumber]->isValidTile(tilex,tiley,levelx,levely))
- {
- //std::cout << "invalid tile : aborting\n";
- throw int();
- }
-
- (*tileOffsets[partNumber])(tilex,tiley,levelx,levely)=chunk_start;
-
- // compute chunk sizes - different procedure for deep tiles and regular
- // ones
- if(header.type()==DEEPTILE)
- {
- Int64 packed_offset;
- Int64 packed_sample;
- OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, packed_offset);
- OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, packed_sample);
-
- //add 40 byte header to packed sizes (tile coordinates, packed sizes, unpacked size)
- size_of_chunk=packed_offset+packed_sample+40;
- }
- else
- {
-
- // regular image has 20 bytes of header, 4 byte chunksize;
- int chunksize;
- OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, chunksize);
- size_of_chunk=chunksize+20;
- }
- }
- else
- {
- int y_coordinate;
- OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, y_coordinate);
-
- y_coordinate -= header.dataWindow().min.y;
- y_coordinate /= rowsizes[partNumber];
-
- if(y_coordinate < 0 || y_coordinate >= int(parts[partNumber]->chunkOffsets.size()))
- {
- //std::cout << "aborting reconstruction: bad data " << y_coordinate << endl;
- //bail to exception catcher: broken scanline
- throw int();
- }
-
- parts[partNumber]->chunkOffsets[y_coordinate]=chunk_start;
- //std::cout << "chunk_start for " << y_coordinate << ':' << chunk_start << std::endl;
-
- if(header.type()==DEEPSCANLINE)
- {
- Int64 packed_offset;
- Int64 packed_sample;
- OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, packed_offset);
- OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, packed_sample);
-
-
- size_of_chunk=packed_offset+packed_sample+28;
- }
- else
- {
- int chunksize;
- OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, chunksize);
- size_of_chunk=chunksize+8;
- }
-
- }
-
- if(isMultiPart(version))
- {
- chunk_start+=4;
- }
-
- chunk_start+=size_of_chunk;
-
- //std::cout << " next chunk +"<<size_of_chunk << " = " << chunk_start << std::endl;
-
- is.seekg(chunk_start);
-
- }
-
- }
- catch (...)
- {
- //
- // Suppress all exceptions. This functions is
- // called only to reconstruct the line offset
- // table for incomplete files, and exceptions
- // are likely.
- //
- }
- // copy tiled part data back to chunk offsets
-
- for(size_t partNumber=0;partNumber<parts.size();partNumber++)
- {
- if(tileOffsets[partNumber])
- {
- size_t pos=0;
- vector<vector<vector <Int64> > > offsets = tileOffsets[partNumber]->getOffsets();
- for (size_t l = 0; l < offsets.size(); l++)
- for (size_t y = 0; y < offsets[l].size(); y++)
- for (size_t x = 0; x < offsets[l][y].size(); x++)
- {
- parts[ partNumber ]->chunkOffsets[pos] = offsets[l][y][x];
- pos++;
- }
- delete tileOffsets[partNumber];
- }
- }
- is.clear();
- is.seekg (position);
- }
- InputPartData*
- MultiPartInputFile::Data::getPart(int partNumber)
- {
- if (partNumber < 0 || partNumber >= (int) parts.size())
- throw IEX_NAMESPACE::ArgExc ("Part number is not in valid range.");
- return parts[partNumber];
- }
- void
- MultiPartInputFile::Data::readChunkOffsetTables(bool reconstructChunkOffsetTable)
- {
- bool brokenPartsExist = false;
- for (size_t i = 0; i < parts.size(); i++)
- {
- int chunkOffsetTableSize = getChunkOffsetTableSize(parts[i]->header,false);
- parts[i]->chunkOffsets.resize(chunkOffsetTableSize);
- for (int j = 0; j < chunkOffsetTableSize; j++)
- OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*is, parts[i]->chunkOffsets[j]);
- //
- // Check chunk offsets, reconstruct if broken.
- // At first we assume the table is complete.
- //
- parts[i]->completed = true;
- for (int j = 0; j < chunkOffsetTableSize; j++)
- {
- if (parts[i]->chunkOffsets[j] <= 0)
- {
- brokenPartsExist = true;
- parts[i]->completed = false;
- break;
- }
- }
- }
- if (brokenPartsExist && reconstructChunkOffsetTable)
- chunkOffsetReconstruction(*is, parts);
- }
- int
- MultiPartInputFile::version() const
- {
- return _data->version;
- }
- bool
- MultiPartInputFile::partComplete(int part) const
- {
- return _data->parts[part]->completed;
- }
- int
- MultiPartInputFile::parts() const
- {
- return int(_data->_headers.size());
- }
- OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT
|