1 ///////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2011, 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 DeepScanLineOutputFile
39 //
40 //-----------------------------------------------------------------------------
41 
42 #include <ImfDeepScanLineOutputFile.h>
43 #include <ImfDeepScanLineInputFile.h>
44 #include <ImfDeepScanLineInputPart.h>
45 #include <ImfChannelList.h>
46 #include <ImfMisc.h>
47 #include <ImfStdIO.h>
48 #include <ImfCompressor.h>
49 #include "ImathBox.h"
50 #include "ImathFun.h"
51 #include <ImfArray.h>
52 #include <ImfXdr.h>
53 #include <ImfPreviewImageAttribute.h>
54 #include <ImfPartType.h>
55 #include "ImfDeepFrameBuffer.h"
56 #include "ImfOutputStreamMutex.h"
57 #include "ImfOutputPartData.h"
58 
59 #include "IlmThreadPool.h"
60 #include "IlmThreadSemaphore.h"
61 #include "IlmThreadMutex.h"
62 #include "Iex.h"
63 #include <string>
64 #include <vector>
65 #include <fstream>
66 #include <assert.h>
67 #include <algorithm>
68 
69 #include "ImfNamespace.h"
70 
71 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
72 
73 using IMATH_NAMESPACE::Box2i;
74 using IMATH_NAMESPACE::divp;
75 using IMATH_NAMESPACE::modp;
76 using std::string;
77 using std::vector;
78 using std::ofstream;
79 using std::min;
80 using std::max;
81 using ILMTHREAD_NAMESPACE::Mutex;
82 using ILMTHREAD_NAMESPACE::Lock;
83 using ILMTHREAD_NAMESPACE::Semaphore;
84 using ILMTHREAD_NAMESPACE::Task;
85 using ILMTHREAD_NAMESPACE::TaskGroup;
86 using ILMTHREAD_NAMESPACE::ThreadPool;
87 
88 namespace {
89 
90 
91 struct OutSliceInfo
92 {
93     PixelType                    type;
94     const char *                 base;
95     ptrdiff_t                    sampleStride;
96     ptrdiff_t                    xStride;
97     ptrdiff_t                    yStride;
98     int                          xSampling;
99     int                          ySampling;
100     bool                         zero;
101 
102     OutSliceInfo (PixelType type = HALF,
103                   const char * base =NULL,
104                   ptrdiff_t sampleStride = 0,
105                   ptrdiff_t xStride = 0,
106                   ptrdiff_t yStride =0,
107                   int xSampling = 1,
108                   int ySampling = 1,
109                   bool zero = false);
110 };
111 
112 
OutSliceInfo(PixelType t,const char * base,ptrdiff_t spstride,ptrdiff_t xst,ptrdiff_t yst,int xsm,int ysm,bool z)113 OutSliceInfo::OutSliceInfo (PixelType t,
114                             const char * base,
115                             ptrdiff_t spstride,
116                             ptrdiff_t xst,
117                             ptrdiff_t yst,
118                             int xsm, int ysm,
119                             bool z)
120 :
121     type (t),
122     base (base),
123     sampleStride (spstride),
124     xStride(xst),
125     yStride(yst),
126     xSampling (xsm),
127     ySampling (ysm),
128     zero (z)
129 {
130     // empty
131 }
132 
133 
134 struct LineBuffer
135 {
136     Array< Array<char> >  buffer;
137     Array<char>           consecutiveBuffer;
138     const char *          dataPtr;
139     Int64                 uncompressedDataSize;
140     Int64                 dataSize;
141     Array<char>           sampleCountTableBuffer;
142     const char *          sampleCountTablePtr;
143     Int64                 sampleCountTableSize;
144     Compressor*           sampleCountTableCompressor;
145     int                   minY;                 // the min y scanline stored
146     int                   maxY;                 // the max y scanline stored
147     int                   scanLineMin;          // the min y scanline writing out
148     int                   scanLineMax;          // the max y scanline writing out
149     Compressor *          compressor;
150     bool                  partiallyFull;        // has incomplete data
151     bool                  hasException;
152     string                exception;
153 
154     LineBuffer (int linesInBuffer);
155     ~LineBuffer ();
156 
wait__anonef189e780111::LineBuffer157     void                  wait () {_sem.wait();}
post__anonef189e780111::LineBuffer158     void                  post () {_sem.post();}
159 
160   private:
161 
162     Semaphore             _sem;
163 };
164 
165 
LineBuffer(int linesInBuffer)166 LineBuffer::LineBuffer (int linesInBuffer) :
167     dataPtr (0),
168     dataSize (0),
169     sampleCountTablePtr (0),
170     sampleCountTableCompressor (0),
171     compressor (0),
172     partiallyFull (false),
173     hasException (false),
174     exception (),
175     _sem (1)
176 {
177     buffer.resizeErase(linesInBuffer);
178 }
179 
180 
~LineBuffer()181 LineBuffer::~LineBuffer ()
182 {
183     if (compressor != 0)
184         delete compressor;
185 
186     if (sampleCountTableCompressor != 0)
187         delete sampleCountTableCompressor;
188 }
189 
190 } // namespace
191 
192 
193 struct DeepScanLineOutputFile::Data
194 {
195     Header                      header;                // the image header
196     int                         version;               // file format version
197     bool                        multipart;             // from a multipart file
198     Int64                       previewPosition;       // file position for preview
199     DeepFrameBuffer             frameBuffer;           // framebuffer to write into
200     int                         currentScanLine;       // next scanline to be written
201     int                         missingScanLines;      // number of lines to write
202     LineOrder                   lineOrder;             // the file's lineorder
203     int                         minX;                  // data window's min x coord
204     int                         maxX;                  // data window's max x coord
205     int                         minY;                  // data window's min y coord
206     int                         maxY;                  // data window's max x coord
207     vector<Int64>               lineOffsets;           // stores offsets in file for
208                                                        // each scanline
209     vector<size_t>              bytesPerLine;          // combined size of a line over
210                                                        // all channels
211     Compressor::Format          format;                // compressor's data format
212     vector<OutSliceInfo*>       slices;                // info about channels in file
213     Int64                       lineOffsetsPosition;   // file position for line
214                                                        // offset table
215 
216     vector<LineBuffer*>         lineBuffers;           // each holds one line buffer
217     int                         linesInBuffer;         // number of scanlines each
218                                                        // buffer holds
219     int                         partNumber;            // the output part number
220 
221     char*                       sampleCountSliceBase;  // the pointer to the number
222                                                        // of samples in each pixel
223     int                         sampleCountXStride;    // the x stride for sampleCountSliceBase
224     int                         sampleCountYStride;    // the y stride for sampleCountSliceBase
225 
226     Array<unsigned int>         lineSampleCount;       // the number of samples
227                                                        // in each line
228 
229     Int64                       maxSampleCountTableSize;
230                                                        // the max size in bytes for a pixel
231                                                        // sample count table
232     OutputStreamMutex*  _streamData;
233     bool                _deleteStream;
234 
235     Data (int numThreads);
236     ~Data ();
237 
238 
239     inline LineBuffer *         getLineBuffer (int number);// hash function from line
240                                                            // buffer indices into our
241                                                            // vector of line buffers
242 
243     inline int&                 getSampleCount(int x, int y); // get the number of samples
244                                                               // in each pixel
245 };
246 
247 
Data(int numThreads)248 DeepScanLineOutputFile::Data::Data (int numThreads):
249     lineOffsetsPosition (0),
250     partNumber (-1) ,
251     _streamData(NULL),
252     _deleteStream(false)
253 {
254     //
255     // We need at least one lineBuffer, but if threading is used,
256     // to keep n threads busy we need 2*n lineBuffers.
257     //
258 
259     lineBuffers.resize (max (1, 2 * numThreads));
260     for (size_t i = 0; i < lineBuffers.size(); i++)
261         lineBuffers[i] = 0;
262 }
263 
264 
~Data()265 DeepScanLineOutputFile::Data::~Data ()
266 {
267     for (size_t i = 0; i < lineBuffers.size(); i++)
268         if (lineBuffers[i] != 0)
269             delete lineBuffers[i];
270 
271     for (size_t i = 0; i < slices.size(); i++)
272         delete slices[i];
273 }
274 
275 
276 int&
getSampleCount(int x,int y)277 DeepScanLineOutputFile::Data::getSampleCount(int x, int y)
278 {
279     return sampleCount(sampleCountSliceBase,
280                        sampleCountXStride,
281                        sampleCountYStride,
282                        x, y);
283 }
284 
285 
286 LineBuffer*
getLineBuffer(int number)287 DeepScanLineOutputFile::Data::getLineBuffer (int number)
288 {
289     return lineBuffers[number % lineBuffers.size()];
290 }
291 
292 
293 namespace {
294 
295 Int64
writeLineOffsets(OPENEXR_IMF_INTERNAL_NAMESPACE::OStream & os,const vector<Int64> & lineOffsets)296 writeLineOffsets (OPENEXR_IMF_INTERNAL_NAMESPACE::OStream &os, const vector<Int64> &lineOffsets)
297 {
298     Int64 pos = os.tellp();
299 
300     if (pos == -1)
301         IEX_NAMESPACE::throwErrnoExc ("Cannot determine current file position (%T).");
302 
303     for (unsigned int i = 0; i < lineOffsets.size(); i++)
304         OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (os, lineOffsets[i]);
305 
306     return pos;
307 }
308 
309 
310 void
writePixelData(OutputStreamMutex * filedata,DeepScanLineOutputFile::Data * partdata,int lineBufferMinY,const char pixelData[],Int64 packedDataSize,Int64 unpackedDataSize,const char sampleCountTableData[],Int64 sampleCountTableSize)311 writePixelData (OutputStreamMutex *filedata,
312                 DeepScanLineOutputFile::Data *partdata,
313                 int lineBufferMinY,
314                 const char pixelData[],
315                 Int64 packedDataSize,
316                 Int64 unpackedDataSize,
317                 const char sampleCountTableData[],
318                 Int64 sampleCountTableSize)
319 {
320     //
321     // Store a block of pixel data in the output file, and try
322     // to keep track of the current writing position the file
323     // without calling tellp() (tellp() can be fairly expensive).
324     //
325 
326     Int64 currentPosition = filedata->currentPosition;
327     filedata->currentPosition = 0;
328 
329     if (currentPosition == 0)
330         currentPosition = filedata->os->tellp();
331 
332     partdata->lineOffsets[(partdata->currentScanLine - partdata->minY) / partdata->linesInBuffer] =
333         currentPosition;
334 
335     #ifdef DEBUG
336 
337         assert (filedata->os->tellp() == currentPosition);
338 
339     #endif
340 
341     //
342     // Write the optional part number.
343     //
344 
345     if (partdata->multipart)
346     {
347         OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*filedata->os, partdata->partNumber);
348     }
349 
350     //
351     // Write the y coordinate of the first scanline in the chunk.
352     //
353 
354     OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*filedata->os, lineBufferMinY);
355 
356     //
357     // Write the packed size of the pixel sample count table.
358     //
359 
360     OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*filedata->os, sampleCountTableSize);
361 
362     //
363     // Write the packed pixel data size.
364     //
365 
366     OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*filedata->os, packedDataSize);
367 
368     //
369     // Write the unpacked pixel data size.
370     //
371 
372     OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*filedata->os, unpackedDataSize);
373 
374     //
375     // Write the packed pixel sample count table.
376     //
377 
378     filedata->os->write (sampleCountTableData, sampleCountTableSize);
379 
380     //
381     // Write the compressed data.
382     //
383 
384     filedata->os->write (pixelData, packedDataSize);
385 
386     //
387     // Update stream position.
388     //
389 
390     filedata->currentPosition = currentPosition      +
391                                 Xdr::size<int>()     +  // y coordinate
392                                 Xdr::size<Int64>()   +  // packed sample count table size
393                                 Xdr::size<Int64>()   +  // packed data size
394                                 Xdr::size<Int64>()   +  // unpacked data size
395                                 sampleCountTableSize +  // pixel sample count table
396                                 packedDataSize;         // pixel data
397 
398     if (partdata->multipart)
399     {
400         filedata->currentPosition += Xdr::size<int>();  // optional part number
401     }
402 }
403 
404 
405 inline void
writePixelData(OutputStreamMutex * filedata,DeepScanLineOutputFile::Data * partdata,const LineBuffer * lineBuffer)406 writePixelData (OutputStreamMutex* filedata,
407                 DeepScanLineOutputFile::Data *partdata,
408                 const LineBuffer *lineBuffer)
409 {
410     writePixelData (filedata, partdata,
411                     lineBuffer->minY,
412                     lineBuffer->dataPtr,
413                     lineBuffer->dataSize,
414                     lineBuffer->uncompressedDataSize,
415                     lineBuffer->sampleCountTablePtr,
416                     lineBuffer->sampleCountTableSize);
417 }
418 
419 
420 void
convertToXdr(DeepScanLineOutputFile::Data * ofd,Array<char> & lineBuffer,int lineBufferMinY,int lineBufferMaxY,int inSize)421 convertToXdr (DeepScanLineOutputFile::Data *ofd,
422               Array<char> &lineBuffer,
423               int lineBufferMinY,
424               int lineBufferMaxY,
425               int inSize)
426 {
427     //
428     // Convert the contents of a lineBuffer from the machine's native
429     // representation to Xdr format.  This function is called by
430     // CompressLineBuffer::execute(), below, if the compressor wanted
431     // its input pixel data in the machine's native format, but then
432     // failed to compress the data (most compressors will expand rather
433     // than compress random input data).
434     //
435     // Note that this routine assumes that the machine's native
436     // representation of the pixel data has the same size as the
437     // Xdr representation.  This makes it possible to convert the
438     // pixel data in place, without an intermediate temporary buffer.
439     //
440 
441     //
442     // Iterate over all scanlines in the lineBuffer to convert.
443     //
444 
445     char* writePtr = &lineBuffer[0];
446     for (int y = lineBufferMinY; y <= lineBufferMaxY; y++)
447     {
448         //
449         // Set these to point to the start of line y.
450         // We will write to writePtr from readPtr.
451         //
452 
453         const char *readPtr = writePtr;
454 
455         //
456         // Iterate over all slices in the file.
457         //
458 
459         for (unsigned int i = 0; i < ofd->slices.size(); ++i)
460         {
461             //
462             // Test if scan line y of this channel is
463             // contains any data (the scan line contains
464             // data only if y % ySampling == 0).
465             //
466 
467             const OutSliceInfo &slice = *ofd->slices[i];
468 
469             if (modp (y, slice.ySampling) != 0)
470                 continue;
471 
472             //
473             // Find the number of sampled pixels, dMaxX-dMinX+1, for
474             // slice i in scan line y (i.e. pixels within the data window
475             // for which x % xSampling == 0).
476             //
477 
478             int xSampleCount = ofd->lineSampleCount[y - ofd->minY];
479 
480             //
481             // Convert the samples in place.
482             //
483 
484             convertInPlace (writePtr, readPtr, slice.type, xSampleCount);
485         }
486     }
487 }
488 
489 
490 //
491 // A LineBufferTask encapsulates the task of copying a set of scanlines
492 // from the user's frame buffer into a LineBuffer object, compressing
493 // the data if necessary.
494 //
495 
496 class LineBufferTask: public Task
497 {
498   public:
499 
500     LineBufferTask (TaskGroup *group,
501                     DeepScanLineOutputFile::Data *ofd,
502                     int number,
503                     int scanLineMin,
504                     int scanLineMax);
505 
506     virtual ~LineBufferTask ();
507 
508     virtual void        execute ();
509 
510   private:
511 
512     DeepScanLineOutputFile::Data *  _ofd;
513     LineBuffer *        _lineBuffer;
514 };
515 
516 
LineBufferTask(TaskGroup * group,DeepScanLineOutputFile::Data * ofd,int number,int scanLineMin,int scanLineMax)517 LineBufferTask::LineBufferTask
518     (TaskGroup *group,
519      DeepScanLineOutputFile::Data *ofd,
520      int number,
521      int scanLineMin,
522      int scanLineMax)
523 :
524     Task (group),
525     _ofd (ofd),
526     _lineBuffer (_ofd->getLineBuffer(number))
527 {
528     //
529     // Wait for the lineBuffer to become available
530     //
531 
532     _lineBuffer->wait ();
533 
534     //
535     // Initialize the lineBuffer data if necessary
536     //
537 
538     if (!_lineBuffer->partiallyFull)
539     {
540 
541         _lineBuffer->minY = _ofd->minY + number * _ofd->linesInBuffer;
542 
543         _lineBuffer->maxY = min (_lineBuffer->minY + _ofd->linesInBuffer - 1,
544                                  _ofd->maxY);
545 
546         _lineBuffer->partiallyFull = true;
547     }
548 
549     _lineBuffer->scanLineMin = max (_lineBuffer->minY, scanLineMin);
550     _lineBuffer->scanLineMax = min (_lineBuffer->maxY, scanLineMax);
551 }
552 
553 
~LineBufferTask()554 LineBufferTask::~LineBufferTask ()
555 {
556     //
557     // Signal that the line buffer is now free
558     //
559 
560     _lineBuffer->post ();
561 }
562 
563 
564 void
execute()565 LineBufferTask::execute ()
566 {
567     try
568     {
569         //
570         // First copy the pixel data from the
571         // frame buffer into the line buffer
572         //
573 
574         int yStart, yStop, dy;
575 
576         if (_ofd->lineOrder == INCREASING_Y)
577         {
578             yStart = _lineBuffer->scanLineMin;
579             yStop = _lineBuffer->scanLineMax + 1;
580             dy = 1;
581         }
582         else
583         {
584             yStart = _lineBuffer->scanLineMax;
585             yStop = _lineBuffer->scanLineMin - 1;
586             dy = -1;
587         }
588 
589         //
590         // Allocate buffers for scanlines.
591         // And calculate the sample counts for each line.
592         //
593 
594         bytesPerDeepLineTable (_ofd->header,
595                                _lineBuffer->scanLineMin,
596                                _lineBuffer->scanLineMax,
597                                _ofd->sampleCountSliceBase,
598                                _ofd->sampleCountXStride,
599                                _ofd->sampleCountYStride,
600                                _ofd->bytesPerLine);
601         for (int i = _lineBuffer->scanLineMin; i <= _lineBuffer->scanLineMax; i++)
602         {
603             // (TODO) don't do this all the time.
604             _lineBuffer->buffer[i - _lineBuffer->minY].resizeErase(
605                             _ofd->bytesPerLine[i - _ofd->minY]);
606 
607             for (int j = _ofd->minX; j <= _ofd->maxX; j++)
608                 _ofd->lineSampleCount[i - _ofd->minY] += _ofd->getSampleCount(j, i);
609         }
610 
611         //
612         // Copy data from frame buffer to line buffer.
613         //
614 
615         int y;
616 
617         for (y = yStart; y != yStop; y += dy)
618         {
619             //
620             // Gather one scan line's worth of pixel data and store
621             // them in _ofd->lineBuffer.
622             //
623 
624             char *writePtr = &_lineBuffer->buffer[y - _lineBuffer->minY][0];
625             //
626             // Iterate over all image channels.
627             //
628 
629             for (unsigned int i = 0; i < _ofd->slices.size(); ++i)
630             {
631                 //
632                 // Test if scan line y of this channel contains any data
633                 // (the scan line contains data only if y % ySampling == 0).
634                 //
635 
636                 const OutSliceInfo &slice = *_ofd->slices[i];
637 
638                 if (modp (y, slice.ySampling) != 0)
639                     continue;
640 
641                 //
642                 // Fill the line buffer with with pixel data.
643                 //
644 
645                 if (slice.zero)
646                 {
647                     //
648                     // The frame buffer contains no data for this channel.
649                     // Store zeroes in _lineBuffer->buffer.
650                     //
651 
652                     fillChannelWithZeroes (writePtr, _ofd->format, slice.type,
653                                            _ofd->lineSampleCount[y - _ofd->minY]);
654                 }
655                 else
656                 {
657 
658                     copyFromDeepFrameBuffer (writePtr, slice.base,
659                                              _ofd->sampleCountSliceBase,
660                                              _ofd->sampleCountXStride,
661                                              _ofd->sampleCountYStride,
662                                              y, _ofd->minX, _ofd->maxX,
663                                              0, 0,//offsets for samplecount
664                                              0, 0,//offsets for data
665                                              slice.sampleStride,
666                                              slice.xStride,
667                                              slice.yStride,
668                                              _ofd->format,
669                                              slice.type);
670                 }
671             }
672         }
673 
674         //
675         // If the next scanline isn't past the bounds of the lineBuffer
676         // then we have partially filled the linebuffer,
677         // otherwise the whole linebuffer is filled and then
678         // we compress the linebuffer and write it out.
679         //
680 
681         if (y >= _lineBuffer->minY && y <= _lineBuffer->maxY)
682             return;
683 
684         //
685         // Copy all data into a consecutive buffer.
686         //
687 
688         Int64 totalBytes = 0;
689         Int64 maxBytesPerLine = 0;
690         for (int i = 0; i < _lineBuffer->maxY - _lineBuffer->minY + 1; i++)
691         {
692             totalBytes += _lineBuffer->buffer[i].size();
693             if (Int64(_lineBuffer->buffer[i].size()) > maxBytesPerLine)
694                 maxBytesPerLine = _lineBuffer->buffer[i].size();
695         }
696         _lineBuffer->consecutiveBuffer.resizeErase(totalBytes);
697 
698         int pos = 0;
699         for (int i = 0; i < _lineBuffer->maxY - _lineBuffer->minY + 1; i++)
700         {
701             memcpy(_lineBuffer->consecutiveBuffer + pos,
702                    &_lineBuffer->buffer[i][0],
703                    _lineBuffer->buffer[i].size());
704             pos += _lineBuffer->buffer[i].size();
705         }
706 
707         _lineBuffer->dataPtr = _lineBuffer->consecutiveBuffer;
708 
709         _lineBuffer->dataSize = totalBytes;
710         _lineBuffer->uncompressedDataSize = _lineBuffer->dataSize;
711 
712         //
713         // Compress the pixel sample count table.
714         //
715 
716         char* ptr = _lineBuffer->sampleCountTableBuffer;
717         Int64 tableDataSize = 0;
718         for (int i = _lineBuffer->minY; i <= _lineBuffer->maxY; i++)
719         {
720             int count = 0;
721             for (int j = _ofd->minX; j <= _ofd->maxX; j++)
722             {
723                 count += _ofd->getSampleCount(j, i);
724                 Xdr::write <CharPtrIO> (ptr, count);
725                 tableDataSize += sizeof (int);
726             }
727         }
728 
729        if(_lineBuffer->sampleCountTableCompressor)
730        {
731           _lineBuffer->sampleCountTableSize =
732                   _lineBuffer->sampleCountTableCompressor->compress (
733                                                       _lineBuffer->sampleCountTableBuffer,
734                                                       tableDataSize,
735                                                       _lineBuffer->minY,
736                                                       _lineBuffer->sampleCountTablePtr);
737        }
738 
739         //
740         // If we can't make data shrink (or we weren't compressing), then just use the raw data.
741         //
742 
743         if (!_lineBuffer->sampleCountTableCompressor ||
744             _lineBuffer->sampleCountTableSize >= tableDataSize)
745         {
746             _lineBuffer->sampleCountTableSize = tableDataSize;
747             _lineBuffer->sampleCountTablePtr = _lineBuffer->sampleCountTableBuffer;
748         }
749 
750         //
751         // Compress the sample data
752         //
753 
754         // (TODO) don't do this all the time.
755         if (_lineBuffer->compressor != 0)
756             delete _lineBuffer->compressor;
757         _lineBuffer->compressor = newCompressor (_ofd->header.compression(),
758                                                  maxBytesPerLine,
759                                                  _ofd->header);
760 
761         Compressor *compressor = _lineBuffer->compressor;
762 
763         if (compressor)
764         {
765             const char *compPtr;
766 
767             Int64 compSize = compressor->compress (_lineBuffer->dataPtr,
768                                                  _lineBuffer->dataSize,
769                                                  _lineBuffer->minY, compPtr);
770 
771             if (compSize < _lineBuffer->dataSize)
772             {
773                 _lineBuffer->dataSize = compSize;
774                 _lineBuffer->dataPtr = compPtr;
775             }
776             else if (_ofd->format == Compressor::NATIVE)
777             {
778                 //
779                 // The data did not shrink during compression, but
780                 // we cannot write to the file using the machine's
781                 // native format, so we need to convert the lineBuffer
782                 // to Xdr.
783                 //
784 
785                 convertToXdr (_ofd, _lineBuffer->consecutiveBuffer, _lineBuffer->minY,
786                               _lineBuffer->maxY, _lineBuffer->dataSize);
787             }
788         }
789 
790         _lineBuffer->partiallyFull = false;
791     }
792     catch (std::exception &e)
793     {
794         if (!_lineBuffer->hasException)
795         {
796             _lineBuffer->exception = e.what ();
797             _lineBuffer->hasException = true;
798         }
799     }
800     catch (...)
801     {
802         if (!_lineBuffer->hasException)
803         {
804             _lineBuffer->exception = "unrecognized exception";
805             _lineBuffer->hasException = true;
806         }
807     }
808 }
809 
810 } // namespace
811 
812 
DeepScanLineOutputFile(const char fileName[],const Header & header,int numThreads)813 DeepScanLineOutputFile::DeepScanLineOutputFile
814     (const char fileName[],
815      const Header &header,
816      int numThreads)
817 :
818     _data (new Data (numThreads))
819 {
820     _data->_streamData=new OutputStreamMutex ();
821     _data->_deleteStream=true;
822     try
823     {
824         header.sanityCheck();
825         _data->_streamData->os = new StdOFStream (fileName);
826         initialize (header);
827         _data->_streamData->currentPosition = _data->_streamData->os->tellp();
828 
829         // Write header and empty offset table to the file.
830         writeMagicNumberAndVersionField(*_data->_streamData->os, _data->header);
831         _data->previewPosition =
832                 _data->header.writeTo (*_data->_streamData->os);
833         _data->lineOffsetsPosition =
834                 writeLineOffsets (*_data->_streamData->os, _data->lineOffsets);
835 	_data->multipart=false;// not multipart; only one header
836     }
837     catch (IEX_NAMESPACE::BaseExc &e)
838     {
839         delete _data->_streamData;
840         delete _data;
841 
842         REPLACE_EXC (e, "Cannot open image file "
843                         "\"" << fileName << "\". " << e);
844         throw;
845     }
846     catch (...)
847     {
848         delete _data->_streamData;
849         delete _data;
850         throw;
851     }
852 }
853 
854 
DeepScanLineOutputFile(OPENEXR_IMF_INTERNAL_NAMESPACE::OStream & os,const Header & header,int numThreads)855 DeepScanLineOutputFile::DeepScanLineOutputFile
856     (OPENEXR_IMF_INTERNAL_NAMESPACE::OStream &os,
857      const Header &header,
858      int numThreads)
859 :
860     _data (new Data (numThreads))
861 
862 {
863     _data->_streamData   = new OutputStreamMutex ();
864     _data->_deleteStream = false;
865     try
866     {
867         header.sanityCheck();
868         _data->_streamData->os = &os;
869         initialize (header);
870         _data->_streamData->currentPosition = _data->_streamData->os->tellp();
871 
872         // Write header and empty offset table to the file.
873         writeMagicNumberAndVersionField(*_data->_streamData->os, _data->header);
874         _data->previewPosition =
875                 _data->header.writeTo (*_data->_streamData->os);
876         _data->lineOffsetsPosition =
877                 writeLineOffsets (*_data->_streamData->os, _data->lineOffsets);
878 	_data->multipart=false;
879     }
880     catch (IEX_NAMESPACE::BaseExc &e)
881     {
882         delete _data->_streamData;
883         delete _data;
884 
885         REPLACE_EXC (e, "Cannot open image file "
886                         "\"" << os.fileName() << "\". " << e);
887         throw;
888     }
889     catch (...)
890     {
891         delete _data->_streamData;
892         delete _data;
893         throw;
894     }
895 }
896 
DeepScanLineOutputFile(const OutputPartData * part)897 DeepScanLineOutputFile::DeepScanLineOutputFile(const OutputPartData* part)
898 {
899     try
900     {
901         if (part->header.type() != DEEPSCANLINE)
902             throw IEX_NAMESPACE::ArgExc("Can't build a DeepScanLineOutputFile from a type-mismatched part.");
903 
904         _data = new Data (part->numThreads);
905         _data->_streamData = part->mutex;
906         _data->_deleteStream=false;
907         initialize (part->header);
908         _data->partNumber = part->partNumber;
909         _data->lineOffsetsPosition = part->chunkOffsetTablePosition;
910         _data->previewPosition = part->previewPosition;
911 	_data->multipart=part->multipart;
912     }
913     catch (IEX_NAMESPACE::BaseExc &e)
914     {
915         delete _data;
916 
917         REPLACE_EXC (e, "Cannot initialize output part "
918                         "\"" << part->partNumber << "\". " << e);
919         throw;
920     }
921     catch (...)
922     {
923         delete _data;
924         throw;
925     }
926 }
927 
928 void
initialize(const Header & header)929 DeepScanLineOutputFile::initialize (const Header &header)
930 {
931     _data->header = header;
932 
933     _data->header.setType(DEEPSCANLINE);
934 
935     const Box2i &dataWindow = header.dataWindow();
936 
937     _data->currentScanLine = (header.lineOrder() == INCREASING_Y)?
938                                  dataWindow.min.y: dataWindow.max.y;
939 
940     _data->missingScanLines = dataWindow.max.y - dataWindow.min.y + 1;
941     _data->lineOrder = header.lineOrder();
942     _data->minX = dataWindow.min.x;
943     _data->maxX = dataWindow.max.x;
944     _data->minY = dataWindow.min.y;
945     _data->maxY = dataWindow.max.y;
946 
947     _data->lineSampleCount.resizeErase(_data->maxY - _data->minY + 1);
948 
949     Compressor* compressor = newCompressor (_data->header.compression(),
950                                             0,
951                                             _data->header);
952     _data->format = defaultFormat (compressor);
953     _data->linesInBuffer = numLinesInBuffer (compressor);
954     if (compressor != 0)
955         delete compressor;
956 
957     int lineOffsetSize = (_data->maxY - _data->minY +
958                           _data->linesInBuffer) / _data->linesInBuffer;
959 
960 
961     _data->header.setChunkCount(lineOffsetSize);
962 
963     _data->lineOffsets.resize (lineOffsetSize);
964 
965     _data->bytesPerLine.resize (_data->maxY - _data->minY + 1);
966 
967     _data->maxSampleCountTableSize = min(_data->linesInBuffer, _data->maxY - _data->minY + 1) *
968                                      (_data->maxX - _data->minX + 1) *
969                                      sizeof(unsigned int);
970 
971     for (size_t i = 0; i < _data->lineBuffers.size(); ++i)
972     {
973         _data->lineBuffers[i] = new LineBuffer (_data->linesInBuffer);
974         _data->lineBuffers[i]->sampleCountTableBuffer.resizeErase(_data->maxSampleCountTableSize);
975 
976         _data->lineBuffers[i]->sampleCountTableCompressor =
977         newCompressor (_data->header.compression(),
978                                _data->maxSampleCountTableSize,
979                                _data->header);
980     }
981 }
982 
983 
~DeepScanLineOutputFile()984 DeepScanLineOutputFile::~DeepScanLineOutputFile ()
985 {
986     {
987         Lock lock(*_data->_streamData);
988         Int64 originalPosition = _data->_streamData->os->tellp();
989 
990         if (_data->lineOffsetsPosition > 0)
991         {
992             try
993             {
994                 _data->_streamData->os->seekp (_data->lineOffsetsPosition);
995                 writeLineOffsets (*_data->_streamData->os, _data->lineOffsets);
996 
997                 //
998                 // Restore the original position.
999                 //
1000                 _data->_streamData->os->seekp (originalPosition);
1001             }
1002             catch (...)
1003             {
1004                 //
1005                 // We cannot safely throw any exceptions from here.
1006                 // This destructor may have been called because the
1007                 // stack is currently being unwound for another
1008                 // exception.
1009                 //
1010             }
1011         }
1012     }
1013 
1014     if (_data->_deleteStream)
1015         delete _data->_streamData->os;
1016 
1017     //
1018     // (TODO) we should have a way to tell if the stream data is owned by this file or
1019     // by a parent multipart file.
1020     //
1021 
1022     if (_data->partNumber == -1)
1023         delete _data->_streamData;
1024 
1025     delete _data;
1026 }
1027 
1028 
1029 const char *
fileName() const1030 DeepScanLineOutputFile::fileName () const
1031 {
1032     return _data->_streamData->os->fileName();
1033 }
1034 
1035 
1036 const Header &
header() const1037 DeepScanLineOutputFile::header () const
1038 {
1039     return _data->header;
1040 }
1041 
1042 
1043 void
setFrameBuffer(const DeepFrameBuffer & frameBuffer)1044 DeepScanLineOutputFile::setFrameBuffer (const DeepFrameBuffer &frameBuffer)
1045 {
1046     Lock lock (*_data->_streamData);
1047 
1048     //
1049     // Check if the new frame buffer descriptor
1050     // is compatible with the image file header.
1051     //
1052 
1053     const ChannelList &channels = _data->header.channels();
1054 
1055     for (ChannelList::ConstIterator i = channels.begin();
1056          i != channels.end();
1057          ++i)
1058     {
1059         DeepFrameBuffer::ConstIterator j = frameBuffer.find (i.name());
1060 
1061         if (j == frameBuffer.end())
1062             continue;
1063 
1064         if (i.channel().type != j.slice().type)
1065         {
1066             THROW (IEX_NAMESPACE::ArgExc, "Pixel type of \"" << i.name() << "\" channel "
1067                                 "of output file \"" << fileName() << "\" is "
1068                                 "not compatible with the frame buffer's "
1069                                 "pixel type.");
1070         }
1071 
1072         if (i.channel().xSampling != j.slice().xSampling ||
1073             i.channel().ySampling != j.slice().ySampling)
1074         {
1075             THROW (IEX_NAMESPACE::ArgExc, "X and/or y subsampling factors "
1076                                 "of \"" << i.name() << "\" channel "
1077                                 "of output file \"" << fileName() << "\" are "
1078                                 "not compatible with the frame buffer's "
1079                                 "subsampling factors.");
1080         }
1081     }
1082 
1083     //
1084     // Store the pixel sample count table.
1085     // (TODO) Support for different sampling rates?
1086     //
1087 
1088     const Slice& sampleCountSlice = frameBuffer.getSampleCountSlice();
1089     if (sampleCountSlice.base == 0)
1090     {
1091         throw IEX_NAMESPACE::ArgExc ("Invalid base pointer, please set a proper sample count slice.");
1092     }
1093     else
1094     {
1095         _data->sampleCountSliceBase = sampleCountSlice.base;
1096         _data->sampleCountXStride = sampleCountSlice.xStride;
1097         _data->sampleCountYStride = sampleCountSlice.yStride;
1098     }
1099 
1100     //
1101     // Initialize slice table for writePixels().
1102     // Pixel sample count slice is not presented in the header,
1103     // so it wouldn't be added here.
1104     // Store the pixel base pointer table.
1105     // (TODO) Support for different sampling rates?
1106     //
1107 
1108     vector<OutSliceInfo*> slices;
1109 
1110     for (ChannelList::ConstIterator i = channels.begin();
1111          i != channels.end();
1112          ++i)
1113     {
1114         DeepFrameBuffer::ConstIterator j = frameBuffer.find (i.name());
1115 
1116         if (j == frameBuffer.end())
1117         {
1118             //
1119             // Channel i is not present in the frame buffer.
1120             // In the file, channel i will contain only zeroes.
1121             //
1122 
1123             slices.push_back (new OutSliceInfo (i.channel().type,
1124                                                 NULL,// base
1125                                                 0,// sampleStride,
1126                                                 0,// xStride
1127                                                 0,// yStride
1128                                                 i.channel().xSampling,
1129                                                 i.channel().ySampling,
1130                                                 true)); // zero
1131         }
1132         else
1133         {
1134             //
1135             // Channel i is present in the frame buffer.
1136             //
1137 
1138             slices.push_back (new OutSliceInfo (j.slice().type,
1139                                                 j.slice().base,
1140                                                 j.slice().sampleStride,
1141                                                 j.slice().xStride,
1142                                                 j.slice().yStride,
1143                                                 j.slice().xSampling,
1144                                                 j.slice().ySampling,
1145                                                 false)); // zero
1146 
1147         }
1148     }
1149 
1150     //
1151     // Store the new frame buffer.
1152     //
1153 
1154     _data->frameBuffer = frameBuffer;
1155 
1156     for (size_t i = 0; i < _data->slices.size(); i++)
1157         delete _data->slices[i];
1158     _data->slices = slices;
1159 }
1160 
1161 
1162 const DeepFrameBuffer &
frameBuffer() const1163 DeepScanLineOutputFile::frameBuffer () const
1164 {
1165     Lock lock (*_data->_streamData);
1166     return _data->frameBuffer;
1167 }
1168 
1169 
1170 void
writePixels(int numScanLines)1171 DeepScanLineOutputFile::writePixels (int numScanLines)
1172 {
1173     try
1174     {
1175         Lock lock (*_data->_streamData);
1176 
1177         if (_data->slices.size() == 0)
1178             throw IEX_NAMESPACE::ArgExc ("No frame buffer specified "
1179                                "as pixel data source.");
1180 
1181         //
1182         // Maintain two iterators:
1183         //     nextWriteBuffer: next linebuffer to be written to the file
1184         //     nextCompressBuffer: next linebuffer to compress
1185         //
1186 
1187         int first = (_data->currentScanLine - _data->minY) /
1188                          _data->linesInBuffer;
1189 
1190         int nextWriteBuffer = first;
1191         int nextCompressBuffer;
1192         int stop;
1193         int step;
1194         int scanLineMin;
1195         int scanLineMax;
1196 
1197         {
1198             //
1199             // Create a task group for all line buffer tasks. When the
1200             // taskgroup goes out of scope, the destructor waits until
1201             // all tasks are complete.
1202             //
1203 
1204             TaskGroup taskGroup;
1205 
1206             //
1207             // Determine the range of lineBuffers that intersect the scan
1208             // line range.  Then add the initial compression tasks to the
1209             // thread pool.  We always add in at least one task but the
1210             // individual task might not do anything if numScanLines == 0.
1211             //
1212 
1213             if (_data->lineOrder == INCREASING_Y)
1214             {
1215                 int last = (_data->currentScanLine + (numScanLines - 1) -
1216                             _data->minY) / _data->linesInBuffer;
1217 
1218                 scanLineMin = _data->currentScanLine;
1219                 scanLineMax = _data->currentScanLine + numScanLines - 1;
1220 
1221                 int numTasks = max (min ((int)_data->lineBuffers.size(),
1222                                          last - first + 1),
1223                                     1);
1224 
1225                 for (int i = 0; i < numTasks; i++)
1226                 {
1227                     ThreadPool::addGlobalTask
1228                         (new LineBufferTask (&taskGroup, _data, first + i,
1229                                              scanLineMin, scanLineMax));
1230                 }
1231 
1232                 nextCompressBuffer = first + numTasks;
1233                 stop = last + 1;
1234                 step = 1;
1235             }
1236             else
1237             {
1238                 int last = (_data->currentScanLine - (numScanLines - 1) -
1239                             _data->minY) / _data->linesInBuffer;
1240 
1241                 scanLineMax = _data->currentScanLine;
1242                 scanLineMin = _data->currentScanLine - numScanLines + 1;
1243 
1244                 int numTasks = max (min ((int)_data->lineBuffers.size(),
1245                                          first - last + 1),
1246                                     1);
1247 
1248                 for (int i = 0; i < numTasks; i++)
1249                 {
1250                     ThreadPool::addGlobalTask
1251                         (new LineBufferTask (&taskGroup, _data, first - i,
1252                                              scanLineMin, scanLineMax));
1253                 }
1254 
1255                 nextCompressBuffer = first - numTasks;
1256                 stop = last - 1;
1257                 step = -1;
1258             }
1259 
1260             while (true)
1261             {
1262                 if (_data->missingScanLines <= 0)
1263                 {
1264                     throw IEX_NAMESPACE::ArgExc ("Tried to write more scan lines "
1265                                        "than specified by the data window.");
1266                 }
1267 
1268                 //
1269                 // Wait until the next line buffer is ready to be written
1270                 //
1271 
1272                 LineBuffer *writeBuffer =
1273                     _data->getLineBuffer (nextWriteBuffer);
1274 
1275                 writeBuffer->wait();
1276 
1277                 int numLines = writeBuffer->scanLineMax -
1278                                writeBuffer->scanLineMin + 1;
1279 
1280                 _data->missingScanLines -= numLines;
1281 
1282                 //
1283                 // If the line buffer is only partially full, then it is
1284                 // not complete and we cannot write it to disk yet.
1285                 //
1286 
1287                 if (writeBuffer->partiallyFull)
1288                 {
1289                     _data->currentScanLine = _data->currentScanLine +
1290                                              step * numLines;
1291                     writeBuffer->post();
1292 
1293                     return;
1294                 }
1295 
1296                 //
1297                 // Write the line buffer
1298                 //
1299 
1300                 writePixelData (_data->_streamData, _data, writeBuffer);
1301                 nextWriteBuffer += step;
1302 
1303                 _data->currentScanLine = _data->currentScanLine +
1304                                          step * numLines;
1305 
1306                 #ifdef DEBUG
1307 
1308                     assert (_data->currentScanLine ==
1309                             ((_data->lineOrder == INCREASING_Y) ?
1310                              writeBuffer->scanLineMax + 1:
1311                              writeBuffer->scanLineMin - 1));
1312 
1313                 #endif
1314 
1315                 //
1316                 // Release the lock on the line buffer
1317                 //
1318 
1319                 writeBuffer->post();
1320 
1321                 //
1322                 // If this was the last line buffer in the scanline range
1323                 //
1324 
1325                 if (nextWriteBuffer == stop)
1326                     break;
1327 
1328                 //
1329                 // If there are no more line buffers to compress,
1330                 // then only continue to write out remaining lineBuffers
1331                 //
1332 
1333                 if (nextCompressBuffer == stop)
1334                     continue;
1335 
1336                 //
1337                 // Add nextCompressBuffer as a compression task
1338                 //
1339 
1340                 ThreadPool::addGlobalTask
1341                     (new LineBufferTask (&taskGroup, _data, nextCompressBuffer,
1342                                          scanLineMin, scanLineMax));
1343 
1344                 //
1345                 // Update the next line buffer we need to compress
1346                 //
1347 
1348                 nextCompressBuffer += step;
1349             }
1350 
1351             //
1352             // Finish all tasks
1353             //
1354         }
1355 
1356         //
1357         // Exeption handling:
1358         //
1359         // LineBufferTask::execute() may have encountered exceptions, but
1360         // those exceptions occurred in another thread, not in the thread
1361         // that is executing this call to OutputFile::writePixels().
1362         // LineBufferTask::execute() has caught all exceptions and stored
1363         // the exceptions' what() strings in the line buffers.
1364         // Now we check if any line buffer contains a stored exception; if
1365         // this is the case then we re-throw the exception in this thread.
1366         // (It is possible that multiple line buffers contain stored
1367         // exceptions.  We re-throw the first exception we find and
1368         // ignore all others.)
1369         //
1370 
1371         const string *exception = 0;
1372 
1373         for (size_t i = 0; i < _data->lineBuffers.size(); ++i)
1374         {
1375             LineBuffer *lineBuffer = _data->lineBuffers[i];
1376 
1377             if (lineBuffer->hasException && !exception)
1378                 exception = &lineBuffer->exception;
1379 
1380             lineBuffer->hasException = false;
1381         }
1382 
1383         if (exception)
1384             throw IEX_NAMESPACE::IoExc (*exception);
1385     }
1386     catch (IEX_NAMESPACE::BaseExc &e)
1387     {
1388         REPLACE_EXC (e, "Failed to write pixel data to image "
1389                         "file \"" << fileName() << "\". " << e);
1390         throw;
1391     }
1392 }
1393 
1394 
1395 int
currentScanLine() const1396 DeepScanLineOutputFile::currentScanLine () const
1397 {
1398     Lock lock (*_data->_streamData);
1399     return _data->currentScanLine;
1400 }
1401 
1402 
1403 void
copyPixels(DeepScanLineInputPart & in)1404 DeepScanLineOutputFile::copyPixels (DeepScanLineInputPart &in)
1405 {
1406     copyPixels(*in.file);
1407 }
1408 
1409 void
copyPixels(DeepScanLineInputFile & in)1410 DeepScanLineOutputFile::copyPixels (DeepScanLineInputFile &in)
1411 {
1412 
1413     Lock lock (*_data->_streamData);
1414 
1415     //
1416     // Check if this file's and and the InputFile's
1417     // headers are compatible.
1418     //
1419 
1420     const Header &hdr = _data->header;
1421     const Header &inHdr = in.header();
1422 
1423     if(!inHdr.hasType() || inHdr.type()!=DEEPSCANLINE)
1424     {
1425         THROW (IEX_NAMESPACE::ArgExc, "Cannot copy pixels from image "
1426                             "file \"" << in.fileName() << "\" to image "
1427                             "file \"" << fileName() << "\": the input needs to be a deep scanline image");
1428     }
1429     if (!(hdr.dataWindow() == inHdr.dataWindow()))
1430         THROW (IEX_NAMESPACE::ArgExc, "Cannot copy pixels from image "
1431                             "file \"" << in.fileName() << "\" to image "
1432                             "file \"" << fileName() << "\". "
1433                             "The files have different data windows.");
1434 
1435     if (!(hdr.lineOrder() == inHdr.lineOrder()))
1436         THROW (IEX_NAMESPACE::ArgExc, "Quick pixel copy from image "
1437                             "file \"" << in.fileName() << "\" to image "
1438                             "file \"" << fileName() << "\" failed. "
1439                             "The files have different line orders.");
1440 
1441     if (!(hdr.compression() == inHdr.compression()))
1442         THROW (IEX_NAMESPACE::ArgExc, "Quick pixel copy from image "
1443                             "file \"" << in.fileName() << "\" to image "
1444                             "file \"" << fileName() << "\" failed. "
1445                             "The files use different compression methods.");
1446 
1447     if (!(hdr.channels() == inHdr.channels()))
1448         THROW (IEX_NAMESPACE::ArgExc, "Quick pixel copy from image "
1449                             "file \"" << in.fileName() << "\" to image "
1450                             "file \"" << fileName() << "\" failed.  "
1451                             "The files have different channel lists.");
1452 
1453     //
1454     // Verify that no pixel data have been written to this file yet.
1455     //
1456 
1457     const Box2i &dataWindow = hdr.dataWindow();
1458 
1459     if (_data->missingScanLines != dataWindow.max.y - dataWindow.min.y + 1)
1460         THROW (IEX_NAMESPACE::LogicExc, "Quick pixel copy from image "
1461                               "file \"" << in.fileName() << "\" to image "
1462                               "file \"" << fileName() << "\" failed. "
1463                               "\"" << fileName() << "\" already contains "
1464                               "pixel data.");
1465 
1466     //
1467     // Copy the pixel data.
1468     //
1469 
1470     vector<char> data(4096);
1471 
1472     while (_data->missingScanLines > 0)
1473     {
1474         Int64 dataSize = (Int64) data.size();
1475         in.rawPixelData(_data->currentScanLine, &data[0], dataSize);
1476         if(dataSize > data.size())
1477         {
1478             // block wasn't big enough - go again with enough memory this time
1479             data.resize(dataSize);
1480             in.rawPixelData(_data->currentScanLine, &data[0], dataSize);
1481         }
1482 
1483         // extract header from block to pass to writePixelData
1484 
1485         Int64 packedSampleCountSize = *(Int64 *) (&data[4]);
1486         Int64 packedDataSize = *(Int64 *) (&data[12]);
1487         Int64 unpackedDataSize = *(Int64 *) (&data[20]);
1488         const char * sampleCountTable = &data[0]+28;
1489         const char * pixelData = sampleCountTable + packedSampleCountSize;
1490 
1491 
1492         writePixelData (_data->_streamData, _data, lineBufferMinY (_data->currentScanLine,
1493                                                _data->minY,
1494                                                _data->linesInBuffer),
1495                         pixelData, packedDataSize, unpackedDataSize,sampleCountTable,packedSampleCountSize);
1496 
1497         _data->currentScanLine += (_data->lineOrder == INCREASING_Y)?
1498                                    _data->linesInBuffer: -_data->linesInBuffer;
1499 
1500         _data->missingScanLines -= _data->linesInBuffer;
1501     }
1502 }
1503 
1504 
1505 void
updatePreviewImage(const PreviewRgba newPixels[])1506 DeepScanLineOutputFile::updatePreviewImage (const PreviewRgba newPixels[])
1507 {
1508     Lock lock (*_data->_streamData);
1509 
1510     if (_data->previewPosition <= 0)
1511         THROW (IEX_NAMESPACE::LogicExc, "Cannot update preview image pixels. "
1512                               "File \"" << fileName() << "\" does not "
1513                               "contain a preview image.");
1514 
1515     //
1516     // Store the new pixels in the header's preview image attribute.
1517     //
1518 
1519     PreviewImageAttribute &pia =
1520         _data->header.typedAttribute <PreviewImageAttribute> ("preview");
1521 
1522     PreviewImage &pi = pia.value();
1523     PreviewRgba *pixels = pi.pixels();
1524     int numPixels = pi.width() * pi.height();
1525 
1526     for (int i = 0; i < numPixels; ++i)
1527         pixels[i] = newPixels[i];
1528 
1529     //
1530     // Save the current file position, jump to the position in
1531     // the file where the preview image starts, store the new
1532     // preview image, and jump back to the saved file position.
1533     //
1534 
1535     Int64 savedPosition = _data->_streamData->os->tellp();
1536 
1537     try
1538     {
1539         _data->_streamData->os->seekp (_data->previewPosition);
1540         pia.writeValueTo (*_data->_streamData->os, _data->version);
1541         _data->_streamData->os->seekp (savedPosition);
1542     }
1543     catch (IEX_NAMESPACE::BaseExc &e)
1544     {
1545         REPLACE_EXC (e, "Cannot update preview image pixels for "
1546                         "file \"" << fileName() << "\". " << e);
1547         throw;
1548     }
1549 }
1550 
1551 
1552 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT
1553