///////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2012 The Foundry Visionmongers Ltd. All Rights Reserved.
//
//
//
// 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.
//
///////////////////////////////////////////////////////////////////////////
#include "DDImage/DeepReader.h"
#include "DDImage/DeepPlane.h"
#include "DDImage/Knobs.h"
#include "DDImage/Thread.h"
#include "dtex.h"
#include "rx.h"
using namespace DD::Image;
static const char* kOpacityType[] = { "auto", "deepopacity", "alpha", nullptr };
enum OpacityType { eAuto = 0, eDeepOpacity, eAlpha } ;
namespace Nuke {
namespace Deep {
static bool endswith(const std::string& target, const std::string& suffix)
{
if (target.length() < suffix.length())
return false;
return target.substr(target.length() - suffix.length()) == suffix;
}
static inline bool IsRGB(Channel channel)
{
return channel == Chan_Red || channel == Chan_Blue || channel == Chan_Green;
}
static inline bool IsRGBA(Channel channel)
{
return channel == Chan_Red || channel == Chan_Blue || channel == Chan_Green || channel == Chan_Alpha;
}
/**
* Fetch the major version of renderman. Returns -1 if indeterminable.
*/
static int RenderManVersion()
{
int version[4];
RxInfoType_t type;
int count;
int status = RxRendererInfo("version", &version, 4 * sizeof(int), &type, &count);
if (type == RxInfoInteger && count == 4 && status == 0)
return version[1];
else
return -1;
}
class dtexDeepReaderFormat : public DeepReaderFormat
{
friend class dtexDeepReader;
bool _raw;
bool _premult;
bool _discrete;
int _type;
public:
dtexDeepReaderFormat()
{
_raw = false;
_premult = false;
_discrete = true; // NOTE this is for backwards compatibility, it is knob defaulted to false ( which is probably more correct and matches weta's implementation )
_type = (int) eAuto;
}
void knobs(Knob_Callback f) override
{
Enumeration_knob(f, &_type, kOpacityType, "type");
Tooltip(f, "The type of dtex file. \n"
"If this is set to deepopacity, the dtex file will be interpreted as "
"though it were rendered as an accumulated deep opacity "
"file, corresponding to a Renderman Display Driver config "
"of:
"
" Display \"Filename.dtex\" \"deepshad\" \"deepopacity\"
"
"
If this is set to alpha then it will be interpreted as the newer point-sampled alpha or color which "
"would be a display line like:
"
" Display \"Filename.dtex\" \"deepshad\" \"a\"
"
"or...
"
" Display \"Filename.dtex\" \"deepshad\" \"rgba\"
"
"
If set to 'auto' the type will be autodetected by looking at the subimage name. If it is either \"Deep Shadow\" "
"or ends with (or is) \".deepopacity\" the file will be treated like a deep opacity file." );
Bool_knob(f, &_discrete, "discrete", "discrete");
Tooltip(f, "Treat the file as discrete samples with the front and back being the same. This is only relevant for deep opacity files, color deep compositing files are always discrete.");
Bool_knob(f, &_premult, "premult", "premultiply");
Tooltip(f, "Premultiply values from file (if off, then it assumes they are already premultiplied)");
Bool_knob(f, &_raw, "raw", "raw values");
Tooltip(f, "Read the samples exactly 'as is' without processing.");
}
void append(Hash& hash) override
{
hash.append(_raw);
hash.append(_premult);
hash.append(_discrete);
hash.append(_type);
}
};
/**
* This is a plugin to read the Pixar Renderman 'dtex' files.
*/
class dtexDeepReader : public DeepReader
{
static Lock sLibraryLock;
DtexFile* _dtexFile;
DtexCache* _dtexCache;
Lock _engineLock;
int _numChans;
DD::Image::OutputContext _outputContext;
int _dtexOpenError; // defined in thirdparty/sony/renderman/version/bin/platform/include/dtex.h
dtexDeepReaderFormat *_dtexReaderFormat;
public:
dtexDeepReader(DeepReaderOwner* op, const std::string& filename) : DeepReader(op)
{
_dtexOpenError = DTEX_NOERR;
_dtexFile = nullptr;
_dtexCache = nullptr;
if (filename.length())
open(filename);
if (!_dtexFile) {
if (filename.length() == 0)
_op->error("no filename specified");
else if (_dtexOpenError != DTEX_NOERR)
_op->error("error (%d) opening file: %s", _dtexOpenError, filename.c_str());
else
_op->error("missing file: %s", filename.c_str());
_deepInfo = DeepInfo();
return;
}
_outputContext = _owner->readerOutputContext();
DtexImage* image;
DtexGetImageByIndex(_dtexFile, 0, &image);
float NP[16];
DtexNP(image, NP);
float Nl[16];
DtexNl(image, Nl);
_numChans = DtexNumChan(image);
setInfo(DtexWidth(image), DtexHeight(image), _outputContext, Mask_RGB | Mask_Alpha | Mask_Deep);
_metaData.setData("dtex/np", NP, 16);
_metaData.setData("dtex/nl", Nl, 16);
_dtexReaderFormat = dynamic_cast( op->handler() );
mFnAssert(_dtexReaderFormat);
}
~dtexDeepReader() override
{
if (_dtexFile) {
DtexClose(_dtexFile);
_dtexFile = nullptr;
}
if (_dtexCache) {
DtexDestroyCache(_dtexCache);
_dtexCache = nullptr;
}
}
void open(const std::string& filename)
{
_dtexCache = DtexCreateCache(10000, nullptr);
_dtexOpenError = DtexOpenFile(filename.c_str(), "rb", _dtexCache, &_dtexFile);
}
bool doDeepEngine(DD::Image::Box box, const ChannelSet& channels, DeepOutputPlane& plane) override
{
static int sRenderManVersion = RenderManVersion();
// RenderMan 15 seems not to be thread-safe (bug 23831)
Guard g(sRenderManVersion < 16 ? sLibraryLock : _engineLock);
if (!_dtexFile) {
if (_dtexOpenError != DTEX_NOERR)
_op->error("error (%d) opening file", _dtexOpenError);
else
_op->error("missing file.");
return false;
}
DtexImage* image;
DtexGetImageByIndex(_dtexFile, 0, &image);
int height = DtexHeight(image);
DtexPixel* pixel = DtexMakePixel(_numChans);
plane = DeepOutputPlane(channels, box);
bool raw = _dtexReaderFormat->_raw;
bool discrete = _dtexReaderFormat->_discrete;
bool premult = _dtexReaderFormat->_premult;
enum OpacityType type = (enum OpacityType)_dtexReaderFormat->_type;
std::map chanMap;
if (_numChans == 4) {
chanMap[Chan_Red] = 0;
chanMap[Chan_Green] = 1;
chanMap[Chan_Blue] = 2;
chanMap[Chan_Alpha] = 3;
} else {
chanMap[Chan_Alpha] = 0;
}
std::vector pts(_numChans);
std::vector pts2(_numChans);
for (Box::iterator it = box.begin(); it != box.end(); it++) {
float x = it.x;
float y = it.y;
_outputContext.from_proxy_xy(x, y);
DtexGetPixel(image, int(x), height - 1 - int(y), pixel);
// try and work out the type automatically
if ( type == eAuto ) {
type = eAlpha;
char* imageName = DtexImageName( image );
if ( imageName && strlen(imageName) ) {
std::string name( imageName );
if ( name == "Deep Shadow" )
type = eDeepOpacity;
else if ( endswith( name, ".deepopacity" ) )
type = eDeepOpacity;
}
}
int numpts = DtexPixelGetNumPoints(pixel);
DeepOutPixel pels(numpts * channels.size());
for (int j = 0; j < numpts; j ++ ) {
float z;
DtexPixelGetPoint(pixel, j, &z, &pts[0]);
float z2;
if ( raw || type == eAlpha ) {
z2 = z;
} else {
if ( DtexPixelGetPoint(pixel, j+1, &z2, &pts2[0]) != DTEX_NOERR )
continue;
if ( pts[chanMap[Chan_Alpha]] == pts2[chanMap[Chan_Alpha]] )
continue;
if ( discrete )
z2 = z ;
}
float alpha = 1;
if (chanMap.count(Chan_Alpha)) {
const int alphaChan = chanMap[Chan_Alpha];
alpha = ( raw || type == eAlpha ) ?
pts[alphaChan] :
((pts[alphaChan] - pts2[alphaChan]) / pts[alphaChan]);
}
foreach(chan, channels) {
if (chan == Chan_Z)
pels.push_back(1/z);
else if (chan == Chan_DeepFront )
pels.push_back(z);
else if ( chan == Chan_DeepBack ) {
pels.push_back(z2);
}
else if (chan == Chan_Alpha)
pels.push_back(alpha);
else if (chanMap.count(chan)) {
if (! raw && premult && IsRGB(chan) && chanMap.count(Chan_Alpha)) {
pels.push_back(pts[chanMap[chan]] * alpha);
}
else {
pels.push_back( pts[ chanMap[chan] ] );
}
}
else if (IsRGBA(chan))
pels.push_back(alpha);
else
pels.push_back(0);
}
}
plane.addPixel(pels);
}
DtexDestroyPixel(pixel);
return true;
}
};
static DeepReader* build(DeepReaderOwner* op, const std::string& fn)
{
return new dtexDeepReader(op, fn);
}
static DeepReaderFormat* buildFormat(DeepReaderOwner* op)
{
return new dtexDeepReaderFormat();
}
static const DeepReader::Description d("dtex\0deepshad\0dshd\0tex\0", "dtex", build, buildFormat);
Lock dtexDeepReader::sLibraryLock;
}
}