ImfMultiPartOutputFile.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. ///////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 2011, 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. #include "ImfMultiPartOutputFile.h"
  35. #include "ImfBoxAttribute.h"
  36. #include "ImfFloatAttribute.h"
  37. #include "ImfTimeCodeAttribute.h"
  38. #include "ImfChromaticitiesAttribute.h"
  39. #include "ImfOutputPartData.h"
  40. #include "ImfPartType.h"
  41. #include "ImfOutputFile.h"
  42. #include "ImfTiledOutputFile.h"
  43. #include "ImfThreading.h"
  44. #include "IlmThreadMutex.h"
  45. #include "ImfMisc.h"
  46. #include "ImfStdIO.h"
  47. #include "ImfDeepScanLineOutputFile.h"
  48. #include "ImfDeepTiledOutputFile.h"
  49. #include "ImfOutputStreamMutex.h"
  50. #include "ImfNamespace.h"
  51. #include <Iex.h>
  52. #include <set>
  53. OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
  54. using IMATH_NAMESPACE::Box2i;
  55. using ILMTHREAD_NAMESPACE::Lock;
  56. using std::vector;
  57. using std::map;
  58. using std::set;
  59. struct MultiPartOutputFile::Data: public OutputStreamMutex
  60. {
  61. vector<OutputPartData*> parts; // Contains data to initialize Output files.
  62. bool deleteStream; // If we should delete the stream when destruction.
  63. int numThreads; // The number of threads.
  64. std::map<int, GenericOutputFile*> _outputFiles;
  65. std::vector<Header> _headers;
  66. void headerNameUniquenessCheck (const std::vector<Header> &headers);
  67. void writeHeadersToFile (const std::vector<Header> &headers);
  68. void writeChunkTableOffsets (std::vector<OutputPartData*> &parts);
  69. //-------------------------------------
  70. // ensure that _headers is valid: called by constructors
  71. //-------------------------------------
  72. void do_header_sanity_checks(bool overrideSharedAttributes);
  73. // ------------------------------------------------
  74. // Given a source header, we copy over all the 'shared attributes' to
  75. // the destination header and remove any conflicting ones.
  76. // ------------------------------------------------
  77. void overrideSharedAttributesValues (const Header & src,
  78. Header & dst);
  79. // ------------------------------------------------
  80. // Given a source header, we check the destination header for any
  81. // attributes that are part of the shared attribute set. For attributes
  82. // present in both we check the values. For attribute present in
  83. // destination but absent in source we return false.
  84. // For attributes present in src but missing from dst we return false
  85. // and add the attribute to dst.
  86. // We return false for all other cases.
  87. // If we return true then we also populate the conflictingAttributes
  88. // vector with the names of the attributes that failed the above.
  89. // ------------------------------------------------
  90. bool checkSharedAttributesValues (const Header & src,
  91. const Header & dst,
  92. std::vector<std::string> & conflictingAttributes) const;
  93. Data (bool deleteStream, int numThreads):
  94. OutputStreamMutex(),
  95. deleteStream (deleteStream),
  96. numThreads (numThreads)
  97. {
  98. }
  99. ~Data()
  100. {
  101. if (deleteStream) delete os;
  102. for (size_t i = 0; i < parts.size(); i++)
  103. delete parts[i];
  104. }
  105. };
  106. void
  107. MultiPartOutputFile::Data::do_header_sanity_checks(bool overrideSharedAttributes)
  108. {
  109. size_t parts = _headers.size();
  110. if (parts == 0)
  111. throw IEX_NAMESPACE::ArgExc ("Empty header list.");
  112. bool isMultiPart = (parts > 1);
  113. //
  114. // Do part 0 checks first.
  115. //
  116. _headers[0].sanityCheck (_headers[0].hasTileDescription(), isMultiPart);
  117. if (isMultiPart)
  118. {
  119. // multipart files must contain a chunkCount attribute
  120. _headers[0].setChunkCount(getChunkOffsetTableSize(_headers[0],true));
  121. for (size_t i = 1; i < parts; i++)
  122. {
  123. if (_headers[i].hasType() == false)
  124. throw IEX_NAMESPACE::ArgExc ("Every header in a multipart file should have a type");
  125. _headers[i].setChunkCount(getChunkOffsetTableSize(_headers[i],true));
  126. _headers[i].sanityCheck (_headers[i].hasTileDescription(), isMultiPart);
  127. if (overrideSharedAttributes)
  128. overrideSharedAttributesValues(_headers[0],_headers[i]);
  129. else
  130. {
  131. std::vector<std::string> conflictingAttributes;
  132. bool valid =checkSharedAttributesValues (_headers[0],
  133. _headers[i],
  134. conflictingAttributes);
  135. if (valid)
  136. {
  137. string excMsg("Conflicting attributes found for header :: ");
  138. excMsg += _headers[i].name();
  139. for (size_t i=0; i<conflictingAttributes.size(); i++)
  140. excMsg += " '" + conflictingAttributes[i] + "' ";
  141. THROW (IEX_NAMESPACE::ArgExc, excMsg);
  142. }
  143. }
  144. }
  145. headerNameUniquenessCheck(_headers);
  146. }else{
  147. // add chunk count offset to single part data (if not an image)
  148. if (_headers[0].hasType() && isImage(_headers[0].type()) == false)
  149. {
  150. _headers[0].setChunkCount(getChunkOffsetTableSize(_headers[0],true));
  151. }
  152. }
  153. }
  154. MultiPartOutputFile::MultiPartOutputFile (const char fileName[],
  155. const Header * headers,
  156. int parts,
  157. bool overrideSharedAttributes,
  158. int numThreads)
  159. :
  160. _data (new Data (true, numThreads))
  161. {
  162. // grab headers
  163. _data->_headers.resize(parts);
  164. for(int i=0;i<parts;i++)
  165. {
  166. _data->_headers[i]=headers[i];
  167. }
  168. try
  169. {
  170. _data->do_header_sanity_checks(overrideSharedAttributes);
  171. //
  172. // Build parts and write headers and offset tables to file.
  173. //
  174. _data->os = new StdOFStream (fileName);
  175. for (size_t i = 0; i < _data->_headers.size(); i++)
  176. _data->parts.push_back( new OutputPartData(_data, _data->_headers[i], i, numThreads, parts>1 ) );
  177. writeMagicNumberAndVersionField(*_data->os, &_data->_headers[0],_data->_headers.size());
  178. _data->writeHeadersToFile(_data->_headers);
  179. _data->writeChunkTableOffsets(_data->parts);
  180. }
  181. catch (IEX_NAMESPACE::BaseExc &e)
  182. {
  183. delete _data;
  184. REPLACE_EXC (e, "Cannot open image file "
  185. "\"" << fileName << "\". " << e.what());
  186. throw;
  187. }
  188. catch (...)
  189. {
  190. delete _data;
  191. throw;
  192. }
  193. }
  194. MultiPartOutputFile::MultiPartOutputFile(OStream& os,
  195. const Header* headers,
  196. int parts,
  197. bool overrideSharedAttributes,
  198. int numThreads):
  199. _data(new Data(false,numThreads))
  200. {
  201. // grab headers
  202. _data->_headers.resize(parts);
  203. _data->os=&os;
  204. for(int i=0;i<parts;i++)
  205. {
  206. _data->_headers[i]=headers[i];
  207. }
  208. try
  209. {
  210. _data->do_header_sanity_checks(overrideSharedAttributes);
  211. //
  212. // Build parts and write headers and offset tables to file.
  213. //
  214. for (size_t i = 0; i < _data->_headers.size(); i++)
  215. _data->parts.push_back( new OutputPartData(_data, _data->_headers[i], i, numThreads, parts>1 ) );
  216. writeMagicNumberAndVersionField(*_data->os, &_data->_headers[0],_data->_headers.size());
  217. _data->writeHeadersToFile(_data->_headers);
  218. _data->writeChunkTableOffsets(_data->parts);
  219. }
  220. catch (IEX_NAMESPACE::BaseExc &e)
  221. {
  222. delete _data;
  223. REPLACE_EXC (e, "Cannot open image stream "
  224. "\"" << os.fileName() << "\". " << e.what());
  225. throw;
  226. }
  227. catch (...)
  228. {
  229. delete _data;
  230. throw;
  231. }
  232. }
  233. const Header &
  234. MultiPartOutputFile::header(int n) const
  235. {
  236. if(n<0 || n>int(_data->_headers.size()))
  237. {
  238. throw IEX_NAMESPACE::ArgExc("MultiPartOutputFile::header called with invalid part number");
  239. }
  240. return _data->_headers[n];
  241. }
  242. int
  243. MultiPartOutputFile::parts() const
  244. {
  245. return _data->_headers.size();
  246. }
  247. MultiPartOutputFile::~MultiPartOutputFile ()
  248. {
  249. for (map<int, GenericOutputFile*>::iterator it = _data->_outputFiles.begin();
  250. it != _data->_outputFiles.end(); it++)
  251. {
  252. delete it->second;
  253. }
  254. delete _data;
  255. }
  256. template <class T>
  257. T*
  258. MultiPartOutputFile::getOutputPart(int partNumber)
  259. {
  260. Lock lock(*_data);
  261. if (_data->_outputFiles.find(partNumber) == _data->_outputFiles.end())
  262. {
  263. T* file = new T(_data->parts[partNumber]);
  264. _data->_outputFiles.insert(std::make_pair(partNumber, (GenericOutputFile*) file));
  265. return file;
  266. }
  267. else return (T*) _data->_outputFiles[partNumber];
  268. }
  269. // instance above function for all four types
  270. template OutputFile* MultiPartOutputFile::getOutputPart<OutputFile>(int);
  271. template TiledOutputFile * MultiPartOutputFile::getOutputPart<TiledOutputFile>(int);
  272. template DeepScanLineOutputFile * MultiPartOutputFile::getOutputPart<DeepScanLineOutputFile> (int);
  273. template DeepTiledOutputFile * MultiPartOutputFile::getOutputPart<DeepTiledOutputFile> (int);
  274. void
  275. MultiPartOutputFile::Data::overrideSharedAttributesValues(const Header & src, Header & dst)
  276. {
  277. //
  278. // Display Window
  279. //
  280. const Box2iAttribute * displayWindow =
  281. src.findTypedAttribute<Box2iAttribute> ("displayWindow");
  282. if (displayWindow)
  283. dst.insert ("displayWindow", *displayWindow);
  284. else
  285. dst.erase ("displayWindow");
  286. //
  287. // Pixel Aspect Ratio
  288. //
  289. const FloatAttribute * pixelAspectRatio =
  290. src.findTypedAttribute<FloatAttribute> ("pixelAspectRatio");
  291. if (pixelAspectRatio)
  292. dst.insert ("pixelAspectRatio", *pixelAspectRatio);
  293. else
  294. dst.erase ("pixelAspectRatio");
  295. //
  296. // Timecode
  297. //
  298. const TimeCodeAttribute * timeCode =
  299. src.findTypedAttribute<TimeCodeAttribute> ("timecode");
  300. if (timeCode)
  301. dst.insert ("timecode", *timeCode);
  302. else
  303. dst.erase ("timecode");
  304. //
  305. // Chromaticities
  306. //
  307. const ChromaticitiesAttribute * chromaticities =
  308. src.findTypedAttribute<ChromaticitiesAttribute> ("chromaticities");
  309. if (chromaticities)
  310. dst.insert ("chromaticities", *chromaticities);
  311. else
  312. dst.erase ("chromaticities");
  313. }
  314. bool
  315. MultiPartOutputFile::Data::checkSharedAttributesValues(const Header & src,
  316. const Header & dst,
  317. vector<string> & conflictingAttributes) const
  318. {
  319. bool conflict = false;
  320. //
  321. // Display Window
  322. //
  323. if (src.displayWindow() != dst.displayWindow())
  324. {
  325. conflict = true;
  326. conflictingAttributes.push_back ("displayWindow");
  327. }
  328. //
  329. // Pixel Aspect Ratio
  330. //
  331. if (src.pixelAspectRatio() != dst.pixelAspectRatio())
  332. {
  333. conflict = true;
  334. conflictingAttributes.push_back ("pixelAspectRatio");
  335. }
  336. //
  337. // Timecode
  338. //
  339. const TimeCodeAttribute * srcTimeCode = src.findTypedAttribute<
  340. TimeCodeAttribute> (TimeCodeAttribute::staticTypeName());
  341. const TimeCodeAttribute * dstTimeCode = dst.findTypedAttribute<
  342. TimeCodeAttribute> (TimeCodeAttribute::staticTypeName());
  343. if (dstTimeCode)
  344. {
  345. if ((srcTimeCode && (srcTimeCode->value() != dstTimeCode->value())) ||
  346. (!srcTimeCode))
  347. {
  348. conflict = true;
  349. conflictingAttributes.push_back (TimeCodeAttribute::staticTypeName());
  350. }
  351. }
  352. //
  353. // Chromaticities
  354. //
  355. const ChromaticitiesAttribute * srcChrom = src.findTypedAttribute<
  356. ChromaticitiesAttribute> (ChromaticitiesAttribute::staticTypeName());
  357. const ChromaticitiesAttribute * dstChrom = dst.findTypedAttribute<
  358. ChromaticitiesAttribute> (ChromaticitiesAttribute::staticTypeName());
  359. if (dstChrom)
  360. {
  361. if ( (srcChrom && (srcChrom->value() != dstChrom->value())) ||
  362. (!srcChrom))
  363. {
  364. conflict = true;
  365. conflictingAttributes.push_back (ChromaticitiesAttribute::staticTypeName());
  366. }
  367. }
  368. return conflict;
  369. }
  370. void
  371. MultiPartOutputFile::Data::headerNameUniquenessCheck (const vector<Header> &headers)
  372. {
  373. set<string> names;
  374. for (size_t i = 0; i < headers.size(); i++)
  375. {
  376. if (names.find(headers[i].name()) != names.end())
  377. throw IEX_NAMESPACE::ArgExc ("Each part should have a unique name.");
  378. names.insert(headers[i].name());
  379. }
  380. }
  381. void
  382. MultiPartOutputFile::Data::writeHeadersToFile (const vector<Header> &headers)
  383. {
  384. for (size_t i = 0; i < headers.size(); i++)
  385. {
  386. // (TODO) consider deep files' preview images here.
  387. if (headers[i].type() == TILEDIMAGE)
  388. parts[i]->previewPosition = headers[i].writeTo(*os, true);
  389. else
  390. parts[i]->previewPosition = headers[i].writeTo(*os, false);
  391. }
  392. //
  393. // If a multipart file, write zero-length attribute name to mark the end of all headers.
  394. //
  395. if (headers.size() !=1)
  396. OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*os, "");
  397. }
  398. void
  399. MultiPartOutputFile::Data::writeChunkTableOffsets (vector<OutputPartData*> &parts)
  400. {
  401. for (size_t i = 0; i < parts.size(); i++)
  402. {
  403. int chunkTableSize = getChunkOffsetTableSize(parts[i]->header,false);
  404. Int64 pos = os->tellp();
  405. if (pos == -1)
  406. IEX_NAMESPACE::throwErrnoExc ("Cannot determine current file position (%T).");
  407. parts[i]->chunkOffsetTablePosition = os->tellp();
  408. //
  409. // Fill in empty data for now. We'll write actual offsets during destruction.
  410. //
  411. for (int j = 0; j < chunkTableSize; j++)
  412. {
  413. Int64 empty = 0;
  414. OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*os, empty);
  415. }
  416. }
  417. }
  418. OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT