Home | History | Annotate | Download | only in IlmImf
      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 OutputFile
     39 //
     40 //-----------------------------------------------------------------------------
     41 
     42 #include <ImfOutputFile.h>
     43 #include <ImfInputFile.h>
     44 #include <ImfChannelList.h>
     45 #include <ImfMisc.h>
     46 #include <ImfStdIO.h>
     47 #include <ImfCompressor.h>
     48 #include "ImathBox.h"
     49 #include "ImathFun.h"
     50 #include <ImfArray.h>
     51 #include <ImfXdr.h>
     52 #include <ImfPreviewImageAttribute.h>
     53 #include "IlmThreadPool.h"
     54 #include "IlmThreadSemaphore.h"
     55 #include "IlmThreadMutex.h"
     56 #include "Iex.h"
     57 #include <string>
     58 #include <vector>
     59 #include <fstream>
     60 #include <assert.h>
     61 #include <algorithm> // for std::max()
     62 
     63 
     64 namespace Imf {
     65 
     66 using Imath::Box2i;
     67 using Imath::divp;
     68 using Imath::modp;
     69 using std::string;
     70 using std::vector;
     71 using std::ofstream;
     72 using std::min;
     73 using std::max;
     74 using IlmThread::Mutex;
     75 using IlmThread::Lock;
     76 using IlmThread::Semaphore;
     77 using IlmThread::Task;
     78 using IlmThread::TaskGroup;
     79 using IlmThread::ThreadPool;
     80 
     81 namespace {
     82 
     83 
     84 struct OutSliceInfo
     85 {
     86     PixelType		type;
     87     const char *	base;
     88     size_t		xStride;
     89     size_t		yStride;
     90     int			xSampling;
     91     int			ySampling;
     92     bool		zero;
     93 
     94     OutSliceInfo (PixelType type = HALF,
     95               const char *base = 0,
     96               size_t xStride = 0,
     97               size_t yStride = 0,
     98               int xSampling = 1,
     99               int ySampling = 1,
    100               bool zero = false);
    101 };
    102 
    103 
    104 OutSliceInfo::OutSliceInfo (PixelType t,
    105                     const char *b,
    106                     size_t xs, size_t ys,
    107                     int xsm, int ysm,
    108                     bool z)
    109 :
    110     type (t),
    111     base (b),
    112     xStride (xs),
    113     yStride (ys),
    114     xSampling (xsm),
    115     ySampling (ysm),
    116     zero (z)
    117 {
    118     // empty
    119 }
    120 
    121 
    122 struct LineBuffer
    123 {
    124     Array<char>		buffer;
    125     const char *	dataPtr;
    126     int			dataSize;
    127     char *		endOfLineBufferData;
    128     int			minY;
    129     int			maxY;
    130     int			scanLineMin;
    131     int			scanLineMax;
    132     Compressor *	compressor;
    133     bool		partiallyFull;        // has incomplete data
    134     bool		hasException;
    135     string		exception;
    136 
    137     LineBuffer (Compressor *comp);
    138     ~LineBuffer ();
    139 
    140     void		wait () {_sem.wait();}
    141     void		post () {_sem.post();}
    142 
    143   private:
    144 
    145     Semaphore		_sem;
    146 };
    147 
    148 
    149 LineBuffer::LineBuffer (Compressor *comp) :
    150     dataPtr (0),
    151     dataSize (0),
    152     compressor (comp),
    153     partiallyFull (false),
    154     hasException (false),
    155     exception (),
    156     _sem (1)
    157 {
    158     // empty
    159 }
    160 
    161 
    162 LineBuffer::~LineBuffer ()
    163 {
    164     delete compressor;
    165 }
    166 
    167 } // namespace
    168 
    169 
    170 struct OutputFile::Data: public Mutex
    171 {
    172     Header		 header;		// the image header
    173     int			 version;		// file format version
    174     Int64		 previewPosition;       // file position for preview
    175     FrameBuffer		 frameBuffer;           // framebuffer to write into
    176     int			 currentScanLine;       // next scanline to be written
    177     int			 missingScanLines;      // number of lines to write
    178     LineOrder		 lineOrder;		// the file's lineorder
    179     int			 minX;			// data window's min x coord
    180     int			 maxX;			// data window's max x coord
    181     int			 minY;			// data window's min y coord
    182     int			 maxY;			// data window's max x coord
    183     vector<Int64>	 lineOffsets;		// stores offsets in file for
    184                         // each scanline
    185     vector<size_t>	 bytesPerLine;          // combined size of a line over
    186                                                 // all channels
    187     vector<size_t>	 offsetInLineBuffer;    // offset for each scanline in
    188                                                 // its linebuffer
    189     Compressor::Format	 format;                // compressor's data format
    190     vector<OutSliceInfo> slices;		// info about channels in file
    191     OStream *		 os;			// file stream to write to
    192     bool		 deleteStream;
    193     Int64		 lineOffsetsPosition;   // file position for line
    194                                                 // offset table
    195     Int64		 currentPosition;       // current file position
    196 
    197     vector<LineBuffer*>  lineBuffers;           // each holds one line buffer
    198     int			 linesInBuffer;         // number of scanlines each
    199                                                 // buffer holds
    200     size_t		 lineBufferSize;        // size of the line buffer
    201 
    202      Data (bool deleteStream, int numThreads);
    203     ~Data ();
    204 
    205 
    206     inline LineBuffer *	getLineBuffer (int number); // hash function from line
    207                                 // buffer indices into our
    208                             // vector of line buffers
    209 };
    210 
    211 
    212 OutputFile::Data::Data (bool deleteStream, int numThreads):
    213     os (0),
    214     deleteStream (deleteStream),
    215     lineOffsetsPosition (0)
    216 {
    217     //
    218     // We need at least one lineBuffer, but if threading is used,
    219     // to keep n threads busy we need 2*n lineBuffers.
    220     //
    221 
    222     lineBuffers.resize (max (1, 2 * numThreads));
    223 }
    224 
    225 
    226 OutputFile::Data::~Data ()
    227 {
    228     if (deleteStream)
    229     delete os;
    230 
    231     for (size_t i = 0; i < lineBuffers.size(); i++)
    232         delete lineBuffers[i];
    233 }
    234 
    235 
    236 LineBuffer*
    237 OutputFile::Data::getLineBuffer (int number)
    238 {
    239     return lineBuffers[number % lineBuffers.size()];
    240 }
    241 
    242 
    243 namespace {
    244 
    245 
    246 Int64
    247 writeLineOffsets (OStream &os, const vector<Int64> &lineOffsets)
    248 {
    249     Int64 pos = os.tellp();
    250 
    251     if (pos == -1)
    252     Iex::throwErrnoExc ("Cannot determine current file position (%T).");
    253 
    254     for (unsigned int i = 0; i < lineOffsets.size(); i++)
    255     Xdr::write <StreamIO> (os, lineOffsets[i]);
    256 
    257     return pos;
    258 }
    259 
    260 
    261 void
    262 writePixelData (OutputFile::Data *ofd,
    263                 int lineBufferMinY,
    264         const char pixelData[],
    265         int pixelDataSize)
    266 {
    267     //
    268     // Store a block of pixel data in the output file, and try
    269     // to keep track of the current writing position the file
    270     // without calling tellp() (tellp() can be fairly expensive).
    271     //
    272 
    273     Int64 currentPosition = ofd->currentPosition;
    274     ofd->currentPosition = 0;
    275 
    276     if (currentPosition == 0)
    277     currentPosition = ofd->os->tellp();
    278 
    279     ofd->lineOffsets[(ofd->currentScanLine - ofd->minY) / ofd->linesInBuffer] =
    280     currentPosition;
    281 
    282     #ifdef DEBUG
    283 
    284     assert (ofd->os->tellp() == currentPosition);
    285 
    286     #endif
    287 
    288     Xdr::write <StreamIO> (*ofd->os, lineBufferMinY);
    289     Xdr::write <StreamIO> (*ofd->os, pixelDataSize);
    290     ofd->os->write (pixelData, pixelDataSize);
    291 
    292     ofd->currentPosition = currentPosition +
    293                Xdr::size<int>() +
    294                Xdr::size<int>() +
    295                pixelDataSize;
    296 }
    297 
    298 
    299 inline void
    300 writePixelData (OutputFile::Data *ofd, const LineBuffer *lineBuffer)
    301 {
    302     writePixelData (ofd,
    303             lineBuffer->minY,
    304                     lineBuffer->dataPtr,
    305             lineBuffer->dataSize);
    306 }
    307 
    308 
    309 void
    310 convertToXdr (OutputFile::Data *ofd,
    311               Array<char> &lineBuffer,
    312               int lineBufferMinY,
    313               int lineBufferMaxY,
    314               int /*inSize*/)
    315 {
    316     //
    317     // Convert the contents of a lineBuffer from the machine's native
    318     // representation to Xdr format.  This function is called by
    319     // CompressLineBuffer::execute(), below, if the compressor wanted
    320     // its input pixel data in the machine's native format, but then
    321     // failed to compress the data (most compressors will expand rather
    322     // than compress random input data).
    323     //
    324     // Note that this routine assumes that the machine's native
    325     // representation of the pixel data has the same size as the
    326     // Xdr representation.  This makes it possible to convert the
    327     // pixel data in place, without an intermediate temporary buffer.
    328     //
    329 
    330     int startY, endY;		// The first and last scanlines in
    331                     // the file that are in the lineBuffer.
    332     int step;
    333 
    334     if (ofd->lineOrder == INCREASING_Y)
    335     {
    336     startY = max (lineBufferMinY, ofd->minY);
    337     endY = min (lineBufferMaxY, ofd->maxY) + 1;
    338         step = 1;
    339     }
    340     else
    341     {
    342     startY = min (lineBufferMaxY, ofd->maxY);
    343     endY = max (lineBufferMinY, ofd->minY) - 1;
    344         step = -1;
    345     }
    346 
    347     //
    348     // Iterate over all scanlines in the lineBuffer to convert.
    349     //
    350 
    351     for (int y = startY; y != endY; y += step)
    352     {
    353     //
    354         // Set these to point to the start of line y.
    355         // We will write to writePtr from readPtr.
    356     //
    357 
    358         char *writePtr = lineBuffer + ofd->offsetInLineBuffer[y - ofd->minY];
    359         const char *readPtr = writePtr;
    360 
    361     //
    362         // Iterate over all slices in the file.
    363     //
    364 
    365         for (unsigned int i = 0; i < ofd->slices.size(); ++i)
    366         {
    367             //
    368             // Test if scan line y of this channel is
    369             // contains any data (the scan line contains
    370             // data only if y % ySampling == 0).
    371             //
    372 
    373             const OutSliceInfo &slice = ofd->slices[i];
    374 
    375             if (modp (y, slice.ySampling) != 0)
    376                 continue;
    377 
    378             //
    379             // Find the number of sampled pixels, dMaxX-dMinX+1, for
    380         // slice i in scan line y (i.e. pixels within the data window
    381             // for which x % xSampling == 0).
    382             //
    383 
    384             int dMinX = divp (ofd->minX, slice.xSampling);
    385             int dMaxX = divp (ofd->maxX, slice.xSampling);
    386 
    387         //
    388             // Convert the samples in place.
    389         //
    390 
    391             convertInPlace (writePtr, readPtr, slice.type, dMaxX - dMinX + 1);
    392         }
    393     }
    394 }
    395 
    396 
    397 //
    398 // A LineBufferTask encapsulates the task of copying a set of scanlines
    399 // from the user's frame buffer into a LineBuffer object, compressing
    400 // the data if necessary.
    401 //
    402 
    403 class LineBufferTask: public Task
    404 {
    405   public:
    406 
    407     LineBufferTask (TaskGroup *group,
    408                     OutputFile::Data *ofd,
    409             int number,
    410                     int scanLineMin,
    411             int scanLineMax);
    412 
    413     virtual ~LineBufferTask ();
    414 
    415     virtual void	execute ();
    416 
    417   private:
    418 
    419     OutputFile::Data *	_ofd;
    420     LineBuffer *	_lineBuffer;
    421 };
    422 
    423 
    424 LineBufferTask::LineBufferTask
    425     (TaskGroup *group,
    426      OutputFile::Data *ofd,
    427      int number,
    428      int scanLineMin,
    429      int scanLineMax)
    430 :
    431     Task (group),
    432     _ofd (ofd),
    433     _lineBuffer (_ofd->getLineBuffer(number))
    434 {
    435     //
    436     // Wait for the lineBuffer to become available
    437     //
    438 
    439     _lineBuffer->wait ();
    440 
    441     //
    442     // Initialize the lineBuffer data if necessary
    443     //
    444 
    445     if (!_lineBuffer->partiallyFull)
    446     {
    447         _lineBuffer->endOfLineBufferData = _lineBuffer->buffer;
    448 
    449         _lineBuffer->minY = _ofd->minY + number * _ofd->linesInBuffer;
    450 
    451         _lineBuffer->maxY = min (_lineBuffer->minY + _ofd->linesInBuffer - 1,
    452                  _ofd->maxY);
    453 
    454         _lineBuffer->partiallyFull = true;
    455     }
    456 
    457     _lineBuffer->scanLineMin = max (_lineBuffer->minY, scanLineMin);
    458     _lineBuffer->scanLineMax = min (_lineBuffer->maxY, scanLineMax);
    459 }
    460 
    461 
    462 LineBufferTask::~LineBufferTask ()
    463 {
    464     //
    465     // Signal that the line buffer is now free
    466     //
    467 
    468     _lineBuffer->post ();
    469 }
    470 
    471 
    472 void
    473 LineBufferTask::execute ()
    474 {
    475     try
    476     {
    477         //
    478         // First copy the pixel data from the
    479     // frame buffer into the line buffer
    480         //
    481 
    482         int yStart, yStop, dy;
    483 
    484         if (_ofd->lineOrder == INCREASING_Y)
    485         {
    486             yStart = _lineBuffer->scanLineMin;
    487             yStop = _lineBuffer->scanLineMax + 1;
    488             dy = 1;
    489         }
    490         else
    491         {
    492             yStart = _lineBuffer->scanLineMax;
    493             yStop = _lineBuffer->scanLineMin - 1;
    494             dy = -1;
    495         }
    496 
    497     int y;
    498 
    499         for (y = yStart; y != yStop; y += dy)
    500         {
    501             //
    502             // Gather one scan line's worth of pixel data and store
    503             // them in _ofd->lineBuffer.
    504             //
    505 
    506             char *writePtr = _lineBuffer->buffer +
    507                              _ofd->offsetInLineBuffer[y - _ofd->minY];
    508             //
    509             // Iterate over all image channels.
    510             //
    511 
    512             for (unsigned int i = 0; i < _ofd->slices.size(); ++i)
    513             {
    514                 //
    515                 // Test if scan line y of this channel contains any data
    516         // (the scan line contains data only if y % ySampling == 0).
    517                 //
    518 
    519                 const OutSliceInfo &slice = _ofd->slices[i];
    520 
    521                 if (modp (y, slice.ySampling) != 0)
    522                     continue;
    523 
    524                 //
    525                 // Find the x coordinates of the leftmost and rightmost
    526                 // sampled pixels (i.e. pixels within the data window
    527                 // for which x % xSampling == 0).
    528                 //
    529 
    530                 int dMinX = divp (_ofd->minX, slice.xSampling);
    531                 int dMaxX = divp (_ofd->maxX, slice.xSampling);
    532 
    533                 //
    534         // Fill the line buffer with with pixel data.
    535                 //
    536 
    537                 if (slice.zero)
    538                 {
    539                     //
    540                     // The frame buffer contains no data for this channel.
    541                     // Store zeroes in _lineBuffer->buffer.
    542                     //
    543 
    544                     fillChannelWithZeroes (writePtr, _ofd->format, slice.type,
    545                                            dMaxX - dMinX + 1);
    546                 }
    547                 else
    548                 {
    549                     //
    550                     // If necessary, convert the pixel data to Xdr format.
    551             // Then store the pixel data in _ofd->lineBuffer.
    552                     //
    553 
    554                     const char *linePtr = slice.base +
    555                                           divp (y, slice.ySampling) *
    556                                           slice.yStride;
    557 
    558                     const char *readPtr = linePtr + dMinX * slice.xStride;
    559                     const char *endPtr  = linePtr + dMaxX * slice.xStride;
    560 
    561                     copyFromFrameBuffer (writePtr, readPtr, endPtr,
    562                                          slice.xStride, _ofd->format,
    563                                          slice.type);
    564                 }
    565             }
    566 
    567             if (_lineBuffer->endOfLineBufferData < writePtr)
    568                 _lineBuffer->endOfLineBufferData = writePtr;
    569 
    570             #ifdef DEBUG
    571 
    572                 assert (writePtr - (_lineBuffer->buffer +
    573                         _ofd->offsetInLineBuffer[y - _ofd->minY]) ==
    574                         (int) _ofd->bytesPerLine[y - _ofd->minY]);
    575 
    576             #endif
    577 
    578         }
    579 
    580         //
    581         // If the next scanline isn't past the bounds of the lineBuffer
    582         // then we are done, otherwise compress the linebuffer
    583         //
    584 
    585         if (y >= _lineBuffer->minY && y <= _lineBuffer->maxY)
    586             return;
    587 
    588         _lineBuffer->dataPtr = _lineBuffer->buffer;
    589 
    590         _lineBuffer->dataSize = _lineBuffer->endOfLineBufferData -
    591                                 _lineBuffer->buffer;
    592 
    593     //
    594         // Compress the data
    595     //
    596 
    597         Compressor *compressor = _lineBuffer->compressor;
    598 
    599         if (compressor)
    600         {
    601             const char *compPtr;
    602 
    603             int compSize = compressor->compress (_lineBuffer->dataPtr,
    604                                                  _lineBuffer->dataSize,
    605                                                  _lineBuffer->minY, compPtr);
    606 
    607             if (compSize < _lineBuffer->dataSize)
    608             {
    609                 _lineBuffer->dataSize = compSize;
    610                 _lineBuffer->dataPtr = compPtr;
    611             }
    612             else if (_ofd->format == Compressor::NATIVE)
    613             {
    614                 //
    615                 // The data did not shrink during compression, but
    616                 // we cannot write to the file using the machine's
    617                 // native format, so we need to convert the lineBuffer
    618                 // to Xdr.
    619                 //
    620 
    621                 convertToXdr (_ofd, _lineBuffer->buffer, _lineBuffer->minY,
    622                               _lineBuffer->maxY, _lineBuffer->dataSize);
    623             }
    624         }
    625 
    626         _lineBuffer->partiallyFull = false;
    627     }
    628     catch (std::exception &e)
    629     {
    630         if (!_lineBuffer->hasException)
    631         {
    632             _lineBuffer->exception = e.what ();
    633             _lineBuffer->hasException = true;
    634         }
    635     }
    636     catch (...)
    637     {
    638         if (!_lineBuffer->hasException)
    639         {
    640             _lineBuffer->exception = "unrecognized exception";
    641             _lineBuffer->hasException = true;
    642         }
    643     }
    644 }
    645 
    646 } // namespace
    647 
    648 
    649 OutputFile::OutputFile
    650     (const char fileName[],
    651      const Header &header,
    652      int numThreads)
    653 :
    654     _data (new Data (true, numThreads))
    655 {
    656     try
    657     {
    658     header.sanityCheck();
    659     _data->os = new StdOFStream (fileName);
    660     initialize (header);
    661     }
    662     catch (Iex::BaseExc &e)
    663     {
    664     delete _data;
    665 
    666     REPLACE_EXC (e, "Cannot open image file "
    667             "\"" << fileName << "\". " << e);
    668     throw;
    669     }
    670     catch (...)
    671     {
    672     delete _data;
    673         throw;
    674     }
    675 }
    676 
    677 
    678 OutputFile::OutputFile
    679     (OStream &os,
    680      const Header &header,
    681      int numThreads)
    682 :
    683     _data (new Data (false, numThreads))
    684 {
    685     try
    686     {
    687     header.sanityCheck();
    688     _data->os = &os;
    689     initialize (header);
    690     }
    691     catch (Iex::BaseExc &e)
    692     {
    693     delete _data;
    694 
    695     REPLACE_EXC (e, "Cannot open image file "
    696             "\"" << os.fileName() << "\". " << e);
    697     throw;
    698     }
    699     catch (...)
    700     {
    701     delete _data;
    702         throw;
    703     }
    704 }
    705 
    706 
    707 void
    708 OutputFile::initialize (const Header &header)
    709 {
    710     _data->header = header;
    711 
    712     const Box2i &dataWindow = header.dataWindow();
    713 
    714     _data->currentScanLine = (header.lineOrder() == INCREASING_Y)?
    715                  dataWindow.min.y: dataWindow.max.y;
    716 
    717     _data->missingScanLines = dataWindow.max.y - dataWindow.min.y + 1;
    718     _data->lineOrder = header.lineOrder();
    719     _data->minX = dataWindow.min.x;
    720     _data->maxX = dataWindow.max.x;
    721     _data->minY = dataWindow.min.y;
    722     _data->maxY = dataWindow.max.y;
    723 
    724     size_t maxBytesPerLine = bytesPerLineTable (_data->header,
    725                         _data->bytesPerLine);
    726 
    727     for (size_t i = 0; i < _data->lineBuffers.size(); ++i)
    728     {
    729         _data->lineBuffers[i] =
    730         new LineBuffer (newCompressor (_data->header.compression(),
    731                        maxBytesPerLine,
    732                        _data->header));
    733     }
    734 
    735     LineBuffer *lineBuffer = _data->lineBuffers[0];
    736     _data->format = defaultFormat (lineBuffer->compressor);
    737     _data->linesInBuffer = numLinesInBuffer (lineBuffer->compressor);
    738     _data->lineBufferSize = maxBytesPerLine * _data->linesInBuffer;
    739 
    740     for (size_t i = 0; i < _data->lineBuffers.size(); i++)
    741         _data->lineBuffers[i]->buffer.resizeErase(_data->lineBufferSize);
    742 
    743     int lineOffsetSize = (dataWindow.max.y - dataWindow.min.y +
    744               _data->linesInBuffer) / _data->linesInBuffer;
    745 
    746     _data->lineOffsets.resize (lineOffsetSize);
    747 
    748     offsetInLineBufferTable (_data->bytesPerLine,
    749                  _data->linesInBuffer,
    750                  _data->offsetInLineBuffer);
    751 
    752     _data->previewPosition =
    753     _data->header.writeTo (*_data->os);
    754 
    755     _data->lineOffsetsPosition =
    756     writeLineOffsets (*_data->os, _data->lineOffsets);
    757 
    758     _data->currentPosition = _data->os->tellp();
    759 }
    760 
    761 
    762 OutputFile::~OutputFile ()
    763 {
    764     if (_data)
    765     {
    766         {
    767             if (_data->lineOffsetsPosition > 0)
    768             {
    769                 try
    770                 {
    771                     _data->os->seekp (_data->lineOffsetsPosition);
    772                     writeLineOffsets (*_data->os, _data->lineOffsets);
    773                 }
    774                 catch (...)
    775                 {
    776                     //
    777                     // We cannot safely throw any exceptions from here.
    778                     // This destructor may have been called because the
    779                     // stack is currently being unwound for another
    780                     // exception.
    781                     //
    782                 }
    783             }
    784         }
    785 
    786     delete _data;
    787     }
    788 }
    789 
    790 
    791 const char *
    792 OutputFile::fileName () const
    793 {
    794     return _data->os->fileName();
    795 }
    796 
    797 
    798 const Header &
    799 OutputFile::header () const
    800 {
    801     return _data->header;
    802 }
    803 
    804 
    805 void
    806 OutputFile::setFrameBuffer (const FrameBuffer &frameBuffer)
    807 {
    808     Lock lock (*_data);
    809 
    810     //
    811     // Check if the new frame buffer descriptor
    812     // is compatible with the image file header.
    813     //
    814 
    815     const ChannelList &channels = _data->header.channels();
    816 
    817     for (ChannelList::ConstIterator i = channels.begin();
    818      i != channels.end();
    819      ++i)
    820     {
    821     FrameBuffer::ConstIterator j = frameBuffer.find (i.name());
    822 
    823     if (j == frameBuffer.end())
    824         continue;
    825 
    826     if (i.channel().type != j.slice().type)
    827     {
    828         THROW (Iex::ArgExc, "Pixel type of \"" << i.name() << "\" channel "
    829                     "of output file \"" << fileName() << "\" is "
    830                     "not compatible with the frame buffer's "
    831                     "pixel type.");
    832     }
    833 
    834     if (i.channel().xSampling != j.slice().xSampling ||
    835         i.channel().ySampling != j.slice().ySampling)
    836     {
    837         THROW (Iex::ArgExc, "X and/or y subsampling factors "
    838                 "of \"" << i.name() << "\" channel "
    839                 "of output file \"" << fileName() << "\" are "
    840                 "not compatible with the frame buffer's "
    841                 "subsampling factors.");
    842     }
    843     }
    844 
    845     //
    846     // Initialize slice table for writePixels().
    847     //
    848 
    849     vector<OutSliceInfo> slices;
    850 
    851     for (ChannelList::ConstIterator i = channels.begin();
    852      i != channels.end();
    853      ++i)
    854     {
    855     FrameBuffer::ConstIterator j = frameBuffer.find (i.name());
    856 
    857     if (j == frameBuffer.end())
    858     {
    859         //
    860         // Channel i is not present in the frame buffer.
    861         // In the file, channel i will contain only zeroes.
    862         //
    863 
    864         slices.push_back (OutSliceInfo (i.channel().type,
    865                         0, // base
    866                         0, // xStride,
    867                         0, // yStride,
    868                         i.channel().xSampling,
    869                         i.channel().ySampling,
    870                         true)); // zero
    871     }
    872     else
    873     {
    874         //
    875         // Channel i is present in the frame buffer.
    876         //
    877 
    878         slices.push_back (OutSliceInfo (j.slice().type,
    879                         j.slice().base,
    880                         j.slice().xStride,
    881                         j.slice().yStride,
    882                         j.slice().xSampling,
    883                         j.slice().ySampling,
    884                         false)); // zero
    885     }
    886     }
    887 
    888     //
    889     // Store the new frame buffer.
    890     //
    891 
    892     _data->frameBuffer = frameBuffer;
    893     _data->slices = slices;
    894 }
    895 
    896 
    897 const FrameBuffer &
    898 OutputFile::frameBuffer () const
    899 {
    900     Lock lock (*_data);
    901     return _data->frameBuffer;
    902 }
    903 
    904 
    905 void
    906 OutputFile::writePixels (int numScanLines)
    907 {
    908     try
    909     {
    910         Lock lock (*_data);
    911 
    912     if (_data->slices.size() == 0)
    913         throw Iex::ArgExc ("No frame buffer specified "
    914                    "as pixel data source.");
    915 
    916         //
    917         // Maintain two iterators:
    918         //     nextWriteBuffer: next linebuffer to be written to the file
    919         //     nextCompressBuffer: next linebuffer to compress
    920         //
    921 
    922         int first = (_data->currentScanLine - _data->minY) /
    923                          _data->linesInBuffer;
    924 
    925         int nextWriteBuffer = first;
    926         int nextCompressBuffer;
    927         int stop;
    928         int step;
    929         int scanLineMin;
    930         int scanLineMax;
    931 
    932         {
    933             //
    934             // Create a task group for all line buffer tasks. When the
    935             // taskgroup goes out of scope, the destructor waits until
    936         // all tasks are complete.
    937             //
    938 
    939             TaskGroup taskGroup;
    940 
    941             //
    942             // Determine the range of lineBuffers that intersect the scan
    943         // line range.  Then add the initial compression tasks to the
    944         // thread pool.  We always add in at least one task but the
    945         // individual task might not do anything if numScanLines == 0.
    946             //
    947 
    948             if (_data->lineOrder == INCREASING_Y)
    949             {
    950                 int last = (_data->currentScanLine + (numScanLines - 1) -
    951                             _data->minY) / _data->linesInBuffer;
    952 
    953                 scanLineMin = _data->currentScanLine;
    954                 scanLineMax = _data->currentScanLine + numScanLines - 1;
    955 
    956                 int numTasks = max (min ((int)_data->lineBuffers.size(),
    957                                          last - first + 1),
    958                     1);
    959 
    960                 for (int i = 0; i < numTasks; i++)
    961         {
    962                     ThreadPool::addGlobalTask
    963                         (new LineBufferTask (&taskGroup, _data, first + i,
    964                                              scanLineMin, scanLineMax));
    965         }
    966 
    967                 nextCompressBuffer = first + numTasks;
    968                 stop = last + 1;
    969                 step = 1;
    970             }
    971             else
    972             {
    973                 int last = (_data->currentScanLine - (numScanLines - 1) -
    974                             _data->minY) / _data->linesInBuffer;
    975 
    976                 scanLineMax = _data->currentScanLine;
    977                 scanLineMin = _data->currentScanLine - numScanLines + 1;
    978 
    979                 int numTasks = max (min ((int)_data->lineBuffers.size(),
    980                                          first - last + 1),
    981                     1);
    982 
    983                 for (int i = 0; i < numTasks; i++)
    984         {
    985                     ThreadPool::addGlobalTask
    986                         (new LineBufferTask (&taskGroup, _data, first - i,
    987                                              scanLineMin, scanLineMax));
    988         }
    989 
    990                 nextCompressBuffer = first - numTasks;
    991                 stop = last - 1;
    992                 step = -1;
    993             }
    994 
    995             while (true)
    996             {
    997                 if (_data->missingScanLines <= 0)
    998                 {
    999                     throw Iex::ArgExc ("Tried to write more scan lines "
   1000                                        "than specified by the data window.");
   1001                 }
   1002 
   1003         //
   1004                 // Wait until the next line buffer is ready to be written
   1005         //
   1006 
   1007                 LineBuffer *writeBuffer =
   1008             _data->getLineBuffer (nextWriteBuffer);
   1009 
   1010                 writeBuffer->wait();
   1011 
   1012                 int numLines = writeBuffer->scanLineMax -
   1013                                writeBuffer->scanLineMin + 1;
   1014 
   1015                 _data->missingScanLines -= numLines;
   1016 
   1017         //
   1018                 // If the line buffer is only partially full, then it is
   1019         // not complete and we cannot write it to disk yet.
   1020         //
   1021 
   1022                 if (writeBuffer->partiallyFull)
   1023                 {
   1024                     _data->currentScanLine = _data->currentScanLine +
   1025                                              step * numLines;
   1026                     writeBuffer->post();
   1027 
   1028                     return;
   1029                 }
   1030 
   1031         //
   1032                 // Write the line buffer
   1033         //
   1034 
   1035                 writePixelData (_data, writeBuffer);
   1036                 nextWriteBuffer += step;
   1037 
   1038                 _data->currentScanLine = _data->currentScanLine +
   1039                                          step * numLines;
   1040 
   1041                 #ifdef DEBUG
   1042 
   1043                     assert (_data->currentScanLine ==
   1044                             ((_data->lineOrder == INCREASING_Y) ?
   1045                              writeBuffer->scanLineMax + 1:
   1046                              writeBuffer->scanLineMin - 1));
   1047 
   1048                 #endif
   1049 
   1050         //
   1051                 // Release the lock on the line buffer
   1052         //
   1053 
   1054                 writeBuffer->post();
   1055 
   1056         //
   1057                 // If this was the last line buffer in the scanline range
   1058         //
   1059 
   1060                 if (nextWriteBuffer == stop)
   1061                     break;
   1062 
   1063         //
   1064                 // If there are no more line buffers to compress,
   1065                 // then only continue to write out remaining lineBuffers
   1066         //
   1067 
   1068                 if (nextCompressBuffer == stop)
   1069                     continue;
   1070 
   1071         //
   1072                 // Add nextCompressBuffer as a compression task
   1073         //
   1074 
   1075                 ThreadPool::addGlobalTask
   1076                     (new LineBufferTask (&taskGroup, _data, nextCompressBuffer,
   1077                                          scanLineMin, scanLineMax));
   1078 
   1079         //
   1080                 // Update the next line buffer we need to compress
   1081         //
   1082 
   1083                 nextCompressBuffer += step;
   1084             }
   1085 
   1086         //
   1087             // Finish all tasks
   1088         //
   1089         }
   1090 
   1091     //
   1092     // Exeption handling:
   1093     //
   1094     // LineBufferTask::execute() may have encountered exceptions, but
   1095     // those exceptions occurred in another thread, not in the thread
   1096     // that is executing this call to OutputFile::writePixels().
   1097     // LineBufferTask::execute() has caught all exceptions and stored
   1098     // the exceptions' what() strings in the line buffers.
   1099     // Now we check if any line buffer contains a stored exception; if
   1100     // this is the case then we re-throw the exception in this thread.
   1101     // (It is possible that multiple line buffers contain stored
   1102     // exceptions.  We re-throw the first exception we find and
   1103     // ignore all others.)
   1104     //
   1105 
   1106     const string *exception = 0;
   1107 
   1108         for (int i = 0; i < _data->lineBuffers.size(); ++i)
   1109     {
   1110             LineBuffer *lineBuffer = _data->lineBuffers[i];
   1111 
   1112         if (lineBuffer->hasException && !exception)
   1113         exception = &lineBuffer->exception;
   1114 
   1115         lineBuffer->hasException = false;
   1116     }
   1117 
   1118     if (exception)
   1119         throw Iex::IoExc (*exception);
   1120     }
   1121     catch (Iex::BaseExc &e)
   1122     {
   1123     REPLACE_EXC (e, "Failed to write pixel data to image "
   1124                 "file \"" << fileName() << "\". " << e);
   1125     throw;
   1126     }
   1127 }
   1128 
   1129 
   1130 int
   1131 OutputFile::currentScanLine () const
   1132 {
   1133     Lock lock (*_data);
   1134     return _data->currentScanLine;
   1135 }
   1136 
   1137 
   1138 void
   1139 OutputFile::copyPixels (InputFile &in)
   1140 {
   1141     Lock lock (*_data);
   1142 
   1143     //
   1144     // Check if this file's and and the InputFile's
   1145     // headers are compatible.
   1146     //
   1147 
   1148     const Header &hdr = _data->header;
   1149     const Header &inHdr = in.header();
   1150 
   1151     if (inHdr.find("tiles") != inHdr.end())
   1152     THROW (Iex::ArgExc, "Cannot copy pixels from image "
   1153                 "file \"" << in.fileName() << "\" to image "
   1154                 "file \"" << fileName() << "\". "
   1155                             "The input file is tiled, but the output file is "
   1156                             "not. Try using TiledOutputFile::copyPixels "
   1157                             "instead.");
   1158 
   1159     if (!(hdr.dataWindow() == inHdr.dataWindow()))
   1160     THROW (Iex::ArgExc, "Cannot copy pixels from image "
   1161                 "file \"" << in.fileName() << "\" to image "
   1162                 "file \"" << fileName() << "\". "
   1163                             "The files have different data windows.");
   1164 
   1165     if (!(hdr.lineOrder() == inHdr.lineOrder()))
   1166     THROW (Iex::ArgExc, "Quick pixel copy from image "
   1167                 "file \"" << in.fileName() << "\" to image "
   1168                 "file \"" << fileName() << "\" failed. "
   1169                 "The files have different line orders.");
   1170 
   1171     if (!(hdr.compression() == inHdr.compression()))
   1172     THROW (Iex::ArgExc, "Quick pixel copy from image "
   1173                 "file \"" << in.fileName() << "\" to image "
   1174                 "file \"" << fileName() << "\" failed. "
   1175                 "The files use different compression methods.");
   1176 
   1177     if (!(hdr.channels() == inHdr.channels()))
   1178     THROW (Iex::ArgExc, "Quick pixel copy from image "
   1179                 "file \"" << in.fileName() << "\" to image "
   1180                 "file \"" << fileName() << "\" failed.  "
   1181                 "The files have different channel lists.");
   1182 
   1183     //
   1184     // Verify that no pixel data have been written to this file yet.
   1185     //
   1186 
   1187     const Box2i &dataWindow = hdr.dataWindow();
   1188 
   1189     if (_data->missingScanLines != dataWindow.max.y - dataWindow.min.y + 1)
   1190     THROW (Iex::LogicExc, "Quick pixel copy from image "
   1191                   "file \"" << in.fileName() << "\" to image "
   1192                   "file \"" << fileName() << "\" failed. "
   1193                   "\"" << fileName() << "\" already contains "
   1194                   "pixel data.");
   1195 
   1196     //
   1197     // Copy the pixel data.
   1198     //
   1199 
   1200     while (_data->missingScanLines > 0)
   1201     {
   1202     const char *pixelData;
   1203     int pixelDataSize;
   1204 
   1205     in.rawPixelData (_data->currentScanLine, pixelData, pixelDataSize);
   1206 
   1207     writePixelData (_data, lineBufferMinY (_data->currentScanLine,
   1208                                _data->minY,
   1209                                _data->linesInBuffer),
   1210                         pixelData, pixelDataSize);
   1211 
   1212     _data->currentScanLine += (_data->lineOrder == INCREASING_Y)?
   1213                    _data->linesInBuffer: -_data->linesInBuffer;
   1214 
   1215     _data->missingScanLines -= _data->linesInBuffer;
   1216     }
   1217 }
   1218 
   1219 
   1220 void
   1221 OutputFile::updatePreviewImage (const PreviewRgba newPixels[])
   1222 {
   1223     Lock lock (*_data);
   1224 
   1225     if (_data->previewPosition <= 0)
   1226     THROW (Iex::LogicExc, "Cannot update preview image pixels. "
   1227                   "File \"" << fileName() << "\" does not "
   1228                   "contain a preview image.");
   1229 
   1230     //
   1231     // Store the new pixels in the header's preview image attribute.
   1232     //
   1233 
   1234     PreviewImageAttribute &pia =
   1235     _data->header.typedAttribute <PreviewImageAttribute> ("preview");
   1236 
   1237     PreviewImage &pi = pia.value();
   1238     PreviewRgba *pixels = pi.pixels();
   1239     int numPixels = pi.width() * pi.height();
   1240 
   1241     for (int i = 0; i < numPixels; ++i)
   1242     pixels[i] = newPixels[i];
   1243 
   1244     //
   1245     // Save the current file position, jump to the position in
   1246     // the file where the preview image starts, store the new
   1247     // preview image, and jump back to the saved file position.
   1248     //
   1249 
   1250     Int64 savedPosition = _data->os->tellp();
   1251 
   1252     try
   1253     {
   1254     _data->os->seekp (_data->previewPosition);
   1255     pia.writeValueTo (*_data->os, _data->version);
   1256     _data->os->seekp (savedPosition);
   1257     }
   1258     catch (Iex::BaseExc &e)
   1259     {
   1260     REPLACE_EXC (e, "Cannot update preview image pixels for "
   1261             "file \"" << fileName() << "\". " << e);
   1262     throw;
   1263     }
   1264 }
   1265 
   1266 
   1267 void
   1268 OutputFile::breakScanLine  (int y, int offset, int length, char c)
   1269 {
   1270     Lock lock (*_data);
   1271 
   1272     Int64 position =
   1273     _data->lineOffsets[(y - _data->minY) / _data->linesInBuffer];
   1274 
   1275     if (!position)
   1276     THROW (Iex::ArgExc, "Cannot overwrite scan line " << y << ". "
   1277                 "The scan line has not yet been stored in "
   1278                 "file \"" << fileName() << "\".");
   1279 
   1280     _data->currentPosition = 0;
   1281     _data->os->seekp (position + offset);
   1282 
   1283     for (int i = 0; i < length; ++i)
   1284     _data->os->write (&c, 1);
   1285 }
   1286 
   1287 
   1288 } // namespace Imf
   1289