### Navigation

- index
- NDK Dev Guide »

# Basic image calls

## _validate (region of definition)

> void _validate(bool)

> > _validate() is called by NUKE on your Op to “validate” it. Validation consists of

> > > 1. calling, in turn, validate() on all the inputs that the Op is using in its current configuration (it is possible to omit to validate inputs which are not used. For example, a mask input controlled

> > >    > by a Bool_knob which is off, and is not used at all, need not be validated)

> > > 2. setting up Op-type-dependent data. For the case of Iops, validate should set the “IopInfo”, which is stored in the info_ member on Iop. For Deep Ops, validate should set the DeepInfo.

> > The IopInfo contains various fields

> > > - Itself, it subclasses from Box. this is a simple bounding box. this represents the area in which the Op has pixels, and includes any overscan which is available
> > > - two formats. One is the “fullSizeFormat”, and one is the regular “format”. In non-proxy mode these are the same, but in proxy mode the original, unscaled format is stored in “fullSizeFormat”, and the effective format as used by proxying is stored in “format.” The format is the region to which the bounding box is clipped when displayed in the Viewer.
> > > - channels. These are the channels for which pixel data is defined. Attempts to fetch Channels from Iops which do not have these channels will result in zeroes.
> > > - first_frame/last_frame. These are the frame range for which the Iop is defined. For example, a Read node will fill in.
> > > - black_outside. By default NUKE edge-extends Iops when you attempt to access them outside their bounding box. This is not always desirable, so Ops can set ‘black_outside’. This is slightly confusing, in that rather than changing the default extension behaviour, it instead means that the pixel data has been padded with an extra layer of black pixels (and the bbox padded similarly), and therefore for use of the bbox as numbers it will strip out that extra padding.
> > > - ydirection indicates the preferred y order. A negative value indicates that the preferred access order is from the top (high y) to the bottom (low y), a positive value indicates that the preferred access order is the other way round.

> > _validate is called on the main thread. It should avoid doing excessive calculations, as it will block the UI during its call. In particular, it should avoid calculating the image on the input, as this can take arbitrarily long.

> > The Iop has another field that can be set, in _validate, “out_channels_”. This defaults to Mask_All, and has no effect. If it’s set to something smaller then NUKE can, as an optimisation, skip calling _request and engine() on this Iop entirely, and go straight to its input Iop (by default input(0), but this can be changed by setting raw_channels_from). This is not guaranteed, and implementations of engine() should still be able to deal with being called for channels not in out_channels_.

> > During _validate you can assume that the Op has been built for the right frame and view. outputContext() is valid and the knobs have been stored via knobs().

## _request (region of interest)

> void _request(int x, int y, int r, int t, ChannelMask, int count)

> > _request() is called by NUKE on your Iop to indicate the region and channels of interest. In turn your Iop should indicate the region and channels of interest by calling request() on its inputs, as appropriate. It is important where possible to keep the region of interest down to the minimum necessary, as NUKE will potentially calculate and cache the full region of interest.

> > In a complex tree, NUKE might call _request on your Iop multiple times. Each time it passes the cumulative region and channels of interest.

> > _request() is usually handled in the main (UI) thread, and, like _validate() should avoid taking too long.

> > The parameter /count/ is a hint to the caching, and indicates how many overlapping requests have been made. When calling request() on your inputs, you can specify a value greater than one to force it to cache the data on this input.

## engine (image processing)

> void engine(int y, int l, int r, ChannelMask channels, Row& row)

> > > engine() is called by NUKE on your Iop as the function which actually processes image data. This is called on the worker threads, and therefore should be thread-safe against itself, and not make large number of calls. In general it should not call functions on knobs, particularly those to set or get values, as these calls are not thread-safe and may result in crashing. Any calculations based on knob values it requires should have already taken place in _validate, and result in fields within the Iop being set that engine() can later refer to.

> > > It is passed in coordinates representing a segment of a scanline, and a set of channels, and a row is passed by reference. It is supposed to set up the row so that all the channels are defined in the range x ... r. This value will previously have been set.

> > > A trivial implementation of engine is as follows:

> > > > void engine(int, int, int, ChannelMask channels, Row& row) {
> > > >   row.erase(channels);

> > > > }

> > This blanks all the channels. Blanking must be done explicitly, as the state of Row is not defined to be empty initially. Another example, that fills in all the pixels with the value 1, is as follows:

> > > void engine(int y, int l, int r, ChannelMask channels, Row& row) {

> > >   foreach(z, channels) {

> > >   > float* out = row.writable(z); for (int x = l ; x < r ; x++) {

> > >   > > out[x] = 1.0f;

> > >   > }

> > >   }

> > > }

> > We see here the use of writable(Channel), which obtains a pointer to the row’s internal buffer for that channel, which the calling code can write into. writable(Channel) is based so that it is valid between the left and right extents of the Row (specified in its constructor, which has been run by the code which calls engine). It is not always valid to simply deference writable(Channel), as x may be higher than 0.

> > engine() can also fetch data from the input. For example, a simple passthrough operator would have this engine:

> > > void engine(int y, int l, int r, ChannelMask channels, Row& row) {
> > >   row.get(input0(), y, l, r, channels);

> > > }

> > If it is possible to write your operator as a simple in-place modification of its input, it is recommended to do so, as this leads to more efficient memory use. For, example, a simple gain would end up being:

> > > > void engine(int y, int l, int r, ChannelMask channels, Row& row) {

> > > >   row.get(input0(), y, l, r, channels); foreach(z, channels) {

> > > >   > if (z == Chan_Red || z == Chan_Blue || z == Chan_Green) {

> > > >   >   const float* in = row[z]; float* out = row.writable(z); for (int x = l ; x < r ; x++) {

> > > >   >   > out[x] = 1.0f;

> > > >   >   }

> > > >   > }

> > > >   }

> > > > }

> > > For more details about how to fetch image data in engine, please see [section]. Also, various subclasses of and helper classes for Iop exist that are primarily to remove boilerplate within engine and add extra functionality.

## _open

> _open is called immediately prior to the first engine() call on an Iop, after it has been _request()ed. Like engine() it is called on a worker thread, and so therefore can take any amount of time without causing the UI to block excessively. Calls to open() are locked so that only one will happen, and this will be complete by the time other calls to engine() begin.

> If you have Iop that has to process its entire input, open() would be a good place to do that processing

> Unlike validate/request/engine _open does not need to and should not recursively call its inputs.

## close

> _close is called some time after rendering has finished (for the moment). This cannot be relied upon to always happen when rendering finishes, but if _close is called it should be safe to .

> If NUKE decides that it needs to do more work, it will re-call open().

> Unlike validate/request/engine _close does not need to and should not recursively call its inputs.

### Navigation

- index
- NDK Dev Guide »