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, 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 129string 130prefixFromLayerName (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 142V3f 143ywFromHeader (const Header &header) 144{ 145 Chromaticities cr; 146 147 if (hasChromaticities (header)) 148 cr = chromaticities (header); 149 150 return computeYw (cr); 151} 152 153 154ptrdiff_t 155cachePadding (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 190class 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 237RgbaOutputFile::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 280RgbaOutputFile::ToYca::~ToYca () 281{ 282 delete [] _bufBase; 283 delete [] _tmpBuf; 284} 285 286 287void 288RgbaOutputFile::ToYca::setYCRounding (unsigned int roundY, 289 unsigned int roundC) 290{ 291 _roundY = roundY; 292 _roundC = roundC; 293} 294 295 296void 297RgbaOutputFile::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 355void 356RgbaOutputFile::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 496int 497RgbaOutputFile::ToYca::currentScanLine () const 498{ 499 return _currentScanLine; 500} 501 502 503void 504RgbaOutputFile::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 514void 515RgbaOutputFile::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 526void 527RgbaOutputFile::ToYca::duplicateLastBuffer () 528{ 529 rotateBuffers(); 530 memcpy (_buf[N - 1], _buf[N - 2], _width * sizeof (Rgba)); 531} 532 533 534void 535RgbaOutputFile::ToYca::duplicateSecondToLastBuffer () 536{ 537 rotateBuffers(); 538 memcpy (_buf[N - 1], _buf[N - 3], _width * sizeof (Rgba)); 539} 540 541 542void 543RgbaOutputFile::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 557RgbaOutputFile::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 573RgbaOutputFile::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 589RgbaOutputFile::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 618RgbaOutputFile::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 647RgbaOutputFile::~RgbaOutputFile () 648{ 649 delete _toYca; 650 delete _outputFile; 651} 652 653 654void 655RgbaOutputFile::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 681void 682RgbaOutputFile::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 696int 697RgbaOutputFile::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 711const Header & 712RgbaOutputFile::header () const 713{ 714 return _outputFile->header(); 715} 716 717 718const FrameBuffer & 719RgbaOutputFile::frameBuffer () const 720{ 721 return _outputFile->frameBuffer(); 722} 723 724 725const Imath::Box2i & 726RgbaOutputFile::displayWindow () const 727{ 728 return _outputFile->header().displayWindow(); 729} 730 731 732const Imath::Box2i & 733RgbaOutputFile::dataWindow () const 734{ 735 return _outputFile->header().dataWindow(); 736} 737 738 739float 740RgbaOutputFile::pixelAspectRatio () const 741{ 742 return _outputFile->header().pixelAspectRatio(); 743} 744 745 746const Imath::V2f 747RgbaOutputFile::screenWindowCenter () const 748{ 749 return _outputFile->header().screenWindowCenter(); 750} 751 752 753float 754RgbaOutputFile::screenWindowWidth () const 755{ 756 return _outputFile->header().screenWindowWidth(); 757} 758 759 760LineOrder 761RgbaOutputFile::lineOrder () const 762{ 763 return _outputFile->header().lineOrder(); 764} 765 766 767Compression 768RgbaOutputFile::compression () const 769{ 770 return _outputFile->header().compression(); 771} 772 773 774RgbaChannels 775RgbaOutputFile::channels () const 776{ 777 return rgbaChannels (_outputFile->header().channels()); 778} 779 780 781void 782RgbaOutputFile::updatePreviewImage (const PreviewRgba newPixels[]) 783{ 784 _outputFile->updatePreviewImage (newPixels); 785} 786 787 788void 789RgbaOutputFile::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 799void 800RgbaOutputFile::breakScanLine (int y, int offset, int length, char c) 801{ 802 _outputFile->breakScanLine (y, offset, length, c); 803} 804 805 806class 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 848RgbaInputFile::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 884RgbaInputFile::FromYca::~FromYca () 885{ 886 delete [] _bufBase; 887 delete [] _tmpBuf; 888} 889 890 891void 892RgbaInputFile::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 949void 950RgbaInputFile::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 968void 969RgbaInputFile::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 1079void 1080RgbaInputFile::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 1094void 1095RgbaInputFile::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 1109void 1110RgbaInputFile::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 1153void 1154RgbaInputFile::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 1164RgbaInputFile::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 1176RgbaInputFile::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 1188RgbaInputFile::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 1203RgbaInputFile::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 1218RgbaInputFile::~RgbaInputFile () 1219{ 1220 delete _inputFile; 1221 delete _fromYca; 1222} 1223 1224 1225void 1226RgbaInputFile::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 1273void 1274RgbaInputFile::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 1291void 1292RgbaInputFile::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 1306void 1307RgbaInputFile::readPixels (int scanLine) 1308{ 1309 readPixels (scanLine, scanLine); 1310} 1311 1312 1313bool 1314RgbaInputFile::isComplete () const 1315{ 1316 return _inputFile->isComplete(); 1317} 1318 1319 1320const Header & 1321RgbaInputFile::header () const 1322{ 1323 return _inputFile->header(); 1324} 1325 1326 1327const char * 1328RgbaInputFile::fileName () const 1329{ 1330 return _inputFile->fileName(); 1331} 1332 1333 1334const FrameBuffer & 1335RgbaInputFile::frameBuffer () const 1336{ 1337 return _inputFile->frameBuffer(); 1338} 1339 1340 1341const Imath::Box2i & 1342RgbaInputFile::displayWindow () const 1343{ 1344 return _inputFile->header().displayWindow(); 1345} 1346 1347 1348const Imath::Box2i & 1349RgbaInputFile::dataWindow () const 1350{ 1351 return _inputFile->header().dataWindow(); 1352} 1353 1354 1355float 1356RgbaInputFile::pixelAspectRatio () const 1357{ 1358 return _inputFile->header().pixelAspectRatio(); 1359} 1360 1361 1362const Imath::V2f 1363RgbaInputFile::screenWindowCenter () const 1364{ 1365 return _inputFile->header().screenWindowCenter(); 1366} 1367 1368 1369float 1370RgbaInputFile::screenWindowWidth () const 1371{ 1372 return _inputFile->header().screenWindowWidth(); 1373} 1374 1375 1376LineOrder 1377RgbaInputFile::lineOrder () const 1378{ 1379 return _inputFile->header().lineOrder(); 1380} 1381 1382 1383Compression 1384RgbaInputFile::compression () const 1385{ 1386 return _inputFile->header().compression(); 1387} 1388 1389 1390RgbaChannels 1391RgbaInputFile::channels () const 1392{ 1393 return rgbaChannels (_inputFile->header().channels(), _channelNamePrefix); 1394} 1395 1396 1397int 1398RgbaInputFile::version () const 1399{ 1400 return _inputFile->version(); 1401} 1402 1403 1404} // namespace Imf 1405