Simple DeepPixelOp
==================

DeepPixelOp is a base class that provides similar functionality to the combination
of NukeWrapper and PixelOp, albeit entirely within the base class rather than
partly in a wrapper.  DeepPixelOps provide no support for spatial operations: each
pixel on the input corresponds exactly to a pixel on the output, and the samples
within each pixel also correspond (with the exception that the samples from the
input can be dropped and not be represented in the output at all).

Here is an example class that implements a desaturate operation on the pixels,
setting the red, green, and blue channels to the luminance.

  .. code-block:: c

    #include "DDImage/DeepPixelOp.h"
    #include "DDImage/Knobs.h"
    #include "DDImage/RGB.h"
    
    static const char* CLASS = "DeepLuma";
    
    using namespace DD::Image;
    
    class DeepLuma : public DeepPixelOp
    {    
    public:
      DeepLuma(Node* node) : DeepPixelOp(node) {
      }
    
      const char* node_help() const
      {
        return "Take the luminance of deep data, eliminating chroma info.";
      }
    
      const char* Class() const {
        return CLASS;
      }
    
      virtual void in_channels(int, ChannelSet& channels) const
      {
        if (channels & Mask_RGB)
          channels += Mask_RGB;
      }
    
      virtual void processSample(int y,
                                 int x,
                                 const DD::Image::DeepPixel& deepPixel,
                                 int sampleNo,
                                 const DD::Image::ChannelSet& channels,
                                 DeepOutPixel& output) const
      {
        bool madeLuma = false;
        float luma;
        foreach(z, channels) {
          if (z == Chan_Red || z == Chan_Green || z==Chan_Blue) {
            if (!madeLuma) {
              luma = y_convert_rec709(deepPixel.getUnorderedSample(sampleNo, Chan_Red),
                                      deepPixel.getUnorderedSample(sampleNo, Chan_Green),
                                      deepPixel.getUnorderedSample(sampleNo, Chan_Blue));
              madeLuma = true;
            }
                                        
            output.push_back(luma);
          } else {
            output.push_back(deepPixel.getUnorderedSample(sampleNo, z));
          }
        }
      }
    };
    
    static Op* build(Node* node) { return new DeepLuma(node); }
    static const Op::Description d(CLASS, "Image/DeepLuma", build);

There are two main functions that a subclass Op needs to implement. These are in_channels and processSample.

  .. cpp:function:: virtual void DeepLuma::in_channels(int, ChannelSet& channels) const

  This function, like the ``PixelIop::in_channels`` function it is based upon,
  is called by NUKE to determine what extra channels are needed from the input.
  The example desaturate implementation always needs RGB, even if only one of those
  channels is requested.

  .. cpp:function:: virtual void DeepLuma::processSample(int y, int x, const DD::Image::DeepPixel& deepPixel, int sampleNo, const DD::Image::ChannelSet& channels, DeepOutPixel& output) const

  This is the main function that subclasses of DeepPixelOp must implement. It is given an input pixel in deepPixel and 
  processes the sample indicated by sampleNo. It is expected to process the channels /channels/ in /output/. It
  is given the x and y coordinates of this pixel too.

  The function may return without altering /output/ - in this case the sample is omitted. Otherwise, it
  prepares /output/ by adding float values to it representing the various channels (in the order that foreach
  uses).  The /channels/ set might be smaller or larger (or indeed disjointed) than the channels in /deepPixel/
  depending upon your in_channels implementation.

  Our example passes through all the channels, except for red, green and blue. For these, it calculates
  a luminance value, and then uses this for any subsequent RGB channels.

