1 ///////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2004, Industrial Light & Magic, a division of Lucas
4 // Digital Ltd. LLC
5 //
6 // All rights reserved.
7 //
8 // Redistribution and use in source and binary forms, with or without
9 // modification, are permitted provided that the following conditions are
10 // met:
11 // *       Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 // *       Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 // *       Neither the name of Industrial Light & Magic nor the names of
18 // its contributors may be used to endorse or promote products derived
19 // from this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 ///////////////////////////////////////////////////////////////////////////
34 
35 //-----------------------------------------------------------------------------
36 //
37 //	class TiledInputFile
38 //
39 //-----------------------------------------------------------------------------
40 
41 #include "ImfTiledInputFile.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 "ImfXdr.h"
49 #include "ImfConvert.h"
50 #include "ImfVersion.h"
51 #include "ImfTileOffsets.h"
52 #include "ImfThreading.h"
53 #include "ImfPartType.h"
54 #include "ImfMultiPartInputFile.h"
55 #include "ImfInputStreamMutex.h"
56 #include "IlmThreadPool.h"
57 #include "IlmThreadSemaphore.h"
58 #include "IlmThreadMutex.h"
59 #include "ImathVec.h"
60 #include "Iex.h"
61 #include <string>
62 #include <vector>
63 #include <algorithm>
64 #include <assert.h>
65 #include "ImfInputPartData.h"
66 #include "ImfNamespace.h"
67 
68 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
69 
70 using IMATH_NAMESPACE::Box2i;
71 using IMATH_NAMESPACE::V2i;
72 using std::string;
73 using std::vector;
74 using std::min;
75 using std::max;
76 using ILMTHREAD_NAMESPACE::Mutex;
77 using ILMTHREAD_NAMESPACE::Lock;
78 using ILMTHREAD_NAMESPACE::Semaphore;
79 using ILMTHREAD_NAMESPACE::Task;
80 using ILMTHREAD_NAMESPACE::TaskGroup;
81 using ILMTHREAD_NAMESPACE::ThreadPool;
82 
83 namespace {
84 
85 struct TInSliceInfo
86 {
87     PixelType   typeInFrameBuffer;
88     PixelType   typeInFile;
89     char *      base;
90     size_t      xStride;
91     size_t      yStride;
92     bool        fill;
93     bool        skip;
94     double      fillValue;
95     int         xTileCoords;
96     int         yTileCoords;
97 
98     TInSliceInfo (PixelType typeInFrameBuffer = HALF,
99                   PixelType typeInFile = HALF,
100                   char *base = 0,
101                   size_t xStride = 0,
102                   size_t yStride = 0,
103                   bool fill = false,
104                   bool skip = false,
105                   double fillValue = 0.0,
106                   int xTileCoords = 0,
107                   int yTileCoords = 0);
108 };
109 
110 
TInSliceInfo(PixelType tifb,PixelType tifl,char * b,size_t xs,size_t ys,bool f,bool s,double fv,int xtc,int ytc)111 TInSliceInfo::TInSliceInfo (PixelType tifb,
112                             PixelType tifl,
113                             char *b,
114                             size_t xs, size_t ys,
115                             bool f, bool s,
116                             double fv,
117                             int xtc,
118                             int ytc)
119 :
120     typeInFrameBuffer (tifb),
121     typeInFile (tifl),
122     base (b),
123     xStride (xs),
124     yStride (ys),
125     fill (f),
126     skip (s),
127     fillValue (fv),
128     xTileCoords (xtc),
129     yTileCoords (ytc)
130 {
131     // empty
132 }
133 
134 
135 struct TileBuffer
136 {
137     const char *	uncompressedData;
138     char *		buffer;
139     int			dataSize;
140     Compressor *	compressor;
141     Compressor::Format	format;
142     int			dx;
143     int			dy;
144     int			lx;
145     int			ly;
146     bool		hasException;
147     string		exception;
148 
149      TileBuffer (Compressor * const comp);
150     ~TileBuffer ();
151 
wait__anondf056c5e0111::TileBuffer152     inline void		wait () {_sem.wait();}
post__anondf056c5e0111::TileBuffer153     inline void		post () {_sem.post();}
154 
155  protected:
156 
157     Semaphore _sem;
158 };
159 
160 
TileBuffer(Compressor * comp)161 TileBuffer::TileBuffer (Compressor *comp):
162     uncompressedData (0),
163     buffer (0),
164     dataSize (0),
165     compressor (comp),
166     format (defaultFormat (compressor)),
167     dx (-1),
168     dy (-1),
169     lx (-1),
170     ly (-1),
171     hasException (false),
172     exception (),
173     _sem (1)
174 {
175     // empty
176 }
177 
178 
~TileBuffer()179 TileBuffer::~TileBuffer ()
180 {
181     delete compressor;
182 }
183 
184 } // namespace
185 
186 
187 class MultiPartInputFile;
188 
189 
190 //
191 // struct TiledInputFile::Data stores things that will be
192 // needed between calls to readTile()
193 //
194 
195 struct TiledInputFile::Data: public Mutex
196 {
197     Header	    header;	        	    // the image header
198     TileDescription tileDesc;		            // describes the tile layout
199     int		    version;		            // file's version
200     FrameBuffer	    frameBuffer;	            // framebuffer to write into
201     LineOrder	    lineOrder;		            // the file's lineorder
202     int		    minX;		            // data window's min x coord
203     int		    maxX;		            // data window's max x coord
204     int		    minY;		            // data window's min y coord
205     int		    maxY;		            // data window's max x coord
206 
207     int		    numXLevels;		            // number of x levels
208     int		    numYLevels;		            // number of y levels
209     int *	    numXTiles;		            // number of x tiles at a level
210     int *	    numYTiles;		            // number of y tiles at a level
211 
212     TileOffsets	    tileOffsets;	            // stores offsets in file for
213     // each tile
214 
215     bool	    fileIsComplete;	            // True if no tiles are missing
216                                                     // in the file
217 
218     vector<TInSliceInfo> slices;        	    // info about channels in file
219 
220     size_t	    bytesPerPixel;                  // size of an uncompressed pixel
221 
222     size_t	    maxBytesPerTileLine;            // combined size of a line
223                                                     // over all channels
224 
225     int             partNumber;                     // part number
226 
227     bool            multiPartBackwardSupport;       // if we are reading a multipart file
228                                                     // using OpenEXR 1.7 API
229 
230     int             numThreads;                     // number of threads
231 
232     MultiPartInputFile* multiPartFile;              // the MultiPartInputFile used to
233                                                     // support backward compatibility
234 
235     vector<TileBuffer*> tileBuffers;                // each holds a single tile
236     size_t          tileBufferSize;	            // size of the tile buffers
237 
238     bool            memoryMapped;                   // if the stream is memory mapped
239 
240     InputStreamMutex * _streamData;
241     bool                _deleteStream;
242 
243      Data (int numThreads);
244     ~Data ();
245 
246     inline TileBuffer * getTileBuffer (int number);
247 					    // hash function from tile indices
248 					    // into our vector of tile buffers
249 };
250 
251 
Data(int numThreads)252 TiledInputFile::Data::Data (int numThreads):
253     numXTiles (0),
254     numYTiles (0),
255     partNumber (-1),
256     multiPartBackwardSupport(false),
257     numThreads(numThreads),
258     memoryMapped(false),
259     _streamData(NULL),
260     _deleteStream(false)
261 {
262     //
263     // We need at least one tileBuffer, but if threading is used,
264     // to keep n threads busy we need 2*n tileBuffers
265     //
266 
267     tileBuffers.resize (max (1, 2 * numThreads));
268 }
269 
270 
~Data()271 TiledInputFile::Data::~Data ()
272 {
273     delete [] numXTiles;
274     delete [] numYTiles;
275 
276     for (size_t i = 0; i < tileBuffers.size(); i++)
277         delete tileBuffers[i];
278 
279     if (multiPartBackwardSupport)
280         delete multiPartFile;
281 }
282 
283 
284 TileBuffer*
getTileBuffer(int number)285 TiledInputFile::Data::getTileBuffer (int number)
286 {
287     return tileBuffers[number % tileBuffers.size()];
288 }
289 
290 
291 namespace {
292 
293 void
readTileData(InputStreamMutex * streamData,TiledInputFile::Data * ifd,int dx,int dy,int lx,int ly,char * & buffer,int & dataSize)294 readTileData (InputStreamMutex *streamData,
295               TiledInputFile::Data *ifd,
296 	      int dx, int dy,
297 	      int lx, int ly,
298               char *&buffer,
299               int &dataSize)
300 {
301     //
302     // Read a single tile block from the file and into the array pointed
303     // to by buffer.  If the file is memory-mapped, then we change where
304     // buffer points instead of writing into the array (hence buffer needs
305     // to be a reference to a char *).
306     //
307 
308     //
309     // Look up the location for this tile in the Index and
310     // seek to that position if necessary
311     //
312 
313     Int64 tileOffset = ifd->tileOffsets (dx, dy, lx, ly);
314 
315     if (tileOffset == 0)
316     {
317         THROW (IEX_NAMESPACE::InputExc, "Tile (" << dx << ", " << dy << ", " <<
318 			      lx << ", " << ly << ") is missing.");
319     }
320 
321 
322     //
323     // In a multi-part file, the next chunk does not need to
324     // belong to the same part, so we have to compare the
325     // offset here.
326     //
327 
328     if (!isMultiPart(ifd->version))
329     {
330         if (streamData->currentPosition != tileOffset)
331             streamData->is->seekg (tileOffset);
332     }
333     else
334     {
335         //
336         // In a multi-part file, the file pointer may be moved by other
337         // parts, so we have to ask tellg() where we are.
338         //
339         if (streamData->is->tellg() != tileOffset)
340             streamData->is->seekg (tileOffset);
341     }
342 
343     //
344     // Read the first few bytes of the tile (the header).
345     // Verify that the tile coordinates and the level number
346     // are correct.
347     //
348 
349     int tileXCoord, tileYCoord, levelX, levelY;
350 
351     if (isMultiPart(ifd->version))
352     {
353         int partNumber;
354         Xdr::read <StreamIO> (*streamData->is, partNumber);
355         if (partNumber != ifd->partNumber)
356         {
357             THROW (IEX_NAMESPACE::ArgExc, "Unexpected part number " << partNumber
358                    << ", should be " << ifd->partNumber << ".");
359         }
360     }
361 
362     OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, tileXCoord);
363     OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, tileYCoord);
364     OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, levelX);
365     OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, levelY);
366     OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, dataSize);
367 
368     if (tileXCoord != dx)
369         throw IEX_NAMESPACE::InputExc ("Unexpected tile x coordinate.");
370 
371     if (tileYCoord != dy)
372         throw IEX_NAMESPACE::InputExc ("Unexpected tile y coordinate.");
373 
374     if (levelX != lx)
375         throw IEX_NAMESPACE::InputExc ("Unexpected tile x level number coordinate.");
376 
377     if (levelY != ly)
378         throw IEX_NAMESPACE::InputExc ("Unexpected tile y level number coordinate.");
379 
380     if (dataSize > (int) ifd->tileBufferSize)
381         throw IEX_NAMESPACE::InputExc ("Unexpected tile block length.");
382 
383     //
384     // Read the pixel data.
385     //
386 
387     if (streamData->is->isMemoryMapped ())
388         buffer = streamData->is->readMemoryMapped (dataSize);
389     else
390         streamData->is->read (buffer, dataSize);
391 
392     //
393     // Keep track of which tile is the next one in
394     // the file, so that we can avoid redundant seekg()
395     // operations (seekg() can be fairly expensive).
396     //
397 
398     streamData->currentPosition = tileOffset + 5 * Xdr::size<int>() + dataSize;
399 }
400 
401 
402 void
readNextTileData(InputStreamMutex * streamData,TiledInputFile::Data * ifd,int & dx,int & dy,int & lx,int & ly,char * & buffer,int & dataSize)403 readNextTileData (InputStreamMutex *streamData,
404                   TiledInputFile::Data *ifd,
405 		  int &dx, int &dy,
406 		  int &lx, int &ly,
407                   char * & buffer,
408 		  int &dataSize)
409 {
410     //
411     // Read the next tile block from the file
412     //
413 
414     if(isMultiPart(ifd->version))
415     {
416         int part;
417         Xdr::read <StreamIO> (*streamData->is, part);
418         if(part!=ifd->partNumber)
419         {
420            throw IEX_NAMESPACE::InputExc("Unexpected part number in readNextTileData");
421         }
422     }
423 
424     //
425     // Read the first few bytes of the tile (the header).
426     //
427 
428     Xdr::read <StreamIO> (*streamData->is, dx);
429     Xdr::read <StreamIO> (*streamData->is, dy);
430     Xdr::read <StreamIO> (*streamData->is, lx);
431     Xdr::read <StreamIO> (*streamData->is, ly);
432     Xdr::read <StreamIO> (*streamData->is, dataSize);
433 
434     if (dataSize > (int) ifd->tileBufferSize)
435         throw IEX_NAMESPACE::InputExc ("Unexpected tile block length.");
436 
437     //
438     // Read the pixel data.
439     //
440 
441     streamData->is->read (buffer, dataSize);
442 
443     //
444     // Keep track of which tile is the next one in
445     // the file, so that we can avoid redundant seekg()
446     // operations (seekg() can be fairly expensive).
447     //
448 
449     streamData->currentPosition += 5 * Xdr::size<int>() + dataSize;
450 }
451 
452 
453 //
454 // A TileBufferTask encapsulates the task of uncompressing
455 // a single tile and copying it into the frame buffer.
456 //
457 
458 class TileBufferTask : public Task
459 {
460   public:
461 
462     TileBufferTask (TaskGroup *group,
463                     TiledInputFile::Data *ifd,
464 		    TileBuffer *tileBuffer);
465 
466     virtual ~TileBufferTask ();
467 
468     virtual void		execute ();
469 
470   private:
471 
472     TiledInputFile::Data *	_ifd;
473     TileBuffer *		_tileBuffer;
474 };
475 
476 
TileBufferTask(TaskGroup * group,TiledInputFile::Data * ifd,TileBuffer * tileBuffer)477 TileBufferTask::TileBufferTask
478     (TaskGroup *group,
479      TiledInputFile::Data *ifd,
480      TileBuffer *tileBuffer)
481 :
482     Task (group),
483     _ifd (ifd),
484     _tileBuffer (tileBuffer)
485 {
486     // empty
487 }
488 
489 
~TileBufferTask()490 TileBufferTask::~TileBufferTask ()
491 {
492     //
493     // Signal that the tile buffer is now free
494     //
495 
496     _tileBuffer->post ();
497 }
498 
499 
500 void
execute()501 TileBufferTask::execute ()
502 {
503     try
504     {
505         //
506         // Calculate information about the tile
507         //
508 
509         Box2i tileRange =  OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile (
510                 _ifd->tileDesc,
511                 _ifd->minX, _ifd->maxX,
512                 _ifd->minY, _ifd->maxY,
513                 _tileBuffer->dx,
514                 _tileBuffer->dy,
515                 _tileBuffer->lx,
516                 _tileBuffer->ly);
517 
518         int numPixelsPerScanLine = tileRange.max.x - tileRange.min.x + 1;
519 
520         int numPixelsInTile = numPixelsPerScanLine *
521                             (tileRange.max.y - tileRange.min.y + 1);
522 
523         int sizeOfTile = _ifd->bytesPerPixel * numPixelsInTile;
524 
525 
526         //
527         // Uncompress the data, if necessary
528         //
529 
530         if (_tileBuffer->compressor && _tileBuffer->dataSize < sizeOfTile)
531         {
532             _tileBuffer->format = _tileBuffer->compressor->format();
533 
534             _tileBuffer->dataSize = _tileBuffer->compressor->uncompressTile
535 		(_tileBuffer->buffer, _tileBuffer->dataSize,
536 		 tileRange, _tileBuffer->uncompressedData);
537         }
538         else
539         {
540             //
541             // If the line is uncompressed, it's in XDR format,
542             // regardless of the compressor's output format.
543             //
544 
545             _tileBuffer->format = Compressor::XDR;
546             _tileBuffer->uncompressedData = _tileBuffer->buffer;
547         }
548 
549         //
550         // Convert the tile of pixel data back from the machine-independent
551 	// representation, and store the result in the frame buffer.
552         //
553 
554         const char *readPtr = _tileBuffer->uncompressedData;
555                                                         // points to where we
556                                                         // read from in the
557                                                         // tile block
558 
559         //
560         // Iterate over the scan lines in the tile.
561         //
562 
563         for (int y = tileRange.min.y; y <= tileRange.max.y; ++y)
564         {
565             //
566             // Iterate over all image channels.
567             //
568 
569             for (unsigned int i = 0; i < _ifd->slices.size(); ++i)
570             {
571                 const TInSliceInfo &slice = _ifd->slices[i];
572 
573                 //
574                 // These offsets are used to facilitate both
575                 // absolute and tile-relative pixel coordinates.
576                 //
577 
578                 int xOffset = slice.xTileCoords * tileRange.min.x;
579                 int yOffset = slice.yTileCoords * tileRange.min.y;
580 
581                 //
582                 // Fill the frame buffer with pixel data.
583                 //
584 
585                 if (slice.skip)
586                 {
587                     //
588                     // The file contains data for this channel, but
589                     // the frame buffer contains no slice for this channel.
590                     //
591 
592                     skipChannel (readPtr, slice.typeInFile,
593                                  numPixelsPerScanLine);
594                 }
595                 else
596                 {
597                     //
598                     // The frame buffer contains a slice for this channel.
599                     //
600 
601                     char *writePtr = slice.base +
602                                      (y - yOffset) * slice.yStride +
603                                      (tileRange.min.x - xOffset) *
604                                      slice.xStride;
605 
606                     char *endPtr = writePtr +
607                                    (numPixelsPerScanLine - 1) * slice.xStride;
608 
609                     copyIntoFrameBuffer (readPtr, writePtr, endPtr,
610                                          slice.xStride,
611                                          slice.fill, slice.fillValue,
612                                          _tileBuffer->format,
613                                          slice.typeInFrameBuffer,
614                                          slice.typeInFile);
615                 }
616             }
617         }
618     }
619     catch (std::exception &e)
620     {
621         if (!_tileBuffer->hasException)
622         {
623             _tileBuffer->exception = e.what ();
624             _tileBuffer->hasException = true;
625         }
626     }
627     catch (...)
628     {
629         if (!_tileBuffer->hasException)
630         {
631             _tileBuffer->exception = "unrecognized exception";
632             _tileBuffer->hasException = true;
633         }
634     }
635 }
636 
637 
638 TileBufferTask *
newTileBufferTask(TaskGroup * group,InputStreamMutex * streamData,TiledInputFile::Data * ifd,int number,int dx,int dy,int lx,int ly)639 newTileBufferTask
640     (TaskGroup *group,
641      InputStreamMutex *streamData,
642      TiledInputFile::Data *ifd,
643      int number,
644      int dx, int dy,
645      int lx, int ly)
646 {
647     //
648     // Wait for a tile buffer to become available,
649     // fill the buffer with raw data from the file,
650     // and create a new TileBufferTask whose execute()
651     // method will uncompress the tile and copy the
652     // tile's pixels into the frame buffer.
653     //
654 
655     TileBuffer *tileBuffer = ifd->getTileBuffer (number);
656 
657     try
658     {
659 	tileBuffer->wait();
660 
661 	tileBuffer->dx = dx;
662 	tileBuffer->dy = dy;
663 	tileBuffer->lx = lx;
664 	tileBuffer->ly = ly;
665 
666 	tileBuffer->uncompressedData = 0;
667 
668 	readTileData (streamData, ifd, dx, dy, lx, ly,
669 		      tileBuffer->buffer,
670 		      tileBuffer->dataSize);
671     }
672     catch (...)
673     {
674 	//
675 	// Reading from the file caused an exception.
676 	// Signal that the tile buffer is free, and
677 	// re-throw the exception.
678 	//
679 
680 	tileBuffer->post();
681 	throw;
682     }
683 
684     return new TileBufferTask (group, ifd, tileBuffer);
685 }
686 
687 
688 } // namespace
689 
690 
TiledInputFile(const char fileName[],int numThreads)691 TiledInputFile::TiledInputFile (const char fileName[], int numThreads):
692     _data (new Data (numThreads))
693 {
694     _data->_streamData=NULL;
695     _data->_deleteStream=true;
696 
697     //
698     // This constructor is called when a user
699     // explicitly wants to read a tiled file.
700     //
701 
702 
703     IStream* is = 0;
704     try
705     {
706         is = new StdIFStream (fileName);
707 	readMagicNumberAndVersionField(*is, _data->version);
708 
709 	//
710         // Backward compatibility to read multpart file.
711         //
712 	if (isMultiPart(_data->version))
713 	{
714 	    compatibilityInitialize(*is);
715 	    return;
716 	}
717 
718 	_data->_streamData = new InputStreamMutex();
719 	_data->_streamData->is = is;
720 	_data->header.readFrom (*_data->_streamData->is, _data->version);
721 	initialize();
722         //read tile offsets - we are not multipart or deep
723         _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete,false,false);
724 	_data->_streamData->currentPosition = _data->_streamData->is->tellg();
725     }
726     catch (IEX_NAMESPACE::BaseExc &e)
727     {
728         if (_data->_streamData != 0)
729         {
730             if (_data->_streamData->is != 0)
731             {
732                 delete _data->_streamData->is;
733                 _data->_streamData->is = is = 0;
734             }
735 
736             delete _data->_streamData;
737         }
738 
739         if (is != 0)
740             delete is;
741 
742 	REPLACE_EXC (e, "Cannot open image file "
743 			"\"" << fileName << "\". " << e);
744 	throw;
745     }
746     catch (...)
747     {
748         if ( _data->_streamData != 0)
749         {
750             if ( _data->_streamData->is != 0)
751             {
752                 delete _data->_streamData->is;
753                 _data->_streamData->is = is = 0;
754             }
755 
756             delete _data->_streamData;
757         }
758 
759         if (is != 0)
760             delete is;
761         throw;
762     }
763 }
764 
765 
TiledInputFile(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream & is,int numThreads)766 TiledInputFile::TiledInputFile (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is, int numThreads):
767     _data (new Data (numThreads))
768 {
769     _data->_deleteStream=false;
770     //
771     // This constructor is called when a user
772     // explicitly wants to read a tiled file.
773     //
774 
775     bool streamDataCreated = false;
776 
777     try
778     {
779 	readMagicNumberAndVersionField(is, _data->version);
780 
781 	//
782 	// Backward compatibility to read multpart file.
783 	//
784 	if (isMultiPart(_data->version))
785         {
786 	    compatibilityInitialize(is);
787             return;
788         }
789 
790 	streamDataCreated = true;
791 	_data->_streamData = new InputStreamMutex();
792 	_data->_streamData->is = &is;
793 	_data->header.readFrom (*_data->_streamData->is, _data->version);
794 	initialize();
795         // file is guaranteed to be single part, regular image
796         _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete,false,false);
797 	_data->memoryMapped = _data->_streamData->is->isMemoryMapped();
798 	_data->_streamData->currentPosition = _data->_streamData->is->tellg();
799     }
800     catch (IEX_NAMESPACE::BaseExc &e)
801     {
802         if (streamDataCreated) delete _data->_streamData;
803 	delete _data;
804 
805 	REPLACE_EXC (e, "Cannot open image file "
806 			"\"" << is.fileName() << "\". " << e);
807 	throw;
808     }
809     catch (...)
810     {
811         if (streamDataCreated) delete _data->_streamData;
812 	delete _data;
813         throw;
814     }
815 }
816 
817 
TiledInputFile(const Header & header,OPENEXR_IMF_INTERNAL_NAMESPACE::IStream * is,int version,int numThreads)818 TiledInputFile::TiledInputFile (const Header &header,
819                                 OPENEXR_IMF_INTERNAL_NAMESPACE::IStream *is,
820                                 int version,
821                                 int numThreads) :
822     _data (new Data (numThreads))
823 {
824     _data->_deleteStream=false;
825     _data->_streamData = new InputStreamMutex();
826     //
827     // This constructor called by class Imf::InputFile
828     // when a user wants to just read an image file, and
829     // doesn't care or know if the file is tiled.
830     // No need to have backward compatibility here, because
831     // we have somehow got the header.
832     //
833 
834     _data->_streamData->is = is;
835     _data->header = header;
836     _data->version = version;
837     initialize();
838     _data->tileOffsets.readFrom (*(_data->_streamData->is),_data->fileIsComplete,false,false);
839     _data->memoryMapped = is->isMemoryMapped();
840     _data->_streamData->currentPosition = _data->_streamData->is->tellg();
841 }
842 
843 
TiledInputFile(InputPartData * part)844 TiledInputFile::TiledInputFile (InputPartData* part)
845 {
846     _data = new Data (part->numThreads);
847     _data->_deleteStream=false;
848     multiPartInitialize(part);
849 }
850 
851 
852 void
compatibilityInitialize(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream & is)853 TiledInputFile::compatibilityInitialize(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is)
854 {
855     is.seekg(0);
856     //
857     // Construct a MultiPartInputFile, initialize TiledInputFile
858     // with the part 0 data.
859     // (TODO) maybe change the third parameter of the constructor of MultiPartInputFile later.
860     //
861     _data->multiPartBackwardSupport = true;
862     _data->multiPartFile = new MultiPartInputFile(is, _data->numThreads);
863     InputPartData* part = _data->multiPartFile->getPart(0);
864 
865     multiPartInitialize(part);
866 }
867 
868 
869 void
multiPartInitialize(InputPartData * part)870 TiledInputFile::multiPartInitialize(InputPartData* part)
871 {
872     if (part->header.type() != TILEDIMAGE)
873         throw IEX_NAMESPACE::ArgExc("Can't build a TiledInputFile from a type-mismatched part.");
874 
875     _data->_streamData = part->mutex;
876     _data->header = part->header;
877     _data->version = part->version;
878     _data->partNumber = part->partNumber;
879     _data->memoryMapped = _data->_streamData->is->isMemoryMapped();
880     initialize();
881     _data->tileOffsets.readFrom(part->chunkOffsets,_data->fileIsComplete);
882     _data->_streamData->currentPosition = _data->_streamData->is->tellg();
883 }
884 
885 
886 void
initialize()887 TiledInputFile::initialize ()
888 {
889     // fix bad types in header (arises when a tool built against an older version of
890     // OpenEXR converts a scanline image to tiled)
891     // only applies when file is a single part, regular image, tiled file
892     //
893     if(!isMultiPart(_data->version) &&
894        !isNonImage(_data->version) &&
895        isTiled(_data->version) &&
896        _data->header.hasType() )
897     {
898         _data->header.setType(TILEDIMAGE);
899     }
900 
901     if (_data->partNumber == -1)
902     {
903         if (!isTiled (_data->version))
904             throw IEX_NAMESPACE::ArgExc ("Expected a tiled file but the file is not tiled.");
905 
906     }
907     else
908     {
909         if(_data->header.hasType() && _data->header.type()!=TILEDIMAGE)
910         {
911             throw IEX_NAMESPACE::ArgExc ("TiledInputFile used for non-tiledimage part.");
912         }
913     }
914 
915     _data->header.sanityCheck (true);
916 
917     _data->tileDesc = _data->header.tileDescription();
918     _data->lineOrder = _data->header.lineOrder();
919 
920     //
921     // Save the dataWindow information
922     //
923 
924     const Box2i &dataWindow = _data->header.dataWindow();
925     _data->minX = dataWindow.min.x;
926     _data->maxX = dataWindow.max.x;
927     _data->minY = dataWindow.min.y;
928     _data->maxY = dataWindow.max.y;
929 
930     //
931     // Precompute level and tile information to speed up utility functions
932     //
933 
934     precalculateTileInfo (_data->tileDesc,
935 			  _data->minX, _data->maxX,
936 			  _data->minY, _data->maxY,
937 			  _data->numXTiles, _data->numYTiles,
938 			  _data->numXLevels, _data->numYLevels);
939 
940     _data->bytesPerPixel = calculateBytesPerPixel (_data->header);
941 
942     _data->maxBytesPerTileLine = _data->bytesPerPixel * _data->tileDesc.xSize;
943 
944     _data->tileBufferSize = _data->maxBytesPerTileLine * _data->tileDesc.ySize;
945 
946     //
947     // Create all the TileBuffers and allocate their internal buffers
948     //
949 
950     for (size_t i = 0; i < _data->tileBuffers.size(); i++)
951     {
952         _data->tileBuffers[i] = new TileBuffer (newTileCompressor
953 						  (_data->header.compression(),
954 						   _data->maxBytesPerTileLine,
955 						   _data->tileDesc.ySize,
956 						   _data->header));
957 
958         if (!_data->_streamData->is->isMemoryMapped ())
959             _data->tileBuffers[i]->buffer = new char [_data->tileBufferSize];
960     }
961 
962     _data->tileOffsets = TileOffsets (_data->tileDesc.mode,
963 				      _data->numXLevels,
964 				      _data->numYLevels,
965 				      _data->numXTiles,
966 				      _data->numYTiles);
967 }
968 
969 
~TiledInputFile()970 TiledInputFile::~TiledInputFile ()
971 {
972     if (!_data->memoryMapped)
973         for (size_t i = 0; i < _data->tileBuffers.size(); i++)
974             delete [] _data->tileBuffers[i]->buffer;
975 
976     if (_data->_deleteStream)
977         delete _data->_streamData->is;
978 
979     if (_data->partNumber == -1)
980         delete _data->_streamData;
981 
982     delete _data;
983 }
984 
985 
986 const char *
fileName() const987 TiledInputFile::fileName () const
988 {
989     return _data->_streamData->is->fileName();
990 }
991 
992 
993 const Header &
header() const994 TiledInputFile::header () const
995 {
996     return _data->header;
997 }
998 
999 
1000 int
version() const1001 TiledInputFile::version () const
1002 {
1003     return _data->version;
1004 }
1005 
1006 
1007 void
setFrameBuffer(const FrameBuffer & frameBuffer)1008 TiledInputFile::setFrameBuffer (const FrameBuffer &frameBuffer)
1009 {
1010     Lock lock (*_data->_streamData);
1011 
1012     //
1013     // Set the frame buffer
1014     //
1015 
1016     //
1017     // Check if the new frame buffer descriptor is
1018     // compatible with the image file header.
1019     //
1020 
1021     const ChannelList &channels = _data->header.channels();
1022 
1023     for (FrameBuffer::ConstIterator j = frameBuffer.begin();
1024          j != frameBuffer.end();
1025          ++j)
1026     {
1027         ChannelList::ConstIterator i = channels.find (j.name());
1028 
1029         if (i == channels.end())
1030             continue;
1031 
1032         if (i.channel().xSampling != j.slice().xSampling ||
1033             i.channel().ySampling != j.slice().ySampling)
1034             THROW (IEX_NAMESPACE::ArgExc, "X and/or y subsampling factors "
1035 				"of \"" << i.name() << "\" channel "
1036 				"of input file \"" << fileName() << "\" are "
1037 				"not compatible with the frame buffer's "
1038 				"subsampling factors.");
1039     }
1040 
1041     //
1042     // Initialize the slice table for readPixels().
1043     //
1044 
1045     vector<TInSliceInfo> slices;
1046     ChannelList::ConstIterator i = channels.begin();
1047 
1048     for (FrameBuffer::ConstIterator j = frameBuffer.begin();
1049          j != frameBuffer.end();
1050          ++j)
1051     {
1052         while (i != channels.end() && strcmp (i.name(), j.name()) < 0)
1053         {
1054             //
1055             // Channel i is present in the file but not
1056             // in the frame buffer; data for channel i
1057             // will be skipped during readPixels().
1058             //
1059 
1060             slices.push_back (TInSliceInfo (i.channel().type,
1061 					    i.channel().type,
1062 					    0,      // base
1063 					    0,      // xStride
1064 					    0,      // yStride
1065 					    false,  // fill
1066 					    true,   // skip
1067 					    0.0));  // fillValue
1068             ++i;
1069         }
1070 
1071         bool fill = false;
1072 
1073         if (i == channels.end() || strcmp (i.name(), j.name()) > 0)
1074         {
1075             //
1076             // Channel i is present in the frame buffer, but not in the file.
1077             // In the frame buffer, slice j will be filled with a default value.
1078             //
1079 
1080             fill = true;
1081         }
1082 
1083         slices.push_back (TInSliceInfo (j.slice().type,
1084                                         fill? j.slice().type: i.channel().type,
1085                                         j.slice().base,
1086                                         j.slice().xStride,
1087                                         j.slice().yStride,
1088                                         fill,
1089                                         false, // skip
1090                                         j.slice().fillValue,
1091                                         (j.slice().xTileCoords)? 1: 0,
1092                                         (j.slice().yTileCoords)? 1: 0));
1093 
1094         if (i != channels.end() && !fill)
1095             ++i;
1096     }
1097 
1098     while (i != channels.end())
1099     {
1100 	//
1101 	// Channel i is present in the file but not
1102 	// in the frame buffer; data for channel i
1103 	// will be skipped during readPixels().
1104 	//
1105 
1106 	slices.push_back (TInSliceInfo (i.channel().type,
1107 					i.channel().type,
1108 					0, // base
1109 					0, // xStride
1110 					0, // yStride
1111 					false,  // fill
1112 					true, // skip
1113 					0.0)); // fillValue
1114 	++i;
1115     }
1116 
1117     //
1118     // Store the new frame buffer.
1119     //
1120 
1121     _data->frameBuffer = frameBuffer;
1122     _data->slices = slices;
1123 }
1124 
1125 
1126 const FrameBuffer &
frameBuffer() const1127 TiledInputFile::frameBuffer () const
1128 {
1129     Lock lock (*_data->_streamData);
1130     return _data->frameBuffer;
1131 }
1132 
1133 
1134 bool
isComplete() const1135 TiledInputFile::isComplete () const
1136 {
1137     return _data->fileIsComplete;
1138 }
1139 
1140 
1141 void
readTiles(int dx1,int dx2,int dy1,int dy2,int lx,int ly)1142 TiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int lx, int ly)
1143 {
1144     //
1145     // Read a range of tiles from the file into the framebuffer
1146     //
1147 
1148     try
1149     {
1150         Lock lock (*_data->_streamData);
1151 
1152         if (_data->slices.size() == 0)
1153             throw IEX_NAMESPACE::ArgExc ("No frame buffer specified "
1154 			       "as pixel data destination.");
1155 
1156         if (!isValidLevel (lx, ly))
1157             THROW (IEX_NAMESPACE::ArgExc,
1158                    "Level coordinate "
1159                    "(" << lx << ", " << ly << ") "
1160                    "is invalid.");
1161 
1162         //
1163         // Determine the first and last tile coordinates in both dimensions.
1164         // We always attempt to read the range of tiles in the order that
1165         // they are stored in the file.
1166         //
1167 
1168         if (dx1 > dx2)
1169             std::swap (dx1, dx2);
1170 
1171         if (dy1 > dy2)
1172             std::swap (dy1, dy2);
1173 
1174         int dyStart = dy1;
1175 	int dyStop  = dy2 + 1;
1176 	int dY      = 1;
1177 
1178         if (_data->lineOrder == DECREASING_Y)
1179         {
1180             dyStart = dy2;
1181             dyStop  = dy1 - 1;
1182             dY      = -1;
1183         }
1184 
1185         //
1186         // Create a task group for all tile buffer tasks.  When the
1187 	// task group goes out of scope, the destructor waits until
1188 	// all tasks are complete.
1189         //
1190 
1191         {
1192             TaskGroup taskGroup;
1193             int tileNumber = 0;
1194 
1195             for (int dy = dyStart; dy != dyStop; dy += dY)
1196             {
1197                 for (int dx = dx1; dx <= dx2; dx++)
1198                 {
1199                     if (!isValidTile (dx, dy, lx, ly))
1200                         THROW (IEX_NAMESPACE::ArgExc,
1201 			       "Tile (" << dx << ", " << dy << ", " <<
1202 			       lx << "," << ly << ") is not a valid tile.");
1203 
1204                     ThreadPool::addGlobalTask (newTileBufferTask (&taskGroup,
1205                                                                   _data->_streamData,
1206                                                                   _data,
1207                                                                   tileNumber++,
1208                                                                   dx, dy,
1209                                                                   lx, ly));
1210                 }
1211             }
1212 
1213 	    //
1214             // finish all tasks
1215 	    //
1216         }
1217 
1218 	//
1219 	// Exeption handling:
1220 	//
1221 	// TileBufferTask::execute() may have encountered exceptions, but
1222 	// those exceptions occurred in another thread, not in the thread
1223 	// that is executing this call to TiledInputFile::readTiles().
1224 	// TileBufferTask::execute() has caught all exceptions and stored
1225 	// the exceptions' what() strings in the tile buffers.
1226 	// Now we check if any tile buffer contains a stored exception; if
1227 	// this is the case then we re-throw the exception in this thread.
1228 	// (It is possible that multiple tile buffers contain stored
1229 	// exceptions.  We re-throw the first exception we find and
1230 	// ignore all others.)
1231 	//
1232 
1233 	const string *exception = 0;
1234 
1235         for (size_t i = 0; i < _data->tileBuffers.size(); ++i)
1236 	{
1237             TileBuffer *tileBuffer = _data->tileBuffers[i];
1238 
1239 	    if (tileBuffer->hasException && !exception)
1240 		exception = &tileBuffer->exception;
1241 
1242 	    tileBuffer->hasException = false;
1243 	}
1244 
1245 	if (exception)
1246 	    throw IEX_NAMESPACE::IoExc (*exception);
1247     }
1248     catch (IEX_NAMESPACE::BaseExc &e)
1249     {
1250         REPLACE_EXC (e, "Error reading pixel data from image "
1251                         "file \"" << fileName() << "\". " << e);
1252         throw;
1253     }
1254 }
1255 
1256 
1257 void
readTiles(int dx1,int dx2,int dy1,int dy2,int l)1258 TiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int l)
1259 {
1260     readTiles (dx1, dx2, dy1, dy2, l, l);
1261 }
1262 
1263 
1264 void
readTile(int dx,int dy,int lx,int ly)1265 TiledInputFile::readTile (int dx, int dy, int lx, int ly)
1266 {
1267     readTiles (dx, dx, dy, dy, lx, ly);
1268 }
1269 
1270 
1271 void
readTile(int dx,int dy,int l)1272 TiledInputFile::readTile (int dx, int dy, int l)
1273 {
1274     readTile (dx, dy, l, l);
1275 }
1276 
1277 
1278 void
rawTileData(int & dx,int & dy,int & lx,int & ly,const char * & pixelData,int & pixelDataSize)1279 TiledInputFile::rawTileData (int &dx, int &dy,
1280 			     int &lx, int &ly,
1281                              const char *&pixelData,
1282 			     int &pixelDataSize)
1283 {
1284     try
1285     {
1286         Lock lock (*_data->_streamData);
1287 
1288         if (!isValidTile (dx, dy, lx, ly))
1289             throw IEX_NAMESPACE::ArgExc ("Tried to read a tile outside "
1290 			       "the image file's data window.");
1291 
1292         TileBuffer *tileBuffer = _data->getTileBuffer (0);
1293 
1294         //
1295         // if file is a multipart file, we have to seek to the required tile
1296         // since we don't know where the file pointer is
1297         //
1298         int old_dx=dx;
1299         int old_dy=dy;
1300         int old_lx=lx;
1301         int old_ly=ly;
1302         if(isMultiPart(version()))
1303         {
1304             _data->_streamData->is->seekg(_data->tileOffsets(dx,dy,lx,ly));
1305         }
1306         readNextTileData (_data->_streamData, _data, dx, dy, lx, ly,
1307 			  tileBuffer->buffer,
1308                           pixelDataSize);
1309         if(isMultiPart(version()))
1310         {
1311             if (old_dx!=dx || old_dy !=dy || old_lx!=lx || old_ly!=ly)
1312             {
1313                 throw IEX_NAMESPACE::ArgExc ("rawTileData read the wrong tile");
1314             }
1315         }
1316         pixelData = tileBuffer->buffer;
1317     }
1318     catch (IEX_NAMESPACE::BaseExc &e)
1319     {
1320         REPLACE_EXC (e, "Error reading pixel data from image "
1321 			"file \"" << fileName() << "\". " << e);
1322         throw;
1323     }
1324 }
1325 
1326 
1327 unsigned int
tileXSize() const1328 TiledInputFile::tileXSize () const
1329 {
1330     return _data->tileDesc.xSize;
1331 }
1332 
1333 
1334 unsigned int
tileYSize() const1335 TiledInputFile::tileYSize () const
1336 {
1337     return _data->tileDesc.ySize;
1338 }
1339 
1340 
1341 LevelMode
levelMode() const1342 TiledInputFile::levelMode () const
1343 {
1344     return _data->tileDesc.mode;
1345 }
1346 
1347 
1348 LevelRoundingMode
levelRoundingMode() const1349 TiledInputFile::levelRoundingMode () const
1350 {
1351     return _data->tileDesc.roundingMode;
1352 }
1353 
1354 
1355 int
numLevels() const1356 TiledInputFile::numLevels () const
1357 {
1358     if (levelMode() == RIPMAP_LEVELS)
1359 	THROW (IEX_NAMESPACE::LogicExc, "Error calling numLevels() on image "
1360 			      "file \"" << fileName() << "\" "
1361 			      "(numLevels() is not defined for files "
1362 			      "with RIPMAP level mode).");
1363 
1364     return _data->numXLevels;
1365 }
1366 
1367 
1368 int
numXLevels() const1369 TiledInputFile::numXLevels () const
1370 {
1371     return _data->numXLevels;
1372 }
1373 
1374 
1375 int
numYLevels() const1376 TiledInputFile::numYLevels () const
1377 {
1378     return _data->numYLevels;
1379 }
1380 
1381 
1382 bool
isValidLevel(int lx,int ly) const1383 TiledInputFile::isValidLevel (int lx, int ly) const
1384 {
1385     if (lx < 0 || ly < 0)
1386 	return false;
1387 
1388     if (levelMode() == MIPMAP_LEVELS && lx != ly)
1389 	return false;
1390 
1391     if (lx >= numXLevels() || ly >= numYLevels())
1392 	return false;
1393 
1394     return true;
1395 }
1396 
1397 
1398 int
levelWidth(int lx) const1399 TiledInputFile::levelWidth (int lx) const
1400 {
1401     try
1402     {
1403         return levelSize (_data->minX, _data->maxX, lx,
1404 			  _data->tileDesc.roundingMode);
1405     }
1406     catch (IEX_NAMESPACE::BaseExc &e)
1407     {
1408 	REPLACE_EXC (e, "Error calling levelWidth() on image "
1409 			"file \"" << fileName() << "\". " << e);
1410 	throw;
1411     }
1412 }
1413 
1414 
1415 int
levelHeight(int ly) const1416 TiledInputFile::levelHeight (int ly) const
1417 {
1418     try
1419     {
1420         return levelSize (_data->minY, _data->maxY, ly,
1421                           _data->tileDesc.roundingMode);
1422     }
1423     catch (IEX_NAMESPACE::BaseExc &e)
1424     {
1425 	REPLACE_EXC (e, "Error calling levelHeight() on image "
1426 			"file \"" << fileName() << "\". " << e);
1427 	throw;
1428     }
1429 }
1430 
1431 
1432 int
numXTiles(int lx) const1433 TiledInputFile::numXTiles (int lx) const
1434 {
1435     if (lx < 0 || lx >= _data->numXLevels)
1436     {
1437         THROW (IEX_NAMESPACE::ArgExc, "Error calling numXTiles() on image "
1438 			    "file \"" << _data->_streamData->is->fileName() << "\" "
1439 			    "(Argument is not in valid range).");
1440 
1441     }
1442 
1443     return _data->numXTiles[lx];
1444 }
1445 
1446 
1447 int
numYTiles(int ly) const1448 TiledInputFile::numYTiles (int ly) const
1449 {
1450     if (ly < 0 || ly >= _data->numYLevels)
1451     {
1452         THROW (IEX_NAMESPACE::ArgExc, "Error calling numYTiles() on image "
1453 			    "file \"" << _data->_streamData->is->fileName() << "\" "
1454 			    "(Argument is not in valid range).");
1455     }
1456 
1457     return _data->numYTiles[ly];
1458 }
1459 
1460 
1461 Box2i
dataWindowForLevel(int l) const1462 TiledInputFile::dataWindowForLevel (int l) const
1463 {
1464     return dataWindowForLevel (l, l);
1465 }
1466 
1467 
1468 Box2i
dataWindowForLevel(int lx,int ly) const1469 TiledInputFile::dataWindowForLevel (int lx, int ly) const
1470 {
1471     try
1472     {
1473 	return OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForLevel (
1474 	        _data->tileDesc,
1475 	        _data->minX, _data->maxX,
1476 	        _data->minY, _data->maxY,
1477 	        lx, ly);
1478     }
1479     catch (IEX_NAMESPACE::BaseExc &e)
1480     {
1481 	REPLACE_EXC (e, "Error calling dataWindowForLevel() on image "
1482 			"file \"" << fileName() << "\". " << e);
1483 	throw;
1484     }
1485 }
1486 
1487 
1488 Box2i
dataWindowForTile(int dx,int dy,int l) const1489 TiledInputFile::dataWindowForTile (int dx, int dy, int l) const
1490 {
1491     return dataWindowForTile (dx, dy, l, l);
1492 }
1493 
1494 
1495 Box2i
dataWindowForTile(int dx,int dy,int lx,int ly) const1496 TiledInputFile::dataWindowForTile (int dx, int dy, int lx, int ly) const
1497 {
1498     try
1499     {
1500 	if (!isValidTile (dx, dy, lx, ly))
1501 	    throw IEX_NAMESPACE::ArgExc ("Arguments not in valid range.");
1502 
1503         return OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile (
1504                 _data->tileDesc,
1505                 _data->minX, _data->maxX,
1506                 _data->minY, _data->maxY,
1507                 dx, dy, lx, ly);
1508     }
1509     catch (IEX_NAMESPACE::BaseExc &e)
1510     {
1511 	REPLACE_EXC (e, "Error calling dataWindowForTile() on image "
1512 			"file \"" << fileName() << "\". " << e);
1513 	throw;
1514     }
1515 }
1516 
1517 
1518 bool
isValidTile(int dx,int dy,int lx,int ly) const1519 TiledInputFile::isValidTile (int dx, int dy, int lx, int ly) const
1520 {
1521     return ((lx < _data->numXLevels && lx >= 0) &&
1522             (ly < _data->numYLevels && ly >= 0) &&
1523             (dx < _data->numXTiles[lx] && dx >= 0) &&
1524             (dy < _data->numYTiles[ly] && dy >= 0));
1525 }
1526 
tileOrder(int dx[],int dy[],int lx[],int ly[]) const1527 void TiledInputFile::tileOrder(int dx[], int dy[], int lx[], int ly[]) const
1528 {
1529    return _data->tileOffsets.getTileOrder(dx,dy,lx,ly);
1530 }
1531 
1532 
1533 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT
1534