This source file includes following definitions.
- insertChannels
 
- rgbaChannels
 
- prefixFromLayerName
 
- ywFromHeader
 
- cachePadding
 
- setYCRounding
 
- setFrameBuffer
 
- writePixels
 
- currentScanLine
 
- padTmpBuf
 
- rotateBuffers
 
- duplicateLastBuffer
 
- duplicateSecondToLastBuffer
 
- decimateChromaVertAndWriteScanLine
 
- _toYca
 
- _toYca
 
- _toYca
 
- _toYca
 
- setFrameBuffer
 
- writePixels
 
- currentScanLine
 
- header
 
- frameBuffer
 
- displayWindow
 
- dataWindow
 
- pixelAspectRatio
 
- screenWindowCenter
 
- screenWindowWidth
 
- lineOrder
 
- compression
 
- channels
 
- updatePreviewImage
 
- setYCRounding
 
- breakScanLine
 
- setFrameBuffer
 
- readPixels
 
- readPixels
 
- rotateBuf1
 
- rotateBuf2
 
- readYCAScanLine
 
- padTmpBuf
 
- _channelNamePrefix
 
- _channelNamePrefix
 
- _channelNamePrefix
 
- _channelNamePrefix
 
- setFrameBuffer
 
- setLayerName
 
- readPixels
 
- readPixels
 
- isComplete
 
- header
 
- fileName
 
- frameBuffer
 
- displayWindow
 
- dataWindow
 
- pixelAspectRatio
 
- screenWindowCenter
 
- screenWindowWidth
 
- lineOrder
 
- compression
 
- channels
 
- version
 
#include <ImfRgbaFile.h>
#include <ImfOutputFile.h>
#include <ImfInputFile.h>
#include <ImfChannelList.h>
#include <ImfRgbaYca.h>
#include <ImfStandardAttributes.h>
#include <ImathFun.h>
#include <IlmThreadMutex.h>
#include <Iex.h>
#include <string.h>
#include <algorithm>
namespace Imf {
using namespace std;
using namespace Imath;
using namespace RgbaYca;
using namespace IlmThread;
namespace {
void
insertChannels (Header &header, RgbaChannels rgbaChannels)
{
    ChannelList ch;
    if (rgbaChannels & (WRITE_Y | WRITE_C))
    {
    if (rgbaChannels & WRITE_Y)
    {
        ch.insert ("Y", Channel (HALF, 1, 1));
    }
    if (rgbaChannels & WRITE_C)
    {
        ch.insert ("RY", Channel (HALF, 2, 2, true));
        ch.insert ("BY", Channel (HALF, 2, 2, true));
    }
    }
    else
    {
    if (rgbaChannels & WRITE_R)
        ch.insert ("R", Channel (HALF, 1, 1));
    if (rgbaChannels & WRITE_G)
        ch.insert ("G", Channel (HALF, 1, 1));
    if (rgbaChannels & WRITE_B)
        ch.insert ("B", Channel (HALF, 1, 1));
    }
    if (rgbaChannels & WRITE_A)
    ch.insert ("A", Channel (HALF, 1, 1));
    header.channels() = ch;
}
RgbaChannels
rgbaChannels (const ChannelList &ch, const string &channelNamePrefix = "")
{
    int i = 0;
    if (ch.findChannel (channelNamePrefix + "R"))
    i |= WRITE_R;
    if (ch.findChannel (channelNamePrefix + "G"))
    i |= WRITE_G;
    if (ch.findChannel (channelNamePrefix + "B"))
    i |= WRITE_B;
    if (ch.findChannel (channelNamePrefix + "A"))
    i |= WRITE_A;
    if (ch.findChannel (channelNamePrefix + "Y"))
    i |= WRITE_Y;
    if (ch.findChannel (channelNamePrefix + "RY") ||
    ch.findChannel (channelNamePrefix + "BY"))
    i |= WRITE_C;
    return RgbaChannels (i);
}
string
prefixFromLayerName (const string &layerName, const Header &header)
{
    if (layerName.empty())
    return "";
    if (hasMultiView (header) && multiView(header)[0] == layerName)
    return "";
    return layerName + ".";
}
V3f
ywFromHeader (const Header &header)
{
    Chromaticities cr;
    if (hasChromaticities (header))
    cr = chromaticities (header);
    return computeYw (cr);
}
ptrdiff_t
cachePadding (ptrdiff_t size)
{
    
    
    
    
    
    
    
    
    
    
    
    
    static int LOG2_CACHE_LINE_SIZE = 8;
    static const ptrdiff_t CACHE_LINE_SIZE = (1 << LOG2_CACHE_LINE_SIZE);
    int i = LOG2_CACHE_LINE_SIZE + 2;
    while ((size >> i) > 1)
    ++i;
    if (size > (1 << (i + 1)) - 64)
    return 64 + ((1 << (i + 1)) - size);
    if (size < (1 << i) + 64)
    return 64 + ((1 << i) - size);
    return 0;
}
} 
class RgbaOutputFile::ToYca: public Mutex
{
  public:
     ToYca (OutputFile &outputFile, RgbaChannels rgbaChannels);
    ~ToYca ();
    void                setYCRounding (unsigned int roundY,
                           unsigned int roundC);
    void                setFrameBuffer (const Rgba *base,
                    size_t xStride,
                    size_t yStride);
    void                writePixels (int numScanLines);
    int                 currentScanLine () const;
  private:
    void                padTmpBuf ();
    void                rotateBuffers ();
    void                duplicateLastBuffer ();
    void                duplicateSecondToLastBuffer ();
    void                decimateChromaVertAndWriteScanLine ();
    OutputFile &        _outputFile;
    bool                _writeY;
    bool                _writeC;
    bool                _writeA;
    int                 _xMin;
    int                 _width;
    int                 _height;
    int                 _linesConverted;
    LineOrder           _lineOrder;
    int                 _currentScanLine;
    V3f                 _yw;
    Rgba *              _bufBase;
    Rgba *              _buf[N];
    Rgba *              _tmpBuf;
    const Rgba *        _fbBase;
    size_t              _fbXStride;
    size_t              _fbYStride;
    int                 _roundY;
    int                 _roundC;
};
RgbaOutputFile::ToYca::ToYca (OutputFile &outputFile,
                  RgbaChannels rgbaChannels)
:
    _outputFile (outputFile)
{
    _writeY = (rgbaChannels & WRITE_Y)? true: false;
    _writeC = (rgbaChannels & WRITE_C)? true: false;
    _writeA = (rgbaChannels & WRITE_A)? true: false;
    const Box2i dw = _outputFile.header().dataWindow();
    _xMin = dw.min.x;
    _width  = dw.max.x - dw.min.x + 1;
    _height = dw.max.y - dw.min.y + 1;
    _linesConverted = 0;
    _lineOrder = _outputFile.header().lineOrder();
    if (_lineOrder == INCREASING_Y)
    _currentScanLine = dw.min.y;
    else
    _currentScanLine = dw.max.y;
    _yw = ywFromHeader (_outputFile.header());
    ptrdiff_t pad = cachePadding (_width * sizeof (Rgba)) / sizeof (Rgba);
    _bufBase = new Rgba[(_width + pad) * N];
    for (int i = 0; i < N; ++i)
    _buf[i] = _bufBase + (i * (_width + pad));
    _tmpBuf = new Rgba[_width + N - 1];
    _fbBase = 0;
    _fbXStride = 0;
    _fbYStride = 0;
    _roundY = 7;
    _roundC = 5;
}
RgbaOutputFile::ToYca::~ToYca ()
{
    delete [] _bufBase;
    delete [] _tmpBuf;
}
void
RgbaOutputFile::ToYca::setYCRounding (unsigned int roundY,
                      unsigned int roundC)
{
    _roundY = roundY;
    _roundC = roundC;
}
void
RgbaOutputFile::ToYca::setFrameBuffer (const Rgba *base,
                       size_t xStride,
                       size_t yStride)
{
    if (_fbBase == 0)
    {
    FrameBuffer fb;
    if (_writeY)
    {
        fb.insert ("Y",
               Slice (HALF,                             
                  (char *) &_tmpBuf[-_xMin].g,  
                  sizeof (Rgba),                        
                  0,                            
                  1,                            
                  1));                          
    }
    if (_writeC)
    {
        fb.insert ("RY",
               Slice (HALF,                             
                  (char *) &_tmpBuf[-_xMin].r,  
                  sizeof (Rgba) * 2,            
                  0,                            
                  2,                            
                  2));                          
        fb.insert ("BY",
               Slice (HALF,                             
                  (char *) &_tmpBuf[-_xMin].b,  
                  sizeof (Rgba) * 2,            
                  0,                            
                  2,                            
                  2));                          
    }
    if (_writeA)
    {
        fb.insert ("A",
               Slice (HALF,                             
                  (char *) &_tmpBuf[-_xMin].a,  
                  sizeof (Rgba),                        
                  0,                            
                  1,                            
                  1));                          
    }
    _outputFile.setFrameBuffer (fb);
    }
    _fbBase = base;
    _fbXStride = xStride;
    _fbYStride = yStride;
}
void
RgbaOutputFile::ToYca::writePixels (int numScanLines)
{
    if (_fbBase == 0)
    {
    THROW (Iex::ArgExc, "No frame buffer was specified as the "
                "pixel data source for image file "
                "\"" << _outputFile.fileName() << "\".");
    }
    if (_writeY && !_writeC)
    {
    
    
    
    
    for (int i = 0; i < numScanLines; ++i)
    {
        
        
        
        
        for (int j = 0; j < _width; ++j)
        {
        _tmpBuf[j] = _fbBase[_fbYStride * _currentScanLine +
                     _fbXStride * (j + _xMin)];
        }
        
        
        
        
        RGBAtoYCA (_yw, _width, _writeA, _tmpBuf, _tmpBuf);
        _outputFile.writePixels (1);
        ++_linesConverted;
        if (_lineOrder == INCREASING_Y)
        ++_currentScanLine;
        else
        --_currentScanLine;
    }
    }
    else
    {
    
    
    
    for (int i = 0; i < numScanLines; ++i)
    {
        
        
        
        
        for (int j = 0; j < _width; ++j)
        {
        _tmpBuf[j + N2] = _fbBase[_fbYStride * _currentScanLine +
                      _fbXStride * (j + _xMin)];
        }
        
        
        
        RGBAtoYCA (_yw, _width, _writeA, _tmpBuf + N2, _tmpBuf + N2);
        
        
        
        
        padTmpBuf ();
        
        
        
        
        rotateBuffers();
        decimateChromaHoriz (_width, _tmpBuf, _buf[N - 1]);
        
        
        
        
        if (_linesConverted == 0)
        {
        for (int j = 0; j < N2; ++j)
            duplicateLastBuffer();
        }
        ++_linesConverted;
        
        
        
        
        
        
        if (_linesConverted > N2)
        decimateChromaVertAndWriteScanLine();
        
        
        
        
        
        if (_linesConverted >= _height)
        {
        for (int j = 0; j < N2 - _height; ++j)
            duplicateLastBuffer();
        duplicateSecondToLastBuffer();
        ++_linesConverted;
        decimateChromaVertAndWriteScanLine();
        for (int j = 1; j < min (_height, N2); ++j)
        {
            duplicateLastBuffer();
            ++_linesConverted;
            decimateChromaVertAndWriteScanLine();
        }
        }
        if (_lineOrder == INCREASING_Y)
        ++_currentScanLine;
        else
        --_currentScanLine;
    }
    }
}
int
RgbaOutputFile::ToYca::currentScanLine () const
{
    return _currentScanLine;
}
void
RgbaOutputFile::ToYca::padTmpBuf ()
{
    for (int i = 0; i < N2; ++i)
    {
    _tmpBuf[i] = _tmpBuf[N2];
    _tmpBuf[_width + N2 + i] = _tmpBuf[_width + N2 - 2];
    }
}
void
RgbaOutputFile::ToYca::rotateBuffers ()
{
    Rgba *tmp = _buf[0];
    for (int i = 0; i < N - 1; ++i)
    _buf[i] = _buf[i + 1];
    _buf[N - 1] = tmp;
}
void
RgbaOutputFile::ToYca::duplicateLastBuffer ()
{
    rotateBuffers();
    memcpy (_buf[N - 1], _buf[N - 2], _width * sizeof (Rgba));
}
void
RgbaOutputFile::ToYca::duplicateSecondToLastBuffer ()
{
    rotateBuffers();
    memcpy (_buf[N - 1], _buf[N - 3], _width * sizeof (Rgba));
}
void
RgbaOutputFile::ToYca::decimateChromaVertAndWriteScanLine ()
{
    if (_linesConverted & 1)
    memcpy (_tmpBuf, _buf[N2], _width * sizeof (Rgba));
    else
    decimateChromaVert (_width, _buf, _tmpBuf);
    if (_writeY && _writeC)
    roundYCA (_width, _roundY, _roundC, _tmpBuf, _tmpBuf);
    _outputFile.writePixels (1);
}
RgbaOutputFile::RgbaOutputFile (const char name[],
                const Header &header,
                RgbaChannels rgbaChannels,
                                int numThreads):
    _outputFile (0),
    _toYca (0)
{
    Header hd (header);
    insertChannels (hd, rgbaChannels);
    _outputFile = new OutputFile (name, hd, numThreads);
    if (rgbaChannels & (WRITE_Y | WRITE_C))
    _toYca = new ToYca (*_outputFile, rgbaChannels);
}
RgbaOutputFile::RgbaOutputFile (OStream &os,
                const Header &header,
                RgbaChannels rgbaChannels,
                                int numThreads):
    _outputFile (0),
    _toYca (0)
{
    Header hd (header);
    insertChannels (hd, rgbaChannels);
    _outputFile = new OutputFile (os, hd, numThreads);
    if (rgbaChannels & (WRITE_Y | WRITE_C))
    _toYca = new ToYca (*_outputFile, rgbaChannels);
}
RgbaOutputFile::RgbaOutputFile (const char name[],
                const Imath::Box2i &displayWindow,
                const Imath::Box2i &dataWindow,
                RgbaChannels rgbaChannels,
                float pixelAspectRatio,
                const Imath::V2f screenWindowCenter,
                float screenWindowWidth,
                LineOrder lineOrder,
                Compression compression,
                                int numThreads):
    _outputFile (0),
    _toYca (0)
{
    Header hd (displayWindow,
           dataWindow.isEmpty()? displayWindow: dataWindow,
           pixelAspectRatio,
           screenWindowCenter,
           screenWindowWidth,
           lineOrder,
           compression);
    insertChannels (hd, rgbaChannels);
    _outputFile = new OutputFile (name, hd, numThreads);
    if (rgbaChannels & (WRITE_Y | WRITE_C))
    _toYca = new ToYca (*_outputFile, rgbaChannels);
}
RgbaOutputFile::RgbaOutputFile (const char name[],
                int width,
                int height,
                RgbaChannels rgbaChannels,
                float pixelAspectRatio,
                const Imath::V2f screenWindowCenter,
                float screenWindowWidth,
                LineOrder lineOrder,
                Compression compression,
                                int numThreads):
    _outputFile (0),
    _toYca (0)
{
    Header hd (width,
           height,
           pixelAspectRatio,
           screenWindowCenter,
           screenWindowWidth,
           lineOrder,
           compression);
    insertChannels (hd, rgbaChannels);
    _outputFile = new OutputFile (name, hd, numThreads);
    if (rgbaChannels & (WRITE_Y | WRITE_C))
    _toYca = new ToYca (*_outputFile, rgbaChannels);
}
RgbaOutputFile::~RgbaOutputFile ()
{
    delete _toYca;
    delete _outputFile;
}
void
RgbaOutputFile::setFrameBuffer (const Rgba *base,
                size_t xStride,
                size_t yStride)
{
    if (_toYca)
    {
    Lock lock (*_toYca);
    _toYca->setFrameBuffer (base, xStride, yStride);
    }
    else
    {
    size_t xs = xStride * sizeof (Rgba);
    size_t ys = yStride * sizeof (Rgba);
    FrameBuffer fb;
    fb.insert ("R", Slice (HALF, (char *) &base[0].r, xs, ys));
    fb.insert ("G", Slice (HALF, (char *) &base[0].g, xs, ys));
    fb.insert ("B", Slice (HALF, (char *) &base[0].b, xs, ys));
    fb.insert ("A", Slice (HALF, (char *) &base[0].a, xs, ys));
    _outputFile->setFrameBuffer (fb);
    }
}
void
RgbaOutputFile::writePixels (int numScanLines)
{
    if (_toYca)
    {
    Lock lock (*_toYca);
    _toYca->writePixels (numScanLines);
    }
    else
    {
    _outputFile->writePixels (numScanLines);
    }
}
int
RgbaOutputFile::currentScanLine () const
{
    if (_toYca)
    {
    Lock lock (*_toYca);
    return _toYca->currentScanLine();
    }
    else
    {
    return _outputFile->currentScanLine();
    }
}
const Header &
RgbaOutputFile::header () const
{
    return _outputFile->header();
}
const FrameBuffer &
RgbaOutputFile::frameBuffer () const
{
    return _outputFile->frameBuffer();
}
const Imath::Box2i &
RgbaOutputFile::displayWindow () const
{
    return _outputFile->header().displayWindow();
}
const Imath::Box2i &
RgbaOutputFile::dataWindow () const
{
    return _outputFile->header().dataWindow();
}
float
RgbaOutputFile::pixelAspectRatio () const
{
    return _outputFile->header().pixelAspectRatio();
}
const Imath::V2f
RgbaOutputFile::screenWindowCenter () const
{
    return _outputFile->header().screenWindowCenter();
}
float
RgbaOutputFile::screenWindowWidth () const
{
    return _outputFile->header().screenWindowWidth();
}
LineOrder
RgbaOutputFile::lineOrder () const
{
    return _outputFile->header().lineOrder();
}
Compression
RgbaOutputFile::compression () const
{
    return _outputFile->header().compression();
}
RgbaChannels
RgbaOutputFile::channels () const
{
    return rgbaChannels (_outputFile->header().channels());
}
void
RgbaOutputFile::updatePreviewImage (const PreviewRgba newPixels[])
{
    _outputFile->updatePreviewImage (newPixels);
}
void
RgbaOutputFile::setYCRounding (unsigned int roundY, unsigned int roundC)
{
    if (_toYca)
    {
    Lock lock (*_toYca);
    _toYca->setYCRounding (roundY, roundC);
    }
}
void
RgbaOutputFile::breakScanLine  (int y, int offset, int length, char c)
{
    _outputFile->breakScanLine (y, offset, length, c);
}
class RgbaInputFile::FromYca: public Mutex
{
  public:
     FromYca (InputFile &inputFile, RgbaChannels rgbaChannels);
    ~FromYca ();
    void                setFrameBuffer (Rgba *base,
                    size_t xStride,
                    size_t yStride,
                    const string &channelNamePrefix);
    void                readPixels (int scanLine1, int scanLine2);
  private:
    void                readPixels (int scanLine);
    void                rotateBuf1 (int d);
    void                rotateBuf2 (int d);
    void                readYCAScanLine (int y, Rgba buf[]);
    void                padTmpBuf ();
    InputFile &         _inputFile;
    bool                _readC;
    int                 _xMin;
    int                 _yMin;
    int                 _yMax;
    int                 _width;
    int                 _height;
    int                 _currentScanLine;
    LineOrder           _lineOrder;
    V3f                 _yw;
    Rgba *              _bufBase;
    Rgba *              _buf1[N + 2];
    Rgba *              _buf2[3];
    Rgba *              _tmpBuf;
    Rgba *              _fbBase;
    size_t              _fbXStride;
    size_t              _fbYStride;
};
RgbaInputFile::FromYca::FromYca (InputFile &inputFile,
                 RgbaChannels rgbaChannels)
:
    _inputFile (inputFile)
{
    _readC = (rgbaChannels & WRITE_C)? true: false;
    const Box2i dw = _inputFile.header().dataWindow();
    _xMin = dw.min.x;
    _yMin = dw.min.y;
    _yMax = dw.max.y;
    _width  = dw.max.x - dw.min.x + 1;
    _height = dw.max.y - dw.min.y + 1;
    _currentScanLine = dw.min.y - N - 2;
    _lineOrder = _inputFile.header().lineOrder();
    _yw = ywFromHeader (_inputFile.header());
    ptrdiff_t pad = cachePadding (_width * sizeof (Rgba)) / sizeof (Rgba);
    _bufBase = new Rgba[(_width + pad) * (N + 2 + 3)];
    for (int i = 0; i < N + 2; ++i)
    _buf1[i] = _bufBase + (i * (_width + pad));
    for (int i = 0; i < 3; ++i)
    _buf2[i] = _bufBase + ((i + N + 2) * (_width + pad));
    _tmpBuf = new Rgba[_width + N - 1];
    _fbBase = 0;
    _fbXStride = 0;
    _fbYStride = 0;
}
RgbaInputFile::FromYca::~FromYca ()
{
    delete [] _bufBase;
    delete [] _tmpBuf;
}
void
RgbaInputFile::FromYca::setFrameBuffer (Rgba *base,
                    size_t xStride,
                    size_t yStride,
                    const string &channelNamePrefix)
{
    if (_fbBase == 0)
    {
    FrameBuffer fb;
    fb.insert (channelNamePrefix + "Y",
           Slice (HALF,                                 
              (char *) &_tmpBuf[N2 - _xMin].g,  
              sizeof (Rgba),                    
              0,                                        
              1,                                        
              1,                                        
              0.5));                            
    if (_readC)
    {
        fb.insert (channelNamePrefix + "RY",
               Slice (HALF,                             
                  (char *) &_tmpBuf[N2 - _xMin].r,      
                  sizeof (Rgba) * 2,            
                  0,                            
                  2,                            
                  2,                            
                  0.0));                                
        fb.insert (channelNamePrefix + "BY",
               Slice (HALF,                             
                  (char *) &_tmpBuf[N2 - _xMin].b,      
                  sizeof (Rgba) * 2,            
                  0,                            
                  2,                            
                  2,                            
                  0.0));                                
    }
    fb.insert (channelNamePrefix + "A",
           Slice (HALF,                                 
              (char *) &_tmpBuf[N2 - _xMin].a,  
              sizeof (Rgba),                    
              0,                                        
              1,                                        
              1,                                        
              1.0));                            
    _inputFile.setFrameBuffer (fb);
    }
    _fbBase = base;
    _fbXStride = xStride;
    _fbYStride = yStride;
}
void
RgbaInputFile::FromYca::readPixels (int scanLine1, int scanLine2)
{
    int minY = min (scanLine1, scanLine2);
    int maxY = max (scanLine1, scanLine2);
    if (_lineOrder == INCREASING_Y)
    {
    for (int y = minY; y <= maxY; ++y)
        readPixels (y);
    }
    else
    {
    for (int y = maxY; y >= minY; --y)
        readPixels (y);
    }
}
void
RgbaInputFile::FromYca::readPixels (int scanLine)
{
    if (_fbBase == 0)
    {
    THROW (Iex::ArgExc, "No frame buffer was specified as the "
                "pixel data destination for image file "
                "\"" << _inputFile.fileName() << "\".");
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    int dy = scanLine - _currentScanLine;
    if (abs (dy) < N + 2)
    rotateBuf1 (dy);
    if (abs (dy) < 3)
    rotateBuf2 (dy);
    if (dy < 0)
    {
    {
        int n = min (-dy, N + 2);
        int yMin = scanLine - N2 - 1;
        for (int i = n - 1; i >= 0; --i)
        readYCAScanLine (yMin + i, _buf1[i]);
    }
    {
        int n = min (-dy, 3);
        for (int i = 0; i < n; ++i)
        {
        if ((scanLine + i) & 1)
        {
            YCAtoRGBA (_yw, _width, _buf1[N2 + i], _buf2[i]);
        }
        else
        {
            reconstructChromaVert (_width, _buf1 + i, _buf2[i]);
            YCAtoRGBA (_yw, _width, _buf2[i], _buf2[i]);
        }
        }
    }
    }
    else
    {
    {
        int n = min (dy, N + 2);
        int yMax = scanLine + N2 + 1;
        for (int i = n - 1; i >= 0; --i)
        readYCAScanLine (yMax - i, _buf1[N + 1 - i]);
    }
    {
        int n = min (dy, 3);
        for (int i = 2; i > 2 - n; --i)
        {
        if ((scanLine + i) & 1)
        {
            YCAtoRGBA (_yw, _width, _buf1[N2 + i], _buf2[i]);
        }
        else
        {
            reconstructChromaVert (_width, _buf1 + i, _buf2[i]);
            YCAtoRGBA (_yw, _width, _buf2[i], _buf2[i]);
        }
        }
    }
    }
    fixSaturation (_yw, _width, _buf2, _tmpBuf);
    for (int i = 0; i < _width; ++i)
    _fbBase[_fbYStride * scanLine + _fbXStride * (i + _xMin)] = _tmpBuf[i];
    _currentScanLine = scanLine;
}
void
RgbaInputFile::FromYca::rotateBuf1 (int d)
{
    d = modp (d, N + 2);
    Rgba *tmp[N + 2];
    for (int i = 0; i < N + 2; ++i)
    tmp[i] = _buf1[i];
    for (int i = 0; i < N + 2; ++i)
    _buf1[i] = tmp[(i + d) % (N + 2)];
}
void
RgbaInputFile::FromYca::rotateBuf2 (int d)
{
    d = modp (d, 3);
    Rgba *tmp[3];
    for (int i = 0; i < 3; ++i)
    tmp[i] = _buf2[i];
    for (int i = 0; i < 3; ++i)
    _buf2[i] = tmp[(i + d) % 3];
}
void
RgbaInputFile::FromYca::readYCAScanLine (int y, Rgba *buf)
{
    
    
    
    if (y < _yMin)
    y = _yMin;
    else if (y > _yMax)
    y = _yMax - 1;
    
    
    
    _inputFile.readPixels (y);
    
    
    
    
    if (!_readC)
    {
    for (int i = 0; i < _width; ++i)
    {
        _tmpBuf[i + N2].r = 0;
        _tmpBuf[i + N2].b = 0;
    }
    }
    if (y & 1)
    {
    memcpy (buf, _tmpBuf + N2, _width * sizeof (Rgba));
    }
    else
    {
    padTmpBuf();
    reconstructChromaHoriz (_width, _tmpBuf, buf);
    }
}
void
RgbaInputFile::FromYca::padTmpBuf ()
{
    for (int i = 0; i < N2; ++i)
    {
    _tmpBuf[i] = _tmpBuf[N2];
    _tmpBuf[_width + N2 + i] = _tmpBuf[_width + N2 - 2];
    }
}
RgbaInputFile::RgbaInputFile (const char name[], int numThreads):
    _inputFile (new InputFile (name, numThreads)),
    _fromYca (0),
    _channelNamePrefix ("")
{
    RgbaChannels rgbaChannels = channels();
    if (rgbaChannels & (WRITE_Y | WRITE_C))
    _fromYca = new FromYca (*_inputFile, rgbaChannels);
}
RgbaInputFile::RgbaInputFile (IStream &is, int numThreads):
    _inputFile (new InputFile (is, numThreads)),
    _fromYca (0),
    _channelNamePrefix ("")
{
    RgbaChannels rgbaChannels = channels();
    if (rgbaChannels & (WRITE_Y | WRITE_C))
    _fromYca = new FromYca (*_inputFile, rgbaChannels);
}
RgbaInputFile::RgbaInputFile (const char name[],
                  const string &layerName,
                  int numThreads)
:
    _inputFile (new InputFile (name, numThreads)),
    _fromYca (0),
    _channelNamePrefix (prefixFromLayerName (layerName, _inputFile->header()))
{
    RgbaChannels rgbaChannels = channels();
    if (rgbaChannels & (WRITE_Y | WRITE_C))
    _fromYca = new FromYca (*_inputFile, rgbaChannels);
}
RgbaInputFile::RgbaInputFile (IStream &is,
                  const string &layerName,
                  int numThreads)
:
    _inputFile (new InputFile (is, numThreads)),
    _fromYca (0),
    _channelNamePrefix (prefixFromLayerName (layerName, _inputFile->header()))
{
    RgbaChannels rgbaChannels = channels();
    if (rgbaChannels & (WRITE_Y | WRITE_C))
    _fromYca = new FromYca (*_inputFile, rgbaChannels);
}
RgbaInputFile::~RgbaInputFile ()
{
    delete _inputFile;
    delete _fromYca;
}
void
RgbaInputFile::setFrameBuffer (Rgba *base, size_t xStride, size_t yStride)
{
    if (_fromYca)
    {
    Lock lock (*_fromYca);
    _fromYca->setFrameBuffer (base, xStride, yStride, _channelNamePrefix);
    }
    else
    {
    size_t xs = xStride * sizeof (Rgba);
    size_t ys = yStride * sizeof (Rgba);
    FrameBuffer fb;
    fb.insert (_channelNamePrefix + "R",
           Slice (HALF,
              (char *) &base[0].r,
              xs, ys,
              1, 1,             
              0.0));    
    fb.insert (_channelNamePrefix + "G",
           Slice (HALF,
              (char *) &base[0].g,
              xs, ys,
              1, 1,             
              0.0));    
    fb.insert (_channelNamePrefix + "B",
           Slice (HALF,
              (char *) &base[0].b,
              xs, ys,
              1, 1,             
              0.0));    
    fb.insert (_channelNamePrefix + "A",
           Slice (HALF,
              (char *) &base[0].a,
              xs, ys,
              1, 1,             
              1.0));    
    _inputFile->setFrameBuffer (fb);
    }
}
void
RgbaInputFile::setLayerName (const string &layerName)
{
    delete _fromYca;
    _fromYca = 0;
    _channelNamePrefix = prefixFromLayerName (layerName, _inputFile->header());
    RgbaChannels rgbaChannels = channels();
    if (rgbaChannels & (WRITE_Y | WRITE_C))
    _fromYca = new FromYca (*_inputFile, rgbaChannels);
    FrameBuffer fb;
    _inputFile->setFrameBuffer (fb);
}
void
RgbaInputFile::readPixels (int scanLine1, int scanLine2)
{
    if (_fromYca)
    {
    Lock lock (*_fromYca);
    _fromYca->readPixels (scanLine1, scanLine2);
    }
    else
    {
    _inputFile->readPixels (scanLine1, scanLine2);
    }
}
void
RgbaInputFile::readPixels (int scanLine)
{
    readPixels (scanLine, scanLine);
}
bool
RgbaInputFile::isComplete () const
{
    return _inputFile->isComplete();
}
const Header &
RgbaInputFile::header () const
{
    return _inputFile->header();
}
const char *
RgbaInputFile::fileName () const
{
    return _inputFile->fileName();
}
const FrameBuffer &
RgbaInputFile::frameBuffer () const
{
    return _inputFile->frameBuffer();
}
const Imath::Box2i &
RgbaInputFile::displayWindow () const
{
    return _inputFile->header().displayWindow();
}
const Imath::Box2i &
RgbaInputFile::dataWindow () const
{
    return _inputFile->header().dataWindow();
}
float
RgbaInputFile::pixelAspectRatio () const
{
    return _inputFile->header().pixelAspectRatio();
}
const Imath::V2f
RgbaInputFile::screenWindowCenter () const
{
    return _inputFile->header().screenWindowCenter();
}
float
RgbaInputFile::screenWindowWidth () const
{
    return _inputFile->header().screenWindowWidth();
}
LineOrder
RgbaInputFile::lineOrder () const
{
    return _inputFile->header().lineOrder();
}
Compression
RgbaInputFile::compression () const
{
    return _inputFile->header().compression();
}
RgbaChannels
RgbaInputFile::channels () const
{
    return rgbaChannels (_inputFile->header().channels(), _channelNamePrefix);
}
int
RgbaInputFile::version () const
{
    return _inputFile->version();
}
}