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//
38//	class ScanLineInputFile
39//
40//-----------------------------------------------------------------------------
41
42#include <ImfScanLineInputFile.h>
43#include <ImfChannelList.h>
44#include <ImfMisc.h>
45#include <ImfStdIO.h>
46#include <ImfCompressor.h>
47#include "ImathBox.h"
48#include "ImathFun.h"
49#include <ImfXdr.h>
50#include <ImfConvert.h>
51#include <ImfThreading.h>
52#include "IlmThreadPool.h"
53#include "IlmThreadSemaphore.h"
54#include "IlmThreadMutex.h"
55#include "Iex.h"
56#include <string>
57#include <vector>
58#include <assert.h>
59
60
61namespace Imf {
62
63using Imath::Box2i;
64using Imath::divp;
65using Imath::modp;
66using std::string;
67using std::vector;
68using std::ifstream;
69using std::min;
70using std::max;
71using IlmThread::Mutex;
72using IlmThread::Lock;
73using IlmThread::Semaphore;
74using IlmThread::Task;
75using IlmThread::TaskGroup;
76using IlmThread::ThreadPool;
77
78namespace {
79
80struct InSliceInfo
81{
82    PixelType	typeInFrameBuffer;
83    PixelType	typeInFile;
84    char *	base;
85    size_t	xStride;
86    size_t	yStride;
87    int		xSampling;
88    int		ySampling;
89    bool	fill;
90    bool	skip;
91    double	fillValue;
92
93    InSliceInfo (PixelType typeInFrameBuffer = HALF,
94		 PixelType typeInFile = HALF,
95	         char *base = 0,
96	         size_t xStride = 0,
97	         size_t yStride = 0,
98	         int xSampling = 1,
99	         int ySampling = 1,
100	         bool fill = false,
101	         bool skip = false,
102	         double fillValue = 0.0);
103};
104
105
106InSliceInfo::InSliceInfo (PixelType tifb,
107			  PixelType tifl,
108			  char *b,
109			  size_t xs, size_t ys,
110			  int xsm, int ysm,
111			  bool f, bool s,
112			  double fv)
113:
114    typeInFrameBuffer (tifb),
115    typeInFile (tifl),
116    base (b),
117    xStride (xs),
118    yStride (ys),
119    xSampling (xsm),
120    ySampling (ysm),
121    fill (f),
122    skip (s),
123    fillValue (fv)
124{
125    // empty
126}
127
128
129struct LineBuffer
130{
131    const char *	uncompressedData;
132    char *		buffer;
133    int			dataSize;
134    int			minY;
135    int			maxY;
136    Compressor *	compressor;
137    Compressor::Format	format;
138    int			number;
139    bool		hasException;
140    string		exception;
141
142    LineBuffer (Compressor * const comp);
143    ~LineBuffer ();
144
145    inline void		wait () {_sem.wait();}
146    inline void		post () {_sem.post();}
147
148  private:
149
150    Semaphore		_sem;
151};
152
153
154LineBuffer::LineBuffer (Compressor *comp):
155    uncompressedData (0),
156    buffer (0),
157    dataSize (0),
158    compressor (comp),
159    format (defaultFormat(compressor)),
160    number (-1),
161    hasException (false),
162    exception (),
163    _sem (1)
164{
165    // empty
166}
167
168
169LineBuffer::~LineBuffer ()
170{
171    delete compressor;
172}
173
174} // namespace
175
176
177struct ScanLineInputFile::Data: public Mutex
178{
179    Header		header;		    // the image header
180    int			version;            // file's version
181    FrameBuffer		frameBuffer;	    // framebuffer to write into
182    LineOrder		lineOrder;          // order of the scanlines in file
183    int			minX;		    // data window's min x coord
184    int			maxX;		    // data window's max x coord
185    int			minY;		    // data window's min y coord
186    int			maxY;		    // data window's max x coord
187    vector<Int64>	lineOffsets;	    // stores offsets in file for
188					    // each line
189    bool		fileIsComplete;	    // True if no scanlines are missing
190    					    // in the file
191    int			nextLineBufferMinY; // minimum y of the next linebuffer
192    vector<size_t>	bytesPerLine;       // combined size of a line over all
193                                            // channels
194    vector<size_t>	offsetInLineBuffer; // offset for each scanline in its
195                                            // linebuffer
196    vector<InSliceInfo>	slices;             // info about channels in file
197    IStream *		is;                 // file stream to read from
198
199    vector<LineBuffer*> lineBuffers;        // each holds one line buffer
200    int			linesInBuffer;      // number of scanlines each buffer
201                                            // holds
202    size_t		lineBufferSize;     // size of the line buffer
203
204     Data (IStream *is, int numThreads);
205    ~Data ();
206
207    inline LineBuffer * getLineBuffer (int number); // hash function from line
208    						    // buffer indices into our
209						    // vector of line buffers
210};
211
212
213ScanLineInputFile::Data::Data (IStream *is, int numThreads):
214    is (is)
215{
216    //
217    // We need at least one lineBuffer, but if threading is used,
218    // to keep n threads busy we need 2*n lineBuffers
219    //
220
221    lineBuffers.resize (max (1, 2 * numThreads));
222}
223
224
225ScanLineInputFile::Data::~Data ()
226{
227    for (size_t i = 0; i < lineBuffers.size(); i++)
228        delete lineBuffers[i];
229}
230
231
232inline LineBuffer *
233ScanLineInputFile::Data::getLineBuffer (int lineBufferNumber)
234{
235    return lineBuffers[lineBufferNumber % lineBuffers.size()];
236}
237
238
239namespace {
240
241
242void
243reconstructLineOffsets (IStream &is,
244			LineOrder lineOrder,
245			vector<Int64> &lineOffsets)
246{
247    Int64 position = is.tellg();
248
249    try
250    {
251	for (unsigned int i = 0; i < lineOffsets.size(); i++)
252	{
253	    Int64 lineOffset = is.tellg();
254
255	    int y;
256	    Xdr::read <StreamIO> (is, y);
257
258	    int dataSize;
259	    Xdr::read <StreamIO> (is, dataSize);
260
261	    Xdr::skip <StreamIO> (is, dataSize);
262
263	    if (lineOrder == INCREASING_Y)
264		lineOffsets[i] = lineOffset;
265	    else
266		lineOffsets[lineOffsets.size() - i - 1] = lineOffset;
267	}
268    }
269    catch (...)
270    {
271	//
272	// Suppress all exceptions.  This functions is
273	// called only to reconstruct the line offset
274	// table for incomplete files, and exceptions
275	// are likely.
276	//
277    }
278
279    is.clear();
280    is.seekg (position);
281}
282
283
284void
285readLineOffsets (IStream &is,
286		 LineOrder lineOrder,
287		 vector<Int64> &lineOffsets,
288		 bool &complete)
289{
290    for (unsigned int i = 0; i < lineOffsets.size(); i++)
291    {
292	Xdr::read <StreamIO> (is, lineOffsets[i]);
293    }
294
295    complete = true;
296
297    for (unsigned int i = 0; i < lineOffsets.size(); i++)
298    {
299	if (lineOffsets[i] <= 0)
300	{
301	    //
302	    // Invalid data in the line offset table mean that
303	    // the file is probably incomplete (the table is
304	    // the last thing written to the file).  Either
305	    // some process is still busy writing the file,
306	    // or writing the file was aborted.
307	    //
308	    // We should still be able to read the existing
309	    // parts of the file.  In order to do this, we
310	    // have to make a sequential scan over the scan
311	    // line data to reconstruct the line offset table.
312	    //
313
314	    complete = false;
315	    reconstructLineOffsets (is, lineOrder, lineOffsets);
316	    break;
317	}
318    }
319}
320
321
322void
323readPixelData (ScanLineInputFile::Data *ifd,
324	       int minY,
325	       char *&buffer,
326	       int &dataSize)
327{
328    //
329    // Read a single line buffer from the input file.
330    //
331    // If the input file is not memory-mapped, we copy the pixel data into
332    // into the array pointed to by buffer.  If the file is memory-mapped,
333    // then we change where buffer points to instead of writing into the
334    // array (hence buffer needs to be a reference to a char *).
335    //
336
337    Int64 lineOffset =
338	ifd->lineOffsets[(minY - ifd->minY) / ifd->linesInBuffer];
339
340    if (lineOffset == 0)
341	THROW (Iex::InputExc, "Scan line " << minY << " is missing.");
342
343    //
344    // Seek to the start of the scan line in the file,
345    // if necessary.
346    //
347
348    if (ifd->nextLineBufferMinY != minY)
349	ifd->is->seekg (lineOffset);
350
351    //
352    // Read the data block's header.
353    //
354
355    int yInFile;
356
357    Xdr::read <StreamIO> (*ifd->is, yInFile);
358    Xdr::read <StreamIO> (*ifd->is, dataSize);
359
360    if (yInFile != minY)
361        throw Iex::InputExc ("Unexpected data block y coordinate.");
362
363    if (dataSize > (int) ifd->lineBufferSize)
364	throw Iex::InputExc ("Unexpected data block length.");
365
366    //
367    // Read the pixel data.
368    //
369
370    if (ifd->is->isMemoryMapped ())
371        buffer = ifd->is->readMemoryMapped (dataSize);
372    else
373        ifd->is->read (buffer, dataSize);
374
375    //
376    // Keep track of which scan line is the next one in
377    // the file, so that we can avoid redundant seekg()
378    // operations (seekg() can be fairly expensive).
379    //
380
381    if (ifd->lineOrder == INCREASING_Y)
382	ifd->nextLineBufferMinY = minY + ifd->linesInBuffer;
383    else
384	ifd->nextLineBufferMinY = minY - ifd->linesInBuffer;
385}
386
387
388//
389// A LineBufferTask encapsulates the task uncompressing a set of
390// scanlines (line buffer) and copying them into the frame buffer.
391//
392
393class LineBufferTask : public Task
394{
395  public:
396
397    LineBufferTask (TaskGroup *group,
398                    ScanLineInputFile::Data *ifd,
399		    LineBuffer *lineBuffer,
400                    int scanLineMin,
401		    int scanLineMax);
402
403    virtual ~LineBufferTask ();
404
405    virtual void		execute ();
406
407  private:
408
409    ScanLineInputFile::Data *	_ifd;
410    LineBuffer *		_lineBuffer;
411    int				_scanLineMin;
412    int				_scanLineMax;
413};
414
415
416LineBufferTask::LineBufferTask
417    (TaskGroup *group,
418     ScanLineInputFile::Data *ifd,
419     LineBuffer *lineBuffer,
420     int scanLineMin,
421     int scanLineMax)
422:
423    Task (group),
424    _ifd (ifd),
425    _lineBuffer (lineBuffer),
426    _scanLineMin (scanLineMin),
427    _scanLineMax (scanLineMax)
428{
429    // empty
430}
431
432
433LineBufferTask::~LineBufferTask ()
434{
435    //
436    // Signal that the line buffer is now free
437    //
438
439    _lineBuffer->post ();
440}
441
442
443void
444LineBufferTask::execute ()
445{
446    try
447    {
448        //
449        // Uncompress the data, if necessary
450        //
451
452        if (_lineBuffer->uncompressedData == 0)
453        {
454            int uncompressedSize = 0;
455            int maxY = min (_lineBuffer->maxY, _ifd->maxY);
456
457            for (int i = _lineBuffer->minY - _ifd->minY;
458                 i <= maxY - _ifd->minY;
459		 ++i)
460	    {
461                uncompressedSize += (int) _ifd->bytesPerLine[i];
462	    }
463
464            if (_lineBuffer->compressor &&
465                _lineBuffer->dataSize < uncompressedSize)
466            {
467                _lineBuffer->format = _lineBuffer->compressor->format();
468
469                _lineBuffer->dataSize = _lineBuffer->compressor->uncompress
470                    (_lineBuffer->buffer, _lineBuffer->dataSize,
471		     _lineBuffer->minY, _lineBuffer->uncompressedData);
472            }
473            else
474            {
475                //
476                // If the line is uncompressed, it's in XDR format,
477                // regardless of the compressor's output format.
478                //
479
480                _lineBuffer->format = Compressor::XDR;
481                _lineBuffer->uncompressedData = _lineBuffer->buffer;
482            }
483        }
484
485        int yStart, yStop, dy;
486
487        if (_ifd->lineOrder == INCREASING_Y)
488        {
489            yStart = _scanLineMin;
490            yStop = _scanLineMax + 1;
491            dy = 1;
492        }
493        else
494        {
495            yStart = _scanLineMax;
496            yStop = _scanLineMin - 1;
497            dy = -1;
498        }
499
500        for (int y = yStart; y != yStop; y += dy)
501        {
502            //
503            // Convert one scan line's worth of pixel data back
504            // from the machine-independent representation, and
505            // store the result in the frame buffer.
506            //
507
508            const char *readPtr = _lineBuffer->uncompressedData +
509                                  _ifd->offsetInLineBuffer[y - _ifd->minY];
510
511            //
512            // Iterate over all image channels.
513            //
514
515            for (unsigned int i = 0; i < _ifd->slices.size(); ++i)
516            {
517                //
518                // Test if scan line y of this channel contains any data
519		// (the scan line contains data only if y % ySampling == 0).
520                //
521
522                const InSliceInfo &slice = _ifd->slices[i];
523
524                if (modp (y, slice.ySampling) != 0)
525                    continue;
526
527                //
528                // Find the x coordinates of the leftmost and rightmost
529                // sampled pixels (i.e. pixels within the data window
530                // for which x % xSampling == 0).
531                //
532
533                int dMinX = divp (_ifd->minX, slice.xSampling);
534                int dMaxX = divp (_ifd->maxX, slice.xSampling);
535
536                //
537		// Fill the frame buffer with pixel data.
538                //
539
540                if (slice.skip)
541                {
542                    //
543                    // The file contains data for this channel, but
544                    // the frame buffer contains no slice for this channel.
545                    //
546
547                    skipChannel (readPtr, slice.typeInFile, dMaxX - dMinX + 1);
548                }
549                else
550                {
551                    //
552                    // The frame buffer contains a slice for this channel.
553                    //
554
555                    char *linePtr  = slice.base +
556                                        divp (y, slice.ySampling) *
557                                        slice.yStride;
558
559                    char *writePtr = linePtr + dMinX * slice.xStride;
560                    char *endPtr   = linePtr + dMaxX * slice.xStride;
561
562                    copyIntoFrameBuffer (readPtr, writePtr, endPtr,
563                                         slice.xStride, slice.fill,
564                                         slice.fillValue, _lineBuffer->format,
565                                         slice.typeInFrameBuffer,
566                                         slice.typeInFile);
567                }
568            }
569        }
570    }
571    catch (std::exception &e)
572    {
573        if (!_lineBuffer->hasException)
574        {
575            _lineBuffer->exception = e.what();
576            _lineBuffer->hasException = true;
577        }
578    }
579    catch (...)
580    {
581        if (!_lineBuffer->hasException)
582        {
583            _lineBuffer->exception = "unrecognized exception";
584            _lineBuffer->hasException = true;
585        }
586    }
587}
588
589
590LineBufferTask *
591newLineBufferTask
592    (TaskGroup *group,
593     ScanLineInputFile::Data *ifd,
594     int number,
595     int scanLineMin,
596     int scanLineMax)
597{
598    //
599    // Wait for a line buffer to become available, fill the line
600    // buffer with raw data from the file if necessary, and create
601    // a new LineBufferTask whose execute() method will uncompress
602    // the contents of the buffer and copy the pixels into the
603    // frame buffer.
604    //
605
606    LineBuffer *lineBuffer = ifd->getLineBuffer (number);
607
608    try
609    {
610	lineBuffer->wait ();
611
612	if (lineBuffer->number != number)
613	{
614	    lineBuffer->minY = ifd->minY + number * ifd->linesInBuffer;
615	    lineBuffer->maxY = lineBuffer->minY + ifd->linesInBuffer - 1;
616
617	    lineBuffer->number = number;
618	    lineBuffer->uncompressedData = 0;
619
620	    readPixelData (ifd, lineBuffer->minY,
621			   lineBuffer->buffer,
622			   lineBuffer->dataSize);
623	}
624    }
625    catch (...)
626    {
627	//
628	// Reading from the file caused an exception.
629	// Signal that the line buffer is free, and
630	// re-throw the exception.
631	//
632
633	lineBuffer->number = -1;
634	lineBuffer->post();
635	throw;
636    }
637
638    scanLineMin = max (lineBuffer->minY, scanLineMin);
639    scanLineMax = min (lineBuffer->maxY, scanLineMax);
640
641    return new LineBufferTask (group, ifd, lineBuffer,
642			       scanLineMin, scanLineMax);
643}
644
645} // namespace
646
647
648ScanLineInputFile::ScanLineInputFile
649    (const Header &header,
650     IStream *is,
651     int numThreads)
652:
653    _data (new Data (is, numThreads))
654{
655    try
656    {
657	_data->header = header;
658
659	_data->lineOrder = _data->header.lineOrder();
660
661	const Box2i &dataWindow = _data->header.dataWindow();
662
663	_data->minX = dataWindow.min.x;
664	_data->maxX = dataWindow.max.x;
665	_data->minY = dataWindow.min.y;
666	_data->maxY = dataWindow.max.y;
667
668	size_t maxBytesPerLine = bytesPerLineTable (_data->header,
669                                                    _data->bytesPerLine);
670
671        for (size_t i = 0; i < _data->lineBuffers.size(); i++)
672        {
673            _data->lineBuffers[i] = new LineBuffer (newCompressor
674                                                (_data->header.compression(),
675                                                 maxBytesPerLine,
676                                                 _data->header));
677        }
678
679        _data->linesInBuffer =
680	    numLinesInBuffer (_data->lineBuffers[0]->compressor);
681
682        _data->lineBufferSize = maxBytesPerLine * _data->linesInBuffer;
683
684        if (!_data->is->isMemoryMapped())
685            for (size_t i = 0; i < _data->lineBuffers.size(); i++)
686                _data->lineBuffers[i]->buffer = new char[_data->lineBufferSize];
687
688	_data->nextLineBufferMinY = _data->minY - 1;
689
690	offsetInLineBufferTable (_data->bytesPerLine,
691				 _data->linesInBuffer,
692				 _data->offsetInLineBuffer);
693
694	int lineOffsetSize = (dataWindow.max.y - dataWindow.min.y +
695			      _data->linesInBuffer) / _data->linesInBuffer;
696
697	_data->lineOffsets.resize (lineOffsetSize);
698
699	readLineOffsets (*_data->is,
700			 _data->lineOrder,
701			 _data->lineOffsets,
702			 _data->fileIsComplete);
703    }
704    catch (...)
705    {
706	delete _data;
707	throw;
708    }
709}
710
711
712ScanLineInputFile::~ScanLineInputFile ()
713{
714    if (!_data->is->isMemoryMapped())
715        for (size_t i = 0; i < _data->lineBuffers.size(); i++)
716            delete [] _data->lineBuffers[i]->buffer;
717
718    delete _data;
719}
720
721
722const char *
723ScanLineInputFile::fileName () const
724{
725    return _data->is->fileName();
726}
727
728
729const Header &
730ScanLineInputFile::header () const
731{
732    return _data->header;
733}
734
735
736int
737ScanLineInputFile::version () const
738{
739    return _data->version;
740}
741
742
743void
744ScanLineInputFile::setFrameBuffer (const FrameBuffer &frameBuffer)
745{
746    Lock lock (*_data);
747
748    //
749    // Check if the new frame buffer descriptor is
750    // compatible with the image file header.
751    //
752
753    const ChannelList &channels = _data->header.channels();
754
755    for (FrameBuffer::ConstIterator j = frameBuffer.begin();
756	 j != frameBuffer.end();
757	 ++j)
758    {
759	ChannelList::ConstIterator i = channels.find (j.name());
760
761	if (i == channels.end())
762	    continue;
763
764	if (i.channel().xSampling != j.slice().xSampling ||
765	    i.channel().ySampling != j.slice().ySampling)
766	    THROW (Iex::ArgExc, "X and/or y subsampling factors "
767				"of \"" << i.name() << "\" channel "
768				"of input file \"" << fileName() << "\" are "
769				"not compatible with the frame buffer's "
770				"subsampling factors.");
771    }
772
773    //
774    // Initialize the slice table for readPixels().
775    //
776
777    vector<InSliceInfo> slices;
778    ChannelList::ConstIterator i = channels.begin();
779
780    for (FrameBuffer::ConstIterator j = frameBuffer.begin();
781	 j != frameBuffer.end();
782	 ++j)
783    {
784	while (i != channels.end() && strcmp (i.name(), j.name()) < 0)
785	{
786	    //
787	    // Channel i is present in the file but not
788	    // in the frame buffer; data for channel i
789	    // will be skipped during readPixels().
790	    //
791
792	    slices.push_back (InSliceInfo (i.channel().type,
793					   i.channel().type,
794					   0, // base
795					   0, // xStride
796					   0, // yStride
797					   i.channel().xSampling,
798					   i.channel().ySampling,
799					   false,  // fill
800					   true, // skip
801					   0.0)); // fillValue
802	    ++i;
803	}
804
805	bool fill = false;
806
807	if (i == channels.end() || strcmp (i.name(), j.name()) > 0)
808	{
809	    //
810	    // Channel i is present in the frame buffer, but not in the file.
811	    // In the frame buffer, slice j will be filled with a default value.
812	    //
813
814	    fill = true;
815	}
816
817	slices.push_back (InSliceInfo (j.slice().type,
818				       fill? j.slice().type:
819				             i.channel().type,
820				       j.slice().base,
821				       j.slice().xStride,
822				       j.slice().yStride,
823				       j.slice().xSampling,
824				       j.slice().ySampling,
825				       fill,
826				       false, // skip
827				       j.slice().fillValue));
828
829	if (i != channels.end() && !fill)
830	    ++i;
831    }
832
833    //
834    // Store the new frame buffer.
835    //
836
837    _data->frameBuffer = frameBuffer;
838    _data->slices = slices;
839}
840
841
842const FrameBuffer &
843ScanLineInputFile::frameBuffer () const
844{
845    Lock lock (*_data);
846    return _data->frameBuffer;
847}
848
849
850bool
851ScanLineInputFile::isComplete () const
852{
853    return _data->fileIsComplete;
854}
855
856
857void
858ScanLineInputFile::readPixels (int scanLine1, int scanLine2)
859{
860    try
861    {
862        Lock lock (*_data);
863
864	if (_data->slices.size() == 0)
865	    throw Iex::ArgExc ("No frame buffer specified "
866			       "as pixel data destination.");
867
868	int scanLineMin = min (scanLine1, scanLine2);
869	int scanLineMax = max (scanLine1, scanLine2);
870
871	if (scanLineMin < _data->minY || scanLineMax > _data->maxY)
872	    throw Iex::ArgExc ("Tried to read scan line outside "
873			       "the image file's data window.");
874
875        //
876        // We impose a numbering scheme on the lineBuffers where the first
877        // scanline is contained in lineBuffer 1.
878        //
879        // Determine the first and last lineBuffer numbers in this scanline
880        // range. We always attempt to read the scanlines in the order that
881        // they are stored in the file.
882        //
883
884        int start, stop, dl;
885
886        if (_data->lineOrder == INCREASING_Y)
887        {
888            start = (scanLineMin - _data->minY) / _data->linesInBuffer;
889            stop  = (scanLineMax - _data->minY) / _data->linesInBuffer + 1;
890            dl = 1;
891        }
892        else
893        {
894            start = (scanLineMax - _data->minY) / _data->linesInBuffer;
895            stop  = (scanLineMin - _data->minY) / _data->linesInBuffer - 1;
896            dl = -1;
897        }
898
899        //
900        // Create a task group for all line buffer tasks.  When the
901	// task group goes out of scope, the destructor waits until
902	// all tasks are complete.
903        //
904
905        {
906            TaskGroup taskGroup;
907
908            //
909            // Add the line buffer tasks.
910            //
911            // The tasks will execute in the order that they are created
912            // because we lock the line buffers during construction and the
913            // constructors are called by the main thread.  Hence, in order
914	    // for a successive task to execute the previous task which
915	    // used that line buffer must have completed already.
916            //
917
918            for (int l = start; l != stop; l += dl)
919            {
920                ThreadPool::addGlobalTask (newLineBufferTask (&taskGroup,
921                                                              _data, l,
922                                                              scanLineMin,
923                                                              scanLineMax));
924            }
925
926	    //
927            // finish all tasks
928	    //
929        }
930
931	//
932	// Exeption handling:
933	//
934	// LineBufferTask::execute() may have encountered exceptions, but
935	// those exceptions occurred in another thread, not in the thread
936	// that is executing this call to ScanLineInputFile::readPixels().
937	// LineBufferTask::execute() has caught all exceptions and stored
938	// the exceptions' what() strings in the line buffers.
939	// Now we check if any line buffer contains a stored exception; if
940	// this is the case then we re-throw the exception in this thread.
941	// (It is possible that multiple line buffers contain stored
942	// exceptions.  We re-throw the first exception we find and
943	// ignore all others.)
944	//
945
946	const string *exception = 0;
947
948        for (int i = 0; i < _data->lineBuffers.size(); ++i)
949	{
950            LineBuffer *lineBuffer = _data->lineBuffers[i];
951
952	    if (lineBuffer->hasException && !exception)
953		exception = &lineBuffer->exception;
954
955	    lineBuffer->hasException = false;
956	}
957
958	if (exception)
959	    throw Iex::IoExc (*exception);
960    }
961    catch (Iex::BaseExc &e)
962    {
963	REPLACE_EXC (e, "Error reading pixel data from image "
964		        "file \"" << fileName() << "\". " << e);
965	throw;
966    }
967}
968
969
970void
971ScanLineInputFile::readPixels (int scanLine)
972{
973    readPixels (scanLine, scanLine);
974}
975
976
977void
978ScanLineInputFile::rawPixelData (int firstScanLine,
979				 const char *&pixelData,
980				 int &pixelDataSize)
981{
982    try
983    {
984        Lock lock (*_data);
985
986	if (firstScanLine < _data->minY || firstScanLine > _data->maxY)
987	{
988	    throw Iex::ArgExc ("Tried to read scan line outside "
989			       "the image file's data window.");
990	}
991
992        int minY = lineBufferMinY
993	    (firstScanLine, _data->minY, _data->linesInBuffer);
994
995	readPixelData
996	    (_data, minY, _data->lineBuffers[0]->buffer, pixelDataSize);
997
998	pixelData = _data->lineBuffers[0]->buffer;
999    }
1000    catch (Iex::BaseExc &e)
1001    {
1002	REPLACE_EXC (e, "Error reading pixel data from image "
1003		        "file \"" << fileName() << "\". " << e);
1004	throw;
1005    }
1006}
1007
1008} // namespace Imf
1009