1 /* ***** BEGIN LICENSE BLOCK *****
2  * This file is part of openfx-misc <https://github.com/devernay/openfx-misc>,
3  * Copyright (C) 2015 INRIA
4  *
5  * openfx-misc is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * openfx-misc is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with openfx-misc.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>
17  * ***** END LICENSE BLOCK ***** */
18 
19 /*
20  * OFX MixableFilter plugin.
21  */
22 
23 #include <cmath>
24 #include <algorithm>
25 //#include <iostream>
26 #ifdef _WINDOWS
27 #include <windows.h>
28 #endif
29 
30 #include "ofxsProcessing.H"
31 #include "ofxsMaskMix.h"
32 #include "ofxsCoords.h"
33 #include "ofxsMacros.h"
34 
35 using namespace OFX;
36 
37 OFXS_NAMESPACE_ANONYMOUS_ENTER
38 
39 #define kPluginName "MixableFilter"
40 #define kPluginGrouping "Filter"
41 #define kPluginDescription "A generic maskable filter template."
42 #define kPluginIdentifier "net.sf.openfx.MixableFilter"
43 // History:
44 // version 1.0: initial version
45 #define kPluginVersionMajor 1 // Incrementing this number means that you have broken backwards compatibility of the plug-in.
46 #define kPluginVersionMinor 0 // Increment this when you have fixed a bug or made it faster.
47 
48 #define kSupportsTiles 1
49 #define kSupportsMultiResolution 1
50 #define kSupportsRenderScale 1
51 #define kSupportsMultipleClipPARs false
52 #define kSupportsMultipleClipDepths false
53 #define kRenderThreadSafety eRenderFullySafe
54 
55 #ifdef OFX_EXTENSIONS_NATRON
56 #define kParamProcessR kNatronOfxParamProcessR
57 #define kParamProcessRLabel kNatronOfxParamProcessRLabel
58 #define kParamProcessRHint kNatronOfxParamProcessRHint
59 #define kParamProcessG kNatronOfxParamProcessG
60 #define kParamProcessGLabel kNatronOfxParamProcessGLabel
61 #define kParamProcessGHint kNatronOfxParamProcessGHint
62 #define kParamProcessB kNatronOfxParamProcessB
63 #define kParamProcessBLabel kNatronOfxParamProcessBLabel
64 #define kParamProcessBHint kNatronOfxParamProcessBHint
65 #define kParamProcessA kNatronOfxParamProcessA
66 #define kParamProcessALabel kNatronOfxParamProcessALabel
67 #define kParamProcessAHint kNatronOfxParamProcessAHint
68 #else
69 #define kParamProcessR      "processR"
70 #define kParamProcessRLabel "R"
71 #define kParamProcessRHint  "Process red component."
72 #define kParamProcessG      "processG"
73 #define kParamProcessGLabel "G"
74 #define kParamProcessGHint  "Process green component."
75 #define kParamProcessB      "processB"
76 #define kParamProcessBLabel "B"
77 #define kParamProcessBHint  "Process blue component."
78 #define kParamProcessA      "processA"
79 #define kParamProcessALabel "A"
80 #define kParamProcessAHint  "Process alpha component."
81 #endif
82 
83 #ifdef OFX_EXTENSIONS_NATRON
84 #define OFX_COMPONENTS_OK(c) ((c)== ePixelComponentAlpha || (c) == ePixelComponentXY || (c) == ePixelComponentRGB || (c) == ePixelComponentRGBA)
85 #else
86 #define OFX_COMPONENTS_OK(c) ((c)== ePixelComponentAlpha || (c) == ePixelComponentRGB || (c) == ePixelComponentRGBA)
87 #endif
88 
89 
90 using namespace OFX;
91 
92 class MixableFilterProcessorBase
93     : public ImageProcessor
94 {
95 protected:
96     const Image *_srcImg;
97     bool _premult;
98     int _premultChannel;
99     double _mix;
100     bool _processR, _processG, _processB, _processA;
101     // TODO: add plugin parameter values
102 
103 public:
MixableFilterProcessorBase(ImageEffect & instance,const RenderArguments &)104     MixableFilterProcessorBase(ImageEffect &instance,
105                                const RenderArguments & /*args*/)
106         : ImageProcessor(instance)
107         , _srcImg(NULL)
108         , _premult(false)
109         , _premultChannel(3)
110         , _mix(1.)
111         , _processR(false)
112         , _processG(false)
113         , _processB(false)
114         , _processA(false)
115         // TODO: initialize plugin parameter values
116     {
117     }
118 
setSrcImg(const Image * v)119     void setSrcImg(const Image *v) {_srcImg = v; }
120 
setValues(bool premult,int premultChannel,double mix,bool processR,bool processG,bool processB,bool processA)121     void setValues(bool premult,
122                    int premultChannel,
123                    double mix,
124                    bool processR,
125                    bool processG,
126                    bool processB,
127                    bool processA
128                    // TODO: add plugin parameters
129                    )
130     {
131         _premult = premult;
132         _premultChannel = premultChannel;
133         _mix = mix;
134         _processR = processR;
135         _processG = processG;
136         _processB = processB;
137         _processA = processA;
138         // TODO: set plugin parameter values
139     }
140 };
141 
142 
143 template <class PIX, int nComponents, int maxValue>
144 class MixableFilterProcessor
145     : public MixableFilterProcessorBase
146 {
147 public:
MixableFilterProcessor(ImageEffect & instance,const RenderArguments & args)148     MixableFilterProcessor(ImageEffect &instance,
149                            const RenderArguments &args)
150         : MixableFilterProcessorBase(instance, args)
151     {
152         //const double time = args.time;
153 
154         // TODO: any pre-computation goes here (such as computing a LUT)
155     }
156 
multiThreadProcessImages(OfxRectI procWindow)157     void multiThreadProcessImages(OfxRectI procWindow)
158     {
159 #     ifndef __COVERITY__ // too many coverity[dead_error_line] errors
160         const bool r = _processR && (nComponents != 1);
161         const bool g = _processG && (nComponents >= 2);
162         const bool b = _processB && (nComponents >= 3);
163         const bool a = _processA && (nComponents == 1 || nComponents == 4);
164         if (r) {
165             if (g) {
166                 if (b) {
167                     if (a) {
168                         return process<true, true, true, true >(procWindow); // RGBA
169                     } else {
170                         return process<true, true, true, false>(procWindow); // RGBa
171                     }
172                 } else {
173                     if (a) {
174                         return process<true, true, false, true >(procWindow); // RGbA
175                     } else {
176                         return process<true, true, false, false>(procWindow); // RGba
177                     }
178                 }
179             } else {
180                 if (b) {
181                     if (a) {
182                         return process<true, false, true, true >(procWindow); // RgBA
183                     } else {
184                         return process<true, false, true, false>(procWindow); // RgBa
185                     }
186                 } else {
187                     if (a) {
188                         return process<true, false, false, true >(procWindow); // RgbA
189                     } else {
190                         return process<true, false, false, false>(procWindow); // Rgba
191                     }
192                 }
193             }
194         } else {
195             if (g) {
196                 if (b) {
197                     if (a) {
198                         return process<false, true, true, true >(procWindow); // rGBA
199                     } else {
200                         return process<false, true, true, false>(procWindow); // rGBa
201                     }
202                 } else {
203                     if (a) {
204                         return process<false, true, false, true >(procWindow); // rGbA
205                     } else {
206                         return process<false, true, false, false>(procWindow); // rGba
207                     }
208                 }
209             } else {
210                 if (b) {
211                     if (a) {
212                         return process<false, false, true, true >(procWindow); // rgBA
213                     } else {
214                         return process<false, false, true, false>(procWindow); // rgBa
215                     }
216                 } else {
217                     if (a) {
218                         return process<false, false, false, true >(procWindow); // rgbA
219                     } else {
220                         return process<false, false, false, false>(procWindow); // rgba
221                     }
222                 }
223             }
224         }
225 #     endif // ifndef __COVERITY__
226     } // multiThreadProcessImages
227 
228 private:
229 
230 
231     template<bool processR, bool processG, bool processB, bool processA>
process(OfxRectI procWindow)232     void process(OfxRectI procWindow)
233     {
234         assert( (!processR && !processG && !processB) || (nComponents == 3 || nComponents == 4) );
235         assert( !processA || (nComponents == 1 || nComponents == 4) );
236         assert(nComponents == 3 || nComponents == 4);
237         float unpPix[4];
238         float tmpPix[4];
239         for (int y = procWindow.y1; y < procWindow.y2; y++) {
240             if ( _effect.abort() ) {
241                 break;
242             }
243 
244             PIX *dstPix = (PIX *) _dstImg->getPixelAddress(procWindow.x1, y);
245             for (int x = procWindow.x1; x < procWindow.x2; x++) {
246                 const PIX *srcPix = (const PIX *)  (_srcImg ? _srcImg->getPixelAddress(x, y) : 0);
247                 ofxsUnPremult<PIX, nComponents, maxValue>(srcPix, unpPix, _premult, _premultChannel);
248                 double t_r = unpPix[0];
249                 double t_g = unpPix[1];
250                 double t_b = unpPix[2];
251                 double t_a = unpPix[3];
252 
253                 // TODO: process the pixel (the actual computation goes here)
254                 t_r = 1. - t_r;
255                 t_g = 1. - t_g;
256                 t_b = 1. - t_b;
257 
258                 tmpPix[0] = (float)t_r;
259                 tmpPix[1] = (float)t_g;
260                 tmpPix[2] = (float)t_b;
261                 tmpPix[3] = (float)t_a;
262                 ofxsPremultMixPix<PIX, nComponents, maxValue>(tmpPix, _premult, _premultChannel, srcPix, (float)_mix, dstPix);
263                 // copy back original values from unprocessed channels
264                 if (nComponents == 1) {
265                     if (!processA) {
266                         dstPix[0] = srcPix ? srcPix[0] : PIX();
267                     }
268                 } else if ( (nComponents == 3) || (nComponents == 4) ) {
269                     if (!processR) {
270                         dstPix[0] = srcPix ? srcPix[0] : PIX();
271                     }
272                     if (!processG) {
273                         dstPix[1] = srcPix ? srcPix[1] : PIX();
274                     }
275                     if (!processB) {
276                         dstPix[2] = srcPix ? srcPix[2] : PIX();
277                     }
278                     if ( !processA && (nComponents == 4) ) {
279                         dstPix[3] = srcPix ? srcPix[3] : PIX();
280                     }
281                 }
282                 // increment the dst pixel
283                 dstPix += nComponents;
284             }
285         }
286     } // process
287 };
288 
289 ////////////////////////////////////////////////////////////////////////////////
290 /** @brief The plugin that does our work */
291 class MixableFilterPlugin
292     : public ImageEffect
293 {
294 public:
295 
296     /** @brief ctor */
MixableFilterPlugin(OfxImageEffectHandle handle)297     MixableFilterPlugin(OfxImageEffectHandle handle)
298         : ImageEffect(handle)
299         , _dstClip(NULL)
300         , _srcClip(NULL)
301         , _processR(NULL)
302         , _processG(NULL)
303         , _processB(NULL)
304         , _processA(NULL)
305         , _premult(NULL)
306         , _premultChannel(NULL)
307         , _mix(NULL)
308     {
309         _dstClip = fetchClip(kOfxImageEffectOutputClipName);
310         assert( _dstClip && (_dstClip->getPixelComponents() == ePixelComponentRGB ||
311                              _dstClip->getPixelComponents() == ePixelComponentRGBA ||
312                              _dstClip->getPixelComponents() == ePixelComponentAlpha) );
313         _srcClip = getContext() == eContextGenerator ? NULL : fetchClip(kOfxImageEffectSimpleSourceClipName);
314         assert( (!_srcClip && getContext() == eContextGenerator) ||
315                 ( _srcClip && (_srcClip->getPixelComponents() == ePixelComponentRGB ||
316                                _srcClip->getPixelComponents() == ePixelComponentRGBA ||
317                                _srcClip->getPixelComponents() == ePixelComponentAlpha) ) );
318 
319         // TODO: fetch noise parameters
320 
321         _premult = fetchBooleanParam(kParamPremult);
322         _premultChannel = fetchChoiceParam(kParamPremultChannel);
323         assert(_premult && _premultChannel);
324         _mix = fetchDoubleParam(kParamMix);
325         assert(_mix);
326 
327         _processR = fetchBooleanParam(kParamProcessR);
328         _processG = fetchBooleanParam(kParamProcessG);
329         _processB = fetchBooleanParam(kParamProcessB);
330         _processA = fetchBooleanParam(kParamProcessA);
331         assert(_processR && _processG && _processB && _processA);
332     }
333 
334 private:
335     /* Override the render */
336     virtual void render(const RenderArguments &args) OVERRIDE FINAL;
337 
338     template<int nComponents>
339     void renderForComponents(const RenderArguments &args);
340 
341     template <class PIX, int nComponents, int maxValue>
342     void renderForBitDepth(const RenderArguments &args);
343 
344     /* set up and run a processor */
345     void setupAndProcess(MixableFilterProcessorBase &, const RenderArguments &args);
346 
347     virtual bool isIdentity(const IsIdentityArguments &args, Clip * &identityClip, double &identityTime, int& view, std::string& plane) OVERRIDE FINAL;
348 
349     /** @brief called when a clip has just been changed in some way (a rewire maybe) */
350     virtual void changedClip(const InstanceChangedArgs &args, const std::string &clipName) OVERRIDE FINAL;
351 
352 private:
353     // do not need to delete these, the ImageEffect is managing them for us
354     Clip *_dstClip;
355     Clip *_srcClip;
356     BooleanParam* _processR;
357     BooleanParam* _processG;
358     BooleanParam* _processB;
359     BooleanParam* _processA;
360     BooleanParam* _premult;
361     ChoiceParam* _premultChannel;
362     DoubleParam* _mix;
363 };
364 
365 
366 ////////////////////////////////////////////////////////////////////////////////
367 /** @brief render for the filter */
368 
369 ////////////////////////////////////////////////////////////////////////////////
370 // basic plugin render function, just a skelington to instantiate templates from
371 
372 /* set up and run a processor */
373 void
setupAndProcess(MixableFilterProcessorBase & processor,const RenderArguments & args)374 MixableFilterPlugin::setupAndProcess(MixableFilterProcessorBase &processor,
375                                      const RenderArguments &args)
376 {
377     const double time = args.time;
378 
379     auto_ptr<Image> dst( _dstClip->fetchImage(time) );
380 
381     if ( !dst.get() ) {
382         throwSuiteStatusException(kOfxStatFailed);
383     }
384     BitDepthEnum dstBitDepth    = dst->getPixelDepth();
385     PixelComponentEnum dstComponents  = dst->getPixelComponents();
386     if ( ( dstBitDepth != _dstClip->getPixelDepth() ) ||
387          ( dstComponents != _dstClip->getPixelComponents() ) ) {
388         setPersistentMessage(Message::eMessageError, "", "OFX Host gave image with wrong depth or components");
389         throwSuiteStatusException(kOfxStatFailed);
390     }
391     if ( (dst->getRenderScale().x != args.renderScale.x) ||
392          ( dst->getRenderScale().y != args.renderScale.y) ||
393          ( ( dst->getField() != eFieldNone) /* for DaVinci Resolve */ && ( dst->getField() != args.fieldToRender) ) ) {
394         setPersistentMessage(Message::eMessageError, "", "OFX Host gave image with wrong scale or field properties");
395         throwSuiteStatusException(kOfxStatFailed);
396     }
397     auto_ptr<const Image> src( ( _srcClip && _srcClip->isConnected() ) ?
398                                     _srcClip->fetchImage(time) : 0 );
399     if ( src.get() ) {
400         if ( (src->getRenderScale().x != args.renderScale.x) ||
401              ( src->getRenderScale().y != args.renderScale.y) ||
402              ( ( src->getField() != eFieldNone) /* for DaVinci Resolve */ && ( src->getField() != args.fieldToRender) ) ) {
403             setPersistentMessage(Message::eMessageError, "", "OFX Host gave image with wrong scale or field properties");
404             throwSuiteStatusException(kOfxStatFailed);
405         }
406         BitDepthEnum srcBitDepth      = src->getPixelDepth();
407         PixelComponentEnum srcComponents = src->getPixelComponents();
408         if ( (srcBitDepth != dstBitDepth) || (srcComponents != dstComponents) ) {
409             throwSuiteStatusException(kOfxStatErrImageFormat);
410         }
411     }
412 
413     processor.setDstImg( dst.get() );
414     processor.setSrcImg( src.get() );
415     processor.setRenderWindow(args.renderWindow);
416 
417     // TODO: fetch noise parameter values
418 
419     bool premult;
420     int premultChannel;
421     _premult->getValueAtTime(time, premult);
422     _premultChannel->getValueAtTime(time, premultChannel);
423     double mix;
424     _mix->getValueAtTime(time, mix);
425 
426     bool processR, processG, processB, processA;
427     _processR->getValueAtTime(time, processR);
428     _processG->getValueAtTime(time, processG);
429     _processB->getValueAtTime(time, processB);
430     _processA->getValueAtTime(time, processA);
431 
432     processor.setValues(premult, premultChannel, mix,
433                         processR, processG, processB, processA);
434     processor.process();
435 } // MixableFilterPlugin::setupAndProcess
436 
437 // the overridden render function
438 void
render(const RenderArguments & args)439 MixableFilterPlugin::render(const RenderArguments &args)
440 {
441     //std::cout << "render!\n";
442     // instantiate the render code based on the pixel depth of the dst clip
443     PixelComponentEnum dstComponents  = _dstClip->getPixelComponents();
444 
445     assert( kSupportsMultipleClipPARs   || !_srcClip || _srcClip->getPixelAspectRatio() == _dstClip->getPixelAspectRatio() );
446     assert( kSupportsMultipleClipDepths || !_srcClip || _srcClip->getPixelDepth()       == _dstClip->getPixelDepth() );
447     assert(OFX_COMPONENTS_OK(dstComponents));
448     // do the rendering
449     switch (dstComponents) {
450     case ePixelComponentRGBA:
451         renderForComponents<4>(args);
452         break;
453     case ePixelComponentRGB:
454         renderForComponents<3>(args);
455         break;
456 #ifdef OFX_EXTENSIONS_NATRON
457     case ePixelComponentXY:
458         renderForComponents<2>(args);
459         break;
460 #endif
461     case ePixelComponentAlpha:
462         renderForComponents<1>(args);
463         break;
464     default:
465         //std::cout << "components usupported\n";
466         throwSuiteStatusException(kOfxStatErrUnsupported);
467         break;
468     } // switch
469       //std::cout << "render! OK\n";
470 }
471 
472 template<int nComponents>
473 void
renderForComponents(const RenderArguments & args)474 MixableFilterPlugin::renderForComponents(const RenderArguments &args)
475 {
476     BitDepthEnum dstBitDepth    = _dstClip->getPixelDepth();
477 
478     switch (dstBitDepth) {
479     case eBitDepthUByte:
480         renderForBitDepth<unsigned char, nComponents, 255>(args);
481         break;
482 
483     case eBitDepthUShort:
484         renderForBitDepth<unsigned short, nComponents, 65535>(args);
485         break;
486 
487     case eBitDepthFloat:
488         renderForBitDepth<float, nComponents, 1>(args);
489         break;
490     default:
491         //std::cout << "depth usupported\n";
492         throwSuiteStatusException(kOfxStatErrUnsupported);
493     }
494 }
495 
496 template <class PIX, int nComponents, int maxValue>
497 void
renderForBitDepth(const RenderArguments & args)498 MixableFilterPlugin::renderForBitDepth(const RenderArguments &args)
499 {
500     MixableFilterProcessor<PIX, nComponents, maxValue> fred(*this, args);
501     setupAndProcess(fred, args);
502 }
503 
504 bool
isIdentity(const IsIdentityArguments & args,Clip * & identityClip,double &,int &,std::string &)505 MixableFilterPlugin::isIdentity(const IsIdentityArguments &args,
506                                 Clip * &identityClip,
507                                 double & /*identityTime*/
508                                 , int& /*view*/, std::string& /*plane*/)
509 {
510     //std::cout << "isIdentity!\n";
511     const double time = args.time;
512     double mix;
513 
514     _mix->getValueAtTime(time, mix);
515 
516     if (mix == 0. /*|| (!processR && !processG && !processB && !processA)*/) {
517         identityClip = _srcClip;
518 
519         return true;
520     }
521 
522     {
523         bool processR;
524         bool processG;
525         bool processB;
526         bool processA;
527         _processR->getValueAtTime(time, processR);
528         _processG->getValueAtTime(time, processG);
529         _processB->getValueAtTime(time, processB);
530         _processA->getValueAtTime(time, processA);
531         if (!processR && !processG && !processB && !processA) {
532             identityClip = _srcClip;
533 
534             return true;
535         }
536     }
537 
538     // TODO: which plugin parameter values give identity?
539     //if (...) {
540     //    identityClip = _srcClip;
541     //    //std::cout << "isIdentity! true\n";
542     //    return true;
543     //}
544 
545     //std::cout << "isIdentity! false\n";
546     return false;
547 } // MixableFilterPlugin::isIdentity
548 
549 void
changedClip(const InstanceChangedArgs & args,const std::string & clipName)550 MixableFilterPlugin::changedClip(const InstanceChangedArgs &args,
551                                  const std::string &clipName)
552 {
553     //std::cout << "changedClip!\n";
554     if ( (clipName == kOfxImageEffectSimpleSourceClipName) && _srcClip && (args.reason == eChangeUserEdit) ) {
555         switch ( _srcClip->getPreMultiplication() ) {
556         case eImageOpaque:
557             _premult->setValue(false);
558             break;
559         case eImagePreMultiplied:
560             _premult->setValue(true);
561             break;
562         case eImageUnPreMultiplied:
563             _premult->setValue(false);
564             break;
565         }
566     }
567     //std::cout << "changedClip OK!\n";
568 }
569 
570 mDeclarePluginFactory(MixableFilterPluginFactory, {ofxsThreadSuiteCheck();}, {});
571 void
describe(ImageEffectDescriptor & desc)572 MixableFilterPluginFactory::describe(ImageEffectDescriptor &desc)
573 {
574     //std::cout << "describe!\n";
575     // basic labels
576     desc.setLabel(kPluginName);
577     desc.setPluginGrouping(kPluginGrouping);
578     desc.setPluginDescription(kPluginDescription);
579 
580     desc.addSupportedContext(eContextFilter);
581     desc.addSupportedContext(eContextGeneral);
582     desc.addSupportedContext(eContextPaint);
583     desc.addSupportedBitDepth(eBitDepthUByte);
584     desc.addSupportedBitDepth(eBitDepthUShort);
585     desc.addSupportedBitDepth(eBitDepthFloat);
586 
587     // set a few flags
588     desc.setSingleInstance(false);
589     desc.setHostFrameThreading(false);
590     desc.setSupportsMultiResolution(kSupportsMultiResolution);
591     desc.setSupportsTiles(kSupportsTiles);
592     desc.setTemporalClipAccess(false);
593     desc.setRenderTwiceAlways(false);
594     desc.setSupportsMultipleClipPARs(kSupportsMultipleClipPARs);
595     desc.setSupportsMultipleClipDepths(kSupportsMultipleClipDepths);
596     desc.setRenderThreadSafety(kRenderThreadSafety);
597 
598 #ifdef OFX_EXTENSIONS_NATRON
599     desc.setChannelSelector(ePixelComponentNone); // we have our own channel selector
600 #endif
601     //std::cout << "describe! OK\n";
602 }
603 
604 void
describeInContext(ImageEffectDescriptor & desc,ContextEnum context)605 MixableFilterPluginFactory::describeInContext(ImageEffectDescriptor &desc,
606                                               ContextEnum context)
607 {
608     //std::cout << "describeInContext!\n";
609     // Source clip only in the filter context
610     // create the mandated source clip
611     ClipDescriptor *srcClip = desc.defineClip(kOfxImageEffectSimpleSourceClipName);
612 
613     srcClip->addSupportedComponent(ePixelComponentRGBA);
614     srcClip->addSupportedComponent(ePixelComponentRGB);
615 #ifdef OFX_EXTENSIONS_NATRON
616     srcClip->addSupportedComponent(ePixelComponentXY);
617 #endif
618     srcClip->addSupportedComponent(ePixelComponentAlpha);
619     srcClip->setTemporalClipAccess(false);
620     srcClip->setSupportsTiles(kSupportsTiles);
621     srcClip->setIsMask(false);
622 
623     // create the mandated output clip
624     ClipDescriptor *dstClip = desc.defineClip(kOfxImageEffectOutputClipName);
625     dstClip->addSupportedComponent(ePixelComponentRGBA);
626     dstClip->addSupportedComponent(ePixelComponentRGB);
627 #ifdef OFX_EXTENSIONS_NATRON
628     dstClip->addSupportedComponent(ePixelComponentXY);
629 #endif
630     dstClip->addSupportedComponent(ePixelComponentAlpha);
631     dstClip->setSupportsTiles(kSupportsTiles);
632 
633     // make some pages and to things in
634     PageParamDescriptor *page = desc.definePageParam("Controls");
635 
636     {
637         BooleanParamDescriptor* param = desc.defineBooleanParam(kParamProcessR);
638         param->setLabel(kParamProcessRLabel);
639         param->setHint(kParamProcessRHint);
640         param->setDefault(true);
641         param->setLayoutHint(eLayoutHintNoNewLine, 1);
642         if (page) {
643             page->addChild(*param);
644         }
645     }
646     {
647         BooleanParamDescriptor* param = desc.defineBooleanParam(kParamProcessG);
648         param->setLabel(kParamProcessGLabel);
649         param->setHint(kParamProcessGHint);
650         param->setDefault(true);
651         param->setLayoutHint(eLayoutHintNoNewLine, 1);
652         if (page) {
653             page->addChild(*param);
654         }
655     }
656     {
657         BooleanParamDescriptor* param = desc.defineBooleanParam(kParamProcessB);
658         param->setLabel(kParamProcessBLabel);
659         param->setHint(kParamProcessBHint);
660         param->setDefault(true);
661         param->setLayoutHint(eLayoutHintNoNewLine, 1);
662         if (page) {
663             page->addChild(*param);
664         }
665     }
666     {
667         BooleanParamDescriptor* param = desc.defineBooleanParam(kParamProcessA);
668         param->setLabel(kParamProcessALabel);
669         param->setHint(kParamProcessAHint);
670         param->setDefault(false);
671         if (page) {
672             page->addChild(*param);
673         }
674     }
675 
676 
677     // TODO: describe plugin params
678 
679     ofxsPremultDescribeParams(desc, page);
680     ofxsMixDescribeParams(desc, page);
681     //std::cout << "describeInContext! OK\n";
682 } // MixableFilterPluginFactory::describeInContext
683 
684 ImageEffect*
createInstance(OfxImageEffectHandle handle,ContextEnum)685 MixableFilterPluginFactory::createInstance(OfxImageEffectHandle handle,
686                                            ContextEnum /*context*/)
687 {
688     return new MixableFilterPlugin(handle);
689 }
690 
691 static MixableFilterPluginFactory p(kPluginIdentifier, kPluginVersionMajor, kPluginVersionMinor);
692 mRegisterPluginFactoryInstance(p)
693 
694 OFXS_NAMESPACE_ANONYMOUS_EXIT
695