📄 imfrgbafile.cpp
字号:
///////////////////////////////////////////////////////////////////////////
//
// 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 <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)
{
int i = 0;
if (ch.findChannel ("R"))
i |= WRITE_R;
if (ch.findChannel ("G"))
i |= WRITE_G;
if (ch.findChannel ("B"))
i |= WRITE_B;
if (ch.findChannel ("A"))
i |= WRITE_A;
if (ch.findChannel ("Y"))
i |= WRITE_Y;
if (ch.findChannel ("RY") || ch.findChannel ("BY"))
i |= WRITE_C;
return RgbaChannels (i);
}
V3f
ywFromHeader (const Header &header)
{
Chromaticities cr;
if (hasChromaticities (header))
cr = chromaticities (header);
return computeYw (cr);
}
} // 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 * _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());
for (int i = 0; i < N; ++i)
_buf[i] = new Rgba[_width];
_tmpBuf = new Rgba[_width + N - 1];
_fbBase = 0;
_fbXStride = 0;
_fbYStride = 0;
_roundY = 7;
_roundC = 5;
}
RgbaOutputFile::ToYca::~ToYca ()
{
for (int i = 0; i < N; ++i)
delete [] _buf[i];
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)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -