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 OutputFile 39 // 40 //----------------------------------------------------------------------------- 41 42 #include <ImfOutputFile.h> 43 #include <ImfInputFile.h> 44 #include <ImfChannelList.h> 45 #include <ImfMisc.h> 46 #include <ImfStdIO.h> 47 #include <ImfCompressor.h> 48 #include "ImathBox.h" 49 #include "ImathFun.h" 50 #include <ImfArray.h> 51 #include <ImfXdr.h> 52 #include <ImfPreviewImageAttribute.h> 53 #include "IlmThreadPool.h" 54 #include "IlmThreadSemaphore.h" 55 #include "IlmThreadMutex.h" 56 #include "Iex.h" 57 #include <string> 58 #include <vector> 59 #include <fstream> 60 #include <assert.h> 61 #include <algorithm> // for std::max() 62 63 64 namespace Imf { 65 66 using Imath::Box2i; 67 using Imath::divp; 68 using Imath::modp; 69 using std::string; 70 using std::vector; 71 using std::ofstream; 72 using std::min; 73 using std::max; 74 using IlmThread::Mutex; 75 using IlmThread::Lock; 76 using IlmThread::Semaphore; 77 using IlmThread::Task; 78 using IlmThread::TaskGroup; 79 using IlmThread::ThreadPool; 80 81 namespace { 82 83 84 struct OutSliceInfo 85 { 86 PixelType type; 87 const char * base; 88 size_t xStride; 89 size_t yStride; 90 int xSampling; 91 int ySampling; 92 bool zero; 93 94 OutSliceInfo (PixelType type = HALF, 95 const char *base = 0, 96 size_t xStride = 0, 97 size_t yStride = 0, 98 int xSampling = 1, 99 int ySampling = 1, 100 bool zero = false); 101 }; 102 103 104 OutSliceInfo::OutSliceInfo (PixelType t, 105 const char *b, 106 size_t xs, size_t ys, 107 int xsm, int ysm, 108 bool z) 109 : 110 type (t), 111 base (b), 112 xStride (xs), 113 yStride (ys), 114 xSampling (xsm), 115 ySampling (ysm), 116 zero (z) 117 { 118 // empty 119 } 120 121 122 struct LineBuffer 123 { 124 Array<char> buffer; 125 const char * dataPtr; 126 int dataSize; 127 char * endOfLineBufferData; 128 int minY; 129 int maxY; 130 int scanLineMin; 131 int scanLineMax; 132 Compressor * compressor; 133 bool partiallyFull; // has incomplete data 134 bool hasException; 135 string exception; 136 137 LineBuffer (Compressor *comp); 138 ~LineBuffer (); 139 140 void wait () {_sem.wait();} 141 void post () {_sem.post();} 142 143 private: 144 145 Semaphore _sem; 146 }; 147 148 149 LineBuffer::LineBuffer (Compressor *comp) : 150 dataPtr (0), 151 dataSize (0), 152 compressor (comp), 153 partiallyFull (false), 154 hasException (false), 155 exception (), 156 _sem (1) 157 { 158 // empty 159 } 160 161 162 LineBuffer::~LineBuffer () 163 { 164 delete compressor; 165 } 166 167 } // namespace 168 169 170 struct OutputFile::Data: public Mutex 171 { 172 Header header; // the image header 173 int version; // file format version 174 Int64 previewPosition; // file position for preview 175 FrameBuffer frameBuffer; // framebuffer to write into 176 int currentScanLine; // next scanline to be written 177 int missingScanLines; // number of lines to write 178 LineOrder lineOrder; // the file's lineorder 179 int minX; // data window's min x coord 180 int maxX; // data window's max x coord 181 int minY; // data window's min y coord 182 int maxY; // data window's max x coord 183 vector<Int64> lineOffsets; // stores offsets in file for 184 // each scanline 185 vector<size_t> bytesPerLine; // combined size of a line over 186 // all channels 187 vector<size_t> offsetInLineBuffer; // offset for each scanline in 188 // its linebuffer 189 Compressor::Format format; // compressor's data format 190 vector<OutSliceInfo> slices; // info about channels in file 191 OStream * os; // file stream to write to 192 bool deleteStream; 193 Int64 lineOffsetsPosition; // file position for line 194 // offset table 195 Int64 currentPosition; // current file position 196 197 vector<LineBuffer*> lineBuffers; // each holds one line buffer 198 int linesInBuffer; // number of scanlines each 199 // buffer holds 200 size_t lineBufferSize; // size of the line buffer 201 202 Data (bool deleteStream, int numThreads); 203 ~Data (); 204 205 206 inline LineBuffer * getLineBuffer (int number); // hash function from line 207 // buffer indices into our 208 // vector of line buffers 209 }; 210 211 212 OutputFile::Data::Data (bool deleteStream, int numThreads): 213 os (0), 214 deleteStream (deleteStream), 215 lineOffsetsPosition (0) 216 { 217 // 218 // We need at least one lineBuffer, but if threading is used, 219 // to keep n threads busy we need 2*n lineBuffers. 220 // 221 222 lineBuffers.resize (max (1, 2 * numThreads)); 223 } 224 225 226 OutputFile::Data::~Data () 227 { 228 if (deleteStream) 229 delete os; 230 231 for (size_t i = 0; i < lineBuffers.size(); i++) 232 delete lineBuffers[i]; 233 } 234 235 236 LineBuffer* 237 OutputFile::Data::getLineBuffer (int number) 238 { 239 return lineBuffers[number % lineBuffers.size()]; 240 } 241 242 243 namespace { 244 245 246 Int64 247 writeLineOffsets (OStream &os, const vector<Int64> &lineOffsets) 248 { 249 Int64 pos = os.tellp(); 250 251 if (pos == -1) 252 Iex::throwErrnoExc ("Cannot determine current file position (%T)."); 253 254 for (unsigned int i = 0; i < lineOffsets.size(); i++) 255 Xdr::write <StreamIO> (os, lineOffsets[i]); 256 257 return pos; 258 } 259 260 261 void 262 writePixelData (OutputFile::Data *ofd, 263 int lineBufferMinY, 264 const char pixelData[], 265 int pixelDataSize) 266 { 267 // 268 // Store a block of pixel data in the output file, and try 269 // to keep track of the current writing position the file 270 // without calling tellp() (tellp() can be fairly expensive). 271 // 272 273 Int64 currentPosition = ofd->currentPosition; 274 ofd->currentPosition = 0; 275 276 if (currentPosition == 0) 277 currentPosition = ofd->os->tellp(); 278 279 ofd->lineOffsets[(ofd->currentScanLine - ofd->minY) / ofd->linesInBuffer] = 280 currentPosition; 281 282 #ifdef DEBUG 283 284 assert (ofd->os->tellp() == currentPosition); 285 286 #endif 287 288 Xdr::write <StreamIO> (*ofd->os, lineBufferMinY); 289 Xdr::write <StreamIO> (*ofd->os, pixelDataSize); 290 ofd->os->write (pixelData, pixelDataSize); 291 292 ofd->currentPosition = currentPosition + 293 Xdr::size<int>() + 294 Xdr::size<int>() + 295 pixelDataSize; 296 } 297 298 299 inline void 300 writePixelData (OutputFile::Data *ofd, const LineBuffer *lineBuffer) 301 { 302 writePixelData (ofd, 303 lineBuffer->minY, 304 lineBuffer->dataPtr, 305 lineBuffer->dataSize); 306 } 307 308 309 void 310 convertToXdr (OutputFile::Data *ofd, 311 Array<char> &lineBuffer, 312 int lineBufferMinY, 313 int lineBufferMaxY, 314 int /*inSize*/) 315 { 316 // 317 // Convert the contents of a lineBuffer from the machine's native 318 // representation to Xdr format. This function is called by 319 // CompressLineBuffer::execute(), below, if the compressor wanted 320 // its input pixel data in the machine's native format, but then 321 // failed to compress the data (most compressors will expand rather 322 // than compress random input data). 323 // 324 // Note that this routine assumes that the machine's native 325 // representation of the pixel data has the same size as the 326 // Xdr representation. This makes it possible to convert the 327 // pixel data in place, without an intermediate temporary buffer. 328 // 329 330 int startY, endY; // The first and last scanlines in 331 // the file that are in the lineBuffer. 332 int step; 333 334 if (ofd->lineOrder == INCREASING_Y) 335 { 336 startY = max (lineBufferMinY, ofd->minY); 337 endY = min (lineBufferMaxY, ofd->maxY) + 1; 338 step = 1; 339 } 340 else 341 { 342 startY = min (lineBufferMaxY, ofd->maxY); 343 endY = max (lineBufferMinY, ofd->minY) - 1; 344 step = -1; 345 } 346 347 // 348 // Iterate over all scanlines in the lineBuffer to convert. 349 // 350 351 for (int y = startY; y != endY; y += step) 352 { 353 // 354 // Set these to point to the start of line y. 355 // We will write to writePtr from readPtr. 356 // 357 358 char *writePtr = lineBuffer + ofd->offsetInLineBuffer[y - ofd->minY]; 359 const char *readPtr = writePtr; 360 361 // 362 // Iterate over all slices in the file. 363 // 364 365 for (unsigned int i = 0; i < ofd->slices.size(); ++i) 366 { 367 // 368 // Test if scan line y of this channel is 369 // contains any data (the scan line contains 370 // data only if y % ySampling == 0). 371 // 372 373 const OutSliceInfo &slice = ofd->slices[i]; 374 375 if (modp (y, slice.ySampling) != 0) 376 continue; 377 378 // 379 // Find the number of sampled pixels, dMaxX-dMinX+1, for 380 // slice i in scan line y (i.e. pixels within the data window 381 // for which x % xSampling == 0). 382 // 383 384 int dMinX = divp (ofd->minX, slice.xSampling); 385 int dMaxX = divp (ofd->maxX, slice.xSampling); 386 387 // 388 // Convert the samples in place. 389 // 390 391 convertInPlace (writePtr, readPtr, slice.type, dMaxX - dMinX + 1); 392 } 393 } 394 } 395 396 397 // 398 // A LineBufferTask encapsulates the task of copying a set of scanlines 399 // from the user's frame buffer into a LineBuffer object, compressing 400 // the data if necessary. 401 // 402 403 class LineBufferTask: public Task 404 { 405 public: 406 407 LineBufferTask (TaskGroup *group, 408 OutputFile::Data *ofd, 409 int number, 410 int scanLineMin, 411 int scanLineMax); 412 413 virtual ~LineBufferTask (); 414 415 virtual void execute (); 416 417 private: 418 419 OutputFile::Data * _ofd; 420 LineBuffer * _lineBuffer; 421 }; 422 423 424 LineBufferTask::LineBufferTask 425 (TaskGroup *group, 426 OutputFile::Data *ofd, 427 int number, 428 int scanLineMin, 429 int scanLineMax) 430 : 431 Task (group), 432 _ofd (ofd), 433 _lineBuffer (_ofd->getLineBuffer(number)) 434 { 435 // 436 // Wait for the lineBuffer to become available 437 // 438 439 _lineBuffer->wait (); 440 441 // 442 // Initialize the lineBuffer data if necessary 443 // 444 445 if (!_lineBuffer->partiallyFull) 446 { 447 _lineBuffer->endOfLineBufferData = _lineBuffer->buffer; 448 449 _lineBuffer->minY = _ofd->minY + number * _ofd->linesInBuffer; 450 451 _lineBuffer->maxY = min (_lineBuffer->minY + _ofd->linesInBuffer - 1, 452 _ofd->maxY); 453 454 _lineBuffer->partiallyFull = true; 455 } 456 457 _lineBuffer->scanLineMin = max (_lineBuffer->minY, scanLineMin); 458 _lineBuffer->scanLineMax = min (_lineBuffer->maxY, scanLineMax); 459 } 460 461 462 LineBufferTask::~LineBufferTask () 463 { 464 // 465 // Signal that the line buffer is now free 466 // 467 468 _lineBuffer->post (); 469 } 470 471 472 void 473 LineBufferTask::execute () 474 { 475 try 476 { 477 // 478 // First copy the pixel data from the 479 // frame buffer into the line buffer 480 // 481 482 int yStart, yStop, dy; 483 484 if (_ofd->lineOrder == INCREASING_Y) 485 { 486 yStart = _lineBuffer->scanLineMin; 487 yStop = _lineBuffer->scanLineMax + 1; 488 dy = 1; 489 } 490 else 491 { 492 yStart = _lineBuffer->scanLineMax; 493 yStop = _lineBuffer->scanLineMin - 1; 494 dy = -1; 495 } 496 497 int y; 498 499 for (y = yStart; y != yStop; y += dy) 500 { 501 // 502 // Gather one scan line's worth of pixel data and store 503 // them in _ofd->lineBuffer. 504 // 505 506 char *writePtr = _lineBuffer->buffer + 507 _ofd->offsetInLineBuffer[y - _ofd->minY]; 508 // 509 // Iterate over all image channels. 510 // 511 512 for (unsigned int i = 0; i < _ofd->slices.size(); ++i) 513 { 514 // 515 // Test if scan line y of this channel contains any data 516 // (the scan line contains data only if y % ySampling == 0). 517 // 518 519 const OutSliceInfo &slice = _ofd->slices[i]; 520 521 if (modp (y, slice.ySampling) != 0) 522 continue; 523 524 // 525 // Find the x coordinates of the leftmost and rightmost 526 // sampled pixels (i.e. pixels within the data window 527 // for which x % xSampling == 0). 528 // 529 530 int dMinX = divp (_ofd->minX, slice.xSampling); 531 int dMaxX = divp (_ofd->maxX, slice.xSampling); 532 533 // 534 // Fill the line buffer with with pixel data. 535 // 536 537 if (slice.zero) 538 { 539 // 540 // The frame buffer contains no data for this channel. 541 // Store zeroes in _lineBuffer->buffer. 542 // 543 544 fillChannelWithZeroes (writePtr, _ofd->format, slice.type, 545 dMaxX - dMinX + 1); 546 } 547 else 548 { 549 // 550 // If necessary, convert the pixel data to Xdr format. 551 // Then store the pixel data in _ofd->lineBuffer. 552 // 553 554 const char *linePtr = slice.base + 555 divp (y, slice.ySampling) * 556 slice.yStride; 557 558 const char *readPtr = linePtr + dMinX * slice.xStride; 559 const char *endPtr = linePtr + dMaxX * slice.xStride; 560 561 copyFromFrameBuffer (writePtr, readPtr, endPtr, 562 slice.xStride, _ofd->format, 563 slice.type); 564 } 565 } 566 567 if (_lineBuffer->endOfLineBufferData < writePtr) 568 _lineBuffer->endOfLineBufferData = writePtr; 569 570 #ifdef DEBUG 571 572 assert (writePtr - (_lineBuffer->buffer + 573 _ofd->offsetInLineBuffer[y - _ofd->minY]) == 574 (int) _ofd->bytesPerLine[y - _ofd->minY]); 575 576 #endif 577 578 } 579 580 // 581 // If the next scanline isn't past the bounds of the lineBuffer 582 // then we are done, otherwise compress the linebuffer 583 // 584 585 if (y >= _lineBuffer->minY && y <= _lineBuffer->maxY) 586 return; 587 588 _lineBuffer->dataPtr = _lineBuffer->buffer; 589 590 _lineBuffer->dataSize = _lineBuffer->endOfLineBufferData - 591 _lineBuffer->buffer; 592 593 // 594 // Compress the data 595 // 596 597 Compressor *compressor = _lineBuffer->compressor; 598 599 if (compressor) 600 { 601 const char *compPtr; 602 603 int compSize = compressor->compress (_lineBuffer->dataPtr, 604 _lineBuffer->dataSize, 605 _lineBuffer->minY, compPtr); 606 607 if (compSize < _lineBuffer->dataSize) 608 { 609 _lineBuffer->dataSize = compSize; 610 _lineBuffer->dataPtr = compPtr; 611 } 612 else if (_ofd->format == Compressor::NATIVE) 613 { 614 // 615 // The data did not shrink during compression, but 616 // we cannot write to the file using the machine's 617 // native format, so we need to convert the lineBuffer 618 // to Xdr. 619 // 620 621 convertToXdr (_ofd, _lineBuffer->buffer, _lineBuffer->minY, 622 _lineBuffer->maxY, _lineBuffer->dataSize); 623 } 624 } 625 626 _lineBuffer->partiallyFull = false; 627 } 628 catch (std::exception &e) 629 { 630 if (!_lineBuffer->hasException) 631 { 632 _lineBuffer->exception = e.what (); 633 _lineBuffer->hasException = true; 634 } 635 } 636 catch (...) 637 { 638 if (!_lineBuffer->hasException) 639 { 640 _lineBuffer->exception = "unrecognized exception"; 641 _lineBuffer->hasException = true; 642 } 643 } 644 } 645 646 } // namespace 647 648 649 OutputFile::OutputFile 650 (const char fileName[], 651 const Header &header, 652 int numThreads) 653 : 654 _data (new Data (true, numThreads)) 655 { 656 try 657 { 658 header.sanityCheck(); 659 _data->os = new StdOFStream (fileName); 660 initialize (header); 661 } 662 catch (Iex::BaseExc &e) 663 { 664 delete _data; 665 666 REPLACE_EXC (e, "Cannot open image file " 667 "\"" << fileName << "\". " << e); 668 throw; 669 } 670 catch (...) 671 { 672 delete _data; 673 throw; 674 } 675 } 676 677 678 OutputFile::OutputFile 679 (OStream &os, 680 const Header &header, 681 int numThreads) 682 : 683 _data (new Data (false, numThreads)) 684 { 685 try 686 { 687 header.sanityCheck(); 688 _data->os = &os; 689 initialize (header); 690 } 691 catch (Iex::BaseExc &e) 692 { 693 delete _data; 694 695 REPLACE_EXC (e, "Cannot open image file " 696 "\"" << os.fileName() << "\". " << e); 697 throw; 698 } 699 catch (...) 700 { 701 delete _data; 702 throw; 703 } 704 } 705 706 707 void 708 OutputFile::initialize (const Header &header) 709 { 710 _data->header = header; 711 712 const Box2i &dataWindow = header.dataWindow(); 713 714 _data->currentScanLine = (header.lineOrder() == INCREASING_Y)? 715 dataWindow.min.y: dataWindow.max.y; 716 717 _data->missingScanLines = dataWindow.max.y - dataWindow.min.y + 1; 718 _data->lineOrder = header.lineOrder(); 719 _data->minX = dataWindow.min.x; 720 _data->maxX = dataWindow.max.x; 721 _data->minY = dataWindow.min.y; 722 _data->maxY = dataWindow.max.y; 723 724 size_t maxBytesPerLine = bytesPerLineTable (_data->header, 725 _data->bytesPerLine); 726 727 for (size_t i = 0; i < _data->lineBuffers.size(); ++i) 728 { 729 _data->lineBuffers[i] = 730 new LineBuffer (newCompressor (_data->header.compression(), 731 maxBytesPerLine, 732 _data->header)); 733 } 734 735 LineBuffer *lineBuffer = _data->lineBuffers[0]; 736 _data->format = defaultFormat (lineBuffer->compressor); 737 _data->linesInBuffer = numLinesInBuffer (lineBuffer->compressor); 738 _data->lineBufferSize = maxBytesPerLine * _data->linesInBuffer; 739 740 for (size_t i = 0; i < _data->lineBuffers.size(); i++) 741 _data->lineBuffers[i]->buffer.resizeErase(_data->lineBufferSize); 742 743 int lineOffsetSize = (dataWindow.max.y - dataWindow.min.y + 744 _data->linesInBuffer) / _data->linesInBuffer; 745 746 _data->lineOffsets.resize (lineOffsetSize); 747 748 offsetInLineBufferTable (_data->bytesPerLine, 749 _data->linesInBuffer, 750 _data->offsetInLineBuffer); 751 752 _data->previewPosition = 753 _data->header.writeTo (*_data->os); 754 755 _data->lineOffsetsPosition = 756 writeLineOffsets (*_data->os, _data->lineOffsets); 757 758 _data->currentPosition = _data->os->tellp(); 759 } 760 761 762 OutputFile::~OutputFile () 763 { 764 if (_data) 765 { 766 { 767 if (_data->lineOffsetsPosition > 0) 768 { 769 try 770 { 771 _data->os->seekp (_data->lineOffsetsPosition); 772 writeLineOffsets (*_data->os, _data->lineOffsets); 773 } 774 catch (...) 775 { 776 // 777 // We cannot safely throw any exceptions from here. 778 // This destructor may have been called because the 779 // stack is currently being unwound for another 780 // exception. 781 // 782 } 783 } 784 } 785 786 delete _data; 787 } 788 } 789 790 791 const char * 792 OutputFile::fileName () const 793 { 794 return _data->os->fileName(); 795 } 796 797 798 const Header & 799 OutputFile::header () const 800 { 801 return _data->header; 802 } 803 804 805 void 806 OutputFile::setFrameBuffer (const FrameBuffer &frameBuffer) 807 { 808 Lock lock (*_data); 809 810 // 811 // Check if the new frame buffer descriptor 812 // is compatible with the image file header. 813 // 814 815 const ChannelList &channels = _data->header.channels(); 816 817 for (ChannelList::ConstIterator i = channels.begin(); 818 i != channels.end(); 819 ++i) 820 { 821 FrameBuffer::ConstIterator j = frameBuffer.find (i.name()); 822 823 if (j == frameBuffer.end()) 824 continue; 825 826 if (i.channel().type != j.slice().type) 827 { 828 THROW (Iex::ArgExc, "Pixel type of \"" << i.name() << "\" channel " 829 "of output file \"" << fileName() << "\" is " 830 "not compatible with the frame buffer's " 831 "pixel type."); 832 } 833 834 if (i.channel().xSampling != j.slice().xSampling || 835 i.channel().ySampling != j.slice().ySampling) 836 { 837 THROW (Iex::ArgExc, "X and/or y subsampling factors " 838 "of \"" << i.name() << "\" channel " 839 "of output file \"" << fileName() << "\" are " 840 "not compatible with the frame buffer's " 841 "subsampling factors."); 842 } 843 } 844 845 // 846 // Initialize slice table for writePixels(). 847 // 848 849 vector<OutSliceInfo> slices; 850 851 for (ChannelList::ConstIterator i = channels.begin(); 852 i != channels.end(); 853 ++i) 854 { 855 FrameBuffer::ConstIterator j = frameBuffer.find (i.name()); 856 857 if (j == frameBuffer.end()) 858 { 859 // 860 // Channel i is not present in the frame buffer. 861 // In the file, channel i will contain only zeroes. 862 // 863 864 slices.push_back (OutSliceInfo (i.channel().type, 865 0, // base 866 0, // xStride, 867 0, // yStride, 868 i.channel().xSampling, 869 i.channel().ySampling, 870 true)); // zero 871 } 872 else 873 { 874 // 875 // Channel i is present in the frame buffer. 876 // 877 878 slices.push_back (OutSliceInfo (j.slice().type, 879 j.slice().base, 880 j.slice().xStride, 881 j.slice().yStride, 882 j.slice().xSampling, 883 j.slice().ySampling, 884 false)); // zero 885 } 886 } 887 888 // 889 // Store the new frame buffer. 890 // 891 892 _data->frameBuffer = frameBuffer; 893 _data->slices = slices; 894 } 895 896 897 const FrameBuffer & 898 OutputFile::frameBuffer () const 899 { 900 Lock lock (*_data); 901 return _data->frameBuffer; 902 } 903 904 905 void 906 OutputFile::writePixels (int numScanLines) 907 { 908 try 909 { 910 Lock lock (*_data); 911 912 if (_data->slices.size() == 0) 913 throw Iex::ArgExc ("No frame buffer specified " 914 "as pixel data source."); 915 916 // 917 // Maintain two iterators: 918 // nextWriteBuffer: next linebuffer to be written to the file 919 // nextCompressBuffer: next linebuffer to compress 920 // 921 922 int first = (_data->currentScanLine - _data->minY) / 923 _data->linesInBuffer; 924 925 int nextWriteBuffer = first; 926 int nextCompressBuffer; 927 int stop; 928 int step; 929 int scanLineMin; 930 int scanLineMax; 931 932 { 933 // 934 // Create a task group for all line buffer tasks. When the 935 // taskgroup goes out of scope, the destructor waits until 936 // all tasks are complete. 937 // 938 939 TaskGroup taskGroup; 940 941 // 942 // Determine the range of lineBuffers that intersect the scan 943 // line range. Then add the initial compression tasks to the 944 // thread pool. We always add in at least one task but the 945 // individual task might not do anything if numScanLines == 0. 946 // 947 948 if (_data->lineOrder == INCREASING_Y) 949 { 950 int last = (_data->currentScanLine + (numScanLines - 1) - 951 _data->minY) / _data->linesInBuffer; 952 953 scanLineMin = _data->currentScanLine; 954 scanLineMax = _data->currentScanLine + numScanLines - 1; 955 956 int numTasks = max (min ((int)_data->lineBuffers.size(), 957 last - first + 1), 958 1); 959 960 for (int i = 0; i < numTasks; i++) 961 { 962 ThreadPool::addGlobalTask 963 (new LineBufferTask (&taskGroup, _data, first + i, 964 scanLineMin, scanLineMax)); 965 } 966 967 nextCompressBuffer = first + numTasks; 968 stop = last + 1; 969 step = 1; 970 } 971 else 972 { 973 int last = (_data->currentScanLine - (numScanLines - 1) - 974 _data->minY) / _data->linesInBuffer; 975 976 scanLineMax = _data->currentScanLine; 977 scanLineMin = _data->currentScanLine - numScanLines + 1; 978 979 int numTasks = max (min ((int)_data->lineBuffers.size(), 980 first - last + 1), 981 1); 982 983 for (int i = 0; i < numTasks; i++) 984 { 985 ThreadPool::addGlobalTask 986 (new LineBufferTask (&taskGroup, _data, first - i, 987 scanLineMin, scanLineMax)); 988 } 989 990 nextCompressBuffer = first - numTasks; 991 stop = last - 1; 992 step = -1; 993 } 994 995 while (true) 996 { 997 if (_data->missingScanLines <= 0) 998 { 999 throw Iex::ArgExc ("Tried to write more scan lines " 1000 "than specified by the data window."); 1001 } 1002 1003 // 1004 // Wait until the next line buffer is ready to be written 1005 // 1006 1007 LineBuffer *writeBuffer = 1008 _data->getLineBuffer (nextWriteBuffer); 1009 1010 writeBuffer->wait(); 1011 1012 int numLines = writeBuffer->scanLineMax - 1013 writeBuffer->scanLineMin + 1; 1014 1015 _data->missingScanLines -= numLines; 1016 1017 // 1018 // If the line buffer is only partially full, then it is 1019 // not complete and we cannot write it to disk yet. 1020 // 1021 1022 if (writeBuffer->partiallyFull) 1023 { 1024 _data->currentScanLine = _data->currentScanLine + 1025 step * numLines; 1026 writeBuffer->post(); 1027 1028 return; 1029 } 1030 1031 // 1032 // Write the line buffer 1033 // 1034 1035 writePixelData (_data, writeBuffer); 1036 nextWriteBuffer += step; 1037 1038 _data->currentScanLine = _data->currentScanLine + 1039 step * numLines; 1040 1041 #ifdef DEBUG 1042 1043 assert (_data->currentScanLine == 1044 ((_data->lineOrder == INCREASING_Y) ? 1045 writeBuffer->scanLineMax + 1: 1046 writeBuffer->scanLineMin - 1)); 1047 1048 #endif 1049 1050 // 1051 // Release the lock on the line buffer 1052 // 1053 1054 writeBuffer->post(); 1055 1056 // 1057 // If this was the last line buffer in the scanline range 1058 // 1059 1060 if (nextWriteBuffer == stop) 1061 break; 1062 1063 // 1064 // If there are no more line buffers to compress, 1065 // then only continue to write out remaining lineBuffers 1066 // 1067 1068 if (nextCompressBuffer == stop) 1069 continue; 1070 1071 // 1072 // Add nextCompressBuffer as a compression task 1073 // 1074 1075 ThreadPool::addGlobalTask 1076 (new LineBufferTask (&taskGroup, _data, nextCompressBuffer, 1077 scanLineMin, scanLineMax)); 1078 1079 // 1080 // Update the next line buffer we need to compress 1081 // 1082 1083 nextCompressBuffer += step; 1084 } 1085 1086 // 1087 // Finish all tasks 1088 // 1089 } 1090 1091 // 1092 // Exeption handling: 1093 // 1094 // LineBufferTask::execute() may have encountered exceptions, but 1095 // those exceptions occurred in another thread, not in the thread 1096 // that is executing this call to OutputFile::writePixels(). 1097 // LineBufferTask::execute() has caught all exceptions and stored 1098 // the exceptions' what() strings in the line buffers. 1099 // Now we check if any line buffer contains a stored exception; if 1100 // this is the case then we re-throw the exception in this thread. 1101 // (It is possible that multiple line buffers contain stored 1102 // exceptions. We re-throw the first exception we find and 1103 // ignore all others.) 1104 // 1105 1106 const string *exception = 0; 1107 1108 for (int i = 0; i < _data->lineBuffers.size(); ++i) 1109 { 1110 LineBuffer *lineBuffer = _data->lineBuffers[i]; 1111 1112 if (lineBuffer->hasException && !exception) 1113 exception = &lineBuffer->exception; 1114 1115 lineBuffer->hasException = false; 1116 } 1117 1118 if (exception) 1119 throw Iex::IoExc (*exception); 1120 } 1121 catch (Iex::BaseExc &e) 1122 { 1123 REPLACE_EXC (e, "Failed to write pixel data to image " 1124 "file \"" << fileName() << "\". " << e); 1125 throw; 1126 } 1127 } 1128 1129 1130 int 1131 OutputFile::currentScanLine () const 1132 { 1133 Lock lock (*_data); 1134 return _data->currentScanLine; 1135 } 1136 1137 1138 void 1139 OutputFile::copyPixels (InputFile &in) 1140 { 1141 Lock lock (*_data); 1142 1143 // 1144 // Check if this file's and and the InputFile's 1145 // headers are compatible. 1146 // 1147 1148 const Header &hdr = _data->header; 1149 const Header &inHdr = in.header(); 1150 1151 if (inHdr.find("tiles") != inHdr.end()) 1152 THROW (Iex::ArgExc, "Cannot copy pixels from image " 1153 "file \"" << in.fileName() << "\" to image " 1154 "file \"" << fileName() << "\". " 1155 "The input file is tiled, but the output file is " 1156 "not. Try using TiledOutputFile::copyPixels " 1157 "instead."); 1158 1159 if (!(hdr.dataWindow() == inHdr.dataWindow())) 1160 THROW (Iex::ArgExc, "Cannot copy pixels from image " 1161 "file \"" << in.fileName() << "\" to image " 1162 "file \"" << fileName() << "\". " 1163 "The files have different data windows."); 1164 1165 if (!(hdr.lineOrder() == inHdr.lineOrder())) 1166 THROW (Iex::ArgExc, "Quick pixel copy from image " 1167 "file \"" << in.fileName() << "\" to image " 1168 "file \"" << fileName() << "\" failed. " 1169 "The files have different line orders."); 1170 1171 if (!(hdr.compression() == inHdr.compression())) 1172 THROW (Iex::ArgExc, "Quick pixel copy from image " 1173 "file \"" << in.fileName() << "\" to image " 1174 "file \"" << fileName() << "\" failed. " 1175 "The files use different compression methods."); 1176 1177 if (!(hdr.channels() == inHdr.channels())) 1178 THROW (Iex::ArgExc, "Quick pixel copy from image " 1179 "file \"" << in.fileName() << "\" to image " 1180 "file \"" << fileName() << "\" failed. " 1181 "The files have different channel lists."); 1182 1183 // 1184 // Verify that no pixel data have been written to this file yet. 1185 // 1186 1187 const Box2i &dataWindow = hdr.dataWindow(); 1188 1189 if (_data->missingScanLines != dataWindow.max.y - dataWindow.min.y + 1) 1190 THROW (Iex::LogicExc, "Quick pixel copy from image " 1191 "file \"" << in.fileName() << "\" to image " 1192 "file \"" << fileName() << "\" failed. " 1193 "\"" << fileName() << "\" already contains " 1194 "pixel data."); 1195 1196 // 1197 // Copy the pixel data. 1198 // 1199 1200 while (_data->missingScanLines > 0) 1201 { 1202 const char *pixelData; 1203 int pixelDataSize; 1204 1205 in.rawPixelData (_data->currentScanLine, pixelData, pixelDataSize); 1206 1207 writePixelData (_data, lineBufferMinY (_data->currentScanLine, 1208 _data->minY, 1209 _data->linesInBuffer), 1210 pixelData, pixelDataSize); 1211 1212 _data->currentScanLine += (_data->lineOrder == INCREASING_Y)? 1213 _data->linesInBuffer: -_data->linesInBuffer; 1214 1215 _data->missingScanLines -= _data->linesInBuffer; 1216 } 1217 } 1218 1219 1220 void 1221 OutputFile::updatePreviewImage (const PreviewRgba newPixels[]) 1222 { 1223 Lock lock (*_data); 1224 1225 if (_data->previewPosition <= 0) 1226 THROW (Iex::LogicExc, "Cannot update preview image pixels. " 1227 "File \"" << fileName() << "\" does not " 1228 "contain a preview image."); 1229 1230 // 1231 // Store the new pixels in the header's preview image attribute. 1232 // 1233 1234 PreviewImageAttribute &pia = 1235 _data->header.typedAttribute <PreviewImageAttribute> ("preview"); 1236 1237 PreviewImage &pi = pia.value(); 1238 PreviewRgba *pixels = pi.pixels(); 1239 int numPixels = pi.width() * pi.height(); 1240 1241 for (int i = 0; i < numPixels; ++i) 1242 pixels[i] = newPixels[i]; 1243 1244 // 1245 // Save the current file position, jump to the position in 1246 // the file where the preview image starts, store the new 1247 // preview image, and jump back to the saved file position. 1248 // 1249 1250 Int64 savedPosition = _data->os->tellp(); 1251 1252 try 1253 { 1254 _data->os->seekp (_data->previewPosition); 1255 pia.writeValueTo (*_data->os, _data->version); 1256 _data->os->seekp (savedPosition); 1257 } 1258 catch (Iex::BaseExc &e) 1259 { 1260 REPLACE_EXC (e, "Cannot update preview image pixels for " 1261 "file \"" << fileName() << "\". " << e); 1262 throw; 1263 } 1264 } 1265 1266 1267 void 1268 OutputFile::breakScanLine (int y, int offset, int length, char c) 1269 { 1270 Lock lock (*_data); 1271 1272 Int64 position = 1273 _data->lineOffsets[(y - _data->minY) / _data->linesInBuffer]; 1274 1275 if (!position) 1276 THROW (Iex::ArgExc, "Cannot overwrite scan line " << y << ". " 1277 "The scan line has not yet been stored in " 1278 "file \"" << fileName() << "\"."); 1279 1280 _data->currentPosition = 0; 1281 _data->os->seekp (position + offset); 1282 1283 for (int i = 0; i < length; ++i) 1284 _data->os->write (&c, 1); 1285 } 1286 1287 1288 } // namespace Imf 1289