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