1 ///////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2004, Industrial Light & Magic, a division of Lucas
4 // Digital Ltd. LLC
5 //
6 // All rights reserved.
7 //
8 // Redistribution and use in source and binary forms, with or without
9 // modification, are permitted provided that the following conditions are
10 // met:
11 // *       Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 // *       Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 // *       Neither the name of Industrial Light & Magic nor the names of
18 // its contributors may be used to endorse or promote products derived
19 // from this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 ///////////////////////////////////////////////////////////////////////////
34 
35 //-----------------------------------------------------------------------------
36 //
37 //	class TiledOutputFile
38 //
39 //-----------------------------------------------------------------------------
40 
41 #include <ImfTiledOutputFile.h>
42 #include <ImfTiledInputFile.h>
43 #include <ImfTiledInputPart.h>
44 #include <ImfInputFile.h>
45 #include <ImfInputPart.h>
46 #include <ImfTileDescriptionAttribute.h>
47 #include <ImfPreviewImageAttribute.h>
48 #include <ImfChannelList.h>
49 #include <ImfMisc.h>
50 #include <ImfTiledMisc.h>
51 #include <ImfStdIO.h>
52 #include <ImfCompressor.h>
53 #include "ImathBox.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 #include "IlmThreadPool.h"
61 #include "IlmThreadSemaphore.h"
62 #include "IlmThreadMutex.h"
63 #include "ImfOutputStreamMutex.h"
64 #include "ImfOutputPartData.h"
65 #include "Iex.h"
66 #include <string>
67 #include <vector>
68 #include <fstream>
69 #include <assert.h>
70 #include <map>
71 #include <algorithm>
72 
73 #include "ImfNamespace.h"
74 
75 
76 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
77 
78 using IMATH_NAMESPACE::Box2i;
79 using IMATH_NAMESPACE::V2i;
80 using std::string;
81 using std::vector;
82 using std::ofstream;
83 using std::map;
84 using std::min;
85 using std::max;
86 using std::swap;
87 using ILMTHREAD_NAMESPACE::Mutex;
88 using ILMTHREAD_NAMESPACE::Lock;
89 using ILMTHREAD_NAMESPACE::Semaphore;
90 using ILMTHREAD_NAMESPACE::Task;
91 using ILMTHREAD_NAMESPACE::TaskGroup;
92 using ILMTHREAD_NAMESPACE::ThreadPool;
93 
94 namespace {
95 
96 struct TOutSliceInfo
97 {
98     PixelType		type;
99     const char *	base;
100     size_t		xStride;
101     size_t		yStride;
102     bool		zero;
103     int                 xTileCoords;
104     int                 yTileCoords;
105 
106     TOutSliceInfo (PixelType type = HALF,
107 	           const char *base = 0,
108 	           size_t xStride = 0,
109 	           size_t yStride = 0,
110 	           bool zero = false,
111                    int xTileCoords = 0,
112                    int yTileCoords = 0);
113 };
114 
115 
TOutSliceInfo(PixelType t,const char * b,size_t xs,size_t ys,bool z,int xtc,int ytc)116 TOutSliceInfo::TOutSliceInfo (PixelType t,
117 		              const char *b,
118 			      size_t xs, size_t ys,
119 			      bool z,
120                               int xtc,
121                               int ytc)
122 :
123     type (t),
124     base (b),
125     xStride (xs),
126     yStride (ys),
127     zero (z),
128     xTileCoords (xtc),
129     yTileCoords (ytc)
130 {
131     // empty
132 }
133 
134 
135 struct TileCoord
136 {
137     int		dx;
138     int		dy;
139     int		lx;
140     int		ly;
141 
142 
TileCoord__anon4a5da71f0111::TileCoord143     TileCoord (int xTile = 0, int yTile = 0,
144 	       int xLevel = 0, int yLevel = 0)
145     :
146         dx (xTile),  dy (yTile),
147 	lx (xLevel), ly (yLevel)
148     {
149         // empty
150     }
151 
152 
153     bool
operator <__anon4a5da71f0111::TileCoord154     operator < (const TileCoord &other) const
155     {
156         return (ly < other.ly) ||
157 	       (ly == other.ly && lx < other.lx) ||
158 	       ((ly == other.ly && lx == other.lx) &&
159 		    ((dy < other.dy) || (dy == other.dy && dx < other.dx)));
160     }
161 
162 
163     bool
operator ==__anon4a5da71f0111::TileCoord164     operator == (const TileCoord &other) const
165     {
166         return lx == other.lx &&
167 	       ly == other.ly &&
168 	       dx == other.dx &&
169 	       dy == other.dy;
170     }
171 };
172 
173 
174 struct BufferedTile
175 {
176     char *	pixelData;
177     int		pixelDataSize;
178 
BufferedTile__anon4a5da71f0111::BufferedTile179     BufferedTile (const char *data, int size):
180 	pixelData (0),
181 	pixelDataSize(size)
182     {
183 	pixelData = new char[pixelDataSize];
184 	memcpy (pixelData, data, pixelDataSize);
185     }
186 
~BufferedTile__anon4a5da71f0111::BufferedTile187     ~BufferedTile()
188     {
189 	delete [] pixelData;
190     }
191 };
192 
193 
194 typedef map <TileCoord, BufferedTile *> TileMap;
195 
196 
197 struct TileBuffer
198 {
199     Array<char>		buffer;
200     const char *	dataPtr;
201     int			dataSize;
202     Compressor *	compressor;
203     TileCoord		tileCoord;
204     bool		hasException;
205     string		exception;
206 
207      TileBuffer (Compressor *comp);
208     ~TileBuffer ();
209 
wait__anon4a5da71f0111::TileBuffer210     inline void		wait () {_sem.wait();}
post__anon4a5da71f0111::TileBuffer211     inline void		post () {_sem.post();}
212 
213   protected:
214 
215     Semaphore		_sem;
216 };
217 
218 
TileBuffer(Compressor * comp)219 TileBuffer::TileBuffer (Compressor *comp):
220     dataPtr (0),
221     dataSize (0),
222     compressor (comp),
223     hasException (false),
224     exception (),
225     _sem (1)
226 {
227     // empty
228 }
229 
230 
~TileBuffer()231 TileBuffer::~TileBuffer ()
232 {
233     delete compressor;
234 }
235 
236 
237 } // namespace
238 
239 
240 struct TiledOutputFile::Data
241 {
242     Header		header;			// the image header
243     int			version;		// file format version
244     bool                multipart;              // part came from a multipart file
245     TileDescription	tileDesc;		// describes the tile layout
246     FrameBuffer		frameBuffer;		// framebuffer to write into
247     Int64		previewPosition;
248     LineOrder		lineOrder;		// the file's lineorder
249     int			minX;			// data window's min x coord
250     int			maxX;			// data window's max x coord
251     int			minY;			// data window's min y coord
252     int			maxY;			// data window's max x coord
253 
254     int			numXLevels;		// number of x levels
255     int			numYLevels;		// number of y levels
256     int *		numXTiles;		// number of x tiles at a level
257     int *		numYTiles;		// number of y tiles at a level
258 
259     TileOffsets		tileOffsets;		// stores offsets in file for
260 						// each tile
261 
262     Compressor::Format	format;			// compressor's data format
263     vector<TOutSliceInfo> slices;		// info about channels in file
264 
265     size_t		maxBytesPerTileLine;	// combined size of a tile line
266 						// over all channels
267 
268 
269     vector<TileBuffer*> tileBuffers;
270     size_t		tileBufferSize;         // size of a tile buffer
271 
272     Int64		tileOffsetsPosition;	// position of the tile index
273 
274     TileMap		tileMap;
275     TileCoord		nextTileToWrite;
276 
277     int                 partNumber;             // the output part number
278 
279      Data (int numThreads);
280     ~Data ();
281 
282     inline TileBuffer *	getTileBuffer (int number);
283     						// hash function from tile
284 						// buffer coords into our
285 						// vector of tile buffers
286 
287     TileCoord		nextTileCoord (const TileCoord &a);
288 };
289 
290 
Data(int numThreads)291 TiledOutputFile::Data::Data (int numThreads):
292     multipart(false),
293     numXTiles(0),
294     numYTiles(0),
295     tileOffsetsPosition (0),
296     partNumber(-1)
297 {
298     //
299     // We need at least one tileBuffer, but if threading is used,
300     // to keep n threads busy we need 2*n tileBuffers
301     //
302 
303     tileBuffers.resize (max (1, 2 * numThreads));
304 }
305 
306 
~Data()307 TiledOutputFile::Data::~Data ()
308 {
309     delete [] numXTiles;
310     delete [] numYTiles;
311 
312     //
313     // Delete all the tile buffers, if any still happen to exist
314     //
315 
316     for (TileMap::iterator i = tileMap.begin(); i != tileMap.end(); ++i)
317 	delete i->second;
318 
319     for (size_t i = 0; i < tileBuffers.size(); i++)
320         delete tileBuffers[i];
321 }
322 
323 
324 TileBuffer*
getTileBuffer(int number)325 TiledOutputFile::Data::getTileBuffer (int number)
326 {
327     return tileBuffers[number % tileBuffers.size()];
328 }
329 
330 
331 TileCoord
nextTileCoord(const TileCoord & a)332 TiledOutputFile::Data::nextTileCoord (const TileCoord &a)
333 {
334     TileCoord b = a;
335 
336     if (lineOrder == INCREASING_Y)
337     {
338         b.dx++;
339 
340         if (b.dx >= numXTiles[b.lx])
341         {
342             b.dx = 0;
343             b.dy++;
344 
345             if (b.dy >= numYTiles[b.ly])
346             {
347 		//
348 		// the next tile is in the next level
349 		//
350 
351                 b.dy = 0;
352 
353                 switch (tileDesc.mode)
354                 {
355                   case ONE_LEVEL:
356                   case MIPMAP_LEVELS:
357 
358                     b.lx++;
359                     b.ly++;
360                     break;
361 
362                   case RIPMAP_LEVELS:
363 
364                     b.lx++;
365 
366                     if (b.lx >= numXLevels)
367                     {
368                         b.lx = 0;
369                         b.ly++;
370 
371 			#ifdef DEBUG
372 			    assert (b.ly <= numYLevels);
373 			#endif
374                     }
375                     break;
376                   case  NUM_LEVELMODES:
377                       throw(IEX_NAMESPACE::ArgExc("Invalid tile description"));
378 
379                 }
380             }
381         }
382     }
383     else if (lineOrder == DECREASING_Y)
384     {
385         b.dx++;
386 
387         if (b.dx >= numXTiles[b.lx])
388         {
389             b.dx = 0;
390             b.dy--;
391 
392             if (b.dy < 0)
393             {
394 		//
395 		// the next tile is in the next level
396 		//
397 
398                 switch (tileDesc.mode)
399                 {
400                   case ONE_LEVEL:
401                   case MIPMAP_LEVELS:
402 
403                     b.lx++;
404                     b.ly++;
405                     break;
406 
407                   case RIPMAP_LEVELS:
408 
409                     b.lx++;
410 
411                     if (b.lx >= numXLevels)
412                     {
413                         b.lx = 0;
414                         b.ly++;
415 
416 			#ifdef DEBUG
417 			    assert (b.ly <= numYLevels);
418 			#endif
419                     }
420                     break;
421                   case  NUM_LEVELMODES:
422                       throw(IEX_NAMESPACE::ArgExc("Invalid tile description"));
423 
424                 }
425 
426 		if (b.ly < numYLevels)
427 		    b.dy = numYTiles[b.ly] - 1;
428             }
429         }
430     }
431 
432     return b;
433 }
434 
435 
436 namespace {
437 
438 void
writeTileData(OutputStreamMutex * streamData,TiledOutputFile::Data * ofd,int dx,int dy,int lx,int ly,const char pixelData[],int pixelDataSize)439 writeTileData (OutputStreamMutex *streamData,
440                TiledOutputFile::Data *ofd,
441                int dx, int dy,
442 	       int lx, int ly,
443                const char pixelData[],
444                int pixelDataSize)
445 {
446     //
447     // Store a block of pixel data in the output file, and try
448     // to keep track of the current writing position the file,
449     // without calling tellp() (tellp() can be fairly expensive).
450     //
451 
452     Int64 currentPosition = streamData->currentPosition;
453     streamData->currentPosition = 0;
454 
455     if (currentPosition == 0)
456         currentPosition = streamData->os->tellp();
457 
458     ofd->tileOffsets (dx, dy, lx, ly) = currentPosition;
459 
460     #ifdef DEBUG
461 	assert (streamData->os->tellp() == currentPosition);
462     #endif
463 
464     //
465     // Write the tile header.
466     //
467 
468     if (ofd->multipart)
469     {
470         Xdr::write <StreamIO> (*streamData->os, ofd->partNumber);
471     }
472     Xdr::write <StreamIO> (*streamData->os, dx);
473     Xdr::write <StreamIO> (*streamData->os, dy);
474     Xdr::write <StreamIO> (*streamData->os, lx);
475     Xdr::write <StreamIO> (*streamData->os, ly);
476     Xdr::write <StreamIO> (*streamData->os, pixelDataSize);
477 
478     streamData->os->write (pixelData, pixelDataSize);
479 
480     //
481     // Keep current position in the file so that we can avoid
482     // redundant seekg() operations (seekg() can be fairly expensive).
483     //
484 
485     streamData->currentPosition = currentPosition +
486                            5 * Xdr::size<int>() +
487                            pixelDataSize;
488 
489     if (ofd->multipart)
490     {
491         streamData->currentPosition += Xdr::size<int>();
492     }
493 }
494 
495 
496 
497 void
bufferedTileWrite(OutputStreamMutex * streamData,TiledOutputFile::Data * ofd,int dx,int dy,int lx,int ly,const char pixelData[],int pixelDataSize)498 bufferedTileWrite (OutputStreamMutex *streamData,
499                    TiledOutputFile::Data *ofd,
500                    int dx, int dy,
501 		   int lx, int ly,
502                    const char pixelData[],
503                    int pixelDataSize)
504 {
505     //
506     // Check if a tile with coordinates (dx,dy,lx,ly) has already been written.
507     //
508 
509     if (ofd->tileOffsets (dx, dy, lx, ly))
510     {
511 	THROW (IEX_NAMESPACE::ArgExc,
512 	       "Attempt to write tile "
513 	       "(" << dx << ", " << dy << ", " << lx << ", " << ly << ") "
514 	       "more than once.");
515     }
516 
517     //
518     // If tiles can be written in random order, then don't buffer anything.
519     //
520 
521     if (ofd->lineOrder == RANDOM_Y)
522     {
523         writeTileData (streamData, ofd, dx, dy, lx, ly, pixelData, pixelDataSize);
524         return;
525     }
526 
527     //
528     // If the tiles cannot be written in random order, then check if a
529     // tile with coordinates (dx,dy,lx,ly) has already been buffered.
530     //
531 
532     TileCoord currentTile = TileCoord(dx, dy, lx, ly);
533 
534     if (ofd->tileMap.find (currentTile) != ofd->tileMap.end())
535     {
536 	THROW (IEX_NAMESPACE::ArgExc,
537 	       "Attempt to write tile "
538 	       "(" << dx << ", " << dy << ", " << lx << ", " << ly << ") "
539 	       "more than once.");
540     }
541 
542     //
543     // If all the tiles before this one have already been written to the file,
544     // then write this tile immediately and check if we have buffered tiles
545     // that can be written after this tile.
546     //
547     // Otherwise, buffer the tile so it can be written to file later.
548     //
549 
550     if (ofd->nextTileToWrite == currentTile)
551     {
552         writeTileData (streamData, ofd, dx, dy, lx, ly, pixelData, pixelDataSize);
553         ofd->nextTileToWrite = ofd->nextTileCoord (ofd->nextTileToWrite);
554 
555         TileMap::iterator i = ofd->tileMap.find (ofd->nextTileToWrite);
556 
557         //
558         // Step through the tiles and write all successive buffered tiles after
559         // the current one.
560         //
561 
562         while(i != ofd->tileMap.end())
563         {
564             //
565             // Write the tile, and then delete the tile's buffered data
566             //
567 
568             writeTileData (streamData,
569                            ofd,
570 			   i->first.dx, i->first.dy,
571 			   i->first.lx, i->first.ly,
572 			   i->second->pixelData,
573 			   i->second->pixelDataSize);
574 
575             delete i->second;
576             ofd->tileMap.erase (i);
577 
578             //
579             // Proceed to the next tile
580             //
581 
582             ofd->nextTileToWrite = ofd->nextTileCoord (ofd->nextTileToWrite);
583             i = ofd->tileMap.find (ofd->nextTileToWrite);
584         }
585     }
586     else
587     {
588         //
589         // Create a new BufferedTile, copy the pixelData into it, and
590         // insert it into the tileMap.
591         //
592 
593 	ofd->tileMap[currentTile] =
594 	    new BufferedTile ((const char *)pixelData, pixelDataSize);
595     }
596 }
597 
598 
599 void
convertToXdr(TiledOutputFile::Data * ofd,Array<char> & tileBuffer,int numScanLines,int numPixelsPerScanLine)600 convertToXdr (TiledOutputFile::Data *ofd,
601               Array<char>& tileBuffer,
602 	      int numScanLines,
603 	      int numPixelsPerScanLine)
604 {
605     //
606     // Convert the contents of a TiledOutputFile's tileBuffer from the
607     // machine's native representation to Xdr format. This function is called
608     // by writeTile(), below, if the compressor wanted its input pixel data
609     // in the machine's native format, but then failed to compress the data
610     // (most compressors will expand rather than compress random input data).
611     //
612     // Note that this routine assumes that the machine's native representation
613     // of the pixel data has the same size as the Xdr representation.  This
614     // makes it possible to convert the pixel data in place, without an
615     // intermediate temporary buffer.
616     //
617 
618     //
619     // Set these to point to the start of the tile.
620     // We will write to toPtr, and read from fromPtr.
621     //
622 
623     char *writePtr = tileBuffer;
624     const char *readPtr = writePtr;
625 
626     //
627     // Iterate over all scan lines in the tile.
628     //
629 
630     for (int y = 0; y < numScanLines; ++y)
631     {
632 	//
633 	// Iterate over all slices in the file.
634 	//
635 
636 	for (unsigned int i = 0; i < ofd->slices.size(); ++i)
637 	{
638 	    const TOutSliceInfo &slice = ofd->slices[i];
639 
640 	    //
641 	    // Convert the samples in place.
642 	    //
643 
644             convertInPlace (writePtr, readPtr, slice.type,
645                             numPixelsPerScanLine);
646 	}
647     }
648 
649     #ifdef DEBUG
650 
651 	assert (writePtr == readPtr);
652 
653     #endif
654 }
655 
656 
657 //
658 // A TileBufferTask encapsulates the task of copying a tile from
659 // the user's framebuffer into a LineBuffer and compressing the data
660 // if necessary.
661 //
662 
663 class TileBufferTask: public Task
664 {
665   public:
666 
667     TileBufferTask (TaskGroup *group,
668                     TiledOutputFile::Data *ofd,
669                     int number,
670 		    int dx, int dy,
671 		    int lx, int ly);
672 
673     virtual ~TileBufferTask ();
674 
675     virtual void		execute ();
676 
677   private:
678 
679     TiledOutputFile::Data *	_ofd;
680     TileBuffer *		_tileBuffer;
681 };
682 
683 
TileBufferTask(TaskGroup * group,TiledOutputFile::Data * ofd,int number,int dx,int dy,int lx,int ly)684 TileBufferTask::TileBufferTask
685     (TaskGroup *group,
686      TiledOutputFile::Data *ofd,
687      int number,
688      int dx, int dy,
689      int lx, int ly)
690 :
691     Task (group),
692     _ofd (ofd),
693     _tileBuffer (_ofd->getTileBuffer (number))
694 {
695     //
696     // Wait for the tileBuffer to become available
697     //
698 
699     _tileBuffer->wait ();
700     _tileBuffer->tileCoord = TileCoord (dx, dy, lx, ly);
701 }
702 
703 
~TileBufferTask()704 TileBufferTask::~TileBufferTask ()
705 {
706     //
707     // Signal that the tile buffer is now free
708     //
709 
710     _tileBuffer->post ();
711 }
712 
713 
714 void
execute()715 TileBufferTask::execute ()
716 {
717     try
718     {
719         //
720         // First copy the pixel data from the frame buffer
721 	// into the tile buffer
722         //
723         // Convert one tile's worth of pixel data to
724         // a machine-independent representation, and store
725         // the result in _tileBuffer->buffer.
726         //
727 
728         char *writePtr = _tileBuffer->buffer;
729 
730         Box2i tileRange = dataWindowForTile (_ofd->tileDesc,
731                                                   _ofd->minX, _ofd->maxX,
732                                                   _ofd->minY, _ofd->maxY,
733                                                   _tileBuffer->tileCoord.dx,
734                                                   _tileBuffer->tileCoord.dy,
735                                                   _tileBuffer->tileCoord.lx,
736                                                   _tileBuffer->tileCoord.ly);
737 
738         int numScanLines = tileRange.max.y - tileRange.min.y + 1;
739         int numPixelsPerScanLine = tileRange.max.x - tileRange.min.x + 1;
740 
741         //
742         // Iterate over the scan lines in the tile.
743         //
744 
745         for (int y = tileRange.min.y; y <= tileRange.max.y; ++y)
746         {
747             //
748             // Iterate over all image channels.
749             //
750 
751             for (unsigned int i = 0; i < _ofd->slices.size(); ++i)
752             {
753                 const TOutSliceInfo &slice = _ofd->slices[i];
754 
755                 //
756                 // These offsets are used to facilitate both absolute
757                 // and tile-relative pixel coordinates.
758                 //
759 
760                 int xOffset = slice.xTileCoords * tileRange.min.x;
761                 int yOffset = slice.yTileCoords * tileRange.min.y;
762 
763 		//
764 		// Fill the tile buffer with pixel data.
765 		//
766 
767                 if (slice.zero)
768                 {
769                     //
770                     // The frame buffer contains no data for this channel.
771                     // Store zeroes in _data->tileBuffer.
772                     //
773 
774                     fillChannelWithZeroes (writePtr, _ofd->format, slice.type,
775                                            numPixelsPerScanLine);
776                 }
777                 else
778                 {
779                     //
780                     // The frame buffer contains data for this channel.
781                     //
782 
783                     const char *readPtr = slice.base +
784                                           (y - yOffset) * slice.yStride +
785                                           (tileRange.min.x - xOffset) *
786                                           slice.xStride;
787 
788                     const char *endPtr  = readPtr +
789                                           (numPixelsPerScanLine - 1) *
790                                           slice.xStride;
791 
792                     copyFromFrameBuffer (writePtr, readPtr, endPtr,
793                                          slice.xStride, _ofd->format,
794                                          slice.type);
795                 }
796             }
797         }
798 
799         //
800         // Compress the contents of the tileBuffer,
801         // and store the compressed data in the output file.
802         //
803 
804         _tileBuffer->dataSize = writePtr - _tileBuffer->buffer;
805         _tileBuffer->dataPtr = _tileBuffer->buffer;
806 
807         if (_tileBuffer->compressor)
808         {
809             const char *compPtr;
810 
811             int compSize = _tileBuffer->compressor->compressTile
812                                                 (_tileBuffer->dataPtr,
813                                                  _tileBuffer->dataSize,
814                                                  tileRange, compPtr);
815 
816             if (compSize < _tileBuffer->dataSize)
817             {
818                 _tileBuffer->dataSize = compSize;
819                 _tileBuffer->dataPtr = compPtr;
820             }
821             else if (_ofd->format == Compressor::NATIVE)
822             {
823                 //
824                 // The data did not shrink during compression, but
825                 // we cannot write to the file using native format,
826                 // so we need to convert the lineBuffer to Xdr.
827                 //
828 
829                 convertToXdr (_ofd, _tileBuffer->buffer, numScanLines,
830                               numPixelsPerScanLine);
831             }
832         }
833     }
834     catch (std::exception &e)
835     {
836         if (!_tileBuffer->hasException)
837         {
838             _tileBuffer->exception = e.what ();
839             _tileBuffer->hasException = true;
840         }
841     }
842     catch (...)
843     {
844         if (!_tileBuffer->hasException)
845         {
846             _tileBuffer->exception = "unrecognized exception";
847             _tileBuffer->hasException = true;
848         }
849     }
850 }
851 
852 } // namespace
853 
854 
TiledOutputFile(const char fileName[],const Header & header,int numThreads)855 TiledOutputFile::TiledOutputFile
856     (const char fileName[],
857      const Header &header,
858      int numThreads)
859 :
860     _data (new Data (numThreads)),
861     _streamData (new OutputStreamMutex()),
862     _deleteStream (true)
863 {
864     try
865     {
866 	header.sanityCheck (true);
867 	_streamData->os = new StdOFStream (fileName);
868         _data->multipart=false; // since we opened with one header we can't be multipart
869 	initialize (header);
870 	_streamData->currentPosition = _streamData->os->tellp();
871 
872 	// Write header and empty offset table to the file.
873         writeMagicNumberAndVersionField(*_streamData->os, _data->header);
874 	_data->previewPosition = _data->header.writeTo (*_streamData->os, true);
875         _data->tileOffsetsPosition = _data->tileOffsets.writeTo (*_streamData->os);
876     }
877     catch (IEX_NAMESPACE::BaseExc &e)
878     {
879         delete _streamData;
880 	delete _data;
881 
882 	REPLACE_EXC (e, "Cannot open image file "
883 			"\"" << fileName << "\". " << e);
884 	throw;
885     }
886     catch (...)
887     {
888         delete _streamData;
889 	delete _data;
890         throw;
891     }
892 }
893 
894 
TiledOutputFile(OPENEXR_IMF_INTERNAL_NAMESPACE::OStream & os,const Header & header,int numThreads)895 TiledOutputFile::TiledOutputFile
896     (OPENEXR_IMF_INTERNAL_NAMESPACE::OStream &os,
897      const Header &header,
898      int numThreads)
899 :
900     _data (new Data (numThreads)),
901     _streamData (new OutputStreamMutex()),
902     _deleteStream (false)
903 {
904     try
905     {
906 	header.sanityCheck(true);
907 	_streamData->os = &os;
908         _data->multipart=false; // since we opened with one header we can't be multipart
909 	initialize (header);
910 	_streamData->currentPosition = _streamData->os->tellp();
911 
912 	// Write header and empty offset table to the file.
913 	writeMagicNumberAndVersionField(*_streamData->os, _data->header);
914 	_data->previewPosition = _data->header.writeTo (*_streamData->os, true);
915         _data->tileOffsetsPosition = _data->tileOffsets.writeTo (*_streamData->os);
916 
917     }
918     catch (IEX_NAMESPACE::BaseExc &e)
919     {
920         delete _streamData;
921 	delete _data;
922 
923 	REPLACE_EXC (e, "Cannot open image file "
924 			"\"" << os.fileName() << "\". " << e);
925 	throw;
926     }
927     catch (...)
928     {
929         delete _streamData;
930 	delete _data;
931         throw;
932     }
933 }
934 
TiledOutputFile(const OutputPartData * part)935 TiledOutputFile::TiledOutputFile(const OutputPartData* part) :
936     _deleteStream (false)
937 {
938     try
939     {
940         if (part->header.type() != TILEDIMAGE)
941             throw IEX_NAMESPACE::ArgExc("Can't build a TiledOutputFile from a type-mismatched part.");
942 
943         _streamData = part->mutex;
944         _data = new Data(part->numThreads);
945         _data->multipart=part->multipart;
946         initialize(part->header);
947         _data->partNumber = part->partNumber;
948         _data->tileOffsetsPosition = part->chunkOffsetTablePosition;
949         _data->previewPosition = part->previewPosition;
950     }
951     catch (IEX_NAMESPACE::BaseExc &e)
952     {
953         delete _data;
954 
955         REPLACE_EXC (e, "Cannot initialize output part "
956                         "\"" << part->partNumber << "\". " << e);
957         throw;
958     }
959     catch (...)
960     {
961         delete _data;
962         throw;
963     }
964 }
965 
966 void
initialize(const Header & header)967 TiledOutputFile::initialize (const Header &header)
968 {
969     _data->header = header;
970     _data->lineOrder = _data->header.lineOrder();
971 
972 
973 
974     //
975     // Check that the file is indeed tiled
976     //
977 
978     _data->tileDesc = _data->header.tileDescription();
979 
980 
981     //
982     // 'Fix' the type attribute if it exists but is incorrectly set
983     // (attribute is optional, but ensure it is correct if it exists)
984     //
985     if(_data->header.hasType())
986     {
987         _data->header.setType(TILEDIMAGE);
988     }
989 
990 
991     //
992     // Save the dataWindow information
993     //
994 
995     const Box2i &dataWindow = _data->header.dataWindow();
996     _data->minX = dataWindow.min.x;
997     _data->maxX = dataWindow.max.x;
998     _data->minY = dataWindow.min.y;
999     _data->maxY = dataWindow.max.y;
1000 
1001     //
1002     // Precompute level and tile information to speed up utility functions
1003     //
1004 
1005     precalculateTileInfo (_data->tileDesc,
1006 			  _data->minX, _data->maxX,
1007 			  _data->minY, _data->maxY,
1008 			  _data->numXTiles, _data->numYTiles,
1009 			  _data->numXLevels, _data->numYLevels);
1010 
1011     //
1012     // Determine the first tile coordinate that we will be writing
1013     // if the file is not RANDOM_Y.
1014     //
1015 
1016     _data->nextTileToWrite = (_data->lineOrder == INCREASING_Y)?
1017 			       TileCoord (0, 0, 0, 0):
1018 			       TileCoord (0, _data->numYTiles[0] - 1, 0, 0);
1019 
1020     _data->maxBytesPerTileLine =
1021 	    calculateBytesPerPixel (_data->header) * _data->tileDesc.xSize;
1022 
1023     _data->tileBufferSize = _data->maxBytesPerTileLine * _data->tileDesc.ySize;
1024 
1025     //
1026     // Create all the TileBuffers and allocate their internal buffers
1027     //
1028 
1029     for (size_t i = 0; i < _data->tileBuffers.size(); i++)
1030     {
1031         _data->tileBuffers[i] = new TileBuffer (newTileCompressor
1032 						  (_data->header.compression(),
1033 						   _data->maxBytesPerTileLine,
1034 						   _data->tileDesc.ySize,
1035 						   _data->header));
1036 
1037         _data->tileBuffers[i]->buffer.resizeErase(_data->tileBufferSize);
1038     }
1039 
1040     _data->format = defaultFormat (_data->tileBuffers[0]->compressor);
1041 
1042     _data->tileOffsets = TileOffsets (_data->tileDesc.mode,
1043 				      _data->numXLevels,
1044 				      _data->numYLevels,
1045 				      _data->numXTiles,
1046 				      _data->numYTiles);
1047 }
1048 
1049 
~TiledOutputFile()1050 TiledOutputFile::~TiledOutputFile ()
1051 {
1052     if (_data)
1053     {
1054         {
1055             Lock lock(*_streamData);
1056             Int64 originalPosition = _streamData->os->tellp();
1057 
1058             if (_data->tileOffsetsPosition > 0)
1059             {
1060                 try
1061                 {
1062                     _streamData->os->seekp (_data->tileOffsetsPosition);
1063                     _data->tileOffsets.writeTo (*_streamData->os);
1064 
1065                     //
1066                     // Restore the original position.
1067                     //
1068                     _streamData->os->seekp (originalPosition);
1069                 }
1070                 catch (...)
1071                 {
1072                     //
1073                     // We cannot safely throw any exceptions from here.
1074                     // This destructor may have been called because the
1075                     // stack is currently being unwound for another
1076                     // exception.
1077                     //
1078                 }
1079             }
1080         }
1081 
1082         if (_deleteStream && _streamData)
1083             delete _streamData->os;
1084 
1085         if (_data->partNumber == -1)
1086             delete _streamData;
1087 
1088         delete _data;
1089     }
1090 }
1091 
1092 
1093 const char *
fileName() const1094 TiledOutputFile::fileName () const
1095 {
1096     return _streamData->os->fileName();
1097 }
1098 
1099 
1100 const Header &
header() const1101 TiledOutputFile::header () const
1102 {
1103     return _data->header;
1104 }
1105 
1106 
1107 void
setFrameBuffer(const FrameBuffer & frameBuffer)1108 TiledOutputFile::setFrameBuffer (const FrameBuffer &frameBuffer)
1109 {
1110     Lock lock (*_streamData);
1111 
1112     //
1113     // Check if the new frame buffer descriptor
1114     // is compatible with the image file header.
1115     //
1116 
1117     const ChannelList &channels = _data->header.channels();
1118 
1119     for (ChannelList::ConstIterator i = channels.begin();
1120 	 i != channels.end();
1121 	 ++i)
1122     {
1123 	FrameBuffer::ConstIterator j = frameBuffer.find (i.name());
1124 
1125 	if (j == frameBuffer.end())
1126 	    continue;
1127 
1128 	if (i.channel().type != j.slice().type)
1129 	    THROW (IEX_NAMESPACE::ArgExc, "Pixel type of \"" << i.name() << "\" channel "
1130 				"of output file \"" << fileName() << "\" is "
1131 				"not compatible with the frame buffer's "
1132 				"pixel type.");
1133 
1134 	if (j.slice().xSampling != 1 || j.slice().ySampling != 1)
1135 	    THROW (IEX_NAMESPACE::ArgExc, "All channels in a tiled file must have"
1136 				"sampling (1,1).");
1137     }
1138 
1139     //
1140     // Initialize slice table for writePixels().
1141     //
1142 
1143     vector<TOutSliceInfo> slices;
1144 
1145     for (ChannelList::ConstIterator i = channels.begin();
1146 	 i != channels.end();
1147 	 ++i)
1148     {
1149 	FrameBuffer::ConstIterator j = frameBuffer.find (i.name());
1150 
1151 	if (j == frameBuffer.end())
1152 	{
1153 	    //
1154 	    // Channel i is not present in the frame buffer.
1155 	    // In the file, channel i will contain only zeroes.
1156 	    //
1157 
1158 	    slices.push_back (TOutSliceInfo (i.channel().type,
1159 					     0, // base
1160 					     0, // xStride,
1161 					     0, // yStride,
1162 					     true)); // zero
1163 	}
1164 	else
1165 	{
1166 	    //
1167 	    // Channel i is present in the frame buffer.
1168 	    //
1169 
1170 	    slices.push_back (TOutSliceInfo (j.slice().type,
1171 					     j.slice().base,
1172 					     j.slice().xStride,
1173 					     j.slice().yStride,
1174 					     false, // zero
1175                                              (j.slice().xTileCoords)? 1: 0,
1176                                              (j.slice().yTileCoords)? 1: 0));
1177 	}
1178     }
1179 
1180     //
1181     // Store the new frame buffer.
1182     //
1183 
1184     _data->frameBuffer = frameBuffer;
1185     _data->slices = slices;
1186 }
1187 
1188 
1189 const FrameBuffer &
frameBuffer() const1190 TiledOutputFile::frameBuffer () const
1191 {
1192     Lock lock (*_streamData);
1193     return _data->frameBuffer;
1194 }
1195 
1196 
1197 void
writeTiles(int dx1,int dx2,int dy1,int dy2,int lx,int ly)1198 TiledOutputFile::writeTiles (int dx1, int dx2, int dy1, int dy2,
1199                              int lx, int ly)
1200 {
1201     try
1202     {
1203         Lock lock (*_streamData);
1204 
1205         if (_data->slices.size() == 0)
1206 	    throw IEX_NAMESPACE::ArgExc ("No frame buffer specified "
1207 			       "as pixel data source.");
1208 
1209 	if (!isValidTile (dx1, dy1, lx, ly) || !isValidTile (dx2, dy2, lx, ly))
1210 	    throw IEX_NAMESPACE::ArgExc ("Tile coordinates are invalid.");
1211 
1212 	if (!isValidLevel (lx, ly))
1213 	    THROW (IEX_NAMESPACE::ArgExc,
1214                    "Level coordinate "
1215                    "(" << lx << ", " << ly << ") "
1216                    "is invalid.");
1217         //
1218         // Determine the first and last tile coordinates in both dimensions
1219         // based on the file's lineOrder
1220         //
1221 
1222         if (dx1 > dx2)
1223             swap (dx1, dx2);
1224 
1225         if (dy1 > dy2)
1226             swap (dy1, dy2);
1227 
1228         int dyStart = dy1;
1229 	int dyStop  = dy2 + 1;
1230 	int dY      = 1;
1231 
1232         if (_data->lineOrder == DECREASING_Y)
1233         {
1234             dyStart = dy2;
1235             dyStop  = dy1 - 1;
1236             dY      = -1;
1237         }
1238 
1239         int numTiles = (dx2 - dx1 + 1) * (dy2 - dy1 + 1);
1240         int numTasks = min ((int)_data->tileBuffers.size(), numTiles);
1241 
1242         //
1243         // Create a task group for all tile buffer tasks.  When the
1244 	// task group goes out of scope, the destructor waits until
1245 	// all tasks are complete.
1246         //
1247 
1248         {
1249             TaskGroup taskGroup;
1250 
1251             //
1252             // Add in the initial compression tasks to the thread pool
1253             //
1254 
1255             int nextCompBuffer = 0;
1256 	    int dxComp         = dx1;
1257 	    int dyComp         = dyStart;
1258 
1259             while (nextCompBuffer < numTasks)
1260             {
1261                 ThreadPool::addGlobalTask (new TileBufferTask (&taskGroup,
1262                                                                _data,
1263                                                                nextCompBuffer++,
1264                                                                dxComp, dyComp,
1265                                                                lx, ly));
1266                 dxComp++;
1267 
1268                 if (dxComp > dx2)
1269                 {
1270                     dxComp = dx1;
1271                     dyComp += dY;
1272                 }
1273             }
1274 
1275             //
1276             // Write the compressed buffers and add in more compression
1277 	    // tasks until done
1278             //
1279 
1280             int nextWriteBuffer = 0;
1281 	    int dxWrite         = dx1;
1282 	    int dyWrite         = dyStart;
1283 
1284             while (nextWriteBuffer < numTiles)
1285             {
1286 		//
1287                 // Wait until the nextWriteBuffer is ready to be written
1288 		//
1289 
1290                 TileBuffer* writeBuffer =
1291                                     _data->getTileBuffer (nextWriteBuffer);
1292 
1293                 writeBuffer->wait();
1294 
1295 		//
1296                 // Write the tilebuffer
1297 		//
1298 
1299                 bufferedTileWrite (_streamData, _data, dxWrite, dyWrite, lx, ly,
1300                                    writeBuffer->dataPtr,
1301                                    writeBuffer->dataSize);
1302 
1303 		//
1304                 // Release the lock on nextWriteBuffer
1305 		//
1306 
1307                 writeBuffer->post();
1308 
1309 		//
1310                 // If there are no more tileBuffers to compress, then
1311 		// only continue to write out remaining tileBuffers,
1312 		// otherwise keep adding compression tasks.
1313 		//
1314 
1315                 if (nextCompBuffer < numTiles)
1316                 {
1317 		    //
1318                     // add nextCompBuffer as a compression Task
1319 		    //
1320 
1321                     ThreadPool::addGlobalTask
1322 			(new TileBufferTask (&taskGroup,
1323 					     _data,
1324 					     nextCompBuffer,
1325                                              dxComp, dyComp,
1326 					     lx, ly));
1327                 }
1328 
1329                 nextWriteBuffer++;
1330                 dxWrite++;
1331 
1332                 if (dxWrite > dx2)
1333                 {
1334                     dxWrite = dx1;
1335                     dyWrite += dY;
1336                 }
1337 
1338                 nextCompBuffer++;
1339                 dxComp++;
1340 
1341                 if (dxComp > dx2)
1342                 {
1343                     dxComp = dx1;
1344                     dyComp += dY;
1345                 }
1346             }
1347 
1348 	    //
1349             // finish all tasks
1350 	    //
1351         }
1352 
1353 	//
1354 	// Exeption handling:
1355 	//
1356 	// TileBufferTask::execute() may have encountered exceptions, but
1357 	// those exceptions occurred in another thread, not in the thread
1358 	// that is executing this call to TiledOutputFile::writeTiles().
1359 	// TileBufferTask::execute() has caught all exceptions and stored
1360 	// the exceptions' what() strings in the tile buffers.
1361 	// Now we check if any tile buffer contains a stored exception; if
1362 	// this is the case then we re-throw the exception in this thread.
1363 	// (It is possible that multiple tile buffers contain stored
1364 	// exceptions.  We re-throw the first exception we find and
1365 	// ignore all others.)
1366 	//
1367 
1368 	const string *exception = 0;
1369 
1370         for (size_t i = 0; i < _data->tileBuffers.size(); ++i)
1371 	{
1372             TileBuffer *tileBuffer = _data->tileBuffers[i];
1373 
1374 	    if (tileBuffer->hasException && !exception)
1375 		exception = &tileBuffer->exception;
1376 
1377 	    tileBuffer->hasException = false;
1378 	}
1379 
1380 	if (exception)
1381 	    throw IEX_NAMESPACE::IoExc (*exception);
1382     }
1383     catch (IEX_NAMESPACE::BaseExc &e)
1384     {
1385         REPLACE_EXC (e, "Failed to write pixel data to image "
1386                         "file \"" << fileName() << "\". " << e);
1387         throw;
1388     }
1389 }
1390 
1391 
1392 void
writeTiles(int dx1,int dxMax,int dyMin,int dyMax,int l)1393 TiledOutputFile::writeTiles (int dx1, int dxMax, int dyMin, int dyMax, int l)
1394 {
1395     writeTiles (dx1, dxMax, dyMin, dyMax, l, l);
1396 }
1397 
1398 
1399 void
writeTile(int dx,int dy,int lx,int ly)1400 TiledOutputFile::writeTile (int dx, int dy, int lx, int ly)
1401 {
1402     writeTiles (dx, dx, dy, dy, lx, ly);
1403 }
1404 
1405 
1406 void
writeTile(int dx,int dy,int l)1407 TiledOutputFile::writeTile (int dx, int dy, int l)
1408 {
1409     writeTile(dx, dy, l, l);
1410 }
1411 
1412 
1413 void
copyPixels(TiledInputFile & in)1414 TiledOutputFile::copyPixels (TiledInputFile &in)
1415 {
1416     Lock lock (*_streamData);
1417 
1418     //
1419     // Check if this file's and and the InputFile's
1420     // headers are compatible.
1421     //
1422 
1423     const Header &hdr = _data->header;
1424     const Header &inHdr = in.header();
1425 
1426     if (!hdr.hasTileDescription() || !inHdr.hasTileDescription())
1427         THROW (IEX_NAMESPACE::ArgExc, "Cannot perform a quick pixel copy from image "
1428 			    "file \"" << in.fileName() << "\" to image "
1429 			    "file \"" << fileName() << "\".  The "
1430                             "output file is tiled, but the input file is not.  "
1431                             "Try using OutputFile::copyPixels() instead.");
1432 
1433     if (!(hdr.tileDescription() == inHdr.tileDescription()))
1434         THROW (IEX_NAMESPACE::ArgExc, "Quick pixel copy from image "
1435 			    "file \"" << in.fileName() << "\" to image "
1436 			    "file \"" << fileName() << "\" failed. "
1437 			    "The files have different tile descriptions.");
1438 
1439     if (!(hdr.dataWindow() == inHdr.dataWindow()))
1440         THROW (IEX_NAMESPACE::ArgExc, "Cannot copy pixels from image "
1441 			    "file \"" << in.fileName() << "\" to image "
1442 			    "file \"" << fileName() << "\". The "
1443                             "files have different data windows.");
1444 
1445     if (!(hdr.lineOrder() == inHdr.lineOrder()))
1446         THROW (IEX_NAMESPACE::ArgExc, "Quick pixel copy from image "
1447 			    "file \"" << in.fileName() << "\" to image "
1448 			    "file \"" << fileName() << "\" failed. "
1449 			    "The files have different line orders.");
1450 
1451     if (!(hdr.compression() == inHdr.compression()))
1452         THROW (IEX_NAMESPACE::ArgExc, "Quick pixel copy from image "
1453 			    "file \"" << in.fileName() << "\" to image "
1454 			    "file \"" << fileName() << "\" failed. "
1455 			    "The files use different compression methods.");
1456 
1457     if (!(hdr.channels() == inHdr.channels()))
1458         THROW (IEX_NAMESPACE::ArgExc, "Quick pixel copy from image "
1459 			     "file \"" << in.fileName() << "\" to image "
1460 			     "file \"" << fileName() << "\" "
1461                              "failed.  The files have different channel "
1462                              "lists.");
1463 
1464     //
1465     // Verify that no pixel data have been written to this file yet.
1466     //
1467 
1468     if (!_data->tileOffsets.isEmpty())
1469         THROW (IEX_NAMESPACE::LogicExc, "Quick pixel copy from image "
1470 			      "file \"" << in.fileName() << "\" to image "
1471 			      "file \"" << _streamData->os->fileName() << "\" "
1472                               "failed. \"" << fileName() << "\" "
1473                               "already contains pixel data.");
1474 
1475     //
1476     // Calculate the total number of tiles in the file
1477     //
1478 
1479     int numAllTiles = 0;
1480 
1481     switch (levelMode ())
1482     {
1483       case ONE_LEVEL:
1484       case MIPMAP_LEVELS:
1485 
1486         for (int i_l = 0; i_l < numLevels (); ++i_l)
1487             numAllTiles += numXTiles (i_l) * numYTiles (i_l);
1488 
1489         break;
1490 
1491       case RIPMAP_LEVELS:
1492 
1493         for (int i_ly = 0; i_ly < numYLevels (); ++i_ly)
1494             for (int i_lx = 0; i_lx < numXLevels (); ++i_lx)
1495                 numAllTiles += numXTiles (i_lx) * numYTiles (i_ly);
1496 
1497         break;
1498 
1499       default:
1500 
1501         throw IEX_NAMESPACE::ArgExc ("Unknown LevelMode format.");
1502     }
1503 
1504      bool random_y = _data->lineOrder==RANDOM_Y;
1505 
1506     std::vector<int> dx_table(random_y ? numAllTiles : 1);
1507     std::vector<int> dy_table(random_y ? numAllTiles : 1);
1508     std::vector<int> lx_table(random_y ? numAllTiles : 1);
1509     std::vector<int> ly_table(random_y ? numAllTiles : 1);
1510 
1511     if(random_y)
1512     {
1513         in.tileOrder(&dx_table[0],&dy_table[0],&lx_table[0],&ly_table[0]);
1514         _data->nextTileToWrite.dx=dx_table[0];
1515         _data->nextTileToWrite.dy=dy_table[0];
1516         _data->nextTileToWrite.lx=lx_table[0];
1517         _data->nextTileToWrite.ly=ly_table[0];
1518     }
1519 
1520     for (int i = 0; i < numAllTiles; ++i)
1521     {
1522         const char *pixelData;
1523         int pixelDataSize;
1524 
1525         int dx = _data->nextTileToWrite.dx;
1526         int dy = _data->nextTileToWrite.dy;
1527         int lx = _data->nextTileToWrite.lx;
1528         int ly = _data->nextTileToWrite.ly;
1529 
1530 
1531         in.rawTileData (dx, dy, lx, ly, pixelData, pixelDataSize);
1532         writeTileData (_streamData, _data, dx, dy, lx, ly, pixelData, pixelDataSize);
1533 
1534         if(random_y)
1535         {
1536             if(i<numAllTiles-1)
1537             {
1538                _data->nextTileToWrite.dx=dx_table[i+1];
1539                _data->nextTileToWrite.dy=dy_table[i+1];
1540                _data->nextTileToWrite.lx=lx_table[i+1];
1541                _data->nextTileToWrite.ly=ly_table[i+1];
1542             }
1543         }else{
1544             _data->nextTileToWrite=_data->nextTileCoord(_data->nextTileToWrite);
1545         }
1546     }
1547 }
1548 
1549 
1550 void
copyPixels(InputFile & in)1551 TiledOutputFile::copyPixels (InputFile &in)
1552 {
1553     copyPixels (*in.tFile());
1554 }
1555 
1556 
1557 void
copyPixels(InputPart & in)1558 TiledOutputFile::copyPixels (InputPart &in)
1559 {
1560     copyPixels (*in.file);
1561 }
1562 
1563 void
copyPixels(TiledInputPart & in)1564 TiledOutputFile::copyPixels (TiledInputPart &in)
1565 {
1566     copyPixels (*in.file);
1567 }
1568 
1569 
1570 
1571 unsigned int
tileXSize() const1572 TiledOutputFile::tileXSize () const
1573 {
1574     return _data->tileDesc.xSize;
1575 }
1576 
1577 
1578 unsigned int
tileYSize() const1579 TiledOutputFile::tileYSize () const
1580 {
1581     return _data->tileDesc.ySize;
1582 }
1583 
1584 
1585 LevelMode
levelMode() const1586 TiledOutputFile::levelMode () const
1587 {
1588     return _data->tileDesc.mode;
1589 }
1590 
1591 
1592 LevelRoundingMode
levelRoundingMode() const1593 TiledOutputFile::levelRoundingMode () const
1594 {
1595     return _data->tileDesc.roundingMode;
1596 }
1597 
1598 
1599 int
numLevels() const1600 TiledOutputFile::numLevels () const
1601 {
1602     if (levelMode() == RIPMAP_LEVELS)
1603 	THROW (IEX_NAMESPACE::LogicExc, "Error calling numLevels() on image "
1604 			      "file \"" << fileName() << "\" "
1605 			      "(numLevels() is not defined for RIPMAPs).");
1606     return _data->numXLevels;
1607 }
1608 
1609 
1610 int
numXLevels() const1611 TiledOutputFile::numXLevels () const
1612 {
1613     return _data->numXLevels;
1614 }
1615 
1616 
1617 int
numYLevels() const1618 TiledOutputFile::numYLevels () const
1619 {
1620     return _data->numYLevels;
1621 }
1622 
1623 
1624 bool
isValidLevel(int lx,int ly) const1625 TiledOutputFile::isValidLevel (int lx, int ly) const
1626 {
1627     if (lx < 0 || ly < 0)
1628 	return false;
1629 
1630     if (levelMode() == MIPMAP_LEVELS && lx != ly)
1631 	return false;
1632 
1633     if (lx >= numXLevels() || ly >= numYLevels())
1634 	return false;
1635 
1636     return true;
1637 }
1638 
1639 
1640 int
levelWidth(int lx) const1641 TiledOutputFile::levelWidth (int lx) const
1642 {
1643     try
1644     {
1645 	int retVal = levelSize (_data->minX, _data->maxX, lx,
1646 			        _data->tileDesc.roundingMode);
1647 
1648         return retVal;
1649     }
1650     catch (IEX_NAMESPACE::BaseExc &e)
1651     {
1652 	REPLACE_EXC (e, "Error calling levelWidth() on image "
1653 			"file \"" << fileName() << "\". " << e);
1654 	throw;
1655     }
1656 }
1657 
1658 
1659 int
levelHeight(int ly) const1660 TiledOutputFile::levelHeight (int ly) const
1661 {
1662     try
1663     {
1664 	return levelSize (_data->minY, _data->maxY, ly,
1665 			  _data->tileDesc.roundingMode);
1666     }
1667     catch (IEX_NAMESPACE::BaseExc &e)
1668     {
1669 	REPLACE_EXC (e, "Error calling levelHeight() on image "
1670 			"file \"" << fileName() << "\". " << e);
1671 	throw;
1672     }
1673 }
1674 
1675 
1676 int
numXTiles(int lx) const1677 TiledOutputFile::numXTiles (int lx) const
1678 {
1679     if (lx < 0 || lx >= _data->numXLevels)
1680 	THROW (IEX_NAMESPACE::LogicExc, "Error calling numXTiles() on image "
1681 			      "file \"" << _streamData->os->fileName() << "\" "
1682 			      "(Argument is not in valid range).");
1683 
1684     return _data->numXTiles[lx];
1685 }
1686 
1687 
1688 int
numYTiles(int ly) const1689 TiledOutputFile::numYTiles (int ly) const
1690 {
1691    if (ly < 0 || ly >= _data->numYLevels)
1692 	THROW (IEX_NAMESPACE::LogicExc, "Error calling numXTiles() on image "
1693 			      "file \"" << _streamData->os->fileName() << "\" "
1694 			      "(Argument is not in valid range).");
1695 
1696     return _data->numYTiles[ly];
1697 }
1698 
1699 
1700 Box2i
dataWindowForLevel(int l) const1701 TiledOutputFile::dataWindowForLevel (int l) const
1702 {
1703     return dataWindowForLevel (l, l);
1704 }
1705 
1706 
1707 Box2i
dataWindowForLevel(int lx,int ly) const1708 TiledOutputFile::dataWindowForLevel (int lx, int ly) const
1709 {
1710     try
1711     {
1712 	return OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForLevel (
1713 	        _data->tileDesc,
1714 	        _data->minX, _data->maxX,
1715 	        _data->minY, _data->maxY,
1716 	        lx, ly);
1717     }
1718     catch (IEX_NAMESPACE::BaseExc &e)
1719     {
1720 	REPLACE_EXC (e, "Error calling dataWindowForLevel() on image "
1721 			"file \"" << fileName() << "\". " << e);
1722 	throw;
1723     }
1724 }
1725 
1726 
1727 Box2i
dataWindowForTile(int dx,int dy,int l) const1728 TiledOutputFile::dataWindowForTile (int dx, int dy, int l) const
1729 {
1730     return dataWindowForTile (dx, dy, l, l);
1731 }
1732 
1733 
1734 Box2i
dataWindowForTile(int dx,int dy,int lx,int ly) const1735 TiledOutputFile::dataWindowForTile (int dx, int dy, int lx, int ly) const
1736 {
1737     try
1738     {
1739 	if (!isValidTile (dx, dy, lx, ly))
1740 	    throw IEX_NAMESPACE::ArgExc ("Arguments not in valid range.");
1741 
1742 	return OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile (
1743 	        _data->tileDesc,
1744 	        _data->minX, _data->maxX,
1745 	        _data->minY, _data->maxY,
1746 	        dx, dy,
1747 	        lx, ly);
1748     }
1749     catch (IEX_NAMESPACE::BaseExc &e)
1750     {
1751 	REPLACE_EXC (e, "Error calling dataWindowForTile() on image "
1752 			"file \"" << fileName() << "\". " << e);
1753 	throw;
1754     }
1755 }
1756 
1757 
1758 bool
isValidTile(int dx,int dy,int lx,int ly) const1759 TiledOutputFile::isValidTile (int dx, int dy, int lx, int ly) const
1760 {
1761     return ((lx < _data->numXLevels && lx >= 0) &&
1762 	    (ly < _data->numYLevels && ly >= 0) &&
1763 	    (dx < _data->numXTiles[lx] && dx >= 0) &&
1764 	    (dy < _data->numYTiles[ly] && dy >= 0));
1765 }
1766 
1767 
1768 void
updatePreviewImage(const PreviewRgba newPixels[])1769 TiledOutputFile::updatePreviewImage (const PreviewRgba newPixels[])
1770 {
1771     Lock lock (*_streamData);
1772 
1773     if (_data->previewPosition <= 0)
1774 	THROW (IEX_NAMESPACE::LogicExc, "Cannot update preview image pixels. "
1775 			      "File \"" << fileName() << "\" does not "
1776 			      "contain a preview image.");
1777 
1778     //
1779     // Store the new pixels in the header's preview image attribute.
1780     //
1781 
1782     PreviewImageAttribute &pia =
1783 	_data->header.typedAttribute <PreviewImageAttribute> ("preview");
1784 
1785     PreviewImage &pi = pia.value();
1786     PreviewRgba *pixels = pi.pixels();
1787     int numPixels = pi.width() * pi.height();
1788 
1789     for (int i = 0; i < numPixels; ++i)
1790 	pixels[i] = newPixels[i];
1791 
1792     //
1793     // Save the current file position, jump to the position in
1794     // the file where the preview image starts, store the new
1795     // preview image, and jump back to the saved file position.
1796     //
1797 
1798     Int64 savedPosition = _streamData->os->tellp();
1799 
1800     try
1801     {
1802         _streamData->os->seekp (_data->previewPosition);
1803 	pia.writeValueTo (*_streamData->os, _data->version);
1804 	_streamData->os->seekp (savedPosition);
1805     }
1806     catch (IEX_NAMESPACE::BaseExc &e)
1807     {
1808 	REPLACE_EXC (e, "Cannot update preview image pixels for "
1809 			"file \"" << fileName() << "\". " << e);
1810 	throw;
1811     }
1812 }
1813 
1814 
1815 void
breakTile(int dx,int dy,int lx,int ly,int offset,int length,char c)1816 TiledOutputFile::breakTile
1817     (int dx, int dy,
1818      int lx, int ly,
1819      int offset,
1820      int length,
1821      char c)
1822 {
1823     Lock lock (*_streamData);
1824 
1825     Int64 position = _data->tileOffsets (dx, dy, lx, ly);
1826 
1827     if (!position)
1828 	THROW (IEX_NAMESPACE::ArgExc,
1829 	       "Cannot overwrite tile "
1830 	       "(" << dx << ", " << dy << ", " << lx << "," << ly << "). "
1831 	       "The tile has not yet been stored in "
1832 	       "file \"" << fileName() << "\".");
1833 
1834     _streamData->currentPosition = 0;
1835     _streamData->os->seekp (position + offset);
1836 
1837     for (int i = 0; i < length; ++i)
1838         _streamData->os->write (&c, 1);
1839 }
1840 
1841 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT
1842