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 InputFile
38 //
39 //-----------------------------------------------------------------------------
40 
41 #include "ImfInputFile.h"
42 #include "ImfScanLineInputFile.h"
43 #include "ImfTiledInputFile.h"
44 #include "ImfChannelList.h"
45 #include "ImfMisc.h"
46 #include "ImfStdIO.h"
47 #include "ImfVersion.h"
48 #include "ImfPartType.h"
49 #include "ImfInputPartData.h"
50 #include "ImfMultiPartInputFile.h"
51 
52 #include <ImfCompositeDeepScanLine.h>
53 #include <ImfDeepScanLineInputFile.h>
54 
55 #include "ImathFun.h"
56 #include "IlmThreadMutex.h"
57 #include "Iex.h"
58 #include "half.h"
59 
60 #include <fstream>
61 #include <algorithm>
62 
63 #include "ImfNamespace.h"
64 
65 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
66 
67 
68 using IMATH_NAMESPACE::Box2i;
69 using IMATH_NAMESPACE::divp;
70 using IMATH_NAMESPACE::modp;
71 using ILMTHREAD_NAMESPACE::Mutex;
72 using ILMTHREAD_NAMESPACE::Lock;
73 
74 
75 //
76 // Struct InputFile::Data stores things that will be
77 // needed between calls to readPixels
78 //
79 
80 struct InputFile::Data : public Mutex
81 {
82     Header              header;
83     int                 version;
84     bool                isTiled;
85 
86     TiledInputFile *	tFile;
87     ScanLineInputFile *	sFile;
88     DeepScanLineInputFile * dsFile;
89 
90     LineOrder		lineOrder;      // the file's lineorder
91     int			minY;           // data window's min y coord
92     int			maxY;           // data window's max x coord
93 
94     FrameBuffer		tFileBuffer;
95     FrameBuffer *	cachedBuffer;
96     CompositeDeepScanLine * compositor; // for loading deep files
97 
98     int			cachedTileY;
99     int                 offset;
100 
101     int                 numThreads;
102 
103     int                 partNumber;
104     InputPartData*      part;
105 
106     bool                multiPartBackwardSupport;
107     MultiPartInputFile* multiPartFile;
108     InputStreamMutex    * _streamData;
109     bool                _deleteStream;
110 
111      Data (int numThreads);
112     ~Data ();
113 
114     void		deleteCachedBuffer();
115 };
116 
117 
Data(int numThreads)118 InputFile::Data::Data (int numThreads):
119     isTiled (false),
120     tFile (0),
121     sFile (0),
122     dsFile(0),
123     cachedBuffer (0),
124     compositor(0),
125     cachedTileY (-1),
126     numThreads (numThreads),
127     partNumber (-1),
128     part(NULL),
129     multiPartBackwardSupport (false),
130     multiPartFile (0),
131     _streamData(0),
132     _deleteStream(false)
133 
134 {
135     // empty
136 }
137 
138 
~Data()139 InputFile::Data::~Data ()
140 {
141     if (tFile)
142         delete tFile;
143     if (sFile)
144         delete sFile;
145     if (dsFile)
146         delete dsFile;
147     if (compositor)
148         delete compositor;
149 
150     deleteCachedBuffer();
151 
152     if (multiPartBackwardSupport && multiPartFile)
153         delete multiPartFile;
154 }
155 
156 
157 void
deleteCachedBuffer()158 InputFile::Data::deleteCachedBuffer()
159 {
160     //
161     // Delete the cached frame buffer, and all memory
162     // allocated for the slices in the cached frameBuffer.
163     //
164 
165     if (cachedBuffer)
166     {
167 	for (FrameBuffer::Iterator k = cachedBuffer->begin();
168 	     k != cachedBuffer->end();
169 	     ++k)
170 	{
171 	    Slice &s = k.slice();
172 
173 	    switch (s.type)
174 	    {
175 	      case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT:
176 
177 		delete [] (((unsigned int *)s.base) + offset);
178 		break;
179 
180 	      case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF:
181 
182 		delete [] ((half *)s.base + offset);
183 		break;
184 
185 	      case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT:
186 
187 		delete [] (((float *)s.base) + offset);
188 		break;
189               case NUM_PIXELTYPES :
190                   throw(IEX_NAMESPACE::ArgExc("Invalid pixel type"));
191 	    }
192 	}
193 
194 	//
195 	// delete the cached frame buffer
196 	//
197 
198 	delete cachedBuffer;
199 	cachedBuffer = 0;
200     }
201 }
202 
203 
204 namespace {
205 
206 void
bufferedReadPixels(InputFile::Data * ifd,int scanLine1,int scanLine2)207 bufferedReadPixels (InputFile::Data* ifd, int scanLine1, int scanLine2)
208 {
209     //
210     // bufferedReadPixels reads each row of tiles that intersect the
211     // scan-line range (scanLine1 to scanLine2). The previous row of
212     // tiles is cached in order to prevent redundent tile reads when
213     // accessing scanlines sequentially.
214     //
215 
216     int minY = std::min (scanLine1, scanLine2);
217     int maxY = std::max (scanLine1, scanLine2);
218 
219     if (minY < ifd->minY || maxY >  ifd->maxY)
220     {
221         throw IEX_NAMESPACE::ArgExc ("Tried to read scan line outside "
222 			   "the image file's data window.");
223     }
224 
225     //
226     // The minimum and maximum y tile coordinates that intersect this
227     // scanline range
228     //
229 
230     int minDy = (minY - ifd->minY) / ifd->tFile->tileYSize();
231     int maxDy = (maxY - ifd->minY) / ifd->tFile->tileYSize();
232 
233     //
234     // Figure out which one is first in the file so we can read without seeking
235     //
236 
237     int yStart, yEnd, yStep;
238 
239     if (ifd->lineOrder == DECREASING_Y)
240     {
241         yStart = maxDy;
242         yEnd = minDy - 1;
243         yStep = -1;
244     }
245     else
246     {
247         yStart = minDy;
248         yEnd = maxDy + 1;
249         yStep = 1;
250     }
251 
252     //
253     // the number of pixels in a row of tiles
254     //
255 
256     Box2i levelRange = ifd->tFile->dataWindowForLevel(0);
257 
258     //
259     // Read the tiles into our temporary framebuffer and copy them into
260     // the user's buffer
261     //
262 
263     for (int j = yStart; j != yEnd; j += yStep)
264     {
265         Box2i tileRange = ifd->tFile->dataWindowForTile (0, j, 0);
266 
267         int minYThisRow = std::max (minY, tileRange.min.y);
268         int maxYThisRow = std::min (maxY, tileRange.max.y);
269 
270         if (j != ifd->cachedTileY)
271         {
272             //
273             // We don't have any valid buffered info, so we need to read in
274             // from the file.
275             //
276 
277             ifd->tFile->readTiles (0, ifd->tFile->numXTiles (0) - 1, j, j);
278             ifd->cachedTileY = j;
279         }
280 
281         //
282         // Copy the data from our cached framebuffer into the user's
283         // framebuffer.
284         //
285 
286         for (FrameBuffer::ConstIterator k = ifd->cachedBuffer->begin();
287              k != ifd->cachedBuffer->end();
288              ++k)
289         {
290             Slice fromSlice = k.slice();		// slice to write from
291             Slice toSlice = ifd->tFileBuffer[k.name()];	// slice to write to
292 
293             char *fromPtr, *toPtr;
294             int size = pixelTypeSize (toSlice.type);
295 
296 	    int xStart = levelRange.min.x;
297 	    int yStart = minYThisRow;
298 
299 	    while (modp (xStart, toSlice.xSampling) != 0)
300 		++xStart;
301 
302 	    while (modp (yStart, toSlice.ySampling) != 0)
303 		++yStart;
304 
305             for (int y = yStart;
306 		 y <= maxYThisRow;
307 		 y += toSlice.ySampling)
308             {
309 		//
310                 // Set the pointers to the start of the y scanline in
311                 // this row of tiles
312 		//
313 
314                 fromPtr = fromSlice.base +
315                           (y - tileRange.min.y) * fromSlice.yStride +
316                           xStart * fromSlice.xStride;
317 
318                 toPtr = toSlice.base +
319                         divp (y, toSlice.ySampling) * toSlice.yStride +
320                         divp (xStart, toSlice.xSampling) * toSlice.xStride;
321 
322 		//
323                 // Copy all pixels for the scanline in this row of tiles
324 		//
325 
326                 for (int x = xStart;
327 		     x <= levelRange.max.x;
328 		     x += toSlice.xSampling)
329                 {
330 		    for (int i = 0; i < size; ++i)
331 			toPtr[i] = fromPtr[i];
332 
333 		    fromPtr += fromSlice.xStride * toSlice.xSampling;
334 		    toPtr += toSlice.xStride;
335                 }
336             }
337         }
338     }
339 }
340 
341 } // namespace
342 
343 
344 
InputFile(const char fileName[],int numThreads)345 InputFile::InputFile (const char fileName[], int numThreads):
346     _data (new Data (numThreads))
347 {
348     _data->_streamData = NULL;
349     _data->_deleteStream=true;
350 
351     OPENEXR_IMF_INTERNAL_NAMESPACE::IStream* is = 0;
352     try
353     {
354         is = new StdIFStream (fileName);
355         readMagicNumberAndVersionField(*is, _data->version);
356 
357         //
358         // compatibility to read multipart file.
359         //
360         if (isMultiPart(_data->version))
361         {
362             compatibilityInitialize(*is);
363         }
364         else
365         {
366             _data->_streamData = new InputStreamMutex();
367             _data->_streamData->is = is;
368             _data->header.readFrom (*_data->_streamData->is, _data->version);
369 
370             // fix type attribute in single part regular image types
371             // (may be wrong if an old version of OpenEXR converts
372             // a tiled image to scanline or vice versa)
373             if(!isNonImage(_data->version)  &&
374                !isMultiPart(_data->version) &&
375                _data->header.hasType())
376             {
377                 _data->header.setType(isTiled(_data->version) ? TILEDIMAGE : SCANLINEIMAGE);
378             }
379 
380             _data->header.sanityCheck (isTiled (_data->version));
381 
382             initialize();
383         }
384     }
385     catch (IEX_NAMESPACE::BaseExc &e)
386     {
387         if (is)          delete is;
388 
389         if ( _data && !_data->multiPartBackwardSupport  && _data->_streamData)
390         {
391             delete _data->_streamData;
392             _data->_streamData=NULL;
393         }
394 
395         if (_data)       delete _data;
396         _data=NULL;
397 
398         REPLACE_EXC (e, "Cannot read image file "
399 			"\"" << fileName << "\". " << e);
400         throw;
401     }
402     catch (...)
403     {
404         if (is)          delete is;
405         if (_data && !_data->multiPartBackwardSupport && _data->_streamData)
406         {
407             delete _data->_streamData;
408         }
409         if (_data)       delete _data;
410 
411         throw;
412     }
413 }
414 
415 
InputFile(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream & is,int numThreads)416 InputFile::InputFile (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is, int numThreads):
417     _data (new Data (numThreads))
418 {
419     _data->_streamData=NULL;
420     _data->_deleteStream=false;
421     try
422     {
423         readMagicNumberAndVersionField(is, _data->version);
424 
425         //
426         // Backward compatibility to read multpart file.
427         //
428         if (isMultiPart(_data->version))
429         {
430             compatibilityInitialize(is);
431         }
432         else
433         {
434             _data->_streamData = new InputStreamMutex();
435             _data->_streamData->is = &is;
436             _data->header.readFrom (*_data->_streamData->is, _data->version);
437 
438             // fix type attribute in single part regular image types
439             // (may be wrong if an old version of OpenEXR converts
440             // a tiled image to scanline or vice versa)
441             if(!isNonImage(_data->version)  &&
442                !isMultiPart(_data->version) &&
443                _data->header.hasType())
444             {
445                 _data->header.setType(isTiled(_data->version) ? TILEDIMAGE : SCANLINEIMAGE);
446             }
447 
448             _data->header.sanityCheck (isTiled (_data->version));
449 
450             initialize();
451         }
452     }
453     catch (IEX_NAMESPACE::BaseExc &e)
454     {
455         if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData;
456         if (_data)       delete _data;
457         _data=NULL;
458 
459         REPLACE_EXC (e, "Cannot read image file "
460 			"\"" << is.fileName() << "\". " << e);
461         throw;
462     }
463     catch (...)
464     {
465         if (_data &&  !_data->multiPartBackwardSupport  && _data->_streamData) delete _data->_streamData;
466         if (_data)       delete _data;
467         _data=NULL;
468         throw;
469     }
470 }
471 
472 
InputFile(InputPartData * part)473 InputFile::InputFile (InputPartData* part) :
474     _data (new Data (part->numThreads))
475 {
476     _data->_deleteStream=false;
477     multiPartInitialize (part);
478 }
479 
480 
481 void
compatibilityInitialize(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream & is)482 InputFile::compatibilityInitialize (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is)
483 {
484     is.seekg(0);
485 
486     //
487     // Construct a MultiPartInputFile, initialize InputFile
488     // with the part 0 data.
489     // (TODO) may want to have a way to set the reconstruction flag.
490     //
491     _data->multiPartBackwardSupport = true;
492     _data->multiPartFile = new MultiPartInputFile(is, _data->numThreads);
493     InputPartData* part = _data->multiPartFile->getPart(0);
494 
495     multiPartInitialize (part);
496 }
497 
498 
499 void
multiPartInitialize(InputPartData * part)500 InputFile::multiPartInitialize (InputPartData* part)
501 {
502     _data->_streamData = part->mutex;
503     _data->version = part->version;
504     _data->header = part->header;
505     _data->partNumber = part->partNumber;
506     _data->part = part;
507 
508     initialize();
509 }
510 
511 
512 void
initialize()513 InputFile::initialize ()
514 {
515     if (!_data->part)
516     {
517         if(_data->header.hasType() && _data->header.type()==DEEPSCANLINE)
518         {
519             _data->isTiled=false;
520             const Box2i &dataWindow = _data->header.dataWindow();
521             _data->minY = dataWindow.min.y;
522             _data->maxY = dataWindow.max.y;
523 
524             _data->dsFile = new DeepScanLineInputFile (_data->header,
525                                                _data->_streamData->is,
526                                                _data->version,
527                                                _data->numThreads);
528             _data->compositor = new CompositeDeepScanLine;
529             _data->compositor->addSource(_data->dsFile);
530         }
531 
532         else if (isTiled (_data->version))
533         {
534             _data->isTiled = true;
535             _data->lineOrder = _data->header.lineOrder();
536 
537             //
538             // Save the dataWindow information
539             //
540 
541             const Box2i &dataWindow = _data->header.dataWindow();
542             _data->minY = dataWindow.min.y;
543             _data->maxY = dataWindow.max.y;
544 
545             _data->tFile = new TiledInputFile (_data->header,
546                                                _data->_streamData->is,
547                                                _data->version,
548                                                _data->numThreads);
549         }
550 
551         else if(!_data->header.hasType() || _data->header.type()==SCANLINEIMAGE)
552         {
553             _data->sFile = new ScanLineInputFile (_data->header,
554                                                   _data->_streamData->is,
555                                                   _data->numThreads);
556         }else{
557             // type set but not recognised
558 
559             THROW(IEX_NAMESPACE::ArgExc, "InputFile cannot handle parts of type " << _data->header.type());
560         }
561     }
562     else
563     {
564         if(_data->header.hasType() && _data->header.type()==DEEPSCANLINE)
565         {
566             _data->isTiled=false;
567             const Box2i &dataWindow = _data->header.dataWindow();
568             _data->minY = dataWindow.min.y;
569             _data->maxY = dataWindow.max.y;
570 
571             _data->dsFile = new DeepScanLineInputFile (_data->part);
572             _data->compositor = new CompositeDeepScanLine;
573             _data->compositor->addSource(_data->dsFile);
574         }
575         else if (isTiled (_data->header.type()))
576         {
577             _data->isTiled = true;
578             _data->lineOrder = _data->header.lineOrder();
579 
580             //
581             // Save the dataWindow information
582             //
583 
584             const Box2i &dataWindow = _data->header.dataWindow();
585             _data->minY = dataWindow.min.y;
586             _data->maxY = dataWindow.max.y;
587 
588             _data->tFile = new TiledInputFile (_data->part);
589         }
590         else if(!_data->header.hasType() || _data->header.type()==SCANLINEIMAGE)
591         {
592             _data->sFile = new ScanLineInputFile (_data->part);
593         }else{
594             THROW(IEX_NAMESPACE::ArgExc, "InputFile cannot handle parts of type " << _data->header.type());
595 
596         }
597     }
598 }
599 
600 #include <iostream>
~InputFile()601 InputFile::~InputFile ()
602 {
603     if (_data->_deleteStream)
604         delete _data->_streamData->is;
605 
606     // unless this file was opened via the multipart API,
607     // delete the streamData object too
608     if (_data->partNumber==-1 && _data->_streamData)
609         delete _data->_streamData;
610 
611     if (_data)  delete _data;
612 }
613 
614 const char *
fileName() const615 InputFile::fileName () const
616 {
617     return _data->_streamData->is->fileName();
618 }
619 
620 
621 const Header &
header() const622 InputFile::header () const
623 {
624     return _data->header;
625 }
626 
627 
628 int
version() const629 InputFile::version () const
630 {
631     return _data->version;
632 }
633 
634 
635 void
setFrameBuffer(const FrameBuffer & frameBuffer)636 InputFile::setFrameBuffer (const FrameBuffer &frameBuffer)
637 {
638     if (_data->isTiled)
639     {
640 	Lock lock (*_data);
641 
642 	//
643         // We must invalidate the cached buffer if the new frame
644 	// buffer has a different set of channels than the old
645 	// frame buffer, or if the type of a channel has changed.
646 	//
647 
648 	const FrameBuffer &oldFrameBuffer = _data->tFileBuffer;
649 
650 	FrameBuffer::ConstIterator i = oldFrameBuffer.begin();
651 	FrameBuffer::ConstIterator j = frameBuffer.begin();
652 
653 	while (i != oldFrameBuffer.end() && j != frameBuffer.end())
654 	{
655 	    if (strcmp (i.name(), j.name()) || i.slice().type != j.slice().type)
656 		break;
657 
658 	    ++i;
659 	    ++j;
660 	}
661 
662 	if (i != oldFrameBuffer.end() || j != frameBuffer.end())
663         {
664 	    //
665 	    // Invalidate the cached buffer.
666 	    //
667 
668             _data->deleteCachedBuffer ();
669 	    _data->cachedTileY = -1;
670 
671 	    //
672 	    // Create new a cached frame buffer.  It can hold a single
673 	    // row of tiles.  The cached buffer can be reused for each
674 	    // row of tiles because we set the yTileCoords parameter of
675 	    // each Slice to true.
676 	    //
677 
678 	    const Box2i &dataWindow = _data->header.dataWindow();
679 	    _data->cachedBuffer = new FrameBuffer();
680 	    _data->offset = dataWindow.min.x;
681 
682 	    int tileRowSize = (dataWindow.max.x - dataWindow.min.x + 1) *
683 			      _data->tFile->tileYSize();
684 
685 	    for (FrameBuffer::ConstIterator k = frameBuffer.begin();
686 		 k != frameBuffer.end();
687 		 ++k)
688 	    {
689 		Slice s = k.slice();
690 
691 		switch (s.type)
692 		{
693 		  case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT:
694 
695 		    _data->cachedBuffer->insert
696 			(k.name(),
697 			 Slice (UINT,
698 				(char *)(new unsigned int[tileRowSize] -
699 					_data->offset),
700 				sizeof (unsigned int),
701 				sizeof (unsigned int) *
702 				    _data->tFile->levelWidth(0),
703 				1, 1,
704 				s.fillValue,
705 				false, true));
706 		    break;
707 
708 		  case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF:
709 
710 		    _data->cachedBuffer->insert
711 			(k.name(),
712 			 Slice (HALF,
713 				(char *)(new half[tileRowSize] -
714 					_data->offset),
715 				sizeof (half),
716 				sizeof (half) *
717 				    _data->tFile->levelWidth(0),
718 				1, 1,
719 				s.fillValue,
720 				false, true));
721 		    break;
722 
723 		  case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT:
724 
725 		    _data->cachedBuffer->insert
726 			(k.name(),
727 			 Slice (OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT,
728 				(char *)(new float[tileRowSize] -
729 					_data->offset),
730 				sizeof(float),
731 				sizeof(float) *
732 				    _data->tFile->levelWidth(0),
733 				1, 1,
734 				s.fillValue,
735 				false, true));
736 		    break;
737 
738 		  default:
739 
740 		    throw IEX_NAMESPACE::ArgExc ("Unknown pixel data type.");
741 		}
742 	    }
743 
744 	    _data->tFile->setFrameBuffer (*_data->cachedBuffer);
745         }
746 
747 	_data->tFileBuffer = frameBuffer;
748     }
749     else if(_data->compositor)
750     {
751         _data->compositor->setFrameBuffer(frameBuffer);
752     }else {
753         _data->sFile->setFrameBuffer(frameBuffer);
754         _data->tFileBuffer = frameBuffer;
755     }
756 }
757 
758 
759 const FrameBuffer &
frameBuffer() const760 InputFile::frameBuffer () const
761 {
762     if(_data->compositor)
763     {
764         return _data->compositor->frameBuffer();
765     }
766     else if(_data->isTiled)
767     {
768 	Lock lock (*_data);
769 	return _data->tFileBuffer;
770     }
771     else
772     {
773 	return _data->sFile->frameBuffer();
774     }
775 }
776 
777 
778 bool
isComplete() const779 InputFile::isComplete () const
780 {
781     if (_data->dsFile)
782         return _data->dsFile->isComplete();
783     else if (_data->isTiled)
784 	return _data->tFile->isComplete();
785     else
786 	return _data->sFile->isComplete();
787 }
788 
789 bool
isOptimizationEnabled() const790 InputFile::isOptimizationEnabled() const
791 {
792    if(_data->sFile)
793    {
794        return _data->sFile->isOptimizationEnabled();
795    }else{
796        return false;
797    }
798 }
799 
800 
801 void
readPixels(int scanLine1,int scanLine2)802 InputFile::readPixels (int scanLine1, int scanLine2)
803 {
804     if (_data->compositor)
805     {
806         _data->compositor->readPixels(scanLine1,scanLine2);
807     }
808     else if (_data->isTiled)
809     {
810 	Lock lock (*_data);
811         bufferedReadPixels (_data, scanLine1, scanLine2);
812     }
813     else
814     {
815         _data->sFile->readPixels (scanLine1, scanLine2);
816     }
817 }
818 
819 
820 void
readPixels(int scanLine)821 InputFile::readPixels (int scanLine)
822 {
823     readPixels (scanLine, scanLine);
824 }
825 
826 
827 void
rawPixelData(int firstScanLine,const char * & pixelData,int & pixelDataSize)828 InputFile::rawPixelData (int firstScanLine,
829 			 const char *&pixelData,
830 			 int &pixelDataSize)
831 {
832     try
833     {
834         if (_data->dsFile)
835         {
836             throw IEX_NAMESPACE::ArgExc ("Tried to read a raw scanline "
837             "from a deep image.");
838         }
839 
840 	else if (_data->isTiled)
841 	{
842 	    throw IEX_NAMESPACE::ArgExc ("Tried to read a raw scanline "
843 			       "from a tiled image.");
844 	}
845 
846         _data->sFile->rawPixelData (firstScanLine, pixelData, pixelDataSize);
847     }
848     catch (IEX_NAMESPACE::BaseExc &e)
849     {
850 	REPLACE_EXC (e, "Error reading pixel data from image "
851 		        "file \"" << fileName() << "\". " << e);
852 	throw;
853     }
854 }
855 
856 
857 void
rawTileData(int & dx,int & dy,int & lx,int & ly,const char * & pixelData,int & pixelDataSize)858 InputFile::rawTileData (int &dx, int &dy,
859 			int &lx, int &ly,
860 			const char *&pixelData,
861 			int &pixelDataSize)
862 {
863     try
864     {
865 	if (!_data->isTiled)
866 	{
867 	    throw IEX_NAMESPACE::ArgExc ("Tried to read a raw tile "
868 			       "from a scanline-based image.");
869 	}
870 
871         _data->tFile->rawTileData (dx, dy, lx, ly, pixelData, pixelDataSize);
872     }
873     catch (IEX_NAMESPACE::BaseExc &e)
874     {
875 	REPLACE_EXC (e, "Error reading tile data from image "
876 		        "file \"" << fileName() << "\". " << e);
877 	throw;
878     }
879 }
880 
881 
882 TiledInputFile*
tFile()883 InputFile::tFile()
884 {
885     if (!_data->isTiled)
886     {
887 	throw IEX_NAMESPACE::ArgExc ("Cannot get a TiledInputFile pointer "
888 			   "from an InputFile that is not tiled.");
889     }
890 
891     return _data->tFile;
892 }
893 
894 
895 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT
896