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