/////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2004, Industrial Light & Magic, a division of Lucas // Digital Ltd. LLC // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Industrial Light & Magic nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- // // class RgbaOutputFile // class RgbaInputFile // //----------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #include #include #include 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) { // // Some of the buffers that are allocated by classes ToYca and // FromYca, below, may need to be padded to avoid cache thrashing. // If the difference between the buffer size and the nearest power // of two is less than CACHE_LINE_SIZE, then we add an appropriate // amount of padding. // // CACHE_LINE_SIZE must be a power of two, and it must be at // least as big as the true size of a cache line on the machine // we are running on. (It is ok if CACHE_LINE_SIZE is larger // than a real cache line.) // 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; } } // namespace 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, // type (char *) &_tmpBuf[-_xMin].g, // base sizeof (Rgba), // xStride 0, // yStride 1, // xSampling 1)); // ySampling } if (_writeC) { fb.insert ("RY", Slice (HALF, // type (char *) &_tmpBuf[-_xMin].r, // base sizeof (Rgba) * 2, // xStride 0, // yStride 2, // xSampling 2)); // ySampling fb.insert ("BY", Slice (HALF, // type (char *) &_tmpBuf[-_xMin].b, // base sizeof (Rgba) * 2, // xStride 0, // yStride 2, // xSampling 2)); // ySampling } if (_writeA) { fb.insert ("A", Slice (HALF, // type (char *) &_tmpBuf[-_xMin].a, // base sizeof (Rgba), // xStride 0, // yStride 1, // xSampling 1)); // ySampling } _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) { // // We are writing only luminance; filtering // and subsampling are not necessary. // for (int i = 0; i < numScanLines; ++i) { // // Copy the next scan line from the caller's // frame buffer into _tmpBuf. // for (int j = 0; j < _width; ++j) { _tmpBuf[j] = _fbBase[_fbYStride * _currentScanLine + _fbXStride * (j + _xMin)]; } // // Convert the scan line from RGB to luminance/chroma, // and store the result in the output file. // RGBAtoYCA (_yw, _width, _writeA, _tmpBuf, _tmpBuf); _outputFile.writePixels (1); ++_linesConverted; if (_lineOrder == INCREASING_Y) ++_currentScanLine; else --_currentScanLine; } } else { // // We are writing chroma; the pixels must be filtered and subsampled. // for (int i = 0; i < numScanLines; ++i) { // // Copy the next scan line from the caller's // frame buffer into _tmpBuf. // for (int j = 0; j < _width; ++j) { _tmpBuf[j + N2] = _fbBase[_fbYStride * _currentScanLine + _fbXStride * (j + _xMin)]; } // // Convert the scan line from RGB to luminance/chroma. // RGBAtoYCA (_yw, _width, _writeA, _tmpBuf + N2, _tmpBuf + N2); // // Append N2 copies of the first and last pixel to the // beginning and end of the scan line. // padTmpBuf (); // // Filter and subsample the scan line's chroma channels // horizontally; store the result in _buf. // rotateBuffers(); decimateChromaHoriz (_width, _tmpBuf, _buf[N - 1]); // // If this is the first scan line in the image, // store N2 more copies of the scan line in _buf. // if (_linesConverted == 0) { for (int j = 0; j < N2; ++j) duplicateLastBuffer(); } ++_linesConverted; // // If we have have converted at least N2 scan lines from // RGBA to luminance/chroma, then we can start to filter // and subsample vertically, and store pixels in the // output file. // if (_linesConverted > N2) decimateChromaVertAndWriteScanLine(); // // If we have already converted the last scan line in // the image to luminance/chroma, filter, subsample and // store the remaining scan lines in _buf. // 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, // type (char *) &_tmpBuf[N2 - _xMin].g, // base sizeof (Rgba), // xStride 0, // yStride 1, // xSampling 1, // ySampling 0.5)); // fillValue if (_readC) { fb.insert (channelNamePrefix + "RY", Slice (HALF, // type (char *) &_tmpBuf[N2 - _xMin].r, // base sizeof (Rgba) * 2, // xStride 0, // yStride 2, // xSampling 2, // ySampling 0.0)); // fillValue fb.insert (channelNamePrefix + "BY", Slice (HALF, // type (char *) &_tmpBuf[N2 - _xMin].b, // base sizeof (Rgba) * 2, // xStride 0, // yStride 2, // xSampling 2, // ySampling 0.0)); // fillValue } fb.insert (channelNamePrefix + "A", Slice (HALF, // type (char *) &_tmpBuf[N2 - _xMin].a, // base sizeof (Rgba), // xStride 0, // yStride 1, // xSampling 1, // ySampling 1.0)); // fillValue _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() << "\"."); } // // In order to convert one scan line to RGB format, we need that // scan line plus N2+1 extra scan lines above and N2+1 scan lines // below in luminance/chroma format. // // We allow random access to scan lines, but we buffer partially // processed luminance/chroma data in order to make reading pixels // in increasing y or decreasing y order reasonably efficient: // // _currentScanLine holds the y coordinate of the scan line // that was most recently read. // // _buf1 contains scan lines _currentScanLine-N2-1 // through _currentScanLine+N2+1 in // luminance/chroma format. Odd-numbered // lines contain no chroma data. Even-numbered // lines have valid chroma data for all pixels. // // _buf2 contains scan lines _currentScanLine-1 // through _currentScanLine+1, in RGB format. // Super-saturated pixels (see ImfRgbaYca.h) // have not yet been eliminated. // // If the scan line we are trying to read now is close enough to // _currentScanLine, we don't have to recompute the contents of _buf1 // and _buf2 from scratch. We can rotate _buf1 and _buf2, and fill // in the missing data. // 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) { // // Clamp y. // if (y < _yMin) y = _yMin; else if (y > _yMax) y = _yMax - 1; // // Read scan line y into _tmpBuf. // _inputFile.readPixels (y); // // Reconstruct missing chroma samples and copy // the scan line into buf. // 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, // xSampling, ySampling 0.0)); // fillValue fb.insert (_channelNamePrefix + "G", Slice (HALF, (char *) &base[0].g, xs, ys, 1, 1, // xSampling, ySampling 0.0)); // fillValue fb.insert (_channelNamePrefix + "B", Slice (HALF, (char *) &base[0].b, xs, ys, 1, 1, // xSampling, ySampling 0.0)); // fillValue fb.insert (_channelNamePrefix + "A", Slice (HALF, (char *) &base[0].a, xs, ys, 1, 1, // xSampling, ySampling 1.0)); // fillValue _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(); } } // namespace Imf