Deep Reader
===========

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:

  .. code-block:: c

    #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.

  .. cpp:function:: 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.

  .. cpp:function:: 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.
