|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// 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 TiledRgbaOutputFile
|
|
|
|
// class TiledRgbaInputFile
|
|
|
|
//
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#include <ImfTiledRgbaFile.h>
|
|
|
|
#include <ImfRgbaFile.h>
|
|
|
|
#include <ImfTiledOutputFile.h>
|
|
|
|
#include <ImfTiledInputFile.h>
|
|
|
|
#include <ImfChannelList.h>
|
|
|
|
#include <ImfTileDescriptionAttribute.h>
|
|
|
|
#include <ImfStandardAttributes.h>
|
|
|
|
#include <ImfRgbaYca.h>
|
|
|
|
#include <ImfArray.h>
|
|
|
|
#include "IlmThreadMutex.h"
|
|
|
|
#include "Iex.h"
|
|
|
|
|
|
|
|
|
|
|
|
namespace Imf {
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace Imath;
|
|
|
|
using namespace RgbaYca;
|
|
|
|
using namespace IlmThread;
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
void
|
|
|
|
insertChannels (Header &header,
|
|
|
|
RgbaChannels rgbaChannels,
|
|
|
|
const char fileName[])
|
|
|
|
{
|
|
|
|
ChannelList ch;
|
|
|
|
|
|
|
|
if (rgbaChannels & (WRITE_Y | WRITE_C))
|
|
|
|
{
|
|
|
|
if (rgbaChannels & WRITE_Y)
|
|
|
|
{
|
|
|
|
ch.insert ("Y", Channel (HALF, 1, 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rgbaChannels & WRITE_C)
|
|
|
|
{
|
|
|
|
THROW (Iex::ArgExc, "Cannot open file \"" << fileName << "\" "
|
|
|
|
"for writing. Tiled image files do not "
|
|
|
|
"support subsampled chroma channels.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
|
|
class TiledRgbaOutputFile::ToYa: public Mutex
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
ToYa (TiledOutputFile &outputFile, RgbaChannels rgbaChannels);
|
|
|
|
|
|
|
|
void setFrameBuffer (const Rgba *base,
|
|
|
|
size_t xStride,
|
|
|
|
size_t yStride);
|
|
|
|
|
|
|
|
void writeTile (int dx, int dy, int lx, int ly);
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
TiledOutputFile & _outputFile;
|
|
|
|
bool _writeA;
|
|
|
|
unsigned int _tileXSize;
|
|
|
|
unsigned int _tileYSize;
|
|
|
|
V3f _yw;
|
|
|
|
Array2D <Rgba> _buf;
|
|
|
|
const Rgba * _fbBase;
|
|
|
|
size_t _fbXStride;
|
|
|
|
size_t _fbYStride;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
TiledRgbaOutputFile::ToYa::ToYa (TiledOutputFile &outputFile,
|
|
|
|
RgbaChannels rgbaChannels)
|
|
|
|
:
|
|
|
|
_outputFile (outputFile)
|
|
|
|
{
|
|
|
|
_writeA = (rgbaChannels & WRITE_A)? true: false;
|
|
|
|
|
|
|
|
const TileDescription &td = outputFile.header().tileDescription();
|
|
|
|
|
|
|
|
_tileXSize = td.xSize;
|
|
|
|
_tileYSize = td.ySize;
|
|
|
|
_yw = ywFromHeader (_outputFile.header());
|
|
|
|
_buf.resizeErase (_tileYSize, _tileXSize);
|
|
|
|
_fbBase = 0;
|
|
|
|
_fbXStride = 0;
|
|
|
|
_fbYStride = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
TiledRgbaOutputFile::ToYa::setFrameBuffer (const Rgba *base,
|
|
|
|
size_t xStride,
|
|
|
|
size_t yStride)
|
|
|
|
{
|
|
|
|
_fbBase = base;
|
|
|
|
_fbXStride = xStride;
|
|
|
|
_fbYStride = yStride;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
TiledRgbaOutputFile::ToYa::writeTile (int dx, int dy, int lx, int ly)
|
|
|
|
{
|
|
|
|
if (_fbBase == 0)
|
|
|
|
{
|
|
|
|
THROW (Iex::ArgExc, "No frame buffer was specified as the "
|
|
|
|
"pixel data source for image file "
|
|
|
|
"\"" << _outputFile.fileName() << "\".");
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Copy the tile's RGBA pixels into _buf and convert
|
|
|
|
// them to luminance/alpha format
|
|
|
|
//
|
|
|
|
|
|
|
|
Box2i dw = _outputFile.dataWindowForTile (dx, dy, lx, ly);
|
|
|
|
int width = dw.max.x - dw.min.x + 1;
|
|
|
|
|
|
|
|
for (int y = dw.min.y, y1 = 0; y <= dw.max.y; ++y, ++y1)
|
|
|
|
{
|
|
|
|
for (int x = dw.min.x, x1 = 0; x <= dw.max.x; ++x, ++x1)
|
|
|
|
_buf[y1][x1] = _fbBase[x * _fbXStride + y * _fbYStride];
|
|
|
|
|
|
|
|
RGBAtoYCA (_yw, width, _writeA, _buf[y1], _buf[y1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Store the contents of _buf in the output file
|
|
|
|
//
|
|
|
|
|
|
|
|
FrameBuffer fb;
|
|
|
|
|
|
|
|
fb.insert ("Y", Slice (HALF, // type
|
|
|
|
(char *) &_buf[-dw.min.y][-dw.min.x].g, // base
|
|
|
|
sizeof (Rgba), // xStride
|
|
|
|
sizeof (Rgba) * _tileXSize)); // yStride
|
|
|
|
|
|
|
|
fb.insert ("A", Slice (HALF, // type
|
|
|
|
(char *) &_buf[-dw.min.y][-dw.min.x].a, // base
|
|
|
|
sizeof (Rgba), // xStride
|
|
|
|
sizeof (Rgba) * _tileXSize)); // yStride
|
|
|
|
|
|
|
|
_outputFile.setFrameBuffer (fb);
|
|
|
|
_outputFile.writeTile (dx, dy, lx, ly);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TiledRgbaOutputFile::TiledRgbaOutputFile
|
|
|
|
(const char name[],
|
|
|
|
const Header &header,
|
|
|
|
RgbaChannels rgbaChannels,
|
|
|
|
int tileXSize,
|
|
|
|
int tileYSize,
|
|
|
|
LevelMode mode,
|
|
|
|
LevelRoundingMode rmode,
|
|
|
|
int numThreads)
|
|
|
|
:
|
|
|
|
_outputFile (0),
|
|
|
|
_toYa (0)
|
|
|
|
{
|
|
|
|
Header hd (header);
|
|
|
|
insertChannels (hd, rgbaChannels, name);
|
|
|
|
hd.setTileDescription (TileDescription (tileXSize, tileYSize, mode, rmode));
|
|
|
|
_outputFile = new TiledOutputFile (name, hd, numThreads);
|
|
|
|
|
|
|
|
if (rgbaChannels & WRITE_Y)
|
|
|
|
_toYa = new ToYa (*_outputFile, rgbaChannels);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TiledRgbaOutputFile::TiledRgbaOutputFile
|
|
|
|
(OStream &os,
|
|
|
|
const Header &header,
|
|
|
|
RgbaChannels rgbaChannels,
|
|
|
|
int tileXSize,
|
|
|
|
int tileYSize,
|
|
|
|
LevelMode mode,
|
|
|
|
LevelRoundingMode rmode,
|
|
|
|
int numThreads)
|
|
|
|
:
|
|
|
|
_outputFile (0),
|
|
|
|
_toYa (0)
|
|
|
|
{
|
|
|
|
Header hd (header);
|
|
|
|
insertChannels (hd, rgbaChannels, os.fileName());
|
|
|
|
hd.setTileDescription (TileDescription (tileXSize, tileYSize, mode, rmode));
|
|
|
|
_outputFile = new TiledOutputFile (os, hd, numThreads);
|
|
|
|
|
|
|
|
if (rgbaChannels & WRITE_Y)
|
|
|
|
_toYa = new ToYa (*_outputFile, rgbaChannels);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TiledRgbaOutputFile::TiledRgbaOutputFile
|
|
|
|
(const char name[],
|
|
|
|
int tileXSize,
|
|
|
|
int tileYSize,
|
|
|
|
LevelMode mode,
|
|
|
|
LevelRoundingMode rmode,
|
|
|
|
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),
|
|
|
|
_toYa (0)
|
|
|
|
{
|
|
|
|
Header hd (displayWindow,
|
|
|
|
dataWindow.isEmpty()? displayWindow: dataWindow,
|
|
|
|
pixelAspectRatio,
|
|
|
|
screenWindowCenter,
|
|
|
|
screenWindowWidth,
|
|
|
|
lineOrder,
|
|
|
|
compression);
|
|
|
|
|
|
|
|
insertChannels (hd, rgbaChannels, name);
|
|
|
|
hd.setTileDescription (TileDescription (tileXSize, tileYSize, mode, rmode));
|
|
|
|
_outputFile = new TiledOutputFile (name, hd, numThreads);
|
|
|
|
|
|
|
|
if (rgbaChannels & WRITE_Y)
|
|
|
|
_toYa = new ToYa (*_outputFile, rgbaChannels);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TiledRgbaOutputFile::TiledRgbaOutputFile
|
|
|
|
(const char name[],
|
|
|
|
int width,
|
|
|
|
int height,
|
|
|
|
int tileXSize,
|
|
|
|
int tileYSize,
|
|
|
|
LevelMode mode,
|
|
|
|
LevelRoundingMode rmode,
|
|
|
|
RgbaChannels rgbaChannels,
|
|
|
|
float pixelAspectRatio,
|
|
|
|
const Imath::V2f screenWindowCenter,
|
|
|
|
float screenWindowWidth,
|
|
|
|
LineOrder lineOrder,
|
|
|
|
Compression compression,
|
|
|
|
int numThreads)
|
|
|
|
:
|
|
|
|
_outputFile (0),
|
|
|
|
_toYa (0)
|
|
|
|
{
|
|
|
|
Header hd (width,
|
|
|
|
height,
|
|
|
|
pixelAspectRatio,
|
|
|
|
screenWindowCenter,
|
|
|
|
screenWindowWidth,
|
|
|
|
lineOrder,
|
|
|
|
compression);
|
|
|
|
|
|
|
|
insertChannels (hd, rgbaChannels, name);
|
|
|
|
hd.setTileDescription (TileDescription (tileXSize, tileYSize, mode, rmode));
|
|
|
|
_outputFile = new TiledOutputFile (name, hd, numThreads);
|
|
|
|
|
|
|
|
if (rgbaChannels & WRITE_Y)
|
|
|
|
_toYa = new ToYa (*_outputFile, rgbaChannels);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TiledRgbaOutputFile::~TiledRgbaOutputFile ()
|
|
|
|
{
|
|
|
|
delete _outputFile;
|
|
|
|
delete _toYa;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
TiledRgbaOutputFile::setFrameBuffer (const Rgba *base,
|
|
|
|
size_t xStride,
|
|
|
|
size_t yStride)
|
|
|
|
{
|
|
|
|
if (_toYa)
|
|
|
|
{
|
|
|
|
Lock lock (*_toYa);
|
|
|
|
_toYa->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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const Header &
|
|
|
|
TiledRgbaOutputFile::header () const
|
|
|
|
{
|
|
|
|
return _outputFile->header();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const FrameBuffer &
|
|
|
|
TiledRgbaOutputFile::frameBuffer () const
|
|
|
|
{
|
|
|
|
return _outputFile->frameBuffer();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const Imath::Box2i &
|
|
|
|
TiledRgbaOutputFile::displayWindow () const
|
|
|
|
{
|
|
|
|
return _outputFile->header().displayWindow();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const Imath::Box2i &
|
|
|
|
TiledRgbaOutputFile::dataWindow () const
|
|
|
|
{
|
|
|
|
return _outputFile->header().dataWindow();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
TiledRgbaOutputFile::pixelAspectRatio () const
|
|
|
|
{
|
|
|
|
return _outputFile->header().pixelAspectRatio();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const Imath::V2f
|
|
|
|
TiledRgbaOutputFile::screenWindowCenter () const
|
|
|
|
{
|
|
|
|
return _outputFile->header().screenWindowCenter();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
TiledRgbaOutputFile::screenWindowWidth () const
|
|
|
|
{
|
|
|
|
return _outputFile->header().screenWindowWidth();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LineOrder
|
|
|
|
TiledRgbaOutputFile::lineOrder () const
|
|
|
|
{
|
|
|
|
return _outputFile->header().lineOrder();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Compression
|
|
|
|
TiledRgbaOutputFile::compression () const
|
|
|
|
{
|
|
|
|
return _outputFile->header().compression();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
RgbaChannels
|
|
|
|
TiledRgbaOutputFile::channels () const
|
|
|
|
{
|
|
|
|
return rgbaChannels (_outputFile->header().channels());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unsigned int
|
|
|
|
TiledRgbaOutputFile::tileXSize () const
|
|
|
|
{
|
|
|
|
return _outputFile->tileXSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unsigned int
|
|
|
|
TiledRgbaOutputFile::tileYSize () const
|
|
|
|
{
|
|
|
|
return _outputFile->tileYSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LevelMode
|
|
|
|
TiledRgbaOutputFile::levelMode () const
|
|
|
|
{
|
|
|
|
return _outputFile->levelMode();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LevelRoundingMode
|
|
|
|
TiledRgbaOutputFile::levelRoundingMode () const
|
|
|
|
{
|
|
|
|
return _outputFile->levelRoundingMode();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
TiledRgbaOutputFile::numLevels () const
|
|
|
|
{
|
|
|
|
return _outputFile->numLevels();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
TiledRgbaOutputFile::numXLevels () const
|
|
|
|
{
|
|
|
|
return _outputFile->numXLevels();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
TiledRgbaOutputFile::numYLevels () const
|
|
|
|
{
|
|
|
|
return _outputFile->numYLevels();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
TiledRgbaOutputFile::isValidLevel (int lx, int ly) const
|
|
|
|
{
|
|
|
|
return _outputFile->isValidLevel (lx, ly);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
TiledRgbaOutputFile::levelWidth (int lx) const
|
|
|
|
{
|
|
|
|
return _outputFile->levelWidth (lx);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
TiledRgbaOutputFile::levelHeight (int ly) const
|
|
|
|
{
|
|
|
|
return _outputFile->levelHeight (ly);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
TiledRgbaOutputFile::numXTiles (int lx) const
|
|
|
|
{
|
|
|
|
return _outputFile->numXTiles (lx);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
TiledRgbaOutputFile::numYTiles (int ly) const
|
|
|
|
{
|
|
|
|
return _outputFile->numYTiles (ly);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Imath::Box2i
|
|
|
|
TiledRgbaOutputFile::dataWindowForLevel (int l) const
|
|
|
|
{
|
|
|
|
return _outputFile->dataWindowForLevel (l);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Imath::Box2i
|
|
|
|
TiledRgbaOutputFile::dataWindowForLevel (int lx, int ly) const
|
|
|
|
{
|
|
|
|
return _outputFile->dataWindowForLevel (lx, ly);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Imath::Box2i
|
|
|
|
TiledRgbaOutputFile::dataWindowForTile (int dx, int dy, int l) const
|
|
|
|
{
|
|
|
|
return _outputFile->dataWindowForTile (dx, dy, l);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Imath::Box2i
|
|
|
|
TiledRgbaOutputFile::dataWindowForTile (int dx, int dy, int lx, int ly) const
|
|
|
|
{
|
|
|
|
return _outputFile->dataWindowForTile (dx, dy, lx, ly);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
TiledRgbaOutputFile::writeTile (int dx, int dy, int l)
|
|
|
|
{
|
|
|
|
if (_toYa)
|
|
|
|
{
|
|
|
|
Lock lock (*_toYa);
|
|
|
|
_toYa->writeTile (dx, dy, l, l);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_outputFile->writeTile (dx, dy, l);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
TiledRgbaOutputFile::writeTile (int dx, int dy, int lx, int ly)
|
|
|
|
{
|
|
|
|
if (_toYa)
|
|
|
|
{
|
|
|
|
Lock lock (*_toYa);
|
|
|
|
_toYa->writeTile (dx, dy, lx, ly);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_outputFile->writeTile (dx, dy, lx, ly);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
TiledRgbaOutputFile::writeTiles
|
|
|
|
(int dxMin, int dxMax, int dyMin, int dyMax, int lx, int ly)
|
|
|
|
{
|
|
|
|
if (_toYa)
|
|
|
|
{
|
|
|
|
Lock lock (*_toYa);
|
|
|
|
|
|
|
|
for (int dy = dyMin; dy <= dyMax; dy++)
|
|
|
|
for (int dx = dxMin; dx <= dxMax; dx++)
|
|
|
|
_toYa->writeTile (dx, dy, lx, ly);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_outputFile->writeTiles (dxMin, dxMax, dyMin, dyMax, lx, ly);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TiledRgbaOutputFile::writeTiles
|
|
|
|
(int dxMin, int dxMax, int dyMin, int dyMax, int l)
|
|
|
|
{
|
|
|
|
writeTiles (dxMin, dxMax, dyMin, dyMax, l, l);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class TiledRgbaInputFile::FromYa: public Mutex
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
FromYa (TiledInputFile &inputFile);
|
|
|
|
|
|
|
|
void setFrameBuffer (Rgba *base,
|
|
|
|
size_t xStride,
|
|
|
|
size_t yStride,
|
|
|
|
const string &channelNamePrefix);
|
|
|
|
|
|
|
|
void readTile (int dx, int dy, int lx, int ly);
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
TiledInputFile & _inputFile;
|
|
|
|
unsigned int _tileXSize;
|
|
|
|
unsigned int _tileYSize;
|
|
|
|
V3f _yw;
|
|
|
|
Array2D <Rgba> _buf;
|
|
|
|
Rgba * _fbBase;
|
|
|
|
size_t _fbXStride;
|
|
|
|
size_t _fbYStride;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
TiledRgbaInputFile::FromYa::FromYa (TiledInputFile &inputFile)
|
|
|
|
:
|
|
|
|
_inputFile (inputFile)
|
|
|
|
{
|
|
|
|
const TileDescription &td = inputFile.header().tileDescription();
|
|
|
|
|
|
|
|
_tileXSize = td.xSize;
|
|
|
|
_tileYSize = td.ySize;
|
|
|
|
_yw = ywFromHeader (_inputFile.header());
|
|
|
|
_buf.resizeErase (_tileYSize, _tileXSize);
|
|
|
|
_fbBase = 0;
|
|
|
|
_fbXStride = 0;
|
|
|
|
_fbYStride = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
TiledRgbaInputFile::FromYa::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 *) &_buf[0][0].g, // base
|
|
|
|
sizeof (Rgba), // xStride
|
|
|
|
sizeof (Rgba) * _tileXSize, // yStride
|
|
|
|
1, 1, // sampling
|
|
|
|
0.0, // fillValue
|
|
|
|
true, true)); // tileCoordinates
|
|
|
|
|
|
|
|
fb.insert (channelNamePrefix + "A",
|
|
|
|
Slice (HALF, // type
|
|
|
|
(char *) &_buf[0][0].a, // base
|
|
|
|
sizeof (Rgba), // xStride
|
|
|
|
sizeof (Rgba) * _tileXSize, // yStride
|
|
|
|
1, 1, // sampling
|
|
|
|
1.0, // fillValue
|
|
|
|
true, true)); // tileCoordinates
|
|
|
|
|
|
|
|
_inputFile.setFrameBuffer (fb);
|
|
|
|
}
|
|
|
|
|
|
|
|
_fbBase = base;
|
|
|
|
_fbXStride = xStride;
|
|
|
|
_fbYStride = yStride;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
TiledRgbaInputFile::FromYa::readTile (int dx, int dy, int lx, int ly)
|
|
|
|
{
|
|
|
|
if (_fbBase == 0)
|
|
|
|
{
|
|
|
|
THROW (Iex::ArgExc, "No frame buffer was specified as the "
|
|
|
|
"pixel data destination for image file "
|
|
|
|
"\"" << _inputFile.fileName() << "\".");
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Read the tile requested by the caller into _buf.
|
|
|
|
//
|
|
|
|
|
|
|
|
_inputFile.readTile (dx, dy, lx, ly);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Convert the luminance/alpha pixels to RGBA
|
|
|
|
// and copy them into the caller's frame buffer.
|
|
|
|
//
|
|
|
|
|
|
|
|
Box2i dw = _inputFile.dataWindowForTile (dx, dy, lx, ly);
|
|
|
|
int width = dw.max.x - dw.min.x + 1;
|
|
|
|
|
|
|
|
for (int y = dw.min.y, y1 = 0; y <= dw.max.y; ++y, ++y1)
|
|
|
|
{
|
|
|
|
for (int x1 = 0; x1 < width; ++x1)
|
|
|
|
{
|
|
|
|
_buf[y1][x1].r = 0;
|
|
|
|
_buf[y1][x1].b = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
YCAtoRGBA (_yw, width, _buf[y1], _buf[y1]);
|
|
|
|
|
|
|
|
for (int x = dw.min.x, x1 = 0; x <= dw.max.x; ++x, ++x1)
|
|
|
|
{
|
|
|
|
_fbBase[x * _fbXStride + y * _fbYStride] = _buf[y1][x1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TiledRgbaInputFile::TiledRgbaInputFile (const char name[], int numThreads):
|
|
|
|
_inputFile (new TiledInputFile (name, numThreads)),
|
|
|
|
_fromYa (0),
|
|
|
|
_channelNamePrefix ("")
|
|
|
|
{
|
|
|
|
if (channels() & WRITE_Y)
|
|
|
|
_fromYa = new FromYa (*_inputFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TiledRgbaInputFile::TiledRgbaInputFile (IStream &is, int numThreads):
|
|
|
|
_inputFile (new TiledInputFile (is, numThreads)),
|
|
|
|
_fromYa (0),
|
|
|
|
_channelNamePrefix ("")
|
|
|
|
{
|
|
|
|
if (channels() & WRITE_Y)
|
|
|
|
_fromYa = new FromYa (*_inputFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TiledRgbaInputFile::TiledRgbaInputFile (const char name[],
|
|
|
|
const string &layerName,
|
|
|
|
int numThreads)
|
|
|
|
:
|
|
|
|
_inputFile (new TiledInputFile (name, numThreads)),
|
|
|
|
_fromYa (0),
|
|
|
|
_channelNamePrefix (prefixFromLayerName (layerName, _inputFile->header()))
|
|
|
|
{
|
|
|
|
if (channels() & WRITE_Y)
|
|
|
|
_fromYa = new FromYa (*_inputFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TiledRgbaInputFile::TiledRgbaInputFile (IStream &is,
|
|
|
|
const string &layerName,
|
|
|
|
int numThreads)
|
|
|
|
:
|
|
|
|
_inputFile (new TiledInputFile (is, numThreads)),
|
|
|
|
_fromYa (0),
|
|
|
|
_channelNamePrefix (prefixFromLayerName (layerName, _inputFile->header()))
|
|
|
|
{
|
|
|
|
if (channels() & WRITE_Y)
|
|
|
|
_fromYa = new FromYa (*_inputFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TiledRgbaInputFile::~TiledRgbaInputFile ()
|
|
|
|
{
|
|
|
|
delete _inputFile;
|
|
|
|
delete _fromYa;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
TiledRgbaInputFile::setFrameBuffer (Rgba *base, size_t xStride, size_t yStride)
|
|
|
|
{
|
|
|
|
if (_fromYa)
|
|
|
|
{
|
|
|
|
Lock lock (*_fromYa);
|
|
|
|
_fromYa->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
|
|
|
|
TiledRgbaInputFile::setLayerName (const std::string &layerName)
|
|
|
|
{
|
|
|
|
delete _fromYa;
|
|
|
|
_fromYa = 0;
|
|
|
|
|
|
|
|
_channelNamePrefix = prefixFromLayerName (layerName, _inputFile->header());
|
|
|
|
|
|
|
|
if (channels() & WRITE_Y)
|
|
|
|
_fromYa = new FromYa (*_inputFile);
|
|
|
|
|
|
|
|
FrameBuffer fb;
|
|
|
|
_inputFile->setFrameBuffer (fb);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const Header &
|
|
|
|
TiledRgbaInputFile::header () const
|
|
|
|
{
|
|
|
|
return _inputFile->header();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
TiledRgbaInputFile::fileName () const
|
|
|
|
{
|
|
|
|
return _inputFile->fileName();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const FrameBuffer &
|
|
|
|
TiledRgbaInputFile::frameBuffer () const
|
|
|
|
{
|
|
|
|
return _inputFile->frameBuffer();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const Imath::Box2i &
|
|
|
|
TiledRgbaInputFile::displayWindow () const
|
|
|
|
{
|
|
|
|
return _inputFile->header().displayWindow();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const Imath::Box2i &
|
|
|
|
TiledRgbaInputFile::dataWindow () const
|
|
|
|
{
|
|
|
|
return _inputFile->header().dataWindow();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
TiledRgbaInputFile::pixelAspectRatio () const
|
|
|
|
{
|
|
|
|
return _inputFile->header().pixelAspectRatio();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const Imath::V2f
|
|
|
|
TiledRgbaInputFile::screenWindowCenter () const
|
|
|
|
{
|
|
|
|
return _inputFile->header().screenWindowCenter();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
TiledRgbaInputFile::screenWindowWidth () const
|
|
|
|
{
|
|
|
|
return _inputFile->header().screenWindowWidth();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LineOrder
|
|
|
|
TiledRgbaInputFile::lineOrder () const
|
|
|
|
{
|
|
|
|
return _inputFile->header().lineOrder();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Compression
|
|
|
|
TiledRgbaInputFile::compression () const
|
|
|
|
{
|
|
|
|
return _inputFile->header().compression();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
RgbaChannels
|
|
|
|
TiledRgbaInputFile::channels () const
|
|
|
|
{
|
|
|
|
return rgbaChannels (_inputFile->header().channels(), _channelNamePrefix);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
TiledRgbaInputFile::version () const
|
|
|
|
{
|
|
|
|
return _inputFile->version();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
TiledRgbaInputFile::isComplete () const
|
|
|
|
{
|
|
|
|
return _inputFile->isComplete();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unsigned int
|
|
|
|
TiledRgbaInputFile::tileXSize () const
|
|
|
|
{
|
|
|
|
return _inputFile->tileXSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unsigned int
|
|
|
|
TiledRgbaInputFile::tileYSize () const
|
|
|
|
{
|
|
|
|
return _inputFile->tileYSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LevelMode
|
|
|
|
TiledRgbaInputFile::levelMode () const
|
|
|
|
{
|
|
|
|
return _inputFile->levelMode();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LevelRoundingMode
|
|
|
|
TiledRgbaInputFile::levelRoundingMode () const
|
|
|
|
{
|
|
|
|
return _inputFile->levelRoundingMode();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
TiledRgbaInputFile::numLevels () const
|
|
|
|
{
|
|
|
|
return _inputFile->numLevels();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
TiledRgbaInputFile::numXLevels () const
|
|
|
|
{
|
|
|
|
return _inputFile->numXLevels();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
TiledRgbaInputFile::numYLevels () const
|
|
|
|
{
|
|
|
|
return _inputFile->numYLevels();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
TiledRgbaInputFile::isValidLevel (int lx, int ly) const
|
|
|
|
{
|
|
|
|
return _inputFile->isValidLevel (lx, ly);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
TiledRgbaInputFile::levelWidth (int lx) const
|
|
|
|
{
|
|
|
|
return _inputFile->levelWidth (lx);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
TiledRgbaInputFile::levelHeight (int ly) const
|
|
|
|
{
|
|
|
|
return _inputFile->levelHeight (ly);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
TiledRgbaInputFile::numXTiles (int lx) const
|
|
|
|
{
|
|
|
|
return _inputFile->numXTiles(lx);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
TiledRgbaInputFile::numYTiles (int ly) const
|
|
|
|
{
|
|
|
|
return _inputFile->numYTiles(ly);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Imath::Box2i
|
|
|
|
TiledRgbaInputFile::dataWindowForLevel (int l) const
|
|
|
|
{
|
|
|
|
return _inputFile->dataWindowForLevel (l);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Imath::Box2i
|
|
|
|
TiledRgbaInputFile::dataWindowForLevel (int lx, int ly) const
|
|
|
|
{
|
|
|
|
return _inputFile->dataWindowForLevel (lx, ly);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Imath::Box2i
|
|
|
|
TiledRgbaInputFile::dataWindowForTile (int dx, int dy, int l) const
|
|
|
|
{
|
|
|
|
return _inputFile->dataWindowForTile (dx, dy, l);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Imath::Box2i
|
|
|
|
TiledRgbaInputFile::dataWindowForTile (int dx, int dy, int lx, int ly) const
|
|
|
|
{
|
|
|
|
return _inputFile->dataWindowForTile (dx, dy, lx, ly);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
TiledRgbaInputFile::readTile (int dx, int dy, int l)
|
|
|
|
{
|
|
|
|
if (_fromYa)
|
|
|
|
{
|
|
|
|
Lock lock (*_fromYa);
|
|
|
|
_fromYa->readTile (dx, dy, l, l);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_inputFile->readTile (dx, dy, l);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
TiledRgbaInputFile::readTile (int dx, int dy, int lx, int ly)
|
|
|
|
{
|
|
|
|
if (_fromYa)
|
|
|
|
{
|
|
|
|
Lock lock (*_fromYa);
|
|
|
|
_fromYa->readTile (dx, dy, lx, ly);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_inputFile->readTile (dx, dy, lx, ly);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
TiledRgbaInputFile::readTiles (int dxMin, int dxMax, int dyMin, int dyMax,
|
|
|
|
int lx, int ly)
|
|
|
|
{
|
|
|
|
if (_fromYa)
|
|
|
|
{
|
|
|
|
Lock lock (*_fromYa);
|
|
|
|
|
|
|
|
for (int dy = dyMin; dy <= dyMax; dy++)
|
|
|
|
for (int dx = dxMin; dx <= dxMax; dx++)
|
|
|
|
_fromYa->readTile (dx, dy, lx, ly);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_inputFile->readTiles (dxMin, dxMax, dyMin, dyMax, lx, ly);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TiledRgbaInputFile::readTiles (int dxMin, int dxMax, int dyMin, int dyMax,
|
|
|
|
int l)
|
|
|
|
{
|
|
|
|
readTiles (dxMin, dxMax, dyMin, dyMax, l, l);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
TiledRgbaOutputFile::updatePreviewImage (const PreviewRgba newPixels[])
|
|
|
|
{
|
|
|
|
_outputFile->updatePreviewImage (newPixels);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
TiledRgbaOutputFile::breakTile (int dx, int dy, int lx, int ly,
|
|
|
|
int offset, int length, char c)
|
|
|
|
{
|
|
|
|
_outputFile->breakTile (dx, dy, lx, ly, offset, length, c);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace Imf
|