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