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