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