The ‘DeepRead’ node supports different file formats through the use of ‘DeepReader’ plugins. This is similar to the relationship betwen the ‘Read’ node and the ‘Reader’ plugins.
A deep file reader should inherit from the class DD::Image::DeepReader, and then register themselves in the symbol table by creating a DD::Image::DeepReader::Description. Here is a trivial example:
#include "DDImage/DeepReader.h" #include "DDImage/DeepPlane.h" using namespace DD::Image; class BlankReaderDeep : public DeepReader { public: BlankReaderDeep(DeepReaderOwner* op, const std::string& filename) : DeepReader(op) { setInfo(10, 10, _owner->readerOutputContext(), Mask_RGBA | Mask_Deep); _metaData.setData("deep/example", "123"); } virtual void doDeepEngine(Box box, const ChannelSet& channels, DeepOutputPlane& outPlane) { outPlane = DeepOutputPlane(channels, box); for (Box::iterator it = box.begin(); it != box.end(); it++) { outPlane.addHole(); } } }; static const DeepReader::Description d("blank\0", "blank", BuildDeepReader<BlankReaderDeep>);
This plugin would then be compiled to the file “blankReaderDeep.so”, and would be invoked by NUKE when a DeepRead wants to open a file with the extension ”.blank”. See the cdfReaderDeep.cpp sample included with the NDK for a more complex example.
- BlankReaderDeep::BlankReaderDeep(DeepReaderOwner* op, const std::string& filename)¶
This constructor is invoked by the DeepRead when it wants to access image metadata, and in preparation for accessing actual image data. It is given the filename, and a pointer to a small interface allowing certain data on the DeepRead to be accessed. The filename is fully-cooked at this point (i.e. with expressions fully evaluated), and can be passed to a fopen() call.
The constructor is supposed to open the file and read from it sufficiently to determine the image size, number of channels present, and possibly other metadata. It presents the basic information by calling setInfo. This function takes width and height as its first parameters, then an outputContext reference, then the set of channels that are available, and then an optional pixel aspect ratio.
setInfo itself looks for an appropriate matching Format, and if no such Format is found, then it creates one. The OutputContext is used to implement downrez. If one is passed along (the example here passes along the owner’s readerOutputContext, which is usual), then it will figure out the ‘proxy’ format and bounding box along with the full-size one. This means that with a downrez of 2, then the bounding box produced by DeepRead for this plugin will be 5x5 not 10x10.
The BlankReaderDeep can also set metadata, as demonstrated.
Other Deep Ops may expect the DeepFront and DeepBack channels to be present. If you only have one depth per sample, mark them both, and set them to the same data.
- virtual void doDeepEngine(Box box, const ChannelSet& channels, DeepOutputPlane& outPlane)
The DeepRead defers the doDeepEngine function directly to the DeepReader. The DeepReader must create a plane and populate it for all the pixels in box and channels in channels. The example here just adds holes: it could also assemble DeepOutPixels and adds those. It is important to handle all the channels, including those which were not part of the mask that the constructor set: these should be filled in with zeroes. Boxes outside the constructor’s bounding box might also be requested: these should be just output as holes (0-sample pixels).
Note that doDeepEngine() will have to do its own downrezzing. The box it has been passed already has been downrezzed, and it can use the readerOutputContext().from_proxy_x() and from_proxy_y() functions to convert pixel coordinates back into full-size ones for file access.