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// 38// class ScanLineInputFile 39// 40//----------------------------------------------------------------------------- 41 42#include <ImfScanLineInputFile.h> 43#include <ImfChannelList.h> 44#include <ImfMisc.h> 45#include <ImfStdIO.h> 46#include <ImfCompressor.h> 47#include "ImathBox.h" 48#include "ImathFun.h" 49#include <ImfXdr.h> 50#include <ImfConvert.h> 51#include <ImfThreading.h> 52#include "IlmThreadPool.h" 53#include "IlmThreadSemaphore.h" 54#include "IlmThreadMutex.h" 55#include "Iex.h" 56#include <string> 57#include <vector> 58#include <assert.h> 59 60 61namespace Imf { 62 63using Imath::Box2i; 64using Imath::divp; 65using Imath::modp; 66using std::string; 67using std::vector; 68using std::ifstream; 69using std::min; 70using std::max; 71using IlmThread::Mutex; 72using IlmThread::Lock; 73using IlmThread::Semaphore; 74using IlmThread::Task; 75using IlmThread::TaskGroup; 76using IlmThread::ThreadPool; 77 78namespace { 79 80struct InSliceInfo 81{ 82 PixelType typeInFrameBuffer; 83 PixelType typeInFile; 84 char * base; 85 size_t xStride; 86 size_t yStride; 87 int xSampling; 88 int ySampling; 89 bool fill; 90 bool skip; 91 double fillValue; 92 93 InSliceInfo (PixelType typeInFrameBuffer = HALF, 94 PixelType typeInFile = HALF, 95 char *base = 0, 96 size_t xStride = 0, 97 size_t yStride = 0, 98 int xSampling = 1, 99 int ySampling = 1, 100 bool fill = false, 101 bool skip = false, 102 double fillValue = 0.0); 103}; 104 105 106InSliceInfo::InSliceInfo (PixelType tifb, 107 PixelType tifl, 108 char *b, 109 size_t xs, size_t ys, 110 int xsm, int ysm, 111 bool f, bool s, 112 double fv) 113: 114 typeInFrameBuffer (tifb), 115 typeInFile (tifl), 116 base (b), 117 xStride (xs), 118 yStride (ys), 119 xSampling (xsm), 120 ySampling (ysm), 121 fill (f), 122 skip (s), 123 fillValue (fv) 124{ 125 // empty 126} 127 128 129struct LineBuffer 130{ 131 const char * uncompressedData; 132 char * buffer; 133 int dataSize; 134 int minY; 135 int maxY; 136 Compressor * compressor; 137 Compressor::Format format; 138 int number; 139 bool hasException; 140 string exception; 141 142 LineBuffer (Compressor * const comp); 143 ~LineBuffer (); 144 145 inline void wait () {_sem.wait();} 146 inline void post () {_sem.post();} 147 148 private: 149 150 Semaphore _sem; 151}; 152 153 154LineBuffer::LineBuffer (Compressor *comp): 155 uncompressedData (0), 156 buffer (0), 157 dataSize (0), 158 compressor (comp), 159 format (defaultFormat(compressor)), 160 number (-1), 161 hasException (false), 162 exception (), 163 _sem (1) 164{ 165 // empty 166} 167 168 169LineBuffer::~LineBuffer () 170{ 171 delete compressor; 172} 173 174} // namespace 175 176 177struct ScanLineInputFile::Data: public Mutex 178{ 179 Header header; // the image header 180 int version; // file's version 181 FrameBuffer frameBuffer; // framebuffer to write into 182 LineOrder lineOrder; // order of the scanlines in file 183 int minX; // data window's min x coord 184 int maxX; // data window's max x coord 185 int minY; // data window's min y coord 186 int maxY; // data window's max x coord 187 vector<Int64> lineOffsets; // stores offsets in file for 188 // each line 189 bool fileIsComplete; // True if no scanlines are missing 190 // in the file 191 int nextLineBufferMinY; // minimum y of the next linebuffer 192 vector<size_t> bytesPerLine; // combined size of a line over all 193 // channels 194 vector<size_t> offsetInLineBuffer; // offset for each scanline in its 195 // linebuffer 196 vector<InSliceInfo> slices; // info about channels in file 197 IStream * is; // file stream to read from 198 199 vector<LineBuffer*> lineBuffers; // each holds one line buffer 200 int linesInBuffer; // number of scanlines each buffer 201 // holds 202 size_t lineBufferSize; // size of the line buffer 203 204 Data (IStream *is, int numThreads); 205 ~Data (); 206 207 inline LineBuffer * getLineBuffer (int number); // hash function from line 208 // buffer indices into our 209 // vector of line buffers 210}; 211 212 213ScanLineInputFile::Data::Data (IStream *is, int numThreads): 214 is (is) 215{ 216 // 217 // We need at least one lineBuffer, but if threading is used, 218 // to keep n threads busy we need 2*n lineBuffers 219 // 220 221 lineBuffers.resize (max (1, 2 * numThreads)); 222} 223 224 225ScanLineInputFile::Data::~Data () 226{ 227 for (size_t i = 0; i < lineBuffers.size(); i++) 228 delete lineBuffers[i]; 229} 230 231 232inline LineBuffer * 233ScanLineInputFile::Data::getLineBuffer (int lineBufferNumber) 234{ 235 return lineBuffers[lineBufferNumber % lineBuffers.size()]; 236} 237 238 239namespace { 240 241 242void 243reconstructLineOffsets (IStream &is, 244 LineOrder lineOrder, 245 vector<Int64> &lineOffsets) 246{ 247 Int64 position = is.tellg(); 248 249 try 250 { 251 for (unsigned int i = 0; i < lineOffsets.size(); i++) 252 { 253 Int64 lineOffset = is.tellg(); 254 255 int y; 256 Xdr::read <StreamIO> (is, y); 257 258 int dataSize; 259 Xdr::read <StreamIO> (is, dataSize); 260 261 Xdr::skip <StreamIO> (is, dataSize); 262 263 if (lineOrder == INCREASING_Y) 264 lineOffsets[i] = lineOffset; 265 else 266 lineOffsets[lineOffsets.size() - i - 1] = lineOffset; 267 } 268 } 269 catch (...) 270 { 271 // 272 // Suppress all exceptions. This functions is 273 // called only to reconstruct the line offset 274 // table for incomplete files, and exceptions 275 // are likely. 276 // 277 } 278 279 is.clear(); 280 is.seekg (position); 281} 282 283 284void 285readLineOffsets (IStream &is, 286 LineOrder lineOrder, 287 vector<Int64> &lineOffsets, 288 bool &complete) 289{ 290 for (unsigned int i = 0; i < lineOffsets.size(); i++) 291 { 292 Xdr::read <StreamIO> (is, lineOffsets[i]); 293 } 294 295 complete = true; 296 297 for (unsigned int i = 0; i < lineOffsets.size(); i++) 298 { 299 if (lineOffsets[i] <= 0) 300 { 301 // 302 // Invalid data in the line offset table mean that 303 // the file is probably incomplete (the table is 304 // the last thing written to the file). Either 305 // some process is still busy writing the file, 306 // or writing the file was aborted. 307 // 308 // We should still be able to read the existing 309 // parts of the file. In order to do this, we 310 // have to make a sequential scan over the scan 311 // line data to reconstruct the line offset table. 312 // 313 314 complete = false; 315 reconstructLineOffsets (is, lineOrder, lineOffsets); 316 break; 317 } 318 } 319} 320 321 322void 323readPixelData (ScanLineInputFile::Data *ifd, 324 int minY, 325 char *&buffer, 326 int &dataSize) 327{ 328 // 329 // Read a single line buffer from the input file. 330 // 331 // If the input file is not memory-mapped, we copy the pixel data into 332 // into the array pointed to by buffer. If the file is memory-mapped, 333 // then we change where buffer points to instead of writing into the 334 // array (hence buffer needs to be a reference to a char *). 335 // 336 337 Int64 lineOffset = 338 ifd->lineOffsets[(minY - ifd->minY) / ifd->linesInBuffer]; 339 340 if (lineOffset == 0) 341 THROW (Iex::InputExc, "Scan line " << minY << " is missing."); 342 343 // 344 // Seek to the start of the scan line in the file, 345 // if necessary. 346 // 347 348 if (ifd->nextLineBufferMinY != minY) 349 ifd->is->seekg (lineOffset); 350 351 // 352 // Read the data block's header. 353 // 354 355 int yInFile; 356 357 Xdr::read <StreamIO> (*ifd->is, yInFile); 358 Xdr::read <StreamIO> (*ifd->is, dataSize); 359 360 if (yInFile != minY) 361 throw Iex::InputExc ("Unexpected data block y coordinate."); 362 363 if (dataSize > (int) ifd->lineBufferSize) 364 throw Iex::InputExc ("Unexpected data block length."); 365 366 // 367 // Read the pixel data. 368 // 369 370 if (ifd->is->isMemoryMapped ()) 371 buffer = ifd->is->readMemoryMapped (dataSize); 372 else 373 ifd->is->read (buffer, dataSize); 374 375 // 376 // Keep track of which scan line is the next one in 377 // the file, so that we can avoid redundant seekg() 378 // operations (seekg() can be fairly expensive). 379 // 380 381 if (ifd->lineOrder == INCREASING_Y) 382 ifd->nextLineBufferMinY = minY + ifd->linesInBuffer; 383 else 384 ifd->nextLineBufferMinY = minY - ifd->linesInBuffer; 385} 386 387 388// 389// A LineBufferTask encapsulates the task uncompressing a set of 390// scanlines (line buffer) and copying them into the frame buffer. 391// 392 393class LineBufferTask : public Task 394{ 395 public: 396 397 LineBufferTask (TaskGroup *group, 398 ScanLineInputFile::Data *ifd, 399 LineBuffer *lineBuffer, 400 int scanLineMin, 401 int scanLineMax); 402 403 virtual ~LineBufferTask (); 404 405 virtual void execute (); 406 407 private: 408 409 ScanLineInputFile::Data * _ifd; 410 LineBuffer * _lineBuffer; 411 int _scanLineMin; 412 int _scanLineMax; 413}; 414 415 416LineBufferTask::LineBufferTask 417 (TaskGroup *group, 418 ScanLineInputFile::Data *ifd, 419 LineBuffer *lineBuffer, 420 int scanLineMin, 421 int scanLineMax) 422: 423 Task (group), 424 _ifd (ifd), 425 _lineBuffer (lineBuffer), 426 _scanLineMin (scanLineMin), 427 _scanLineMax (scanLineMax) 428{ 429 // empty 430} 431 432 433LineBufferTask::~LineBufferTask () 434{ 435 // 436 // Signal that the line buffer is now free 437 // 438 439 _lineBuffer->post (); 440} 441 442 443void 444LineBufferTask::execute () 445{ 446 try 447 { 448 // 449 // Uncompress the data, if necessary 450 // 451 452 if (_lineBuffer->uncompressedData == 0) 453 { 454 int uncompressedSize = 0; 455 int maxY = min (_lineBuffer->maxY, _ifd->maxY); 456 457 for (int i = _lineBuffer->minY - _ifd->minY; 458 i <= maxY - _ifd->minY; 459 ++i) 460 { 461 uncompressedSize += (int) _ifd->bytesPerLine[i]; 462 } 463 464 if (_lineBuffer->compressor && 465 _lineBuffer->dataSize < uncompressedSize) 466 { 467 _lineBuffer->format = _lineBuffer->compressor->format(); 468 469 _lineBuffer->dataSize = _lineBuffer->compressor->uncompress 470 (_lineBuffer->buffer, _lineBuffer->dataSize, 471 _lineBuffer->minY, _lineBuffer->uncompressedData); 472 } 473 else 474 { 475 // 476 // If the line is uncompressed, it's in XDR format, 477 // regardless of the compressor's output format. 478 // 479 480 _lineBuffer->format = Compressor::XDR; 481 _lineBuffer->uncompressedData = _lineBuffer->buffer; 482 } 483 } 484 485 int yStart, yStop, dy; 486 487 if (_ifd->lineOrder == INCREASING_Y) 488 { 489 yStart = _scanLineMin; 490 yStop = _scanLineMax + 1; 491 dy = 1; 492 } 493 else 494 { 495 yStart = _scanLineMax; 496 yStop = _scanLineMin - 1; 497 dy = -1; 498 } 499 500 for (int y = yStart; y != yStop; y += dy) 501 { 502 // 503 // Convert one scan line's worth of pixel data back 504 // from the machine-independent representation, and 505 // store the result in the frame buffer. 506 // 507 508 const char *readPtr = _lineBuffer->uncompressedData + 509 _ifd->offsetInLineBuffer[y - _ifd->minY]; 510 511 // 512 // Iterate over all image channels. 513 // 514 515 for (unsigned int i = 0; i < _ifd->slices.size(); ++i) 516 { 517 // 518 // Test if scan line y of this channel contains any data 519 // (the scan line contains data only if y % ySampling == 0). 520 // 521 522 const InSliceInfo &slice = _ifd->slices[i]; 523 524 if (modp (y, slice.ySampling) != 0) 525 continue; 526 527 // 528 // Find the x coordinates of the leftmost and rightmost 529 // sampled pixels (i.e. pixels within the data window 530 // for which x % xSampling == 0). 531 // 532 533 int dMinX = divp (_ifd->minX, slice.xSampling); 534 int dMaxX = divp (_ifd->maxX, slice.xSampling); 535 536 // 537 // Fill the frame buffer with pixel data. 538 // 539 540 if (slice.skip) 541 { 542 // 543 // The file contains data for this channel, but 544 // the frame buffer contains no slice for this channel. 545 // 546 547 skipChannel (readPtr, slice.typeInFile, dMaxX - dMinX + 1); 548 } 549 else 550 { 551 // 552 // The frame buffer contains a slice for this channel. 553 // 554 555 char *linePtr = slice.base + 556 divp (y, slice.ySampling) * 557 slice.yStride; 558 559 char *writePtr = linePtr + dMinX * slice.xStride; 560 char *endPtr = linePtr + dMaxX * slice.xStride; 561 562 copyIntoFrameBuffer (readPtr, writePtr, endPtr, 563 slice.xStride, slice.fill, 564 slice.fillValue, _lineBuffer->format, 565 slice.typeInFrameBuffer, 566 slice.typeInFile); 567 } 568 } 569 } 570 } 571 catch (std::exception &e) 572 { 573 if (!_lineBuffer->hasException) 574 { 575 _lineBuffer->exception = e.what(); 576 _lineBuffer->hasException = true; 577 } 578 } 579 catch (...) 580 { 581 if (!_lineBuffer->hasException) 582 { 583 _lineBuffer->exception = "unrecognized exception"; 584 _lineBuffer->hasException = true; 585 } 586 } 587} 588 589 590LineBufferTask * 591newLineBufferTask 592 (TaskGroup *group, 593 ScanLineInputFile::Data *ifd, 594 int number, 595 int scanLineMin, 596 int scanLineMax) 597{ 598 // 599 // Wait for a line buffer to become available, fill the line 600 // buffer with raw data from the file if necessary, and create 601 // a new LineBufferTask whose execute() method will uncompress 602 // the contents of the buffer and copy the pixels into the 603 // frame buffer. 604 // 605 606 LineBuffer *lineBuffer = ifd->getLineBuffer (number); 607 608 try 609 { 610 lineBuffer->wait (); 611 612 if (lineBuffer->number != number) 613 { 614 lineBuffer->minY = ifd->minY + number * ifd->linesInBuffer; 615 lineBuffer->maxY = lineBuffer->minY + ifd->linesInBuffer - 1; 616 617 lineBuffer->number = number; 618 lineBuffer->uncompressedData = 0; 619 620 readPixelData (ifd, lineBuffer->minY, 621 lineBuffer->buffer, 622 lineBuffer->dataSize); 623 } 624 } 625 catch (...) 626 { 627 // 628 // Reading from the file caused an exception. 629 // Signal that the line buffer is free, and 630 // re-throw the exception. 631 // 632 633 lineBuffer->number = -1; 634 lineBuffer->post(); 635 throw; 636 } 637 638 scanLineMin = max (lineBuffer->minY, scanLineMin); 639 scanLineMax = min (lineBuffer->maxY, scanLineMax); 640 641 return new LineBufferTask (group, ifd, lineBuffer, 642 scanLineMin, scanLineMax); 643} 644 645} // namespace 646 647 648ScanLineInputFile::ScanLineInputFile 649 (const Header &header, 650 IStream *is, 651 int numThreads) 652: 653 _data (new Data (is, numThreads)) 654{ 655 try 656 { 657 _data->header = header; 658 659 _data->lineOrder = _data->header.lineOrder(); 660 661 const Box2i &dataWindow = _data->header.dataWindow(); 662 663 _data->minX = dataWindow.min.x; 664 _data->maxX = dataWindow.max.x; 665 _data->minY = dataWindow.min.y; 666 _data->maxY = dataWindow.max.y; 667 668 size_t maxBytesPerLine = bytesPerLineTable (_data->header, 669 _data->bytesPerLine); 670 671 for (size_t i = 0; i < _data->lineBuffers.size(); i++) 672 { 673 _data->lineBuffers[i] = new LineBuffer (newCompressor 674 (_data->header.compression(), 675 maxBytesPerLine, 676 _data->header)); 677 } 678 679 _data->linesInBuffer = 680 numLinesInBuffer (_data->lineBuffers[0]->compressor); 681 682 _data->lineBufferSize = maxBytesPerLine * _data->linesInBuffer; 683 684 if (!_data->is->isMemoryMapped()) 685 for (size_t i = 0; i < _data->lineBuffers.size(); i++) 686 _data->lineBuffers[i]->buffer = new char[_data->lineBufferSize]; 687 688 _data->nextLineBufferMinY = _data->minY - 1; 689 690 offsetInLineBufferTable (_data->bytesPerLine, 691 _data->linesInBuffer, 692 _data->offsetInLineBuffer); 693 694 int lineOffsetSize = (dataWindow.max.y - dataWindow.min.y + 695 _data->linesInBuffer) / _data->linesInBuffer; 696 697 _data->lineOffsets.resize (lineOffsetSize); 698 699 readLineOffsets (*_data->is, 700 _data->lineOrder, 701 _data->lineOffsets, 702 _data->fileIsComplete); 703 } 704 catch (...) 705 { 706 delete _data; 707 throw; 708 } 709} 710 711 712ScanLineInputFile::~ScanLineInputFile () 713{ 714 if (!_data->is->isMemoryMapped()) 715 for (size_t i = 0; i < _data->lineBuffers.size(); i++) 716 delete [] _data->lineBuffers[i]->buffer; 717 718 delete _data; 719} 720 721 722const char * 723ScanLineInputFile::fileName () const 724{ 725 return _data->is->fileName(); 726} 727 728 729const Header & 730ScanLineInputFile::header () const 731{ 732 return _data->header; 733} 734 735 736int 737ScanLineInputFile::version () const 738{ 739 return _data->version; 740} 741 742 743void 744ScanLineInputFile::setFrameBuffer (const FrameBuffer &frameBuffer) 745{ 746 Lock lock (*_data); 747 748 // 749 // Check if the new frame buffer descriptor is 750 // compatible with the image file header. 751 // 752 753 const ChannelList &channels = _data->header.channels(); 754 755 for (FrameBuffer::ConstIterator j = frameBuffer.begin(); 756 j != frameBuffer.end(); 757 ++j) 758 { 759 ChannelList::ConstIterator i = channels.find (j.name()); 760 761 if (i == channels.end()) 762 continue; 763 764 if (i.channel().xSampling != j.slice().xSampling || 765 i.channel().ySampling != j.slice().ySampling) 766 THROW (Iex::ArgExc, "X and/or y subsampling factors " 767 "of \"" << i.name() << "\" channel " 768 "of input file \"" << fileName() << "\" are " 769 "not compatible with the frame buffer's " 770 "subsampling factors."); 771 } 772 773 // 774 // Initialize the slice table for readPixels(). 775 // 776 777 vector<InSliceInfo> slices; 778 ChannelList::ConstIterator i = channels.begin(); 779 780 for (FrameBuffer::ConstIterator j = frameBuffer.begin(); 781 j != frameBuffer.end(); 782 ++j) 783 { 784 while (i != channels.end() && strcmp (i.name(), j.name()) < 0) 785 { 786 // 787 // Channel i is present in the file but not 788 // in the frame buffer; data for channel i 789 // will be skipped during readPixels(). 790 // 791 792 slices.push_back (InSliceInfo (i.channel().type, 793 i.channel().type, 794 0, // base 795 0, // xStride 796 0, // yStride 797 i.channel().xSampling, 798 i.channel().ySampling, 799 false, // fill 800 true, // skip 801 0.0)); // fillValue 802 ++i; 803 } 804 805 bool fill = false; 806 807 if (i == channels.end() || strcmp (i.name(), j.name()) > 0) 808 { 809 // 810 // Channel i is present in the frame buffer, but not in the file. 811 // In the frame buffer, slice j will be filled with a default value. 812 // 813 814 fill = true; 815 } 816 817 slices.push_back (InSliceInfo (j.slice().type, 818 fill? j.slice().type: 819 i.channel().type, 820 j.slice().base, 821 j.slice().xStride, 822 j.slice().yStride, 823 j.slice().xSampling, 824 j.slice().ySampling, 825 fill, 826 false, // skip 827 j.slice().fillValue)); 828 829 if (i != channels.end() && !fill) 830 ++i; 831 } 832 833 // 834 // Store the new frame buffer. 835 // 836 837 _data->frameBuffer = frameBuffer; 838 _data->slices = slices; 839} 840 841 842const FrameBuffer & 843ScanLineInputFile::frameBuffer () const 844{ 845 Lock lock (*_data); 846 return _data->frameBuffer; 847} 848 849 850bool 851ScanLineInputFile::isComplete () const 852{ 853 return _data->fileIsComplete; 854} 855 856 857void 858ScanLineInputFile::readPixels (int scanLine1, int scanLine2) 859{ 860 try 861 { 862 Lock lock (*_data); 863 864 if (_data->slices.size() == 0) 865 throw Iex::ArgExc ("No frame buffer specified " 866 "as pixel data destination."); 867 868 int scanLineMin = min (scanLine1, scanLine2); 869 int scanLineMax = max (scanLine1, scanLine2); 870 871 if (scanLineMin < _data->minY || scanLineMax > _data->maxY) 872 throw Iex::ArgExc ("Tried to read scan line outside " 873 "the image file's data window."); 874 875 // 876 // We impose a numbering scheme on the lineBuffers where the first 877 // scanline is contained in lineBuffer 1. 878 // 879 // Determine the first and last lineBuffer numbers in this scanline 880 // range. We always attempt to read the scanlines in the order that 881 // they are stored in the file. 882 // 883 884 int start, stop, dl; 885 886 if (_data->lineOrder == INCREASING_Y) 887 { 888 start = (scanLineMin - _data->minY) / _data->linesInBuffer; 889 stop = (scanLineMax - _data->minY) / _data->linesInBuffer + 1; 890 dl = 1; 891 } 892 else 893 { 894 start = (scanLineMax - _data->minY) / _data->linesInBuffer; 895 stop = (scanLineMin - _data->minY) / _data->linesInBuffer - 1; 896 dl = -1; 897 } 898 899 // 900 // Create a task group for all line buffer tasks. When the 901 // task group goes out of scope, the destructor waits until 902 // all tasks are complete. 903 // 904 905 { 906 TaskGroup taskGroup; 907 908 // 909 // Add the line buffer tasks. 910 // 911 // The tasks will execute in the order that they are created 912 // because we lock the line buffers during construction and the 913 // constructors are called by the main thread. Hence, in order 914 // for a successive task to execute the previous task which 915 // used that line buffer must have completed already. 916 // 917 918 for (int l = start; l != stop; l += dl) 919 { 920 ThreadPool::addGlobalTask (newLineBufferTask (&taskGroup, 921 _data, l, 922 scanLineMin, 923 scanLineMax)); 924 } 925 926 // 927 // finish all tasks 928 // 929 } 930 931 // 932 // Exeption handling: 933 // 934 // LineBufferTask::execute() may have encountered exceptions, but 935 // those exceptions occurred in another thread, not in the thread 936 // that is executing this call to ScanLineInputFile::readPixels(). 937 // LineBufferTask::execute() has caught all exceptions and stored 938 // the exceptions' what() strings in the line buffers. 939 // Now we check if any line buffer contains a stored exception; if 940 // this is the case then we re-throw the exception in this thread. 941 // (It is possible that multiple line buffers contain stored 942 // exceptions. We re-throw the first exception we find and 943 // ignore all others.) 944 // 945 946 const string *exception = 0; 947 948 for (int i = 0; i < _data->lineBuffers.size(); ++i) 949 { 950 LineBuffer *lineBuffer = _data->lineBuffers[i]; 951 952 if (lineBuffer->hasException && !exception) 953 exception = &lineBuffer->exception; 954 955 lineBuffer->hasException = false; 956 } 957 958 if (exception) 959 throw Iex::IoExc (*exception); 960 } 961 catch (Iex::BaseExc &e) 962 { 963 REPLACE_EXC (e, "Error reading pixel data from image " 964 "file \"" << fileName() << "\". " << e); 965 throw; 966 } 967} 968 969 970void 971ScanLineInputFile::readPixels (int scanLine) 972{ 973 readPixels (scanLine, scanLine); 974} 975 976 977void 978ScanLineInputFile::rawPixelData (int firstScanLine, 979 const char *&pixelData, 980 int &pixelDataSize) 981{ 982 try 983 { 984 Lock lock (*_data); 985 986 if (firstScanLine < _data->minY || firstScanLine > _data->maxY) 987 { 988 throw Iex::ArgExc ("Tried to read scan line outside " 989 "the image file's data window."); 990 } 991 992 int minY = lineBufferMinY 993 (firstScanLine, _data->minY, _data->linesInBuffer); 994 995 readPixelData 996 (_data, minY, _data->lineBuffers[0]->buffer, pixelDataSize); 997 998 pixelData = _data->lineBuffers[0]->buffer; 999 } 1000 catch (Iex::BaseExc &e) 1001 { 1002 REPLACE_EXC (e, "Error reading pixel data from image " 1003 "file \"" << fileName() << "\". " << e); 1004 throw; 1005 } 1006} 1007 1008} // namespace Imf 1009