.. _2d-channels:

Working With Channels
=====================

In this section we'll look at a few practical examples of working with channels as it's often a topic that causes a little confusion.

Adding & Removing Channels
--------------------------

First up, the simplest manipulation of all - adding or removing channels from the stream. There's two examples in the NDK named, appropriately,
AddChannels.cpp and Remove.cpp. Note that AddChannels is listed in the interface under an 'Add' menu entry (in the channels sub-menu). The Op
itself couldn't be called simply 'Add' as that was already taken by the maths Add node.

In both cases, outside of knob storage and setup, the work happens in **_validate**. Indeed, both samples inherit from
**NoIop**, so don't even have to go to the effort of implementing engine methods copying the actual image data through. The two **_validate** methods
are reproduced below:

.. code-block:: c

  void AddChannels::_validate(bool for_real)
  {
    copy_info();
    ChannelSet newchan = channels;
    newchan += (channels2);
    newchan += (channels3);
    newchan += (channels4);
    set_out_channels(newchan);
    info_.turn_on(newchan);
  }

.. code-block:: c

  void Remove::_validate(bool for_real)
  {
    copy_info();
    ChannelSet c = channels;
    c += (channels2);
    c += (channels3);
    c += (channels4);
    if (operation) {
      info_.channels() &= (c);
      set_out_channels(info_.channels());
    }
    else {
      info_.turn_off(c);
      set_out_channels(c);
    }
  }

In both cases we build a temporary **ChannelSet** from the **Channels** used by the knobs, then use this to set the channels present in the output
(either adding them or removing them). We then additionally use **set_out_channels** to inform NUKE what channels are changed by this operator (either
added or removed).

Working With Multiple Channels Simultaneously
---------------------------------------------

Many calculations require access to multiple channels simultaneously. Let's say, for example, a colourspace conversion from RGB to YUV; to produce any of the output
YUV channels requires access to all of the input RGB channels. To handle circumstances like this effectively you should register interest in all required input channels, 
then when calculating the output,calculate all the output channels possible from the available input channels and keep an internal list of those done, 
so that when your **foreach** channel loop hits a channel you've already calculated you don't redo the work. The Saturation.cpp sample is an excellent example of
this technique being applied.

.. code-block:: c

  class SaturationIop : public PixelIop
  {
    //...
    void in_channels(int input_number, ChannelSet& channels) const
    {
      // Must turn on the other color channels if any color channels are requested:
      ChannelSet done;
      foreach (z, channels) {
	if (colourIndex(z) < 3) { // it is red, green, or blue
	  if (!(done & z)) { // save some time if we already turned this on
	    done.addBrothers(z, 3); // add all three to the "done" set
	  }
	}
      }
      channels += done; // add the colors to the channels we need
    }
    //...
  };

  void SaturationIop::pixel_engine(const Row& in, int y, int x, int r,
				   ChannelMask channels, Row& out)
  {
    ChannelSet done;
    foreach (z, channels) { // visit every channel asked for
      if (done & z)
	continue;             // skip if we did it as a side-effect of another channel

      // If the channel is not a color, we return it unchanged:
      if (colourIndex(z) >= 3) {
	out.copy(in, z, x, r);
	continue;
      }

      // Find the rgb channels that belong to the set this channel is in.
      // Add them all to "done" so we don't run them a second time:
      Channel rchan = brother(z, 0);
      done += rchan;
      Channel gchan = brother(z, 1);
      done += gchan;
      Channel bchan = brother(z, 2);
      done += bchan;

      // pixel_engine is called with the channels indicated by in_channels()
      // already filled in. So we can just read them here:
      const float* rIn = in[rchan] + x;
      const float* gIn = in[gchan] + x;
      const float* bIn = in[bchan] + x;

      // We want to write into the channels. This is done with a different
      // call that returns a non-const float* pointer. We must call this
      // *after* getting the in pointers into local variables. This is
      // because in and out may be the same row structure, and calling
      // these may change the pointers from const buffers (such as a cache
      // line) to allocated writable buffers:
      float* rOut = out.writable(rchan) + x;
      float* gOut = out.writable(gchan) + x;
      float* bOut = out.writable(bchan) + x;

      //.... Loop across pixels calculating saturation for all three channels
      //.... and setting them in the output row.
  }

Here we use the **ChannelSet** done to keep track of what we've already calculated and what we haven't. 
At the start of every channel loop we check to see if it's done and skip if it has, otherwise we do the 3.

Accessing Other Channelsets
---------------------------

We can use a very similar technique to access data from any other Channel(Set)s in the incoming image - we 
simply add them to the channels requested from the input (either using **in_channels** in a PixelIop or **_request** in an
Iop), at which point we can call on them in our **engine** method with impunity. Take the following code taken from
the AccessMotion.cpp example:

.. code-block:: c

  virtual void in_channels(int, ChannelSet& mask) const {
    mask += _requestChannels;
  }

  virtual void  _request (int x, int y, int r, int t, ChannelMask channels, int count) {
    for (int n = 0; n < inputs(); n++) {
      Iop * thisInput = dynamic_cast<Iop*>(Op::input(n));
      ChannelSet in = channels;
      in_channels(n, in);
      if (thisInput && in) {
        thisInput->request(in, 0);
      }
    }
  }

This essentially reimplements the use of **in_channels** found in PixelIop, from our **_request** for convenvience and 
improved readability. The 
_requestChannels in this case is initialised to a Mask_MoVec to allow access to the predefined motion vector channels found
in NUKE. The remainder of the example uses the motion vectors to warp previous and next frames onto the current one, before
medianing them, in a full frame buffer access manner. The _validate coupled with the engine simply copy the incoming channels
down the tree, so that the motionvectors stay accessible after the current op.


.. TODO Merging & Manipulating Channels

