Home | History | Annotate | Download | only in IlmImf
      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