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

https://bitbucket.org/cabalistic/ogredeps/ · C++ · 1693 lines · 1069 code · 342 blank · 282 comment · 133 complexity · 118aa125a9b1e2503902ae53538891c0 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. // class TiledOutputFile
  37. //
  38. //-----------------------------------------------------------------------------
  39. #include <ImfTiledOutputFile.h>
  40. #include <ImfTiledInputFile.h>
  41. #include <ImfInputFile.h>
  42. #include <ImfTileDescriptionAttribute.h>
  43. #include <ImfPreviewImageAttribute.h>
  44. #include <ImfChannelList.h>
  45. #include <ImfMisc.h>
  46. #include <ImfTiledMisc.h>
  47. #include <ImfStdIO.h>
  48. #include <ImfCompressor.h>
  49. #include "ImathBox.h"
  50. #include <ImfArray.h>
  51. #include <ImfXdr.h>
  52. #include <ImfVersion.h>
  53. #include <ImfTileOffsets.h>
  54. #include <ImfThreading.h>
  55. #include "IlmThreadPool.h"
  56. #include "IlmThreadSemaphore.h"
  57. #include "IlmThreadMutex.h"
  58. #include "Iex.h"
  59. #include <string>
  60. #include <vector>
  61. #include <fstream>
  62. #include <assert.h>
  63. #include <map>
  64. #include <algorithm>
  65. namespace Imf {
  66. using Imath::Box2i;
  67. using Imath::V2i;
  68. using std::string;
  69. using std::vector;
  70. using std::ofstream;
  71. using std::map;
  72. using std::min;
  73. using std::max;
  74. using std::swap;
  75. using IlmThread::Mutex;
  76. using IlmThread::Lock;
  77. using IlmThread::Semaphore;
  78. using IlmThread::Task;
  79. using IlmThread::TaskGroup;
  80. using IlmThread::ThreadPool;
  81. namespace {
  82. struct TOutSliceInfo
  83. {
  84. PixelType type;
  85. const char * base;
  86. size_t xStride;
  87. size_t yStride;
  88. bool zero;
  89. int xTileCoords;
  90. int yTileCoords;
  91. TOutSliceInfo (PixelType type = HALF,
  92. const char *base = 0,
  93. size_t xStride = 0,
  94. size_t yStride = 0,
  95. bool zero = false,
  96. int xTileCoords = 0,
  97. int yTileCoords = 0);
  98. };
  99. TOutSliceInfo::TOutSliceInfo (PixelType t,
  100. const char *b,
  101. size_t xs, size_t ys,
  102. bool z,
  103. int xtc,
  104. int ytc)
  105. :
  106. type (t),
  107. base (b),
  108. xStride (xs),
  109. yStride (ys),
  110. zero (z),
  111. xTileCoords (xtc),
  112. yTileCoords (ytc)
  113. {
  114. // empty
  115. }
  116. struct TileCoord
  117. {
  118. int dx;
  119. int dy;
  120. int lx;
  121. int ly;
  122. TileCoord (int xTile = 0, int yTile = 0,
  123. int xLevel = 0, int yLevel = 0)
  124. :
  125. dx (xTile), dy (yTile),
  126. lx (xLevel), ly (yLevel)
  127. {
  128. // empty
  129. }
  130. bool
  131. operator < (const TileCoord &other) const
  132. {
  133. return (ly < other.ly) ||
  134. (ly == other.ly && lx < other.lx) ||
  135. ((ly == other.ly && lx == other.lx) &&
  136. ((dy < other.dy) || (dy == other.dy && dx < other.dx)));
  137. }
  138. bool
  139. operator == (const TileCoord &other) const
  140. {
  141. return lx == other.lx &&
  142. ly == other.ly &&
  143. dx == other.dx &&
  144. dy == other.dy;
  145. }
  146. };
  147. struct BufferedTile
  148. {
  149. char * pixelData;
  150. int pixelDataSize;
  151. BufferedTile (const char *data, int size):
  152. pixelData (0),
  153. pixelDataSize(size)
  154. {
  155. pixelData = new char[pixelDataSize];
  156. memcpy (pixelData, data, pixelDataSize);
  157. }
  158. ~BufferedTile()
  159. {
  160. delete [] pixelData;
  161. }
  162. };
  163. typedef map <TileCoord, BufferedTile *> TileMap;
  164. struct TileBuffer
  165. {
  166. Array<char> buffer;
  167. const char * dataPtr;
  168. int dataSize;
  169. Compressor * compressor;
  170. TileCoord tileCoord;
  171. bool hasException;
  172. string exception;
  173. TileBuffer (Compressor *comp);
  174. ~TileBuffer ();
  175. inline void wait () {_sem.wait();}
  176. inline void post () {_sem.post();}
  177. protected:
  178. Semaphore _sem;
  179. };
  180. TileBuffer::TileBuffer (Compressor *comp):
  181. dataPtr (0),
  182. dataSize (0),
  183. compressor (comp),
  184. hasException (false),
  185. exception (),
  186. _sem (1)
  187. {
  188. // empty
  189. }
  190. TileBuffer::~TileBuffer ()
  191. {
  192. delete compressor;
  193. }
  194. } // namespace
  195. struct TiledOutputFile::Data: public Mutex
  196. {
  197. Header header; // the image header
  198. int version; // file format version
  199. TileDescription tileDesc; // describes the tile layout
  200. FrameBuffer frameBuffer; // framebuffer to write into
  201. Int64 previewPosition;
  202. LineOrder lineOrder; // the file's lineorder
  203. int minX; // data window's min x coord
  204. int maxX; // data window's max x coord
  205. int minY; // data window's min y coord
  206. int maxY; // data window's max x coord
  207. int numXLevels; // number of x levels
  208. int numYLevels; // number of y levels
  209. int * numXTiles; // number of x tiles at a level
  210. int * numYTiles; // number of y tiles at a level
  211. TileOffsets tileOffsets; // stores offsets in file for
  212. // each tile
  213. Compressor::Format format; // compressor's data format
  214. vector<TOutSliceInfo> slices; // info about channels in file
  215. OStream * os; // file stream to write to
  216. bool deleteStream;
  217. size_t maxBytesPerTileLine; // combined size of a tile line
  218. // over all channels
  219. vector<TileBuffer*> tileBuffers;
  220. size_t tileBufferSize; // size of a tile buffer
  221. Int64 tileOffsetsPosition; // position of the tile index
  222. Int64 currentPosition; // current position in the file
  223. TileMap tileMap;
  224. TileCoord nextTileToWrite;
  225. Data (bool del, int numThreads);
  226. ~Data ();
  227. inline TileBuffer * getTileBuffer (int number);
  228. // hash function from tile
  229. // buffer coords into our
  230. // vector of tile buffers
  231. TileCoord nextTileCoord (const TileCoord &a);
  232. };
  233. TiledOutputFile::Data::Data (bool del, int numThreads):
  234. numXTiles(0),
  235. numYTiles(0),
  236. os (0),
  237. deleteStream (del),
  238. tileOffsetsPosition (0)
  239. {
  240. //
  241. // We need at least one tileBuffer, but if threading is used,
  242. // to keep n threads busy we need 2*n tileBuffers
  243. //
  244. tileBuffers.resize (max (1, 2 * numThreads));
  245. }
  246. TiledOutputFile::Data::~Data ()
  247. {
  248. delete [] numXTiles;
  249. delete [] numYTiles;
  250. if (deleteStream)
  251. delete os;
  252. //
  253. // Delete all the tile buffers, if any still happen to exist
  254. //
  255. for (TileMap::iterator i = tileMap.begin(); i != tileMap.end(); ++i)
  256. delete i->second;
  257. for (size_t i = 0; i < tileBuffers.size(); i++)
  258. delete tileBuffers[i];
  259. }
  260. TileBuffer*
  261. TiledOutputFile::Data::getTileBuffer (int number)
  262. {
  263. return tileBuffers[number % tileBuffers.size()];
  264. }
  265. TileCoord
  266. TiledOutputFile::Data::nextTileCoord (const TileCoord &a)
  267. {
  268. TileCoord b = a;
  269. if (lineOrder == INCREASING_Y)
  270. {
  271. b.dx++;
  272. if (b.dx >= numXTiles[b.lx])
  273. {
  274. b.dx = 0;
  275. b.dy++;
  276. if (b.dy >= numYTiles[b.ly])
  277. {
  278. //
  279. // the next tile is in the next level
  280. //
  281. b.dy = 0;
  282. switch (tileDesc.mode)
  283. {
  284. case ONE_LEVEL:
  285. case MIPMAP_LEVELS:
  286. b.lx++;
  287. b.ly++;
  288. break;
  289. case RIPMAP_LEVELS:
  290. b.lx++;
  291. if (b.lx >= numXLevels)
  292. {
  293. b.lx = 0;
  294. b.ly++;
  295. #ifdef DEBUG
  296. assert (b.ly <= numYLevels);
  297. #endif
  298. }
  299. break;
  300. }
  301. }
  302. }
  303. }
  304. else if (lineOrder == DECREASING_Y)
  305. {
  306. b.dx++;
  307. if (b.dx >= numXTiles[b.lx])
  308. {
  309. b.dx = 0;
  310. b.dy--;
  311. if (b.dy < 0)
  312. {
  313. //
  314. // the next tile is in the next level
  315. //
  316. switch (tileDesc.mode)
  317. {
  318. case ONE_LEVEL:
  319. case MIPMAP_LEVELS:
  320. b.lx++;
  321. b.ly++;
  322. break;
  323. case RIPMAP_LEVELS:
  324. b.lx++;
  325. if (b.lx >= numXLevels)
  326. {
  327. b.lx = 0;
  328. b.ly++;
  329. #ifdef DEBUG
  330. assert (b.ly <= numYLevels);
  331. #endif
  332. }
  333. break;
  334. }
  335. if (b.ly < numYLevels)
  336. b.dy = numYTiles[b.ly] - 1;
  337. }
  338. }
  339. }
  340. return b;
  341. }
  342. namespace {
  343. void
  344. writeTileData (TiledOutputFile::Data *ofd,
  345. int dx, int dy,
  346. int lx, int ly,
  347. const char pixelData[],
  348. int pixelDataSize)
  349. {
  350. //
  351. // Store a block of pixel data in the output file, and try
  352. // to keep track of the current writing position the file,
  353. // without calling tellp() (tellp() can be fairly expensive).
  354. //
  355. Int64 currentPosition = ofd->currentPosition;
  356. ofd->currentPosition = 0;
  357. if (currentPosition == 0)
  358. currentPosition = ofd->os->tellp();
  359. ofd->tileOffsets (dx, dy, lx, ly) = currentPosition;
  360. #ifdef DEBUG
  361. assert (ofd->os->tellp() == currentPosition);
  362. #endif
  363. //
  364. // Write the tile header.
  365. //
  366. Xdr::write <StreamIO> (*ofd->os, dx);
  367. Xdr::write <StreamIO> (*ofd->os, dy);
  368. Xdr::write <StreamIO> (*ofd->os, lx);
  369. Xdr::write <StreamIO> (*ofd->os, ly);
  370. Xdr::write <StreamIO> (*ofd->os, pixelDataSize);
  371. ofd->os->write (pixelData, pixelDataSize);
  372. //
  373. // Keep current position in the file so that we can avoid
  374. // redundant seekg() operations (seekg() can be fairly expensive).
  375. //
  376. ofd->currentPosition = currentPosition +
  377. 5 * Xdr::size<int>() +
  378. pixelDataSize;
  379. }
  380. void
  381. bufferedTileWrite (TiledOutputFile::Data *ofd,
  382. int dx, int dy,
  383. int lx, int ly,
  384. const char pixelData[],
  385. int pixelDataSize)
  386. {
  387. //
  388. // Check if a tile with coordinates (dx,dy,lx,ly) has already been written.
  389. //
  390. if (ofd->tileOffsets (dx, dy, lx, ly))
  391. {
  392. THROW (Iex::ArgExc,
  393. "Attempt to write tile "
  394. "(" << dx << ", " << dy << ", " << lx << "," << ly << ") "
  395. "more than once.");
  396. }
  397. //
  398. // If tiles can be written in random order, then don't buffer anything.
  399. //
  400. if (ofd->lineOrder == RANDOM_Y)
  401. {
  402. writeTileData (ofd, dx, dy, lx, ly, pixelData, pixelDataSize);
  403. return;
  404. }
  405. //
  406. // If the tiles cannot be written in random order, then check if a
  407. // tile with coordinates (dx,dy,lx,ly) has already been buffered.
  408. //
  409. TileCoord currentTile = TileCoord(dx, dy, lx, ly);
  410. if (ofd->tileMap.find (currentTile) != ofd->tileMap.end())
  411. {
  412. THROW (Iex::ArgExc,
  413. "Attempt to write tile "
  414. "(" << dx << ", " << dy << ", " << lx << "," << ly << ") "
  415. "more than once.");
  416. }
  417. //
  418. // If all the tiles before this one have already been written to the file,
  419. // then write this tile immediately and check if we have buffered tiles
  420. // that can be written after this tile.
  421. //
  422. // Otherwise, buffer the tile so it can be written to file later.
  423. //
  424. if (ofd->nextTileToWrite == currentTile)
  425. {
  426. writeTileData (ofd, dx, dy, lx, ly, pixelData, pixelDataSize);
  427. ofd->nextTileToWrite = ofd->nextTileCoord (ofd->nextTileToWrite);
  428. TileMap::iterator i = ofd->tileMap.find (ofd->nextTileToWrite);
  429. //
  430. // Step through the tiles and write all successive buffered tiles after
  431. // the current one.
  432. //
  433. while(i != ofd->tileMap.end())
  434. {
  435. //
  436. // Write the tile, and then delete the tile's buffered data
  437. //
  438. writeTileData (ofd,
  439. i->first.dx, i->first.dy,
  440. i->first.lx, i->first.ly,
  441. i->second->pixelData,
  442. i->second->pixelDataSize);
  443. delete i->second;
  444. ofd->tileMap.erase (i);
  445. //
  446. // Proceed to the next tile
  447. //
  448. ofd->nextTileToWrite = ofd->nextTileCoord (ofd->nextTileToWrite);
  449. i = ofd->tileMap.find (ofd->nextTileToWrite);
  450. }
  451. }
  452. else
  453. {
  454. //
  455. // Create a new BufferedTile, copy the pixelData into it, and
  456. // insert it into the tileMap.
  457. //
  458. ofd->tileMap[currentTile] =
  459. new BufferedTile ((const char *)pixelData, pixelDataSize);
  460. }
  461. }
  462. void
  463. convertToXdr (TiledOutputFile::Data *ofd,
  464. Array<char>& tileBuffer,
  465. int numScanLines,
  466. int numPixelsPerScanLine)
  467. {
  468. //
  469. // Convert the contents of a TiledOutputFile's tileBuffer from the
  470. // machine's native representation to Xdr format. This function is called
  471. // by writeTile(), below, if the compressor wanted its input pixel data
  472. // in the machine's native format, but then failed to compress the data
  473. // (most compressors will expand rather than compress random input data).
  474. //
  475. // Note that this routine assumes that the machine's native representation
  476. // of the pixel data has the same size as the Xdr representation. This
  477. // makes it possible to convert the pixel data in place, without an
  478. // intermediate temporary buffer.
  479. //
  480. //
  481. // Set these to point to the start of the tile.
  482. // We will write to toPtr, and read from fromPtr.
  483. //
  484. char *writePtr = tileBuffer;
  485. const char *readPtr = writePtr;
  486. //
  487. // Iterate over all scan lines in the tile.
  488. //
  489. for (int y = 0; y < numScanLines; ++y)
  490. {
  491. //
  492. // Iterate over all slices in the file.
  493. //
  494. for (unsigned int i = 0; i < ofd->slices.size(); ++i)
  495. {
  496. const TOutSliceInfo &slice = ofd->slices[i];
  497. //
  498. // Convert the samples in place.
  499. //
  500. convertInPlace (writePtr, readPtr, slice.type,
  501. numPixelsPerScanLine);
  502. }
  503. }
  504. #ifdef DEBUG
  505. assert (writePtr == readPtr);
  506. #endif
  507. }
  508. //
  509. // A TileBufferTask encapsulates the task of copying a tile from
  510. // the user's framebuffer into a LineBuffer and compressing the data
  511. // if necessary.
  512. //
  513. class TileBufferTask: public Task
  514. {
  515. public:
  516. TileBufferTask (TaskGroup *group,
  517. TiledOutputFile::Data *ofd,
  518. int number,
  519. int dx, int dy,
  520. int lx, int ly);
  521. virtual ~TileBufferTask ();
  522. virtual void execute ();
  523. private:
  524. TiledOutputFile::Data * _ofd;
  525. TileBuffer * _tileBuffer;
  526. };
  527. TileBufferTask::TileBufferTask
  528. (TaskGroup *group,
  529. TiledOutputFile::Data *ofd,
  530. int number,
  531. int dx, int dy,
  532. int lx, int ly)
  533. :
  534. Task (group),
  535. _ofd (ofd),
  536. _tileBuffer (_ofd->getTileBuffer (number))
  537. {
  538. //
  539. // Wait for the tileBuffer to become available
  540. //
  541. _tileBuffer->wait ();
  542. _tileBuffer->tileCoord = TileCoord (dx, dy, lx, ly);
  543. }
  544. TileBufferTask::~TileBufferTask ()
  545. {
  546. //
  547. // Signal that the tile buffer is now free
  548. //
  549. _tileBuffer->post ();
  550. }
  551. void
  552. TileBufferTask::execute ()
  553. {
  554. try
  555. {
  556. //
  557. // First copy the pixel data from the frame buffer
  558. // into the tile buffer
  559. //
  560. // Convert one tile's worth of pixel data to
  561. // a machine-independent representation, and store
  562. // the result in _tileBuffer->buffer.
  563. //
  564. char *writePtr = _tileBuffer->buffer;
  565. Box2i tileRange = Imf::dataWindowForTile (_ofd->tileDesc,
  566. _ofd->minX, _ofd->maxX,
  567. _ofd->minY, _ofd->maxY,
  568. _tileBuffer->tileCoord.dx,
  569. _tileBuffer->tileCoord.dy,
  570. _tileBuffer->tileCoord.lx,
  571. _tileBuffer->tileCoord.ly);
  572. int numScanLines = tileRange.max.y - tileRange.min.y + 1;
  573. int numPixelsPerScanLine = tileRange.max.x - tileRange.min.x + 1;
  574. //
  575. // Iterate over the scan lines in the tile.
  576. //
  577. for (int y = tileRange.min.y; y <= tileRange.max.y; ++y)
  578. {
  579. //
  580. // Iterate over all image channels.
  581. //
  582. for (unsigned int i = 0; i < _ofd->slices.size(); ++i)
  583. {
  584. const TOutSliceInfo &slice = _ofd->slices[i];
  585. //
  586. // These offsets are used to facilitate both absolute
  587. // and tile-relative pixel coordinates.
  588. //
  589. int xOffset = slice.xTileCoords * tileRange.min.x;
  590. int yOffset = slice.yTileCoords * tileRange.min.y;
  591. //
  592. // Fill the tile buffer with pixel data.
  593. //
  594. if (slice.zero)
  595. {
  596. //
  597. // The frame buffer contains no data for this channel.
  598. // Store zeroes in _data->tileBuffer.
  599. //
  600. fillChannelWithZeroes (writePtr, _ofd->format, slice.type,
  601. numPixelsPerScanLine);
  602. }
  603. else
  604. {
  605. //
  606. // The frame buffer contains data for this channel.
  607. //
  608. const char *readPtr = slice.base +
  609. (y - yOffset) * slice.yStride +
  610. (tileRange.min.x - xOffset) *
  611. slice.xStride;
  612. const char *endPtr = readPtr +
  613. (numPixelsPerScanLine - 1) *
  614. slice.xStride;
  615. copyFromFrameBuffer (writePtr, readPtr, endPtr,
  616. slice.xStride, _ofd->format,
  617. slice.type);
  618. }
  619. }
  620. }
  621. //
  622. // Compress the contents of the tileBuffer,
  623. // and store the compressed data in the output file.
  624. //
  625. _tileBuffer->dataSize = writePtr - _tileBuffer->buffer;
  626. _tileBuffer->dataPtr = _tileBuffer->buffer;
  627. if (_tileBuffer->compressor)
  628. {
  629. const char *compPtr;
  630. int compSize = _tileBuffer->compressor->compressTile
  631. (_tileBuffer->dataPtr,
  632. _tileBuffer->dataSize,
  633. tileRange, compPtr);
  634. if (compSize < _tileBuffer->dataSize)
  635. {
  636. _tileBuffer->dataSize = compSize;
  637. _tileBuffer->dataPtr = compPtr;
  638. }
  639. else if (_ofd->format == Compressor::NATIVE)
  640. {
  641. //
  642. // The data did not shrink during compression, but
  643. // we cannot write to the file using native format,
  644. // so we need to convert the lineBuffer to Xdr.
  645. //
  646. convertToXdr (_ofd, _tileBuffer->buffer, numScanLines,
  647. numPixelsPerScanLine);
  648. }
  649. }
  650. }
  651. catch (std::exception &e)
  652. {
  653. if (!_tileBuffer->hasException)
  654. {
  655. _tileBuffer->exception = e.what ();
  656. _tileBuffer->hasException = true;
  657. }
  658. }
  659. catch (...)
  660. {
  661. if (!_tileBuffer->hasException)
  662. {
  663. _tileBuffer->exception = "unrecognized exception";
  664. _tileBuffer->hasException = true;
  665. }
  666. }
  667. }
  668. } // namespace
  669. TiledOutputFile::TiledOutputFile
  670. (const char fileName[],
  671. const Header &header,
  672. int numThreads)
  673. :
  674. _data (new Data (true, numThreads))
  675. {
  676. try
  677. {
  678. header.sanityCheck (true);
  679. _data->os = new StdOFStream (fileName);
  680. initialize (header);
  681. }
  682. catch (Iex::BaseExc &e)
  683. {
  684. delete _data;
  685. REPLACE_EXC (e, "Cannot open image file "
  686. "\"" << fileName << "\". " << e);
  687. throw;
  688. }
  689. catch (...)
  690. {
  691. delete _data;
  692. throw;
  693. }
  694. }
  695. TiledOutputFile::TiledOutputFile
  696. (OStream &os,
  697. const Header &header,
  698. int numThreads)
  699. :
  700. _data (new Data (false, numThreads))
  701. {
  702. try
  703. {
  704. header.sanityCheck(true);
  705. _data->os = &os;
  706. initialize (header);
  707. }
  708. catch (Iex::BaseExc &e)
  709. {
  710. delete _data;
  711. REPLACE_EXC (e, "Cannot open image file "
  712. "\"" << os.fileName() << "\". " << e);
  713. throw;
  714. }
  715. catch (...)
  716. {
  717. delete _data;
  718. throw;
  719. }
  720. }
  721. void
  722. TiledOutputFile::initialize (const Header &header)
  723. {
  724. _data->header = header;
  725. _data->lineOrder = _data->header.lineOrder();
  726. //
  727. // Check that the file is indeed tiled
  728. //
  729. _data->tileDesc = _data->header.tileDescription();
  730. //
  731. // Save the dataWindow information
  732. //
  733. const Box2i &dataWindow = _data->header.dataWindow();
  734. _data->minX = dataWindow.min.x;
  735. _data->maxX = dataWindow.max.x;
  736. _data->minY = dataWindow.min.y;
  737. _data->maxY = dataWindow.max.y;
  738. //
  739. // Precompute level and tile information to speed up utility functions
  740. //
  741. precalculateTileInfo (_data->tileDesc,
  742. _data->minX, _data->maxX,
  743. _data->minY, _data->maxY,
  744. _data->numXTiles, _data->numYTiles,
  745. _data->numXLevels, _data->numYLevels);
  746. //
  747. // Determine the first tile coordinate that we will be writing
  748. // if the file is not RANDOM_Y.
  749. //
  750. _data->nextTileToWrite = (_data->lineOrder == INCREASING_Y)?
  751. TileCoord (0, 0, 0, 0):
  752. TileCoord (0, _data->numYTiles[0] - 1, 0, 0);
  753. _data->maxBytesPerTileLine =
  754. calculateBytesPerPixel (_data->header) * _data->tileDesc.xSize;
  755. _data->tileBufferSize = _data->maxBytesPerTileLine * _data->tileDesc.ySize;
  756. //
  757. // Create all the TileBuffers and allocate their internal buffers
  758. //
  759. for (size_t i = 0; i < _data->tileBuffers.size(); i++)
  760. {
  761. _data->tileBuffers[i] = new TileBuffer (newTileCompressor
  762. (_data->header.compression(),
  763. _data->maxBytesPerTileLine,
  764. _data->tileDesc.ySize,
  765. _data->header));
  766. _data->tileBuffers[i]->buffer.resizeErase(_data->tileBufferSize);
  767. }
  768. _data->format = defaultFormat (_data->tileBuffers[0]->compressor);
  769. _data->tileOffsets = TileOffsets (_data->tileDesc.mode,
  770. _data->numXLevels,
  771. _data->numYLevels,
  772. _data->numXTiles,
  773. _data->numYTiles);
  774. _data->previewPosition = _data->header.writeTo (*_data->os, true);
  775. _data->tileOffsetsPosition = _data->tileOffsets.writeTo (*_data->os);
  776. _data->currentPosition = _data->os->tellp();
  777. }
  778. TiledOutputFile::~TiledOutputFile ()
  779. {
  780. if (_data)
  781. {
  782. {
  783. if (_data->tileOffsetsPosition > 0)
  784. {
  785. try
  786. {
  787. _data->os->seekp (_data->tileOffsetsPosition);
  788. _data->tileOffsets.writeTo (*_data->os);
  789. }
  790. catch (...)
  791. {
  792. //
  793. // We cannot safely throw any exceptions from here.
  794. // This destructor may have been called because the
  795. // stack is currently being unwound for another
  796. // exception.
  797. //
  798. }
  799. }
  800. }
  801. delete _data;
  802. }
  803. }
  804. const char *
  805. TiledOutputFile::fileName () const
  806. {
  807. return _data->os->fileName();
  808. }
  809. const Header &
  810. TiledOutputFile::header () const
  811. {
  812. return _data->header;
  813. }
  814. void
  815. TiledOutputFile::setFrameBuffer (const FrameBuffer &frameBuffer)
  816. {
  817. Lock lock (*_data);
  818. //
  819. // Check if the new frame buffer descriptor
  820. // is compatible with the image file header.
  821. //
  822. const ChannelList &channels = _data->header.channels();
  823. for (ChannelList::ConstIterator i = channels.begin();
  824. i != channels.end();
  825. ++i)
  826. {
  827. FrameBuffer::ConstIterator j = frameBuffer.find (i.name());
  828. if (j == frameBuffer.end())
  829. continue;
  830. if (i.channel().type != j.slice().type)
  831. THROW (Iex::ArgExc, "Pixel type of \"" << i.name() << "\" channel "
  832. "of output file \"" << fileName() << "\" is "
  833. "not compatible with the frame buffer's "
  834. "pixel type.");
  835. if (j.slice().xSampling != 1 || j.slice().ySampling != 1)
  836. THROW (Iex::ArgExc, "All channels in a tiled file must have"
  837. "sampling (1,1).");
  838. }
  839. //
  840. // Initialize slice table for writePixels().
  841. //
  842. vector<TOutSliceInfo> slices;
  843. for (ChannelList::ConstIterator i = channels.begin();
  844. i != channels.end();
  845. ++i)
  846. {
  847. FrameBuffer::ConstIterator j = frameBuffer.find (i.name());
  848. if (j == frameBuffer.end())
  849. {
  850. //
  851. // Channel i is not present in the frame buffer.
  852. // In the file, channel i will contain only zeroes.
  853. //
  854. slices.push_back (TOutSliceInfo (i.channel().type,
  855. 0, // base
  856. 0, // xStride,
  857. 0, // yStride,
  858. true)); // zero
  859. }
  860. else
  861. {
  862. //
  863. // Channel i is present in the frame buffer.
  864. //
  865. slices.push_back (TOutSliceInfo (j.slice().type,
  866. j.slice().base,
  867. j.slice().xStride,
  868. j.slice().yStride,
  869. false, // zero
  870. (j.slice().xTileCoords)? 1: 0,
  871. (j.slice().yTileCoords)? 1: 0));
  872. }
  873. }
  874. //
  875. // Store the new frame buffer.
  876. //
  877. _data->frameBuffer = frameBuffer;
  878. _data->slices = slices;
  879. }
  880. const FrameBuffer &
  881. TiledOutputFile::frameBuffer () const
  882. {
  883. Lock lock (*_data);
  884. return _data->frameBuffer;
  885. }
  886. void
  887. TiledOutputFile::writeTiles (int dx1, int dx2, int dy1, int dy2,
  888. int lx, int ly)
  889. {
  890. try
  891. {
  892. Lock lock (*_data);
  893. if (_data->slices.size() == 0)
  894. throw Iex::ArgExc ("No frame buffer specified "
  895. "as pixel data source.");
  896. if (!isValidTile (dx1, dy1, lx, ly) || !isValidTile (dx2, dy2, lx, ly))
  897. throw Iex::ArgExc ("Tile coordinates are invalid.");
  898. //
  899. // Determine the first and last tile coordinates in both dimensions
  900. // based on the file's lineOrder
  901. //
  902. if (dx1 > dx2)
  903. swap (dx1, dx2);
  904. if (dy1 > dy2)
  905. swap (dy1, dy2);
  906. int dyStart = dy1;
  907. int dyStop = dy2 + 1;
  908. int dY = 1;
  909. if (_data->lineOrder == DECREASING_Y)
  910. {
  911. dyStart = dy2;
  912. dyStop = dy1 - 1;
  913. dY = -1;
  914. }
  915. int numTiles = (dx2 - dx1 + 1) * (dy2 - dy1 + 1);
  916. int numTasks = min ((int)_data->tileBuffers.size(), numTiles);
  917. //
  918. // Create a task group for all tile buffer tasks. When the
  919. // task group goes out of scope, the destructor waits until
  920. // all tasks are complete.
  921. //
  922. {
  923. TaskGroup taskGroup;
  924. //
  925. // Add in the initial compression tasks to the thread pool
  926. //
  927. int nextCompBuffer = 0;
  928. int dxComp = dx1;
  929. int dyComp = dyStart;
  930. while (nextCompBuffer < numTasks)
  931. {
  932. ThreadPool::addGlobalTask (new TileBufferTask (&taskGroup,
  933. _data,
  934. nextCompBuffer++,
  935. dxComp, dyComp,
  936. lx, ly));
  937. dxComp++;
  938. if (dxComp > dx2)
  939. {
  940. dxComp = dx1;
  941. dyComp += dY;
  942. }
  943. }
  944. //
  945. // Write the compressed buffers and add in more compression
  946. // tasks until done
  947. //
  948. int nextWriteBuffer = 0;
  949. int dxWrite = dx1;
  950. int dyWrite = dyStart;
  951. while (nextWriteBuffer < numTiles)
  952. {
  953. //
  954. // Wait until the nextWriteBuffer is ready to be written
  955. //
  956. TileBuffer* writeBuffer =
  957. _data->getTileBuffer (nextWriteBuffer);
  958. writeBuffer->wait();
  959. //
  960. // Write the tilebuffer
  961. //
  962. bufferedTileWrite (_data, dxWrite, dyWrite, lx, ly,
  963. writeBuffer->dataPtr,
  964. writeBuffer->dataSize);
  965. //
  966. // Release the lock on nextWriteBuffer
  967. //
  968. writeBuffer->post();
  969. //
  970. // If there are no more tileBuffers to compress, then
  971. // only continue to write out remaining tileBuffers,
  972. // otherwise keep adding compression tasks.
  973. //
  974. if (nextCompBuffer < numTiles)
  975. {
  976. //
  977. // add nextCompBuffer as a compression Task
  978. //
  979. ThreadPool::addGlobalTask
  980. (new TileBufferTask (&taskGroup,
  981. _data,
  982. nextCompBuffer,
  983. dxComp, dyComp,
  984. lx, ly));
  985. }
  986. nextWriteBuffer++;
  987. dxWrite++;
  988. if (dxWrite > dx2)
  989. {
  990. dxWrite = dx1;
  991. dyWrite += dY;
  992. }
  993. nextCompBuffer++;
  994. dxComp++;
  995. if (dxComp > dx2)
  996. {
  997. dxComp = dx1;
  998. dyComp += dY;
  999. }
  1000. }
  1001. //
  1002. // finish all tasks
  1003. //
  1004. }
  1005. //
  1006. // Exeption handling:
  1007. //
  1008. // TileBufferTask::execute() may have encountered exceptions, but
  1009. // those exceptions occurred in another thread, not in the thread
  1010. // that is executing this call to TiledOutputFile::writeTiles().
  1011. // TileBufferTask::execute() has caught all exceptions and stored
  1012. // the exceptions' what() strings in the tile buffers.
  1013. // Now we check if any tile buffer contains a stored exception; if
  1014. // this is the case then we re-throw the exception in this thread.
  1015. // (It is possible that multiple tile buffers contain stored
  1016. // exceptions. We re-throw the first exception we find and
  1017. // ignore all others.)
  1018. //
  1019. const string *exception = 0;
  1020. for (int i = 0; i < _data->tileBuffers.size(); ++i)
  1021. {
  1022. TileBuffer *tileBuffer = _data->tileBuffers[i];
  1023. if (tileBuffer->hasException && !exception)
  1024. exception = &tileBuffer->exception;
  1025. tileBuffer->hasException = false;
  1026. }
  1027. if (exception)
  1028. throw Iex::IoExc (*exception);
  1029. }
  1030. catch (Iex::BaseExc &e)
  1031. {
  1032. REPLACE_EXC (e, "Failed to write pixel data to image "
  1033. "file \"" << fileName() << "\". " << e);
  1034. throw;
  1035. }
  1036. }
  1037. void
  1038. TiledOutputFile::writeTiles (int dx1, int dxMax, int dyMin, int dyMax, int l)
  1039. {
  1040. writeTiles (dx1, dxMax, dyMin, dyMax, l, l);
  1041. }
  1042. void
  1043. TiledOutputFile::writeTile (int dx, int dy, int lx, int ly)
  1044. {
  1045. writeTiles (dx, dx, dy, dy, lx, ly);
  1046. }
  1047. void
  1048. TiledOutputFile::writeTile (int dx, int dy, int l)
  1049. {
  1050. writeTile(dx, dy, l, l);
  1051. }
  1052. void
  1053. TiledOutputFile::copyPixels (TiledInputFile &in)
  1054. {
  1055. Lock lock (*_data);
  1056. //
  1057. // Check if this file's and and the InputFile's
  1058. // headers are compatible.
  1059. //
  1060. const Header &hdr = _data->header;
  1061. const Header &inHdr = in.header();
  1062. if (!hdr.hasTileDescription() || !inHdr.hasTileDescription())
  1063. THROW (Iex::ArgExc, "Cannot perform a quick pixel copy from image "
  1064. "file \"" << in.fileName() << "\" to image "
  1065. "file \"" << fileName() << "\". The "
  1066. "output file is tiled, but the input file is not. "
  1067. "Try using OutputFile::copyPixels() instead.");
  1068. if (!(hdr.tileDescription() == inHdr.tileDescription()))
  1069. THROW (Iex::ArgExc, "Quick pixel copy from image "
  1070. "file \"" << in.fileName() << "\" to image "
  1071. "file \"" << fileName() << "\" failed. "
  1072. "The files have different tile descriptions.");
  1073. if (!(hdr.dataWindow() == inHdr.dataWindow()))
  1074. THROW (Iex::ArgExc, "Cannot copy pixels from image "
  1075. "file \"" << in.fileName() << "\" to image "
  1076. "file \"" << fileName() << "\". The "
  1077. "files have different data windows.");
  1078. if (!(hdr.lineOrder() == inHdr.lineOrder()))
  1079. THROW (Iex::ArgExc, "Quick pixel copy from image "
  1080. "file \"" << in.fileName() << "\" to image "
  1081. "file \"" << fileName() << "\" failed. "
  1082. "The files have different line orders.");
  1083. if (!(hdr.compression() == inHdr.compression()))
  1084. THROW (Iex::ArgExc, "Quick pixel copy from image "
  1085. "file \"" << in.fileName() << "\" to image "
  1086. "file \"" << fileName() << "\" failed. "
  1087. "The files use different compression methods.");
  1088. if (!(hdr.channels() == inHdr.channels()))
  1089. THROW (Iex::ArgExc, "Quick pixel copy from image "
  1090. "file \"" << in.fileName() << "\" to image "
  1091. "file \"" << fileName() << "\" "
  1092. "failed. The files have different channel "
  1093. "lists.");
  1094. //
  1095. // Verify that no pixel data have been written to this file yet.
  1096. //
  1097. if (!_data->tileOffsets.isEmpty())
  1098. THROW (Iex::LogicExc, "Quick pixel copy from image "
  1099. "file \"" << in.fileName() << "\" to image "
  1100. "file \"" << _data->os->fileName() << "\" "
  1101. "failed. \"" << fileName() << "\" "
  1102. "already contains pixel data.");
  1103. //
  1104. // Calculate the total number of tiles in the file
  1105. //
  1106. int numAllTiles = 0;
  1107. switch (levelMode ())
  1108. {
  1109. case ONE_LEVEL:
  1110. case MIPMAP_LEVELS:
  1111. for (size_t i_l = 0; i_l < numLevels (); ++i_l)
  1112. numAllTiles += numXTiles (i_l) * numYTiles (i_l);
  1113. break;
  1114. case RIPMAP_LEVELS:
  1115. for (size_t i_ly = 0; i_ly < numYLevels (); ++i_ly)
  1116. for (size_t i_lx = 0; i_lx < numXLevels (); ++i_lx)
  1117. numAllTiles += numXTiles (i_lx) * numYTiles (i_ly);
  1118. break;
  1119. default:
  1120. throw Iex::ArgExc ("Unknown LevelMode format.");
  1121. }
  1122. for (int i = 0; i < numAllTiles; ++i)
  1123. {
  1124. const char *pixelData;
  1125. int pixelDataSize;
  1126. int dx = _data->nextTileToWrite.dx;
  1127. int dy = _data->nextTileToWrite.dy;
  1128. int lx = _data->nextTileToWrite.lx;
  1129. int ly = _data->nextTileToWrite.ly;
  1130. in.rawTileData (dx, dy, lx, ly, pixelData, pixelDataSize);
  1131. writeTileData (_data, dx, dy, lx, ly, pixelData, pixelDataSize);
  1132. }
  1133. }
  1134. void
  1135. TiledOutputFile::copyPixels (InputFile &in)
  1136. {
  1137. copyPixels (*in.tFile());
  1138. }
  1139. unsigned int
  1140. TiledOutputFile::tileXSize () const
  1141. {
  1142. return _data->tileDesc.xSize;
  1143. }
  1144. unsigned int
  1145. TiledOutputFile::tileYSize () const
  1146. {
  1147. return _data->tileDesc.ySize;
  1148. }
  1149. LevelMode
  1150. TiledOutputFile::levelMode () const
  1151. {
  1152. return _data->tileDesc.mode;
  1153. }
  1154. LevelRoundingMode
  1155. TiledOutputFile::levelRoundingMode () const
  1156. {
  1157. return _data->tileDesc.roundingMode;
  1158. }
  1159. int
  1160. TiledOutputFile::numLevels () const
  1161. {
  1162. if (levelMode() == RIPMAP_LEVELS)
  1163. THROW (Iex::LogicExc, "Error calling numLevels() on image "
  1164. "file \"" << fileName() << "\" "
  1165. "(numLevels() is not defined for RIPMAPs).");
  1166. return _data->numXLevels;
  1167. }
  1168. int
  1169. TiledOutputFile::numXLevels () const
  1170. {
  1171. return _data->numXLevels;
  1172. }
  1173. int
  1174. TiledOutputFile::numYLevels () const
  1175. {
  1176. return _data->numYLevels;
  1177. }
  1178. bool
  1179. TiledOutputFile::isValidLevel (int lx, int ly) const
  1180. {
  1181. if (lx < 0 || ly < 0)
  1182. return false;
  1183. if (levelMode() == MIPMAP_LEVELS && lx != ly)
  1184. return false;
  1185. if (lx >= numXLevels() || ly >= numYLevels())
  1186. return false;
  1187. return true;
  1188. }
  1189. int
  1190. TiledOutputFile::levelWidth (int lx) const
  1191. {
  1192. try
  1193. {
  1194. int retVal = levelSize (_data->minX, _data->maxX, lx,
  1195. _data->tileDesc.roundingMode);
  1196. return retVal;
  1197. }
  1198. catch (Iex::BaseExc &e)
  1199. {
  1200. REPLACE_EXC (e, "Error calling levelWidth() on image "
  1201. "file \"" << fileName() << "\". " << e);
  1202. throw;
  1203. }
  1204. }
  1205. int
  1206. TiledOutputFile::levelHeight (int ly) const
  1207. {
  1208. try
  1209. {
  1210. return levelSize (_data->minY, _data->maxY, ly,
  1211. _data->tileDesc.roundingMode);
  1212. }
  1213. catch (Iex::BaseExc &e)
  1214. {
  1215. REPLACE_EXC (e, "Error calling levelHeight() on image "
  1216. "file \"" << fileName() << "\". " << e);
  1217. throw;
  1218. }
  1219. }
  1220. int
  1221. TiledOutputFile::numXTiles (int lx) const
  1222. {
  1223. if (lx < 0 || lx >= _data->numXLevels)
  1224. THROW (Iex::LogicExc, "Error calling numXTiles() on image "
  1225. "file \"" << _data->os->fileName() << "\" "
  1226. "(Argument is not in valid range).");
  1227. return _data->numXTiles[lx];
  1228. }
  1229. int
  1230. TiledOutputFile::numYTiles (int ly) const
  1231. {
  1232. if (ly < 0 || ly >= _data->numYLevels)
  1233. THROW (Iex::LogicExc, "Error calling numXTiles() on image "
  1234. "file \"" << _data->os->fileName() << "\" "
  1235. "(Argument is not in valid range).");
  1236. return _data->numYTiles[ly];
  1237. }
  1238. Box2i
  1239. TiledOutputFile::dataWindowForLevel (int l) const
  1240. {
  1241. return dataWindowForLevel (l, l);
  1242. }
  1243. Box2i
  1244. TiledOutputFile::dataWindowForLevel (int lx, int ly) const
  1245. {
  1246. try
  1247. {
  1248. return Imf::dataWindowForLevel (_data->tileDesc,
  1249. _data->minX, _data->maxX,
  1250. _data->minY, _data->maxY,
  1251. lx, ly);
  1252. }
  1253. catch (Iex::BaseExc &e)
  1254. {
  1255. REPLACE_EXC (e, "Error calling dataWindowForLevel() on image "
  1256. "file \"" << fileName() << "\". " << e);
  1257. throw;
  1258. }
  1259. }
  1260. Box2i
  1261. TiledOutputFile::dataWindowForTile (int dx, int dy, int l) const
  1262. {
  1263. return dataWindowForTile (dx, dy, l, l);
  1264. }
  1265. Box2i
  1266. TiledOutputFile::dataWindowForTile (int dx, int dy, int lx, int ly) const
  1267. {
  1268. try
  1269. {
  1270. if (!isValidTile (dx, dy, lx, ly))
  1271. throw Iex::ArgExc ("Arguments not in valid range.");
  1272. return Imf::dataWindowForTile (_data->tileDesc,
  1273. _data->minX, _data->maxX,
  1274. _data->minY, _data->maxY,
  1275. dx, dy,
  1276. lx, ly);
  1277. }
  1278. catch (Iex::BaseExc &e)
  1279. {
  1280. REPLACE_EXC (e, "Error calling dataWindowForTile() on image "
  1281. "file \"" << fileName() << "\". " << e);
  1282. throw;
  1283. }
  1284. }
  1285. bool
  1286. TiledOutputFile::isValidTile (int dx, int dy, int lx, int ly) const
  1287. {
  1288. return ((lx < _data->numXLevels && lx >= 0) &&
  1289. (ly < _data->numYLevels && ly >= 0) &&
  1290. (dx < _data->numXTiles[lx] && dx >= 0) &&
  1291. (dy < _data->numYTiles[ly] && dy >= 0));
  1292. }
  1293. void
  1294. TiledOutputFile::updatePreviewImage (const PreviewRgba newPixels[])
  1295. {
  1296. Lock lock (*_data);
  1297. if (_data->previewPosition <= 0)
  1298. THROW (Iex::LogicExc, "Cannot update preview image pixels. "
  1299. "File \"" << fileName() << "\" does not "
  1300. "contain a preview image.");
  1301. //
  1302. // Store the new pixels in the header's preview image attribute.
  1303. //
  1304. PreviewImageAttribute &pia =
  1305. _data->header.typedAttribute <PreviewImageAttribute> ("preview");
  1306. PreviewImage &pi = pia.value();
  1307. PreviewRgba *pixels = pi.pixels();
  1308. int numPixels = pi.width() * pi.height();
  1309. for (int i = 0; i < numPixels; ++i)
  1310. pixels[i] = newPixels[i];
  1311. //
  1312. // Save the current file position, jump to the position in
  1313. // the file where the preview image starts, store the new
  1314. // preview image, and jump back to the saved file position.
  1315. //
  1316. Int64 savedPosition = _data->os->tellp();
  1317. try
  1318. {
  1319. _data->os->seekp (_data->previewPosition);
  1320. pia.writeValueTo (*_data->os, _data->version);
  1321. _data->os->seekp (savedPosition);
  1322. }
  1323. catch (Iex::BaseExc &e)
  1324. {
  1325. REPLACE_EXC (e, "Cannot update preview image pixels for "
  1326. "file \"" << fileName() << "\". " << e);
  1327. throw;
  1328. }
  1329. }
  1330. void
  1331. TiledOutputFile::breakTile
  1332. (int dx, int dy,
  1333. int lx, int ly,
  1334. int offset,
  1335. int length,
  1336. char c)
  1337. {
  1338. Lock lock (*_data);
  1339. Int64 position = _data->tileOffsets (dx, dy, lx, ly);
  1340. if (!position)
  1341. THROW (Iex::ArgExc,
  1342. "Cannot overwrite tile "
  1343. "(" << dx << ", " << dy << ", " << lx << "," << ly << "). "
  1344. "The tile has not yet been stored in "
  1345. "file \"" << fileName() << "\".");
  1346. _data->currentPosition = 0;
  1347. _data->os->seekp (position + offset);
  1348. for (int i = 0; i < length; ++i)
  1349. _data->os->write (&c, 1);
  1350. }
  1351. } // namespace Imf