1.. _invertExample:
2
3This guide will take you through the fundamentals of processing images
4in OFX. An example plugin will be used to illustrate how it all works
5and its source can be found in the C++ file
6`invert.cpp <https://github.com/ofxa/openfx/blob/master/Guide/Code/Example2/invert.cpp>`_.
7This plugin takes an image and
8inverts it (or rather calculates the complement of each component).
9Ideally you should have read the guide to the :ref:`basic machinery of an OFX
10plugin <basicExample>` before you read this guide.
11
12Action Stations!
13================
14
15The invert example is pretty much the most minimal OFX plugin you can
16write that processes images. It leaves many things at their default
17settings which means it doesn’t have to trap more than four actions in
18total  [1]_ and set very few switches.
19
20From the source, here is the main entry routine that traps those
21actions...
22
23`invert.cpp <https://github.com/ofxa/openfx/blob/doc/Documentation/sources/Guide/Code/Example2/invert.cpp#L416>`_
24
25.. code:: c++
26
27      ////////////////////////////////////////////////////////////////////////////////
28      // The main entry point function, the host calls this to get the plugin to do things.
29      OfxStatus MainEntryPoint(const char *action,
30                               const void *handle,
31                               OfxPropertySetHandle inArgs,
32                               OfxPropertySetHandle outArgs)
33      {
34        // cast to appropriate type
35        OfxImageEffectHandle effect = (OfxImageEffectHandle) handle;
36
37        OfxStatus returnStatus = kOfxStatReplyDefault;
38
39        if(strcmp(action, kOfxActionLoad) == 0) {
40          // The very first action called on a plugin.
41          returnStatus = LoadAction();
42        }
43        else if(strcmp(action, kOfxActionDescribe) == 0) {
44          // the first action called to describe what the plugin does
45          returnStatus = DescribeAction(effect);
46        }
47        else if(strcmp(action, kOfxImageEffectActionDescribeInContext) == 0) {
48          // the second action called to describe what the plugin does
49          returnStatus = DescribeInContextAction(effect, inArgs);
50        }
51        else if(strcmp(action, kOfxImageEffectActionRender) == 0) {
52          // action called to render a frame
53          returnStatus = RenderAction(effect, inArgs, outArgs);
54        }
55
56        /// other actions to take the default value
57        return returnStatus;
58      }
59
60    } // end of anonymous namespace
61
62It leaves out some of the actions we had in the last example, which were
63there for only illustrative purposes only. However, it is now trapping
64one more, the :c:macro:`kOfxImageEffectActionRender` action. Funnily
65enough, that is the action called to render a frame of output.
66
67Describing Our Plugin
68=====================
69
70We have the standard two step description process for this plugin.
71
72`invert.cpp <https://github.com/ofxa/openfx/blob/doc/Documentation/sources/Guide/Code/Example2/invert.cpp#L117>`_
73
74.. code:: c++
75
76      ////////////////////////////////////////////////////////////////////////////////
77      // the plugin's basic description routine
78      OfxStatus DescribeAction(OfxImageEffectHandle descriptor)
79      {
80        // set some labels and the group it belongs to
81        gPropertySuite->propSetString(effectProps,
82                                      kOfxPropLabel,
83                                      0,
84                                      "OFX Invert Example");
85        gPropertySuite->propSetString(effectProps,
86                                      kOfxImageEffectPluginPropGrouping,
87                                      0,
88                                      "OFX Example");
89
90        // define the image effects contexts we can be used in, in this case a simple filter
91        gPropertySuite->propSetString(effectProps,
92                                      kOfxImageEffectPropSupportedContexts,
93                                      0,
94                                      kOfxImageEffectContextFilter);
95
96        // set the bit depths the plugin can handle
97        gPropertySuite->propSetString(effectProps,
98                                      kOfxImageEffectPropSupportedPixelDepths,
99                                      0,
100                                      kOfxBitDepthFloat);
101        gPropertySuite->propSetString(effectProps,
102                                      kOfxImageEffectPropSupportedPixelDepths,
103                                      1,
104                                      kOfxBitDepthShort);
105        gPropertySuite->propSetString(effectProps,
106                                      kOfxImageEffectPropSupportedPixelDepths,
107                                      2,
108                                      kOfxBitDepthByte);
109
110        // get the property set handle for the plugin
111        OfxPropertySetHandle effectProps;
112        gImageEffectSuite->getPropertySet(descriptor, &effectProps);
113
114        // say that a single instance of this plugin can be rendered in multiple threads
115        gPropertySuite->propSetString(effectProps,
116                                      kOfxImageEffectPluginRenderThreadSafety,
117                                      0,
118                                      kOfxImageEffectRenderFullySafe);
119
120        // say that the host should manage SMP threading over a single frame
121        gPropertySuite->propSetInt(effectProps,
122                                   kOfxImageEffectPluginPropHostFrameThreading,
123                                   0,
124                                   1);
125
126        return kOfxStatOK;
127      }
128
129The function called for the describe action sets all the properties on
130an effect that are independent of specific contexts. In this case it
131sets some labels and says what contexts it can be used in, which is only
132the **filter** context, where an effect has a single input and output.
133It also says what data types it can support when processing images. This
134is a property that belongs to the plugin as a whole, not to individual
135clips (see below). If a plugin doesn’t support a data type needed by the
136host, the host is at liberty to ignore it and get on with it’s life.
137
138We said our plugin supports all the three standard pixel data types,
139which various properties throughout the API use. The values are:
140
141
142* :c:macro:`kOfxBitDepthByte`  Each component will be an 8 bit unsigned integer with a maximum value of 255.
143
144* :c:macro:`kOfxBitDepthShort`  Each component will be an 16 bit unsigned integer with a maximum value of 65535.
145
146* :c:macro:`kOfxBitDepthFloat`  Each component will be a 32 bit floating point number with a nominal white point of 1.
147
148
149.. note::
150
151    The :cpp:type:`OfxImageEffectHandle` passed to the describe calls should not
152    be cached away, It only represents some object used while describing
153    the effect. It is *not* the effect itself and when instances are
154    created the handle will refer to a different object entirely. In
155    general, never hang onto any effect handles in any global state.
156
157Finally our plugin is setting some flags to do with multithreaded
158rendering. The first flag, :c:macro:`kOfxImageEffectPluginRenderThreadSafety`
159is used to indicate how plugins and instances should be used when
160rendering in multiple threads. We are setting it to
161:c:macro:`kOfxImageEffectRenderFullySafe`, which means that the host can have
162any number of instances rendering and each instance could have possibly
163have simultaneous renders called on it. (eg: at separate frames). The
164other options are listed in the programming reference.
165
166The second call sets the
167:c:macro:`kOfxImageEffectPluginPropHostFrameThreading`, which says that the
168host should manage any symmetric multiprocessing when rendering the
169effect. Typically done by calling render on different tiles of the
170output image. If not set, it is up to the plugin to launch the
171appropriate number of threads and divide the processing appropriately
172across them.
173
174`invert.cpp <https://github.com/ofxa/openfx/blob/doc/Documentation/sources/Guide/Code/Example2/invert.cpp#L171>`_
175
176.. code:: c++
177
178      ////////////////////////////////////////////////////////////////////////////////
179      //  describe the plugin in context
180      OfxStatus
181      DescribeInContextAction(OfxImageEffectHandle descriptor,
182                              OfxPropertySetHandle inArgs)
183      {
184        OfxPropertySetHandle props;
185        // define the mandated single output clip
186        gImageEffectSuite->clipDefine(descriptor, "Output", &props);
187
188        // set the component types we can handle on out output
189        gPropertySuite->propSetString(props,
190                                      kOfxImageEffectPropSupportedComponents,
191                                      0,
192                                      kOfxImageComponentRGBA);
193        gPropertySuite->propSetString(props,
194                                      kOfxImageEffectPropSupportedComponents,
195                                      1,
196                                      kOfxImageComponentAlpha);
197        gPropertySuite->propSetString(props,
198                                      kOfxImageEffectPropSupportedComponents,
199                                      2,
200                                      kOfxImageComponentRGB);
201
202        // define the mandated single source clip
203        gImageEffectSuite->clipDefine(descriptor, "Source", &props);
204
205        // set the component types we can handle on our main input
206        gPropertySuite->propSetString(props,
207                                      kOfxImageEffectPropSupportedComponents,
208                                      0,
209                                      kOfxImageComponentRGBA);
210        gPropertySuite->propSetString(props,
211                                      kOfxImageEffectPropSupportedComponents,
212                                      1,
213                                      kOfxImageComponentAlpha);
214        gPropertySuite->propSetString(props,
215                                      kOfxImageEffectPropSupportedComponents,
216                                      2,
217                                      kOfxImageComponentRGB);
218
219        return kOfxStatOK;
220      }
221
222Here we are describing the plugin when it is being used as a filter. In
223this case we are describing two clips, the mandated *Source* and
224*Output* clips. Each clip has a variety of properties on them, in this
225case we are only setting what pixel components we accept on those
226inputs. The components supported (unlike the data type) is a per clip
227thinumgy. Pixels in OFX can currently only be of three types, which are
228listed below.
229
230
231:c:macro:`kOfxImageComponentRGBA` Each pixel has four samples, corresponding to Red, Green, Blue and Alpha. Packed as RGBA
232
233:c:macro:`kOfxImageComponentRGB` Each pixel has three samples, corresponding to Red, Green and Blue. Packed as RGB.
234
235:c:macro:`kOfxImageComponentAlpha` Each pixel has one sample, generally interpretted as an Alpha value.
236
237.. note::
238
239    The OpenGL rendering extension has significantly different set of
240    capabilities for this.
241
242.. _clips:
243
244Clips
245=====
246
247I hear you ask "What are these clips of which you speak Mr Nicoletti?",
248well they are a sequence of images that vary over time. They are
249represented in the API by an :cpp:type:`OfxImageClipHandle` and have a name
250plus an associated property set.
251
252Depending on the context, you will have to describe some mandated number
253of clips with specific names. For example the filter effect has two and
254only two clips you must describe *Source* and *Output*, a **transition**
255effect has three and only three clips *SourceFrom*, *SourceTo* and
256*Output* while a **general** effect has to have one clip called *Output*
257but as many other input clips as we want. There are ``**#defines**`` for
258these in the various OFX header files. The Programming Reference has
259more information on other contexts, and we will use more in later
260examples.
261
262There are many properties on a clip, and during description you get to
263set a whole raft of them as to how you want them to behave. We are
264relying on the defaults in this example that allow us to avoid issues
265like field rendering and more.
266
267You fetch images out of clips with a function call in the image effect
268suite, where you ask for an image at a specific frame. In all cases the
269clip named "Output" is the one that will give you the images you will be
270writing to, the other clips are always sources and you should not modify
271the data in them.
272
273.. _images:
274
275Images In OFX
276=============
277
278Before I start talking over the rendering in the example plugin, I
279should tell you about images in OFX.
280
281
282Images and the Image Plane
283--------------------------
284
285Images are contiguous rectangular regions of a nominally infinite 2D
286image plane for which the host has data samples, in the form of
287`pixels <http://alvyray.com/Memos/CG/Microsoft/6_pixel.pdf>`_.
288
289.. figure:: Pics/imagePlane.jpg
290    :scale: 50 %
291    :align: center
292    :alt: An image on the infinite image plane
293
294The figure above shows our image spanning the plane from coordinates X1
295to X2 in the X dimension and Y1 to Y2 in the Y dimension. We call these
296four numbers the image’s **bounds**, and is the region an image is
297guaranteed to have addressable data for.
298
299.. note::
300
301    Y goes **up** in OFX land, not down as is common in desktop
302    publishing.
303
304.. note::
305
306    That the image bound is open on the right, so iteration is
307    ``for (int x = x1; x < x2; ++x)``. This means the number of pixels
308    in the X dimension is given by X2-X1, similarly for the Y dimension.
309
310Image Data
311----------
312
313Images are made up of chunk of memory which is interpreted to be a 2D
314array of pixels. Each pixel in an image has exactly the same number of
315**components**, each component being of exactly the same **data type**.
316OFX currently has pixels with one (A), three (RGB) or four components
317(RGBA), which can be bytes, shorts, or a 32 bit floats.
318
319.. figure:: Pics/dataLayout.jpg
320    :scale: 50 %
321    :align: center
322    :alt: Image Data Layout
323
324
325The figure above shows a small (3x4) image containing RGBA pixels. OFX
326returns a ``void *`` data pointer to the first component of the bottom
327left pixel in the image, which will be at (X1, Y1) on the image plane.
328Memory addresses increase left to right across the row of an OFX image,
329with all components and pixels hard packed and contiguous within that
330row.
331
332Rows may or may not be contiguous in memory, so in our example the
333address of component **R** at row 1 column 0, may or may not come
334directly after component **A** at (2, 0). To manage this we use "row
335bytes", which are the byte offset between rows, (**not** pixel or
336component offsets). By breaking this offset out, hosts can more easily
337map their pixel data into OFX images without having to copy. For example
338a host that natively runs with Y down and packs images with the top row
339first in memory would use negative row bytes and have the data pointer
340point to it’s last row (which is the bottom row).
341
342Pixel Address Calculation
343-------------------------
344
345So, given a coordinate on the image plane how do you calculate the
346address of a pixel in the image? Well you use the following information:
347
348-  a ``void*`` pointer to the bottom left corner of the image
349
350-  four integers that define the **bounds** of the image for which there
351   is data
352
353-  the data type of each component
354
355-  the type of each pixel (which yields the number of components per
356   pixel)
357
358-  the number of bytes that is the offset between rows
359
360The code snippet below shows you how to use all that to find the address
361of a pixel whose coordinates are on the image plane.
362
363`invert.cpp <https://github.com/ofxa/openfx/blob/doc/Documentation/sources/Guide/Code/Example2/invert.cpp#L216>`_
364
365.. code:: c++
366
367      // Look up a pixel in the image. returns null if the pixel was not
368      // in the bounds of the image
369      template <class T>
370      static inline T * pixelAddress(int x, int y,
371                                     void *baseAddress,
372                                     OfxRectI bounds,
373                                     int rowBytes,
374                                     int nCompsPerPixel)
375      {
376        // Inside the bounds of this image?
377        if(x < bounds.x1 || x >= bounds.x2 || y < bounds.y1 || y >= bounds.y2)
378          return NULL;
379
380        // turn image plane coordinates into offsets from the bottom left
381        int yOffset = y - bounds.y1;
382        int xOffset = x - bounds.x1;
383
384        // Find the start of our row, using byte arithmetic
385        void *rowStartAsVoid = reinterpret_cast<char *>(baseAddress) + yOffset * rowBytes;
386
387        // turn the row start into a pointer to our data type
388        T *rowStart = reinterpret_cast<T *>(rowStartAsVoid);
389
390        // finally find the position of the first component of column
391        return rowStart + (xOffset * nCompsPerPixel);
392      }
393
394You will notice it is a templated function, where **T** will be
395instantiated with the appropriate component type by other code.
396**T** will be one of ``unsigned char``, ``unsigned short``
397or ``float``.
398
399In order the function…
400
401-  checks if the pixel coordinate is within the bounds of the image. If
402   it is not then we have no addressable pixel data at the point, so the
403   function gives up and return NULL as an indication of that,
404
405-  as we have ``x`` and ``y`` as coordinates on the *image
406   plane*, it then turn the coordinates into offsets from the bottom
407   left of the image with a simple subtraction,
408
409-  it then finds the start of the row we are interested in by scaling
410   our local y offset by ``rowBytes`` to figure the offset from our
411   base address data pointer, *in bytes*. It adds that to the base
412   address and now has the start of our row.
413
414-  it turns the raw address at the start of the row into a pointer of
415   our data type,
416
417-  finally it offsets to the correct column by skippying over *xLocal*
418   number of pixels, each of each which contain ``nComponents``.
419
420
421Images Are Property Sets
422------------------------
423
424Images are property sets, you access all the data needed via the
425standard OFX property mechanism. This has allowed us to expand the
426information in an image and be 100% backwards compatible to existing
427hosts and plugins.
428
429Anyway, here is code from our example using the property mechanism to
430get the required data from an image…
431
432`invert.cpp <https://github.com/ofxa/openfx/blob/doc/Documentation/sources/Guide/Code/Example2/invert.cpp#L242>`_
433
434.. code:: c++
435
436     template <class T, int MAX>
437      void PixelProcessing(OfxImageEffectHandle instance,
438                           OfxPropertySetHandle sourceImg,
439                           OfxPropertySetHandle outputImg,
440                           OfxRectI renderWindow,
441                           int nComps)
442      {
443    ...
444        // fetch output image info from the property handle
445        int dstRowBytes;
446        OfxRectI dstBounds;
447        void *dstPtr = NULL;
448        gPropertySuite->propGetInt(outputImg, kOfxImagePropRowBytes, 0, &dstRowBytes);
449        gPropertySuite->propGetIntN(outputImg, kOfxImagePropBounds, 4, &dstBounds.x1);
450        gPropertySuite->propGetPointer(outputImg, kOfxImagePropData, 0, &dstPtr);
451
452    ...
453      }
454
455
456      OfxStatus RenderAction( OfxImageEffectHandle instance,
457                              OfxPropertySetHandle inArgs,
458                              OfxPropertySetHandle outArgs)
459      {
460    ...
461          // figure out the component type
462          char *cstr;
463          gPropertySuite->propGetString(outputImg, kOfxImageEffectPropComponents, 0, &cstr);
464          std::string components = cstr;
465
466    ...
467          // figure out the data types
468          gPropertySuite->propGetString(outputImg, kOfxImageEffectPropPixelDepth, 0, &cstr);
469          std::string dataType = cstr;
470    ...
471    }
472
473There are many more properties in an image, but we won’t need them for
474this simple example and they’ll be covered in other tutorials.
475
476.. _the_render_action:
477
478The Render Action
479=================
480
481As stated above, the render action is the one used to get a plugin to
482actually process images. I’ll go through it in stages rather than have
483one big listing.
484
485`invert.cpp <https://github.com/ofxa/openfx/blob/doc/Documentation/sources/Guide/Code/Example2/invert.cpp#L310>`_
486
487.. code:: c++
488
489      ////////////////////////////////////////////////////////////////////////////////
490      // Render an output image
491      OfxStatus RenderAction( OfxImageEffectHandle instance,
492                              OfxPropertySetHandle inArgs,
493                              OfxPropertySetHandle outArgs)
494      {
495        // get the render window and the time from the inArgs
496        OfxTime time;
497        OfxRectI renderWindow;
498        OfxStatus status = kOfxStatOK;
499
500        gPropertySuite->propGetDouble(inArgs, kOfxPropTime, 0, &time);
501        gPropertySuite->propGetIntN(inArgs, kOfxImageEffectPropRenderWindow, 4, &renderWindow.x1);
502
503This first listing shows how the **inArgs** are being used to say what
504exactly to render. The property :c:macro:`kOfxPropTime` on **inArgs** is
505the frame of the output clip to render. The property
506:c:macro:`kOfxImageEffectPropRenderWindow` is the region that should be
507written to.
508
509The output image (which will be fetched later on) will have a **bounds**
510that are at least as big as the render window. The bounds of the output
511image could infact be larger. This could happen if a host is
512simultaneously calling the render action in separate threads to perform
513symmetric multi-processing, each thread would be given a different
514render window to fill in of the larger output image.
515
516.. note::
517
518    A plugin can have multiple actions being simultaneously in separate
519    threads, especially the render action. Do not rely on any local
520    state if you can help it. You can control how threading works in the
521    describe actions.
522
523.. note::
524
525    To allow a plugin to be called in an SMP manner, or have multiple
526    instances simultaneously rendering, the API has been designed so
527    that the plugin does not rely on any implicit state, such as time,
528    everything is explicit.
529
530`invert.cpp <https://github.com/ofxa/openfx/blob/doc/Documentation/sources/Guide/Code/Example2/invert.cpp#L323>`_
531
532.. code:: c++
533
534        // fetch output clip
535        OfxImageClipHandle outputClip;
536        gImageEffectSuite->clipGetHandle(instance, "Output", &outputClip, NULL);
537
538        // fetch main input clip
539        OfxImageClipHandle sourceClip;
540        gImageEffectSuite->clipGetHandle(instance, "Source", &sourceClip, NULL);
541
542This next snippet fetches two clip handles by name from the instance,
543using the image effect suite.  [2]_
544
545`invert.cpp <https://github.com/ofxa/openfx/blob/doc/Documentation/sources/Guide/Code/Example2/invert.cpp#L331>`_
546
547.. code:: c++
548
549        // the property sets holding our images
550        OfxPropertySetHandle outputImg = NULL, sourceImg = NULL;
551        try {
552          // fetch image to render into from that clip
553          OfxPropertySetHandle outputImg;
554          if(gImageEffectSuite->clipGetImage(outputClip, time, NULL, &outputImg) != kOfxStatOK) {
555            throw " no output image!";
556          }
557
558          // fetch image at render time from that clip
559          if (gImageEffectSuite->clipGetImage(sourceClip, time, NULL, &sourceImg) != kOfxStatOK) {
560            throw " no source image!";
561          }
562
563We now (inside a try/catch block) fetch two images from the clips, again
564using the image effect suite. Note we are asking for images at the frame
565we were told to render. Effects that need images from other frames can
566pass in different values to :cpp:func:`OfxImageEffectSuiteV1::clipGetImage`, but will need to trap
567more actions than we have to make that all work correctly.
568
569We will be given back two property set handles which represent our
570images. If the call failed (which could be for a variety of good
571reasons) we give up with a ``throw``.
572
573`invert.cpp <https://github.com/ofxa/openfx/blob/doc/Documentation/sources/Guide/Code/Example2/invert.cpp#L345>`_
574
575.. code:: c++
576
577          // figure out the data types
578          char *cstr;
579          gPropertySuite->propGetString(outputImg, kOfxImageEffectPropComponents, 0, &cstr);
580          std::string components = cstr;
581
582          // how many components per pixel?
583          int nComps = 0;
584          if(components == kOfxImageComponentRGBA) {
585            nComps = 4;
586          }
587          else if(components == kOfxImageComponentRGB) {
588            nComps = 3;
589          }
590          else if(components == kOfxImageComponentAlpha) {
591            nComps = 1;
592          }
593          else {
594            throw " bad pixel type!";
595          }
596
597Now we want to know what’s inside our image’s pixels, so we can
598correctly process it. We ask what components are present in the output
599image. Because we have left certain settings at the default, the source
600and output images will always have the same number of components and the
601same data types. Which is why we aren’t checking for the source for its
602pixel information.
603
604`invert.cpp <https://github.com/ofxa/openfx/blob/doc/Documentation/sources/Guide/Code/Example2/invert.cpp#L365>`_
605
606.. code:: c++
607
608          // now do our render depending on the data type
609          gPropertySuite->propGetString(outputImg, kOfxImageEffectPropPixelDepth, 0, &cstr);
610          std::string dataType = cstr;
611
612          if(dataType == kOfxBitDepthByte) {
613            PixelProcessing<unsigned char, 255>(instance, sourceImg, outputImg, renderWindow, nComps);
614          }
615          else if(dataType == kOfxBitDepthShort) {
616            PixelProcessing<unsigned short, 65535>(instance, sourceImg, outputImg, renderWindow, nComps);
617          }
618          else if (dataType == kOfxBitDepthFloat) {
619            PixelProcessing<float, 1>(instance, sourceImg, outputImg, renderWindow, nComps);
620          }
621          else {
622            throw " bad data type!";
623            throw 1;
624          }
625
626Now we are enquiring as to what C type the components our image will be.
627Again throwing if something has gone wrong. We use the data type to
628correctly instantiate our templated function which will do the grunt
629work of iterating over pixels. Note also that it is passing the nominal
630maximum value of the data type as a template argument.
631
632`invert.cpp <https://github.com/ofxa/openfx/blob/doc/Documentation/sources/Guide/Code/Example2/invert.cpp#L383>`_
633
634.. code:: c++
635
636        }
637        catch(const char *errStr ) {
638          bool isAborting = gImageEffectSuite->abort(instance);
639
640          // if we were interrupted, the failed fetch is fine, just return kOfxStatOK
641          // otherwise, something weird happened
642          if(!isAborting) {
643            status = kOfxStatFailed;
644          }
645          ERROR_IF(!isAborting, " Rendering failed because %s", errStr);
646
647        }
648
649        if(sourceImg)
650          gImageEffectSuite->clipReleaseImage(sourceImg);
651        if(outputImg)
652          gImageEffectSuite->clipReleaseImage(outputImg);
653
654        // all was well
655        return status;
656      }
657
658This last bit is basically clean up. We have the ``catch`` for our
659try/catch block. The first thing it does is ask the host application is
660the effect being told to stop by calling the :cpp:func:`OfxImageEffectSuiteV1::abort` function on
661the effect suite. We might have ended up in the catch block because the
662an image could not be fetched, if that was a side effect of the host
663interrupting processing, it is *not* counted as an error. So we check
664that before we return a failed error state from our action.
665
666Finally we release the images we have fetched and return the error
667status.
668
669.. note::
670
671    Images should not be held onto outside the scope of the action they
672    were fetched in, the data will not be guaranteed to be valid. It is
673    polite to release them as soon as possible, especially if you are
674    fetching multiple images on input.
675
676Now for our pixel pushing code. [3]_
677
678`invert.cpp <https://github.com/ofxa/openfx/blob/doc/Documentation/sources/Guide/Code/Example2/invert.cpp#L242>`_
679
680.. code:: c++
681
682      // iterate over our pixels and process them
683      template <class T, int MAX>
684      void PixelProcessing(OfxImageEffectHandle instance,
685                           OfxPropertySetHandle sourceImg,
686                           OfxPropertySetHandle outputImg,
687                           OfxRectI renderWindow,
688                           int nComps)
689      {
690        // fetch output image info from the property handle
691        int dstRowBytes;
692        OfxRectI dstBounds;
693        void *dstPtr = NULL;
694        gPropertySuite->propGetInt(outputImg, kOfxImagePropRowBytes, 0, &dstRowBytes);
695        gPropertySuite->propGetIntN(outputImg, kOfxImagePropBounds, 4, &dstBounds.x1);
696        gPropertySuite->propGetPointer(outputImg, kOfxImagePropData, 0, &dstPtr);
697
698        if(dstPtr == NULL) {
699          throw "Bad destination pointer";
700        }
701
702        // fetch input image info from the property handle
703        int srcRowBytes;
704        OfxRectI srcBounds;
705        void *srcPtr = NULL;
706        gPropertySuite->propGetInt(sourceImg, kOfxImagePropRowBytes, 0, &srcRowBytes);
707        gPropertySuite->propGetIntN(sourceImg, kOfxImagePropBounds, 4, &srcBounds.x1);
708        gPropertySuite->propGetPointer(sourceImg, kOfxImagePropData, 0, &srcPtr);
709
710        if(srcPtr == NULL) {
711          throw "Bad source pointer";
712        }
713
714We’ve shown bits of this before. Here we have a templated function that
715we use to process our pixels. It is templated on the data type that the
716components in each pixel will be, as well as a nominal *max* value to
717use in our invert computation.
718
719The first thing it does is to pull out the bounds, rowbytes and
720destination pointer of our two images. We can now iterate over the
721render window and set pixels in the output image.
722
723`invert.cpp <https://github.com/ofxa/openfx/blob/doc/Documentation/sources/Guide/Code/Example2/invert.cpp#L273>`_
724
725.. code:: c++
726
727        // and do some inverting
728        for(int y = renderWindow.y1; y < renderWindow.y2; y++) {
729          if(y % 20 == 0 && gImageEffectSuite->abort(instance)) break;
730
731          // get the row start for the output image
732          T *dstPix = pixelAddress<T>(renderWindow.x1, y, dstPtr, dstBounds, dstRowBytes, nComps);
733
734          for(int x = renderWindow.x1; x < renderWindow.x2; x++) {
735
736            // get the source pixel
737            T *srcPix = pixelAddress<T>(x, y, srcPtr, srcBounds, srcRowBytes, nComps);
738
739            if(srcPix) {
740              // we have one, iterate each component in the pixels
741              for(int i = 0; i < nComps; ++i) {
742                if(i != 3) { // We don't invert alpha.
743                  *dstPix = MAX - *srcPix; // invert
744                }
745                else {
746                  *dstPix = *srcPix;
747                }
748                ++dstPix; ++srcPix;
749              }
750            }
751            else {
752              // we don't have a pixel in the source image, set output to black
753              for(int i = 0; i < nComps; ++i) {
754                *dstPix = 0;
755                ++dstPix;
756              }
757            }
758          }
759        }
760      }
761
762The first thing we do at each row we are processing is to check that the
763host hasn’t told our plugin to abort processing. (Ideally you can do
764this a bit less often than every line). We only to this every 20th row,
765as the overhead on the host side to check for an abort might be quite
766high.
767
768The next thing we do is to use the ``pixelAddress`` function to find
769the address of the first component of the first pixel in the current,
770and we put it in ``dstPix``. Because we have a guarantee that the
771bounds of the output image are at least as big as the render window, we
772can simply increment ``dstPix`` across the row as we iterate over
773the image.
774
775Now we iterate across the row. We attempt to fetch the address of the
776source pixel at our x,y location in the image plane. If we get it we
777iterate over the number of component, setting the output to be the
778invert  [4]_ of the input. If we don’t get it, we set the output pixel
779to all zero.
780
781.. note::
782
783    You notice that we are continually calculating the address of
784    ``srcPix`` at each pixel location and not incrementing the
785    pointer as we could with ``dstPix``. The reason for this is
786    that, at the default settings, there is no guarantee as to the
787    bounds of the input image. It need not be congruent with any other
788    input, the output or the render window.
789
790I could obviously write this much more efficiently and avoid the
791continual address calculation. However for illustrative purposes I
792haven’t done that.
793
794Summary
795=======
796
797This plugin has shown you the basics of working with OFX images, the
798main things it illustrated were…
799
800-  what are :ref:`clips <clips>` and how we get images from clips,
801
802-  how :ref:`images <images>` are laid out in memory and how to
803   access pixels,
804
805-  the basics of the :ref:`render action <the_render_action>`
806
807.. [1]
808   I won’t bother going into the boot strapping boiler plate, if you are
809   interested you can look at the source directly.
810
811.. [2]
812   The **NULL** at the end could have been the address of a property set
813   handle if the effect needed to enquire about the clips properties.
814
815.. [3]
816   This is purely illustrative as to how the API works, it is in no way
817   fast code, I would be ashamed to put code like this into a serious
818   piece of image processing.
819
820.. [4]
821   complement really
822
823
824