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