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 RgbaOutputFile 38 // class RgbaInputFile 39 // 40 //----------------------------------------------------------------------------- 41 42 #include <ImfRgbaFile.h> 43 #include <ImfOutputFile.h> 44 #include <ImfInputFile.h> 45 #include <ImfChannelList.h> 46 #include <ImfRgbaYca.h> 47 #include <ImfStandardAttributes.h> 48 #include <ImathFun.h> 49 #include <IlmThreadMutex.h> 50 #include <Iex.h> 51 #include <string.h> 52 #include <algorithm> 53 54 55 namespace Imf { 56 57 using namespace std; 58 using namespace Imath; 59 using namespace RgbaYca; 60 using namespace IlmThread; 61 62 namespace { 63 64 void 65 insertChannels (Header &header, RgbaChannels rgbaChannels) 66 { 67 ChannelList ch; 68 69 if (rgbaChannels & (WRITE_Y | WRITE_C)) 70 { 71 if (rgbaChannels & WRITE_Y) 72 { 73 ch.insert ("Y", Channel (HALF, 1, 1)); 74 } 75 76 if (rgbaChannels & WRITE_C) 77 { 78 ch.insert ("RY", Channel (HALF, 2, 2, true)); 79 ch.insert ("BY", Channel (HALF, 2, 2, true)); 80 } 81 } 82 else 83 { 84 if (rgbaChannels & WRITE_R) 85 ch.insert ("R", Channel (HALF, 1, 1)); 86 87 if (rgbaChannels & WRITE_G) 88 ch.insert ("G", Channel (HALF, 1, 1)); 89 90 if (rgbaChannels & WRITE_B) 91 ch.insert ("B", Channel (HALF, 1, 1)); 92 } 93 94 if (rgbaChannels & WRITE_A) 95 ch.insert ("A", Channel (HALF, 1, 1)); 96 97 header.channels() = ch; 98 } 99 100 101 RgbaChannels 102 rgbaChannels (const ChannelList &ch, const string &channelNamePrefix = "") 103 { 104 int i = 0; 105 106 if (ch.findChannel (channelNamePrefix + "R")) 107 i |= WRITE_R; 108 109 if (ch.findChannel (channelNamePrefix + "G")) 110 i |= WRITE_G; 111 112 if (ch.findChannel (channelNamePrefix + "B")) 113 i |= WRITE_B; 114 115 if (ch.findChannel (channelNamePrefix + "A")) 116 i |= WRITE_A; 117 118 if (ch.findChannel (channelNamePrefix + "Y")) 119 i |= WRITE_Y; 120 121 if (ch.findChannel (channelNamePrefix + "RY") || 122 ch.findChannel (channelNamePrefix + "BY")) 123 i |= WRITE_C; 124 125 return RgbaChannels (i); 126 } 127 128 129 string 130 prefixFromLayerName (const string &layerName, const Header &header) 131 { 132 if (layerName.empty()) 133 return ""; 134 135 if (hasMultiView (header) && multiView(header)[0] == layerName) 136 return ""; 137 138 return layerName + "."; 139 } 140 141 142 V3f 143 ywFromHeader (const Header &header) 144 { 145 Chromaticities cr; 146 147 if (hasChromaticities (header)) 148 cr = chromaticities (header); 149 150 return computeYw (cr); 151 } 152 153 154 ptrdiff_t 155 cachePadding (ptrdiff_t size) 156 { 157 // 158 // Some of the buffers that are allocated by classes ToYca and 159 // FromYca, below, may need to be padded to avoid cache thrashing. 160 // If the difference between the buffer size and the nearest power 161 // of two is less than CACHE_LINE_SIZE, then we add an appropriate 162 // amount of padding. 163 // 164 // CACHE_LINE_SIZE must be a power of two, and it must be at 165 // least as big as the true size of a cache line on the machine 166 // we are running on. (It is ok if CACHE_LINE_SIZE is larger 167 // than a real cache line.) 168 // 169 170 static int LOG2_CACHE_LINE_SIZE = 8; 171 static const ptrdiff_t CACHE_LINE_SIZE = (1 << LOG2_CACHE_LINE_SIZE); 172 173 int i = LOG2_CACHE_LINE_SIZE + 2; 174 175 while ((size >> i) > 1) 176 ++i; 177 178 if (size > (1 << (i + 1)) - 64) 179 return 64 + ((1 << (i + 1)) - size); 180 181 if (size < (1 << i) + 64) 182 return 64 + ((1 << i) - size); 183 184 return 0; 185 } 186 187 } // namespace 188 189 190 class RgbaOutputFile::ToYca: public Mutex 191 { 192 public: 193 194 ToYca (OutputFile &outputFile, RgbaChannels rgbaChannels); 195 ~ToYca (); 196 197 void setYCRounding (unsigned int roundY, 198 unsigned int roundC); 199 200 void setFrameBuffer (const Rgba *base, 201 size_t xStride, 202 size_t yStride); 203 204 void writePixels (int numScanLines); 205 int currentScanLine () const; 206 207 private: 208 209 void padTmpBuf (); 210 void rotateBuffers (); 211 void duplicateLastBuffer (); 212 void duplicateSecondToLastBuffer (); 213 void decimateChromaVertAndWriteScanLine (); 214 215 OutputFile & _outputFile; 216 bool _writeY; 217 bool _writeC; 218 bool _writeA; 219 int _xMin; 220 int _width; 221 int _height; 222 int _linesConverted; 223 LineOrder _lineOrder; 224 int _currentScanLine; 225 V3f _yw; 226 Rgba * _bufBase; 227 Rgba * _buf[N]; 228 Rgba * _tmpBuf; 229 const Rgba * _fbBase; 230 size_t _fbXStride; 231 size_t _fbYStride; 232 int _roundY; 233 int _roundC; 234 }; 235 236 237 RgbaOutputFile::ToYca::ToYca (OutputFile &outputFile, 238 RgbaChannels rgbaChannels) 239 : 240 _outputFile (outputFile) 241 { 242 _writeY = (rgbaChannels & WRITE_Y)? true: false; 243 _writeC = (rgbaChannels & WRITE_C)? true: false; 244 _writeA = (rgbaChannels & WRITE_A)? true: false; 245 246 const Box2i dw = _outputFile.header().dataWindow(); 247 248 _xMin = dw.min.x; 249 _width = dw.max.x - dw.min.x + 1; 250 _height = dw.max.y - dw.min.y + 1; 251 252 _linesConverted = 0; 253 _lineOrder = _outputFile.header().lineOrder(); 254 255 if (_lineOrder == INCREASING_Y) 256 _currentScanLine = dw.min.y; 257 else 258 _currentScanLine = dw.max.y; 259 260 _yw = ywFromHeader (_outputFile.header()); 261 262 ptrdiff_t pad = cachePadding (_width * sizeof (Rgba)) / sizeof (Rgba); 263 264 _bufBase = new Rgba[(_width + pad) * N]; 265 266 for (int i = 0; i < N; ++i) 267 _buf[i] = _bufBase + (i * (_width + pad)); 268 269 _tmpBuf = new Rgba[_width + N - 1]; 270 271 _fbBase = 0; 272 _fbXStride = 0; 273 _fbYStride = 0; 274 275 _roundY = 7; 276 _roundC = 5; 277 } 278 279 280 RgbaOutputFile::ToYca::~ToYca () 281 { 282 delete [] _bufBase; 283 delete [] _tmpBuf; 284 } 285 286 287 void 288 RgbaOutputFile::ToYca::setYCRounding (unsigned int roundY, 289 unsigned int roundC) 290 { 291 _roundY = roundY; 292 _roundC = roundC; 293 } 294 295 296 void 297 RgbaOutputFile::ToYca::setFrameBuffer (const Rgba *base, 298 size_t xStride, 299 size_t yStride) 300 { 301 if (_fbBase == 0) 302 { 303 FrameBuffer fb; 304 305 if (_writeY) 306 { 307 fb.insert ("Y", 308 Slice (HALF, // type 309 (char *) &_tmpBuf[-_xMin].g, // base 310 sizeof (Rgba), // xStride 311 0, // yStride 312 1, // xSampling 313 1)); // ySampling 314 } 315 316 if (_writeC) 317 { 318 fb.insert ("RY", 319 Slice (HALF, // type 320 (char *) &_tmpBuf[-_xMin].r, // base 321 sizeof (Rgba) * 2, // xStride 322 0, // yStride 323 2, // xSampling 324 2)); // ySampling 325 326 fb.insert ("BY", 327 Slice (HALF, // type 328 (char *) &_tmpBuf[-_xMin].b, // base 329 sizeof (Rgba) * 2, // xStride 330 0, // yStride 331 2, // xSampling 332 2)); // ySampling 333 } 334 335 if (_writeA) 336 { 337 fb.insert ("A", 338 Slice (HALF, // type 339 (char *) &_tmpBuf[-_xMin].a, // base 340 sizeof (Rgba), // xStride 341 0, // yStride 342 1, // xSampling 343 1)); // ySampling 344 } 345 346 _outputFile.setFrameBuffer (fb); 347 } 348 349 _fbBase = base; 350 _fbXStride = xStride; 351 _fbYStride = yStride; 352 } 353 354 355 void 356 RgbaOutputFile::ToYca::writePixels (int numScanLines) 357 { 358 if (_fbBase == 0) 359 { 360 THROW (Iex::ArgExc, "No frame buffer was specified as the " 361 "pixel data source for image file " 362 "\"" << _outputFile.fileName() << "\"."); 363 } 364 365 if (_writeY && !_writeC) 366 { 367 // 368 // We are writing only luminance; filtering 369 // and subsampling are not necessary. 370 // 371 372 for (int i = 0; i < numScanLines; ++i) 373 { 374 // 375 // Copy the next scan line from the caller's 376 // frame buffer into _tmpBuf. 377 // 378 379 for (int j = 0; j < _width; ++j) 380 { 381 _tmpBuf[j] = _fbBase[_fbYStride * _currentScanLine + 382 _fbXStride * (j + _xMin)]; 383 } 384 385 // 386 // Convert the scan line from RGB to luminance/chroma, 387 // and store the result in the output file. 388 // 389 390 RGBAtoYCA (_yw, _width, _writeA, _tmpBuf, _tmpBuf); 391 _outputFile.writePixels (1); 392 393 ++_linesConverted; 394 395 if (_lineOrder == INCREASING_Y) 396 ++_currentScanLine; 397 else 398 --_currentScanLine; 399 } 400 } 401 else 402 { 403 // 404 // We are writing chroma; the pixels must be filtered and subsampled. 405 // 406 407 for (int i = 0; i < numScanLines; ++i) 408 { 409 // 410 // Copy the next scan line from the caller's 411 // frame buffer into _tmpBuf. 412 // 413 414 for (int j = 0; j < _width; ++j) 415 { 416 _tmpBuf[j + N2] = _fbBase[_fbYStride * _currentScanLine + 417 _fbXStride * (j + _xMin)]; 418 } 419 420 // 421 // Convert the scan line from RGB to luminance/chroma. 422 // 423 424 RGBAtoYCA (_yw, _width, _writeA, _tmpBuf + N2, _tmpBuf + N2); 425 426 // 427 // Append N2 copies of the first and last pixel to the 428 // beginning and end of the scan line. 429 // 430 431 padTmpBuf (); 432 433 // 434 // Filter and subsample the scan line's chroma channels 435 // horizontally; store the result in _buf. 436 // 437 438 rotateBuffers(); 439 decimateChromaHoriz (_width, _tmpBuf, _buf[N - 1]); 440 441 // 442 // If this is the first scan line in the image, 443 // store N2 more copies of the scan line in _buf. 444 // 445 446 if (_linesConverted == 0) 447 { 448 for (int j = 0; j < N2; ++j) 449 duplicateLastBuffer(); 450 } 451 452 ++_linesConverted; 453 454 // 455 // If we have have converted at least N2 scan lines from 456 // RGBA to luminance/chroma, then we can start to filter 457 // and subsample vertically, and store pixels in the 458 // output file. 459 // 460 461 if (_linesConverted > N2) 462 decimateChromaVertAndWriteScanLine(); 463 464 // 465 // If we have already converted the last scan line in 466 // the image to luminance/chroma, filter, subsample and 467 // store the remaining scan lines in _buf. 468 // 469 470 if (_linesConverted >= _height) 471 { 472 for (int j = 0; j < N2 - _height; ++j) 473 duplicateLastBuffer(); 474 475 duplicateSecondToLastBuffer(); 476 ++_linesConverted; 477 decimateChromaVertAndWriteScanLine(); 478 479 for (int j = 1; j < min (_height, N2); ++j) 480 { 481 duplicateLastBuffer(); 482 ++_linesConverted; 483 decimateChromaVertAndWriteScanLine(); 484 } 485 } 486 487 if (_lineOrder == INCREASING_Y) 488 ++_currentScanLine; 489 else 490 --_currentScanLine; 491 } 492 } 493 } 494 495 496 int 497 RgbaOutputFile::ToYca::currentScanLine () const 498 { 499 return _currentScanLine; 500 } 501 502 503 void 504 RgbaOutputFile::ToYca::padTmpBuf () 505 { 506 for (int i = 0; i < N2; ++i) 507 { 508 _tmpBuf[i] = _tmpBuf[N2]; 509 _tmpBuf[_width + N2 + i] = _tmpBuf[_width + N2 - 2]; 510 } 511 } 512 513 514 void 515 RgbaOutputFile::ToYca::rotateBuffers () 516 { 517 Rgba *tmp = _buf[0]; 518 519 for (int i = 0; i < N - 1; ++i) 520 _buf[i] = _buf[i + 1]; 521 522 _buf[N - 1] = tmp; 523 } 524 525 526 void 527 RgbaOutputFile::ToYca::duplicateLastBuffer () 528 { 529 rotateBuffers(); 530 memcpy (_buf[N - 1], _buf[N - 2], _width * sizeof (Rgba)); 531 } 532 533 534 void 535 RgbaOutputFile::ToYca::duplicateSecondToLastBuffer () 536 { 537 rotateBuffers(); 538 memcpy (_buf[N - 1], _buf[N - 3], _width * sizeof (Rgba)); 539 } 540 541 542 void 543 RgbaOutputFile::ToYca::decimateChromaVertAndWriteScanLine () 544 { 545 if (_linesConverted & 1) 546 memcpy (_tmpBuf, _buf[N2], _width * sizeof (Rgba)); 547 else 548 decimateChromaVert (_width, _buf, _tmpBuf); 549 550 if (_writeY && _writeC) 551 roundYCA (_width, _roundY, _roundC, _tmpBuf, _tmpBuf); 552 553 _outputFile.writePixels (1); 554 } 555 556 557 RgbaOutputFile::RgbaOutputFile (const char name[], 558 const Header &header, 559 RgbaChannels rgbaChannels, 560 int numThreads): 561 _outputFile (0), 562 _toYca (0) 563 { 564 Header hd (header); 565 insertChannels (hd, rgbaChannels); 566 _outputFile = new OutputFile (name, hd, numThreads); 567 568 if (rgbaChannels & (WRITE_Y | WRITE_C)) 569 _toYca = new ToYca (*_outputFile, rgbaChannels); 570 } 571 572 573 RgbaOutputFile::RgbaOutputFile (OStream &os, 574 const Header &header, 575 RgbaChannels rgbaChannels, 576 int numThreads): 577 _outputFile (0), 578 _toYca (0) 579 { 580 Header hd (header); 581 insertChannels (hd, rgbaChannels); 582 _outputFile = new OutputFile (os, hd, numThreads); 583 584 if (rgbaChannels & (WRITE_Y | WRITE_C)) 585 _toYca = new ToYca (*_outputFile, rgbaChannels); 586 } 587 588 589 RgbaOutputFile::RgbaOutputFile (const char name[], 590 const Imath::Box2i &displayWindow, 591 const Imath::Box2i &dataWindow, 592 RgbaChannels rgbaChannels, 593 float pixelAspectRatio, 594 const Imath::V2f screenWindowCenter, 595 float screenWindowWidth, 596 LineOrder lineOrder, 597 Compression compression, 598 int numThreads): 599 _outputFile (0), 600 _toYca (0) 601 { 602 Header hd (displayWindow, 603 dataWindow.isEmpty()? displayWindow: dataWindow, 604 pixelAspectRatio, 605 screenWindowCenter, 606 screenWindowWidth, 607 lineOrder, 608 compression); 609 610 insertChannels (hd, rgbaChannels); 611 _outputFile = new OutputFile (name, hd, numThreads); 612 613 if (rgbaChannels & (WRITE_Y | WRITE_C)) 614 _toYca = new ToYca (*_outputFile, rgbaChannels); 615 } 616 617 618 RgbaOutputFile::RgbaOutputFile (const char name[], 619 int width, 620 int height, 621 RgbaChannels rgbaChannels, 622 float pixelAspectRatio, 623 const Imath::V2f screenWindowCenter, 624 float screenWindowWidth, 625 LineOrder lineOrder, 626 Compression compression, 627 int numThreads): 628 _outputFile (0), 629 _toYca (0) 630 { 631 Header hd (width, 632 height, 633 pixelAspectRatio, 634 screenWindowCenter, 635 screenWindowWidth, 636 lineOrder, 637 compression); 638 639 insertChannels (hd, rgbaChannels); 640 _outputFile = new OutputFile (name, hd, numThreads); 641 642 if (rgbaChannels & (WRITE_Y | WRITE_C)) 643 _toYca = new ToYca (*_outputFile, rgbaChannels); 644 } 645 646 647 RgbaOutputFile::~RgbaOutputFile () 648 { 649 delete _toYca; 650 delete _outputFile; 651 } 652 653 654 void 655 RgbaOutputFile::setFrameBuffer (const Rgba *base, 656 size_t xStride, 657 size_t yStride) 658 { 659 if (_toYca) 660 { 661 Lock lock (*_toYca); 662 _toYca->setFrameBuffer (base, xStride, yStride); 663 } 664 else 665 { 666 size_t xs = xStride * sizeof (Rgba); 667 size_t ys = yStride * sizeof (Rgba); 668 669 FrameBuffer fb; 670 671 fb.insert ("R", Slice (HALF, (char *) &base[0].r, xs, ys)); 672 fb.insert ("G", Slice (HALF, (char *) &base[0].g, xs, ys)); 673 fb.insert ("B", Slice (HALF, (char *) &base[0].b, xs, ys)); 674 fb.insert ("A", Slice (HALF, (char *) &base[0].a, xs, ys)); 675 676 _outputFile->setFrameBuffer (fb); 677 } 678 } 679 680 681 void 682 RgbaOutputFile::writePixels (int numScanLines) 683 { 684 if (_toYca) 685 { 686 Lock lock (*_toYca); 687 _toYca->writePixels (numScanLines); 688 } 689 else 690 { 691 _outputFile->writePixels (numScanLines); 692 } 693 } 694 695 696 int 697 RgbaOutputFile::currentScanLine () const 698 { 699 if (_toYca) 700 { 701 Lock lock (*_toYca); 702 return _toYca->currentScanLine(); 703 } 704 else 705 { 706 return _outputFile->currentScanLine(); 707 } 708 } 709 710 711 const Header & 712 RgbaOutputFile::header () const 713 { 714 return _outputFile->header(); 715 } 716 717 718 const FrameBuffer & 719 RgbaOutputFile::frameBuffer () const 720 { 721 return _outputFile->frameBuffer(); 722 } 723 724 725 const Imath::Box2i & 726 RgbaOutputFile::displayWindow () const 727 { 728 return _outputFile->header().displayWindow(); 729 } 730 731 732 const Imath::Box2i & 733 RgbaOutputFile::dataWindow () const 734 { 735 return _outputFile->header().dataWindow(); 736 } 737 738 739 float 740 RgbaOutputFile::pixelAspectRatio () const 741 { 742 return _outputFile->header().pixelAspectRatio(); 743 } 744 745 746 const Imath::V2f 747 RgbaOutputFile::screenWindowCenter () const 748 { 749 return _outputFile->header().screenWindowCenter(); 750 } 751 752 753 float 754 RgbaOutputFile::screenWindowWidth () const 755 { 756 return _outputFile->header().screenWindowWidth(); 757 } 758 759 760 LineOrder 761 RgbaOutputFile::lineOrder () const 762 { 763 return _outputFile->header().lineOrder(); 764 } 765 766 767 Compression 768 RgbaOutputFile::compression () const 769 { 770 return _outputFile->header().compression(); 771 } 772 773 774 RgbaChannels 775 RgbaOutputFile::channels () const 776 { 777 return rgbaChannels (_outputFile->header().channels()); 778 } 779 780 781 void 782 RgbaOutputFile::updatePreviewImage (const PreviewRgba newPixels[]) 783 { 784 _outputFile->updatePreviewImage (newPixels); 785 } 786 787 788 void 789 RgbaOutputFile::setYCRounding (unsigned int roundY, unsigned int roundC) 790 { 791 if (_toYca) 792 { 793 Lock lock (*_toYca); 794 _toYca->setYCRounding (roundY, roundC); 795 } 796 } 797 798 799 void 800 RgbaOutputFile::breakScanLine (int y, int offset, int length, char c) 801 { 802 _outputFile->breakScanLine (y, offset, length, c); 803 } 804 805 806 class RgbaInputFile::FromYca: public Mutex 807 { 808 public: 809 810 FromYca (InputFile &inputFile, RgbaChannels rgbaChannels); 811 ~FromYca (); 812 813 void setFrameBuffer (Rgba *base, 814 size_t xStride, 815 size_t yStride, 816 const string &channelNamePrefix); 817 818 void readPixels (int scanLine1, int scanLine2); 819 820 private: 821 822 void readPixels (int scanLine); 823 void rotateBuf1 (int d); 824 void rotateBuf2 (int d); 825 void readYCAScanLine (int y, Rgba buf[]); 826 void padTmpBuf (); 827 828 InputFile & _inputFile; 829 bool _readC; 830 int _xMin; 831 int _yMin; 832 int _yMax; 833 int _width; 834 int _height; 835 int _currentScanLine; 836 LineOrder _lineOrder; 837 V3f _yw; 838 Rgba * _bufBase; 839 Rgba * _buf1[N + 2]; 840 Rgba * _buf2[3]; 841 Rgba * _tmpBuf; 842 Rgba * _fbBase; 843 size_t _fbXStride; 844 size_t _fbYStride; 845 }; 846 847 848 RgbaInputFile::FromYca::FromYca (InputFile &inputFile, 849 RgbaChannels rgbaChannels) 850 : 851 _inputFile (inputFile) 852 { 853 _readC = (rgbaChannels & WRITE_C)? true: false; 854 855 const Box2i dw = _inputFile.header().dataWindow(); 856 857 _xMin = dw.min.x; 858 _yMin = dw.min.y; 859 _yMax = dw.max.y; 860 _width = dw.max.x - dw.min.x + 1; 861 _height = dw.max.y - dw.min.y + 1; 862 _currentScanLine = dw.min.y - N - 2; 863 _lineOrder = _inputFile.header().lineOrder(); 864 _yw = ywFromHeader (_inputFile.header()); 865 866 ptrdiff_t pad = cachePadding (_width * sizeof (Rgba)) / sizeof (Rgba); 867 868 _bufBase = new Rgba[(_width + pad) * (N + 2 + 3)]; 869 870 for (int i = 0; i < N + 2; ++i) 871 _buf1[i] = _bufBase + (i * (_width + pad)); 872 873 for (int i = 0; i < 3; ++i) 874 _buf2[i] = _bufBase + ((i + N + 2) * (_width + pad)); 875 876 _tmpBuf = new Rgba[_width + N - 1]; 877 878 _fbBase = 0; 879 _fbXStride = 0; 880 _fbYStride = 0; 881 } 882 883 884 RgbaInputFile::FromYca::~FromYca () 885 { 886 delete [] _bufBase; 887 delete [] _tmpBuf; 888 } 889 890 891 void 892 RgbaInputFile::FromYca::setFrameBuffer (Rgba *base, 893 size_t xStride, 894 size_t yStride, 895 const string &channelNamePrefix) 896 { 897 if (_fbBase == 0) 898 { 899 FrameBuffer fb; 900 901 fb.insert (channelNamePrefix + "Y", 902 Slice (HALF, // type 903 (char *) &_tmpBuf[N2 - _xMin].g, // base 904 sizeof (Rgba), // xStride 905 0, // yStride 906 1, // xSampling 907 1, // ySampling 908 0.5)); // fillValue 909 910 if (_readC) 911 { 912 fb.insert (channelNamePrefix + "RY", 913 Slice (HALF, // type 914 (char *) &_tmpBuf[N2 - _xMin].r, // base 915 sizeof (Rgba) * 2, // xStride 916 0, // yStride 917 2, // xSampling 918 2, // ySampling 919 0.0)); // fillValue 920 921 fb.insert (channelNamePrefix + "BY", 922 Slice (HALF, // type 923 (char *) &_tmpBuf[N2 - _xMin].b, // base 924 sizeof (Rgba) * 2, // xStride 925 0, // yStride 926 2, // xSampling 927 2, // ySampling 928 0.0)); // fillValue 929 } 930 931 fb.insert (channelNamePrefix + "A", 932 Slice (HALF, // type 933 (char *) &_tmpBuf[N2 - _xMin].a, // base 934 sizeof (Rgba), // xStride 935 0, // yStride 936 1, // xSampling 937 1, // ySampling 938 1.0)); // fillValue 939 940 _inputFile.setFrameBuffer (fb); 941 } 942 943 _fbBase = base; 944 _fbXStride = xStride; 945 _fbYStride = yStride; 946 } 947 948 949 void 950 RgbaInputFile::FromYca::readPixels (int scanLine1, int scanLine2) 951 { 952 int minY = min (scanLine1, scanLine2); 953 int maxY = max (scanLine1, scanLine2); 954 955 if (_lineOrder == INCREASING_Y) 956 { 957 for (int y = minY; y <= maxY; ++y) 958 readPixels (y); 959 } 960 else 961 { 962 for (int y = maxY; y >= minY; --y) 963 readPixels (y); 964 } 965 } 966 967 968 void 969 RgbaInputFile::FromYca::readPixels (int scanLine) 970 { 971 if (_fbBase == 0) 972 { 973 THROW (Iex::ArgExc, "No frame buffer was specified as the " 974 "pixel data destination for image file " 975 "\"" << _inputFile.fileName() << "\"."); 976 } 977 978 // 979 // In order to convert one scan line to RGB format, we need that 980 // scan line plus N2+1 extra scan lines above and N2+1 scan lines 981 // below in luminance/chroma format. 982 // 983 // We allow random access to scan lines, but we buffer partially 984 // processed luminance/chroma data in order to make reading pixels 985 // in increasing y or decreasing y order reasonably efficient: 986 // 987 // _currentScanLine holds the y coordinate of the scan line 988 // that was most recently read. 989 // 990 // _buf1 contains scan lines _currentScanLine-N2-1 991 // through _currentScanLine+N2+1 in 992 // luminance/chroma format. Odd-numbered 993 // lines contain no chroma data. Even-numbered 994 // lines have valid chroma data for all pixels. 995 // 996 // _buf2 contains scan lines _currentScanLine-1 997 // through _currentScanLine+1, in RGB format. 998 // Super-saturated pixels (see ImfRgbaYca.h) 999 // have not yet been eliminated. 1000 // 1001 // If the scan line we are trying to read now is close enough to 1002 // _currentScanLine, we don't have to recompute the contents of _buf1 1003 // and _buf2 from scratch. We can rotate _buf1 and _buf2, and fill 1004 // in the missing data. 1005 // 1006 1007 int dy = scanLine - _currentScanLine; 1008 1009 if (abs (dy) < N + 2) 1010 rotateBuf1 (dy); 1011 1012 if (abs (dy) < 3) 1013 rotateBuf2 (dy); 1014 1015 if (dy < 0) 1016 { 1017 { 1018 int n = min (-dy, N + 2); 1019 int yMin = scanLine - N2 - 1; 1020 1021 for (int i = n - 1; i >= 0; --i) 1022 readYCAScanLine (yMin + i, _buf1[i]); 1023 } 1024 1025 { 1026 int n = min (-dy, 3); 1027 1028 for (int i = 0; i < n; ++i) 1029 { 1030 if ((scanLine + i) & 1) 1031 { 1032 YCAtoRGBA (_yw, _width, _buf1[N2 + i], _buf2[i]); 1033 } 1034 else 1035 { 1036 reconstructChromaVert (_width, _buf1 + i, _buf2[i]); 1037 YCAtoRGBA (_yw, _width, _buf2[i], _buf2[i]); 1038 } 1039 } 1040 } 1041 } 1042 else 1043 { 1044 { 1045 int n = min (dy, N + 2); 1046 int yMax = scanLine + N2 + 1; 1047 1048 for (int i = n - 1; i >= 0; --i) 1049 readYCAScanLine (yMax - i, _buf1[N + 1 - i]); 1050 } 1051 1052 { 1053 int n = min (dy, 3); 1054 1055 for (int i = 2; i > 2 - n; --i) 1056 { 1057 if ((scanLine + i) & 1) 1058 { 1059 YCAtoRGBA (_yw, _width, _buf1[N2 + i], _buf2[i]); 1060 } 1061 else 1062 { 1063 reconstructChromaVert (_width, _buf1 + i, _buf2[i]); 1064 YCAtoRGBA (_yw, _width, _buf2[i], _buf2[i]); 1065 } 1066 } 1067 } 1068 } 1069 1070 fixSaturation (_yw, _width, _buf2, _tmpBuf); 1071 1072 for (int i = 0; i < _width; ++i) 1073 _fbBase[_fbYStride * scanLine + _fbXStride * (i + _xMin)] = _tmpBuf[i]; 1074 1075 _currentScanLine = scanLine; 1076 } 1077 1078 1079 void 1080 RgbaInputFile::FromYca::rotateBuf1 (int d) 1081 { 1082 d = modp (d, N + 2); 1083 1084 Rgba *tmp[N + 2]; 1085 1086 for (int i = 0; i < N + 2; ++i) 1087 tmp[i] = _buf1[i]; 1088 1089 for (int i = 0; i < N + 2; ++i) 1090 _buf1[i] = tmp[(i + d) % (N + 2)]; 1091 } 1092 1093 1094 void 1095 RgbaInputFile::FromYca::rotateBuf2 (int d) 1096 { 1097 d = modp (d, 3); 1098 1099 Rgba *tmp[3]; 1100 1101 for (int i = 0; i < 3; ++i) 1102 tmp[i] = _buf2[i]; 1103 1104 for (int i = 0; i < 3; ++i) 1105 _buf2[i] = tmp[(i + d) % 3]; 1106 } 1107 1108 1109 void 1110 RgbaInputFile::FromYca::readYCAScanLine (int y, Rgba *buf) 1111 { 1112 // 1113 // Clamp y. 1114 // 1115 1116 if (y < _yMin) 1117 y = _yMin; 1118 else if (y > _yMax) 1119 y = _yMax - 1; 1120 1121 // 1122 // Read scan line y into _tmpBuf. 1123 // 1124 1125 _inputFile.readPixels (y); 1126 1127 // 1128 // Reconstruct missing chroma samples and copy 1129 // the scan line into buf. 1130 // 1131 1132 if (!_readC) 1133 { 1134 for (int i = 0; i < _width; ++i) 1135 { 1136 _tmpBuf[i + N2].r = 0; 1137 _tmpBuf[i + N2].b = 0; 1138 } 1139 } 1140 1141 if (y & 1) 1142 { 1143 memcpy (buf, _tmpBuf + N2, _width * sizeof (Rgba)); 1144 } 1145 else 1146 { 1147 padTmpBuf(); 1148 reconstructChromaHoriz (_width, _tmpBuf, buf); 1149 } 1150 } 1151 1152 1153 void 1154 RgbaInputFile::FromYca::padTmpBuf () 1155 { 1156 for (int i = 0; i < N2; ++i) 1157 { 1158 _tmpBuf[i] = _tmpBuf[N2]; 1159 _tmpBuf[_width + N2 + i] = _tmpBuf[_width + N2 - 2]; 1160 } 1161 } 1162 1163 1164 RgbaInputFile::RgbaInputFile (const char name[], int numThreads): 1165 _inputFile (new InputFile (name, numThreads)), 1166 _fromYca (0), 1167 _channelNamePrefix ("") 1168 { 1169 RgbaChannels rgbaChannels = channels(); 1170 1171 if (rgbaChannels & (WRITE_Y | WRITE_C)) 1172 _fromYca = new FromYca (*_inputFile, rgbaChannels); 1173 } 1174 1175 1176 RgbaInputFile::RgbaInputFile (IStream &is, int numThreads): 1177 _inputFile (new InputFile (is, numThreads)), 1178 _fromYca (0), 1179 _channelNamePrefix ("") 1180 { 1181 RgbaChannels rgbaChannels = channels(); 1182 1183 if (rgbaChannels & (WRITE_Y | WRITE_C)) 1184 _fromYca = new FromYca (*_inputFile, rgbaChannels); 1185 } 1186 1187 1188 RgbaInputFile::RgbaInputFile (const char name[], 1189 const string &layerName, 1190 int numThreads) 1191 : 1192 _inputFile (new InputFile (name, numThreads)), 1193 _fromYca (0), 1194 _channelNamePrefix (prefixFromLayerName (layerName, _inputFile->header())) 1195 { 1196 RgbaChannels rgbaChannels = channels(); 1197 1198 if (rgbaChannels & (WRITE_Y | WRITE_C)) 1199 _fromYca = new FromYca (*_inputFile, rgbaChannels); 1200 } 1201 1202 1203 RgbaInputFile::RgbaInputFile (IStream &is, 1204 const string &layerName, 1205 int numThreads) 1206 : 1207 _inputFile (new InputFile (is, numThreads)), 1208 _fromYca (0), 1209 _channelNamePrefix (prefixFromLayerName (layerName, _inputFile->header())) 1210 { 1211 RgbaChannels rgbaChannels = channels(); 1212 1213 if (rgbaChannels & (WRITE_Y | WRITE_C)) 1214 _fromYca = new FromYca (*_inputFile, rgbaChannels); 1215 } 1216 1217 1218 RgbaInputFile::~RgbaInputFile () 1219 { 1220 delete _inputFile; 1221 delete _fromYca; 1222 } 1223 1224 1225 void 1226 RgbaInputFile::setFrameBuffer (Rgba *base, size_t xStride, size_t yStride) 1227 { 1228 if (_fromYca) 1229 { 1230 Lock lock (*_fromYca); 1231 _fromYca->setFrameBuffer (base, xStride, yStride, _channelNamePrefix); 1232 } 1233 else 1234 { 1235 size_t xs = xStride * sizeof (Rgba); 1236 size_t ys = yStride * sizeof (Rgba); 1237 1238 FrameBuffer fb; 1239 1240 fb.insert (_channelNamePrefix + "R", 1241 Slice (HALF, 1242 (char *) &base[0].r, 1243 xs, ys, 1244 1, 1, // xSampling, ySampling 1245 0.0)); // fillValue 1246 1247 fb.insert (_channelNamePrefix + "G", 1248 Slice (HALF, 1249 (char *) &base[0].g, 1250 xs, ys, 1251 1, 1, // xSampling, ySampling 1252 0.0)); // fillValue 1253 1254 fb.insert (_channelNamePrefix + "B", 1255 Slice (HALF, 1256 (char *) &base[0].b, 1257 xs, ys, 1258 1, 1, // xSampling, ySampling 1259 0.0)); // fillValue 1260 1261 fb.insert (_channelNamePrefix + "A", 1262 Slice (HALF, 1263 (char *) &base[0].a, 1264 xs, ys, 1265 1, 1, // xSampling, ySampling 1266 1.0)); // fillValue 1267 1268 _inputFile->setFrameBuffer (fb); 1269 } 1270 } 1271 1272 1273 void 1274 RgbaInputFile::setLayerName (const string &layerName) 1275 { 1276 delete _fromYca; 1277 _fromYca = 0; 1278 1279 _channelNamePrefix = prefixFromLayerName (layerName, _inputFile->header()); 1280 1281 RgbaChannels rgbaChannels = channels(); 1282 1283 if (rgbaChannels & (WRITE_Y | WRITE_C)) 1284 _fromYca = new FromYca (*_inputFile, rgbaChannels); 1285 1286 FrameBuffer fb; 1287 _inputFile->setFrameBuffer (fb); 1288 } 1289 1290 1291 void 1292 RgbaInputFile::readPixels (int scanLine1, int scanLine2) 1293 { 1294 if (_fromYca) 1295 { 1296 Lock lock (*_fromYca); 1297 _fromYca->readPixels (scanLine1, scanLine2); 1298 } 1299 else 1300 { 1301 _inputFile->readPixels (scanLine1, scanLine2); 1302 } 1303 } 1304 1305 1306 void 1307 RgbaInputFile::readPixels (int scanLine) 1308 { 1309 readPixels (scanLine, scanLine); 1310 } 1311 1312 1313 bool 1314 RgbaInputFile::isComplete () const 1315 { 1316 return _inputFile->isComplete(); 1317 } 1318 1319 1320 const Header & 1321 RgbaInputFile::header () const 1322 { 1323 return _inputFile->header(); 1324 } 1325 1326 1327 const char * 1328 RgbaInputFile::fileName () const 1329 { 1330 return _inputFile->fileName(); 1331 } 1332 1333 1334 const FrameBuffer & 1335 RgbaInputFile::frameBuffer () const 1336 { 1337 return _inputFile->frameBuffer(); 1338 } 1339 1340 1341 const Imath::Box2i & 1342 RgbaInputFile::displayWindow () const 1343 { 1344 return _inputFile->header().displayWindow(); 1345 } 1346 1347 1348 const Imath::Box2i & 1349 RgbaInputFile::dataWindow () const 1350 { 1351 return _inputFile->header().dataWindow(); 1352 } 1353 1354 1355 float 1356 RgbaInputFile::pixelAspectRatio () const 1357 { 1358 return _inputFile->header().pixelAspectRatio(); 1359 } 1360 1361 1362 const Imath::V2f 1363 RgbaInputFile::screenWindowCenter () const 1364 { 1365 return _inputFile->header().screenWindowCenter(); 1366 } 1367 1368 1369 float 1370 RgbaInputFile::screenWindowWidth () const 1371 { 1372 return _inputFile->header().screenWindowWidth(); 1373 } 1374 1375 1376 LineOrder 1377 RgbaInputFile::lineOrder () const 1378 { 1379 return _inputFile->header().lineOrder(); 1380 } 1381 1382 1383 Compression 1384 RgbaInputFile::compression () const 1385 { 1386 return _inputFile->header().compression(); 1387 } 1388 1389 1390 RgbaChannels 1391 RgbaInputFile::channels () const 1392 { 1393 return rgbaChannels (_inputFile->header().channels(), _channelNamePrefix); 1394 } 1395 1396 1397 int 1398 RgbaInputFile::version () const 1399 { 1400 return _inputFile->version(); 1401 } 1402 1403 1404 } // namespace Imf 1405