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 1303