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