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 MaskableFilter 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 "MaskableFilter"
40 #define kPluginGrouping "Filter"
41 #define kPluginDescription "A generic maskable filter template."
42 #define kPluginIdentifier "net.sf.openfx.MaskableFilter"
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 MaskableFilterProcessorBase
93     : public ImageProcessor
94 {
95 protected:
96     const Image *_srcImg;
97     const Image *_maskImg;
98     bool _premult;
99     int _premultChannel;
100     bool _doMasking;
101     double _mix;
102     bool _maskInvert;
103     bool _processR, _processG, _processB, _processA;
104     // TODO: add plugin parameter values
105 
106 public:
MaskableFilterProcessorBase(ImageEffect & instance,const RenderArguments &)107     MaskableFilterProcessorBase(ImageEffect &instance,
108                                 const RenderArguments & /*args*/)
109         : ImageProcessor(instance)
110         , _srcImg(NULL)
111         , _maskImg(NULL)
112         , _premult(false)
113         , _premultChannel(3)
114         , _doMasking(false)
115         , _mix(1.)
116         , _maskInvert(false)
117         , _processR(false)
118         , _processG(false)
119         , _processB(false)
120         , _processA(false)
121         // TODO: initialize plugin parameter values
122     {
123     }
124 
setSrcImg(const Image * v)125     void setSrcImg(const Image *v) {_srcImg = v; }
126 
setMaskImg(const Image * v,bool maskInvert)127     void setMaskImg(const Image *v,
128                     bool maskInvert) {_maskImg = v; _maskInvert = maskInvert; }
129 
doMasking(bool v)130     void doMasking(bool v) {_doMasking = v; }
131 
setValues(bool premult,int premultChannel,double mix,bool processR,bool processG,bool processB,bool processA)132     void setValues(bool premult,
133                    int premultChannel,
134                    double mix,
135                    bool processR,
136                    bool processG,
137                    bool processB,
138                    bool processA
139                    // TODO: add plugin parameters
140                    )
141     {
142         _premult = premult;
143         _premultChannel = premultChannel;
144         _mix = mix;
145         _processR = processR;
146         _processG = processG;
147         _processB = processB;
148         _processA = processA;
149         // TODO: set plugin parameter values
150     }
151 };
152 
153 
154 template <class PIX, int nComponents, int maxValue>
155 class MaskableFilterProcessor
156     : public MaskableFilterProcessorBase
157 {
158 public:
MaskableFilterProcessor(ImageEffect & instance,const RenderArguments & args)159     MaskableFilterProcessor(ImageEffect &instance,
160                             const RenderArguments &args)
161         : MaskableFilterProcessorBase(instance, args)
162     {
163         //const double time = args.time;
164 
165         // TODO: any pre-computation goes here (such as computing a LUT)
166     }
167 
multiThreadProcessImages(OfxRectI procWindow)168     void multiThreadProcessImages(OfxRectI procWindow)
169     {
170 #     ifndef __COVERITY__ // too many coverity[dead_error_line] errors
171         const bool r = _processR && (nComponents != 1);
172         const bool g = _processG && (nComponents >= 2);
173         const bool b = _processB && (nComponents >= 3);
174         const bool a = _processA && (nComponents == 1 || nComponents == 4);
175         if (r) {
176             if (g) {
177                 if (b) {
178                     if (a) {
179                         return process<true, true, true, true >(procWindow); // RGBA
180                     } else {
181                         return process<true, true, true, false>(procWindow); // RGBa
182                     }
183                 } else {
184                     if (a) {
185                         return process<true, true, false, true >(procWindow); // RGbA
186                     } else {
187                         return process<true, true, false, false>(procWindow); // RGba
188                     }
189                 }
190             } else {
191                 if (b) {
192                     if (a) {
193                         return process<true, false, true, true >(procWindow); // RgBA
194                     } else {
195                         return process<true, false, true, false>(procWindow); // RgBa
196                     }
197                 } else {
198                     if (a) {
199                         return process<true, false, false, true >(procWindow); // RgbA
200                     } else {
201                         return process<true, false, false, false>(procWindow); // Rgba
202                     }
203                 }
204             }
205         } else {
206             if (g) {
207                 if (b) {
208                     if (a) {
209                         return process<false, true, true, true >(procWindow); // rGBA
210                     } else {
211                         return process<false, true, true, false>(procWindow); // rGBa
212                     }
213                 } else {
214                     if (a) {
215                         return process<false, true, false, true >(procWindow); // rGbA
216                     } else {
217                         return process<false, true, false, false>(procWindow); // rGba
218                     }
219                 }
220             } else {
221                 if (b) {
222                     if (a) {
223                         return process<false, false, true, true >(procWindow); // rgBA
224                     } else {
225                         return process<false, false, true, false>(procWindow); // rgBa
226                     }
227                 } else {
228                     if (a) {
229                         return process<false, false, false, true >(procWindow); // rgbA
230                     } else {
231                         return process<false, false, false, false>(procWindow); // rgba
232                     }
233                 }
234             }
235         }
236 #     endif // ifndef __COVERITY__
237     } // multiThreadProcessImages
238 
239 private:
240 
241 
242     template<bool processR, bool processG, bool processB, bool processA>
process(OfxRectI procWindow)243     void process(OfxRectI procWindow)
244     {
245         assert( (!processR && !processG && !processB) || (nComponents == 3 || nComponents == 4) );
246         assert( !processA || (nComponents == 1 || nComponents == 4) );
247         assert(nComponents == 3 || nComponents == 4);
248         float unpPix[4];
249         float tmpPix[4];
250         for (int y = procWindow.y1; y < procWindow.y2; y++) {
251             if ( _effect.abort() ) {
252                 break;
253             }
254 
255             PIX *dstPix = (PIX *) _dstImg->getPixelAddress(procWindow.x1, y);
256             for (int x = procWindow.x1; x < procWindow.x2; x++) {
257                 const PIX *srcPix = (const PIX *)  (_srcImg ? _srcImg->getPixelAddress(x, y) : 0);
258                 ofxsUnPremult<PIX, nComponents, maxValue>(srcPix, unpPix, _premult, _premultChannel);
259                 double t_r = unpPix[0];
260                 double t_g = unpPix[1];
261                 double t_b = unpPix[2];
262                 double t_a = unpPix[3];
263 
264                 // TODO: process the pixel (the actual computation goes here)
265                 t_r = 1. - t_r;
266                 t_g = 1. - t_g;
267                 t_b = 1. - t_b;
268 
269                 tmpPix[0] = (float)t_r;
270                 tmpPix[1] = (float)t_g;
271                 tmpPix[2] = (float)t_b;
272                 tmpPix[3] = (float)t_a;
273                 ofxsPremultMaskMixPix<PIX, nComponents, maxValue, true>(tmpPix, _premult, _premultChannel, x, y, srcPix, _doMasking, _maskImg, (float)_mix, _maskInvert, dstPix);
274                 // copy back original values from unprocessed channels
275                 if (nComponents == 1) {
276                     if (!processA) {
277                         dstPix[0] = srcPix ? srcPix[0] : PIX();
278                     }
279                 } else if ( (nComponents == 3) || (nComponents == 4) ) {
280                     if (!processR) {
281                         dstPix[0] = srcPix ? srcPix[0] : PIX();
282                     }
283                     if (!processG) {
284                         dstPix[1] = srcPix ? srcPix[1] : PIX();
285                     }
286                     if (!processB) {
287                         dstPix[2] = srcPix ? srcPix[2] : PIX();
288                     }
289                     if ( !processA && (nComponents == 4) ) {
290                         dstPix[3] = srcPix ? srcPix[3] : PIX();
291                     }
292                 }
293                 // increment the dst pixel
294                 dstPix += nComponents;
295             }
296         }
297     } // process
298 };
299 
300 ////////////////////////////////////////////////////////////////////////////////
301 /** @brief The plugin that does our work */
302 class MaskableFilterPlugin
303     : public ImageEffect
304 {
305 public:
306 
307     /** @brief ctor */
MaskableFilterPlugin(OfxImageEffectHandle handle)308     MaskableFilterPlugin(OfxImageEffectHandle handle)
309         : ImageEffect(handle)
310         , _dstClip(NULL)
311         , _srcClip(NULL)
312         , _maskClip(NULL)
313         , _processR(NULL)
314         , _processG(NULL)
315         , _processB(NULL)
316         , _processA(NULL)
317         , _premult(NULL)
318         , _premultChannel(NULL)
319         , _mix(NULL)
320         , _maskApply(NULL)
321         , _maskInvert(NULL)
322     {
323         _dstClip = fetchClip(kOfxImageEffectOutputClipName);
324         assert( _dstClip && (_dstClip->getPixelComponents() == ePixelComponentRGB ||
325                              _dstClip->getPixelComponents() == ePixelComponentRGBA ||
326                              _dstClip->getPixelComponents() == ePixelComponentAlpha) );
327         _srcClip = getContext() == eContextGenerator ? NULL : fetchClip(kOfxImageEffectSimpleSourceClipName);
328         assert( (!_srcClip && getContext() == eContextGenerator) ||
329                 ( _srcClip && (_srcClip->getPixelComponents() == ePixelComponentRGB ||
330                                _srcClip->getPixelComponents() == ePixelComponentRGBA ||
331                                _srcClip->getPixelComponents() == ePixelComponentAlpha) ) );
332         _maskClip = fetchClip(getContext() == eContextPaint ? "Brush" : "Mask");
333         assert(!_maskClip || _maskClip->getPixelComponents() == ePixelComponentAlpha);
334 
335         // TODO: fetch noise parameters
336 
337         _premult = fetchBooleanParam(kParamPremult);
338         _premultChannel = fetchChoiceParam(kParamPremultChannel);
339         assert(_premult && _premultChannel);
340         _mix = fetchDoubleParam(kParamMix);
341         _maskApply = ( ofxsMaskIsAlwaysConnected( OFX::getImageEffectHostDescription() ) && paramExists(kParamMaskApply) ) ? fetchBooleanParam(kParamMaskApply) : 0;
342         _maskInvert = fetchBooleanParam(kParamMaskInvert);
343         assert(_mix && _maskInvert);
344 
345         _processR = fetchBooleanParam(kParamProcessR);
346         _processG = fetchBooleanParam(kParamProcessG);
347         _processB = fetchBooleanParam(kParamProcessB);
348         _processA = fetchBooleanParam(kParamProcessA);
349         assert(_processR && _processG && _processB && _processA);
350     }
351 
352 private:
353     /* Override the render */
354     virtual void render(const RenderArguments &args) OVERRIDE FINAL;
355 
356     template<int nComponents>
357     void renderForComponents(const RenderArguments &args);
358 
359     template <class PIX, int nComponents, int maxValue>
360     void renderForBitDepth(const RenderArguments &args);
361 
362     /* set up and run a processor */
363     void setupAndProcess(MaskableFilterProcessorBase &, const RenderArguments &args);
364 
365     virtual bool isIdentity(const IsIdentityArguments &args, Clip * &identityClip, double &identityTime, int& view, std::string& plane) OVERRIDE FINAL;
366 
367     /** @brief called when a clip has just been changed in some way (a rewire maybe) */
368     virtual void changedClip(const InstanceChangedArgs &args, const std::string &clipName) OVERRIDE FINAL;
369 
370 private:
371     // do not need to delete these, the ImageEffect is managing them for us
372     Clip *_dstClip;
373     Clip *_srcClip;
374     Clip *_maskClip;
375     BooleanParam* _processR;
376     BooleanParam* _processG;
377     BooleanParam* _processB;
378     BooleanParam* _processA;
379     BooleanParam* _premult;
380     ChoiceParam* _premultChannel;
381     DoubleParam* _mix;
382     BooleanParam* _maskApply;
383     BooleanParam* _maskInvert;
384 };
385 
386 
387 ////////////////////////////////////////////////////////////////////////////////
388 /** @brief render for the filter */
389 
390 ////////////////////////////////////////////////////////////////////////////////
391 // basic plugin render function, just a skelington to instantiate templates from
392 
393 /* set up and run a processor */
394 void
setupAndProcess(MaskableFilterProcessorBase & processor,const RenderArguments & args)395 MaskableFilterPlugin::setupAndProcess(MaskableFilterProcessorBase &processor,
396                                       const RenderArguments &args)
397 {
398     const double time = args.time;
399 
400     auto_ptr<Image> dst( _dstClip->fetchImage(time) );
401 
402     if ( !dst.get() ) {
403         throwSuiteStatusException(kOfxStatFailed);
404     }
405     BitDepthEnum dstBitDepth    = dst->getPixelDepth();
406     PixelComponentEnum dstComponents  = dst->getPixelComponents();
407     if ( ( dstBitDepth != _dstClip->getPixelDepth() ) ||
408          ( dstComponents != _dstClip->getPixelComponents() ) ) {
409         setPersistentMessage(Message::eMessageError, "", "OFX Host gave image with wrong depth or components");
410         throwSuiteStatusException(kOfxStatFailed);
411     }
412     if ( (dst->getRenderScale().x != args.renderScale.x) ||
413          ( dst->getRenderScale().y != args.renderScale.y) ||
414          ( ( dst->getField() != eFieldNone) /* for DaVinci Resolve */ && ( dst->getField() != args.fieldToRender) ) ) {
415         setPersistentMessage(Message::eMessageError, "", "OFX Host gave image with wrong scale or field properties");
416         throwSuiteStatusException(kOfxStatFailed);
417     }
418     auto_ptr<const Image> src( ( _srcClip && _srcClip->isConnected() ) ?
419                                     _srcClip->fetchImage(time) : 0 );
420     if ( src.get() ) {
421         if ( (src->getRenderScale().x != args.renderScale.x) ||
422              ( src->getRenderScale().y != args.renderScale.y) ||
423              ( ( src->getField() != eFieldNone) /* for DaVinci Resolve */ && ( src->getField() != args.fieldToRender) ) ) {
424             setPersistentMessage(Message::eMessageError, "", "OFX Host gave image with wrong scale or field properties");
425             throwSuiteStatusException(kOfxStatFailed);
426         }
427         BitDepthEnum srcBitDepth      = src->getPixelDepth();
428         PixelComponentEnum srcComponents = src->getPixelComponents();
429         if ( (srcBitDepth != dstBitDepth) || (srcComponents != dstComponents) ) {
430             throwSuiteStatusException(kOfxStatErrImageFormat);
431         }
432     }
433     bool doMasking = ( ( !_maskApply || _maskApply->getValueAtTime(time) ) && _maskClip && _maskClip->isConnected() );
434     auto_ptr<const Image> mask(doMasking ? _maskClip->fetchImage(time) : 0);
435     if ( mask.get() ) {
436         if ( (mask->getRenderScale().x != args.renderScale.x) ||
437              ( mask->getRenderScale().y != args.renderScale.y) ||
438              ( ( mask->getField() != eFieldNone) /* for DaVinci Resolve */ && ( mask->getField() != args.fieldToRender) ) ) {
439             setPersistentMessage(Message::eMessageError, "", "OFX Host gave image with wrong scale or field properties");
440             throwSuiteStatusException(kOfxStatFailed);
441         }
442     }
443     if (doMasking) {
444         bool maskInvert;
445         _maskInvert->getValueAtTime(time, maskInvert);
446         processor.doMasking(true);
447         processor.setMaskImg(mask.get(), maskInvert);
448     }
449 
450     processor.setDstImg( dst.get() );
451     processor.setSrcImg( src.get() );
452     processor.setRenderWindow(args.renderWindow);
453 
454     // TODO: fetch noise parameter values
455 
456     bool premult;
457     int premultChannel;
458     _premult->getValueAtTime(time, premult);
459     _premultChannel->getValueAtTime(time, premultChannel);
460     double mix;
461     _mix->getValueAtTime(time, mix);
462 
463     bool processR, processG, processB, processA;
464     _processR->getValueAtTime(time, processR);
465     _processG->getValueAtTime(time, processG);
466     _processB->getValueAtTime(time, processB);
467     _processA->getValueAtTime(time, processA);
468 
469     processor.setValues(premult, premultChannel, mix,
470                         processR, processG, processB, processA);
471     processor.process();
472 } // MaskableFilterPlugin::setupAndProcess
473 
474 // the overridden render function
475 void
render(const RenderArguments & args)476 MaskableFilterPlugin::render(const RenderArguments &args)
477 {
478     //std::cout << "render!\n";
479     // instantiate the render code based on the pixel depth of the dst clip
480     PixelComponentEnum dstComponents  = _dstClip->getPixelComponents();
481 
482     assert( kSupportsMultipleClipPARs   || !_srcClip || _srcClip->getPixelAspectRatio() == _dstClip->getPixelAspectRatio() );
483     assert( kSupportsMultipleClipDepths || !_srcClip || _srcClip->getPixelDepth()       == _dstClip->getPixelDepth() );
484     assert(OFX_COMPONENTS_OK(dstComponents));
485     // do the rendering
486     switch (dstComponents) {
487     case ePixelComponentRGBA:
488         renderForComponents<4>(args);
489         break;
490     case ePixelComponentRGB:
491         renderForComponents<3>(args);
492         break;
493 #ifdef OFX_EXTENSIONS_NATRON
494     case ePixelComponentXY:
495         renderForComponents<2>(args);
496         break;
497 #endif
498     case ePixelComponentAlpha:
499         renderForComponents<1>(args);
500         break;
501     default:
502         //std::cout << "components usupported\n";
503         throwSuiteStatusException(kOfxStatErrUnsupported);
504         break;
505     } // switch
506       //std::cout << "render! OK\n";
507 }
508 
509 template<int nComponents>
510 void
renderForComponents(const RenderArguments & args)511 MaskableFilterPlugin::renderForComponents(const RenderArguments &args)
512 {
513     BitDepthEnum dstBitDepth    = _dstClip->getPixelDepth();
514 
515     switch (dstBitDepth) {
516     case eBitDepthUByte:
517         renderForBitDepth<unsigned char, nComponents, 255>(args);
518         break;
519 
520     case eBitDepthUShort:
521         renderForBitDepth<unsigned short, nComponents, 65535>(args);
522         break;
523 
524     case eBitDepthFloat:
525         renderForBitDepth<float, nComponents, 1>(args);
526         break;
527     default:
528         //std::cout << "depth usupported\n";
529         throwSuiteStatusException(kOfxStatErrUnsupported);
530     }
531 }
532 
533 template <class PIX, int nComponents, int maxValue>
534 void
renderForBitDepth(const RenderArguments & args)535 MaskableFilterPlugin::renderForBitDepth(const RenderArguments &args)
536 {
537     MaskableFilterProcessor<PIX, nComponents, maxValue> fred(*this, args);
538     setupAndProcess(fred, args);
539 }
540 
541 bool
isIdentity(const IsIdentityArguments & args,Clip * & identityClip,double &,int &,std::string &)542 MaskableFilterPlugin::isIdentity(const IsIdentityArguments &args,
543                                  Clip * &identityClip,
544                                  double & /*identityTime*/
545                                  , int& /*view*/, std::string& /*plane*/)
546 {
547     //std::cout << "isIdentity!\n";
548     const double time = args.time;
549     double mix;
550 
551     _mix->getValueAtTime(time, mix);
552 
553     if (mix == 0. /*|| (!processR && !processG && !processB && !processA)*/) {
554         identityClip = _srcClip;
555 
556         return true;
557     }
558 
559     {
560         bool processR;
561         bool processG;
562         bool processB;
563         bool processA;
564         _processR->getValueAtTime(time, processR);
565         _processG->getValueAtTime(time, processG);
566         _processB->getValueAtTime(time, processB);
567         _processA->getValueAtTime(time, processA);
568         if (!processR && !processG && !processB && !processA) {
569             identityClip = _srcClip;
570 
571             return true;
572         }
573     }
574 
575     // TODO: which plugin parameter values give identity?
576     //if (...) {
577     //    identityClip = _srcClip;
578     //    //std::cout << "isIdentity! true\n";
579     //    return true;
580     //}
581 
582     bool doMasking = ( ( !_maskApply || _maskApply->getValueAtTime(time) ) && _maskClip && _maskClip->isConnected() );
583     if (doMasking) {
584         bool maskInvert;
585         _maskInvert->getValueAtTime(time, maskInvert);
586         if (!maskInvert) {
587             OfxRectI maskRoD;
588             Coords::toPixelEnclosing(_maskClip->getRegionOfDefinition(time), args.renderScale, _maskClip->getPixelAspectRatio(), &maskRoD);
589             // effect is identity if the renderWindow doesn't intersect the mask RoD
590             if ( !Coords::rectIntersection<OfxRectI>(args.renderWindow, maskRoD, 0) ) {
591                 identityClip = _srcClip;
592 
593                 return true;
594             }
595         }
596     }
597 
598     //std::cout << "isIdentity! false\n";
599     return false;
600 } // MaskableFilterPlugin::isIdentity
601 
602 void
changedClip(const InstanceChangedArgs & args,const std::string & clipName)603 MaskableFilterPlugin::changedClip(const InstanceChangedArgs &args,
604                                   const std::string &clipName)
605 {
606     //std::cout << "changedClip!\n";
607     if ( (clipName == kOfxImageEffectSimpleSourceClipName) && _srcClip && (args.reason == eChangeUserEdit) ) {
608         switch ( _srcClip->getPreMultiplication() ) {
609         case eImageOpaque:
610             _premult->setValue(false);
611             break;
612         case eImagePreMultiplied:
613             _premult->setValue(true);
614             break;
615         case eImageUnPreMultiplied:
616             _premult->setValue(false);
617             break;
618         }
619     }
620     //std::cout << "changedClip OK!\n";
621 }
622 
623 mDeclarePluginFactory(MaskableFilterPluginFactory, {ofxsThreadSuiteCheck();}, {});
624 void
describe(ImageEffectDescriptor & desc)625 MaskableFilterPluginFactory::describe(ImageEffectDescriptor &desc)
626 {
627     //std::cout << "describe!\n";
628     // basic labels
629     desc.setLabel(kPluginName);
630     desc.setPluginGrouping(kPluginGrouping);
631     desc.setPluginDescription(kPluginDescription);
632 
633     desc.addSupportedContext(eContextFilter);
634     desc.addSupportedContext(eContextGeneral);
635     desc.addSupportedContext(eContextPaint);
636     desc.addSupportedBitDepth(eBitDepthUByte);
637     desc.addSupportedBitDepth(eBitDepthUShort);
638     desc.addSupportedBitDepth(eBitDepthFloat);
639 
640     // set a few flags
641     desc.setSingleInstance(false);
642     desc.setHostFrameThreading(false);
643     desc.setSupportsMultiResolution(kSupportsMultiResolution);
644     desc.setSupportsTiles(kSupportsTiles);
645     desc.setTemporalClipAccess(false);
646     desc.setRenderTwiceAlways(false);
647     desc.setSupportsMultipleClipPARs(kSupportsMultipleClipPARs);
648     desc.setSupportsMultipleClipDepths(kSupportsMultipleClipDepths);
649     desc.setRenderThreadSafety(kRenderThreadSafety);
650 
651 #ifdef OFX_EXTENSIONS_NATRON
652     desc.setChannelSelector(ePixelComponentNone); // we have our own channel selector
653 #endif
654     //std::cout << "describe! OK\n";
655 }
656 
657 void
describeInContext(ImageEffectDescriptor & desc,ContextEnum context)658 MaskableFilterPluginFactory::describeInContext(ImageEffectDescriptor &desc,
659                                                ContextEnum context)
660 {
661     //std::cout << "describeInContext!\n";
662     // Source clip only in the filter context
663     // create the mandated source clip
664     ClipDescriptor *srcClip = desc.defineClip(kOfxImageEffectSimpleSourceClipName);
665 
666     srcClip->addSupportedComponent(ePixelComponentRGBA);
667     srcClip->addSupportedComponent(ePixelComponentRGB);
668 #ifdef OFX_EXTENSIONS_NATRON
669     srcClip->addSupportedComponent(ePixelComponentXY);
670 #endif
671     srcClip->addSupportedComponent(ePixelComponentAlpha);
672     srcClip->setTemporalClipAccess(false);
673     srcClip->setSupportsTiles(kSupportsTiles);
674     srcClip->setIsMask(false);
675 
676     // create the mandated output clip
677     ClipDescriptor *dstClip = desc.defineClip(kOfxImageEffectOutputClipName);
678     dstClip->addSupportedComponent(ePixelComponentRGBA);
679     dstClip->addSupportedComponent(ePixelComponentRGB);
680 #ifdef OFX_EXTENSIONS_NATRON
681     dstClip->addSupportedComponent(ePixelComponentXY);
682 #endif
683     dstClip->addSupportedComponent(ePixelComponentAlpha);
684     dstClip->setSupportsTiles(kSupportsTiles);
685 
686     ClipDescriptor *maskClip = (context == eContextPaint) ? desc.defineClip("Brush") : desc.defineClip("Mask");
687     maskClip->addSupportedComponent(ePixelComponentAlpha);
688     maskClip->setTemporalClipAccess(false);
689     if (context != eContextPaint) {
690         maskClip->setOptional(true);
691     }
692     maskClip->setSupportsTiles(kSupportsTiles);
693     maskClip->setIsMask(true);
694 
695     // make some pages and to things in
696     PageParamDescriptor *page = desc.definePageParam("Controls");
697 
698     {
699         BooleanParamDescriptor* param = desc.defineBooleanParam(kParamProcessR);
700         param->setLabel(kParamProcessRLabel);
701         param->setHint(kParamProcessRHint);
702         param->setDefault(true);
703         param->setLayoutHint(eLayoutHintNoNewLine, 1);
704         if (page) {
705             page->addChild(*param);
706         }
707     }
708     {
709         BooleanParamDescriptor* param = desc.defineBooleanParam(kParamProcessG);
710         param->setLabel(kParamProcessGLabel);
711         param->setHint(kParamProcessGHint);
712         param->setDefault(true);
713         param->setLayoutHint(eLayoutHintNoNewLine, 1);
714         if (page) {
715             page->addChild(*param);
716         }
717     }
718     {
719         BooleanParamDescriptor* param = desc.defineBooleanParam(kParamProcessB);
720         param->setLabel(kParamProcessBLabel);
721         param->setHint(kParamProcessBHint);
722         param->setDefault(true);
723         param->setLayoutHint(eLayoutHintNoNewLine, 1);
724         if (page) {
725             page->addChild(*param);
726         }
727     }
728     {
729         BooleanParamDescriptor* param = desc.defineBooleanParam(kParamProcessA);
730         param->setLabel(kParamProcessALabel);
731         param->setHint(kParamProcessAHint);
732         param->setDefault(false);
733         if (page) {
734             page->addChild(*param);
735         }
736     }
737 
738 
739     // TODO: describe plugin params
740 
741     ofxsPremultDescribeParams(desc, page);
742     ofxsMaskMixDescribeParams(desc, page);
743     //std::cout << "describeInContext! OK\n";
744 } // MaskableFilterPluginFactory::describeInContext
745 
746 ImageEffect*
createInstance(OfxImageEffectHandle handle,ContextEnum)747 MaskableFilterPluginFactory::createInstance(OfxImageEffectHandle handle,
748                                             ContextEnum /*context*/)
749 {
750     return new MaskableFilterPlugin(handle);
751 }
752 
753 static MaskableFilterPluginFactory p(kPluginIdentifier, kPluginVersionMajor, kPluginVersionMinor);
754 mRegisterPluginFactoryInstance(p)
755 
756 OFXS_NAMESPACE_ANONYMOUS_EXIT
757