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 <ImfPartType.h>
53 #include "IlmThreadPool.h"
54 #include "IlmThreadSemaphore.h"
55 #include "IlmThreadMutex.h"
56 #include "Iex.h"
57 #include "ImfVersion.h"
58 #include "ImfOptimizedPixelReading.h"
59 #include "ImfNamespace.h"
60 #include "ImfStandardAttributes.h"
61 
62 #include <algorithm>
63 #include <string>
64 #include <vector>
65 #include <assert.h>
66 #include <cstring>
67 
68 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
69 
70 
71 using IMATH_NAMESPACE::Box2i;
72 using IMATH_NAMESPACE::divp;
73 using IMATH_NAMESPACE::modp;
74 using std::string;
75 using std::vector;
76 using std::ifstream;
77 using std::min;
78 using std::max;
79 using std::sort;
80 using ILMTHREAD_NAMESPACE::Mutex;
81 using ILMTHREAD_NAMESPACE::Lock;
82 using ILMTHREAD_NAMESPACE::Semaphore;
83 using ILMTHREAD_NAMESPACE::Task;
84 using ILMTHREAD_NAMESPACE::TaskGroup;
85 using ILMTHREAD_NAMESPACE::ThreadPool;
86 
87 namespace {
88 
89 struct InSliceInfo
90 {
91     PixelType	typeInFrameBuffer;
92     PixelType	typeInFile;
93     char *	base;
94     size_t	xStride;
95     size_t	yStride;
96     int		xSampling;
97     int		ySampling;
98     bool	fill;
99     bool	skip;
100     double	fillValue;
101 
102     InSliceInfo (PixelType typeInFrameBuffer = HALF,
103 		 PixelType typeInFile = HALF,
104 	         char *base = 0,
105 	         size_t xStride = 0,
106 	         size_t yStride = 0,
107 	         int xSampling = 1,
108 	         int ySampling = 1,
109 	         bool fill = false,
110 	         bool skip = false,
111 	         double fillValue = 0.0);
112 };
113 
114 
InSliceInfo(PixelType tifb,PixelType tifl,char * b,size_t xs,size_t ys,int xsm,int ysm,bool f,bool s,double fv)115 InSliceInfo::InSliceInfo (PixelType tifb,
116 			  PixelType tifl,
117 			  char *b,
118 			  size_t xs, size_t ys,
119 			  int xsm, int ysm,
120 			  bool f, bool s,
121 			  double fv)
122 :
123     typeInFrameBuffer (tifb),
124     typeInFile (tifl),
125     base (b),
126     xStride (xs),
127     yStride (ys),
128     xSampling (xsm),
129     ySampling (ysm),
130     fill (f),
131     skip (s),
132     fillValue (fv)
133 {
134     // empty
135 }
136 
137 
138 struct LineBuffer
139 {
140     const char *	uncompressedData;
141     char *		buffer;
142     int			dataSize;
143     int			minY;
144     int			maxY;
145     Compressor *	compressor;
146     Compressor::Format	format;
147     int			number;
148     bool		hasException;
149     string		exception;
150 
151     LineBuffer (Compressor * const comp);
152     ~LineBuffer ();
153 
wait__anon7b89a3790111::LineBuffer154     inline void		wait () {_sem.wait();}
post__anon7b89a3790111::LineBuffer155     inline void		post () {_sem.post();}
156 
157   private:
158 
159     Semaphore		_sem;
160 };
161 
162 
LineBuffer(Compressor * comp)163 LineBuffer::LineBuffer (Compressor *comp):
164     uncompressedData (0),
165     buffer (0),
166     dataSize (0),
167     compressor (comp),
168     format (defaultFormat(compressor)),
169     number (-1),
170     hasException (false),
171     exception (),
172     _sem (1)
173 {
174     // empty
175 }
176 
177 
~LineBuffer()178 LineBuffer::~LineBuffer ()
179 {
180     delete compressor;
181 }
182 
183 /// helper struct used to detect the order that the channels are stored
184 
185 struct sliceOptimizationData
186 {
187     const char * base;   ///< pointer to pixel data
188     bool fill;           ///< is this channel being filled with constant, instead of read?
189     half fillValue;      ///< if filling, the value to use
190     size_t offset;       ///< position this channel will be in the read buffer, accounting for previous channels, as well as their type
191     PixelType type;      ///< type of channel
192     size_t xStride;      ///< x-stride of channel in buffer (must be set to cause channels to interleave)
193     size_t yStride;      ///< y-stride of channel in buffer (must be same in all channels, else order will change, which is bad)
194     int xSampling;       ///< channel x sampling
195     int ySampling;       ///< channel y sampling
196 
197 
198     /// we need to keep the list sorted in the order they'll be written to memory
operator <__anon7b89a3790111::sliceOptimizationData199     bool operator<(const sliceOptimizationData& other ) const
200     {
201         return base < other.base;
202     }
203 };
204 
205 
206 } // namespace
207 
208 
209 struct ScanLineInputFile::Data: public Mutex
210 {
211     Header		header;		    // the image header
212     int			version;            // file's version
213     FrameBuffer		frameBuffer;	    // framebuffer to write into
214     LineOrder		lineOrder;          // order of the scanlines in file
215     int			minX;		    // data window's min x coord
216     int			maxX;		    // data window's max x coord
217     int			minY;		    // data window's min y coord
218     int			maxY;		    // data window's max x coord
219     vector<Int64>	lineOffsets;	    // stores offsets in file for
220 					    // each line
221     bool		fileIsComplete;	    // True if no scanlines are missing
222     					    // in the file
223     int			nextLineBufferMinY; // minimum y of the next linebuffer
224     vector<size_t>	bytesPerLine;       // combined size of a line over all
225                                             // channels
226     vector<size_t>	offsetInLineBuffer; // offset for each scanline in its
227                                             // linebuffer
228     vector<InSliceInfo>	slices;             // info about channels in file
229 
230     vector<LineBuffer*> lineBuffers;        // each holds one line buffer
231     int			linesInBuffer;      // number of scanlines each buffer
232                                             // holds
233     size_t		lineBufferSize;     // size of the line buffer
234     int                 partNumber;         // part number
235 
236     bool                memoryMapped;       // if the stream is memory mapped
237     OptimizationMode    optimizationMode;   // optimizibility of the input file
238     vector<sliceOptimizationData>  optimizationData; ///< channel ordering for optimized reading
239 
240     Data (int numThreads);
241     ~Data ();
242 
243     inline LineBuffer * getLineBuffer (int number); // hash function from line
244     						    // buffer indices into our
245 						    // vector of line buffers
246 
247 
248 };
249 
250 
Data(int numThreads)251 ScanLineInputFile::Data::Data (int numThreads):
252         partNumber(-1),
253         memoryMapped(false)
254 {
255     //
256     // We need at least one lineBuffer, but if threading is used,
257     // to keep n threads busy we need 2*n lineBuffers
258     //
259 
260     lineBuffers.resize (max (1, 2 * numThreads));
261 }
262 
263 
~Data()264 ScanLineInputFile::Data::~Data ()
265 {
266     for (size_t i = 0; i < lineBuffers.size(); i++)
267         delete lineBuffers[i];
268 }
269 
270 
271 inline LineBuffer *
getLineBuffer(int lineBufferNumber)272 ScanLineInputFile::Data::getLineBuffer (int lineBufferNumber)
273 {
274     return lineBuffers[lineBufferNumber % lineBuffers.size()];
275 }
276 
277 
278 namespace {
279 
280 
281 void
reconstructLineOffsets(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream & is,LineOrder lineOrder,vector<Int64> & lineOffsets)282 reconstructLineOffsets (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is,
283 			LineOrder lineOrder,
284 			vector<Int64> &lineOffsets)
285 {
286     Int64 position = is.tellg();
287 
288     try
289     {
290 	for (unsigned int i = 0; i < lineOffsets.size(); i++)
291 	{
292 	    Int64 lineOffset = is.tellg();
293 
294 	    int y;
295 	    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, y);
296 
297 	    int dataSize;
298 	    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, dataSize);
299 
300 	    Xdr::skip <StreamIO> (is, dataSize);
301 
302 	    if (lineOrder == INCREASING_Y)
303 		lineOffsets[i] = lineOffset;
304 	    else
305 		lineOffsets[lineOffsets.size() - i - 1] = lineOffset;
306 	}
307     }
308     catch (...)
309     {
310 	//
311 	// Suppress all exceptions.  This functions is
312 	// called only to reconstruct the line offset
313 	// table for incomplete files, and exceptions
314 	// are likely.
315 	//
316     }
317 
318     is.clear();
319     is.seekg (position);
320 }
321 
322 
323 void
readLineOffsets(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream & is,LineOrder lineOrder,vector<Int64> & lineOffsets,bool & complete)324 readLineOffsets (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is,
325 		 LineOrder lineOrder,
326 		 vector<Int64> &lineOffsets,
327 		 bool &complete)
328 {
329     for (unsigned int i = 0; i < lineOffsets.size(); i++)
330     {
331 	OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, lineOffsets[i]);
332     }
333 
334     complete = true;
335 
336     for (unsigned int i = 0; i < lineOffsets.size(); i++)
337     {
338 	if (lineOffsets[i] <= 0)
339 	{
340 	    //
341 	    // Invalid data in the line offset table mean that
342 	    // the file is probably incomplete (the table is
343 	    // the last thing written to the file).  Either
344 	    // some process is still busy writing the file,
345 	    // or writing the file was aborted.
346 	    //
347 	    // We should still be able to read the existing
348 	    // parts of the file.  In order to do this, we
349 	    // have to make a sequential scan over the scan
350 	    // line data to reconstruct the line offset table.
351 	    //
352 
353 	    complete = false;
354 	    reconstructLineOffsets (is, lineOrder, lineOffsets);
355 	    break;
356 	}
357     }
358 }
359 
360 
361 void
readPixelData(InputStreamMutex * streamData,ScanLineInputFile::Data * ifd,int minY,char * & buffer,int & dataSize)362 readPixelData (InputStreamMutex *streamData,
363                ScanLineInputFile::Data *ifd,
364 	       int minY,
365 	       char *&buffer,
366 	       int &dataSize)
367 {
368     //
369     // Read a single line buffer from the input file.
370     //
371     // If the input file is not memory-mapped, we copy the pixel data into
372     // into the array pointed to by buffer.  If the file is memory-mapped,
373     // then we change where buffer points to instead of writing into the
374     // array (hence buffer needs to be a reference to a char *).
375     //
376 
377     int lineBufferNumber = (minY - ifd->minY) / ifd->linesInBuffer;
378 
379     Int64 lineOffset = ifd->lineOffsets[lineBufferNumber];
380 
381     if (lineOffset == 0)
382 	THROW (IEX_NAMESPACE::InputExc, "Scan line " << minY << " is missing.");
383 
384     //
385     // Seek to the start of the scan line in the file,
386     // if necessary.
387     //
388 
389     if ( !isMultiPart(ifd->version) )
390     {
391         if (ifd->nextLineBufferMinY != minY)
392             streamData->is->seekg (lineOffset);
393     }
394     else
395     {
396         //
397         // In a multi-part file, the file pointer may have been moved by
398         // other parts, so we have to ask tellg() where we are.
399         //
400         if (streamData->is->tellg() != ifd->lineOffsets[lineBufferNumber])
401             streamData->is->seekg (lineOffset);
402     }
403 
404     //
405     // Read the data block's header.
406     //
407 
408     int yInFile;
409 
410     //
411     // Read the part number when we are dealing with a multi-part file.
412     //
413     if (isMultiPart(ifd->version))
414     {
415         int partNumber;
416         OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, partNumber);
417         if (partNumber != ifd->partNumber)
418         {
419             THROW (IEX_NAMESPACE::ArgExc, "Unexpected part number " << partNumber
420                    << ", should be " << ifd->partNumber << ".");
421         }
422     }
423 
424     OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, yInFile);
425     OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, dataSize);
426 
427     if (yInFile != minY)
428         throw IEX_NAMESPACE::InputExc ("Unexpected data block y coordinate.");
429 
430     if (dataSize > (int) ifd->lineBufferSize)
431 	throw IEX_NAMESPACE::InputExc ("Unexpected data block length.");
432 
433     //
434     // Read the pixel data.
435     //
436 
437     if (streamData->is->isMemoryMapped ())
438         buffer = streamData->is->readMemoryMapped (dataSize);
439     else
440         streamData->is->read (buffer, dataSize);
441 
442     //
443     // Keep track of which scan line is the next one in
444     // the file, so that we can avoid redundant seekg()
445     // operations (seekg() can be fairly expensive).
446     //
447 
448     if (ifd->lineOrder == INCREASING_Y)
449         ifd->nextLineBufferMinY = minY + ifd->linesInBuffer;
450     else
451         ifd->nextLineBufferMinY = minY - ifd->linesInBuffer;
452 }
453 
454 
455 
456 //
457 // A LineBufferTask encapsulates the task uncompressing a set of
458 // scanlines (line buffer) and copying them into the frame buffer.
459 //
460 
461 class LineBufferTask : public Task
462 {
463   public:
464 
465     LineBufferTask (TaskGroup *group,
466                     ScanLineInputFile::Data *ifd,
467 		    LineBuffer *lineBuffer,
468                     int scanLineMin,
469 		    int scanLineMax,
470                     OptimizationMode optimizationMode);
471 
472     virtual ~LineBufferTask ();
473 
474     virtual void		execute ();
475 
476   private:
477 
478     ScanLineInputFile::Data *	_ifd;
479     LineBuffer *		_lineBuffer;
480     int				_scanLineMin;
481     int				_scanLineMax;
482     OptimizationMode            _optimizationMode;
483 };
484 
485 
LineBufferTask(TaskGroup * group,ScanLineInputFile::Data * ifd,LineBuffer * lineBuffer,int scanLineMin,int scanLineMax,OptimizationMode optimizationMode)486 LineBufferTask::LineBufferTask
487     (TaskGroup *group,
488      ScanLineInputFile::Data *ifd,
489      LineBuffer *lineBuffer,
490      int scanLineMin,
491      int scanLineMax,OptimizationMode optimizationMode)
492 :
493     Task (group),
494     _ifd (ifd),
495     _lineBuffer (lineBuffer),
496     _scanLineMin (scanLineMin),
497     _scanLineMax (scanLineMax),
498     _optimizationMode(optimizationMode)
499 {
500     // empty
501 }
502 
503 
~LineBufferTask()504 LineBufferTask::~LineBufferTask ()
505 {
506     //
507     // Signal that the line buffer is now free
508     //
509 
510     _lineBuffer->post ();
511 }
512 
513 
514 void
execute()515 LineBufferTask::execute ()
516 {
517     try
518     {
519         //
520         // Uncompress the data, if necessary
521         //
522 
523         if (_lineBuffer->uncompressedData == 0)
524         {
525             int uncompressedSize = 0;
526             int maxY = min (_lineBuffer->maxY, _ifd->maxY);
527 
528             for (int i = _lineBuffer->minY - _ifd->minY;
529                  i <= maxY - _ifd->minY;
530 		 ++i)
531 	    {
532                 uncompressedSize += (int) _ifd->bytesPerLine[i];
533 	    }
534 
535             if (_lineBuffer->compressor &&
536                 _lineBuffer->dataSize < uncompressedSize)
537             {
538                 _lineBuffer->format = _lineBuffer->compressor->format();
539 
540                 _lineBuffer->dataSize = _lineBuffer->compressor->uncompress
541                     (_lineBuffer->buffer,
542                      _lineBuffer->dataSize,
543 		     _lineBuffer->minY,
544                      _lineBuffer->uncompressedData);
545             }
546             else
547             {
548                 //
549                 // If the line is uncompressed, it's in XDR format,
550                 // regardless of the compressor's output format.
551                 //
552 
553                 _lineBuffer->format = Compressor::XDR;
554                 _lineBuffer->uncompressedData = _lineBuffer->buffer;
555             }
556         }
557 
558         int yStart, yStop, dy;
559 
560         if (_ifd->lineOrder == INCREASING_Y)
561         {
562             yStart = _scanLineMin;
563             yStop = _scanLineMax + 1;
564             dy = 1;
565         }
566         else
567         {
568             yStart = _scanLineMax;
569             yStop = _scanLineMin - 1;
570             dy = -1;
571         }
572 
573         for (int y = yStart; y != yStop; y += dy)
574         {
575             //
576             // Convert one scan line's worth of pixel data back
577             // from the machine-independent representation, and
578             // store the result in the frame buffer.
579             //
580 
581             const char *readPtr = _lineBuffer->uncompressedData +
582                                   _ifd->offsetInLineBuffer[y - _ifd->minY];
583 
584             //
585             // Iterate over all image channels.
586             //
587 
588             for (unsigned int i = 0; i < _ifd->slices.size(); ++i)
589             {
590                 //
591                 // Test if scan line y of this channel contains any data
592 		// (the scan line contains data only if y % ySampling == 0).
593                 //
594 
595                 const InSliceInfo &slice = _ifd->slices[i];
596 
597                 if (modp (y, slice.ySampling) != 0)
598                     continue;
599 
600                 //
601                 // Find the x coordinates of the leftmost and rightmost
602                 // sampled pixels (i.e. pixels within the data window
603                 // for which x % xSampling == 0).
604                 //
605 
606                 int dMinX = divp (_ifd->minX, slice.xSampling);
607                 int dMaxX = divp (_ifd->maxX, slice.xSampling);
608 
609                 //
610 		// Fill the frame buffer with pixel data.
611                 //
612 
613                 if (slice.skip)
614                 {
615                     //
616                     // The file contains data for this channel, but
617                     // the frame buffer contains no slice for this channel.
618                     //
619 
620                     skipChannel (readPtr, slice.typeInFile, dMaxX - dMinX + 1);
621                 }
622                 else
623                 {
624                     //
625                     // The frame buffer contains a slice for this channel.
626                     //
627 
628                     char *linePtr  = slice.base +
629                                         divp (y, slice.ySampling) *
630                                         slice.yStride;
631 
632                     char *writePtr = linePtr + dMinX * slice.xStride;
633                     char *endPtr   = linePtr + dMaxX * slice.xStride;
634 
635                     copyIntoFrameBuffer (readPtr, writePtr, endPtr,
636                                          slice.xStride, slice.fill,
637                                          slice.fillValue, _lineBuffer->format,
638                                          slice.typeInFrameBuffer,
639                                          slice.typeInFile);
640                 }
641             }
642         }
643     }
644     catch (std::exception &e)
645     {
646         if (!_lineBuffer->hasException)
647         {
648             _lineBuffer->exception = e.what();
649             _lineBuffer->hasException = true;
650         }
651     }
652     catch (...)
653     {
654         if (!_lineBuffer->hasException)
655         {
656             _lineBuffer->exception = "unrecognized exception";
657             _lineBuffer->hasException = true;
658         }
659     }
660 }
661 
662 
663 #ifdef IMF_HAVE_SSE2
664 //
665 // IIF format is more restricted than a perfectly generic one,
666 // so it is possible to perform some optimizations.
667 //
668 class LineBufferTaskIIF : public Task
669 {
670     public:
671 
672         LineBufferTaskIIF (TaskGroup *group,
673                            ScanLineInputFile::Data *ifd,
674                            LineBuffer *lineBuffer,
675                            int scanLineMin,
676                            int scanLineMax,
677                            OptimizationMode optimizationMode);
678 
679         virtual ~LineBufferTaskIIF ();
680 
681         virtual void                execute ();
682 
683         template<typename TYPE>
684         void getWritePointer (int y,
685                               unsigned short*& pOutWritePointerRight,
686                               size_t& outPixelsToCopySSE,
687                               size_t& outPixelsToCopyNormal,int bank=0) const;
688 
689         template<typename TYPE>
690         void getWritePointerStereo (int y,
691                                     unsigned short*& outWritePointerRight,
692                                     unsigned short*& outWritePointerLeft,
693                                     size_t& outPixelsToCopySSE,
694                                     size_t& outPixelsToCopyNormal) const;
695 
696     private:
697 
698         ScanLineInputFile::Data *   _ifd;
699         LineBuffer *                _lineBuffer;
700         int                         _scanLineMin;
701         int                         _scanLineMax;
702         OptimizationMode            _optimizationMode;
703 
704 };
705 
LineBufferTaskIIF(TaskGroup * group,ScanLineInputFile::Data * ifd,LineBuffer * lineBuffer,int scanLineMin,int scanLineMax,OptimizationMode optimizationMode)706 LineBufferTaskIIF::LineBufferTaskIIF
707     (TaskGroup *group,
708      ScanLineInputFile::Data *ifd,
709      LineBuffer *lineBuffer,
710      int scanLineMin,
711      int scanLineMax,
712      OptimizationMode optimizationMode
713     )
714     :
715      Task (group),
716      _ifd (ifd),
717      _lineBuffer (lineBuffer),
718      _scanLineMin (scanLineMin),
719      _scanLineMax (scanLineMax),
720      _optimizationMode (optimizationMode)
721 {
722      /*
723      //
724      // indicates the optimised path has been taken
725      //
726      static bool could_optimise=false;
727      if(could_optimise==false)
728      {
729          std::cerr << " optimised path\n";
730          could_optimise=true;
731      }
732      */
733 }
734 
~LineBufferTaskIIF()735 LineBufferTaskIIF::~LineBufferTaskIIF ()
736 {
737      //
738      // Signal that the line buffer is now free
739      //
740 
741      _lineBuffer->post ();
742 }
743 
744 // Return 0 if we are to skip because of sampling
745 // channelBank is 0 for the first group of channels, 1 for the second
746 template<typename TYPE>
getWritePointer(int y,unsigned short * & outWritePointerRight,size_t & outPixelsToCopySSE,size_t & outPixelsToCopyNormal,int channelBank) const747 void LineBufferTaskIIF::getWritePointer
748                             (int y,
749                              unsigned short*& outWritePointerRight,
750                              size_t& outPixelsToCopySSE,
751                              size_t& outPixelsToCopyNormal,
752                              int channelBank
753                             ) const
754 {
755       // Channels are saved alphabetically, so the order is B G R.
756       // The last slice (R) will give us the location of our write pointer.
757       // The only slice that we support skipping is alpha, i.e. the first one.
758       // This does not impact the write pointer or the pixels to copy at all.
759 
760       size_t nbSlicesInBank = _ifd->optimizationData.size();
761 
762       int sizeOfSingleValue = sizeof(TYPE);
763 
764       if(_ifd->optimizationData.size()>4)
765       {
766           // there are two banks - we only copy one at once
767           nbSlicesInBank/=2;
768       }
769 
770 
771       size_t firstChannel = 0;
772       if(channelBank==1)
773       {
774           firstChannel = _ifd->optimizationData.size()/2;
775       }
776 
777        sliceOptimizationData& firstSlice = _ifd->optimizationData[firstChannel];
778 
779       if (modp (y, firstSlice.ySampling) != 0)
780       {
781           outPixelsToCopySSE    = 0;
782           outPixelsToCopyNormal = 0;
783           outWritePointerRight  = 0;
784       }
785 
786       const char* linePtr1  = firstSlice.base +
787       divp (y, firstSlice.ySampling) *
788       firstSlice.yStride;
789 
790       int dMinX1 = divp (_ifd->minX, firstSlice.xSampling);
791       int dMaxX1 = divp (_ifd->maxX, firstSlice.xSampling);
792 
793       // Construct the writePtr so that we start writing at
794       // linePtr + Min offset in the line.
795       outWritePointerRight =  (unsigned short*)(linePtr1 +
796       dMinX1 * firstSlice.xStride );
797 
798       size_t bytesToCopy  = ((linePtr1 + dMaxX1 * firstSlice.xStride ) -
799       (linePtr1 + dMinX1 * firstSlice.xStride )) + 2;
800       size_t shortsToCopy = bytesToCopy / sizeOfSingleValue;
801       size_t pixelsToCopy = (shortsToCopy / nbSlicesInBank ) + 1;
802 
803       // We only support writing to SSE if we have no pixels to copy normally
804       outPixelsToCopySSE    = pixelsToCopy / 8;
805       outPixelsToCopyNormal = pixelsToCopy % 8;
806 
807 }
808 
809 
810 template<typename TYPE>
getWritePointerStereo(int y,unsigned short * & outWritePointerRight,unsigned short * & outWritePointerLeft,size_t & outPixelsToCopySSE,size_t & outPixelsToCopyNormal) const811 void LineBufferTaskIIF::getWritePointerStereo
812                           (int y,
813                            unsigned short*& outWritePointerRight,
814                            unsigned short*& outWritePointerLeft,
815                            size_t& outPixelsToCopySSE,
816                            size_t& outPixelsToCopyNormal) const
817 {
818    getWritePointer<TYPE>(y,outWritePointerRight,outPixelsToCopySSE,outPixelsToCopyNormal,0);
819 
820 
821    if(outWritePointerRight)
822    {
823        getWritePointer<TYPE>(y,outWritePointerLeft,outPixelsToCopySSE,outPixelsToCopyNormal,1);
824    }
825 
826 }
827 
828 void
execute()829 LineBufferTaskIIF::execute()
830 {
831     try
832     {
833         //
834         // Uncompress the data, if necessary
835         //
836 
837         if (_lineBuffer->uncompressedData == 0)
838         {
839             int uncompressedSize = 0;
840             int maxY = min (_lineBuffer->maxY, _ifd->maxY);
841 
842             for (int i = _lineBuffer->minY - _ifd->minY;
843             i <= maxY - _ifd->minY;
844             ++i)
845             {
846                 uncompressedSize += (int) _ifd->bytesPerLine[i];
847             }
848 
849             if (_lineBuffer->compressor &&
850                 _lineBuffer->dataSize < uncompressedSize)
851             {
852                 _lineBuffer->format = _lineBuffer->compressor->format();
853 
854                 _lineBuffer->dataSize =
855                 _lineBuffer->compressor->uncompress (_lineBuffer->buffer,
856                                                      _lineBuffer->dataSize,
857                                                      _lineBuffer->minY,
858                                                      _lineBuffer->uncompressedData);
859             }
860             else
861             {
862                 //
863                 // If the line is uncompressed, it's in XDR format,
864                 // regardless of the compressor's output format.
865                 //
866 
867                 _lineBuffer->format = Compressor::XDR;
868                 _lineBuffer->uncompressedData = _lineBuffer->buffer;
869             }
870         }
871 
872         int yStart, yStop, dy;
873 
874         if (_ifd->lineOrder == INCREASING_Y)
875         {
876             yStart = _scanLineMin;
877             yStop = _scanLineMax + 1;
878             dy = 1;
879         }
880         else
881         {
882             yStart = _scanLineMax;
883             yStop = _scanLineMin - 1;
884             dy = -1;
885         }
886 
887         for (int y = yStart; y != yStop; y += dy)
888         {
889             if (modp (y, _optimizationMode._ySampling) != 0)
890                 continue;
891 
892             //
893             // Convert one scan line's worth of pixel data back
894             // from the machine-independent representation, and
895             // store the result in the frame buffer.
896             //
897 
898             // Set the readPtr to read at the start of uncompressedData
899             // but with an offet based on calculated array.
900             // _ifd->offsetInLineBuffer contains offsets based on which
901             // line we are currently processing.
902             // Stride will be taken into consideration later.
903 
904 
905             const char* readPtr = _lineBuffer->uncompressedData +
906             _ifd->offsetInLineBuffer[y - _ifd->minY];
907 
908             size_t pixelsToCopySSE = 0;
909             size_t pixelsToCopyNormal = 0;
910 
911             unsigned short* writePtrLeft = 0;
912             unsigned short* writePtrRight = 0;
913 
914             size_t channels = _ifd->optimizationData.size();
915 
916             if(channels>4)
917             {
918                 getWritePointerStereo<half>(y, writePtrRight, writePtrLeft, pixelsToCopySSE, pixelsToCopyNormal);
919             }
920             else
921             {
922                 getWritePointer<half>(y, writePtrRight, pixelsToCopySSE, pixelsToCopyNormal);
923             }
924 
925             if (writePtrRight == 0 && pixelsToCopySSE == 0 && pixelsToCopyNormal == 0)
926             {
927                 continue;
928             }
929 
930 
931             //
932             // support reading up to eight channels
933             //
934             unsigned short* readPointers[8];
935 
936             for (size_t i = 0; i < channels ; ++i)
937             {
938                 readPointers[i] = (unsigned short*)readPtr + (_ifd->optimizationData[i].offset * (pixelsToCopySSE * 8 + pixelsToCopyNormal));
939             }
940 
941             //RGB only
942             if(channels==3 || channels == 6 )
943             {
944                     optimizedWriteToRGB(readPointers[0], readPointers[1], readPointers[2], writePtrRight, pixelsToCopySSE, pixelsToCopyNormal);
945 
946                     //stereo RGB
947                     if( channels == 6)
948                     {
949                         optimizedWriteToRGB(readPointers[3], readPointers[4], readPointers[5], writePtrLeft, pixelsToCopySSE, pixelsToCopyNormal);
950                     }
951             //RGBA
952             }else if(channels==4 || channels==8)
953             {
954 
955                 if(_ifd->optimizationData[3].fill)
956                 {
957                     optimizedWriteToRGBAFillA(readPointers[0], readPointers[1], readPointers[2], _ifd->optimizationData[3].fillValue.bits() , writePtrRight, pixelsToCopySSE, pixelsToCopyNormal);
958                 }else{
959                     optimizedWriteToRGBA(readPointers[0], readPointers[1], readPointers[2], readPointers[3] , writePtrRight, pixelsToCopySSE, pixelsToCopyNormal);
960                 }
961 
962                 //stereo RGBA
963                 if( channels == 8)
964                 {
965                     if(_ifd->optimizationData[7].fill)
966                     {
967                         optimizedWriteToRGBAFillA(readPointers[4], readPointers[5], readPointers[6], _ifd->optimizationData[7].fillValue.bits() , writePtrLeft, pixelsToCopySSE, pixelsToCopyNormal);
968                     }else{
969                         optimizedWriteToRGBA(readPointers[4], readPointers[5], readPointers[6], readPointers[7] , writePtrLeft, pixelsToCopySSE, pixelsToCopyNormal);
970                     }
971                 }
972             }
973             else {
974                 throw(IEX_NAMESPACE::LogicExc("IIF mode called with incorrect channel pattern"));
975             }
976 
977             // If we are in NO_OPTIMIZATION mode, this class will never
978             // get instantiated, so no need to check for it and duplicate
979             // the code.
980         }
981     }
982     catch (std::exception &e)
983     {
984         if (!_lineBuffer->hasException)
985         {
986             _lineBuffer->exception = e.what();
987             _lineBuffer->hasException = true;
988         }
989     }
990     catch (...)
991     {
992         if (!_lineBuffer->hasException)
993         {
994             _lineBuffer->exception = "unrecognized exception";
995             _lineBuffer->hasException = true;
996         }
997     }
998 }
999 #endif
1000 
1001 
1002 Task *
newLineBufferTask(TaskGroup * group,InputStreamMutex * streamData,ScanLineInputFile::Data * ifd,int number,int scanLineMin,int scanLineMax,OptimizationMode optimizationMode)1003 newLineBufferTask (TaskGroup *group,
1004                    InputStreamMutex *streamData,
1005                    ScanLineInputFile::Data *ifd,
1006                    int number,
1007                    int scanLineMin,
1008                    int scanLineMax,
1009                    OptimizationMode optimizationMode)
1010 {
1011      //
1012      // Wait for a line buffer to become available, fill the line
1013      // buffer with raw data from the file if necessary, and create
1014      // a new LineBufferTask whose execute() method will uncompress
1015      // the contents of the buffer and copy the pixels into the
1016      // frame buffer.
1017      //
1018 
1019      LineBuffer *lineBuffer = ifd->getLineBuffer (number);
1020 
1021      try
1022      {
1023          lineBuffer->wait ();
1024 
1025          if (lineBuffer->number != number)
1026          {
1027              lineBuffer->minY = ifd->minY + number * ifd->linesInBuffer;
1028              lineBuffer->maxY = lineBuffer->minY + ifd->linesInBuffer - 1;
1029 
1030              lineBuffer->number = number;
1031              lineBuffer->uncompressedData = 0;
1032 
1033              readPixelData (streamData, ifd, lineBuffer->minY,
1034                             lineBuffer->buffer,
1035                             lineBuffer->dataSize);
1036          }
1037      }
1038      catch (std::exception &e)
1039      {
1040          if (!lineBuffer->hasException)
1041          {
1042              lineBuffer->exception = e.what();
1043              lineBuffer->hasException = true;
1044          }
1045          lineBuffer->number = -1;
1046          lineBuffer->post();
1047          throw;
1048      }
1049      catch (...)
1050      {
1051          //
1052          // Reading from the file caused an exception.
1053          // Signal that the line buffer is free, and
1054          // re-throw the exception.
1055          //
1056 
1057          lineBuffer->exception = "unrecognized exception";
1058          lineBuffer->hasException = true;
1059          lineBuffer->number = -1;
1060          lineBuffer->post();
1061          throw;
1062      }
1063 
1064      scanLineMin = max (lineBuffer->minY, scanLineMin);
1065      scanLineMax = min (lineBuffer->maxY, scanLineMax);
1066 
1067 
1068      Task* retTask = 0;
1069 
1070 #ifdef IMF_HAVE_SSE2
1071      if (optimizationMode._optimizable)
1072      {
1073 
1074          retTask = new LineBufferTaskIIF (group, ifd, lineBuffer,
1075                                           scanLineMin, scanLineMax,
1076                                           optimizationMode);
1077 
1078      }
1079      else
1080 #endif
1081      {
1082          retTask = new LineBufferTask (group, ifd, lineBuffer,
1083                                        scanLineMin, scanLineMax,
1084                                        optimizationMode);
1085      }
1086 
1087      return retTask;
1088 
1089  }
1090 
1091 
1092 
1093 
1094 } // namespace
1095 
1096 
initialize(const Header & header)1097 void ScanLineInputFile::initialize(const Header& header)
1098 {
1099     try
1100     {
1101         _data->header = header;
1102 
1103         _data->lineOrder = _data->header.lineOrder();
1104 
1105         const Box2i &dataWindow = _data->header.dataWindow();
1106 
1107         _data->minX = dataWindow.min.x;
1108         _data->maxX = dataWindow.max.x;
1109         _data->minY = dataWindow.min.y;
1110         _data->maxY = dataWindow.max.y;
1111 
1112         size_t maxBytesPerLine = bytesPerLineTable (_data->header,
1113                                                     _data->bytesPerLine);
1114 
1115         for (size_t i = 0; i < _data->lineBuffers.size(); i++)
1116         {
1117             _data->lineBuffers[i] = new LineBuffer (newCompressor
1118                                                 (_data->header.compression(),
1119                                                  maxBytesPerLine,
1120                                                  _data->header));
1121         }
1122 
1123         _data->linesInBuffer =
1124             numLinesInBuffer (_data->lineBuffers[0]->compressor);
1125 
1126         _data->lineBufferSize = maxBytesPerLine * _data->linesInBuffer;
1127 
1128         if (!_streamData->is->isMemoryMapped())
1129         {
1130             for (size_t i = 0; i < _data->lineBuffers.size(); i++)
1131             {
1132                 _data->lineBuffers[i]->buffer = (char *) EXRAllocAligned(_data->lineBufferSize*sizeof(char),16);
1133             }
1134         }
1135         _data->nextLineBufferMinY = _data->minY - 1;
1136 
1137         offsetInLineBufferTable (_data->bytesPerLine,
1138                                  _data->linesInBuffer,
1139                                  _data->offsetInLineBuffer);
1140 
1141         int lineOffsetSize = (dataWindow.max.y - dataWindow.min.y +
1142                               _data->linesInBuffer) / _data->linesInBuffer;
1143 
1144         _data->lineOffsets.resize (lineOffsetSize);
1145     }
1146     catch (...)
1147     {
1148         delete _data;
1149         _data=NULL;
1150         throw;
1151     }
1152 }
1153 
1154 
ScanLineInputFile(InputPartData * part)1155 ScanLineInputFile::ScanLineInputFile(InputPartData* part)
1156 {
1157     if (part->header.type() != SCANLINEIMAGE)
1158         throw IEX_NAMESPACE::ArgExc("Can't build a ScanLineInputFile from a type-mismatched part.");
1159 
1160     _data = new Data(part->numThreads);
1161     _streamData = part->mutex;
1162     _data->memoryMapped = _streamData->is->isMemoryMapped();
1163 
1164     _data->version = part->version;
1165 
1166     initialize(part->header);
1167 
1168     _data->lineOffsets = part->chunkOffsets;
1169 
1170     _data->partNumber = part->partNumber;
1171     //
1172     // (TODO) change this code later.
1173     // The completeness of the file should be detected in MultiPartInputFile.
1174     //
1175     _data->fileIsComplete = true;
1176 }
1177 
1178 
ScanLineInputFile(const Header & header,OPENEXR_IMF_INTERNAL_NAMESPACE::IStream * is,int numThreads)1179 ScanLineInputFile::ScanLineInputFile
1180     (const Header &header,
1181      OPENEXR_IMF_INTERNAL_NAMESPACE::IStream *is,
1182      int numThreads)
1183 :
1184     _data (new Data (numThreads)),
1185     _streamData (new InputStreamMutex())
1186 {
1187     _streamData->is = is;
1188     _data->memoryMapped = is->isMemoryMapped();
1189 
1190     initialize(header);
1191 
1192     //
1193     // (TODO) this is nasty - we need a better way of working out what type of file has been used.
1194     // in any case I believe this constructor only gets used with single part files
1195     // and 'version' currently only tracks multipart state, so setting to 0 (not multipart) works for us
1196     //
1197 
1198     _data->version=0;
1199     readLineOffsets (*_streamData->is,
1200                      _data->lineOrder,
1201                      _data->lineOffsets,
1202                      _data->fileIsComplete);
1203 }
1204 
1205 
~ScanLineInputFile()1206 ScanLineInputFile::~ScanLineInputFile ()
1207 {
1208     if (!_data->memoryMapped)
1209     {
1210         for (size_t i = 0; i < _data->lineBuffers.size(); i++)
1211         {
1212             EXRFreeAligned(_data->lineBuffers[i]->buffer);
1213         }
1214     }
1215 
1216 
1217     //
1218     // ScanLineInputFile should never delete the stream,
1219     // because it does not own the stream.
1220     // We just delete the Mutex here.
1221     //
1222     if (_data->partNumber == -1)
1223         delete _streamData;
1224 
1225     delete _data;
1226 }
1227 
1228 
1229 const char *
fileName() const1230 ScanLineInputFile::fileName () const
1231 {
1232     return _streamData->is->fileName();
1233 }
1234 
1235 
1236 const Header &
header() const1237 ScanLineInputFile::header () const
1238 {
1239     return _data->header;
1240 }
1241 
1242 
1243 int
version() const1244 ScanLineInputFile::version () const
1245 {
1246     return _data->version;
1247 }
1248 
1249 
1250 namespace
1251 {
1252 
1253 
1254 // returns the optimization state for the given arrangement of frame bufers
1255 // this assumes:
1256 //   both the file and framebuffer are half float data
1257 //   both the file and framebuffer have xSampling and ySampling=1
1258 //   entries in optData are sorted into their interleave order (i.e. by base address)
1259 //   These tests are done by SetFrameBuffer as it is building optData
1260 //
1261 OptimizationMode
detectOptimizationMode(const vector<sliceOptimizationData> & optData)1262 detectOptimizationMode (const vector<sliceOptimizationData>& optData)
1263 {
1264     OptimizationMode w;
1265 
1266     // need to be compiled with SSE optimisations: if not, just returns false
1267 #if IMF_HAVE_SSE2
1268 
1269 
1270     // only handle reading 3,4,6 or 8 channels
1271     switch(optData.size())
1272     {
1273         case 3 : break;
1274         case 4 : break;
1275         case 6 : break;
1276         case 8 : break;
1277         default :
1278             return w;
1279     }
1280 
1281     //
1282     // the point at which data switches between the primary and secondary bank
1283     //
1284     size_t bankSize = optData.size()>4 ? optData.size()/2 : optData.size();
1285 
1286     for(size_t i=0;i<optData.size();i++)
1287     {
1288         const sliceOptimizationData& data = optData[i];
1289         // can't fill anything other than channel 3 or channel 7
1290         if(data.fill)
1291         {
1292             if(i!=3 && i!=7)
1293             {
1294                 return w;
1295             }
1296         }
1297 
1298         // cannot have gaps in the channel layout, so the stride must be (number of channels written in the bank)*2
1299         if(data.xStride !=bankSize*2)
1300         {
1301             return w;
1302         }
1303 
1304         // each bank of channels must be channel interleaved: each channel base pointer must be (previous channel+2)
1305         // this also means channel sampling pattern must be consistent, as must yStride
1306         if(i!=0 && i!=bankSize)
1307         {
1308             if(data.base!=optData[i-1].base+2)
1309             {
1310                 return w;
1311             }
1312         }
1313         if(i!=0)
1314         {
1315 
1316             if(data.yStride!=optData[i-1].yStride)
1317             {
1318                 return w;
1319             }
1320         }
1321     }
1322 
1323 
1324     w._ySampling=optData[0].ySampling;
1325     w._optimizable=true;
1326 
1327 #endif
1328 
1329     return w;
1330 }
1331 
1332 
1333 } // Anonymous namespace
1334 
1335 void
setFrameBuffer(const FrameBuffer & frameBuffer)1336 ScanLineInputFile::setFrameBuffer (const FrameBuffer &frameBuffer)
1337 {
1338     Lock lock (*_streamData);
1339 
1340 
1341 
1342     const ChannelList &channels = _data->header.channels();
1343     for (FrameBuffer::ConstIterator j = frameBuffer.begin();
1344 	 j != frameBuffer.end();
1345 	 ++j)
1346     {
1347 	ChannelList::ConstIterator i = channels.find (j.name());
1348 
1349 	if (i == channels.end())
1350 	    continue;
1351 
1352 	if (i.channel().xSampling != j.slice().xSampling ||
1353 	    i.channel().ySampling != j.slice().ySampling)
1354 	    THROW (IEX_NAMESPACE::ArgExc, "X and/or y subsampling factors "
1355 				"of \"" << i.name() << "\" channel "
1356 				"of input file \"" << fileName() << "\" are "
1357 				"not compatible with the frame buffer's "
1358 				"subsampling factors.");
1359     }
1360 
1361     // optimization is possible if this is a little endian system
1362     // and both inputs and outputs are half floats
1363     //
1364     bool optimizationPossible = true;
1365 
1366     if (!GLOBAL_SYSTEM_LITTLE_ENDIAN)
1367     {
1368         optimizationPossible =false;
1369     }
1370 
1371     vector<sliceOptimizationData> optData;
1372 
1373 
1374     //
1375     // Initialize the slice table for readPixels().
1376     //
1377 
1378     vector<InSliceInfo> slices;
1379     ChannelList::ConstIterator i = channels.begin();
1380 
1381     // current offset of channel: pixel data starts at offset*width into the
1382     // decompressed scanline buffer
1383     size_t offset = 0;
1384 
1385     for (FrameBuffer::ConstIterator j = frameBuffer.begin();
1386 	 j != frameBuffer.end();
1387 	 ++j)
1388     {
1389 	while (i != channels.end() && strcmp (i.name(), j.name()) < 0)
1390 	{
1391 	    //
1392 	    // Channel i is present in the file but not
1393 	    // in the frame buffer; data for channel i
1394 	    // will be skipped during readPixels().
1395 	    //
1396 
1397 	    slices.push_back (InSliceInfo (i.channel().type,
1398 					   i.channel().type,
1399 					   0, // base
1400 					   0, // xStride
1401 					   0, // yStride
1402 					   i.channel().xSampling,
1403 					   i.channel().ySampling,
1404 					   false,  // fill
1405 					   true, // skip
1406 					   0.0)); // fillValue
1407 
1408               switch(i.channel().type)
1409               {
1410                   case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF :
1411                       offset++;
1412                       break;
1413                   case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT :
1414                       offset+=2;
1415                       break;
1416                   case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT :
1417                       offset+=2;
1418                       break;
1419               }
1420               ++i;
1421 	}
1422 
1423 	bool fill = false;
1424 
1425 	if (i == channels.end() || strcmp (i.name(), j.name()) > 0)
1426 	{
1427 	    //
1428 	    // Channel i is present in the frame buffer, but not in the file.
1429 	    // In the frame buffer, slice j will be filled with a default value.
1430 	    //
1431 
1432 	    fill = true;
1433 	}
1434 
1435 	slices.push_back (InSliceInfo (j.slice().type,
1436 				       fill? j.slice().type:
1437 				             i.channel().type,
1438 				       j.slice().base,
1439 				       j.slice().xStride,
1440 				       j.slice().yStride,
1441 				       j.slice().xSampling,
1442 				       j.slice().ySampling,
1443 				       fill,
1444 				       false, // skip
1445 				       j.slice().fillValue));
1446 
1447           if(!fill && i.channel().type!=OPENEXR_IMF_INTERNAL_NAMESPACE::HALF)
1448           {
1449               optimizationPossible = false;
1450           }
1451 
1452           if(j.slice().type != OPENEXR_IMF_INTERNAL_NAMESPACE::HALF)
1453           {
1454               optimizationPossible = false;
1455           }
1456           if(j.slice().xSampling!=1 || j.slice().ySampling!=1)
1457           {
1458               optimizationPossible = false;
1459           }
1460 
1461 
1462           if(optimizationPossible)
1463           {
1464               sliceOptimizationData dat;
1465               dat.base = j.slice().base;
1466               dat.fill = fill;
1467               dat.fillValue = j.slice().fillValue;
1468               dat.offset = offset;
1469               dat.xStride = j.slice().xStride;
1470               dat.yStride = j.slice().yStride;
1471               dat.xSampling = j.slice().xSampling;
1472               dat.ySampling = j.slice().ySampling;
1473               optData.push_back(dat);
1474           }
1475 
1476           if(!fill)
1477           {
1478               switch(i.channel().type)
1479               {
1480                   case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF :
1481                       offset++;
1482                       break;
1483                   case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT :
1484                       offset+=2;
1485                       break;
1486                   case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT :
1487                       offset+=2;
1488                       break;
1489               }
1490           }
1491 
1492 
1493 
1494 	if (i != channels.end() && !fill)
1495 	    ++i;
1496     }
1497 
1498 
1499    if(optimizationPossible)
1500    {
1501        //
1502        // check optimisibility
1503        // based on channel ordering and fill channel positions
1504        //
1505        sort(optData.begin(),optData.end());
1506        _data->optimizationMode = detectOptimizationMode(optData);
1507    }
1508 
1509    if(!optimizationPossible || _data->optimizationMode._optimizable==false)
1510    {
1511        optData = vector<sliceOptimizationData>();
1512        _data->optimizationMode._optimizable=false;
1513    }
1514 
1515     //
1516     // Store the new frame buffer.
1517     //
1518 
1519     _data->frameBuffer = frameBuffer;
1520     _data->slices = slices;
1521     _data->optimizationData = optData;
1522 }
1523 
1524 
1525 const FrameBuffer &
frameBuffer() const1526 ScanLineInputFile::frameBuffer () const
1527 {
1528     Lock lock (*_streamData);
1529     return _data->frameBuffer;
1530 }
1531 
1532 
1533 bool
isComplete() const1534 ScanLineInputFile::isComplete () const
1535 {
1536     return _data->fileIsComplete;
1537 }
1538 
isOptimizationEnabled() const1539 bool ScanLineInputFile::isOptimizationEnabled() const
1540 {
1541     if (_data->slices.size() == 0)
1542         throw IEX_NAMESPACE::ArgExc ("No frame buffer specified "
1543         "as pixel data destination.");
1544 
1545     return _data->optimizationMode._optimizable;
1546 }
1547 
1548 
1549 void
readPixels(int scanLine1,int scanLine2)1550 ScanLineInputFile::readPixels (int scanLine1, int scanLine2)
1551 {
1552     try
1553     {
1554         Lock lock (*_streamData);
1555 
1556 	if (_data->slices.size() == 0)
1557 	    throw IEX_NAMESPACE::ArgExc ("No frame buffer specified "
1558 			       "as pixel data destination.");
1559 
1560 	int scanLineMin = min (scanLine1, scanLine2);
1561 	int scanLineMax = max (scanLine1, scanLine2);
1562 
1563 	if (scanLineMin < _data->minY || scanLineMax > _data->maxY)
1564 	    throw IEX_NAMESPACE::ArgExc ("Tried to read scan line outside "
1565 			       "the image file's data window.");
1566 
1567         //
1568         // We impose a numbering scheme on the lineBuffers where the first
1569         // scanline is contained in lineBuffer 1.
1570         //
1571         // Determine the first and last lineBuffer numbers in this scanline
1572         // range. We always attempt to read the scanlines in the order that
1573         // they are stored in the file.
1574         //
1575 
1576         int start, stop, dl;
1577 
1578         if (_data->lineOrder == INCREASING_Y)
1579         {
1580             start = (scanLineMin - _data->minY) / _data->linesInBuffer;
1581             stop  = (scanLineMax - _data->minY) / _data->linesInBuffer + 1;
1582             dl = 1;
1583         }
1584         else
1585         {
1586             start = (scanLineMax - _data->minY) / _data->linesInBuffer;
1587             stop  = (scanLineMin - _data->minY) / _data->linesInBuffer - 1;
1588             dl = -1;
1589         }
1590 
1591         //
1592         // Create a task group for all line buffer tasks.  When the
1593 	// task group goes out of scope, the destructor waits until
1594 	// all tasks are complete.
1595         //
1596 
1597         {
1598             TaskGroup taskGroup;
1599 
1600             //
1601             // Add the line buffer tasks.
1602             //
1603             // The tasks will execute in the order that they are created
1604             // because we lock the line buffers during construction and the
1605             // constructors are called by the main thread.  Hence, in order
1606 	    // for a successive task to execute the previous task which
1607 	    // used that line buffer must have completed already.
1608             //
1609 
1610             for (int l = start; l != stop; l += dl)
1611             {
1612                 ThreadPool::addGlobalTask (newLineBufferTask (&taskGroup,
1613                                                               _streamData,
1614                                                               _data, l,
1615                                                               scanLineMin,
1616                                                               scanLineMax,
1617                                                               _data->optimizationMode));
1618             }
1619 
1620 	    //
1621             // finish all tasks
1622 	    //
1623         }
1624 
1625 	//
1626 	// Exeption handling:
1627 	//
1628 	// LineBufferTask::execute() may have encountered exceptions, but
1629 	// those exceptions occurred in another thread, not in the thread
1630 	// that is executing this call to ScanLineInputFile::readPixels().
1631 	// LineBufferTask::execute() has caught all exceptions and stored
1632 	// the exceptions' what() strings in the line buffers.
1633 	// Now we check if any line buffer contains a stored exception; if
1634 	// this is the case then we re-throw the exception in this thread.
1635 	// (It is possible that multiple line buffers contain stored
1636 	// exceptions.  We re-throw the first exception we find and
1637 	// ignore all others.)
1638 	//
1639 
1640 	const string *exception = 0;
1641 
1642         for (size_t i = 0; i < _data->lineBuffers.size(); ++i)
1643 	{
1644             LineBuffer *lineBuffer = _data->lineBuffers[i];
1645 
1646 	    if (lineBuffer->hasException && !exception)
1647 		exception = &lineBuffer->exception;
1648 
1649 	    lineBuffer->hasException = false;
1650 	}
1651 
1652 	if (exception)
1653 	    throw IEX_NAMESPACE::IoExc (*exception);
1654     }
1655     catch (IEX_NAMESPACE::BaseExc &e)
1656     {
1657 	REPLACE_EXC (e, "Error reading pixel data from image "
1658 		        "file \"" << fileName() << "\". " << e);
1659 	throw;
1660     }
1661 }
1662 
1663 
1664 void
readPixels(int scanLine)1665 ScanLineInputFile::readPixels (int scanLine)
1666 {
1667     readPixels (scanLine, scanLine);
1668 }
1669 
1670 
1671 void
rawPixelData(int firstScanLine,const char * & pixelData,int & pixelDataSize)1672 ScanLineInputFile::rawPixelData (int firstScanLine,
1673 				 const char *&pixelData,
1674 				 int &pixelDataSize)
1675 {
1676     try
1677     {
1678         Lock lock (*_streamData);
1679 
1680 	if (firstScanLine < _data->minY || firstScanLine > _data->maxY)
1681 	{
1682 	    throw IEX_NAMESPACE::ArgExc ("Tried to read scan line outside "
1683 			       "the image file's data window.");
1684 	}
1685 
1686         int minY = lineBufferMinY
1687 	    (firstScanLine, _data->minY, _data->linesInBuffer);
1688 
1689 	readPixelData
1690 	    (_streamData, _data, minY, _data->lineBuffers[0]->buffer, pixelDataSize);
1691 
1692 	pixelData = _data->lineBuffers[0]->buffer;
1693     }
1694     catch (IEX_NAMESPACE::BaseExc &e)
1695     {
1696 	REPLACE_EXC (e, "Error reading pixel data from image "
1697 		        "file \"" << fileName() << "\". " << e);
1698 	throw;
1699     }
1700 }
1701 
1702 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT
1703