PageRenderTime 123ms CodeModel.GetById 25ms app.highlight 86ms RepoModel.GetById 1ms app.codeStats 1ms

/src/FreeImage/Source/OpenEXR/IlmImf/ImfTiledOutputFile.cpp

https://bitbucket.org/cabalistic/ogredeps/
C++ | 1693 lines | 1069 code | 342 blank | 282 comment | 132 complexity | 118aa125a9b1e2503902ae53538891c0 MD5 | raw file
   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>
  67
  68
  69namespace Imf {
  70
  71using Imath::Box2i;
  72using Imath::V2i;
  73using std::string;
  74using std::vector;
  75using std::ofstream;
  76using std::map;
  77using std::min;
  78using std::max;
  79using std::swap;
  80using IlmThread::Mutex;
  81using IlmThread::Lock;
  82using IlmThread::Semaphore;
  83using IlmThread::Task;
  84using IlmThread::TaskGroup;
  85using IlmThread::ThreadPool;
  86
  87namespace {
  88
  89struct 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
 109TOutSliceInfo::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
 128struct 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
 167struct 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
 187typedef map <TileCoord, BufferedTile *> TileMap;
 188
 189
 190struct 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
 212TileBuffer::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
 224TileBuffer::~TileBuffer ()
 225{
 226    delete compressor;
 227}
 228
 229
 230} // namespace
 231
 232
 233struct 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
 284TiledOutputFile::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
 300TiledOutputFile::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
 320TileBuffer*
 321TiledOutputFile::Data::getTileBuffer (int number)
 322{
 323    return tileBuffers[number % tileBuffers.size()];
 324}
 325
 326
 327TileCoord
 328TiledOutputFile::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
 426namespace {
 427
 428void
 429writeTileData (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
 477void
 478bufferedTileWrite (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
 577void
 578convertToXdr (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
 641class 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
 662TileBufferTask::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
 682TileBufferTask::~TileBufferTask ()
 683{
 684    //
 685    // Signal that the tile buffer is now free
 686    //
 687
 688    _tileBuffer->post ();
 689}
 690
 691
 692void
 693TileBufferTask::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
 833TiledOutputFile::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
 862TiledOutputFile::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
 891void
 892TiledOutputFile::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
 967TiledOutputFile::~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
 996const char *
 997TiledOutputFile::fileName () const
 998{
 999    return _data->os->fileName();
1000}
1001
1002
1003const Header &
1004TiledOutputFile::header () const
1005{
1006    return _data->header;
1007}
1008
1009
1010void	
1011TiledOutputFile::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
1092const FrameBuffer &
1093TiledOutputFile::frameBuffer () const
1094{
1095    Lock lock (*_data);
1096    return _data->frameBuffer;
1097}
1098
1099
1100void	
1101TiledOutputFile::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
1290void	
1291TiledOutputFile::writeTiles (int dx1, int dxMax, int dyMin, int dyMax, int l)
1292{
1293    writeTiles (dx1, dxMax, dyMin, dyMax, l, l);
1294}
1295
1296
1297void	
1298TiledOutputFile::writeTile (int dx, int dy, int lx, int ly)
1299{
1300    writeTiles (dx, dx, dy, dy, lx, ly);
1301}
1302
1303
1304void
1305TiledOutputFile::writeTile (int dx, int dy, int l)
1306{
1307    writeTile(dx, dy, l, l);
1308}
1309
1310
1311void	
1312TiledOutputFile::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
1418void	
1419TiledOutputFile::copyPixels (InputFile &in)
1420{
1421    copyPixels (*in.tFile());
1422}
1423
1424
1425unsigned int
1426TiledOutputFile::tileXSize () const
1427{
1428    return _data->tileDesc.xSize;
1429}
1430
1431
1432unsigned int
1433TiledOutputFile::tileYSize () const
1434{
1435    return _data->tileDesc.ySize;
1436}
1437
1438
1439LevelMode
1440TiledOutputFile::levelMode () const
1441{
1442    return _data->tileDesc.mode;
1443}
1444
1445
1446LevelRoundingMode
1447TiledOutputFile::levelRoundingMode () const
1448{
1449    return _data->tileDesc.roundingMode;
1450}
1451
1452
1453int
1454TiledOutputFile::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
1464int
1465TiledOutputFile::numXLevels () const
1466{
1467    return _data->numXLevels;
1468}
1469
1470
1471int
1472TiledOutputFile::numYLevels () const
1473{
1474    return _data->numYLevels;
1475}
1476
1477
1478bool	
1479TiledOutputFile::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
1494int
1495TiledOutputFile::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
1513int
1514TiledOutputFile::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
1530int
1531TiledOutputFile::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
1542int
1543TiledOutputFile::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
1554Box2i
1555TiledOutputFile::dataWindowForLevel (int l) const
1556{
1557    return dataWindowForLevel (l, l);
1558}
1559
1560
1561Box2i
1562TiledOutputFile::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
1580Box2i
1581TiledOutputFile::dataWindowForTile (int dx, int dy, int l) const
1582{
1583    return dataWindowForTile (dx, dy, l, l);
1584}
1585
1586
1587Box2i
1588TiledOutputFile::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
1610bool
1611TiledOutputFile::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
1620void
1621TiledOutputFile::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
1667void
1668TiledOutputFile::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