PageRenderTime 134ms CodeModel.GetById 41ms app.highlight 84ms RepoModel.GetById 1ms app.codeStats 1ms

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

https://bitbucket.org/cabalistic/ogredeps/
C++ | 1302 lines | 813 code | 259 blank | 230 comment | 83 complexity | 7727a6f1da00416ff90dd698e8463548 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 TiledInputFile
  38//
  39//-----------------------------------------------------------------------------
  40
  41#include <ImfTiledInputFile.h>
  42#include <ImfTileDescriptionAttribute.h>
  43#include <ImfChannelList.h>
  44#include <ImfMisc.h>
  45#include <ImfTiledMisc.h>
  46#include <ImfStdIO.h>
  47#include <ImfCompressor.h>
  48#include "ImathBox.h"
  49#include <ImfXdr.h>
  50#include <ImfConvert.h>
  51#include <ImfVersion.h>
  52#include <ImfTileOffsets.h>
  53#include <ImfThreading.h>
  54#include "IlmThreadPool.h"
  55#include "IlmThreadSemaphore.h"
  56#include "IlmThreadMutex.h"
  57#include "ImathVec.h"
  58#include "Iex.h"
  59#include <string>
  60#include <vector>
  61#include <algorithm>
  62#include <assert.h>
  63
  64
  65namespace Imf {
  66
  67using Imath::Box2i;
  68using Imath::V2i;
  69using std::string;
  70using std::vector;
  71using std::min;
  72using std::max;
  73using IlmThread::Mutex;
  74using IlmThread::Lock;
  75using IlmThread::Semaphore;
  76using IlmThread::Task;
  77using IlmThread::TaskGroup;
  78using IlmThread::ThreadPool;
  79
  80namespace {
  81
  82struct TInSliceInfo
  83{
  84    PixelType   typeInFrameBuffer;
  85    PixelType   typeInFile;
  86    char *      base;
  87    size_t      xStride;
  88    size_t      yStride;
  89    bool        fill;
  90    bool        skip;
  91    double      fillValue;
  92    int         xTileCoords;
  93    int         yTileCoords;
  94
  95    TInSliceInfo (PixelType typeInFrameBuffer = HALF,
  96                  PixelType typeInFile = HALF,
  97                  char *base = 0,
  98                  size_t xStride = 0,
  99                  size_t yStride = 0,
 100                  bool fill = false,
 101                  bool skip = false,
 102                  double fillValue = 0.0,
 103                  int xTileCoords = 0,
 104                  int yTileCoords = 0);
 105};
 106
 107
 108TInSliceInfo::TInSliceInfo (PixelType tifb,
 109                            PixelType tifl,
 110                            char *b,
 111                            size_t xs, size_t ys,
 112                            bool f, bool s,
 113                            double fv,
 114                            int xtc,
 115                            int ytc)
 116:
 117    typeInFrameBuffer (tifb),
 118    typeInFile (tifl),
 119    base (b),
 120    xStride (xs),
 121    yStride (ys),
 122    fill (f),
 123    skip (s),
 124    fillValue (fv),
 125    xTileCoords (xtc),
 126    yTileCoords (ytc)
 127{
 128    // empty
 129}
 130
 131
 132struct TileBuffer
 133{
 134    const char *	uncompressedData;
 135    char *		buffer;
 136    int			dataSize;
 137    Compressor *	compressor;
 138    Compressor::Format	format;
 139    int			dx;
 140    int			dy;
 141    int			lx;
 142    int			ly;
 143    bool		hasException;
 144    string		exception;
 145
 146     TileBuffer (Compressor * const comp);
 147    ~TileBuffer ();
 148
 149    inline void		wait () {_sem.wait();}
 150    inline void		post () {_sem.post();}
 151
 152 protected:
 153
 154    Semaphore _sem;
 155};
 156
 157
 158TileBuffer::TileBuffer (Compressor *comp):
 159    uncompressedData (0),
 160    dataSize (0),
 161    compressor (comp),
 162    format (defaultFormat (compressor)),
 163    dx (-1),
 164    dy (-1),
 165    lx (-1),
 166    ly (-1),
 167    hasException (false),
 168    exception (),
 169    _sem (1)
 170{
 171    // empty
 172}
 173
 174
 175TileBuffer::~TileBuffer ()
 176{
 177    delete compressor;
 178}
 179
 180} // namespace
 181
 182
 183//
 184// struct TiledInputFile::Data stores things that will be
 185// needed between calls to readTile()
 186//
 187
 188struct TiledInputFile::Data: public Mutex
 189{
 190    Header	    header;		    // the image header
 191    TileDescription tileDesc;		    // describes the tile layout
 192    int		    version;		    // file's version
 193    FrameBuffer	    frameBuffer;	    // framebuffer to write into
 194    LineOrder	    lineOrder;		    // the file's lineorder
 195    int		    minX;		    // data window's min x coord
 196    int		    maxX;		    // data window's max x coord
 197    int		    minY;		    // data window's min y coord
 198    int		    maxY;		    // data window's max x coord
 199
 200    int		    numXLevels;		    // number of x levels
 201    int		    numYLevels;		    // number of y levels
 202    int *	    numXTiles;		    // number of x tiles at a level
 203    int *	    numYTiles;		    // number of y tiles at a level
 204
 205    TileOffsets	    tileOffsets;	    // stores offsets in file for
 206					    // each tile
 207
 208    bool	    fileIsComplete;	    // True if no tiles are missing
 209    					    // in the file
 210
 211    Int64	    currentPosition;        // file offset for current tile,
 212					    // used to prevent unnecessary
 213					    // seeking
 214
 215    vector<TInSliceInfo> slices;	    // info about channels in file
 216    IStream *	    is;			    // file stream to read from
 217
 218    bool	    deleteStream;	    // should we delete the stream
 219					    // ourselves? or does someone
 220					    // else do it?
 221
 222    size_t	    bytesPerPixel;          // size of an uncompressed pixel
 223
 224    size_t	    maxBytesPerTileLine;    // combined size of a line
 225					    // over all channels
 226
 227    
 228    vector<TileBuffer*> tileBuffers;        // each holds a single tile
 229    size_t          tileBufferSize;	    // size of the tile buffers
 230
 231     Data (bool deleteStream, int numThreads);
 232    ~Data ();
 233
 234    inline TileBuffer * getTileBuffer (int number);
 235					    // hash function from tile indices
 236					    // into our vector of tile buffers
 237};
 238
 239
 240TiledInputFile::Data::Data (bool del, int numThreads):
 241    numXTiles (0),
 242    numYTiles (0),
 243    is (0),
 244    deleteStream (del)
 245{
 246    //
 247    // We need at least one tileBuffer, but if threading is used,
 248    // to keep n threads busy we need 2*n tileBuffers
 249    //
 250
 251    tileBuffers.resize (max (1, 2 * numThreads));
 252}
 253
 254
 255TiledInputFile::Data::~Data ()
 256{
 257    delete [] numXTiles;
 258    delete [] numYTiles;
 259
 260    if (deleteStream)
 261	delete is;
 262
 263    for (size_t i = 0; i < tileBuffers.size(); i++)
 264        delete tileBuffers[i];
 265}
 266
 267
 268TileBuffer*
 269TiledInputFile::Data::getTileBuffer (int number)
 270{
 271    return tileBuffers[number % tileBuffers.size()];
 272}
 273
 274
 275namespace {
 276
 277void
 278readTileData (TiledInputFile::Data *ifd,
 279	      int dx, int dy,
 280	      int lx, int ly,
 281              char *&buffer,
 282              int &dataSize)
 283{
 284    //
 285    // Read a single tile block from the file and into the array pointed
 286    // to by buffer.  If the file is memory-mapped, then we change where
 287    // buffer points instead of writing into the array (hence buffer needs
 288    // to be a reference to a char *).
 289    //
 290
 291    //
 292    // Look up the location for this tile in the Index and
 293    // seek to that position if necessary
 294    //
 295    
 296    Int64 tileOffset = ifd->tileOffsets (dx, dy, lx, ly);
 297
 298    if (tileOffset == 0)
 299    {
 300        THROW (Iex::InputExc, "Tile (" << dx << ", " << dy << ", " <<
 301			      lx << ", " << ly << ") is missing.");
 302    }
 303
 304    if (ifd->currentPosition != tileOffset)
 305        ifd->is->seekg (tileOffset);
 306
 307    //
 308    // Read the first few bytes of the tile (the header).
 309    // Verify that the tile coordinates and the level number
 310    // are correct.
 311    //
 312    
 313    int tileXCoord, tileYCoord, levelX, levelY;
 314
 315    Xdr::read <StreamIO> (*ifd->is, tileXCoord);
 316    Xdr::read <StreamIO> (*ifd->is, tileYCoord);
 317    Xdr::read <StreamIO> (*ifd->is, levelX);
 318    Xdr::read <StreamIO> (*ifd->is, levelY);
 319    Xdr::read <StreamIO> (*ifd->is, dataSize);
 320
 321    if (tileXCoord != dx)
 322        throw Iex::InputExc ("Unexpected tile x coordinate.");
 323
 324    if (tileYCoord != dy)
 325        throw Iex::InputExc ("Unexpected tile y coordinate.");
 326
 327    if (levelX != lx)
 328        throw Iex::InputExc ("Unexpected tile x level number coordinate.");
 329
 330    if (levelY != ly)
 331        throw Iex::InputExc ("Unexpected tile y level number coordinate.");
 332
 333    if (dataSize > (int) ifd->tileBufferSize)
 334        throw Iex::InputExc ("Unexpected tile block length.");
 335
 336    //
 337    // Read the pixel data.
 338    //
 339
 340    if (ifd->is->isMemoryMapped ())
 341        buffer = ifd->is->readMemoryMapped (dataSize);
 342    else
 343        ifd->is->read (buffer, dataSize);
 344
 345    //
 346    // Keep track of which tile is the next one in
 347    // the file, so that we can avoid redundant seekg()
 348    // operations (seekg() can be fairly expensive).
 349    //
 350    
 351    ifd->currentPosition = tileOffset + 5 * Xdr::size<int>() + dataSize;
 352}
 353
 354
 355void
 356readNextTileData (TiledInputFile::Data *ifd,
 357		  int &dx, int &dy,
 358		  int &lx, int &ly,
 359                  char * & buffer,
 360		  int &dataSize)
 361{
 362    //
 363    // Read the next tile block from the file
 364    //
 365
 366    //
 367    // Read the first few bytes of the tile (the header).
 368    //
 369
 370    Xdr::read <StreamIO> (*ifd->is, dx);
 371    Xdr::read <StreamIO> (*ifd->is, dy);
 372    Xdr::read <StreamIO> (*ifd->is, lx);
 373    Xdr::read <StreamIO> (*ifd->is, ly);
 374    Xdr::read <StreamIO> (*ifd->is, dataSize);
 375
 376    if (dataSize > (int) ifd->tileBufferSize)
 377        throw Iex::InputExc ("Unexpected tile block length.");
 378    
 379    //
 380    // Read the pixel data.
 381    //
 382
 383    ifd->is->read (buffer, dataSize);
 384    
 385    //
 386    // Keep track of which tile is the next one in
 387    // the file, so that we can avoid redundant seekg()
 388    // operations (seekg() can be fairly expensive).
 389    //
 390
 391    ifd->currentPosition += 5 * Xdr::size<int>() + dataSize;
 392}
 393
 394
 395//
 396// A TileBufferTask encapsulates the task of uncompressing
 397// a single tile and copying it into the frame buffer.
 398//
 399
 400class TileBufferTask : public Task
 401{
 402  public:
 403
 404    TileBufferTask (TaskGroup *group,
 405                    TiledInputFile::Data *ifd,
 406		    TileBuffer *tileBuffer);
 407                    
 408    virtual ~TileBufferTask ();
 409
 410    virtual void		execute ();
 411    
 412  private:
 413
 414    TiledInputFile::Data *	_ifd;
 415    TileBuffer *		_tileBuffer;
 416};
 417
 418
 419TileBufferTask::TileBufferTask
 420    (TaskGroup *group,
 421     TiledInputFile::Data *ifd,
 422     TileBuffer *tileBuffer)
 423:
 424    Task (group),
 425    _ifd (ifd),
 426    _tileBuffer (tileBuffer)
 427{
 428    // empty
 429}
 430
 431
 432TileBufferTask::~TileBufferTask ()
 433{
 434    //
 435    // Signal that the tile buffer is now free
 436    //
 437
 438    _tileBuffer->post ();
 439}
 440
 441
 442void
 443TileBufferTask::execute ()
 444{
 445    try
 446    {
 447        //
 448        // Calculate information about the tile
 449        //
 450    
 451        Box2i tileRange = Imf::dataWindowForTile (_ifd->tileDesc,
 452                                                  _ifd->minX, _ifd->maxX,
 453                                                  _ifd->minY, _ifd->maxY,
 454                                                  _tileBuffer->dx,
 455                                                  _tileBuffer->dy,
 456                                                  _tileBuffer->lx,
 457                                                  _tileBuffer->ly);
 458    
 459        int numPixelsPerScanLine = tileRange.max.x - tileRange.min.x + 1;
 460    
 461        int numPixelsInTile = numPixelsPerScanLine *
 462                            (tileRange.max.y - tileRange.min.y + 1);
 463    
 464        int sizeOfTile = _ifd->bytesPerPixel * numPixelsInTile;
 465    
 466    
 467        //
 468        // Uncompress the data, if necessary
 469        //
 470    
 471        if (_tileBuffer->compressor && _tileBuffer->dataSize < sizeOfTile)
 472        {
 473            _tileBuffer->format = _tileBuffer->compressor->format();
 474
 475            _tileBuffer->dataSize = _tileBuffer->compressor->uncompressTile
 476		(_tileBuffer->buffer, _tileBuffer->dataSize,
 477		 tileRange, _tileBuffer->uncompressedData);
 478        }
 479        else
 480        {
 481            //
 482            // If the line is uncompressed, it's in XDR format,
 483            // regardless of the compressor's output format.
 484            //
 485    
 486            _tileBuffer->format = Compressor::XDR;
 487            _tileBuffer->uncompressedData = _tileBuffer->buffer;
 488        }
 489    
 490        //
 491        // Convert the tile of pixel data back from the machine-independent
 492	// representation, and store the result in the frame buffer.
 493        //
 494    
 495        const char *readPtr = _tileBuffer->uncompressedData;
 496                                                        // points to where we
 497                                                        // read from in the
 498                                                        // tile block
 499        
 500        //
 501        // Iterate over the scan lines in the tile.
 502        //
 503    
 504        for (int y = tileRange.min.y; y <= tileRange.max.y; ++y)
 505        {
 506            //
 507            // Iterate over all image channels.
 508            //
 509            
 510            for (unsigned int i = 0; i < _ifd->slices.size(); ++i)
 511            {
 512                const TInSliceInfo &slice = _ifd->slices[i];
 513    
 514                //
 515                // These offsets are used to facilitate both
 516                // absolute and tile-relative pixel coordinates.
 517                //
 518            
 519                int xOffset = slice.xTileCoords * tileRange.min.x;
 520                int yOffset = slice.yTileCoords * tileRange.min.y;
 521    
 522                //
 523                // Fill the frame buffer with pixel data.
 524                //
 525    
 526                if (slice.skip)
 527                {
 528                    //
 529                    // The file contains data for this channel, but
 530                    // the frame buffer contains no slice for this channel.
 531                    //
 532    
 533                    skipChannel (readPtr, slice.typeInFile,
 534                                 numPixelsPerScanLine);
 535                }
 536                else
 537                {
 538                    //
 539                    // The frame buffer contains a slice for this channel.
 540                    //
 541    
 542                    char *writePtr = slice.base +
 543                                     (y - yOffset) * slice.yStride +
 544                                     (tileRange.min.x - xOffset) *
 545                                     slice.xStride;
 546
 547                    char *endPtr = writePtr +
 548                                   (numPixelsPerScanLine - 1) * slice.xStride;
 549                                    
 550                    copyIntoFrameBuffer (readPtr, writePtr, endPtr,
 551                                         slice.xStride,
 552                                         slice.fill, slice.fillValue,
 553                                         _tileBuffer->format,
 554                                         slice.typeInFrameBuffer,
 555                                         slice.typeInFile);
 556                }
 557            }
 558        }
 559    }
 560    catch (std::exception &e)
 561    {
 562        if (!_tileBuffer->hasException)
 563        {
 564            _tileBuffer->exception = e.what ();
 565            _tileBuffer->hasException = true;
 566        }
 567    }
 568    catch (...)
 569    {
 570        if (!_tileBuffer->hasException)
 571        {
 572            _tileBuffer->exception = "unrecognized exception";
 573            _tileBuffer->hasException = true;
 574        }
 575    }
 576}
 577
 578
 579TileBufferTask *
 580newTileBufferTask
 581    (TaskGroup *group,
 582     TiledInputFile::Data *ifd,
 583     int number,
 584     int dx, int dy,
 585     int lx, int ly)
 586{
 587    //
 588    // Wait for a tile buffer to become available,
 589    // fill the buffer with raw data from the file,
 590    // and create a new TileBufferTask whose execute()
 591    // method will uncompress the tile and copy the
 592    // tile's pixels into the frame buffer.
 593    //
 594
 595    TileBuffer *tileBuffer = ifd->getTileBuffer (number);
 596
 597    try
 598    {
 599	tileBuffer->wait();
 600	
 601	tileBuffer->dx = dx;
 602	tileBuffer->dy = dy;
 603	tileBuffer->lx = lx;
 604	tileBuffer->ly = ly;
 605
 606	tileBuffer->uncompressedData = 0;
 607
 608	readTileData (ifd, dx, dy, lx, ly,
 609		      tileBuffer->buffer,
 610		      tileBuffer->dataSize);
 611    }
 612    catch (...)
 613    {
 614	//
 615	// Reading from the file caused an exception.
 616	// Signal that the tile buffer is free, and
 617	// re-throw the exception.
 618	//
 619
 620	tileBuffer->post();
 621	throw;
 622    }
 623
 624    return new TileBufferTask (group, ifd, tileBuffer);
 625}
 626
 627
 628} // namespace
 629
 630
 631TiledInputFile::TiledInputFile (const char fileName[], int numThreads):
 632    _data (new Data (true, numThreads))
 633{
 634    //
 635    // This constructor is called when a user
 636    // explicitly wants to read a tiled file.
 637    //
 638
 639    try
 640    {
 641	_data->is = new StdIFStream (fileName);
 642	_data->header.readFrom (*_data->is, _data->version);
 643	initialize();
 644    }
 645    catch (Iex::BaseExc &e)
 646    {
 647	delete _data;
 648
 649	REPLACE_EXC (e, "Cannot open image file "
 650			"\"" << fileName << "\". " << e);
 651	throw;
 652    }
 653    catch (...)
 654    {
 655	delete _data;
 656        throw;
 657    }
 658}
 659
 660
 661TiledInputFile::TiledInputFile (IStream &is, int numThreads):
 662    _data (new Data (false, numThreads))
 663{
 664    //
 665    // This constructor is called when a user
 666    // explicitly wants to read a tiled file.
 667    //
 668
 669    try
 670    {
 671	_data->is = &is;
 672	_data->header.readFrom (*_data->is, _data->version);
 673	initialize();
 674    }
 675    catch (Iex::BaseExc &e)
 676    {
 677	delete _data;
 678
 679	REPLACE_EXC (e, "Cannot open image file "
 680			"\"" << is.fileName() << "\". " << e);
 681	throw;
 682    }
 683    catch (...)
 684    {
 685	delete _data;
 686        throw;
 687    }
 688}
 689
 690
 691TiledInputFile::TiledInputFile
 692    (const Header &header,
 693     IStream *is,
 694     int version,
 695     int numThreads)
 696:
 697    _data (new Data (false, numThreads))
 698{
 699    //
 700    // This constructor called by class Imf::InputFile
 701    // when a user wants to just read an image file, and
 702    // doesn't care or know if the file is tiled.
 703    //
 704
 705    _data->is = is;
 706    _data->header = header;
 707    _data->version = version;
 708    initialize();
 709}
 710
 711
 712void
 713TiledInputFile::initialize ()
 714{
 715    if (!isTiled (_data->version))
 716	throw Iex::ArgExc ("Expected a tiled file but the file is not tiled.");
 717
 718    _data->header.sanityCheck (true);
 719
 720    _data->tileDesc = _data->header.tileDescription();
 721    _data->lineOrder = _data->header.lineOrder();
 722
 723    //
 724    // Save the dataWindow information
 725    //
 726    
 727    const Box2i &dataWindow = _data->header.dataWindow();
 728    _data->minX = dataWindow.min.x;
 729    _data->maxX = dataWindow.max.x;
 730    _data->minY = dataWindow.min.y;
 731    _data->maxY = dataWindow.max.y;
 732
 733    //
 734    // Precompute level and tile information to speed up utility functions
 735    //
 736
 737    precalculateTileInfo (_data->tileDesc,
 738			  _data->minX, _data->maxX,
 739			  _data->minY, _data->maxY,
 740			  _data->numXTiles, _data->numYTiles,
 741			  _data->numXLevels, _data->numYLevels);    
 742
 743    _data->bytesPerPixel = calculateBytesPerPixel (_data->header);
 744
 745    _data->maxBytesPerTileLine = _data->bytesPerPixel * _data->tileDesc.xSize;
 746
 747    _data->tileBufferSize = _data->maxBytesPerTileLine * _data->tileDesc.ySize;
 748
 749    //
 750    // Create all the TileBuffers and allocate their internal buffers
 751    //
 752
 753    for (size_t i = 0; i < _data->tileBuffers.size(); i++)
 754    {
 755        _data->tileBuffers[i] = new TileBuffer (newTileCompressor
 756						  (_data->header.compression(),
 757						   _data->maxBytesPerTileLine,
 758						   _data->tileDesc.ySize,
 759						   _data->header));
 760
 761        if (!_data->is->isMemoryMapped ())
 762            _data->tileBuffers[i]->buffer = new char [_data->tileBufferSize];
 763    }
 764
 765    _data->tileOffsets = TileOffsets (_data->tileDesc.mode,
 766				      _data->numXLevels,
 767				      _data->numYLevels,
 768				      _data->numXTiles,
 769				      _data->numYTiles);
 770
 771    _data->tileOffsets.readFrom (*(_data->is), _data->fileIsComplete);
 772
 773    _data->currentPosition = _data->is->tellg();
 774}
 775
 776
 777TiledInputFile::~TiledInputFile ()
 778{
 779    if (!_data->is->isMemoryMapped())
 780        for (size_t i = 0; i < _data->tileBuffers.size(); i++)
 781            delete [] _data->tileBuffers[i]->buffer;
 782
 783    delete _data;
 784}
 785
 786
 787const char *
 788TiledInputFile::fileName () const
 789{
 790    return _data->is->fileName();
 791}
 792
 793
 794const Header &
 795TiledInputFile::header () const
 796{
 797    return _data->header;
 798}
 799
 800
 801int
 802TiledInputFile::version () const
 803{
 804    return _data->version;
 805}
 806
 807
 808void	
 809TiledInputFile::setFrameBuffer (const FrameBuffer &frameBuffer)
 810{
 811    Lock lock (*_data);
 812
 813    //
 814    // Set the frame buffer
 815    //
 816
 817    //
 818    // Check if the new frame buffer descriptor is
 819    // compatible with the image file header.
 820    //
 821
 822    const ChannelList &channels = _data->header.channels();
 823
 824    for (FrameBuffer::ConstIterator j = frameBuffer.begin();
 825         j != frameBuffer.end();
 826         ++j)
 827    {
 828        ChannelList::ConstIterator i = channels.find (j.name());
 829
 830        if (i == channels.end())
 831            continue;
 832
 833        if (i.channel().xSampling != j.slice().xSampling ||
 834            i.channel().ySampling != j.slice().ySampling)
 835            THROW (Iex::ArgExc, "X and/or y subsampling factors "
 836				"of \"" << i.name() << "\" channel "
 837				"of input file \"" << fileName() << "\" are "
 838				"not compatible with the frame buffer's "
 839				"subsampling factors.");
 840    }
 841
 842    //
 843    // Initialize the slice table for readPixels().
 844    //
 845
 846    vector<TInSliceInfo> slices;
 847    ChannelList::ConstIterator i = channels.begin();
 848
 849    for (FrameBuffer::ConstIterator j = frameBuffer.begin();
 850         j != frameBuffer.end();
 851         ++j)
 852    {
 853        while (i != channels.end() && strcmp (i.name(), j.name()) < 0)
 854        {
 855            //
 856            // Channel i is present in the file but not
 857            // in the frame buffer; data for channel i
 858            // will be skipped during readPixels().
 859            //
 860
 861            slices.push_back (TInSliceInfo (i.channel().type,
 862					    i.channel().type,
 863					    0,      // base
 864					    0,      // xStride
 865					    0,      // yStride
 866					    false,  // fill
 867					    true,   // skip
 868					    0.0));  // fillValue
 869            ++i;
 870        }
 871
 872        bool fill = false;
 873
 874        if (i == channels.end() || strcmp (i.name(), j.name()) > 0)
 875        {
 876            //
 877            // Channel i is present in the frame buffer, but not in the file.
 878            // In the frame buffer, slice j will be filled with a default value.
 879            //
 880
 881            fill = true;
 882        }
 883
 884        slices.push_back (TInSliceInfo (j.slice().type,
 885                                        fill? j.slice().type: i.channel().type,
 886                                        j.slice().base,
 887                                        j.slice().xStride,
 888                                        j.slice().yStride,
 889                                        fill,
 890                                        false, // skip
 891                                        j.slice().fillValue,
 892                                        (j.slice().xTileCoords)? 1: 0,
 893                                        (j.slice().yTileCoords)? 1: 0));
 894
 895        if (i != channels.end() && !fill)
 896            ++i;
 897    }
 898
 899    while (i != channels.end())
 900    {
 901	//
 902	// Channel i is present in the file but not
 903	// in the frame buffer; data for channel i
 904	// will be skipped during readPixels().
 905	//
 906
 907	slices.push_back (TInSliceInfo (i.channel().type,
 908					i.channel().type,
 909					0, // base
 910					0, // xStride
 911					0, // yStride
 912					false,  // fill
 913					true, // skip
 914					0.0)); // fillValue
 915	++i;
 916    }
 917
 918    //
 919    // Store the new frame buffer.
 920    //
 921
 922    _data->frameBuffer = frameBuffer;
 923    _data->slices = slices;
 924}
 925
 926
 927const FrameBuffer &
 928TiledInputFile::frameBuffer () const
 929{
 930    Lock lock (*_data);
 931    return _data->frameBuffer;
 932}
 933
 934
 935bool
 936TiledInputFile::isComplete () const
 937{
 938    return _data->fileIsComplete;
 939}
 940
 941
 942void
 943TiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int lx, int ly)
 944{
 945    //
 946    // Read a range of tiles from the file into the framebuffer
 947    //
 948
 949    try
 950    {
 951        Lock lock (*_data);
 952
 953        if (_data->slices.size() == 0)
 954            throw Iex::ArgExc ("No frame buffer specified "
 955			       "as pixel data destination.");
 956        
 957        //
 958        // Determine the first and last tile coordinates in both dimensions.
 959        // We always attempt to read the range of tiles in the order that
 960        // they are stored in the file.
 961        //
 962                               
 963        if (dx1 > dx2)
 964            std::swap (dx1, dx2);
 965        
 966        if (dy1 > dy2)
 967            std::swap (dy1, dy2);
 968        
 969        int dyStart = dy1;
 970	int dyStop  = dy2 + 1;
 971	int dY      = 1;
 972
 973        if (_data->lineOrder == DECREASING_Y)
 974        {
 975            dyStart = dy2;
 976            dyStop  = dy1 - 1;
 977            dY      = -1;
 978        }
 979
 980        //
 981        // Create a task group for all tile buffer tasks.  When the
 982	// task group goes out of scope, the destructor waits until
 983	// all tasks are complete.
 984        //
 985        
 986        {
 987            TaskGroup taskGroup;
 988            int tileNumber = 0;
 989    
 990            for (int dy = dyStart; dy != dyStop; dy += dY)
 991            {
 992                for (int dx = dx1; dx <= dx2; dx++)
 993                {
 994                    if (!isValidTile (dx, dy, lx, ly))
 995                        THROW (Iex::ArgExc,
 996			       "Tile (" << dx << ", " << dy << ", " <<
 997			       lx << "," << ly << ") is not a valid tile.");
 998                    
 999                    ThreadPool::addGlobalTask (newTileBufferTask (&taskGroup,
1000                                                                  _data,
1001                                                                  tileNumber++,
1002                                                                  dx, dy,
1003                                                                  lx, ly));
1004                }
1005            }
1006
1007	    //
1008            // finish all tasks
1009	    //
1010        }
1011
1012	//
1013	// Exeption handling:
1014	//
1015	// TileBufferTask::execute() may have encountered exceptions, but
1016	// those exceptions occurred in another thread, not in the thread
1017	// that is executing this call to TiledInputFile::readTiles().
1018	// TileBufferTask::execute() has caught all exceptions and stored
1019	// the exceptions' what() strings in the tile buffers.
1020	// Now we check if any tile buffer contains a stored exception; if
1021	// this is the case then we re-throw the exception in this thread.
1022	// (It is possible that multiple tile buffers contain stored
1023	// exceptions.  We re-throw the first exception we find and
1024	// ignore all others.)
1025	//
1026
1027	const string *exception = 0;
1028
1029        for (int i = 0; i < _data->tileBuffers.size(); ++i)
1030	{
1031            TileBuffer *tileBuffer = _data->tileBuffers[i];
1032
1033	    if (tileBuffer->hasException && !exception)
1034		exception = &tileBuffer->exception;
1035
1036	    tileBuffer->hasException = false;
1037	}
1038
1039	if (exception)
1040	    throw Iex::IoExc (*exception);
1041    }
1042    catch (Iex::BaseExc &e)
1043    {
1044        REPLACE_EXC (e, "Error reading pixel data from image "
1045                        "file \"" << fileName() << "\". " << e);
1046        throw;
1047    }
1048}
1049
1050
1051void	
1052TiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int l)
1053{
1054    readTiles (dx1, dx2, dy1, dy2, l, l);
1055}
1056
1057
1058void	
1059TiledInputFile::readTile (int dx, int dy, int lx, int ly)
1060{
1061    readTiles (dx, dx, dy, dy, lx, ly);
1062}
1063
1064
1065void	
1066TiledInputFile::readTile (int dx, int dy, int l)
1067{
1068    readTile (dx, dy, l, l);
1069}
1070
1071
1072void
1073TiledInputFile::rawTileData (int &dx, int &dy,
1074			     int &lx, int &ly,
1075                             const char *&pixelData,
1076			     int &pixelDataSize)
1077{
1078    try
1079    {
1080        Lock lock (*_data);
1081
1082        if (!isValidTile (dx, dy, lx, ly))
1083            throw Iex::ArgExc ("Tried to read a tile outside "
1084			       "the image file's data window.");
1085
1086        TileBuffer *tileBuffer = _data->getTileBuffer (0);
1087        
1088        readNextTileData (_data, dx, dy, lx, ly,
1089			  tileBuffer->buffer,
1090                          pixelDataSize);
1091
1092        pixelData = tileBuffer->buffer;
1093    }
1094    catch (Iex::BaseExc &e)
1095    {
1096        REPLACE_EXC (e, "Error reading pixel data from image "
1097			"file \"" << fileName() << "\". " << e);
1098        throw;
1099    }
1100}
1101
1102
1103unsigned int
1104TiledInputFile::tileXSize () const
1105{
1106    return _data->tileDesc.xSize;
1107}
1108
1109
1110unsigned int
1111TiledInputFile::tileYSize () const
1112{
1113    return _data->tileDesc.ySize;
1114}
1115
1116
1117LevelMode
1118TiledInputFile::levelMode () const
1119{
1120    return _data->tileDesc.mode;
1121}
1122
1123
1124LevelRoundingMode
1125TiledInputFile::levelRoundingMode () const
1126{
1127    return _data->tileDesc.roundingMode;
1128}
1129
1130
1131int
1132TiledInputFile::numLevels () const
1133{
1134    if (levelMode() == RIPMAP_LEVELS)
1135	THROW (Iex::LogicExc, "Error calling numLevels() on image "
1136			      "file \"" << fileName() << "\" "
1137			      "(numLevels() is not defined for files "
1138			      "with RIPMAP level mode).");
1139
1140    return _data->numXLevels;
1141}
1142
1143
1144int
1145TiledInputFile::numXLevels () const
1146{
1147    return _data->numXLevels;
1148}
1149
1150
1151int
1152TiledInputFile::numYLevels () const
1153{
1154    return _data->numYLevels;
1155}
1156
1157
1158bool	
1159TiledInputFile::isValidLevel (int lx, int ly) const
1160{
1161    if (lx < 0 || ly < 0)
1162	return false;
1163
1164    if (levelMode() == MIPMAP_LEVELS && lx != ly)
1165	return false;
1166
1167    if (lx >= numXLevels() || ly >= numYLevels())
1168	return false;
1169
1170    return true;
1171}
1172
1173
1174int
1175TiledInputFile::levelWidth (int lx) const
1176{
1177    try
1178    {
1179        return levelSize (_data->minX, _data->maxX, lx,
1180			  _data->tileDesc.roundingMode);
1181    }
1182    catch (Iex::BaseExc &e)
1183    {
1184	REPLACE_EXC (e, "Error calling levelWidth() on image "
1185			"file \"" << fileName() << "\". " << e);
1186	throw;
1187    }
1188}
1189
1190
1191int
1192TiledInputFile::levelHeight (int ly) const
1193{
1194    try
1195    {
1196        return levelSize (_data->minY, _data->maxY, ly,
1197                          _data->tileDesc.roundingMode);
1198    }
1199    catch (Iex::BaseExc &e)
1200    {
1201	REPLACE_EXC (e, "Error calling levelHeight() on image "
1202			"file \"" << fileName() << "\". " << e);
1203	throw;
1204    }
1205}
1206
1207
1208int
1209TiledInputFile::numXTiles (int lx) const
1210{
1211    if (lx < 0 || lx >= _data->numXLevels)
1212    {
1213        THROW (Iex::ArgExc, "Error calling numXTiles() on image "
1214			    "file \"" << _data->is->fileName() << "\" "
1215			    "(Argument is not in valid range).");
1216
1217    }
1218    
1219    return _data->numXTiles[lx];
1220}
1221
1222
1223int
1224TiledInputFile::numYTiles (int ly) const
1225{
1226    if (ly < 0 || ly >= _data->numYLevels)
1227    {
1228        THROW (Iex::ArgExc, "Error calling numYTiles() on image "
1229			    "file \"" << _data->is->fileName() << "\" "
1230			    "(Argument is not in valid range).");
1231    }
1232    
1233    return _data->numYTiles[ly];
1234}
1235
1236
1237Box2i
1238TiledInputFile::dataWindowForLevel (int l) const
1239{
1240    return dataWindowForLevel (l, l);
1241}
1242
1243
1244Box2i
1245TiledInputFile::dataWindowForLevel (int lx, int ly) const
1246{
1247    try
1248    {
1249	return Imf::dataWindowForLevel (_data->tileDesc,
1250			        	_data->minX, _data->maxX,
1251				        _data->minY, _data->maxY,
1252				        lx, ly);
1253    }
1254    catch (Iex::BaseExc &e)
1255    {
1256	REPLACE_EXC (e, "Error calling dataWindowForLevel() on image "
1257			"file \"" << fileName() << "\". " << e);
1258	throw;
1259    }
1260}
1261
1262
1263Box2i
1264TiledInputFile::dataWindowForTile (int dx, int dy, int l) const
1265{
1266    return dataWindowForTile (dx, dy, l, l);
1267}
1268
1269
1270Box2i
1271TiledInputFile::dataWindowForTile (int dx, int dy, int lx, int ly) const
1272{
1273    try
1274    {
1275	if (!isValidTile (dx, dy, lx, ly))
1276	    throw Iex::ArgExc ("Arguments not in valid range.");
1277
1278        return Imf::dataWindowForTile (_data->tileDesc,
1279				       _data->minX, _data->maxX,
1280				       _data->minY, _data->maxY,
1281				       dx, dy, lx, ly);
1282    }
1283    catch (Iex::BaseExc &e)
1284    {
1285	REPLACE_EXC (e, "Error calling dataWindowForTile() on image "
1286			"file \"" << fileName() << "\". " << e);
1287	throw;
1288    }
1289}
1290
1291
1292bool
1293TiledInputFile::isValidTile (int dx, int dy, int lx, int ly) const
1294{
1295    return ((lx < _data->numXLevels && lx >= 0) &&
1296            (ly < _data->numYLevels && ly >= 0) &&
1297            (dx < _data->numXTiles[lx] && dx >= 0) &&
1298            (dy < _data->numYTiles[ly] && dy >= 0));
1299}
1300
1301
1302} // namespace Imf