1
///////////////////////////////////////////////////////////////////////////
3
// Copyright (c) 2004, Industrial Light & Magic, a division of Lucas
6
// All rights reserved.
8
// Redistribution and use in source and binary forms, with or without
9
// modification, are permitted provided that the following conditions are
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
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.
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.
33
///////////////////////////////////////////////////////////////////////////
36
//-----------------------------------------------------------------------------
38
// class ScanLineInputFile
40
//-----------------------------------------------------------------------------
42
#include <ImfScanLineInputFile.h>
43
#include <ImfChannelList.h>
46
#include <ImfCompressor.h>
50
#include <ImfConvert.h>
51
#include <ImfThreading.h>
52
#include "IlmThreadPool.h"
53
#include "IlmThreadSemaphore.h"
54
#include "IlmThreadMutex.h"
59
#include <algorithm> // for std::max()
72
using IlmThread::Mutex;
73
using IlmThread::Lock;
74
using IlmThread::Semaphore;
75
using IlmThread::Task;
76
using IlmThread::TaskGroup;
77
using IlmThread::ThreadPool;
83
PixelType typeInFrameBuffer;
94
InSliceInfo (PixelType typeInFrameBuffer = HALF,
95
PixelType typeInFile = HALF,
103
double fillValue = 0.0);
107
InSliceInfo::InSliceInfo (PixelType tifb,
110
size_t xs, size_t ys,
115
typeInFrameBuffer (tifb),
132
const char * uncompressedData;
137
Compressor * compressor;
138
Compressor::Format format;
143
LineBuffer (Compressor * const comp);
146
inline void wait () {_sem.wait();}
147
inline void post () {_sem.post();}
155
LineBuffer::LineBuffer (Compressor *comp):
156
uncompressedData (0),
160
format (defaultFormat(compressor)),
162
hasException (false),
170
LineBuffer::~LineBuffer ()
178
struct ScanLineInputFile::Data: public Mutex
180
Header header; // the image header
181
int version; // file's version
182
FrameBuffer frameBuffer; // framebuffer to write into
183
LineOrder lineOrder; // order of the scanlines in file
184
int minX; // data window's min x coord
185
int maxX; // data window's max x coord
186
int minY; // data window's min y coord
187
int maxY; // data window's max x coord
188
vector<Int64> lineOffsets; // stores offsets in file for
190
bool fileIsComplete; // True if no scanlines are missing
192
int nextLineBufferMinY; // minimum y of the next linebuffer
193
vector<size_t> bytesPerLine; // combined size of a line over all
195
vector<size_t> offsetInLineBuffer; // offset for each scanline in its
197
vector<InSliceInfo> slices; // info about channels in file
198
IStream * is; // file stream to read from
200
vector<LineBuffer*> lineBuffers; // each holds one line buffer
201
int linesInBuffer; // number of scanlines each buffer
203
size_t lineBufferSize; // size of the line buffer
205
Data (IStream *is, int numThreads);
208
inline LineBuffer * getLineBuffer (int number); // hash function from line
209
// buffer indices into our
210
// vector of line buffers
214
ScanLineInputFile::Data::Data (IStream *is, int numThreads):
218
// We need at least one lineBuffer, but if threading is used,
219
// to keep n threads busy we need 2*n lineBuffers
222
lineBuffers.resize (max (1, 2 * numThreads));
226
ScanLineInputFile::Data::~Data ()
228
for (size_t i = 0; i < lineBuffers.size(); i++)
229
delete lineBuffers[i];
234
ScanLineInputFile::Data::getLineBuffer (int lineBufferNumber)
236
return lineBuffers[lineBufferNumber % lineBuffers.size()];
244
reconstructLineOffsets (IStream &is,
246
vector<Int64> &lineOffsets)
248
Int64 position = is.tellg();
252
for (unsigned int i = 0; i < lineOffsets.size(); i++)
254
Int64 lineOffset = is.tellg();
257
Xdr::read <StreamIO> (is, y);
260
Xdr::read <StreamIO> (is, dataSize);
262
Xdr::skip <StreamIO> (is, dataSize);
264
if (lineOrder == INCREASING_Y)
265
lineOffsets[i] = lineOffset;
267
lineOffsets[lineOffsets.size() - i - 1] = lineOffset;
273
// Suppress all exceptions. This functions is
274
// called only to reconstruct the line offset
275
// table for incomplete files, and exceptions
286
readLineOffsets (IStream &is,
288
vector<Int64> &lineOffsets,
291
for (unsigned int i = 0; i < lineOffsets.size(); i++)
293
Xdr::read <StreamIO> (is, lineOffsets[i]);
298
for (unsigned int i = 0; i < lineOffsets.size(); i++)
300
if (lineOffsets[i] <= 0)
303
// Invalid data in the line offset table mean that
304
// the file is probably incomplete (the table is
305
// the last thing written to the file). Either
306
// some process is still busy writing the file,
307
// or writing the file was aborted.
309
// We should still be able to read the existing
310
// parts of the file. In order to do this, we
311
// have to make a sequential scan over the scan
312
// line data to reconstruct the line offset table.
316
reconstructLineOffsets (is, lineOrder, lineOffsets);
324
readPixelData (ScanLineInputFile::Data *ifd,
330
// Read a single line buffer from the input file.
332
// If the input file is not memory-mapped, we copy the pixel data into
333
// into the array pointed to by buffer. If the file is memory-mapped,
334
// then we change where buffer points to instead of writing into the
335
// array (hence buffer needs to be a reference to a char *).
339
ifd->lineOffsets[(minY - ifd->minY) / ifd->linesInBuffer];
342
THROW (Iex::InputExc, "Scan line " << minY << " is missing.");
345
// Seek to the start of the scan line in the file,
349
if (ifd->nextLineBufferMinY != minY)
350
ifd->is->seekg (lineOffset);
353
// Read the data block's header.
358
Xdr::read <StreamIO> (*ifd->is, yInFile);
359
Xdr::read <StreamIO> (*ifd->is, dataSize);
362
throw Iex::InputExc ("Unexpected data block y coordinate.");
364
if (dataSize > (int) ifd->lineBufferSize)
365
throw Iex::InputExc ("Unexpected data block length.");
368
// Read the pixel data.
371
if (ifd->is->isMemoryMapped ())
372
buffer = ifd->is->readMemoryMapped (dataSize);
374
ifd->is->read (buffer, dataSize);
377
// Keep track of which scan line is the next one in
378
// the file, so that we can avoid redundant seekg()
379
// operations (seekg() can be fairly expensive).
382
if (ifd->lineOrder == INCREASING_Y)
383
ifd->nextLineBufferMinY = minY + ifd->linesInBuffer;
385
ifd->nextLineBufferMinY = minY - ifd->linesInBuffer;
390
// A LineBufferTask encapsulates the task uncompressing a set of
391
// scanlines (line buffer) and copying them into the frame buffer.
394
class LineBufferTask : public Task
398
LineBufferTask (TaskGroup *group,
399
ScanLineInputFile::Data *ifd,
400
LineBuffer *lineBuffer,
404
virtual ~LineBufferTask ();
406
virtual void execute ();
410
ScanLineInputFile::Data * _ifd;
411
LineBuffer * _lineBuffer;
417
LineBufferTask::LineBufferTask
419
ScanLineInputFile::Data *ifd,
420
LineBuffer *lineBuffer,
426
_lineBuffer (lineBuffer),
427
_scanLineMin (scanLineMin),
428
_scanLineMax (scanLineMax)
434
LineBufferTask::~LineBufferTask ()
437
// Signal that the line buffer is now free
440
_lineBuffer->post ();
445
LineBufferTask::execute ()
450
// Uncompress the data, if necessary
453
if (_lineBuffer->uncompressedData == 0)
455
int uncompressedSize = 0;
456
int maxY = min (_lineBuffer->maxY, _ifd->maxY);
458
for (int i = _lineBuffer->minY - _ifd->minY;
459
i <= maxY - _ifd->minY;
462
uncompressedSize += (int) _ifd->bytesPerLine[i];
465
if (_lineBuffer->compressor &&
466
_lineBuffer->dataSize < uncompressedSize)
468
_lineBuffer->format = _lineBuffer->compressor->format();
470
_lineBuffer->dataSize = _lineBuffer->compressor->uncompress
471
(_lineBuffer->buffer, _lineBuffer->dataSize,
472
_lineBuffer->minY, _lineBuffer->uncompressedData);
477
// If the line is uncompressed, it's in XDR format,
478
// regardless of the compressor's output format.
481
_lineBuffer->format = Compressor::XDR;
482
_lineBuffer->uncompressedData = _lineBuffer->buffer;
486
int yStart, yStop, dy;
488
if (_ifd->lineOrder == INCREASING_Y)
490
yStart = _scanLineMin;
491
yStop = _scanLineMax + 1;
496
yStart = _scanLineMax;
497
yStop = _scanLineMin - 1;
501
for (int y = yStart; y != yStop; y += dy)
504
// Convert one scan line's worth of pixel data back
505
// from the machine-independent representation, and
506
// store the result in the frame buffer.
509
const char *readPtr = _lineBuffer->uncompressedData +
510
_ifd->offsetInLineBuffer[y - _ifd->minY];
513
// Iterate over all image channels.
516
for (unsigned int i = 0; i < _ifd->slices.size(); ++i)
519
// Test if scan line y of this channel contains any data
520
// (the scan line contains data only if y % ySampling == 0).
523
const InSliceInfo &slice = _ifd->slices[i];
525
if (modp (y, slice.ySampling) != 0)
529
// Find the x coordinates of the leftmost and rightmost
530
// sampled pixels (i.e. pixels within the data window
531
// for which x % xSampling == 0).
534
int dMinX = divp (_ifd->minX, slice.xSampling);
535
int dMaxX = divp (_ifd->maxX, slice.xSampling);
538
// Fill the frame buffer with pixel data.
544
// The file contains data for this channel, but
545
// the frame buffer contains no slice for this channel.
548
skipChannel (readPtr, slice.typeInFile, dMaxX - dMinX + 1);
553
// The frame buffer contains a slice for this channel.
556
char *linePtr = slice.base +
557
divp (y, slice.ySampling) *
560
char *writePtr = linePtr + dMinX * slice.xStride;
561
char *endPtr = linePtr + dMaxX * slice.xStride;
563
copyIntoFrameBuffer (readPtr, writePtr, endPtr,
564
slice.xStride, slice.fill,
565
slice.fillValue, _lineBuffer->format,
566
slice.typeInFrameBuffer,
572
catch (std::exception &e)
574
if (!_lineBuffer->hasException)
576
_lineBuffer->exception = e.what();
577
_lineBuffer->hasException = true;
582
if (!_lineBuffer->hasException)
584
_lineBuffer->exception = "unrecognized exception";
585
_lineBuffer->hasException = true;
594
ScanLineInputFile::Data *ifd,
600
// Wait for a line buffer to become available, fill the line
601
// buffer with raw data from the file if necessary, and create
602
// a new LineBufferTask whose execute() method will uncompress
603
// the contents of the buffer and copy the pixels into the
607
LineBuffer *lineBuffer = ifd->getLineBuffer (number);
613
if (lineBuffer->number != number)
615
lineBuffer->minY = ifd->minY + number * ifd->linesInBuffer;
616
lineBuffer->maxY = lineBuffer->minY + ifd->linesInBuffer - 1;
618
lineBuffer->number = number;
619
lineBuffer->uncompressedData = 0;
621
readPixelData (ifd, lineBuffer->minY,
623
lineBuffer->dataSize);
626
catch (std::exception &e)
628
if (!lineBuffer->hasException)
630
lineBuffer->exception = e.what();
631
lineBuffer->hasException = true;
633
lineBuffer->number = -1;
640
// Reading from the file caused an exception.
641
// Signal that the line buffer is free, and
642
// re-throw the exception.
645
lineBuffer->exception = "unrecognized exception";
646
lineBuffer->hasException = true;
647
lineBuffer->number = -1;
652
scanLineMin = max (lineBuffer->minY, scanLineMin);
653
scanLineMax = min (lineBuffer->maxY, scanLineMax);
655
return new LineBufferTask (group, ifd, lineBuffer,
656
scanLineMin, scanLineMax);
662
ScanLineInputFile::ScanLineInputFile
663
(const Header &header,
667
_data (new Data (is, numThreads))
671
_data->header = header;
673
_data->lineOrder = _data->header.lineOrder();
675
const Box2i &dataWindow = _data->header.dataWindow();
677
_data->minX = dataWindow.min.x;
678
_data->maxX = dataWindow.max.x;
679
_data->minY = dataWindow.min.y;
680
_data->maxY = dataWindow.max.y;
682
size_t maxBytesPerLine = bytesPerLineTable (_data->header,
683
_data->bytesPerLine);
685
for (size_t i = 0; i < _data->lineBuffers.size(); i++)
687
_data->lineBuffers[i] = new LineBuffer (newCompressor
688
(_data->header.compression(),
693
_data->linesInBuffer =
694
numLinesInBuffer (_data->lineBuffers[0]->compressor);
696
_data->lineBufferSize = maxBytesPerLine * _data->linesInBuffer;
698
if (!_data->is->isMemoryMapped())
699
for (size_t i = 0; i < _data->lineBuffers.size(); i++)
700
_data->lineBuffers[i]->buffer = new char[_data->lineBufferSize];
702
_data->nextLineBufferMinY = _data->minY - 1;
704
offsetInLineBufferTable (_data->bytesPerLine,
705
_data->linesInBuffer,
706
_data->offsetInLineBuffer);
708
int lineOffsetSize = (dataWindow.max.y - dataWindow.min.y +
709
_data->linesInBuffer) / _data->linesInBuffer;
711
_data->lineOffsets.resize (lineOffsetSize);
713
readLineOffsets (*_data->is,
716
_data->fileIsComplete);
726
ScanLineInputFile::~ScanLineInputFile ()
728
if (!_data->is->isMemoryMapped())
729
for (size_t i = 0; i < _data->lineBuffers.size(); i++)
730
delete [] _data->lineBuffers[i]->buffer;
737
ScanLineInputFile::fileName () const
739
return _data->is->fileName();
744
ScanLineInputFile::header () const
746
return _data->header;
751
ScanLineInputFile::version () const
753
return _data->version;
758
ScanLineInputFile::setFrameBuffer (const FrameBuffer &frameBuffer)
763
// Check if the new frame buffer descriptor is
764
// compatible with the image file header.
767
const ChannelList &channels = _data->header.channels();
769
for (FrameBuffer::ConstIterator j = frameBuffer.begin();
770
j != frameBuffer.end();
773
ChannelList::ConstIterator i = channels.find (j.name());
775
if (i == channels.end())
778
if (i.channel().xSampling != j.slice().xSampling ||
779
i.channel().ySampling != j.slice().ySampling)
780
THROW (Iex::ArgExc, "X and/or y subsampling factors "
781
"of \"" << i.name() << "\" channel "
782
"of input file \"" << fileName() << "\" are "
783
"not compatible with the frame buffer's "
784
"subsampling factors.");
788
// Initialize the slice table for readPixels().
791
vector<InSliceInfo> slices;
792
ChannelList::ConstIterator i = channels.begin();
794
for (FrameBuffer::ConstIterator j = frameBuffer.begin();
795
j != frameBuffer.end();
798
while (i != channels.end() && strcmp (i.name(), j.name()) < 0)
801
// Channel i is present in the file but not
802
// in the frame buffer; data for channel i
803
// will be skipped during readPixels().
806
slices.push_back (InSliceInfo (i.channel().type,
811
i.channel().xSampling,
812
i.channel().ySampling,
821
if (i == channels.end() || strcmp (i.name(), j.name()) > 0)
824
// Channel i is present in the frame buffer, but not in the file.
825
// In the frame buffer, slice j will be filled with a default value.
831
slices.push_back (InSliceInfo (j.slice().type,
832
fill? j.slice().type:
841
j.slice().fillValue));
843
if (i != channels.end() && !fill)
848
// Store the new frame buffer.
851
_data->frameBuffer = frameBuffer;
852
_data->slices = slices;
857
ScanLineInputFile::frameBuffer () const
860
return _data->frameBuffer;
865
ScanLineInputFile::isComplete () const
867
return _data->fileIsComplete;
872
ScanLineInputFile::readPixels (int scanLine1, int scanLine2)
878
if (_data->slices.size() == 0)
879
throw Iex::ArgExc ("No frame buffer specified "
880
"as pixel data destination.");
882
int scanLineMin = min (scanLine1, scanLine2);
883
int scanLineMax = max (scanLine1, scanLine2);
885
if (scanLineMin < _data->minY || scanLineMax > _data->maxY)
886
throw Iex::ArgExc ("Tried to read scan line outside "
887
"the image file's data window.");
890
// We impose a numbering scheme on the lineBuffers where the first
891
// scanline is contained in lineBuffer 1.
893
// Determine the first and last lineBuffer numbers in this scanline
894
// range. We always attempt to read the scanlines in the order that
895
// they are stored in the file.
900
if (_data->lineOrder == INCREASING_Y)
902
start = (scanLineMin - _data->minY) / _data->linesInBuffer;
903
stop = (scanLineMax - _data->minY) / _data->linesInBuffer + 1;
908
start = (scanLineMax - _data->minY) / _data->linesInBuffer;
909
stop = (scanLineMin - _data->minY) / _data->linesInBuffer - 1;
914
// Create a task group for all line buffer tasks. When the
915
// task group goes out of scope, the destructor waits until
916
// all tasks are complete.
923
// Add the line buffer tasks.
925
// The tasks will execute in the order that they are created
926
// because we lock the line buffers during construction and the
927
// constructors are called by the main thread. Hence, in order
928
// for a successive task to execute the previous task which
929
// used that line buffer must have completed already.
932
for (int l = start; l != stop; l += dl)
934
ThreadPool::addGlobalTask (newLineBufferTask (&taskGroup,
946
// Exeption handling:
948
// LineBufferTask::execute() may have encountered exceptions, but
949
// those exceptions occurred in another thread, not in the thread
950
// that is executing this call to ScanLineInputFile::readPixels().
951
// LineBufferTask::execute() has caught all exceptions and stored
952
// the exceptions' what() strings in the line buffers.
953
// Now we check if any line buffer contains a stored exception; if
954
// this is the case then we re-throw the exception in this thread.
955
// (It is possible that multiple line buffers contain stored
956
// exceptions. We re-throw the first exception we find and
957
// ignore all others.)
960
const string *exception = 0;
962
for (int i = 0; i < _data->lineBuffers.size(); ++i)
964
LineBuffer *lineBuffer = _data->lineBuffers[i];
966
if (lineBuffer->hasException && !exception)
967
exception = &lineBuffer->exception;
969
lineBuffer->hasException = false;
973
throw Iex::IoExc (*exception);
975
catch (Iex::BaseExc &e)
977
REPLACE_EXC (e, "Error reading pixel data from image "
978
"file \"" << fileName() << "\". " << e);
985
ScanLineInputFile::readPixels (int scanLine)
987
readPixels (scanLine, scanLine);
992
ScanLineInputFile::rawPixelData (int firstScanLine,
993
const char *&pixelData,
1000
if (firstScanLine < _data->minY || firstScanLine > _data->maxY)
1002
throw Iex::ArgExc ("Tried to read scan line outside "
1003
"the image file's data window.");
1006
int minY = lineBufferMinY
1007
(firstScanLine, _data->minY, _data->linesInBuffer);
1010
(_data, minY, _data->lineBuffers[0]->buffer, pixelDataSize);
1012
pixelData = _data->lineBuffers[0]->buffer;
1014
catch (Iex::BaseExc &e)
1016
REPLACE_EXC (e, "Error reading pixel data from image "
1017
"file \"" << fileName() << "\". " << e);