Deep to 2D ops
==============

It is possible to write a regular 2D Iop that takes deep inputs (or a mix of deep
an Iop inputs).  We present a simple one here:

  .. code-block:: c

    #include "DDImage/Iop.h"
    #include "DDImage/Row.h"
    #include "DDImage/DeepOp.h"
    
    const char* CLASS = "DeepSampleCount";
    
    using namespace DD::Image;
    
    class DeepSampleCount : public Iop
    {
    public:
      DeepSampleCount(Node* node) : Iop(node)
      {
        inputs(1);
      }

      virtual bool test_input(int idx, Op*op) const
      {
        return dynamic_cast<DeepOp*>(op);
      }
    
      virtual Op* default_input(int idx) const
      {
        return NULL;
      }
    
      DeepOp* input0()
      {
        return dynamic_cast<DeepOp*>(Op::input(0));
      }
    
      const char* Class() const
      {
        return CLASS;
      }
    
      const char* node_help() const
      {
        return "counts the number of deep samples at each position, and places this in the red channel";
      }
    
      void _validate(bool real)
      {
        if (input0()) {
          input0()->validate(real);
          info_ = input0()->deepInfo();
          info_.channels(Mask_Red);
          set_out_channels(Mask_Red);
        } else {
          info_.set(Box());
          info_.channels(Mask_None);
        }
      }
    
      void _request(int x, int y, int r, int t, ChannelMask channels, int count)
      {
        if (input0()) {
          input0()->deepRequest(Box(x, y, r, t), Mask_Red, count);
        }
      }
    
      void engine(int y, int x, int r, const ChannelSet& cs, Row& row)
      {
        DeepOp* deepIn = input0();
    
        if (!deepIn) {
          foreach(z, cs) {
            row.erase(z);
          }
          return;
        }
    
        DeepPlane deepRow;
        if (!deepIn->deepEngine(y, x, r, Mask_Red, deepRow)) {
          Iop::abort();
          foreach(z, cs) {
            row.erase(z);
          }
          return;
        }
    
        while (x < r) {
          DeepPixel deepPixel = deepRow.getPixel(y, x);
          foreach(z, cs) {
            row.writable(z)[x] = deepPixel.getSampleCount();
          }
          x++;
        }
      }
    
      virtual int getViewableModes() const
      {
        return eViewableMode2D;
      }
      };
    
    static Op* build(Node* node) { return new DeepSampleCount(node); }
    
    static const Op::Description desc(::CLASS, "Image/DeepSampleCount", build);
    
Some of this is simple Iop boilerplate, covered elsewhere.  The important functions here are
as follows:

  .. cpp:function:: virtual bool DeepSampleCount::test_input(int idx, Op* op) const

  This function is called by nuke to determine whether a given op can be used as the input
  connection for another.  The default implementation in Iop returns true only for Iops. If
  your node is to take something other than an Iop as an input, it needs to be overriden.
  Our sample implementation only has one, deep, input, and so a simple dynamic_cast to DeepOp
  suffices.
  
  .. cpp:function:: virtual Op* DeepSampleCount::default_input(int idx) const

  If ``test_input`` returns false or if no input is connected, then NUKE will call default_input
  to obtain a fallback to use as an input.  For Iops the default implementation of this returns
  a Black operator set to the Root's format.

  For our operator, this would be inappropriate, as it requires a Deep input, not an Iop, so we
  instead return simply NULL.  This means that the uses of the input in later functions have to
  check for NULL and handle these cases.

  .. cpp:function:: virtual void DeepSampleCount::_validate(bool for_real)

  validate should set the info_ of the Iop.  It does this here by validating the input, copying
  the deepInfo of the input, and then altering any parts that need changing (in this case, it
  emits only the Red channel).  It also handles the case where Iop is NULL.

  .. cpp:function:: virtual void DeepSampleCount::_request(int x, int y, int r, int t, ChannelMask channels, int count)
  
  This function makes a deepRequest() call on the input DeepOp, if connected.

  .. cpp:function:: virtual void engine(int y, int x, int r, const ChannelSet& cs, Row& row)

  This function does the actual work of converting from a Deep image to a 2D image.  Firstly,
  it handles the case where there is no deep input by erasing all the requested channels from
  the row, then returning.

  Nextly, it prepares and fetches a DeepPlane corresponding to the row that has been requested.
  The deepEngine() function has a special three-int variant which takes "y, x, r" - the same
  order that the parameters to engine are in - just for fetching a single row.  The return
  value for this is then tested - if it was false there is an abort and it calls Iop::abort()
  to abort further processing, and blanks the output lines.

  Otherwise, it iterates over the horizontal span of the row being fetched, and places the
  sample count into each pixel.
