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 // class DeepTiledOutputFile
38 //
39 //-----------------------------------------------------------------------------
40
41 #include "ImfDeepTiledOutputFile.h"
42 #include "ImfDeepTiledInputFile.h"
43 #include "ImfDeepTiledInputPart.h"
44 #include "ImfInputFile.h"
45 #include "ImfTileDescriptionAttribute.h"
46 #include "ImfPreviewImageAttribute.h"
47 #include "ImfChannelList.h"
48 #include "ImfMisc.h"
49 #include "ImfTiledMisc.h"
50 #include "ImfStdIO.h"
51 #include "ImfCompressor.h"
52 #include "ImfOutputStreamMutex.h"
53 #include "ImfOutputPartData.h"
54 #include "ImfArray.h"
55 #include "ImfXdr.h"
56 #include "ImfVersion.h"
57 #include "ImfTileOffsets.h"
58 #include "ImfThreading.h"
59 #include "ImfPartType.h"
60
61 #include "ImathBox.h"
62
63 #include "IlmThreadPool.h"
64 #include "IlmThreadSemaphore.h"
65 #include "IlmThreadMutex.h"
66
67 #include "Iex.h"
68
69 #include <string>
70 #include <vector>
71 #include <fstream>
72 #include <assert.h>
73 #include <map>
74 #include <algorithm>
75
76 #include "ImfNamespace.h"
77
78 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
79
80 using IMATH_NAMESPACE::Box2i;
81 using IMATH_NAMESPACE::V2i;
82 using std::string;
83 using std::vector;
84 using std::ofstream;
85 using std::map;
86 using std::min;
87 using std::max;
88 using std::swap;
89 using ILMTHREAD_NAMESPACE::Mutex;
90 using ILMTHREAD_NAMESPACE::Lock;
91 using ILMTHREAD_NAMESPACE::Semaphore;
92 using ILMTHREAD_NAMESPACE::Task;
93 using ILMTHREAD_NAMESPACE::TaskGroup;
94 using ILMTHREAD_NAMESPACE::ThreadPool;
95
96 namespace {
97
98 struct TOutSliceInfo
99 {
100 PixelType type;
101 const char * base;
102 size_t sampleStride;
103 size_t xStride;
104 size_t yStride;
105 bool zero;
106 int xTileCoords;
107 int yTileCoords;
108
109 TOutSliceInfo (PixelType type = HALF,
110 size_t sampleStride = 0,
111 size_t xStride = 0,
112 size_t yStride = 0,
113 bool zero = false,
114 int xTileCoords = 0,
115 int yTileCoords = 0);
116 };
117
118
TOutSliceInfo(PixelType t,size_t spst,size_t xStride,size_t yStride,bool z,int xtc,int ytc)119 TOutSliceInfo::TOutSliceInfo (PixelType t,
120 size_t spst,
121 size_t xStride,
122 size_t yStride,
123 bool z,
124 int xtc,
125 int ytc)
126 :
127 type (t),
128 sampleStride (spst),
129 xStride(xStride),
130 yStride(yStride),
131 zero (z),
132 xTileCoords (xtc),
133 yTileCoords (ytc)
134 {
135 // empty
136 }
137
138
139 struct TileCoord
140 {
141 int dx;
142 int dy;
143 int lx;
144 int ly;
145
146
TileCoord__anonb09980bd0111::TileCoord147 TileCoord (int xTile = 0, int yTile = 0,
148 int xLevel = 0, int yLevel = 0)
149 :
150 dx (xTile), dy (yTile),
151 lx (xLevel), ly (yLevel)
152 {
153 // empty
154 }
155
156
157 bool
operator <__anonb09980bd0111::TileCoord158 operator < (const TileCoord &other) const
159 {
160 return (ly < other.ly) ||
161 (ly == other.ly && lx < other.lx) ||
162 ((ly == other.ly && lx == other.lx) &&
163 ((dy < other.dy) || (dy == other.dy && dx < other.dx)));
164 }
165
166
167 bool
operator ==__anonb09980bd0111::TileCoord168 operator == (const TileCoord &other) const
169 {
170 return lx == other.lx &&
171 ly == other.ly &&
172 dx == other.dx &&
173 dy == other.dy;
174 }
175 };
176
177
178 struct BufferedTile
179 {
180 char * pixelData;
181 Int64 pixelDataSize;
182 Int64 unpackedDataSize;
183 char * sampleCountTableData;
184 Int64 sampleCountTableSize;
185
BufferedTile__anonb09980bd0111::BufferedTile186 BufferedTile (const char *data, int size, int unpackedSize,
187 const char *tableData, int tableSize):
188 pixelData (0),
189 pixelDataSize(size),
190 unpackedDataSize(unpackedSize),
191 sampleCountTableData(0),
192 sampleCountTableSize(tableSize)
193 {
194 pixelData = new char[pixelDataSize];
195 memcpy (pixelData, data, pixelDataSize);
196
197 sampleCountTableData = new char[tableSize];
198 memcpy (sampleCountTableData, tableData, tableSize);
199 }
200
~BufferedTile__anonb09980bd0111::BufferedTile201 ~BufferedTile()
202 {
203 delete [] pixelData;
204 delete [] sampleCountTableData;
205 }
206 };
207
208
209 typedef map <TileCoord, BufferedTile *> TileMap;
210
211
212 struct TileBuffer
213 {
214 Array<char> buffer;
215 const char * dataPtr;
216 Int64 dataSize;
217 Int64 uncompressedSize;
218 Compressor * compressor;
219 Array<char> sampleCountTableBuffer;
220 const char * sampleCountTablePtr;
221 Int64 sampleCountTableSize;
222 Compressor* sampleCountTableCompressor;
223 TileCoord tileCoord;
224 bool hasException;
225 string exception;
226
227 TileBuffer ();
228 ~TileBuffer ();
229
wait__anonb09980bd0111::TileBuffer230 inline void wait () {_sem.wait();}
post__anonb09980bd0111::TileBuffer231 inline void post () {_sem.post();}
232
233 protected:
234
235 Semaphore _sem;
236 };
237
238
TileBuffer()239 TileBuffer::TileBuffer ():
240 dataPtr (0),
241 dataSize (0),
242 compressor (0),
243 sampleCountTablePtr (0),
244 sampleCountTableCompressor (0),
245 hasException (false),
246 exception (),
247 _sem (1)
248 {
249 // empty
250 }
251
252
~TileBuffer()253 TileBuffer::~TileBuffer ()
254 {
255 if (compressor != 0)
256 delete compressor;
257
258 if (sampleCountTableCompressor != 0)
259 delete sampleCountTableCompressor;
260 }
261
262
263 } // namespace
264
265
266 struct DeepTiledOutputFile::Data
267 {
268 Header header; // the image header
269 int version; // file format version
270 bool multipart; // file is multipart
271 TileDescription tileDesc; // describes the tile layout
272 DeepFrameBuffer frameBuffer; // framebuffer to write into
273 Int64 previewPosition;
274 LineOrder lineOrder; // the file's lineorder
275 int minX; // data window's min x coord
276 int maxX; // data window's max x coord
277 int minY; // data window's min y coord
278 int maxY; // data window's max x coord
279
280 int numXLevels; // number of x levels
281 int numYLevels; // number of y levels
282 int * numXTiles; // number of x tiles at a level
283 int * numYTiles; // number of y tiles at a level
284
285 TileOffsets tileOffsets; // stores offsets in file for
286 // each tile
287
288 Compressor::Format format; // compressor's data format
289 vector<TOutSliceInfo*> slices; // info about channels in file
290
291 vector<TileBuffer*> tileBuffers;
292
293 Int64 tileOffsetsPosition; // position of the tile index
294
295 TileMap tileMap; // the map of buffered tiles
296 TileCoord nextTileToWrite;
297
298 int partNumber; // the output part number
299
300 char* sampleCountSliceBase; // the pointer to the number
301 // of samples in each pixel
302 int sampleCountXStride; // the x stride for sampleCountSliceBase
303 int sampleCountYStride; // the y stride for sampleCountSliceBase
304 int sampleCountXTileCoords; // using x coordinates relative to current tile
305 int sampleCountYTileCoords; // using y coordinates relative to current tile
306
307 Int64 maxSampleCountTableSize;// the max size in bytes for a pixel
308 // sample count table
309 OutputStreamMutex* _streamData;
310 bool _deleteStream;
311
312 Data (int numThreads);
313 ~Data ();
314
315 inline TileBuffer * getTileBuffer (int number);
316 // hash function from tile
317 // buffer coords into our
318 // vector of tile buffers
319
320 int& getSampleCount(int x, int y);
321 // get the number of samples
322 // in each pixel
323
324 TileCoord nextTileCoord (const TileCoord &a);
325 };
326
327
Data(int numThreads)328 DeepTiledOutputFile::Data::Data (int numThreads):
329 numXTiles(0),
330 numYTiles(0),
331 tileOffsetsPosition (0),
332 partNumber(-1),
333 _streamData(NULL),
334 _deleteStream(true)
335 {
336 //
337 // We need at least one tileBuffer, but if threading is used,
338 // to keep n threads busy we need 2*n tileBuffers
339 //
340
341 tileBuffers.resize (max (1, 2 * numThreads));
342 for (size_t i = 0; i < tileBuffers.size(); i++)
343 tileBuffers[i] = 0;
344 }
345
346
~Data()347 DeepTiledOutputFile::Data::~Data ()
348 {
349 delete [] numXTiles;
350 delete [] numYTiles;
351
352 //
353 // Delete all the tile buffers, if any still happen to exist
354 //
355
356 for (TileMap::iterator i = tileMap.begin(); i != tileMap.end(); ++i)
357 delete i->second;
358
359 for (size_t i = 0; i < tileBuffers.size(); i++)
360 if (tileBuffers[i] != 0)
361 delete tileBuffers[i];
362
363 for (size_t i = 0; i < slices.size(); i++)
364 delete slices[i];
365 }
366
367
368 int&
getSampleCount(int x,int y)369 DeepTiledOutputFile::Data::getSampleCount(int x, int y)
370 {
371 return sampleCount(sampleCountSliceBase,
372 sampleCountXStride,
373 sampleCountYStride,
374 x, y);
375 }
376
377
378 TileBuffer*
getTileBuffer(int number)379 DeepTiledOutputFile::Data::getTileBuffer (int number)
380 {
381 return tileBuffers[number % tileBuffers.size()];
382 }
383
384
385 TileCoord
nextTileCoord(const TileCoord & a)386 DeepTiledOutputFile::Data::nextTileCoord (const TileCoord &a)
387 {
388 TileCoord b = a;
389
390 if (lineOrder == INCREASING_Y)
391 {
392 b.dx++;
393
394 if (b.dx >= numXTiles[b.lx])
395 {
396 b.dx = 0;
397 b.dy++;
398
399 if (b.dy >= numYTiles[b.ly])
400 {
401 //
402 // the next tile is in the next level
403 //
404
405 b.dy = 0;
406
407 switch (tileDesc.mode)
408 {
409 case ONE_LEVEL:
410 case MIPMAP_LEVELS:
411
412 b.lx++;
413 b.ly++;
414 break;
415
416 case RIPMAP_LEVELS:
417
418 b.lx++;
419
420 if (b.lx >= numXLevels)
421 {
422 b.lx = 0;
423 b.ly++;
424
425 #ifdef DEBUG
426 assert (b.ly <= numYLevels);
427 #endif
428 }
429 break;
430 case NUM_LEVELMODES :
431 throw IEX_NAMESPACE::LogicExc("unknown level mode computing nextTileCoord");
432 }
433 }
434 }
435 }
436 else if (lineOrder == DECREASING_Y)
437 {
438 b.dx++;
439
440 if (b.dx >= numXTiles[b.lx])
441 {
442 b.dx = 0;
443 b.dy--;
444
445 if (b.dy < 0)
446 {
447 //
448 // the next tile is in the next level
449 //
450
451 switch (tileDesc.mode)
452 {
453 case ONE_LEVEL:
454 case MIPMAP_LEVELS:
455
456 b.lx++;
457 b.ly++;
458 break;
459
460 case RIPMAP_LEVELS:
461
462 b.lx++;
463
464 if (b.lx >= numXLevels)
465 {
466 b.lx = 0;
467 b.ly++;
468
469 #ifdef DEBUG
470 assert (b.ly <= numYLevels);
471 #endif
472 }
473 break;
474 case NUM_LEVELMODES :
475 throw IEX_NAMESPACE::LogicExc("unknown level mode computing nextTileCoord");
476 }
477
478 if (b.ly < numYLevels)
479 b.dy = numYTiles[b.ly] - 1;
480 }
481 }
482 }else if(lineOrder==RANDOM_Y)
483 {
484 THROW (IEX_NAMESPACE::ArgExc,
485 "can't compute next tile from randomly ordered image: use getTilesInOrder instead");
486
487 }
488
489 return b;
490 }
491
492
493 namespace {
494
495 void
writeTileData(DeepTiledOutputFile::Data * ofd,int dx,int dy,int lx,int ly,const char pixelData[],Int64 pixelDataSize,Int64 unpackedDataSize,const char sampleCountTableData[],Int64 sampleCountTableSize)496 writeTileData (DeepTiledOutputFile::Data *ofd,
497 int dx, int dy,
498 int lx, int ly,
499 const char pixelData[],
500 Int64 pixelDataSize,
501 Int64 unpackedDataSize,
502 const char sampleCountTableData[],
503 Int64 sampleCountTableSize)
504 {
505
506 //
507 // Store a block of pixel data in the output file, and try
508 // to keep track of the current writing position the file,
509 // without calling tellp() (tellp() can be fairly expensive).
510 //
511
512 Int64 currentPosition = ofd->_streamData->currentPosition;
513 ofd->_streamData->currentPosition = 0;
514
515 if (currentPosition == 0)
516 currentPosition = ofd->_streamData->os->tellp();
517
518 ofd->tileOffsets (dx, dy, lx, ly) = currentPosition;
519
520 #ifdef DEBUG
521 assert (ofd->_streamData->os->tellp() == currentPosition);
522 #endif
523
524 //
525 // Write the tile header.
526 //
527
528 if (ofd->multipart)
529 {
530 Xdr::write <StreamIO> (*ofd->_streamData->os, ofd->partNumber);
531 }
532 Xdr::write <StreamIO> (*ofd->_streamData->os, dx);
533 Xdr::write <StreamIO> (*ofd->_streamData->os, dy);
534 Xdr::write <StreamIO> (*ofd->_streamData->os, lx);
535 Xdr::write <StreamIO> (*ofd->_streamData->os, ly);
536
537 //
538 // Write the packed size of the pixel sample count table (64 bits)
539 //
540
541 Xdr::write <StreamIO> (*ofd->_streamData->os, sampleCountTableSize);
542
543 //
544 // Write the packed and unpacked data size (64 bits each)
545 //
546
547 Xdr::write <StreamIO> (*ofd->_streamData->os, pixelDataSize);
548 Xdr::write <StreamIO> (*ofd->_streamData->os, unpackedDataSize);
549
550 //
551 // Write the compressed pixel sample count table.
552 //
553
554 ofd->_streamData->os->write (sampleCountTableData, sampleCountTableSize);
555
556 //
557 // Write the compressed data.
558 //
559
560 ofd->_streamData->os->write (pixelData, pixelDataSize);
561
562 //
563 // Keep current position in the file so that we can avoid
564 // redundant seekg() operations (seekg() can be fairly expensive).
565 //
566
567 ofd->_streamData->currentPosition = currentPosition +
568 4 * Xdr::size<int>() + // dx, dy, lx, ly,
569 3 * Xdr::size<Int64>() + // sampleCountTableSize,
570 // pixelDataSize,
571 // unpackedDataSize
572 sampleCountTableSize +
573 pixelDataSize;
574
575 if (ofd->multipart)
576 {
577 ofd->_streamData->currentPosition += Xdr::size<int>();
578 }
579 }
580
581
582
583 void
bufferedTileWrite(DeepTiledOutputFile::Data * ofd,int dx,int dy,int lx,int ly,const char pixelData[],Int64 pixelDataSize,Int64 unpackedDataSize,const char sampleCountTableData[],Int64 sampleCountTableSize)584 bufferedTileWrite (
585 DeepTiledOutputFile::Data *ofd,
586 int dx, int dy,
587 int lx, int ly,
588 const char pixelData[],
589 Int64 pixelDataSize,
590 Int64 unpackedDataSize,
591 const char sampleCountTableData[],
592 Int64 sampleCountTableSize)
593 {
594 //
595 // Check if a tile with coordinates (dx,dy,lx,ly) has already been written.
596 //
597
598 if (ofd->tileOffsets (dx, dy, lx, ly))
599 {
600 THROW (IEX_NAMESPACE::ArgExc,
601 "Attempt to write tile "
602 "(" << dx << ", " << dy << ", " << lx << ", " << ly << ") "
603 "more than once.");
604 }
605
606 //
607 // If tiles can be written in random order, then don't buffer anything.
608 //
609
610 if (ofd->lineOrder == RANDOM_Y)
611 {
612 writeTileData (ofd, dx, dy, lx, ly,
613 pixelData, pixelDataSize, unpackedDataSize,
614 sampleCountTableData, sampleCountTableSize);
615 return;
616 }
617
618 //
619 // If the tiles cannot be written in random order, then check if a
620 // tile with coordinates (dx,dy,lx,ly) has already been buffered.
621 //
622
623 TileCoord currentTile = TileCoord(dx, dy, lx, ly);
624
625 if (ofd->tileMap.find (currentTile) != ofd->tileMap.end())
626 {
627 THROW (IEX_NAMESPACE::ArgExc,
628 "Attempt to write tile "
629 "(" << dx << ", " << dy << ", " << lx << ", " << ly << ") "
630 "more than once.");
631 }
632
633 //
634 // If all the tiles before this one have already been written to the file,
635 // then write this tile immediately and check if we have buffered tiles
636 // that can be written after this tile.
637 //
638 // Otherwise, buffer the tile so it can be written to file later.
639 //
640
641 if (ofd->nextTileToWrite == currentTile)
642 {
643 writeTileData (ofd, dx, dy, lx, ly,
644 pixelData, pixelDataSize, unpackedDataSize,
645 sampleCountTableData, sampleCountTableSize);
646 ofd->nextTileToWrite = ofd->nextTileCoord (ofd->nextTileToWrite);
647
648 TileMap::iterator i = ofd->tileMap.find (ofd->nextTileToWrite);
649
650 //
651 // Step through the tiles and write all successive buffered tiles after
652 // the current one.
653 //
654
655 while(i != ofd->tileMap.end())
656 {
657 //
658 // Write the tile, and then delete the tile's buffered data
659 //
660
661 writeTileData (ofd,
662 i->first.dx, i->first.dy,
663 i->first.lx, i->first.ly,
664 i->second->pixelData,
665 i->second->pixelDataSize,
666 i->second->unpackedDataSize,
667 i->second->sampleCountTableData,
668 i->second->sampleCountTableSize);
669
670 delete i->second;
671 ofd->tileMap.erase (i);
672
673 //
674 // Proceed to the next tile
675 //
676
677 ofd->nextTileToWrite = ofd->nextTileCoord (ofd->nextTileToWrite);
678 i = ofd->tileMap.find (ofd->nextTileToWrite);
679 }
680 }
681 else
682 {
683 //
684 // Create a new BufferedTile, copy the pixelData into it, and
685 // insert it into the tileMap.
686 //
687
688 ofd->tileMap[currentTile] =
689 new BufferedTile ((const char *)pixelData, pixelDataSize, unpackedDataSize,
690 sampleCountTableData, sampleCountTableSize);
691 }
692 }
693
694
695 void
convertToXdr(DeepTiledOutputFile::Data * ofd,Array<char> & tileBuffer,int numScanLines,vector<Int64> & bytesPerLine)696 convertToXdr (DeepTiledOutputFile::Data *ofd,
697 Array<char>& tileBuffer,
698 int numScanLines,
699 vector<Int64>& bytesPerLine)
700 {
701 //
702 // Convert the contents of a TiledOutputFile's tileBuffer from the
703 // machine's native representation to Xdr format. This function is called
704 // by writeTile(), below, if the compressor wanted its input pixel data
705 // in the machine's native format, but then failed to compress the data
706 // (most compressors will expand rather than compress random input data).
707 //
708 // Note that this routine assumes that the machine's native representation
709 // of the pixel data has the same size as the Xdr representation. This
710 // makes it possible to convert the pixel data in place, without an
711 // intermediate temporary buffer.
712 //
713
714 //
715 // Set these to point to the start of the tile.
716 // We will write to toPtr, and read from fromPtr.
717 //
718
719 char *writePtr = tileBuffer;
720 const char *readPtr = writePtr;
721
722 //
723 // Iterate over all scan lines in the tile.
724 //
725
726 for (int y = 0; y < numScanLines; ++y)
727 {
728 //
729 // Iterate over all slices in the file.
730 //
731
732 for (unsigned int i = 0; i < ofd->slices.size(); ++i)
733 {
734 const TOutSliceInfo &slice = *ofd->slices[i];
735
736 //
737 // Convert the samples in place.
738 //
739
740 Int64 numPixelsPerScanLine = bytesPerLine[y];
741
742 convertInPlace (writePtr, readPtr, slice.type,
743 numPixelsPerScanLine);
744 }
745 }
746
747 #ifdef DEBUG
748
749 assert (writePtr == readPtr);
750
751 #endif
752 }
753
754
755 //
756 // A TileBufferTask encapsulates the task of copying a tile from
757 // the user's framebuffer into a LineBuffer and compressing the data
758 // if necessary.
759 //
760
761 class TileBufferTask: public Task
762 {
763 public:
764
765 TileBufferTask (TaskGroup *group,
766 DeepTiledOutputFile::Data *ofd,
767 int number,
768 int dx, int dy,
769 int lx, int ly);
770
771 virtual ~TileBufferTask ();
772
773 virtual void execute ();
774
775 private:
776
777 DeepTiledOutputFile::Data * _ofd;
778 TileBuffer * _tileBuffer;
779 };
780
781
TileBufferTask(TaskGroup * group,DeepTiledOutputFile::Data * ofd,int number,int dx,int dy,int lx,int ly)782 TileBufferTask::TileBufferTask
783 (TaskGroup *group,
784 DeepTiledOutputFile::Data *ofd,
785 int number,
786 int dx, int dy,
787 int lx, int ly)
788 :
789 Task (group),
790 _ofd (ofd),
791 _tileBuffer (_ofd->getTileBuffer (number))
792 {
793 //
794 // Wait for the tileBuffer to become available
795 //
796
797 _tileBuffer->wait ();
798 _tileBuffer->tileCoord = TileCoord (dx, dy, lx, ly);
799 }
800
801
~TileBufferTask()802 TileBufferTask::~TileBufferTask ()
803 {
804 //
805 // Signal that the tile buffer is now free
806 //
807
808 _tileBuffer->post ();
809 }
810
811
812 void
execute()813 TileBufferTask::execute ()
814 {
815 try
816 {
817 //
818 // First copy the pixel data from the frame buffer
819 // into the tile buffer
820 //
821 // Convert one tile's worth of pixel data to
822 // a machine-independent representation, and store
823 // the result in _tileBuffer->buffer.
824 //
825
826 Box2i tileRange = OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile (
827 _ofd->tileDesc,
828 _ofd->minX, _ofd->maxX,
829 _ofd->minY, _ofd->maxY,
830 _tileBuffer->tileCoord.dx,
831 _tileBuffer->tileCoord.dy,
832 _tileBuffer->tileCoord.lx,
833 _tileBuffer->tileCoord.ly);
834
835 int numScanLines = tileRange.max.y - tileRange.min.y + 1;
836 // int numPixelsPerScanLine = tileRange.max.x - tileRange.min.x + 1;
837
838 //
839 // Get the bytes for each line.
840 //
841
842 vector<Int64> bytesPerLine(_ofd->tileDesc.ySize);
843 vector<int> xOffsets(_ofd->slices.size());
844 vector<int> yOffsets(_ofd->slices.size());
845 for (size_t i = 0; i < _ofd->slices.size(); i++)
846 {
847 const TOutSliceInfo &slice = *_ofd->slices[i];
848 xOffsets[i] = slice.xTileCoords * tileRange.min.x;
849 yOffsets[i] = slice.yTileCoords * tileRange.min.y;
850 }
851
852 calculateBytesPerLine(_ofd->header,
853 _ofd->sampleCountSliceBase,
854 _ofd->sampleCountXStride,
855 _ofd->sampleCountYStride,
856 tileRange.min.x, tileRange.max.x,
857 tileRange.min.y, tileRange.max.y,
858 xOffsets, yOffsets,
859 bytesPerLine);
860
861 //
862 // Allocate the memory for internal buffer.
863 // (TODO) more efficient memory management?
864 //
865
866 Int64 totalBytes = 0;
867 Int64 maxBytesPerTileLine = 0;
868 for (size_t i = 0; i < bytesPerLine.size(); i++)
869 {
870 totalBytes += bytesPerLine[i];
871 if (bytesPerLine[i] > maxBytesPerTileLine)
872 maxBytesPerTileLine = bytesPerLine[i];
873 }
874 _tileBuffer->buffer.resizeErase(totalBytes);
875
876 char *writePtr = _tileBuffer->buffer;
877
878 //
879 // Iterate over the scan lines in the tile.
880 //
881
882 int xOffsetForSampleCount =
883 (_ofd->sampleCountXTileCoords == 0) ? 0 : tileRange.min.x;
884 int yOffsetForSampleCount =
885 (_ofd->sampleCountYTileCoords == 0) ? 0 : tileRange.min.y;
886
887 for (int y = tileRange.min.y; y <= tileRange.max.y; ++y)
888 {
889 //
890 // Iterate over all image channels.
891 //
892
893 for (unsigned int i = 0; i < _ofd->slices.size(); ++i)
894 {
895 const TOutSliceInfo &slice = *_ofd->slices[i];
896
897
898 //
899 // Fill the tile buffer with pixel data.
900 //
901
902 if (slice.zero)
903 {
904 //
905 // The frame buffer contains no data for this channel.
906 // Store zeroes in _data->tileBuffer.
907 //
908
909 fillChannelWithZeroes (writePtr, _ofd->format, slice.type,
910 bytesPerLine[y - tileRange.min.y]);
911 }
912 else
913 {
914 //
915 // The frame buffer contains data for this channel.
916 //
917
918
919 int xOffsetForData = slice.xTileCoords ? tileRange.min.x : 0;
920 int yOffsetForData = slice.yTileCoords ? tileRange.min.y : 0;
921
922 // (TOOD) treat sample count offsets differently.
923 copyFromDeepFrameBuffer (writePtr,
924 slice.base,
925 _ofd->sampleCountSliceBase,
926 _ofd->sampleCountXStride,
927 _ofd->sampleCountYStride,
928 y,
929 tileRange.min.x,
930 tileRange.max.x,
931 xOffsetForSampleCount,
932 yOffsetForSampleCount,
933 xOffsetForData,
934 yOffsetForData,
935 slice.sampleStride,
936 slice.xStride,
937 slice.yStride,
938 _ofd->format,
939 slice.type);
940 #if defined(DEBUG)
941 assert(writePtr-_tileBuffer->buffer<=totalBytes);
942 #endif
943 }
944 }
945 }
946
947 //
948 // Compress the pixel sample count table.
949 //
950
951 char* ptr = _tileBuffer->sampleCountTableBuffer;
952 Int64 tableDataSize = 0;
953 for (int i = tileRange.min.y; i <= tileRange.max.y; i++)
954 {
955 int count = 0;
956 for (int j = tileRange.min.x; j <= tileRange.max.x; j++)
957 {
958 count += _ofd->getSampleCount(j - xOffsetForSampleCount,
959 i - yOffsetForSampleCount);
960 Xdr::write <CharPtrIO> (ptr, count);
961 tableDataSize += sizeof (int);
962 }
963 }
964
965 if(_tileBuffer->sampleCountTableCompressor)
966 {
967 _tileBuffer->sampleCountTableSize =
968 _tileBuffer->sampleCountTableCompressor->compress (
969 _tileBuffer->sampleCountTableBuffer,
970 tableDataSize,
971 tileRange.min.y,
972 _tileBuffer->sampleCountTablePtr);
973 }
974
975 //
976 // If we can't make data shrink (or compression was disabled), then just use the raw data.
977 //
978
979 if ( ! _tileBuffer->sampleCountTableCompressor ||
980 _tileBuffer->sampleCountTableSize >= _ofd->maxSampleCountTableSize)
981 {
982 _tileBuffer->sampleCountTableSize = _ofd->maxSampleCountTableSize;
983 _tileBuffer->sampleCountTablePtr = _tileBuffer->sampleCountTableBuffer;
984 }
985
986 //
987 // Compress the contents of the tileBuffer,
988 // and store the compressed data in the output file.
989 //
990
991 _tileBuffer->dataSize = writePtr - _tileBuffer->buffer;
992 _tileBuffer->uncompressedSize = _tileBuffer->dataSize;
993 _tileBuffer->dataPtr = _tileBuffer->buffer;
994
995 // (TODO) don't do this all the time.
996 if (_tileBuffer->compressor != 0)
997 delete _tileBuffer->compressor;
998 _tileBuffer->compressor = newTileCompressor
999 (_ofd->header.compression(),
1000 maxBytesPerTileLine,
1001 _ofd->tileDesc.ySize,
1002 _ofd->header);
1003
1004 if (_tileBuffer->compressor)
1005 {
1006 const char *compPtr;
1007
1008 Int64 compSize = _tileBuffer->compressor->compressTile
1009 (_tileBuffer->dataPtr,
1010 _tileBuffer->dataSize,
1011 tileRange, compPtr);
1012
1013 if (compSize < _tileBuffer->dataSize)
1014 {
1015 _tileBuffer->dataSize = compSize;
1016 _tileBuffer->dataPtr = compPtr;
1017 }
1018 else if (_ofd->format == Compressor::NATIVE)
1019 {
1020 //
1021 // The data did not shrink during compression, but
1022 // we cannot write to the file using native format,
1023 // so we need to convert the lineBuffer to Xdr.
1024 //
1025
1026 convertToXdr (_ofd, _tileBuffer->buffer, numScanLines,
1027 bytesPerLine);
1028 }
1029 }
1030 }
1031 catch (std::exception &e)
1032 {
1033 if (!_tileBuffer->hasException)
1034 {
1035 _tileBuffer->exception = e.what ();
1036 _tileBuffer->hasException = true;
1037 }
1038 }
1039 catch (...)
1040 {
1041 if (!_tileBuffer->hasException)
1042 {
1043 _tileBuffer->exception = "unrecognized exception";
1044 _tileBuffer->hasException = true;
1045 }
1046 }
1047 }
1048
1049 } // namespace
1050
1051
DeepTiledOutputFile(const char fileName[],const Header & header,int numThreads)1052 DeepTiledOutputFile::DeepTiledOutputFile
1053 (const char fileName[],
1054 const Header &header,
1055 int numThreads)
1056 :
1057 _data (new Data (numThreads))
1058
1059 {
1060 _data->_streamData=new OutputStreamMutex();
1061 _data->_deleteStream =true;
1062 try
1063 {
1064 header.sanityCheck (true);
1065 _data->_streamData->os = new StdOFStream (fileName);
1066 initialize (header);
1067 _data->_streamData->currentPosition = _data->_streamData->os->tellp();
1068
1069 // Write header and empty offset table to the file.
1070 writeMagicNumberAndVersionField(*_data->_streamData->os, _data->header);
1071 _data->previewPosition = _data->header.writeTo (*_data->_streamData->os, true);
1072 _data->tileOffsetsPosition = _data->tileOffsets.writeTo (*_data->_streamData->os);
1073 _data->multipart = false;
1074 }
1075 catch (IEX_NAMESPACE::BaseExc &e)
1076 {
1077 if (_data && _data->_streamData && _data->_streamData->os) delete _data->_streamData->os;
1078 if (_data && _data->_streamData) delete _data->_streamData;
1079 if (_data) delete _data;
1080
1081 REPLACE_EXC (e, "Cannot open image file "
1082 "\"" << fileName << "\". " << e);
1083 throw;
1084 }
1085 catch (...)
1086 {
1087 if (_data && _data->_streamData && _data->_streamData->os) delete _data->_streamData->os;
1088 if (_data->_streamData) delete _data->_streamData;
1089 if (_data) delete _data;
1090
1091 throw;
1092 }
1093 }
1094
1095
DeepTiledOutputFile(OPENEXR_IMF_INTERNAL_NAMESPACE::OStream & os,const Header & header,int numThreads)1096 DeepTiledOutputFile::DeepTiledOutputFile
1097 (OPENEXR_IMF_INTERNAL_NAMESPACE::OStream &os,
1098 const Header &header,
1099 int numThreads)
1100 :
1101 _data (new Data (numThreads))
1102 {
1103 _data->_streamData=new OutputStreamMutex();
1104 _data->_deleteStream=false;
1105
1106 try
1107 {
1108 header.sanityCheck(true);
1109 _data->_streamData->os = &os;
1110 initialize (header);
1111 _data->_streamData->currentPosition = _data->_streamData->os->tellp();
1112
1113 // Write header and empty offset table to the file.
1114 writeMagicNumberAndVersionField(*_data->_streamData->os, _data->header);
1115 _data->previewPosition = _data->header.writeTo (*_data->_streamData->os, true);
1116 _data->tileOffsetsPosition = _data->tileOffsets.writeTo (*_data->_streamData->os);
1117 _data->multipart = false;
1118 }
1119 catch (IEX_NAMESPACE::BaseExc &e)
1120 {
1121 if (_data && _data->_streamData) delete _data->_streamData;
1122 if (_data) delete _data;
1123
1124 REPLACE_EXC (e, "Cannot open image file "
1125 "\"" << os.fileName() << "\". " << e);
1126 throw;
1127 }
1128 catch (...)
1129 {
1130 if (_data && _data->_streamData) delete _data->_streamData;
1131 if (_data) delete _data;
1132
1133 throw;
1134 }
1135 }
1136
DeepTiledOutputFile(const OutputPartData * part)1137 DeepTiledOutputFile::DeepTiledOutputFile(const OutputPartData* part)
1138 {
1139
1140 try
1141 {
1142 if (part->header.type() != DEEPTILE)
1143 throw IEX_NAMESPACE::ArgExc("Can't build a DeepTiledOutputFile from "
1144 "a type-mismatched part.");
1145
1146 _data = new Data (part->numThreads);
1147 _data->_streamData=part->mutex;
1148 _data->_deleteStream=false;
1149 initialize(part->header);
1150 _data->partNumber = part->partNumber;
1151 _data->tileOffsetsPosition = part->chunkOffsetTablePosition;
1152 _data->previewPosition = part->previewPosition;
1153 _data->multipart = part->multipart;
1154 }
1155 catch (IEX_NAMESPACE::BaseExc &e)
1156 {
1157 if (_data) delete _data;
1158
1159 REPLACE_EXC (e, "Cannot initialize output part "
1160 "\"" << part->partNumber << "\". " << e);
1161 throw;
1162 }
1163 catch (...)
1164 {
1165 if (_data) delete _data;
1166
1167 throw;
1168 }
1169 }
1170
1171 void
initialize(const Header & header)1172 DeepTiledOutputFile::initialize (const Header &header)
1173 {
1174 _data->header = header;
1175 _data->header.setType(DEEPTILE);
1176 _data->lineOrder = _data->header.lineOrder();
1177
1178 //
1179 // Check that the file is indeed tiled
1180 //
1181
1182 _data->tileDesc = _data->header.tileDescription();
1183
1184 //
1185 // Save the dataWindow information
1186 //
1187
1188 const Box2i &dataWindow = _data->header.dataWindow();
1189 _data->minX = dataWindow.min.x;
1190 _data->maxX = dataWindow.max.x;
1191 _data->minY = dataWindow.min.y;
1192 _data->maxY = dataWindow.max.y;
1193
1194 //
1195 // Precompute level and tile information to speed up utility functions
1196 //
1197
1198 precalculateTileInfo (_data->tileDesc,
1199 _data->minX, _data->maxX,
1200 _data->minY, _data->maxY,
1201 _data->numXTiles, _data->numYTiles,
1202 _data->numXLevels, _data->numYLevels);
1203
1204 //
1205 // Determine the first tile coordinate that we will be writing
1206 // if the file is not RANDOM_Y.
1207 //
1208
1209 _data->nextTileToWrite = (_data->lineOrder == INCREASING_Y)?
1210 TileCoord (0, 0, 0, 0):
1211 TileCoord (0, _data->numYTiles[0] - 1, 0, 0);
1212
1213 Compressor* compressor = newTileCompressor
1214 (_data->header.compression(),
1215 0,
1216 _data->tileDesc.ySize,
1217 _data->header);
1218
1219 _data->format = defaultFormat (compressor);
1220
1221 if (compressor != 0)
1222 delete compressor;
1223
1224 _data->tileOffsets = TileOffsets (_data->tileDesc.mode,
1225 _data->numXLevels,
1226 _data->numYLevels,
1227 _data->numXTiles,
1228 _data->numYTiles);
1229
1230 //ignore the existing value of chunkCount - correct it if it's wrong
1231 _data->header.setChunkCount(getChunkOffsetTableSize(_data->header,true));
1232
1233 _data->maxSampleCountTableSize = _data->tileDesc.ySize *
1234 _data->tileDesc.xSize *
1235 sizeof(int);
1236
1237
1238 for (size_t i = 0; i < _data->tileBuffers.size(); i++)
1239 {
1240 _data->tileBuffers[i] = new TileBuffer ();
1241
1242 _data->tileBuffers[i]->sampleCountTableBuffer.
1243 resizeErase(_data->maxSampleCountTableSize);
1244
1245 char * p = &(_data->tileBuffers[i]->sampleCountTableBuffer[0]);
1246 memset (p, 0, _data->maxSampleCountTableSize);
1247
1248 _data->tileBuffers[i]->sampleCountTableCompressor =
1249 newCompressor (_data->header.compression(),
1250 _data->maxSampleCountTableSize,
1251 _data->header);
1252 }
1253 }
1254
1255
~DeepTiledOutputFile()1256 DeepTiledOutputFile::~DeepTiledOutputFile ()
1257 {
1258 if (_data)
1259 {
1260 {
1261 Lock lock(*_data->_streamData);
1262 Int64 originalPosition = _data->_streamData->os->tellp();
1263
1264 if (_data->tileOffsetsPosition > 0)
1265 {
1266 try
1267 {
1268 _data->_streamData->os->seekp (_data->tileOffsetsPosition);
1269 _data->tileOffsets.writeTo (*_data->_streamData->os);
1270
1271 //
1272 // Restore the original position.
1273 //
1274 _data->_streamData->os->seekp (originalPosition);
1275 }
1276 catch (...)
1277 {
1278 //
1279 // We cannot safely throw any exceptions from here.
1280 // This destructor may have been called because the
1281 // stack is currently being unwound for another
1282 // exception.
1283 //
1284 }
1285 }
1286 }
1287
1288 if (_data->_deleteStream && _data->_streamData)
1289 delete _data->_streamData->os;
1290
1291 //
1292 // (TODO) we should have a way to tell if the stream data is owned by
1293 // this file or by a parent multipart file.
1294 //
1295
1296 if (_data->partNumber == -1 && _data->_streamData)
1297 delete _data->_streamData;
1298
1299 delete _data;
1300 }
1301 }
1302
1303
1304 const char *
fileName() const1305 DeepTiledOutputFile::fileName () const
1306 {
1307 return _data->_streamData->os->fileName();
1308 }
1309
1310
1311 const Header &
header() const1312 DeepTiledOutputFile::header () const
1313 {
1314 return _data->header;
1315 }
1316
1317
1318 void
setFrameBuffer(const DeepFrameBuffer & frameBuffer)1319 DeepTiledOutputFile::setFrameBuffer (const DeepFrameBuffer &frameBuffer)
1320 {
1321 Lock lock (*_data->_streamData);
1322
1323 //
1324 // Check if the new frame buffer descriptor
1325 // is compatible with the image file header.
1326 //
1327
1328 const ChannelList &channels = _data->header.channels();
1329
1330 for (ChannelList::ConstIterator i = channels.begin();
1331 i != channels.end();
1332 ++i)
1333 {
1334 DeepFrameBuffer::ConstIterator j = frameBuffer.find (i.name());
1335
1336 if (j == frameBuffer.end())
1337 continue;
1338
1339 if (i.channel().type != j.slice().type)
1340 THROW (IEX_NAMESPACE::ArgExc, "Pixel type of \"" << i.name() << "\" channel "
1341 "of output file \"" << fileName() << "\" is "
1342 "not compatible with the frame buffer's "
1343 "pixel type.");
1344
1345 if (j.slice().xSampling != 1 || j.slice().ySampling != 1)
1346 THROW (IEX_NAMESPACE::ArgExc, "All channels in a tiled file must have"
1347 "sampling (1,1).");
1348 }
1349
1350 //
1351 // Store the pixel sample count table.
1352 //
1353
1354 const Slice& sampleCountSlice = frameBuffer.getSampleCountSlice();
1355 if (sampleCountSlice.base == 0)
1356 {
1357 throw IEX_NAMESPACE::ArgExc ("Invalid base pointer, please set a proper sample count slice.");
1358 }
1359 else
1360 {
1361 _data->sampleCountSliceBase = sampleCountSlice.base;
1362 _data->sampleCountXStride = sampleCountSlice.xStride;
1363 _data->sampleCountYStride = sampleCountSlice.yStride;
1364 _data->sampleCountXTileCoords = sampleCountSlice.xTileCoords;
1365 _data->sampleCountYTileCoords = sampleCountSlice.yTileCoords;
1366 }
1367
1368 //
1369 // Initialize slice table for writePixels().
1370 // Pixel sample count slice is not presented in the header,
1371 // so it wouldn't be added here.
1372 // Store the pixel base pointer table.
1373 //
1374
1375 vector<TOutSliceInfo*> slices;
1376
1377 for (ChannelList::ConstIterator i = channels.begin();
1378 i != channels.end();
1379 ++i)
1380 {
1381 DeepFrameBuffer::ConstIterator j = frameBuffer.find (i.name());
1382
1383 if (j == frameBuffer.end())
1384 {
1385 //
1386 // Channel i is not present in the frame buffer.
1387 // In the file, channel i will contain only zeroes.
1388 //
1389
1390 slices.push_back (new TOutSliceInfo (i.channel().type,
1391 0, // sampleStride,
1392 0, // xStride
1393 0, // yStride
1394 true)); // zero
1395 }
1396 else
1397 {
1398 //
1399 // Channel i is present in the frame buffer.
1400 //
1401
1402 slices.push_back (new TOutSliceInfo (j.slice().type,
1403 j.slice().sampleStride,
1404 j.slice().xStride,
1405 j.slice().yStride,
1406 false, // zero
1407 (j.slice().xTileCoords)? 1: 0,
1408 (j.slice().yTileCoords)? 1: 0));
1409
1410 TOutSliceInfo* slice = slices.back();
1411 slice->base = j.slice().base;
1412
1413 }
1414 }
1415
1416 //
1417 // Store the new frame buffer.
1418 //
1419
1420 _data->frameBuffer = frameBuffer;
1421
1422 for (size_t i = 0; i < _data->slices.size(); i++)
1423 delete _data->slices[i];
1424 _data->slices = slices;
1425 }
1426
1427
1428 const DeepFrameBuffer &
frameBuffer() const1429 DeepTiledOutputFile::frameBuffer () const
1430 {
1431 Lock lock (*_data->_streamData);
1432 return _data->frameBuffer;
1433 }
1434
1435
1436 void
writeTiles(int dx1,int dx2,int dy1,int dy2,int lx,int ly)1437 DeepTiledOutputFile::writeTiles (int dx1, int dx2, int dy1, int dy2,
1438 int lx, int ly)
1439 {
1440 try
1441 {
1442 Lock lock (*_data->_streamData);
1443
1444 if (_data->slices.size() == 0)
1445 throw IEX_NAMESPACE::ArgExc ("No frame buffer specified "
1446 "as pixel data source.");
1447
1448 if (!isValidTile (dx1, dy1, lx, ly) || !isValidTile (dx2, dy2, lx, ly))
1449 throw IEX_NAMESPACE::ArgExc ("Tile coordinates are invalid.");
1450
1451 if (!isValidLevel (lx, ly))
1452 THROW (IEX_NAMESPACE::ArgExc,
1453 "Level coordinate "
1454 "(" << lx << ", " << ly << ") "
1455 "is invalid.");
1456 //
1457 // Determine the first and last tile coordinates in both dimensions
1458 // based on the file's lineOrder
1459 //
1460
1461 if (dx1 > dx2)
1462 swap (dx1, dx2);
1463
1464 if (dy1 > dy2)
1465 swap (dy1, dy2);
1466
1467 int dyStart = dy1;
1468 int dyStop = dy2 + 1;
1469 int dY = 1;
1470
1471 if (_data->lineOrder == DECREASING_Y)
1472 {
1473 dyStart = dy2;
1474 dyStop = dy1 - 1;
1475 dY = -1;
1476 }
1477
1478 int numTiles = (dx2 - dx1 + 1) * (dy2 - dy1 + 1);
1479 int numTasks = min ((int)_data->tileBuffers.size(), numTiles);
1480
1481 //
1482 // Create a task group for all tile buffer tasks. When the
1483 // task group goes out of scope, the destructor waits until
1484 // all tasks are complete.
1485 //
1486
1487 {
1488 TaskGroup taskGroup;
1489
1490 //
1491 // Add in the initial compression tasks to the thread pool
1492 //
1493
1494 int nextCompBuffer = 0;
1495 int dxComp = dx1;
1496 int dyComp = dyStart;
1497
1498 while (nextCompBuffer < numTasks)
1499 {
1500 ThreadPool::addGlobalTask (new TileBufferTask (&taskGroup,
1501 _data,
1502 nextCompBuffer++,
1503 dxComp, dyComp,
1504 lx, ly));
1505 dxComp++;
1506
1507 if (dxComp > dx2)
1508 {
1509 dxComp = dx1;
1510 dyComp += dY;
1511 }
1512 }
1513
1514 //
1515 // Write the compressed buffers and add in more compression
1516 // tasks until done
1517 //
1518
1519 int nextWriteBuffer = 0;
1520 int dxWrite = dx1;
1521 int dyWrite = dyStart;
1522
1523 while (nextWriteBuffer < numTiles)
1524 {
1525 //
1526 // Wait until the nextWriteBuffer is ready to be written
1527 //
1528
1529 TileBuffer* writeBuffer =
1530 _data->getTileBuffer (nextWriteBuffer);
1531
1532 writeBuffer->wait();
1533
1534 //
1535 // Write the tilebuffer
1536 //
1537
1538 bufferedTileWrite ( _data, dxWrite, dyWrite, lx, ly,
1539 writeBuffer->dataPtr,
1540 writeBuffer->dataSize,
1541 writeBuffer->uncompressedSize,
1542 writeBuffer->sampleCountTablePtr,
1543 writeBuffer->sampleCountTableSize);
1544
1545 //
1546 // Release the lock on nextWriteBuffer
1547 //
1548
1549 writeBuffer->post();
1550
1551 //
1552 // If there are no more tileBuffers to compress, then
1553 // only continue to write out remaining tileBuffers,
1554 // otherwise keep adding compression tasks.
1555 //
1556
1557 if (nextCompBuffer < numTiles)
1558 {
1559 //
1560 // add nextCompBuffer as a compression Task
1561 //
1562
1563 ThreadPool::addGlobalTask
1564 (new TileBufferTask (&taskGroup,
1565 _data,
1566 nextCompBuffer,
1567 dxComp, dyComp,
1568 lx, ly));
1569 }
1570
1571 nextWriteBuffer++;
1572 dxWrite++;
1573
1574 if (dxWrite > dx2)
1575 {
1576 dxWrite = dx1;
1577 dyWrite += dY;
1578 }
1579
1580 nextCompBuffer++;
1581 dxComp++;
1582
1583 if (dxComp > dx2)
1584 {
1585 dxComp = dx1;
1586 dyComp += dY;
1587 }
1588 }
1589
1590 //
1591 // finish all tasks
1592 //
1593 }
1594
1595 //
1596 // Exeption handling:
1597 //
1598 // TileBufferTask::execute() may have encountered exceptions, but
1599 // those exceptions occurred in another thread, not in the thread
1600 // that is executing this call to TiledOutputFile::writeTiles().
1601 // TileBufferTask::execute() has caught all exceptions and stored
1602 // the exceptions' what() strings in the tile buffers.
1603 // Now we check if any tile buffer contains a stored exception; if
1604 // this is the case then we re-throw the exception in this thread.
1605 // (It is possible that multiple tile buffers contain stored
1606 // exceptions. We re-throw the first exception we find and
1607 // ignore all others.)
1608 //
1609
1610 const string *exception = 0;
1611
1612 for (size_t i = 0; i < _data->tileBuffers.size(); ++i)
1613 {
1614 TileBuffer *tileBuffer = _data->tileBuffers[i];
1615
1616 if (tileBuffer->hasException && !exception)
1617 exception = &tileBuffer->exception;
1618
1619 tileBuffer->hasException = false;
1620 }
1621
1622 if (exception)
1623 throw IEX_NAMESPACE::IoExc (*exception);
1624 }
1625 catch (IEX_NAMESPACE::BaseExc &e)
1626 {
1627 REPLACE_EXC (e, "Failed to write pixel data to image "
1628 "file \"" << fileName() << "\". " << e);
1629 throw;
1630 }
1631 }
1632
1633
1634 void
writeTiles(int dx1,int dxMax,int dyMin,int dyMax,int l)1635 DeepTiledOutputFile::writeTiles (int dx1, int dxMax, int dyMin, int dyMax, int l)
1636 {
1637 writeTiles (dx1, dxMax, dyMin, dyMax, l, l);
1638 }
1639
1640
1641 void
writeTile(int dx,int dy,int lx,int ly)1642 DeepTiledOutputFile::writeTile (int dx, int dy, int lx, int ly)
1643 {
1644 writeTiles (dx, dx, dy, dy, lx, ly);
1645 }
1646
1647
1648 void
writeTile(int dx,int dy,int l)1649 DeepTiledOutputFile::writeTile (int dx, int dy, int l)
1650 {
1651 writeTile(dx, dy, l, l);
1652 }
1653
1654
1655 void
copyPixels(DeepTiledInputFile & in)1656 DeepTiledOutputFile::copyPixels (DeepTiledInputFile &in)
1657 {
1658
1659 //
1660 // Check if this file's and and the InputFile's
1661 // headers are compatible.
1662 //
1663
1664 const Header &hdr = _data->header;
1665 const Header &inHdr = in.header();
1666
1667
1668
1669 if (!(hdr.tileDescription() == inHdr.tileDescription()))
1670 THROW (IEX_NAMESPACE::ArgExc, "Quick pixel copy from image "
1671 "file \"" << in.fileName() << "\" to image "
1672 "file \"" << fileName() << "\" failed. "
1673 "The files have different tile descriptions.");
1674
1675 if (!(hdr.dataWindow() == inHdr.dataWindow()))
1676 THROW (IEX_NAMESPACE::ArgExc, "Cannot copy pixels from image "
1677 "file \"" << in.fileName() << "\" to image "
1678 "file \"" << fileName() << "\". The "
1679 "files have different data windows.");
1680
1681 if (!(hdr.lineOrder() == inHdr.lineOrder()))
1682 THROW (IEX_NAMESPACE::ArgExc, "Quick pixel copy from image "
1683 "file \"" << in.fileName() << "\" to image "
1684 "file \"" << fileName() << "\" failed. "
1685 "The files have different line orders.");
1686
1687 if (!(hdr.compression() == inHdr.compression()))
1688 THROW (IEX_NAMESPACE::ArgExc, "Quick pixel copy from image "
1689 "file \"" << in.fileName() << "\" to image "
1690 "file \"" << fileName() << "\" failed. "
1691 "The files use different compression methods.");
1692
1693 if (!(hdr.channels() == inHdr.channels()))
1694 THROW (IEX_NAMESPACE::ArgExc, "Quick pixel copy from image "
1695 "file \"" << in.fileName() << "\" to image "
1696 "file \"" << fileName() << "\" "
1697 "failed. The files have different channel "
1698 "lists.");
1699
1700
1701 // Verify that no pixel data have been written to this file yet.
1702 //
1703
1704 if (!_data->tileOffsets.isEmpty())
1705 THROW (IEX_NAMESPACE::LogicExc, "Quick pixel copy from image "
1706 "file \"" << in.fileName() << "\" to image "
1707 "file \"" << _data->_streamData->os->fileName() << "\" "
1708 "failed. \"" << fileName() << "\" "
1709 "already contains pixel data.");
1710
1711
1712 int numAllTiles = in.totalTiles();
1713
1714 Lock lock (*_data->_streamData);
1715
1716 //
1717 // special handling for random tiles
1718 //
1719
1720 vector<int> dx_list(_data->lineOrder==RANDOM_Y ? numAllTiles : 1);
1721 vector<int> dy_list(_data->lineOrder==RANDOM_Y ? numAllTiles : 1);
1722 vector<int> lx_list(_data->lineOrder==RANDOM_Y ? numAllTiles : 1);
1723 vector<int> ly_list(_data->lineOrder==RANDOM_Y ? numAllTiles : 1);
1724
1725 if(_data->lineOrder==RANDOM_Y)
1726 {
1727 in.getTileOrder(&dx_list[0],&dy_list[0],&lx_list[0],&ly_list[0]);
1728 _data->nextTileToWrite.dx=dx_list[0];
1729 _data->nextTileToWrite.dy=dy_list[0];
1730 _data->nextTileToWrite.lx=lx_list[0];
1731 _data->nextTileToWrite.ly=ly_list[0];
1732 }
1733
1734
1735 vector<char> data(4096);
1736 for (int i = 0; i < numAllTiles; ++i)
1737 {
1738
1739 int dx = _data->nextTileToWrite.dx;
1740 int dy = _data->nextTileToWrite.dy;
1741 int lx = _data->nextTileToWrite.lx;
1742 int ly = _data->nextTileToWrite.ly;
1743
1744 Int64 dataSize = data.size();
1745
1746 in.rawTileData (dx, dy, lx, ly, &data[0], dataSize);
1747 if(dataSize>data.size())
1748 {
1749 data.resize(dataSize);
1750 in.rawTileData (dx, dy, lx, ly, &data[0], dataSize);
1751 }
1752 Int64 sampleCountTableSize = *(Int64 *)(&data[0] + 16);
1753 Int64 pixelDataSize = *(Int64 *)(&data[0] + 24);
1754 Int64 unpackedPixelDataSize = *(Int64 *)(&data[0] + 32);
1755 char * sampleCountTable = &data[0]+40;
1756 char * pixelData = sampleCountTable + sampleCountTableSize;
1757
1758 writeTileData (_data, dx, dy, lx, ly, pixelData, pixelDataSize,unpackedPixelDataSize,sampleCountTable,sampleCountTableSize);
1759
1760
1761 if(_data->lineOrder==RANDOM_Y)
1762 {
1763 if(i<numAllTiles-1)
1764 {
1765 _data->nextTileToWrite.dx=dx_list[i+1];
1766 _data->nextTileToWrite.dy=dy_list[i+1];
1767 _data->nextTileToWrite.lx=lx_list[i+1];
1768 _data->nextTileToWrite.ly=ly_list[i+1];
1769 }
1770 }else{
1771 _data->nextTileToWrite = _data->nextTileCoord (_data->nextTileToWrite);
1772 }
1773
1774 }
1775 }
1776
1777
1778 void
copyPixels(DeepTiledInputPart & in)1779 DeepTiledOutputFile::copyPixels (DeepTiledInputPart &in)
1780 {
1781 copyPixels(*in.file);
1782 }
1783
1784
1785 unsigned int
tileXSize() const1786 DeepTiledOutputFile::tileXSize () const
1787 {
1788 return _data->tileDesc.xSize;
1789 }
1790
1791
1792 unsigned int
tileYSize() const1793 DeepTiledOutputFile::tileYSize () const
1794 {
1795 return _data->tileDesc.ySize;
1796 }
1797
1798
1799 LevelMode
levelMode() const1800 DeepTiledOutputFile::levelMode () const
1801 {
1802 return _data->tileDesc.mode;
1803 }
1804
1805
1806 LevelRoundingMode
levelRoundingMode() const1807 DeepTiledOutputFile::levelRoundingMode () const
1808 {
1809 return _data->tileDesc.roundingMode;
1810 }
1811
1812
1813 int
numLevels() const1814 DeepTiledOutputFile::numLevels () const
1815 {
1816 if (levelMode() == RIPMAP_LEVELS)
1817 THROW (IEX_NAMESPACE::LogicExc, "Error calling numLevels() on image "
1818 "file \"" << fileName() << "\" "
1819 "(numLevels() is not defined for RIPMAPs).");
1820 return _data->numXLevels;
1821 }
1822
1823
1824 int
numXLevels() const1825 DeepTiledOutputFile::numXLevels () const
1826 {
1827 return _data->numXLevels;
1828 }
1829
1830
1831 int
numYLevels() const1832 DeepTiledOutputFile::numYLevels () const
1833 {
1834 return _data->numYLevels;
1835 }
1836
1837
1838 bool
isValidLevel(int lx,int ly) const1839 DeepTiledOutputFile::isValidLevel (int lx, int ly) const
1840 {
1841 if (lx < 0 || ly < 0)
1842 return false;
1843
1844 if (levelMode() == MIPMAP_LEVELS && lx != ly)
1845 return false;
1846
1847 if (lx >= numXLevels() || ly >= numYLevels())
1848 return false;
1849
1850 return true;
1851 }
1852
1853
1854 int
levelWidth(int lx) const1855 DeepTiledOutputFile::levelWidth (int lx) const
1856 {
1857 try
1858 {
1859 int retVal = levelSize (_data->minX, _data->maxX, lx,
1860 _data->tileDesc.roundingMode);
1861
1862 return retVal;
1863 }
1864 catch (IEX_NAMESPACE::BaseExc &e)
1865 {
1866 REPLACE_EXC (e, "Error calling levelWidth() on image "
1867 "file \"" << fileName() << "\". " << e);
1868 throw;
1869 }
1870 }
1871
1872
1873 int
levelHeight(int ly) const1874 DeepTiledOutputFile::levelHeight (int ly) const
1875 {
1876 try
1877 {
1878 return levelSize (_data->minY, _data->maxY, ly,
1879 _data->tileDesc.roundingMode);
1880 }
1881 catch (IEX_NAMESPACE::BaseExc &e)
1882 {
1883 REPLACE_EXC (e, "Error calling levelHeight() on image "
1884 "file \"" << fileName() << "\". " << e);
1885 throw;
1886 }
1887 }
1888
1889
1890 int
numXTiles(int lx) const1891 DeepTiledOutputFile::numXTiles (int lx) const
1892 {
1893 if (lx < 0 || lx >= _data->numXLevels)
1894 THROW (IEX_NAMESPACE::LogicExc, "Error calling numXTiles() on image "
1895 "file \"" << _data->_streamData->os->fileName() << "\" "
1896 "(Argument is not in valid range).");
1897
1898 return _data->numXTiles[lx];
1899 }
1900
1901
1902 int
numYTiles(int ly) const1903 DeepTiledOutputFile::numYTiles (int ly) const
1904 {
1905 if (ly < 0 || ly >= _data->numYLevels)
1906 THROW (IEX_NAMESPACE::LogicExc, "Error calling numXTiles() on image "
1907 "file \"" << _data->_streamData->os->fileName() << "\" "
1908 "(Argument is not in valid range).");
1909
1910 return _data->numYTiles[ly];
1911 }
1912
1913
1914 Box2i
dataWindowForLevel(int l) const1915 DeepTiledOutputFile::dataWindowForLevel (int l) const
1916 {
1917 return dataWindowForLevel (l, l);
1918 }
1919
1920
1921 Box2i
dataWindowForLevel(int lx,int ly) const1922 DeepTiledOutputFile::dataWindowForLevel (int lx, int ly) const
1923 {
1924 try
1925 {
1926 return OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForLevel (
1927 _data->tileDesc,
1928 _data->minX, _data->maxX,
1929 _data->minY, _data->maxY,
1930 lx, ly);
1931 }
1932 catch (IEX_NAMESPACE::BaseExc &e)
1933 {
1934 REPLACE_EXC (e, "Error calling dataWindowForLevel() on image "
1935 "file \"" << fileName() << "\". " << e);
1936 throw;
1937 }
1938 }
1939
1940
1941 Box2i
dataWindowForTile(int dx,int dy,int l) const1942 DeepTiledOutputFile::dataWindowForTile (int dx, int dy, int l) const
1943 {
1944 return dataWindowForTile (dx, dy, l, l);
1945 }
1946
1947
1948 Box2i
dataWindowForTile(int dx,int dy,int lx,int ly) const1949 DeepTiledOutputFile::dataWindowForTile (int dx, int dy, int lx, int ly) const
1950 {
1951 try
1952 {
1953 if (!isValidTile (dx, dy, lx, ly))
1954 throw IEX_NAMESPACE::ArgExc ("Arguments not in valid range.");
1955
1956 return OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile (
1957 _data->tileDesc,
1958 _data->minX, _data->maxX,
1959 _data->minY, _data->maxY,
1960 dx, dy,
1961 lx, ly);
1962 }
1963 catch (IEX_NAMESPACE::BaseExc &e)
1964 {
1965 REPLACE_EXC (e, "Error calling dataWindowForTile() on image "
1966 "file \"" << fileName() << "\". " << e);
1967 throw;
1968 }
1969 }
1970
1971
1972 bool
isValidTile(int dx,int dy,int lx,int ly) const1973 DeepTiledOutputFile::isValidTile (int dx, int dy, int lx, int ly) const
1974 {
1975 return ((lx < _data->numXLevels && lx >= 0) &&
1976 (ly < _data->numYLevels && ly >= 0) &&
1977 (dx < _data->numXTiles[lx] && dx >= 0) &&
1978 (dy < _data->numYTiles[ly] && dy >= 0));
1979 }
1980
1981
1982 void
updatePreviewImage(const PreviewRgba newPixels[])1983 DeepTiledOutputFile::updatePreviewImage (const PreviewRgba newPixels[])
1984 {
1985 Lock lock (*_data->_streamData);
1986
1987 if (_data->previewPosition <= 0)
1988 THROW (IEX_NAMESPACE::LogicExc, "Cannot update preview image pixels. "
1989 "File \"" << fileName() << "\" does not "
1990 "contain a preview image.");
1991
1992 //
1993 // Store the new pixels in the header's preview image attribute.
1994 //
1995
1996 PreviewImageAttribute &pia =
1997 _data->header.typedAttribute <PreviewImageAttribute> ("preview");
1998
1999 PreviewImage &pi = pia.value();
2000 PreviewRgba *pixels = pi.pixels();
2001 int numPixels = pi.width() * pi.height();
2002
2003 for (int i = 0; i < numPixels; ++i)
2004 pixels[i] = newPixels[i];
2005
2006 //
2007 // Save the current file position, jump to the position in
2008 // the file where the preview image starts, store the new
2009 // preview image, and jump back to the saved file position.
2010 //
2011
2012 Int64 savedPosition = _data->_streamData->os->tellp();
2013
2014 try
2015 {
2016 _data->_streamData->os->seekp (_data->previewPosition);
2017 pia.writeValueTo (*_data->_streamData->os, _data->version);
2018 _data->_streamData->os->seekp (savedPosition);
2019 }
2020 catch (IEX_NAMESPACE::BaseExc &e)
2021 {
2022 REPLACE_EXC (e, "Cannot update preview image pixels for "
2023 "file \"" << fileName() << "\". " << e);
2024 throw;
2025 }
2026 }
2027
2028
2029 void
breakTile(int dx,int dy,int lx,int ly,int offset,int length,char c)2030 DeepTiledOutputFile::breakTile
2031 (int dx, int dy,
2032 int lx, int ly,
2033 int offset,
2034 int length,
2035 char c)
2036 {
2037 Lock lock (*_data->_streamData);
2038
2039 Int64 position = _data->tileOffsets (dx, dy, lx, ly);
2040
2041 if (!position)
2042 THROW (IEX_NAMESPACE::ArgExc,
2043 "Cannot overwrite tile "
2044 "(" << dx << ", " << dy << ", " << lx << "," << ly << "). "
2045 "The tile has not yet been stored in "
2046 "file \"" << fileName() << "\".");
2047
2048 _data->_streamData->currentPosition = 0;
2049 _data->_streamData->os->seekp (position + offset);
2050
2051 for (int i = 0; i < length; ++i)
2052 _data->_streamData->os->write (&c, 1);
2053 }
2054
2055 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT
2056