Home | History | Annotate | Download | only in IlmImf
      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 
    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 
    136     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
    147     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
    157     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 
    172     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 
    180     ~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 
    203     inline void		wait () {_sem.wait();}
    204     inline void		post () {_sem.post();}
    205 
    206   protected:
    207 
    208     Semaphore		_sem;
    209 };
    210 
    211 
    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 
    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 
    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 
    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*
    321 TiledOutputFile::Data::getTileBuffer (int number)
    322 {
    323     return tileBuffers[number % tileBuffers.size()];
    324 }
    325 
    326 
    327 TileCoord
    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
    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
    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
    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 
    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 
    682 TileBufferTask::~TileBufferTask ()
    683 {
    684     //
    685     // Signal that the tile buffer is now free
    686     //
    687 
    688     _tileBuffer->post ();
    689 }
    690 
    691 
    692 void
    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 
    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 
    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
    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 
    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 *
    997 TiledOutputFile::fileName () const
    998 {
    999     return _data->os->fileName();
   1000 }
   1001 
   1002 
   1003 const Header &
   1004 TiledOutputFile::header () const
   1005 {
   1006     return _data->header;
   1007 }
   1008 
   1009 
   1010 void
   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 &
   1093 TiledOutputFile::frameBuffer () const
   1094 {
   1095     Lock lock (*_data);
   1096     return _data->frameBuffer;
   1097 }
   1098 
   1099 
   1100 void
   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
   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
   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
   1305 TiledOutputFile::writeTile (int dx, int dy, int l)
   1306 {
   1307     writeTile(dx, dy, l, l);
   1308 }
   1309 
   1310 
   1311 void
   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
   1419 TiledOutputFile::copyPixels (InputFile &in)
   1420 {
   1421     copyPixels (*in.tFile());
   1422 }
   1423 
   1424 
   1425 unsigned int
   1426 TiledOutputFile::tileXSize () const
   1427 {
   1428     return _data->tileDesc.xSize;
   1429 }
   1430 
   1431 
   1432 unsigned int
   1433 TiledOutputFile::tileYSize () const
   1434 {
   1435     return _data->tileDesc.ySize;
   1436 }
   1437 
   1438 
   1439 LevelMode
   1440 TiledOutputFile::levelMode () const
   1441 {
   1442     return _data->tileDesc.mode;
   1443 }
   1444 
   1445 
   1446 LevelRoundingMode
   1447 TiledOutputFile::levelRoundingMode () const
   1448 {
   1449     return _data->tileDesc.roundingMode;
   1450 }
   1451 
   1452 
   1453 int
   1454 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
   1465 TiledOutputFile::numXLevels () const
   1466 {
   1467     return _data->numXLevels;
   1468 }
   1469 
   1470 
   1471 int
   1472 TiledOutputFile::numYLevels () const
   1473 {
   1474     return _data->numYLevels;
   1475 }
   1476 
   1477 
   1478 bool
   1479 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
   1495 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
   1514 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
   1531 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
   1543 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
   1555 TiledOutputFile::dataWindowForLevel (int l) const
   1556 {
   1557     return dataWindowForLevel (l, l);
   1558 }
   1559 
   1560 
   1561 Box2i
   1562 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
   1581 TiledOutputFile::dataWindowForTile (int dx, int dy, int l) const
   1582 {
   1583     return dataWindowForTile (dx, dy, l, l);
   1584 }
   1585 
   1586 
   1587 Box2i
   1588 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
   1611 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
   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
   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