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