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 //
37 //      class DeepTiledInputFile
38 //
39 //-----------------------------------------------------------------------------
40 
41 #include <ImfDeepTiledInputFile.h>
42 #include <ImfTileDescriptionAttribute.h>
43 #include <ImfChannelList.h>
44 #include <ImfMisc.h>
45 #include <ImfTiledMisc.h>
46 #include <ImfStdIO.h>
47 #include <ImfCompressor.h>
48 #include "ImathBox.h"
49 #include <ImfXdr.h>
50 #include <ImfConvert.h>
51 #include <ImfVersion.h>
52 #include <ImfTileOffsets.h>
53 #include <ImfThreading.h>
54 #include <ImfPartType.h>
55 #include <ImfMultiPartInputFile.h>
56 #include "IlmThreadPool.h"
57 #include "IlmThreadSemaphore.h"
58 #include "IlmThreadMutex.h"
59 #include "ImfInputStreamMutex.h"
60 #include "ImfInputPartData.h"
61 #include "ImathVec.h"
62 #include "Iex.h"
63 #include <string>
64 #include <vector>
65 #include <algorithm>
66 #include <assert.h>
67 #include <limits>
68 
69 #include "ImfNamespace.h"
70 
71 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
72 
73 using IMATH_NAMESPACE::Box2i;
74 using IMATH_NAMESPACE::V2i;
75 using std::string;
76 using std::vector;
77 using std::min;
78 using std::max;
79 using ILMTHREAD_NAMESPACE::Mutex;
80 using ILMTHREAD_NAMESPACE::Lock;
81 using ILMTHREAD_NAMESPACE::Semaphore;
82 using ILMTHREAD_NAMESPACE::Task;
83 using ILMTHREAD_NAMESPACE::TaskGroup;
84 using ILMTHREAD_NAMESPACE::ThreadPool;
85 
86 namespace {
87 
88 struct TInSliceInfo
89 {
90     PixelType           typeInFrameBuffer;
91     PixelType           typeInFile;
92     char*               pointerArrayBase;
93     size_t              xStride;
94     size_t              yStride;
95     ptrdiff_t           sampleStride;
96     bool                fill;
97     bool                skip;
98     double              fillValue;
99     int                 xTileCoords;
100     int                 yTileCoords;
101 
102     TInSliceInfo (PixelType typeInFrameBuffer = HALF,
103                   char * base = NULL,
104                   PixelType typeInFile = HALF,
105                   size_t xStride = 0,
106                   size_t yStride = 0,
107                   ptrdiff_t sampleStride = 0,
108                   bool fill = false,
109                   bool skip = false,
110                   double fillValue = 0.0,
111                   int xTileCoords = 0,
112                   int yTileCoords = 0);
113 };
114 
115 
TInSliceInfo(PixelType tifb,char * b,PixelType tifl,size_t xs,size_t ys,ptrdiff_t spst,bool f,bool s,double fv,int xtc,int ytc)116 TInSliceInfo::TInSliceInfo (PixelType tifb,
117                             char * b,
118                             PixelType tifl,
119                             size_t xs, size_t ys,
120                             ptrdiff_t spst,
121                             bool f, bool s,
122                             double fv,
123                             int xtc,
124                             int ytc)
125 :
126     typeInFrameBuffer (tifb),
127     typeInFile (tifl),
128     pointerArrayBase (b),
129     xStride (xs),
130     yStride (ys),
131     sampleStride (spst),
132     fill (f),
133     skip (s),
134     fillValue (fv),
135     xTileCoords (xtc),
136     yTileCoords (ytc)
137 {
138     // empty
139 }
140 
141 
142 struct TileBuffer
143 {
144     Array2D<unsigned int>       sampleCount;
145     const char *                uncompressedData;
146     char *                      buffer;
147     Int64                         dataSize;
148     Int64                         uncompressedDataSize;
149     Compressor *                compressor;
150     Compressor::Format          format;
151     int                         dx;
152     int                         dy;
153     int                         lx;
154     int                         ly;
155     bool                        hasException;
156     string                      exception;
157 
158      TileBuffer ();
159     ~TileBuffer ();
160 
wait__anon5e3d8a3c0111::TileBuffer161     inline void         wait () {_sem.wait();}
post__anon5e3d8a3c0111::TileBuffer162     inline void         post () {_sem.post();}
163 
164  protected:
165 
166     Semaphore _sem;
167 };
168 
169 
TileBuffer()170 TileBuffer::TileBuffer ():
171     uncompressedData (0),
172     buffer (0),
173     dataSize (0),
174     compressor (0),
175     format (defaultFormat (compressor)),
176     dx (-1),
177     dy (-1),
178     lx (-1),
179     ly (-1),
180     hasException (false),
181     exception (),
182     _sem (1)
183 {
184     // empty
185 }
186 
187 
~TileBuffer()188 TileBuffer::~TileBuffer ()
189 {
190     delete compressor;
191 }
192 
193 } // namespace
194 
195 
196 class MultiPartInputFile;
197 
198 
199 //
200 // struct TiledInputFile::Data stores things that will be
201 // needed between calls to readTile()
202 //
203 
204 struct DeepTiledInputFile::Data: public Mutex
205 {
206     Header          header;                         // the image header
207     TileDescription tileDesc;                       // describes the tile layout
208     int             version;                        // file's version
209     DeepFrameBuffer frameBuffer;                    // framebuffer to write into
210     LineOrder       lineOrder;                      // the file's lineorder
211     int             minX;                           // data window's min x coord
212     int             maxX;                           // data window's max x coord
213     int             minY;                           // data window's min y coord
214     int             maxY;                           // data window's max x coord
215 
216     int             numXLevels;                     // number of x levels
217     int             numYLevels;                     // number of y levels
218     int *           numXTiles;                      // number of x tiles at a level
219     int *           numYTiles;                      // number of y tiles at a level
220 
221     TileOffsets     tileOffsets;                    // stores offsets in file for
222     // each tile
223 
224     bool            fileIsComplete;                 // True if no tiles are missing
225                                                     // in the file
226 
227     vector<TInSliceInfo*> slices;                    // info about channels in file
228 
229                                                     // ourselves? or does someone
230                                                     // else do it?
231 
232     int             partNumber;                     // part number
233 
234     bool            multiPartBackwardSupport;       // if we are reading a multipart file
235                                                     // using OpenEXR 1.7 API
236 
237     int             numThreads;                     // number of threads
238 
239     MultiPartInputFile* multiPartFile;              // the MultiPartInputFile used to
240                                                     // support backward compatibility
241 
242     vector<TileBuffer*> tileBuffers;                // each holds a single tile
243 
244     bool            memoryMapped;                   // if the stream is memory mapped
245 
246     char*           sampleCountSliceBase;           // pointer to the start of
247                                                     // the sample count array
248     ptrdiff_t       sampleCountXStride;             // x stride of the sample count array
249     ptrdiff_t       sampleCountYStride;             // y stride of the sample count array
250 
251     int             sampleCountXTileCoords;         // the value of xTileCoords from the
252                                                     // sample count slice
253     int             sampleCountYTileCoords;         // the value of yTileCoords from the
254                                                     // sample count slice
255 
256     Array<char>     sampleCountTableBuffer;         // the buffer for sample count table
257 
258     Compressor*     sampleCountTableComp;           // the decompressor for sample count table
259 
260     Int64           maxSampleCountTableSize;        // the max size in bytes for a pixel
261                                                     // sample count table
262     int             combinedSampleSize;             // total size of all channels combined to check sampletable size
263 
264     InputStreamMutex *  _streamData;
265     bool                _deleteStream; // should we delete the stream
266      Data (int numThreads);
267     ~Data ();
268 
269     inline TileBuffer * getTileBuffer (int number);
270                                                     // hash function from tile indices
271                                                     // into our vector of tile buffers
272 
273     int&                getSampleCount(int x, int y);
274                                                     // get the number of samples
275                                                     // in each pixel
276 };
277 
278 
Data(int numThreads)279 DeepTiledInputFile::Data::Data (int numThreads):
280     numXTiles (0),
281     numYTiles (0),
282     partNumber (-1),
283     multiPartBackwardSupport(false),
284     numThreads(numThreads),
285     memoryMapped(false),
286     _streamData(NULL),
287     _deleteStream(false)
288 {
289     //
290     // We need at least one tileBuffer, but if threading is used,
291     // to keep n threads busy we need 2*n tileBuffers
292     //
293 
294     tileBuffers.resize (max (1, 2 * numThreads));
295 }
296 
297 
~Data()298 DeepTiledInputFile::Data::~Data ()
299 {
300     delete [] numXTiles;
301     delete [] numYTiles;
302 
303     for (size_t i = 0; i < tileBuffers.size(); i++)
304         delete tileBuffers[i];
305 
306     if (multiPartBackwardSupport)
307         delete multiPartFile;
308 
309     for (size_t i = 0; i < slices.size(); i++)
310         delete slices[i];
311 }
312 
313 
314 TileBuffer*
getTileBuffer(int number)315 DeepTiledInputFile::Data::getTileBuffer (int number)
316 {
317     return tileBuffers[number % tileBuffers.size()];
318 }
319 
320 
321 int&
getSampleCount(int x,int y)322 DeepTiledInputFile::Data::getSampleCount(int x, int y)
323 {
324     return sampleCount(sampleCountSliceBase,
325                        sampleCountXStride,
326                        sampleCountYStride,
327                        x, y);
328 }
329 
330 
331 namespace {
332 
333 void
readTileData(InputStreamMutex * streamData,DeepTiledInputFile::Data * ifd,int dx,int dy,int lx,int ly,char * & buffer,Int64 & dataSize,Int64 & unpackedDataSize)334 readTileData (InputStreamMutex *streamData,
335               DeepTiledInputFile::Data *ifd,
336               int dx, int dy,
337               int lx, int ly,
338               char *&buffer,
339               Int64 &dataSize,
340               Int64 &unpackedDataSize)
341 {
342     //
343     // Read a single tile block from the file and into the array pointed
344     // to by buffer.  If the file is memory-mapped, then we change where
345     // buffer points instead of writing into the array (hence buffer needs
346     // to be a reference to a char *).
347     //
348 
349     //
350     // Look up the location for this tile in the Index and
351     // seek to that position if necessary
352     //
353 
354     Int64 tileOffset = ifd->tileOffsets (dx, dy, lx, ly);
355 
356     if (tileOffset == 0)
357     {
358         THROW (IEX_NAMESPACE::InputExc, "Tile (" << dx << ", " << dy << ", " <<
359                               lx << ", " << ly << ") is missing.");
360     }
361 
362     //
363     // In a multi-part file, the next chunk does not need to
364     // belong to the same part, so we have to compare the
365     // offset here.
366     //
367 
368     if ( !isMultiPart(ifd->version) )
369     {
370         if (streamData->currentPosition != tileOffset)
371             streamData->is->seekg(tileOffset);
372     }
373     else
374     {
375         //
376         // In a multi-part file, the file pointer may be moved by other
377         // parts, so we have to ask tellg() where we are.
378         //
379         if (streamData->is->tellg() != tileOffset)
380             streamData->is->seekg (tileOffset);
381     }
382 
383     //
384     // Read the first few bytes of the tile (the header).
385     // Verify that the tile coordinates and the level number
386     // are correct.
387     //
388 
389     int tileXCoord, tileYCoord, levelX, levelY;
390 
391     if (isMultiPart(ifd->version))
392     {
393         int partNumber;
394         Xdr::read <StreamIO> (*streamData->is, partNumber);
395         if (partNumber != ifd->partNumber)
396         {
397             THROW (IEX_NAMESPACE::ArgExc, "Unexpected part number " << partNumber
398                    << ", should be " << ifd->partNumber << ".");
399         }
400     }
401 
402     Xdr::read <StreamIO> (*streamData->is, tileXCoord);
403     Xdr::read <StreamIO> (*streamData->is, tileYCoord);
404     Xdr::read <StreamIO> (*streamData->is, levelX);
405     Xdr::read <StreamIO> (*streamData->is, levelY);
406 
407     Int64 tableSize;
408     Xdr::read <StreamIO> (*streamData->is, tableSize);
409 
410     Xdr::read <StreamIO> (*streamData->is, dataSize);
411     Xdr::read <StreamIO> (*streamData->is, unpackedDataSize);
412 
413 
414     //
415     // Skip the pixel sample count table because we have read this data.
416     //
417 
418     Xdr::skip <StreamIO> (*streamData->is, tableSize);
419 
420 
421     if (tileXCoord != dx)
422         throw IEX_NAMESPACE::InputExc ("Unexpected tile x coordinate.");
423 
424     if (tileYCoord != dy)
425         throw IEX_NAMESPACE::InputExc ("Unexpected tile y coordinate.");
426 
427     if (levelX != lx)
428         throw IEX_NAMESPACE::InputExc ("Unexpected tile x level number coordinate.");
429 
430     if (levelY != ly)
431         throw IEX_NAMESPACE::InputExc ("Unexpected tile y level number coordinate.");
432 
433     //
434     // Read the pixel data.
435     //
436 
437     if (streamData->is->isMemoryMapped ())
438         buffer = streamData->is->readMemoryMapped (dataSize);
439     else
440     {
441         // (TODO) check if the packed data size is too big?
442         // (TODO) better memory management here. Don't delete buffer everytime.
443         if (buffer != 0) delete[] buffer;
444         buffer = new char[dataSize];
445         streamData->is->read (buffer, dataSize);
446     }
447 
448     //
449     // Keep track of which tile is the next one in
450     // the file, so that we can avoid redundant seekg()
451     // operations (seekg() can be fairly expensive).
452     //
453 
454     streamData->currentPosition = tileOffset + 4 * Xdr::size<int>() +
455                                   3 * Xdr::size<Int64>()            +
456                                   tableSize                         +
457                                   dataSize;
458 }
459 
460 
461 void
readNextTileData(InputStreamMutex * streamData,DeepTiledInputFile::Data * ifd,int & dx,int & dy,int & lx,int & ly,char * & buffer,Int64 & dataSize,Int64 & unpackedDataSize)462 readNextTileData (InputStreamMutex *streamData,
463                   DeepTiledInputFile::Data *ifd,
464                   int &dx, int &dy,
465                   int &lx, int &ly,
466                   char * & buffer,
467                   Int64 &dataSize,
468                   Int64 &unpackedDataSize)
469 {
470     //
471     // Read the next tile block from the file
472     //
473 
474     //
475     // Read the first few bytes of the tile (the header).
476     //
477 
478     Xdr::read <StreamIO> (*streamData->is, dx);
479     Xdr::read <StreamIO> (*streamData->is, dy);
480     Xdr::read <StreamIO> (*streamData->is, lx);
481     Xdr::read <StreamIO> (*streamData->is, ly);
482 
483     Int64 tableSize;
484     Xdr::read <StreamIO> (*streamData->is, tableSize);
485 
486     Xdr::read <StreamIO> (*streamData->is, dataSize);
487     Xdr::read <StreamIO> (*streamData->is, unpackedDataSize);
488 
489     //
490     // Skip the pixel sample count table because we have read this data.
491     //
492 
493     Xdr::skip <StreamIO> (*streamData->is, tableSize);
494 
495     //
496     // Read the pixel data.
497     //
498 
499     streamData->is->read (buffer, dataSize);
500 
501     //
502     // Keep track of which tile is the next one in
503     // the file, so that we can avoid redundant seekg()
504     // operations (seekg() can be fairly expensive).
505     //
506 
507     streamData->currentPosition += 4 * Xdr::size<int>()   +
508                                    3 * Xdr::size<Int64>() +
509                                    tableSize              +
510                                    dataSize;
511 }
512 
513 
514 //
515 // A TileBufferTask encapsulates the task of uncompressing
516 // a single tile and copying it into the frame buffer.
517 //
518 
519 class TileBufferTask : public Task
520 {
521   public:
522 
523     TileBufferTask (TaskGroup *group,
524                     DeepTiledInputFile::Data *ifd,
525                     TileBuffer *tileBuffer);
526 
527     virtual ~TileBufferTask ();
528 
529     virtual void                execute ();
530 
531   private:
532 
533     DeepTiledInputFile::Data *      _ifd;
534     TileBuffer *                _tileBuffer;
535 };
536 
537 
TileBufferTask(TaskGroup * group,DeepTiledInputFile::Data * ifd,TileBuffer * tileBuffer)538 TileBufferTask::TileBufferTask
539     (TaskGroup *group,
540      DeepTiledInputFile::Data *ifd,
541      TileBuffer *tileBuffer)
542 :
543     Task (group),
544     _ifd (ifd),
545     _tileBuffer (tileBuffer)
546 {
547     // empty
548 }
549 
550 
~TileBufferTask()551 TileBufferTask::~TileBufferTask ()
552 {
553     //
554     // Signal that the tile buffer is now free
555     //
556 
557     _tileBuffer->post ();
558 }
559 
560 
561 void
execute()562 TileBufferTask::execute ()
563 {
564     try
565     {
566         //
567         // Calculate information about the tile
568         //
569 
570         Box2i tileRange = OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile (
571                 _ifd->tileDesc,
572                 _ifd->minX, _ifd->maxX,
573                 _ifd->minY, _ifd->maxY,
574                 _tileBuffer->dx,
575                 _tileBuffer->dy,
576                 _tileBuffer->lx,
577                 _tileBuffer->ly);
578 
579         //
580         // Get the size of the tile.
581         //
582 
583         Array<unsigned int> numPixelsPerScanLine;
584         numPixelsPerScanLine.resizeErase(tileRange.max.y - tileRange.min.y + 1);
585 
586         int sizeOfTile = 0;
587         int maxBytesPerTileLine = 0;
588 
589         for (int y = tileRange.min.y; y <= tileRange.max.y; y++)
590         {
591             numPixelsPerScanLine[y - tileRange.min.y] = 0;
592 
593             int bytesPerLine = 0;
594 
595             for (int x = tileRange.min.x; x <= tileRange.max.x; x++)
596             {
597                 int xOffset = _ifd->sampleCountXTileCoords * tileRange.min.x;
598                 int yOffset = _ifd->sampleCountYTileCoords * tileRange.min.y;
599 
600                 int count = _ifd->getSampleCount(x - xOffset, y - yOffset);
601                 for (unsigned int c = 0; c < _ifd->slices.size(); ++c)
602                 {
603                     sizeOfTile += count * pixelTypeSize(_ifd->slices[c]->typeInFile);
604                     bytesPerLine += count * pixelTypeSize(_ifd->slices[c]->typeInFile);
605                 }
606                 numPixelsPerScanLine[y - tileRange.min.y] += count;
607             }
608 
609             if (bytesPerLine > maxBytesPerTileLine)
610                 maxBytesPerTileLine = bytesPerLine;
611         }
612 
613         // (TODO) don't do this every time.
614         if (_tileBuffer->compressor != 0)
615             delete _tileBuffer->compressor;
616         _tileBuffer->compressor = newTileCompressor
617                                   (_ifd->header.compression(),
618                                    maxBytesPerTileLine,
619                                    _ifd->tileDesc.ySize,
620                                    _ifd->header);
621 
622         //
623         // Uncompress the data, if necessary
624         //
625 
626         if (_tileBuffer->compressor && _tileBuffer->dataSize < Int64(sizeOfTile))
627         {
628             _tileBuffer->format = _tileBuffer->compressor->format();
629 
630             _tileBuffer->dataSize = _tileBuffer->compressor->uncompressTile
631                 (_tileBuffer->buffer, _tileBuffer->dataSize,
632                  tileRange, _tileBuffer->uncompressedData);
633         }
634         else
635         {
636             //
637             // If the line is uncompressed, it's in XDR format,
638             // regardless of the compressor's output format.
639             //
640 
641             _tileBuffer->format = Compressor::XDR;
642             _tileBuffer->uncompressedData = _tileBuffer->buffer;
643         }
644 
645         //
646         // Convert the tile of pixel data back from the machine-independent
647         // representation, and store the result in the frame buffer.
648         //
649 
650         const char *readPtr = _tileBuffer->uncompressedData;
651                                                         // points to where we
652                                                         // read from in the
653                                                         // tile block
654 
655         //
656         // Iterate over the scan lines in the tile.
657         //
658 
659         for (int y = tileRange.min.y; y <= tileRange.max.y; ++y)
660         {
661             //
662             // Iterate over all image channels.
663             //
664 
665             for (unsigned int i = 0; i < _ifd->slices.size(); ++i)
666             {
667                 TInSliceInfo &slice = *_ifd->slices[i];
668 
669                 //
670                 // These offsets are used to facilitate both
671                 // absolute and tile-relative pixel coordinates.
672                 //
673 
674                 int xOffsetForData = (slice.xTileCoords == 0) ? 0 : tileRange.min.x;
675                 int yOffsetForData = (slice.yTileCoords == 0) ? 0 : tileRange.min.y;
676                 int xOffsetForSampleCount =
677                         (_ifd->sampleCountXTileCoords == 0) ? 0 : tileRange.min.x;
678                 int yOffsetForSampleCount =
679                         (_ifd->sampleCountYTileCoords == 0) ? 0 : tileRange.min.y;
680 
681                 //
682                 // Fill the frame buffer with pixel data.
683                 //
684 
685                 if (slice.skip)
686                 {
687                     //
688                     // The file contains data for this channel, but
689                     // the frame buffer contains no slice for this channel.
690                     //
691 
692                     skipChannel (readPtr, slice.typeInFile,
693                                  numPixelsPerScanLine[y - tileRange.min.y]);
694                 }
695                 else
696                 {
697                     //
698                     // The frame buffer contains a slice for this channel.
699                     //
700 
701                     copyIntoDeepFrameBuffer (readPtr, slice.pointerArrayBase,
702                                              _ifd->sampleCountSliceBase,
703                                              _ifd->sampleCountXStride,
704                                              _ifd->sampleCountYStride,
705                                              y,
706                                              tileRange.min.x,
707                                              tileRange.max.x,
708                                              xOffsetForSampleCount, yOffsetForSampleCount,
709                                              xOffsetForData, yOffsetForData,
710                                              slice.sampleStride,
711                                              slice.xStride,
712                                              slice.yStride,
713                                              slice.fill,
714                                              slice.fillValue, _tileBuffer->format,
715                                              slice.typeInFrameBuffer,
716                                              slice.typeInFile);
717                 }
718             }
719         }
720     }
721     catch (std::exception &e)
722     {
723         if (!_tileBuffer->hasException)
724         {
725             _tileBuffer->exception = e.what ();
726             _tileBuffer->hasException = true;
727         }
728     }
729     catch (...)
730     {
731         if (!_tileBuffer->hasException)
732         {
733             _tileBuffer->exception = "unrecognized exception";
734             _tileBuffer->hasException = true;
735         }
736     }
737 }
738 
739 
740 TileBufferTask *
newTileBufferTask(TaskGroup * group,DeepTiledInputFile::Data * ifd,int number,int dx,int dy,int lx,int ly)741 newTileBufferTask
742     (TaskGroup *group,
743      DeepTiledInputFile::Data *ifd,
744      int number,
745      int dx, int dy,
746      int lx, int ly)
747 {
748     //
749     // Wait for a tile buffer to become available,
750     // fill the buffer with raw data from the file,
751     // and create a new TileBufferTask whose execute()
752     // method will uncompress the tile and copy the
753     // tile's pixels into the frame buffer.
754     //
755 
756     TileBuffer *tileBuffer = ifd->getTileBuffer (number);
757 
758     try
759     {
760         tileBuffer->wait();
761 
762         tileBuffer->dx = dx;
763         tileBuffer->dy = dy;
764         tileBuffer->lx = lx;
765         tileBuffer->ly = ly;
766 
767         tileBuffer->uncompressedData = 0;
768 
769         readTileData (ifd->_streamData, ifd, dx, dy, lx, ly,
770                       tileBuffer->buffer,
771                       tileBuffer->dataSize,
772                       tileBuffer->uncompressedDataSize);
773     }
774     catch (...)
775     {
776         //
777         // Reading from the file caused an exception.
778         // Signal that the tile buffer is free, and
779         // re-throw the exception.
780         //
781 
782         tileBuffer->post();
783         throw;
784     }
785 
786     return new TileBufferTask (group, ifd, tileBuffer);
787 }
788 
789 
790 } // namespace
791 
792 
DeepTiledInputFile(const char fileName[],int numThreads)793 DeepTiledInputFile::DeepTiledInputFile (const char fileName[], int numThreads):
794     _data (new Data (numThreads))
795 {
796     _data->_deleteStream=true;
797     //
798     // This constructor is called when a user
799     // explicitly wants to read a tiled file.
800     //
801 
802     IStream* is = 0;
803     try
804     {
805         is = new StdIFStream (fileName);
806         readMagicNumberAndVersionField(*is, _data->version);
807 
808         //
809         // Compatibility to read multpart file.
810         //
811         if (isMultiPart(_data->version))
812         {
813             compatibilityInitialize(*is);
814         }
815         else
816         {
817             _data->_streamData = new InputStreamMutex();
818             _data->_streamData->is = is;
819             _data->header.readFrom (*_data->_streamData->is, _data->version);
820             initialize();
821             _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete,false,true);
822             _data->_streamData->currentPosition = _data->_streamData->is->tellg();
823         }
824     }
825     catch (IEX_NAMESPACE::BaseExc &e)
826     {
827         if (is)          delete is;
828         if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData;
829         if (_data)       delete _data;
830 
831         REPLACE_EXC (e, "Cannot open image file "
832                         "\"" << fileName << "\". " << e);
833         throw;
834     }
835     catch (...)
836     {
837         if (is)          delete is;
838         if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData;
839         if (_data)       delete _data;
840 
841         throw;
842     }
843 }
844 
845 
DeepTiledInputFile(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream & is,int numThreads)846 DeepTiledInputFile::DeepTiledInputFile (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is, int numThreads):
847     _data (new Data (numThreads))
848 {
849     _data->_streamData=0;
850     _data->_deleteStream=false;
851 
852     //
853     // This constructor is called when a user
854     // explicitly wants to read a tiled file.
855     //
856 
857     try
858     {
859         readMagicNumberAndVersionField(is, _data->version);
860 
861         //
862         // Backward compatibility to read multpart file.
863         //
864         if (isMultiPart(_data->version))
865         {
866             compatibilityInitialize(is);
867         }
868         else
869         {
870             _data->_streamData = new InputStreamMutex();
871             _data->_streamData->is = &is;
872             _data->header.readFrom (*_data->_streamData->is, _data->version);
873             initialize();
874             // file is guaranteed not to be multipart, but is deep
875             _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete, false,true);
876             _data->memoryMapped = _data->_streamData->is->isMemoryMapped();
877             _data->_streamData->currentPosition = _data->_streamData->is->tellg();
878         }
879     }
880     catch (IEX_NAMESPACE::BaseExc &e)
881     {
882         if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData;
883         if (_data)       delete _data;
884 
885         REPLACE_EXC (e, "Cannot open image file "
886                         "\"" << is.fileName() << "\". " << e);
887         throw;
888     }
889     catch (...)
890     {
891         if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData;
892         if (_data)       delete _data;
893 
894         throw;
895     }
896 }
897 
898 
DeepTiledInputFile(const Header & header,OPENEXR_IMF_INTERNAL_NAMESPACE::IStream * is,int version,int numThreads)899 DeepTiledInputFile::DeepTiledInputFile (const Header &header,
900                                         OPENEXR_IMF_INTERNAL_NAMESPACE::IStream *is,
901                                         int version,
902                                         int numThreads) :
903     _data (new Data (numThreads))
904 
905 {
906     _data->_streamData->is = is;
907     _data->_deleteStream=false;
908 
909     //
910     // This constructor called by class Imf::InputFile
911     // when a user wants to just read an image file, and
912     // doesn't care or know if the file is tiled.
913     // No need to have backward compatibility here, because
914     // we have the header.
915     //
916 
917     _data->header = header;
918     _data->version = version;
919     initialize();
920     _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete,false,true);
921     _data->memoryMapped = is->isMemoryMapped();
922     _data->_streamData->currentPosition = _data->_streamData->is->tellg();
923 }
924 
925 
DeepTiledInputFile(InputPartData * part)926 DeepTiledInputFile::DeepTiledInputFile (InputPartData* part) :
927     _data (new Data (part->numThreads))
928 {
929     _data->_deleteStream=false;
930     multiPartInitialize(part);
931 }
932 
933 
934 void
compatibilityInitialize(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream & is)935 DeepTiledInputFile::compatibilityInitialize(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is)
936 {
937     is.seekg(0);
938     //
939     // Construct a MultiPartInputFile, initialize TiledInputFile
940     // with the part 0 data.
941     // (TODO) maybe change the third parameter of the constructor of MultiPartInputFile later.
942     //
943     _data->multiPartFile = new MultiPartInputFile(is, _data->numThreads);
944     _data->multiPartBackwardSupport = true;
945     InputPartData* part = _data->multiPartFile->getPart(0);
946 
947     multiPartInitialize(part);
948 }
949 
950 
951 void
multiPartInitialize(InputPartData * part)952 DeepTiledInputFile::multiPartInitialize(InputPartData* part)
953 {
954     if (isTiled(part->header.type()) == false)
955         THROW (IEX_NAMESPACE::ArgExc, "Can't build a DeepTiledInputFile from a part of type " << part->header.type());
956 
957     _data->_streamData = part->mutex;
958     _data->header = part->header;
959     _data->version = part->version;
960     _data->partNumber = part->partNumber;
961     _data->memoryMapped = _data->_streamData->is->isMemoryMapped();
962     initialize();
963     _data->tileOffsets.readFrom(part->chunkOffsets , _data->fileIsComplete);
964     _data->_streamData->currentPosition = _data->_streamData->is->tellg();
965 }
966 
967 
968 void
initialize()969 DeepTiledInputFile::initialize ()
970 {
971     if (_data->partNumber == -1)
972         if (_data->header.type() != DEEPTILE)
973             throw IEX_NAMESPACE::ArgExc ("Expected a deep tiled file but the file is not deep tiled.");
974    if(_data->header.version()!=1)
975    {
976        THROW(IEX_NAMESPACE::ArgExc, "Version " << _data->header.version() << " not supported for deeptiled images in this version of the library");
977    }
978 
979     _data->header.sanityCheck (true);
980 
981     _data->tileDesc = _data->header.tileDescription();
982     _data->lineOrder = _data->header.lineOrder();
983 
984     //
985     // Save the dataWindow information
986     //
987 
988     const Box2i &dataWindow = _data->header.dataWindow();
989     _data->minX = dataWindow.min.x;
990     _data->maxX = dataWindow.max.x;
991     _data->minY = dataWindow.min.y;
992     _data->maxY = dataWindow.max.y;
993 
994     //
995     // Precompute level and tile information to speed up utility functions
996     //
997 
998     precalculateTileInfo (_data->tileDesc,
999                           _data->minX, _data->maxX,
1000                           _data->minY, _data->maxY,
1001                           _data->numXTiles, _data->numYTiles,
1002                           _data->numXLevels, _data->numYLevels);
1003 
1004     //
1005     // Create all the TileBuffers and allocate their internal buffers
1006     //
1007 
1008     _data->tileOffsets = TileOffsets (_data->tileDesc.mode,
1009                                       _data->numXLevels,
1010                                       _data->numYLevels,
1011                                       _data->numXTiles,
1012                                       _data->numYTiles);
1013 
1014     for (size_t i = 0; i < _data->tileBuffers.size(); i++)
1015         _data->tileBuffers[i] = new TileBuffer ();
1016 
1017     _data->maxSampleCountTableSize = _data->tileDesc.ySize *
1018                                      _data->tileDesc.xSize *
1019                                      sizeof(int);
1020 
1021     _data->sampleCountTableBuffer.resizeErase(_data->maxSampleCountTableSize);
1022 
1023     _data->sampleCountTableComp = newCompressor(_data->header.compression(),
1024                                                 _data->maxSampleCountTableSize,
1025                                                 _data->header);
1026 
1027 
1028     const ChannelList & c=_data->header.channels();
1029     _data->combinedSampleSize=0;
1030     for(ChannelList::ConstIterator i=c.begin();i!=c.end();i++)
1031     {
1032         switch( i.channel().type )
1033         {
1034             case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF  :
1035                 _data->combinedSampleSize+=Xdr::size<half>();
1036                 break;
1037             case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT :
1038                 _data->combinedSampleSize+=Xdr::size<float>();
1039                 break;
1040             case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT  :
1041                 _data->combinedSampleSize+=Xdr::size<unsigned int>();
1042                 break;
1043             default :
1044                 THROW(IEX_NAMESPACE::ArgExc, "Bad type for channel " << i.name() << " initializing deepscanline reader");
1045         }
1046     }
1047 
1048 }
1049 
1050 
~DeepTiledInputFile()1051 DeepTiledInputFile::~DeepTiledInputFile ()
1052 {
1053     if (!_data->memoryMapped)
1054         for (size_t i = 0; i < _data->tileBuffers.size(); i++)
1055             if (_data->tileBuffers[i]->buffer != 0)
1056                 delete [] _data->tileBuffers[i]->buffer;
1057 
1058     if (_data->_deleteStream)
1059         delete _data->_streamData->is;
1060 
1061     //
1062     // (TODO) we should have a way to tell if the stream data is owned by this file or
1063     // by a parent multipart file.
1064     //
1065 
1066     if (_data->partNumber == -1)
1067         delete _data->_streamData;
1068 
1069     delete _data;
1070 }
1071 
1072 
1073 const char *
fileName() const1074 DeepTiledInputFile::fileName () const
1075 {
1076     return _data->_streamData->is->fileName();
1077 }
1078 
1079 
1080 const Header &
header() const1081 DeepTiledInputFile::header () const
1082 {
1083     return _data->header;
1084 }
1085 
1086 
1087 int
version() const1088 DeepTiledInputFile::version () const
1089 {
1090     return _data->version;
1091 }
1092 
1093 
1094 void
setFrameBuffer(const DeepFrameBuffer & frameBuffer)1095 DeepTiledInputFile::setFrameBuffer (const DeepFrameBuffer &frameBuffer)
1096 {
1097     Lock lock (*_data->_streamData);
1098 
1099     //
1100     // Set the frame buffer
1101     //
1102 
1103     //
1104     // Check if the new frame buffer descriptor is
1105     // compatible with the image file header.
1106     //
1107 
1108     const ChannelList &channels = _data->header.channels();
1109 
1110     for (DeepFrameBuffer::ConstIterator j = frameBuffer.begin();
1111          j != frameBuffer.end();
1112          ++j)
1113     {
1114         ChannelList::ConstIterator i = channels.find (j.name());
1115 
1116         if (i == channels.end())
1117             continue;
1118 
1119         if (i.channel().xSampling != j.slice().xSampling ||
1120             i.channel().ySampling != j.slice().ySampling)
1121             THROW (IEX_NAMESPACE::ArgExc, "X and/or y subsampling factors "
1122                                 "of \"" << i.name() << "\" channel "
1123                                 "of input file \"" << fileName() << "\" are "
1124                                 "not compatible with the frame buffer's "
1125                                 "subsampling factors.");
1126     }
1127 
1128     //
1129     // Store the pixel sample count table.
1130     // (TODO) Support for different sampling rates?
1131     //
1132 
1133     const Slice& sampleCountSlice = frameBuffer.getSampleCountSlice();
1134     if (sampleCountSlice.base == 0)
1135     {
1136         throw IEX_NAMESPACE::ArgExc ("Invalid base pointer, please set a proper sample count slice.");
1137     }
1138     else
1139     {
1140         _data->sampleCountSliceBase = sampleCountSlice.base;
1141         _data->sampleCountXStride = sampleCountSlice.xStride;
1142         _data->sampleCountYStride = sampleCountSlice.yStride;
1143         _data->sampleCountXTileCoords = sampleCountSlice.xTileCoords;
1144         _data->sampleCountYTileCoords = sampleCountSlice.yTileCoords;
1145     }
1146 
1147     //
1148     // Initialize the slice table for readPixels().
1149     //
1150 
1151     vector<TInSliceInfo*> slices;
1152     ChannelList::ConstIterator i = channels.begin();
1153 
1154     for (DeepFrameBuffer::ConstIterator j = frameBuffer.begin();
1155          j != frameBuffer.end();
1156          ++j)
1157     {
1158         while (i != channels.end() && strcmp (i.name(), j.name()) < 0)
1159         {
1160             //
1161             // Channel i is present in the file but not
1162             // in the frame buffer; data for channel i
1163             // will be skipped during readPixels().
1164             //
1165 
1166             slices.push_back (new TInSliceInfo (i.channel().type,
1167                                                 NULL,
1168                                                 i.channel().type,
1169                                                 0,      // xStride
1170                                                 0,      // yStride
1171                                                 0,      // sampleStride
1172                                                 false,  // fill
1173                                                 true,   // skip
1174                                                 0.0));  // fillValue
1175             ++i;
1176         }
1177 
1178         bool fill = false;
1179 
1180         if (i == channels.end() || strcmp (i.name(), j.name()) > 0)
1181         {
1182             //
1183             // Channel i is present in the frame buffer, but not in the file.
1184             // In the frame buffer, slice j will be filled with a default value.
1185             //
1186 
1187             fill = true;
1188         }
1189 
1190         slices.push_back (new TInSliceInfo (j.slice().type,
1191                                             j.slice().base,
1192                                             fill? j.slice().type: i.channel().type,
1193                                             j.slice().xStride,
1194                                             j.slice().yStride,
1195                                             j.slice().sampleStride,
1196                                             fill,
1197                                             false, // skip
1198                                             j.slice().fillValue,
1199                                             (j.slice().xTileCoords)? 1: 0,
1200                                             (j.slice().yTileCoords)? 1: 0));
1201 
1202 
1203         if (i != channels.end() && !fill)
1204             ++i;
1205     }
1206 
1207     // (TODO) inspect the following code. It's additional to the scanline input file.
1208     // Is this needed?
1209 
1210     while (i != channels.end())
1211     {
1212         //
1213         // Channel i is present in the file but not
1214         // in the frame buffer; data for channel i
1215         // will be skipped during readPixels().
1216         //
1217 
1218         slices.push_back (new TInSliceInfo (i.channel().type,
1219                                             NULL,
1220                                             i.channel().type,
1221                                             0, // xStride
1222                                             0, // yStride
1223                                             0, // sampleStride
1224                                             false,  // fill
1225                                             true, // skip
1226                                             0.0)); // fillValue
1227         ++i;
1228     }
1229 
1230     //
1231     // Store the new frame buffer.
1232     //
1233 
1234     _data->frameBuffer = frameBuffer;
1235 
1236     for (size_t i = 0; i < _data->slices.size(); i++)
1237         delete _data->slices[i];
1238     _data->slices = slices;
1239 }
1240 
1241 
1242 const DeepFrameBuffer &
frameBuffer() const1243 DeepTiledInputFile::frameBuffer () const
1244 {
1245     Lock lock (*_data->_streamData);
1246     return _data->frameBuffer;
1247 }
1248 
1249 
1250 bool
isComplete() const1251 DeepTiledInputFile::isComplete () const
1252 {
1253     return _data->fileIsComplete;
1254 }
1255 
1256 
1257 void
readTiles(int dx1,int dx2,int dy1,int dy2,int lx,int ly)1258 DeepTiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int lx, int ly)
1259 {
1260     //
1261     // Read a range of tiles from the file into the framebuffer
1262     //
1263 
1264     try
1265     {
1266         Lock lock (*_data->_streamData);
1267 
1268         if (_data->slices.size() == 0)
1269             throw IEX_NAMESPACE::ArgExc ("No frame buffer specified "
1270                                "as pixel data destination.");
1271 
1272         if (!isValidLevel (lx, ly))
1273             THROW (IEX_NAMESPACE::ArgExc,
1274                    "Level coordinate "
1275                    "(" << lx << ", " << ly << ") "
1276                    "is invalid.");
1277 
1278         //
1279         // Determine the first and last tile coordinates in both dimensions.
1280         // We always attempt to read the range of tiles in the order that
1281         // they are stored in the file.
1282         //
1283 
1284         if (dx1 > dx2)
1285             std::swap (dx1, dx2);
1286 
1287         if (dy1 > dy2)
1288             std::swap (dy1, dy2);
1289 
1290         int dyStart = dy1;
1291         int dyStop  = dy2 + 1;
1292         int dY      = 1;
1293 
1294         if (_data->lineOrder == DECREASING_Y)
1295         {
1296             dyStart = dy2;
1297             dyStop  = dy1 - 1;
1298             dY      = -1;
1299         }
1300 
1301         //
1302         // Create a task group for all tile buffer tasks.  When the
1303         // task group goes out of scope, the destructor waits until
1304         // all tasks are complete.
1305         //
1306 
1307         {
1308             TaskGroup taskGroup;
1309             int tileNumber = 0;
1310 
1311             for (int dy = dyStart; dy != dyStop; dy += dY)
1312             {
1313                 for (int dx = dx1; dx <= dx2; dx++)
1314                 {
1315                     if (!isValidTile (dx, dy, lx, ly))
1316                         THROW (IEX_NAMESPACE::ArgExc,
1317                                "Tile (" << dx << ", " << dy << ", " <<
1318                                lx << "," << ly << ") is not a valid tile.");
1319 
1320                     ThreadPool::addGlobalTask (newTileBufferTask (&taskGroup,
1321                                                                   _data,
1322                                                                   tileNumber++,
1323                                                                   dx, dy,
1324                                                                   lx, ly));
1325                 }
1326             }
1327 
1328             //
1329             // finish all tasks
1330             //
1331         }
1332 
1333         //
1334         // Exeption handling:
1335         //
1336         // TileBufferTask::execute() may have encountered exceptions, but
1337         // those exceptions occurred in another thread, not in the thread
1338         // that is executing this call to TiledInputFile::readTiles().
1339         // TileBufferTask::execute() has caught all exceptions and stored
1340         // the exceptions' what() strings in the tile buffers.
1341         // Now we check if any tile buffer contains a stored exception; if
1342         // this is the case then we re-throw the exception in this thread.
1343         // (It is possible that multiple tile buffers contain stored
1344         // exceptions.  We re-throw the first exception we find and
1345         // ignore all others.)
1346         //
1347 
1348         const string *exception = 0;
1349 
1350         for (size_t i = 0; i < _data->tileBuffers.size(); ++i)
1351         {
1352             TileBuffer *tileBuffer = _data->tileBuffers[i];
1353 
1354             if (tileBuffer->hasException && !exception)
1355                 exception = &tileBuffer->exception;
1356 
1357             tileBuffer->hasException = false;
1358         }
1359 
1360         if (exception)
1361             throw IEX_NAMESPACE::IoExc (*exception);
1362     }
1363     catch (IEX_NAMESPACE::BaseExc &e)
1364     {
1365         REPLACE_EXC (e, "Error reading pixel data from image "
1366                         "file \"" << fileName() << "\". " << e);
1367         throw;
1368     }
1369 }
1370 
1371 
1372 void
readTiles(int dx1,int dx2,int dy1,int dy2,int l)1373 DeepTiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int l)
1374 {
1375     readTiles (dx1, dx2, dy1, dy2, l, l);
1376 }
1377 
1378 
1379 void
readTile(int dx,int dy,int lx,int ly)1380 DeepTiledInputFile::readTile (int dx, int dy, int lx, int ly)
1381 {
1382     readTiles (dx, dx, dy, dy, lx, ly);
1383 }
1384 
1385 
1386 void
readTile(int dx,int dy,int l)1387 DeepTiledInputFile::readTile (int dx, int dy, int l)
1388 {
1389     readTile (dx, dy, l, l);
1390 }
1391 
1392 
1393 void
rawTileData(int & dx,int & dy,int & lx,int & ly,char * pixelData,Int64 & pixelDataSize) const1394 DeepTiledInputFile::rawTileData (int &dx, int &dy,
1395                              int &lx, int &ly,
1396                              char * pixelData,
1397                              Int64 &pixelDataSize) const
1398 {
1399      if (!isValidTile (dx, dy, lx, ly))
1400                throw IEX_NAMESPACE::ArgExc ("Tried to read a tile outside "
1401                                    "the image file's data window.");
1402 
1403      Int64 tileOffset = _data->tileOffsets (dx, dy, lx, ly);
1404 
1405      if(tileOffset == 0)
1406      {
1407         THROW (IEX_NAMESPACE::InputExc, "Tile (" << dx << ", " << dy << ", " <<
1408         lx << ", " << ly << ") is missing.");
1409      }
1410 
1411      Lock lock(*_data->_streamData);
1412 
1413      if (_data->_streamData->is->tellg() != tileOffset)
1414                                           _data->_streamData->is->seekg (tileOffset);
1415 
1416 
1417      //
1418      // Read the first few bytes of the tile (the header).
1419      // Verify that the tile coordinates and the level number
1420      // are correct.
1421      //
1422 
1423      int tileXCoord, tileYCoord, levelX, levelY;
1424 
1425      if (isMultiPart(_data->version))
1426      {
1427          int partNumber;
1428          Xdr::read <StreamIO> (*_data->_streamData->is, partNumber);
1429          if (partNumber != _data->partNumber)
1430          {
1431              THROW (IEX_NAMESPACE::ArgExc, "Unexpected part number " << partNumber
1432              << ", should be " << _data->partNumber << ".");
1433          }
1434      }
1435 
1436      Xdr::read <StreamIO> (*_data->_streamData->is, tileXCoord);
1437      Xdr::read <StreamIO> (*_data->_streamData->is, tileYCoord);
1438      Xdr::read <StreamIO> (*_data->_streamData->is, levelX);
1439      Xdr::read <StreamIO> (*_data->_streamData->is, levelY);
1440 
1441      Int64 sampleCountTableSize;
1442      Int64 packedDataSize;
1443      Xdr::read <StreamIO> (*_data->_streamData->is, sampleCountTableSize);
1444 
1445      Xdr::read <StreamIO> (*_data->_streamData->is, packedDataSize);
1446 
1447 
1448 
1449      if (tileXCoord != dx)
1450          throw IEX_NAMESPACE::InputExc ("Unexpected tile x coordinate.");
1451 
1452      if (tileYCoord != dy)
1453          throw IEX_NAMESPACE::InputExc ("Unexpected tile y coordinate.");
1454 
1455      if (levelX != lx)
1456          throw IEX_NAMESPACE::InputExc ("Unexpected tile x level number coordinate.");
1457 
1458      if (levelY != ly)
1459          throw IEX_NAMESPACE::InputExc ("Unexpected tile y level number coordinate.");
1460 
1461 
1462      // total requirement for reading all the data
1463 
1464      Int64 totalSizeRequired=40+sampleCountTableSize+packedDataSize;
1465 
1466      bool big_enough = totalSizeRequired<=pixelDataSize;
1467 
1468      pixelDataSize = totalSizeRequired;
1469 
1470      // was the block we were given big enough?
1471      if(!big_enough || pixelData==NULL)
1472      {
1473          // special case: seek stream back to start if we are at the beginning (regular reading pixels assumes it doesn't need to seek
1474          // in single part files)
1475          if(!isMultiPart(_data->version))
1476          {
1477              _data->_streamData->is->seekg(_data->_streamData->currentPosition);
1478          }
1479          // leave lock here - bail before reading more data
1480          return;
1481      }
1482 
1483      // copy the values we have read into the output block
1484      *(int *) (pixelData+0) = dx;
1485      *(int *) (pixelData+4) = dy;
1486      *(int *) (pixelData+8) = levelX;
1487      *(int *) (pixelData+12) = levelY;
1488      *(Int64 *) (pixelData+16) =sampleCountTableSize;
1489      *(Int64 *) (pixelData+24) = packedDataSize;
1490 
1491      // didn't read the unpackedsize - do that now
1492      Xdr::read<StreamIO> (*_data->_streamData->is, *(Int64 *) (pixelData+32));
1493 
1494      // read the actual data
1495      _data->_streamData->is->read(pixelData+40, sampleCountTableSize+packedDataSize);
1496 
1497 
1498      if(!isMultiPart(_data->version))
1499      {
1500          _data->_streamData->currentPosition+=sampleCountTableSize+packedDataSize+40;
1501      }
1502 
1503      // leave lock here
1504 
1505 
1506 }
1507 
1508 
1509 unsigned int
tileXSize() const1510 DeepTiledInputFile::tileXSize () const
1511 {
1512     return _data->tileDesc.xSize;
1513 }
1514 
1515 
1516 unsigned int
tileYSize() const1517 DeepTiledInputFile::tileYSize () const
1518 {
1519     return _data->tileDesc.ySize;
1520 }
1521 
1522 
1523 LevelMode
levelMode() const1524 DeepTiledInputFile::levelMode () const
1525 {
1526     return _data->tileDesc.mode;
1527 }
1528 
1529 
1530 LevelRoundingMode
levelRoundingMode() const1531 DeepTiledInputFile::levelRoundingMode () const
1532 {
1533     return _data->tileDesc.roundingMode;
1534 }
1535 
1536 
1537 int
numLevels() const1538 DeepTiledInputFile::numLevels () const
1539 {
1540     if (levelMode() == RIPMAP_LEVELS)
1541         THROW (IEX_NAMESPACE::LogicExc, "Error calling numLevels() on image "
1542                               "file \"" << fileName() << "\" "
1543                               "(numLevels() is not defined for files "
1544                               "with RIPMAP level mode).");
1545 
1546     return _data->numXLevels;
1547 }
1548 
1549 
1550 int
numXLevels() const1551 DeepTiledInputFile::numXLevels () const
1552 {
1553     return _data->numXLevels;
1554 }
1555 
1556 
1557 int
numYLevels() const1558 DeepTiledInputFile::numYLevels () const
1559 {
1560     return _data->numYLevels;
1561 }
1562 
1563 
1564 bool
isValidLevel(int lx,int ly) const1565 DeepTiledInputFile::isValidLevel (int lx, int ly) const
1566 {
1567     if (lx < 0 || ly < 0)
1568         return false;
1569 
1570     if (levelMode() == MIPMAP_LEVELS && lx != ly)
1571         return false;
1572 
1573     if (lx >= numXLevels() || ly >= numYLevels())
1574         return false;
1575 
1576     return true;
1577 }
1578 
1579 
1580 int
levelWidth(int lx) const1581 DeepTiledInputFile::levelWidth (int lx) const
1582 {
1583     try
1584     {
1585         return levelSize (_data->minX, _data->maxX, lx,
1586                           _data->tileDesc.roundingMode);
1587     }
1588     catch (IEX_NAMESPACE::BaseExc &e)
1589     {
1590         REPLACE_EXC (e, "Error calling levelWidth() on image "
1591                         "file \"" << fileName() << "\". " << e);
1592         throw;
1593     }
1594 }
1595 
1596 
1597 int
levelHeight(int ly) const1598 DeepTiledInputFile::levelHeight (int ly) const
1599 {
1600     try
1601     {
1602         return levelSize (_data->minY, _data->maxY, ly,
1603                           _data->tileDesc.roundingMode);
1604     }
1605     catch (IEX_NAMESPACE::BaseExc &e)
1606     {
1607         REPLACE_EXC (e, "Error calling levelHeight() on image "
1608                         "file \"" << fileName() << "\". " << e);
1609         throw;
1610     }
1611 }
1612 
1613 
1614 int
numXTiles(int lx) const1615 DeepTiledInputFile::numXTiles (int lx) const
1616 {
1617     if (lx < 0 || lx >= _data->numXLevels)
1618     {
1619         THROW (IEX_NAMESPACE::ArgExc, "Error calling numXTiles() on image "
1620                             "file \"" << _data->_streamData->is->fileName() << "\" "
1621                             "(Argument is not in valid range).");
1622 
1623     }
1624 
1625     return _data->numXTiles[lx];
1626 }
1627 
1628 
1629 int
numYTiles(int ly) const1630 DeepTiledInputFile::numYTiles (int ly) const
1631 {
1632     if (ly < 0 || ly >= _data->numYLevels)
1633     {
1634         THROW (IEX_NAMESPACE::ArgExc, "Error calling numYTiles() on image "
1635                             "file \"" << _data->_streamData->is->fileName() << "\" "
1636                             "(Argument is not in valid range).");
1637     }
1638 
1639     return _data->numYTiles[ly];
1640 }
1641 
1642 
1643 Box2i
dataWindowForLevel(int l) const1644 DeepTiledInputFile::dataWindowForLevel (int l) const
1645 {
1646     return dataWindowForLevel (l, l);
1647 }
1648 
1649 
1650 Box2i
dataWindowForLevel(int lx,int ly) const1651 DeepTiledInputFile::dataWindowForLevel (int lx, int ly) const
1652 {
1653     try
1654     {
1655         return OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForLevel (
1656                 _data->tileDesc,
1657                 _data->minX, _data->maxX,
1658                 _data->minY, _data->maxY,
1659                 lx, ly);
1660     }
1661     catch (IEX_NAMESPACE::BaseExc &e)
1662     {
1663         REPLACE_EXC (e, "Error calling dataWindowForLevel() on image "
1664                         "file \"" << fileName() << "\". " << e);
1665         throw;
1666     }
1667 }
1668 
1669 
1670 Box2i
dataWindowForTile(int dx,int dy,int l) const1671 DeepTiledInputFile::dataWindowForTile (int dx, int dy, int l) const
1672 {
1673     return dataWindowForTile (dx, dy, l, l);
1674 }
1675 
1676 
1677 Box2i
dataWindowForTile(int dx,int dy,int lx,int ly) const1678 DeepTiledInputFile::dataWindowForTile (int dx, int dy, int lx, int ly) const
1679 {
1680     try
1681     {
1682         if (!isValidTile (dx, dy, lx, ly))
1683             throw IEX_NAMESPACE::ArgExc ("Arguments not in valid range.");
1684 
1685         return OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile (
1686                 _data->tileDesc,
1687                 _data->minX, _data->maxX,
1688                 _data->minY, _data->maxY,
1689                 dx, dy, lx, ly);
1690     }
1691     catch (IEX_NAMESPACE::BaseExc &e)
1692     {
1693         REPLACE_EXC (e, "Error calling dataWindowForTile() on image "
1694                         "file \"" << fileName() << "\". " << e);
1695         throw;
1696     }
1697 }
1698 
1699 
1700 bool
isValidTile(int dx,int dy,int lx,int ly) const1701 DeepTiledInputFile::isValidTile (int dx, int dy, int lx, int ly) const
1702 {
1703     return ((lx < _data->numXLevels && lx >= 0) &&
1704             (ly < _data->numYLevels && ly >= 0) &&
1705             (dx < _data->numXTiles[lx] && dx >= 0) &&
1706             (dy < _data->numYTiles[ly] && dy >= 0));
1707 }
1708 
1709 
1710 void
readPixelSampleCounts(int dx1,int dx2,int dy1,int dy2,int lx,int ly)1711 DeepTiledInputFile::readPixelSampleCounts (int dx1, int dx2,
1712                                            int dy1, int dy2,
1713                                            int lx,  int ly)
1714 {
1715     Int64 savedFilePos = 0;
1716 
1717     try
1718     {
1719         Lock lock (*_data->_streamData);
1720 
1721         savedFilePos = _data->_streamData->is->tellg();
1722 
1723 
1724         if (!isValidLevel (lx, ly))
1725         {
1726             THROW (IEX_NAMESPACE::ArgExc,
1727                    "Level coordinate "
1728                    "(" << lx << ", " << ly << ") "
1729                    "is invalid.");
1730         }
1731 
1732         if (dx1 > dx2)
1733             std::swap (dx1, dx2);
1734 
1735         if (dy1 > dy2)
1736             std::swap (dy1, dy2);
1737 
1738         int dyStart = dy1;
1739         int dyStop  = dy2 + 1;
1740         int dY      = 1;
1741 
1742         if (_data->lineOrder == DECREASING_Y)
1743         {
1744             dyStart = dy2;
1745             dyStop  = dy1 - 1;
1746             dY      = -1;
1747         }
1748 
1749         // (TODO) Check if we have read the sample counts for those tiles,
1750         // if we have, no need to read again.
1751         for (int dy = dyStart; dy != dyStop; dy += dY)
1752         {
1753             for (int dx = dx1; dx <= dx2; dx++)
1754             {
1755 
1756                 if (!isValidTile (dx, dy, lx, ly))
1757                 {
1758                     THROW (IEX_NAMESPACE::ArgExc,
1759                            "Tile (" << dx << ", " << dy << ", " <<
1760                            lx << "," << ly << ") is not a valid tile.");
1761                 }
1762 
1763                 Box2i tileRange = OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile (
1764                         _data->tileDesc,
1765                         _data->minX, _data->maxX,
1766                         _data->minY, _data->maxY,
1767                         dx, dy, lx, ly);
1768 
1769                 int xOffset = _data->sampleCountXTileCoords * tileRange.min.x;
1770                 int yOffset = _data->sampleCountYTileCoords * tileRange.min.y;
1771 
1772                 //
1773                 // Skip and check the tile coordinates.
1774                 //
1775 
1776                 _data->_streamData->is->seekg(_data->tileOffsets(dx, dy, lx, ly));
1777 
1778                 if (isMultiPart(_data->version))
1779                 {
1780                     int partNumber;
1781                     Xdr::read <StreamIO> (*_data->_streamData->is, partNumber);
1782 
1783                     if (partNumber != _data->partNumber)
1784                         throw IEX_NAMESPACE::InputExc ("Unexpected part number.");
1785                 }
1786 
1787                 int xInFile, yInFile, lxInFile, lyInFile;
1788                 Xdr::read <StreamIO> (*_data->_streamData->is, xInFile);
1789                 Xdr::read <StreamIO> (*_data->_streamData->is, yInFile);
1790                 Xdr::read <StreamIO> (*_data->_streamData->is, lxInFile);
1791                 Xdr::read <StreamIO> (*_data->_streamData->is, lyInFile);
1792 
1793                 if (xInFile != dx)
1794                     throw IEX_NAMESPACE::InputExc ("Unexpected tile x coordinate.");
1795 
1796                 if (yInFile != dy)
1797                     throw IEX_NAMESPACE::InputExc ("Unexpected tile y coordinate.");
1798 
1799                 if (lxInFile != lx)
1800                     throw IEX_NAMESPACE::InputExc ("Unexpected tile x level number coordinate.");
1801 
1802                 if (lyInFile != ly)
1803                     throw IEX_NAMESPACE::InputExc ("Unexpected tile y level number coordinate.");
1804 
1805                 Int64 tableSize, dataSize, unpackedDataSize;
1806                 Xdr::read <StreamIO> (*_data->_streamData->is, tableSize);
1807                 Xdr::read <StreamIO> (*_data->_streamData->is, dataSize);
1808                 Xdr::read <StreamIO> (*_data->_streamData->is, unpackedDataSize);
1809 
1810 
1811                 if(tableSize>_data->maxSampleCountTableSize)
1812                 {
1813                     THROW (IEX_NAMESPACE::ArgExc, "Bad sampleCountTableDataSize read from tile "<< dx << ',' << dy << ',' << lx << ',' << ly << ": expected " << _data->maxSampleCountTableSize << " or less, got "<< tableSize);
1814                 }
1815 
1816 
1817                 //
1818                 // We make a check on the data size requirements here.
1819                 // Whilst we wish to store 64bit sizes on disk, not all the compressors
1820                 // have been made to work with such data sizes and are still limited to
1821                 // using signed 32 bit (int) for the data size. As such, this version
1822                 // insists that we validate that the data size does not exceed the data
1823                 // type max limit.
1824                 // @TODO refactor the compressor code to ensure full 64-bit support.
1825                 //
1826 
1827                 Int64 compressorMaxDataSize = Int64(std::numeric_limits<int>::max());
1828                 if (dataSize         > compressorMaxDataSize ||
1829                     unpackedDataSize > compressorMaxDataSize ||
1830                     tableSize        > compressorMaxDataSize)
1831                 {
1832                     THROW (IEX_NAMESPACE::ArgExc, "This version of the library does not"
1833                           << "support the allocation of data with size  > "
1834                           << compressorMaxDataSize
1835                           << " file table size    :" << tableSize
1836                           << " file unpacked size :" << unpackedDataSize
1837                           << " file packed size   :" << dataSize << ".\n");
1838                 }
1839 
1840                 //
1841                 // Read and uncompress the pixel sample count table.
1842                 //
1843 
1844                 _data->_streamData->is->read(_data->sampleCountTableBuffer, tableSize);
1845 
1846                 const char* readPtr;
1847 
1848                 if (tableSize < _data->maxSampleCountTableSize)
1849                 {
1850                     if(!_data->sampleCountTableComp)
1851                     {
1852                         THROW(IEX_NAMESPACE::ArgExc,"Deep scanline data corrupt at tile " << dx << ',' << dy << ',' << lx << ',' <<  ly << " (sampleCountTableDataSize error)");
1853                     }
1854                     _data->sampleCountTableComp->uncompress(_data->sampleCountTableBuffer,
1855                                                             tableSize,
1856                                                             tileRange.min.y,
1857                                                             readPtr);
1858                 }
1859                 else
1860                     readPtr = _data->sampleCountTableBuffer;
1861 
1862                 size_t cumulative_total_samples =0;
1863                 int lastAccumulatedCount;
1864                 for (int j = tileRange.min.y; j <= tileRange.max.y; j++)
1865                 {
1866                     lastAccumulatedCount = 0;
1867                     for (int i = tileRange.min.x; i <= tileRange.max.x; i++)
1868                     {
1869                         int accumulatedCount;
1870                         Xdr::read <CharPtrIO> (readPtr, accumulatedCount);
1871 
1872                         if (accumulatedCount < lastAccumulatedCount)
1873                         {
1874                             THROW(IEX_NAMESPACE::ArgExc,"Deep tile sampleCount data corrupt at tile "
1875                                   << dx << ',' << dy << ',' << lx << ',' <<  ly << " (negative sample count detected)");
1876                         }
1877 
1878                         int count = accumulatedCount - lastAccumulatedCount;
1879                         lastAccumulatedCount = accumulatedCount;
1880 
1881                         _data->getSampleCount(i - xOffset, j - yOffset) =count;
1882                     }
1883                     cumulative_total_samples += lastAccumulatedCount;
1884                 }
1885 
1886                 if(cumulative_total_samples * _data->combinedSampleSize > unpackedDataSize)
1887                 {
1888                     THROW(IEX_NAMESPACE::ArgExc,"Deep scanline sampleCount data corrupt at tile "
1889                                                 << dx << ',' << dy << ',' << lx << ',' <<  ly
1890                                                 << ": pixel data only contains " << unpackedDataSize
1891                                                 << " bytes of data but table references at least "
1892                                                 << cumulative_total_samples*_data->combinedSampleSize << " bytes of sample data" );
1893                 }
1894 
1895             }
1896         }
1897 
1898         _data->_streamData->is->seekg(savedFilePos);
1899     }
1900     catch (IEX_NAMESPACE::BaseExc &e)
1901     {
1902         REPLACE_EXC (e, "Error reading sample count data from image "
1903                         "file \"" << fileName() << "\". " << e);
1904 
1905          _data->_streamData->is->seekg(savedFilePos);
1906 
1907         throw;
1908     }
1909 }
1910 
1911 
1912 void
readPixelSampleCount(int dx,int dy,int l)1913 DeepTiledInputFile::readPixelSampleCount (int dx, int dy, int l)
1914 {
1915     readPixelSampleCount (dx, dy, l, l);
1916 }
1917 
1918 
1919 void
readPixelSampleCount(int dx,int dy,int lx,int ly)1920 DeepTiledInputFile::readPixelSampleCount (int dx, int dy, int lx, int ly)
1921 {
1922     readPixelSampleCounts (dx, dx, dy, dy, lx, ly);
1923 }
1924 
1925 
1926 void
readPixelSampleCounts(int dx1,int dx2,int dy1,int dy2,int l)1927 DeepTiledInputFile::readPixelSampleCounts (int dx1, int dx2,
1928                                            int dy1, int dy2,
1929                                            int l)
1930 {
1931     readPixelSampleCounts (dx1, dx2, dy1, dy2, l, l);
1932 }
1933 
1934 
1935 size_t
totalTiles() const1936 DeepTiledInputFile::totalTiles() const
1937 {
1938     //
1939     // Calculate the total number of tiles in the file
1940     //
1941 
1942     int numAllTiles = 0;
1943 
1944     switch (levelMode ())
1945     {
1946         case ONE_LEVEL:
1947         case MIPMAP_LEVELS:
1948 
1949             for (int i_l = 0; i_l < numLevels (); ++i_l)
1950                 numAllTiles += numXTiles (i_l) * numYTiles (i_l);
1951 
1952             break;
1953 
1954         case RIPMAP_LEVELS:
1955 
1956             for (int i_ly = 0; i_ly < numYLevels (); ++i_ly)
1957                 for (int i_lx = 0; i_lx < numXLevels (); ++i_lx)
1958                     numAllTiles += numXTiles (i_lx) * numYTiles (i_ly);
1959 
1960                 break;
1961 
1962         default:
1963 
1964             throw IEX_NAMESPACE::ArgExc ("Unknown LevelMode format.");
1965     }
1966     return numAllTiles;
1967 }
1968 
1969 
1970 
1971 
1972 void
getTileOrder(int dx[],int dy[],int lx[],int ly[]) const1973 DeepTiledInputFile::getTileOrder(int dx[],int dy[],int lx[],int ly[]) const
1974 {
1975   return _data->tileOffsets.getTileOrder(dx,dy,lx,ly);
1976 
1977 }
1978 
1979 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT
1980