PageRenderTime 30ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/Source/OpenEXR/IlmImf/ImfDeepTiledInputFile.cpp

https://gitlab.com/seranth/FreeImage
C++ | 1636 lines | 1020 code | 322 blank | 294 comment | 149 complexity | 3174a214ee735a82e0d598822ac0a5dc MD5 | raw file
  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. //-----------------------------------------------------------------------------
  35. //
  36. // class DeepTiledInputFile
  37. //
  38. //-----------------------------------------------------------------------------
  39. #include <ImfDeepTiledInputFile.h>
  40. #include <ImfTileDescriptionAttribute.h>
  41. #include <ImfChannelList.h>
  42. #include <ImfMisc.h>
  43. #include <ImfTiledMisc.h>
  44. #include <ImfStdIO.h>
  45. #include <ImfCompressor.h>
  46. #include "ImathBox.h"
  47. #include <ImfXdr.h>
  48. #include <ImfConvert.h>
  49. #include <ImfVersion.h>
  50. #include <ImfTileOffsets.h>
  51. #include <ImfThreading.h>
  52. #include <ImfPartType.h>
  53. #include <ImfMultiPartInputFile.h>
  54. #include "IlmThreadPool.h"
  55. #include "IlmThreadSemaphore.h"
  56. #include "IlmThreadMutex.h"
  57. #include "ImfInputStreamMutex.h"
  58. #include "ImfInputPartData.h"
  59. #include "ImathVec.h"
  60. #include "Iex.h"
  61. #include <string>
  62. #include <vector>
  63. #include <algorithm>
  64. #include <assert.h>
  65. #include <limits>
  66. #include "ImfNamespace.h"
  67. OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
  68. using IMATH_NAMESPACE::Box2i;
  69. using IMATH_NAMESPACE::V2i;
  70. using std::string;
  71. using std::vector;
  72. using std::min;
  73. using std::max;
  74. using ILMTHREAD_NAMESPACE::Mutex;
  75. using ILMTHREAD_NAMESPACE::Lock;
  76. using ILMTHREAD_NAMESPACE::Semaphore;
  77. using ILMTHREAD_NAMESPACE::Task;
  78. using ILMTHREAD_NAMESPACE::TaskGroup;
  79. using ILMTHREAD_NAMESPACE::ThreadPool;
  80. namespace {
  81. struct TInSliceInfo
  82. {
  83. PixelType typeInFrameBuffer;
  84. PixelType typeInFile;
  85. char* pointerArrayBase;
  86. size_t xStride;
  87. size_t yStride;
  88. ptrdiff_t sampleStride;
  89. bool fill;
  90. bool skip;
  91. double fillValue;
  92. int xTileCoords;
  93. int yTileCoords;
  94. TInSliceInfo (PixelType typeInFrameBuffer = HALF,
  95. char * base = NULL,
  96. PixelType typeInFile = HALF,
  97. size_t xStride = 0,
  98. size_t yStride = 0,
  99. ptrdiff_t sampleStride = 0,
  100. bool fill = false,
  101. bool skip = false,
  102. double fillValue = 0.0,
  103. int xTileCoords = 0,
  104. int yTileCoords = 0);
  105. };
  106. TInSliceInfo::TInSliceInfo (PixelType tifb,
  107. char * b,
  108. PixelType tifl,
  109. size_t xs, size_t ys,
  110. ptrdiff_t spst,
  111. bool f, bool s,
  112. double fv,
  113. int xtc,
  114. int ytc)
  115. :
  116. typeInFrameBuffer (tifb),
  117. typeInFile (tifl),
  118. pointerArrayBase (b),
  119. xStride (xs),
  120. yStride (ys),
  121. sampleStride (spst),
  122. fill (f),
  123. skip (s),
  124. fillValue (fv),
  125. xTileCoords (xtc),
  126. yTileCoords (ytc)
  127. {
  128. // empty
  129. }
  130. struct TileBuffer
  131. {
  132. Array2D<unsigned int> sampleCount;
  133. const char * uncompressedData;
  134. char * buffer;
  135. Int64 dataSize;
  136. Int64 uncompressedDataSize;
  137. Compressor * compressor;
  138. Compressor::Format format;
  139. int dx;
  140. int dy;
  141. int lx;
  142. int ly;
  143. bool hasException;
  144. string exception;
  145. TileBuffer ();
  146. ~TileBuffer ();
  147. inline void wait () {_sem.wait();}
  148. inline void post () {_sem.post();}
  149. protected:
  150. Semaphore _sem;
  151. };
  152. TileBuffer::TileBuffer ():
  153. uncompressedData (0),
  154. buffer (0),
  155. dataSize (0),
  156. compressor (0),
  157. format (defaultFormat (compressor)),
  158. dx (-1),
  159. dy (-1),
  160. lx (-1),
  161. ly (-1),
  162. hasException (false),
  163. exception (),
  164. _sem (1)
  165. {
  166. // empty
  167. }
  168. TileBuffer::~TileBuffer ()
  169. {
  170. delete compressor;
  171. }
  172. } // namespace
  173. class MultiPartInputFile;
  174. //
  175. // struct TiledInputFile::Data stores things that will be
  176. // needed between calls to readTile()
  177. //
  178. struct DeepTiledInputFile::Data: public Mutex
  179. {
  180. Header header; // the image header
  181. TileDescription tileDesc; // describes the tile layout
  182. int version; // file's version
  183. DeepFrameBuffer frameBuffer; // framebuffer to write into
  184. LineOrder lineOrder; // the file's lineorder
  185. int minX; // data window's min x coord
  186. int maxX; // data window's max x coord
  187. int minY; // data window's min y coord
  188. int maxY; // data window's max x coord
  189. int numXLevels; // number of x levels
  190. int numYLevels; // number of y levels
  191. int * numXTiles; // number of x tiles at a level
  192. int * numYTiles; // number of y tiles at a level
  193. TileOffsets tileOffsets; // stores offsets in file for
  194. // each tile
  195. bool fileIsComplete; // True if no tiles are missing
  196. // in the file
  197. vector<TInSliceInfo*> slices; // info about channels in file
  198. // ourselves? or does someone
  199. // else do it?
  200. int partNumber; // part number
  201. bool multiPartBackwardSupport; // if we are reading a multipart file
  202. // using OpenEXR 1.7 API
  203. int numThreads; // number of threads
  204. MultiPartInputFile* multiPartFile; // the MultiPartInputFile used to
  205. // support backward compatibility
  206. vector<TileBuffer*> tileBuffers; // each holds a single tile
  207. bool memoryMapped; // if the stream is memory mapped
  208. char* sampleCountSliceBase; // pointer to the start of
  209. // the sample count array
  210. ptrdiff_t sampleCountXStride; // x stride of the sample count array
  211. ptrdiff_t sampleCountYStride; // y stride of the sample count array
  212. int sampleCountXTileCoords; // the value of xTileCoords from the
  213. // sample count slice
  214. int sampleCountYTileCoords; // the value of yTileCoords from the
  215. // sample count slice
  216. Array<char> sampleCountTableBuffer; // the buffer for sample count table
  217. Compressor* sampleCountTableComp; // the decompressor for sample count table
  218. Int64 maxSampleCountTableSize; // the max size in bytes for a pixel
  219. // sample count table
  220. int combinedSampleSize; // total size of all channels combined to check sampletable size
  221. InputStreamMutex * _streamData;
  222. bool _deleteStream; // should we delete the stream
  223. Data (int numThreads);
  224. ~Data ();
  225. inline TileBuffer * getTileBuffer (int number);
  226. // hash function from tile indices
  227. // into our vector of tile buffers
  228. int& getSampleCount(int x, int y);
  229. // get the number of samples
  230. // in each pixel
  231. };
  232. DeepTiledInputFile::Data::Data (int numThreads):
  233. numXTiles (0),
  234. numYTiles (0),
  235. partNumber (-1),
  236. multiPartBackwardSupport(false),
  237. numThreads(numThreads),
  238. memoryMapped(false),
  239. _streamData(NULL),
  240. _deleteStream(false)
  241. {
  242. //
  243. // We need at least one tileBuffer, but if threading is used,
  244. // to keep n threads busy we need 2*n tileBuffers
  245. //
  246. tileBuffers.resize (max (1, 2 * numThreads));
  247. }
  248. DeepTiledInputFile::Data::~Data ()
  249. {
  250. delete [] numXTiles;
  251. delete [] numYTiles;
  252. for (size_t i = 0; i < tileBuffers.size(); i++)
  253. delete tileBuffers[i];
  254. if (multiPartBackwardSupport)
  255. delete multiPartFile;
  256. for (size_t i = 0; i < slices.size(); i++)
  257. delete slices[i];
  258. }
  259. TileBuffer*
  260. DeepTiledInputFile::Data::getTileBuffer (int number)
  261. {
  262. return tileBuffers[number % tileBuffers.size()];
  263. }
  264. int&
  265. DeepTiledInputFile::Data::getSampleCount(int x, int y)
  266. {
  267. return sampleCount(sampleCountSliceBase,
  268. sampleCountXStride,
  269. sampleCountYStride,
  270. x, y);
  271. }
  272. namespace {
  273. void
  274. readTileData (InputStreamMutex *streamData,
  275. DeepTiledInputFile::Data *ifd,
  276. int dx, int dy,
  277. int lx, int ly,
  278. char *&buffer,
  279. Int64 &dataSize,
  280. Int64 &unpackedDataSize)
  281. {
  282. //
  283. // Read a single tile block from the file and into the array pointed
  284. // to by buffer. If the file is memory-mapped, then we change where
  285. // buffer points instead of writing into the array (hence buffer needs
  286. // to be a reference to a char *).
  287. //
  288. //
  289. // Look up the location for this tile in the Index and
  290. // seek to that position if necessary
  291. //
  292. Int64 tileOffset = ifd->tileOffsets (dx, dy, lx, ly);
  293. if (tileOffset == 0)
  294. {
  295. THROW (IEX_NAMESPACE::InputExc, "Tile (" << dx << ", " << dy << ", " <<
  296. lx << ", " << ly << ") is missing.");
  297. }
  298. //
  299. // In a multi-part file, the next chunk does not need to
  300. // belong to the same part, so we have to compare the
  301. // offset here.
  302. //
  303. if ( !isMultiPart(ifd->version) )
  304. {
  305. if (streamData->currentPosition != tileOffset)
  306. streamData->is->seekg(tileOffset);
  307. }
  308. else
  309. {
  310. //
  311. // In a multi-part file, the file pointer may be moved by other
  312. // parts, so we have to ask tellg() where we are.
  313. //
  314. if (streamData->is->tellg() != tileOffset)
  315. streamData->is->seekg (tileOffset);
  316. }
  317. //
  318. // Read the first few bytes of the tile (the header).
  319. // Verify that the tile coordinates and the level number
  320. // are correct.
  321. //
  322. int tileXCoord, tileYCoord, levelX, levelY;
  323. if (isMultiPart(ifd->version))
  324. {
  325. int partNumber;
  326. Xdr::read <StreamIO> (*streamData->is, partNumber);
  327. if (partNumber != ifd->partNumber)
  328. {
  329. THROW (IEX_NAMESPACE::ArgExc, "Unexpected part number " << partNumber
  330. << ", should be " << ifd->partNumber << ".");
  331. }
  332. }
  333. Xdr::read <StreamIO> (*streamData->is, tileXCoord);
  334. Xdr::read <StreamIO> (*streamData->is, tileYCoord);
  335. Xdr::read <StreamIO> (*streamData->is, levelX);
  336. Xdr::read <StreamIO> (*streamData->is, levelY);
  337. Int64 tableSize;
  338. Xdr::read <StreamIO> (*streamData->is, tableSize);
  339. Xdr::read <StreamIO> (*streamData->is, dataSize);
  340. Xdr::read <StreamIO> (*streamData->is, unpackedDataSize);
  341. //
  342. // Skip the pixel sample count table because we have read this data.
  343. //
  344. Xdr::skip <StreamIO> (*streamData->is, tableSize);
  345. if (tileXCoord != dx)
  346. throw IEX_NAMESPACE::InputExc ("Unexpected tile x coordinate.");
  347. if (tileYCoord != dy)
  348. throw IEX_NAMESPACE::InputExc ("Unexpected tile y coordinate.");
  349. if (levelX != lx)
  350. throw IEX_NAMESPACE::InputExc ("Unexpected tile x level number coordinate.");
  351. if (levelY != ly)
  352. throw IEX_NAMESPACE::InputExc ("Unexpected tile y level number coordinate.");
  353. //
  354. // Read the pixel data.
  355. //
  356. if (streamData->is->isMemoryMapped ())
  357. buffer = streamData->is->readMemoryMapped (dataSize);
  358. else
  359. {
  360. // (TODO) check if the packed data size is too big?
  361. // (TODO) better memory management here. Don't delete buffer everytime.
  362. if (buffer != 0) delete[] buffer;
  363. buffer = new char[dataSize];
  364. streamData->is->read (buffer, dataSize);
  365. }
  366. //
  367. // Keep track of which tile is the next one in
  368. // the file, so that we can avoid redundant seekg()
  369. // operations (seekg() can be fairly expensive).
  370. //
  371. streamData->currentPosition = tileOffset + 4 * Xdr::size<int>() +
  372. 3 * Xdr::size<Int64>() +
  373. tableSize +
  374. dataSize;
  375. }
  376. void
  377. readNextTileData (InputStreamMutex *streamData,
  378. DeepTiledInputFile::Data *ifd,
  379. int &dx, int &dy,
  380. int &lx, int &ly,
  381. char * & buffer,
  382. Int64 &dataSize,
  383. Int64 &unpackedDataSize)
  384. {
  385. //
  386. // Read the next tile block from the file
  387. //
  388. //
  389. // Read the first few bytes of the tile (the header).
  390. //
  391. Xdr::read <StreamIO> (*streamData->is, dx);
  392. Xdr::read <StreamIO> (*streamData->is, dy);
  393. Xdr::read <StreamIO> (*streamData->is, lx);
  394. Xdr::read <StreamIO> (*streamData->is, ly);
  395. Int64 tableSize;
  396. Xdr::read <StreamIO> (*streamData->is, tableSize);
  397. Xdr::read <StreamIO> (*streamData->is, dataSize);
  398. Xdr::read <StreamIO> (*streamData->is, unpackedDataSize);
  399. //
  400. // Skip the pixel sample count table because we have read this data.
  401. //
  402. Xdr::skip <StreamIO> (*streamData->is, tableSize);
  403. //
  404. // Read the pixel data.
  405. //
  406. streamData->is->read (buffer, dataSize);
  407. //
  408. // Keep track of which tile is the next one in
  409. // the file, so that we can avoid redundant seekg()
  410. // operations (seekg() can be fairly expensive).
  411. //
  412. streamData->currentPosition += 4 * Xdr::size<int>() +
  413. 3 * Xdr::size<Int64>() +
  414. tableSize +
  415. dataSize;
  416. }
  417. //
  418. // A TileBufferTask encapsulates the task of uncompressing
  419. // a single tile and copying it into the frame buffer.
  420. //
  421. class TileBufferTask : public Task
  422. {
  423. public:
  424. TileBufferTask (TaskGroup *group,
  425. DeepTiledInputFile::Data *ifd,
  426. TileBuffer *tileBuffer);
  427. virtual ~TileBufferTask ();
  428. virtual void execute ();
  429. private:
  430. DeepTiledInputFile::Data * _ifd;
  431. TileBuffer * _tileBuffer;
  432. };
  433. TileBufferTask::TileBufferTask
  434. (TaskGroup *group,
  435. DeepTiledInputFile::Data *ifd,
  436. TileBuffer *tileBuffer)
  437. :
  438. Task (group),
  439. _ifd (ifd),
  440. _tileBuffer (tileBuffer)
  441. {
  442. // empty
  443. }
  444. TileBufferTask::~TileBufferTask ()
  445. {
  446. //
  447. // Signal that the tile buffer is now free
  448. //
  449. _tileBuffer->post ();
  450. }
  451. void
  452. TileBufferTask::execute ()
  453. {
  454. try
  455. {
  456. //
  457. // Calculate information about the tile
  458. //
  459. Box2i tileRange = OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile (
  460. _ifd->tileDesc,
  461. _ifd->minX, _ifd->maxX,
  462. _ifd->minY, _ifd->maxY,
  463. _tileBuffer->dx,
  464. _tileBuffer->dy,
  465. _tileBuffer->lx,
  466. _tileBuffer->ly);
  467. //
  468. // Get the size of the tile.
  469. //
  470. Array<unsigned int> numPixelsPerScanLine;
  471. numPixelsPerScanLine.resizeErase(tileRange.max.y - tileRange.min.y + 1);
  472. int sizeOfTile = 0;
  473. int maxBytesPerTileLine = 0;
  474. for (int y = tileRange.min.y; y <= tileRange.max.y; y++)
  475. {
  476. numPixelsPerScanLine[y - tileRange.min.y] = 0;
  477. int bytesPerLine = 0;
  478. for (int x = tileRange.min.x; x <= tileRange.max.x; x++)
  479. {
  480. int xOffset = _ifd->sampleCountXTileCoords * tileRange.min.x;
  481. int yOffset = _ifd->sampleCountYTileCoords * tileRange.min.y;
  482. int count = _ifd->getSampleCount(x - xOffset, y - yOffset);
  483. for (unsigned int c = 0; c < _ifd->slices.size(); ++c)
  484. {
  485. sizeOfTile += count * pixelTypeSize(_ifd->slices[c]->typeInFile);
  486. bytesPerLine += count * pixelTypeSize(_ifd->slices[c]->typeInFile);
  487. }
  488. numPixelsPerScanLine[y - tileRange.min.y] += count;
  489. }
  490. if (bytesPerLine > maxBytesPerTileLine)
  491. maxBytesPerTileLine = bytesPerLine;
  492. }
  493. // (TODO) don't do this every time.
  494. if (_tileBuffer->compressor != 0)
  495. delete _tileBuffer->compressor;
  496. _tileBuffer->compressor = newTileCompressor
  497. (_ifd->header.compression(),
  498. maxBytesPerTileLine,
  499. _ifd->tileDesc.ySize,
  500. _ifd->header);
  501. //
  502. // Uncompress the data, if necessary
  503. //
  504. if (_tileBuffer->compressor && _tileBuffer->dataSize < Int64(sizeOfTile))
  505. {
  506. _tileBuffer->format = _tileBuffer->compressor->format();
  507. _tileBuffer->dataSize = _tileBuffer->compressor->uncompressTile
  508. (_tileBuffer->buffer, _tileBuffer->dataSize,
  509. tileRange, _tileBuffer->uncompressedData);
  510. }
  511. else
  512. {
  513. //
  514. // If the line is uncompressed, it's in XDR format,
  515. // regardless of the compressor's output format.
  516. //
  517. _tileBuffer->format = Compressor::XDR;
  518. _tileBuffer->uncompressedData = _tileBuffer->buffer;
  519. }
  520. //
  521. // Convert the tile of pixel data back from the machine-independent
  522. // representation, and store the result in the frame buffer.
  523. //
  524. const char *readPtr = _tileBuffer->uncompressedData;
  525. // points to where we
  526. // read from in the
  527. // tile block
  528. //
  529. // Iterate over the scan lines in the tile.
  530. //
  531. for (int y = tileRange.min.y; y <= tileRange.max.y; ++y)
  532. {
  533. //
  534. // Iterate over all image channels.
  535. //
  536. for (unsigned int i = 0; i < _ifd->slices.size(); ++i)
  537. {
  538. TInSliceInfo &slice = *_ifd->slices[i];
  539. //
  540. // These offsets are used to facilitate both
  541. // absolute and tile-relative pixel coordinates.
  542. //
  543. int xOffsetForData = (slice.xTileCoords == 0) ? 0 : tileRange.min.x;
  544. int yOffsetForData = (slice.yTileCoords == 0) ? 0 : tileRange.min.y;
  545. int xOffsetForSampleCount =
  546. (_ifd->sampleCountXTileCoords == 0) ? 0 : tileRange.min.x;
  547. int yOffsetForSampleCount =
  548. (_ifd->sampleCountYTileCoords == 0) ? 0 : tileRange.min.y;
  549. //
  550. // Fill the frame buffer with pixel data.
  551. //
  552. if (slice.skip)
  553. {
  554. //
  555. // The file contains data for this channel, but
  556. // the frame buffer contains no slice for this channel.
  557. //
  558. skipChannel (readPtr, slice.typeInFile,
  559. numPixelsPerScanLine[y - tileRange.min.y]);
  560. }
  561. else
  562. {
  563. //
  564. // The frame buffer contains a slice for this channel.
  565. //
  566. copyIntoDeepFrameBuffer (readPtr, slice.pointerArrayBase,
  567. _ifd->sampleCountSliceBase,
  568. _ifd->sampleCountXStride,
  569. _ifd->sampleCountYStride,
  570. y,
  571. tileRange.min.x,
  572. tileRange.max.x,
  573. xOffsetForSampleCount, yOffsetForSampleCount,
  574. xOffsetForData, yOffsetForData,
  575. slice.sampleStride,
  576. slice.xStride,
  577. slice.yStride,
  578. slice.fill,
  579. slice.fillValue, _tileBuffer->format,
  580. slice.typeInFrameBuffer,
  581. slice.typeInFile);
  582. }
  583. }
  584. }
  585. }
  586. catch (std::exception &e)
  587. {
  588. if (!_tileBuffer->hasException)
  589. {
  590. _tileBuffer->exception = e.what ();
  591. _tileBuffer->hasException = true;
  592. }
  593. }
  594. catch (...)
  595. {
  596. if (!_tileBuffer->hasException)
  597. {
  598. _tileBuffer->exception = "unrecognized exception";
  599. _tileBuffer->hasException = true;
  600. }
  601. }
  602. }
  603. TileBufferTask *
  604. newTileBufferTask
  605. (TaskGroup *group,
  606. DeepTiledInputFile::Data *ifd,
  607. int number,
  608. int dx, int dy,
  609. int lx, int ly)
  610. {
  611. //
  612. // Wait for a tile buffer to become available,
  613. // fill the buffer with raw data from the file,
  614. // and create a new TileBufferTask whose execute()
  615. // method will uncompress the tile and copy the
  616. // tile's pixels into the frame buffer.
  617. //
  618. TileBuffer *tileBuffer = ifd->getTileBuffer (number);
  619. try
  620. {
  621. tileBuffer->wait();
  622. tileBuffer->dx = dx;
  623. tileBuffer->dy = dy;
  624. tileBuffer->lx = lx;
  625. tileBuffer->ly = ly;
  626. tileBuffer->uncompressedData = 0;
  627. readTileData (ifd->_streamData, ifd, dx, dy, lx, ly,
  628. tileBuffer->buffer,
  629. tileBuffer->dataSize,
  630. tileBuffer->uncompressedDataSize);
  631. }
  632. catch (...)
  633. {
  634. //
  635. // Reading from the file caused an exception.
  636. // Signal that the tile buffer is free, and
  637. // re-throw the exception.
  638. //
  639. tileBuffer->post();
  640. throw;
  641. }
  642. return new TileBufferTask (group, ifd, tileBuffer);
  643. }
  644. } // namespace
  645. DeepTiledInputFile::DeepTiledInputFile (const char fileName[], int numThreads):
  646. _data (new Data (numThreads))
  647. {
  648. _data->_deleteStream=true;
  649. //
  650. // This constructor is called when a user
  651. // explicitly wants to read a tiled file.
  652. //
  653. IStream* is = 0;
  654. try
  655. {
  656. is = new StdIFStream (fileName);
  657. readMagicNumberAndVersionField(*is, _data->version);
  658. //
  659. // Compatibility to read multpart file.
  660. //
  661. if (isMultiPart(_data->version))
  662. {
  663. compatibilityInitialize(*is);
  664. }
  665. else
  666. {
  667. _data->_streamData = new InputStreamMutex();
  668. _data->_streamData->is = is;
  669. _data->header.readFrom (*_data->_streamData->is, _data->version);
  670. initialize();
  671. _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete,false,true);
  672. _data->_streamData->currentPosition = _data->_streamData->is->tellg();
  673. }
  674. }
  675. catch (IEX_NAMESPACE::BaseExc &e)
  676. {
  677. if (is) delete is;
  678. if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData;
  679. if (_data) delete _data;
  680. REPLACE_EXC (e, "Cannot open image file "
  681. "\"" << fileName << "\". " << e);
  682. throw;
  683. }
  684. catch (...)
  685. {
  686. if (is) delete is;
  687. if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData;
  688. if (_data) delete _data;
  689. throw;
  690. }
  691. }
  692. DeepTiledInputFile::DeepTiledInputFile (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is, int numThreads):
  693. _data (new Data (numThreads))
  694. {
  695. _data->_streamData=0;
  696. _data->_deleteStream=false;
  697. //
  698. // This constructor is called when a user
  699. // explicitly wants to read a tiled file.
  700. //
  701. try
  702. {
  703. readMagicNumberAndVersionField(is, _data->version);
  704. //
  705. // Backward compatibility to read multpart file.
  706. //
  707. if (isMultiPart(_data->version))
  708. {
  709. compatibilityInitialize(is);
  710. }
  711. else
  712. {
  713. _data->_streamData = new InputStreamMutex();
  714. _data->_streamData->is = &is;
  715. _data->header.readFrom (*_data->_streamData->is, _data->version);
  716. initialize();
  717. // file is guaranteed not to be multipart, but is deep
  718. _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete, false,true);
  719. _data->memoryMapped = _data->_streamData->is->isMemoryMapped();
  720. _data->_streamData->currentPosition = _data->_streamData->is->tellg();
  721. }
  722. }
  723. catch (IEX_NAMESPACE::BaseExc &e)
  724. {
  725. if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData;
  726. if (_data) delete _data;
  727. REPLACE_EXC (e, "Cannot open image file "
  728. "\"" << is.fileName() << "\". " << e);
  729. throw;
  730. }
  731. catch (...)
  732. {
  733. if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData;
  734. if (_data) delete _data;
  735. throw;
  736. }
  737. }
  738. DeepTiledInputFile::DeepTiledInputFile (const Header &header,
  739. OPENEXR_IMF_INTERNAL_NAMESPACE::IStream *is,
  740. int version,
  741. int numThreads) :
  742. _data (new Data (numThreads))
  743. {
  744. _data->_streamData->is = is;
  745. _data->_deleteStream=false;
  746. //
  747. // This constructor called by class Imf::InputFile
  748. // when a user wants to just read an image file, and
  749. // doesn't care or know if the file is tiled.
  750. // No need to have backward compatibility here, because
  751. // we have the header.
  752. //
  753. _data->header = header;
  754. _data->version = version;
  755. initialize();
  756. _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete,false,true);
  757. _data->memoryMapped = is->isMemoryMapped();
  758. _data->_streamData->currentPosition = _data->_streamData->is->tellg();
  759. }
  760. DeepTiledInputFile::DeepTiledInputFile (InputPartData* part) :
  761. _data (new Data (part->numThreads))
  762. {
  763. _data->_deleteStream=false;
  764. multiPartInitialize(part);
  765. }
  766. void
  767. DeepTiledInputFile::compatibilityInitialize(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is)
  768. {
  769. is.seekg(0);
  770. //
  771. // Construct a MultiPartInputFile, initialize TiledInputFile
  772. // with the part 0 data.
  773. // (TODO) maybe change the third parameter of the constructor of MultiPartInputFile later.
  774. //
  775. _data->multiPartFile = new MultiPartInputFile(is, _data->numThreads);
  776. _data->multiPartBackwardSupport = true;
  777. InputPartData* part = _data->multiPartFile->getPart(0);
  778. multiPartInitialize(part);
  779. }
  780. void
  781. DeepTiledInputFile::multiPartInitialize(InputPartData* part)
  782. {
  783. if (isTiled(part->header.type()) == false)
  784. THROW (IEX_NAMESPACE::ArgExc, "Can't build a DeepTiledInputFile from a part of type " << part->header.type());
  785. _data->_streamData = part->mutex;
  786. _data->header = part->header;
  787. _data->version = part->version;
  788. _data->partNumber = part->partNumber;
  789. _data->memoryMapped = _data->_streamData->is->isMemoryMapped();
  790. initialize();
  791. _data->tileOffsets.readFrom(part->chunkOffsets , _data->fileIsComplete);
  792. _data->_streamData->currentPosition = _data->_streamData->is->tellg();
  793. }
  794. void
  795. DeepTiledInputFile::initialize ()
  796. {
  797. if (_data->partNumber == -1)
  798. if (_data->header.type() != DEEPTILE)
  799. throw IEX_NAMESPACE::ArgExc ("Expected a deep tiled file but the file is not deep tiled.");
  800. if(_data->header.version()!=1)
  801. {
  802. THROW(IEX_NAMESPACE::ArgExc, "Version " << _data->header.version() << " not supported for deeptiled images in this version of the library");
  803. }
  804. _data->header.sanityCheck (true);
  805. _data->tileDesc = _data->header.tileDescription();
  806. _data->lineOrder = _data->header.lineOrder();
  807. //
  808. // Save the dataWindow information
  809. //
  810. const Box2i &dataWindow = _data->header.dataWindow();
  811. _data->minX = dataWindow.min.x;
  812. _data->maxX = dataWindow.max.x;
  813. _data->minY = dataWindow.min.y;
  814. _data->maxY = dataWindow.max.y;
  815. //
  816. // Precompute level and tile information to speed up utility functions
  817. //
  818. precalculateTileInfo (_data->tileDesc,
  819. _data->minX, _data->maxX,
  820. _data->minY, _data->maxY,
  821. _data->numXTiles, _data->numYTiles,
  822. _data->numXLevels, _data->numYLevels);
  823. //
  824. // Create all the TileBuffers and allocate their internal buffers
  825. //
  826. _data->tileOffsets = TileOffsets (_data->tileDesc.mode,
  827. _data->numXLevels,
  828. _data->numYLevels,
  829. _data->numXTiles,
  830. _data->numYTiles);
  831. for (size_t i = 0; i < _data->tileBuffers.size(); i++)
  832. _data->tileBuffers[i] = new TileBuffer ();
  833. _data->maxSampleCountTableSize = _data->tileDesc.ySize *
  834. _data->tileDesc.xSize *
  835. sizeof(int);
  836. _data->sampleCountTableBuffer.resizeErase(_data->maxSampleCountTableSize);
  837. _data->sampleCountTableComp = newCompressor(_data->header.compression(),
  838. _data->maxSampleCountTableSize,
  839. _data->header);
  840. const ChannelList & c=_data->header.channels();
  841. _data->combinedSampleSize=0;
  842. for(ChannelList::ConstIterator i=c.begin();i!=c.end();i++)
  843. {
  844. switch( i.channel().type )
  845. {
  846. case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF :
  847. _data->combinedSampleSize+=Xdr::size<half>();
  848. break;
  849. case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT :
  850. _data->combinedSampleSize+=Xdr::size<float>();
  851. break;
  852. case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT :
  853. _data->combinedSampleSize+=Xdr::size<unsigned int>();
  854. break;
  855. default :
  856. THROW(IEX_NAMESPACE::ArgExc, "Bad type for channel " << i.name() << " initializing deepscanline reader");
  857. }
  858. }
  859. }
  860. DeepTiledInputFile::~DeepTiledInputFile ()
  861. {
  862. if (!_data->memoryMapped)
  863. for (size_t i = 0; i < _data->tileBuffers.size(); i++)
  864. if (_data->tileBuffers[i]->buffer != 0)
  865. delete [] _data->tileBuffers[i]->buffer;
  866. if (_data->_deleteStream)
  867. delete _data->_streamData->is;
  868. //
  869. // (TODO) we should have a way to tell if the stream data is owned by this file or
  870. // by a parent multipart file.
  871. //
  872. if (_data->partNumber == -1)
  873. delete _data->_streamData;
  874. delete _data;
  875. }
  876. const char *
  877. DeepTiledInputFile::fileName () const
  878. {
  879. return _data->_streamData->is->fileName();
  880. }
  881. const Header &
  882. DeepTiledInputFile::header () const
  883. {
  884. return _data->header;
  885. }
  886. int
  887. DeepTiledInputFile::version () const
  888. {
  889. return _data->version;
  890. }
  891. void
  892. DeepTiledInputFile::setFrameBuffer (const DeepFrameBuffer &frameBuffer)
  893. {
  894. Lock lock (*_data->_streamData);
  895. //
  896. // Set the frame buffer
  897. //
  898. //
  899. // Check if the new frame buffer descriptor is
  900. // compatible with the image file header.
  901. //
  902. const ChannelList &channels = _data->header.channels();
  903. for (DeepFrameBuffer::ConstIterator j = frameBuffer.begin();
  904. j != frameBuffer.end();
  905. ++j)
  906. {
  907. ChannelList::ConstIterator i = channels.find (j.name());
  908. if (i == channels.end())
  909. continue;
  910. if (i.channel().xSampling != j.slice().xSampling ||
  911. i.channel().ySampling != j.slice().ySampling)
  912. THROW (IEX_NAMESPACE::ArgExc, "X and/or y subsampling factors "
  913. "of \"" << i.name() << "\" channel "
  914. "of input file \"" << fileName() << "\" are "
  915. "not compatible with the frame buffer's "
  916. "subsampling factors.");
  917. }
  918. //
  919. // Store the pixel sample count table.
  920. // (TODO) Support for different sampling rates?
  921. //
  922. const Slice& sampleCountSlice = frameBuffer.getSampleCountSlice();
  923. if (sampleCountSlice.base == 0)
  924. {
  925. throw IEX_NAMESPACE::ArgExc ("Invalid base pointer, please set a proper sample count slice.");
  926. }
  927. else
  928. {
  929. _data->sampleCountSliceBase = sampleCountSlice.base;
  930. _data->sampleCountXStride = sampleCountSlice.xStride;
  931. _data->sampleCountYStride = sampleCountSlice.yStride;
  932. _data->sampleCountXTileCoords = sampleCountSlice.xTileCoords;
  933. _data->sampleCountYTileCoords = sampleCountSlice.yTileCoords;
  934. }
  935. //
  936. // Initialize the slice table for readPixels().
  937. //
  938. vector<TInSliceInfo*> slices;
  939. ChannelList::ConstIterator i = channels.begin();
  940. for (DeepFrameBuffer::ConstIterator j = frameBuffer.begin();
  941. j != frameBuffer.end();
  942. ++j)
  943. {
  944. while (i != channels.end() && strcmp (i.name(), j.name()) < 0)
  945. {
  946. //
  947. // Channel i is present in the file but not
  948. // in the frame buffer; data for channel i
  949. // will be skipped during readPixels().
  950. //
  951. slices.push_back (new TInSliceInfo (i.channel().type,
  952. NULL,
  953. i.channel().type,
  954. 0, // xStride
  955. 0, // yStride
  956. 0, // sampleStride
  957. false, // fill
  958. true, // skip
  959. 0.0)); // fillValue
  960. ++i;
  961. }
  962. bool fill = false;
  963. if (i == channels.end() || strcmp (i.name(), j.name()) > 0)
  964. {
  965. //
  966. // Channel i is present in the frame buffer, but not in the file.
  967. // In the frame buffer, slice j will be filled with a default value.
  968. //
  969. fill = true;
  970. }
  971. slices.push_back (new TInSliceInfo (j.slice().type,
  972. j.slice().base,
  973. fill? j.slice().type: i.channel().type,
  974. j.slice().xStride,
  975. j.slice().yStride,
  976. j.slice().sampleStride,
  977. fill,
  978. false, // skip
  979. j.slice().fillValue,
  980. (j.slice().xTileCoords)? 1: 0,
  981. (j.slice().yTileCoords)? 1: 0));
  982. if (i != channels.end() && !fill)
  983. ++i;
  984. }
  985. // (TODO) inspect the following code. It's additional to the scanline input file.
  986. // Is this needed?
  987. while (i != channels.end())
  988. {
  989. //
  990. // Channel i is present in the file but not
  991. // in the frame buffer; data for channel i
  992. // will be skipped during readPixels().
  993. //
  994. slices.push_back (new TInSliceInfo (i.channel().type,
  995. NULL,
  996. i.channel().type,
  997. 0, // xStride
  998. 0, // yStride
  999. 0, // sampleStride
  1000. false, // fill
  1001. true, // skip
  1002. 0.0)); // fillValue
  1003. ++i;
  1004. }
  1005. //
  1006. // Store the new frame buffer.
  1007. //
  1008. _data->frameBuffer = frameBuffer;
  1009. for (size_t i = 0; i < _data->slices.size(); i++)
  1010. delete _data->slices[i];
  1011. _data->slices = slices;
  1012. }
  1013. const DeepFrameBuffer &
  1014. DeepTiledInputFile::frameBuffer () const
  1015. {
  1016. Lock lock (*_data->_streamData);
  1017. return _data->frameBuffer;
  1018. }
  1019. bool
  1020. DeepTiledInputFile::isComplete () const
  1021. {
  1022. return _data->fileIsComplete;
  1023. }
  1024. void
  1025. DeepTiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int lx, int ly)
  1026. {
  1027. //
  1028. // Read a range of tiles from the file into the framebuffer
  1029. //
  1030. try
  1031. {
  1032. Lock lock (*_data->_streamData);
  1033. if (_data->slices.size() == 0)
  1034. throw IEX_NAMESPACE::ArgExc ("No frame buffer specified "
  1035. "as pixel data destination.");
  1036. if (!isValidLevel (lx, ly))
  1037. THROW (IEX_NAMESPACE::ArgExc,
  1038. "Level coordinate "
  1039. "(" << lx << ", " << ly << ") "
  1040. "is invalid.");
  1041. //
  1042. // Determine the first and last tile coordinates in both dimensions.
  1043. // We always attempt to read the range of tiles in the order that
  1044. // they are stored in the file.
  1045. //
  1046. if (dx1 > dx2)
  1047. std::swap (dx1, dx2);
  1048. if (dy1 > dy2)
  1049. std::swap (dy1, dy2);
  1050. int dyStart = dy1;
  1051. int dyStop = dy2 + 1;
  1052. int dY = 1;
  1053. if (_data->lineOrder == DECREASING_Y)
  1054. {
  1055. dyStart = dy2;
  1056. dyStop = dy1 - 1;
  1057. dY = -1;
  1058. }
  1059. //
  1060. // Create a task group for all tile buffer tasks. When the
  1061. // task group goes out of scope, the destructor waits until
  1062. // all tasks are complete.
  1063. //
  1064. {
  1065. TaskGroup taskGroup;
  1066. int tileNumber = 0;
  1067. for (int dy = dyStart; dy != dyStop; dy += dY)
  1068. {
  1069. for (int dx = dx1; dx <= dx2; dx++)
  1070. {
  1071. if (!isValidTile (dx, dy, lx, ly))
  1072. THROW (IEX_NAMESPACE::ArgExc,
  1073. "Tile (" << dx << ", " << dy << ", " <<
  1074. lx << "," << ly << ") is not a valid tile.");
  1075. ThreadPool::addGlobalTask (newTileBufferTask (&taskGroup,
  1076. _data,
  1077. tileNumber++,
  1078. dx, dy,
  1079. lx, ly));
  1080. }
  1081. }
  1082. //
  1083. // finish all tasks
  1084. //
  1085. }
  1086. //
  1087. // Exeption handling:
  1088. //
  1089. // TileBufferTask::execute() may have encountered exceptions, but
  1090. // those exceptions occurred in another thread, not in the thread
  1091. // that is executing this call to TiledInputFile::readTiles().
  1092. // TileBufferTask::execute() has caught all exceptions and stored
  1093. // the exceptions' what() strings in the tile buffers.
  1094. // Now we check if any tile buffer contains a stored exception; if
  1095. // this is the case then we re-throw the exception in this thread.
  1096. // (It is possible that multiple tile buffers contain stored
  1097. // exceptions. We re-throw the first exception we find and
  1098. // ignore all others.)
  1099. //
  1100. const string *exception = 0;
  1101. for (size_t i = 0; i < _data->tileBuffers.size(); ++i)
  1102. {
  1103. TileBuffer *tileBuffer = _data->tileBuffers[i];
  1104. if (tileBuffer->hasException && !exception)
  1105. exception = &tileBuffer->exception;
  1106. tileBuffer->hasException = false;
  1107. }
  1108. if (exception)
  1109. throw IEX_NAMESPACE::IoExc (*exception);
  1110. }
  1111. catch (IEX_NAMESPACE::BaseExc &e)
  1112. {
  1113. REPLACE_EXC (e, "Error reading pixel data from image "
  1114. "file \"" << fileName() << "\". " << e);
  1115. throw;
  1116. }
  1117. }
  1118. void
  1119. DeepTiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int l)
  1120. {
  1121. readTiles (dx1, dx2, dy1, dy2, l, l);
  1122. }
  1123. void
  1124. DeepTiledInputFile::readTile (int dx, int dy, int lx, int ly)
  1125. {
  1126. readTiles (dx, dx, dy, dy, lx, ly);
  1127. }
  1128. void
  1129. DeepTiledInputFile::readTile (int dx, int dy, int l)
  1130. {
  1131. readTile (dx, dy, l, l);
  1132. }
  1133. void
  1134. DeepTiledInputFile::rawTileData (int &dx, int &dy,
  1135. int &lx, int &ly,
  1136. char * pixelData,
  1137. Int64 &pixelDataSize) const
  1138. {
  1139. if (!isValidTile (dx, dy, lx, ly))
  1140. throw IEX_NAMESPACE::ArgExc ("Tried to read a tile outside "
  1141. "the image file's data window.");
  1142. Int64 tileOffset = _data->tileOffsets (dx, dy, lx, ly);
  1143. if(tileOffset == 0)
  1144. {
  1145. THROW (IEX_NAMESPACE::InputExc, "Tile (" << dx << ", " << dy << ", " <<
  1146. lx << ", " << ly << ") is missing.");
  1147. }
  1148. Lock lock(*_data->_streamData);
  1149. if (_data->_streamData->is->tellg() != tileOffset)
  1150. _data->_streamData->is->seekg (tileOffset);
  1151. //
  1152. // Read the first few bytes of the tile (the header).
  1153. // Verify that the tile coordinates and the level number
  1154. // are correct.
  1155. //
  1156. int tileXCoord, tileYCoord, levelX, levelY;
  1157. if (isMultiPart(_data->version))
  1158. {
  1159. int partNumber;
  1160. Xdr::read <StreamIO> (*_data->_streamData->is, partNumber);
  1161. if (partNumber != _data->partNumber)
  1162. {
  1163. THROW (IEX_NAMESPACE::ArgExc, "Unexpected part number " << partNumber
  1164. << ", should be " << _data->partNumber << ".");
  1165. }
  1166. }
  1167. Xdr::read <StreamIO> (*_data->_streamData->is, tileXCoord);
  1168. Xdr::read <StreamIO> (*_data->_streamData->is, tileYCoord);
  1169. Xdr::read <StreamIO> (*_data->_streamData->is, levelX);
  1170. Xdr::read <StreamIO> (*_data->_streamData->is, levelY);
  1171. Int64 sampleCountTableSize;
  1172. Int64 packedDataSize;
  1173. Xdr::read <StreamIO> (*_data->_streamData->is, sampleCountTableSize);
  1174. Xdr::read <StreamIO> (*_data->_streamData->is, packedDataSize);
  1175. if (tileXCoord != dx)
  1176. throw IEX_NAMESPACE::InputExc ("Unexpected tile x coordinate.");
  1177. if (tileYCoord != dy)
  1178. throw IEX_NAMESPACE::InputExc ("Unexpected tile y coordinate.");
  1179. if (levelX != lx)
  1180. throw IEX_NAMESPACE::InputExc ("Unexpected tile x level number coordinate.");
  1181. if (levelY != ly)
  1182. throw IEX_NAMESPACE::InputExc ("Unexpected tile y level number coordinate.");
  1183. // total requirement for reading all the data
  1184. Int64 totalSizeRequired=40+sampleCountTableSize+packedDataSize;
  1185. bool big_enough = totalSizeRequired<=pixelDataSize;
  1186. pixelDataSize = totalSizeRequired;
  1187. // was the block we were given big enough?
  1188. if(!big_enough || pixelData==NULL)
  1189. {
  1190. // special case: seek stream back to start if we are at the beginning (regular reading pixels assumes it doesn't need to seek
  1191. // in single part files)
  1192. if(!isMultiPart(_data->version))
  1193. {
  1194. _data->_streamData->is->seekg(_data->_streamData->currentPosition);
  1195. }
  1196. // leave lock here - bail before reading more data
  1197. return;
  1198. }
  1199. // copy the values we have read into the output block
  1200. *(int *) (pixelData+0) = dx;
  1201. *(int *) (pixelData+4) = dy;
  1202. *(int *) (pixelData+8) = levelX;
  1203. *(int *) (pixelData+12) = levelY;
  1204. *(Int64 *) (pixelData+16) =sampleCountTableSize;
  1205. *(Int64 *) (pixelData+24) = packedDataSize;
  1206. // didn't read the unpackedsize - do that now
  1207. Xdr::read<StreamIO> (*_data->_streamData->is, *(Int64 *) (pixelData+32));
  1208. // read the actual data
  1209. _data->_streamData->is->read(pixelData+40, sampleCountTableSize+packedDataSize);
  1210. if(!isMultiPart(_data->version))
  1211. {
  1212. _data->_streamData->currentPosition+=sampleCountTableSize+packedDataSize+40;
  1213. }
  1214. // leave lock here
  1215. }
  1216. unsigned int
  1217. DeepTiledInputFile::tileXSize () const
  1218. {
  1219. return _data->tileDesc.xSize;
  1220. }
  1221. unsigned int
  1222. DeepTiledInputFile::tileYSize () const
  1223. {
  1224. return _data->tileDesc.ySize;
  1225. }
  1226. LevelMode
  1227. DeepTiledInputFile::levelMode () const
  1228. {
  1229. return _data->tileDesc.mode;
  1230. }
  1231. LevelRoundingMode
  1232. DeepTiledInputFile::levelRoundingMode () const
  1233. {
  1234. return _data->tileDesc.roundingMode;
  1235. }
  1236. int
  1237. DeepTiledInputFile::numLevels () const
  1238. {
  1239. if (levelMode() == RIPMAP_LEVELS)
  1240. THROW (IEX_NAMESPACE::LogicExc, "Error calling numLevels() on image "
  1241. "file \"" << fileName() << "\" "
  1242. "(numLevels() is not defined for files "
  1243. "with RIPMAP level mode).");
  1244. return _data->numXLevels;
  1245. }
  1246. int
  1247. DeepTiledInputFile::numXLevels () const
  1248. {
  1249. return _data->numXLevels;
  1250. }
  1251. int
  1252. DeepTiledInputFile::numYLevels () const
  1253. {
  1254. return _data->numYLevels;
  1255. }
  1256. bool
  1257. DeepTiledInputFile::isValidLevel (int lx, int ly) const
  1258. {
  1259. if (lx < 0 || ly < 0)
  1260. return false;
  1261. if (levelMode() == MIPMAP_LEVELS && lx != ly)
  1262. return false;
  1263. if (lx >= numXLevels() || ly >= numYLevels())
  1264. return false;
  1265. return true;
  1266. }
  1267. int
  1268. DeepTiledInputFile::levelWidth (int lx) const
  1269. {
  1270. try
  1271. {
  1272. return levelSize (_data->minX, _data->maxX, lx,
  1273. _data->tileDesc.roundingMode);
  1274. }
  1275. catch (IEX_NAMESPACE::BaseExc &e)
  1276. {
  1277. REPLACE_EXC (e, "Error calling levelWidth() on image "
  1278. "file \"" << fileName() << "\". " << e);
  1279. throw;
  1280. }
  1281. }
  1282. int
  1283. DeepTiledInputFile::levelHeight (int ly) const
  1284. {
  1285. try
  1286. {
  1287. return levelSize (_data->minY, _data->maxY, ly,
  1288. _data->tileDesc.roundingMode);
  1289. }
  1290. catch (IEX_NAMESPACE::BaseExc &e)
  1291. {
  1292. REPLACE_EXC (e, "Error calling levelHeight() on image "
  1293. "file \"" << fileName() << "\". " << e);
  1294. throw;
  1295. }
  1296. }
  1297. int
  1298. DeepTiledInputFile::numXTiles (int lx) const
  1299. {
  1300. if (lx < 0 || lx >= _data->numXLevels)
  1301. {
  1302. THROW (IEX_NAMESPACE::ArgExc, "Error calling numXTiles() on image "
  1303. "file \"" << _data->_streamData->is->fileName() << "\" "
  1304. "(Argument is not in valid range).");
  1305. }
  1306. return _data->numXTiles[lx];
  1307. }
  1308. int
  1309. DeepTiledInputFile::numYTiles (int ly) const
  1310. {
  1311. if (ly < 0 || ly >= _data->numYLevels)
  1312. {
  1313. THROW (IEX_NAMESPACE::ArgExc, "Error calling numYTiles() on image "
  1314. "file \"" << _data->_streamData->is->fileName() << "\" "