1 /* ***** BEGIN LICENSE BLOCK *****
2  * This file is part of openfx-misc <https://github.com/devernay/openfx-misc>,
3  * Copyright (C) 2013-2018 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 //  CImgFilter.h
21 //
22 //  A base class to simplify the creation of CImg plugins that have one image as input and an optional mask.
23 //
24 
25 #ifndef Misc_CImgFilter_h
26 #define Misc_CImgFilter_h
27 
28 #define PLUGIN_PACK_GPL2 // include GPL2 plugins by default
29 
30 #include <cassert>
31 #include <memory>
32 #include <algorithm> // max
33 
34 #include "ofxsImageEffect.h"
35 #include "ofxsMacros.h"
36 #include "ofxsPixelProcessor.h"
37 #include "ofxsCopier.h"
38 #include "ofxsCoords.h"
39 #ifdef OFX_EXTENSIONS_NATRON
40 #include "ofxNatron.h"
41 #endif
42 #include "ofxsThreadSuite.h"
43 
44 #ifdef thread_local
45 # define HAVE_THREAD_LOCAL
46 #else
47 # if __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__
48 #  define thread_local _Thread_local
49 #  define HAVE_THREAD_LOCAL
50 # elif defined _WIN32 && ( \
51     defined _MSC_VER || \
52     defined __ICL || \
53     defined __DMC__ || \
54     defined __BORLANDC__ )
55 // For DLLs that are loaded dynamically after the process has started (delay load, COM objects,
56 // explicit LoadLibrary, etc) __declspec(thread) does not work on Windows XP, 2003 Server and
57 // earlier OSes, but does work on Vista and 2008 Server.
58 // http://msdn.microsoft.com/en-us/library/9w1sdazb%28v=vs.80%29.aspx#1
59 // Unfortunately, OFX plugins are loaded using LoadLibrary()
60 //#  define thread_local __declspec(thread)
61 #  warning "CImg plugins cannot be aborted when compiled with this compiler. Please use MinGW, GCC or Clang."
62 /* note that ICC (linux) and Clang are covered by __GNUC__ */
63 # elif defined __GNUC__ || \
64     defined __SUNPRO_C || \
65     defined __xlC__
66 // Clang 3.4 also support SD-6 (feature test macros __cpp_*), but no thread local macro
67 #   if defined(__clang__)
68 #    if __has_feature(cxx_thread_local)
69 // thread_local was added in Clang 3.3
70 // Still requires libstdc++ from GCC 4.8
71 // For that __GLIBCXX__ isn't good enough
72 // Also the MacOS build of clang does *not* support thread local yet.
73 #      define thread_local __thread
74 #      define HAVE_THREAD_LOCAL
75 #    elif __has_feature(c_thread_local) || __has_extension(c_thread_local)
76 #      define thread_local _Thread_local
77 #      define HAVE_THREAD_LOCAL
78 #    endif
79 
80 #   elif defined(__GNUC__)
81 //#    if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)
82 //// The C++11 thread_local keyword is supported in GCC only since 4.8
83 //#      define thread_local __thread
84 //#    endif
85 //#    define HAVE_THREAD_LOCAL
86 // __thread became widely supported with GCC 4.7
87 #    if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
88 #      define thread_local __thread
89 #      define HAVE_THREAD_LOCAL
90 #    endif
91 #   endif
92 # endif
93 #endif
94 
95 #if !defined(HAVE_THREAD_LOCAL)
96 #ifdef WIN32
97 #  warning "Most CImg plugins cannot be aborted when compiled with this compiler. Please use MinGW, GCC or Clang."
98 #else
99 // non-Win32 systems should have at least pthread
100 #  warning "CImg plugins use pthread-based thread-local storage."
101 #include <assert.h>
102 #include <pthread.h>
103 #define HAVE_PTHREAD
104 #endif
105 #endif //!HAVE_THREAD_LOCAL
106 
107 
108 //#define CIMG_DEBUG
109 
110 // use the locally-downloaded CImg.h
111 //
112 // To download the latest CImg.h, use:
113 // git archive --remote=git://git.code.sf.net/p/gmic/source HEAD:src CImg.h |tar xf -
114 //
115 // CImg.h must at least be the version from Oct 17 2014, commit 9b52016cab3368744ea9f3cc20a3e9b4f0c66eb3
116 // To download, use:
117 // git archive --remote=git://git.code.sf.net/p/gmic/source 9b52016cab3368744ea9f3cc20a3e9b4f0c66eb3:src CImg.h |tar xf -
118 #define cimg_display 0
119 #define cimg_namespace_suffix openfx_misc
120 #ifdef _OPENMP
121 #  ifndef cimg_use_openmp
122 #    define cimg_use_openmp
123 #  endif
124 #endif
125 #define cimg_verbosity 0
126 
127 // Abort mechanism:
128 // we have a struct with a thread-local storage that holds the OFX::ImageEffect
129 // for the thread being rendered
130 #if defined(HAVE_THREAD_LOCAL) || defined(HAVE_PTHREAD)
131 #define cimg_abort_test gImageEffectAbort()
132 inline void gImageEffectAbort();
133 #endif
134 
135 
136 #ifdef cimg_version
137 #error "CImg.h was included before this file"
138 #endif
139 
140 #ifdef PLUGIN_PACK_GPL2
141 
142 // include the inpaint and nlmeans cimg plugins
143 #if 0 // not necessary since CImg cimmit 7c83bdad65ab7447220b54851a5a1035976777fa
144 namespace cimg_library_openfx_misc {
145     namespace cimg {
146         //! Return the maximum between two values.
147         template<typename t>
148         inline t max(const t& a, const t& b) {
149             return std::max(a,b);
150         }
151         //! Return the minimum between two values.
152         template<typename t>
153         inline t min(const t& a, const t& b) {
154             return std::min(a,b);
155         }
156     }
157 }
158 #endif
159 #define cimg_plugin "Inpaint/inpaint.h"
160 //#define cimg_plugin1 "nlmeans.h"
161 #endif
162 
163 CLANG_DIAG_OFF(shorten-64-to-32)
164 #include "CImg.h"
165 CLANG_DIAG_ON(shorten-64-to-32)
166 #define cimg_library cimg_library_suffixed // so that namespace is private, but code requires no change
167 
168 typedef float cimgpix_t;
169 typedef float cimgpixfloat_t;
170 
171 #define CIMG_ABORTABLE // use abortable versions of CImg functions
172 
173 #if defined(HAVE_THREAD_LOCAL)
174 struct tls
175 {
176     static thread_local OFX::ImageEffect *gImageEffect;
177 };
178 
179 inline void
gImageEffectAbort()180 gImageEffectAbort()
181 {
182 #  ifdef cimg_use_openmp
183     if ( omp_get_thread_num() ) {
184         return;
185     }
186 #  endif
187     if ( tls::gImageEffect && tls::gImageEffect->abort() ) {
188         throw cimg_library::CImgAbortException("");
189     }
190 }
191 
192 #elif defined(HAVE_PTHREAD)
193 struct tls
194 {
195     // the key is created once and *never* deleted using pthread_key_delete()
196     static pthread_key_t gImageEffect_key;
gImageEffect_key_deletetls197     static void gImageEffect_key_delete(void * arg)
198     {
199         assert (NULL != arg);
200         free(arg);
201     }
gImageEffect_key_createtls202     static void gImageEffect_key_create(void)
203     {
204         int _ret;
205         _ret = pthread_key_create(&(gImageEffect_key), gImageEffect_key_delete);
206         cimg_library::cimg::unused(_ret); /* To get rid of warnings in case of NDEBUG */
207         assert (0 == _ret);
208     }
209     static pthread_once_t gImageEffect_once/* = PTHREAD_ONCE_INIT*/;
210 };
211 
212 inline void
gImageEffectAbort()213 gImageEffectAbort()
214 {
215 #  ifdef cimg_use_openmp
216     if ( omp_get_thread_num() ) {
217         return;
218     }
219 #  endif
220     OFX::ImageEffect **_ptr = (OFX::ImageEffect **)pthread_getspecific(tls::gImageEffect_key);
221     if ( *_ptr && (*_ptr)->abort() ) {
222         throw cimg_library::CImgAbortException("");
223     }
224 }
225 #endif
226 
227 
228 class CImgFilterPluginHelperBase
229     : public OFX::ImageEffect
230 {
231 public:
232 
233     CImgFilterPluginHelperBase(OfxImageEffectHandle handle,
234                                bool usesMask, // true if the mask parameter to render should be a single-channel image containing the mask
235                                bool supportsComponentRemapping, // true if the number and order of components of the image passed to render() has no importance
236                                bool supportsTiles,
237                                bool supportsMultiResolution,
238                                bool supportsRenderScale,
239                                bool defaultUnpremult /* = true*/,
240                                bool isFilter /* = true*/);
241 
242 
243     virtual void changedClip(const OFX::InstanceChangedArgs &args, const std::string &clipName) OVERRIDE;
244     virtual void changedParam(const OFX::InstanceChangedArgs &args, const std::string &paramName) OVERRIDE;
245     static OFX::PageParamDescriptor* describeInContextBegin(bool sourceIsOptional,
246                                                             OFX::ImageEffectDescriptor &desc,
247                                                             OFX::ContextEnum context,
248                                                             bool supportsRGBA,
249                                                             bool supportsRGB,
250                                                             bool supportsXY,
251                                                             bool supportsAlpha,
252                                                             bool supportsTiles,
253                                                             bool processRGB /* = true*/,
254                                                             bool processAlpha /* = false*/,
255                                                             bool processIsSecret /* = false*/);
256     static void describeInContextEnd(OFX::ImageEffectDescriptor &desc,
257                                      OFX::ContextEnum context,
258                                      OFX::PageParamDescriptor* page,
259                                      bool hasUnpremult = true);
260 
261 protected:
262 #ifdef CIMG_DEBUG
printRectI(const char * name,const OfxRectI & rect)263     static void printRectI(const char*name,
264                            const OfxRectI& rect)
265     {
266         printf("%s= (%d, %d)-(%d, %d)\n", name, rect.x1, rect.y1, rect.x2, rect.y2);
267     }
268 
printRectD(const char * name,const OfxRectD & rect)269     static void printRectD(const char*name,
270                            const OfxRectD& rect)
271     {
272         printf("%s= (%g, %g)-(%g, %g)\n", name, rect.x1, rect.y1, rect.x2, rect.y2);
273     }
274 
275 #else
276     static void printRectI(const char*,
277                            const OfxRectI&) {}
278 
279     static void printRectD(const char*,
280                            const OfxRectD&) {}
281 
282 #endif
283 
284 
285     void setupAndFill(OFX::PixelProcessorFilterBase & processor,
286                       const OfxRectI &renderWindow,
287                       void *dstPixelData,
288                       const OfxRectI& dstBounds,
289                       OFX::PixelComponentEnum dstPixelComponents,
290                       int dstPixelComponentCount,
291                       OFX::BitDepthEnum dstPixelDepth,
292                       int dstRowBytes);
293 
294     void setupAndCopy(OFX::PixelProcessorFilterBase & processor,
295                       double time,
296                       const OfxRectI &renderWindow,
297                       const OFX::Image* orig,
298                       const OFX::Image* mask,
299                       const void *srcPixelData,
300                       const OfxRectI& srcBounds,
301                       OFX::PixelComponentEnum srcPixelComponents,
302                       int srcPixelComponentCount,
303                       OFX::BitDepthEnum srcBitDepth,
304                       int srcRowBytes,
305                       int srcBoundary,
306                       void *dstPixelData,
307                       const OfxRectI& dstBounds,
308                       OFX::PixelComponentEnum dstPixelComponents,
309                       int dstPixelComponentCount,
310                       OFX::BitDepthEnum dstPixelDepth,
311                       int dstRowBytes,
312                       bool premult,
313                       int premultChannel,
314                       double mix,
315                       bool maskInvert);
316 
317 
318     // utility functions
319     static
320     bool maskLineIsZero(const OFX::Image* mask, int x1, int x2, int y, bool maskInvert);
321     static
322     bool maskColumnIsZero(const OFX::Image* mask, int x, int y1, int y2, bool maskInvert);
323 
324 protected:
325     // do not need to delete these, the ImageEffect is managing them for us
326     OFX::Clip *_dstClip;
327     OFX::Clip *_srcClip;
328     OFX::Clip *_maskClip;
329 
330     // params
331     OFX::BooleanParam* _processR;
332     OFX::BooleanParam* _processG;
333     OFX::BooleanParam* _processB;
334     OFX::BooleanParam* _processA;
335     OFX::BooleanParam* _premult;
336     OFX::ChoiceParam* _premultChannel;
337     OFX::DoubleParam* _mix;
338     OFX::BooleanParam* _maskApply;
339     OFX::BooleanParam* _maskInvert;
340     bool _usesMask; // true if the mask parameter to render() should be a single-channel mask of the same size as the image
341     bool _supportsComponentRemapping; // true if the number and order of components of the image passed to render() has no importance
342     bool _supportsTiles;
343     bool _supportsMultiResolution;
344     bool _supportsRenderScale;
345     bool _defaultUnpremult; //!< unpremult by default
346     OFX::BooleanParam* _premultChanged; // set to true the when user changes premult
347 };
348 
349 template <class Params, bool sourceIsOptional>
350 class CImgFilterPluginHelper
351     : public CImgFilterPluginHelperBase
352 {
353 public:
354 
CImgFilterPluginHelper(OfxImageEffectHandle handle,bool usesMask,bool supportsComponentRemapping,bool supportsTiles,bool supportsMultiResolution,bool supportsRenderScale,bool defaultUnpremult)355     CImgFilterPluginHelper(OfxImageEffectHandle handle,
356                            bool usesMask, // true if the mask parameter to render should be a single-channel image containing the mask
357                            bool supportsComponentRemapping, // true if the number and order of components of the image passed to render() has no importance
358                            bool supportsTiles,
359                            bool supportsMultiResolution,
360                            bool supportsRenderScale,
361                            bool defaultUnpremult /* = true*/)
362         : CImgFilterPluginHelperBase(handle, usesMask, supportsComponentRemapping, supportsTiles, supportsMultiResolution, supportsRenderScale, defaultUnpremult, /*isFilter=*/ true)
363     {
364     }
365 
366     // override the roi call
367     virtual void getRegionsOfInterest(const OFX::RegionsOfInterestArguments &args, OFX::RegionOfInterestSetter &rois) OVERRIDE FINAL;
368     virtual bool getRegionOfDefinition(const OFX::RegionOfDefinitionArguments &args, OfxRectD &rod) OVERRIDE FINAL;
369 
370     /* Override the render */
371     virtual void render(const OFX::RenderArguments &args) OVERRIDE FINAL;
372     virtual bool isIdentity(const OFX::IsIdentityArguments &args, OFX::Clip* &identityClip, double &identityTime, int& /*view*/, std::string& /*plane*/) OVERRIDE FINAL;
373 
374     // the following functions can be overridden/implemented by the plugin
375     virtual void getValuesAtTime(double time, Params& params) = 0;
376 
377     // compute the roi required to compute rect, given params. This roi is then intersected with the image rod.
378     virtual void getRoI(const OfxRectI& rect, const OfxPointD& renderScale, const Params& params, OfxRectI* roi) = 0;
getRegionOfDefinition(const OfxRectI &,const OfxPointD &,const Params &,OfxRectI *)379     virtual bool getRegionOfDefinition(const OfxRectI& /*srcRoD*/,
380                                        const OfxPointD& /*renderScale*/,
381                                        const Params& /*params*/,
382                                        OfxRectI* /*dstRoD*/) { return false; };
383     virtual void render(const OFX::RenderArguments &args,
384                         const Params& params,
385                         int x1, //!< origin of the image tile
386                         int y1, //!< origin of the image tile
387                         cimg_library::CImg<cimgpix_t>& mask, //!< in: if the filter uses the mask, a single-channel mask (can be modified by the render func without any side-effect), else an empty image.
388                         cimg_library::CImg<cimgpix_t>& cimg, //!< in/out: image
389                         int alphaChannel //!< alpha channel in cimg, or -1 if there is no alpha channel
390                         ) = 0;
isIdentity(const OFX::IsIdentityArguments &,const Params &)391     virtual bool isIdentity(const OFX::IsIdentityArguments & /*args*/,
392                             const Params& /*params*/) { return false; };
393 
394     // 0: Black/Dirichlet, 1: Nearest/Neumann, 2: Repeat/Periodic
getBoundary(const Params &)395     virtual int getBoundary(const Params& /*params*/) { return 0; }
396 
397     //static void describe(OFX::ImageEffectDescriptor &desc, bool supportsTiles);
398 
describeInContextBegin(OFX::ImageEffectDescriptor & desc,OFX::ContextEnum context,bool supportsRGBA,bool supportsRGB,bool supportsXY,bool supportsAlpha,bool supportsTiles,bool processRGB,bool processAlpha,bool processIsSecret)399     static OFX::PageParamDescriptor* describeInContextBegin(OFX::ImageEffectDescriptor &desc,
400                                                             OFX::ContextEnum context,
401                                                             bool supportsRGBA,
402                                                             bool supportsRGB,
403                                                             bool supportsXY,
404                                                             bool supportsAlpha,
405                                                             bool supportsTiles,
406                                                             bool processRGB /* = true*/,
407                                                             bool processAlpha /* = false*/,
408                                                             bool processIsSecret /* = false*/)
409     {
410         return CImgFilterPluginHelperBase::describeInContextBegin(sourceIsOptional,
411                                                                   desc,
412                                                                   context,
413                                                                   supportsRGBA,
414                                                                   supportsRGB,
415                                                                   supportsXY,
416                                                                   supportsAlpha,
417                                                                   supportsTiles,
418                                                                   processRGB,
419                                                                   processAlpha,
420                                                                   processIsSecret);
421     }
422 };
423 
424 
425 template <class Params, bool sourceIsOptional>
426 void
render(const OFX::RenderArguments & args)427 CImgFilterPluginHelper<Params, sourceIsOptional>::render(const OFX::RenderArguments &args)
428 {
429     if ( !_supportsRenderScale && ( (args.renderScale.x != 1.) || (args.renderScale.y != 1.) ) ) {
430         OFX::throwSuiteStatusException(kOfxStatFailed);
431     }
432 
433     const double time = args.time;
434     const OfxPointD& renderScale = args.renderScale;
435     const OfxRectI& renderWindow = args.renderWindow;
436     OFX::auto_ptr<OFX::Image> dst( _dstClip->fetchImage(time) );
437     if ( !dst.get() ) {
438         OFX::throwSuiteStatusException(kOfxStatFailed);
439     }
440     if ( (dst->getRenderScale().x != renderScale.x) ||
441          ( dst->getRenderScale().y != renderScale.y) ||
442          ( ( dst->getField() != OFX::eFieldNone) /* for DaVinci Resolve */ && ( dst->getField() != args.fieldToRender) ) ) {
443         setPersistentMessage(OFX::Message::eMessageError, "", "OFX Host gave image with wrong scale or field properties");
444         OFX::throwSuiteStatusException(kOfxStatFailed);
445     }
446     const OFX::BitDepthEnum dstBitDepth       = dst->getPixelDepth();
447     const OFX::PixelComponentEnum dstPixelComponents  = dst->getPixelComponents();
448     const int dstPixelComponentCount = dst->getPixelComponentCount();
449     assert(dstBitDepth == OFX::eBitDepthFloat); // only float is supported for now (others are untested)
450 
451     OFX::auto_ptr<const OFX::Image> src( ( _srcClip && _srcClip->isConnected() ) ?
452                                          _srcClip->fetchImage(args.time) : 0 );
453     if ( src.get() ) {
454         OFX::BitDepthEnum srcBitDepth      = src->getPixelDepth();
455         OFX::PixelComponentEnum srcPixelComponents = src->getPixelComponents();
456         if ( (srcBitDepth != dstBitDepth) || (srcPixelComponents != dstPixelComponents) ) {
457             OFX::throwSuiteStatusException(kOfxStatErrImageFormat);
458         }
459         if ( (src->getRenderScale().x != renderScale.x) ||
460              ( src->getRenderScale().y != renderScale.y) ||
461              ( ( src->getField() != OFX::eFieldNone) /* for DaVinci Resolve */ && ( src->getField() != args.fieldToRender) ) ) {
462             setPersistentMessage(OFX::Message::eMessageError, "", "OFX Host gave image with wrong scale or field properties");
463             OFX::throwSuiteStatusException(kOfxStatFailed);
464         }
465 #if 0
466     } else {
467         // src is considered black and transparent, just fill black to dst and return
468 #pragma message WARN("BUG: even when src is black and transparent, the output may not be black (cf. NoiseCImg)")
469         void* dstPixelData = NULL;
470         OfxRectI dstBounds;
471         OFX::PixelComponentEnum dstPixelComponents;
472         OFX::BitDepthEnum dstBitDepth;
473         int dstRowBytes;
474         getImageData(dst.get(), &dstPixelData, &dstBounds, &dstPixelComponents, &dstBitDepth, &dstRowBytes);
475 
476         int nComponents = dst->getPixelComponentCount();
477         switch (nComponents) {
478         case 1: {
479             OFX::BlackFiller<float, 1> fred(*this);
480             setupAndFill(fred, renderWindow, dstPixelData, dstBounds, dstPixelComponents, dstPixelComponentCOunt, dstBitDepth, dstRowBytes);
481             break;
482         }
483         case 2: {
484             OFX::BlackFiller<float, 2> fred(*this);
485             setupAndFill(fred, renderWindow, dstPixelData, dstBounds, dstPixelComponents, dstPixelComponentCOunt, dstBitDepth, dstRowBytes);
486             break;
487         }
488         case 3: {
489             OFX::BlackFiller<float, 3> fred(*this);
490             setupAndFill(fred, renderWindow, dstPixelData, dstBounds, dstPixelComponents, dstPixelComponentCOunt, dstBitDepth, dstRowBytes);
491             break;
492         }
493         case 4: {
494             OFX::BlackFiller<float, 4> fred(*this);
495             setupAndFill(fred, renderWindow, dstPixelData, dstBounds, dstPixelComponents, dstPixelComponentCOunt, dstBitDepth, dstRowBytes);
496             break;
497         }
498         default:
499             assert(false);
500             break;
501         } // switch
502 
503         return;
504 #endif
505     }
506 
507     const void *srcPixelData;
508     OfxRectI srcBounds;
509     OfxRectI srcRoD;
510     OFX::PixelComponentEnum srcPixelComponents;
511     int srcPixelComponentCount;
512     OFX::BitDepthEnum srcBitDepth;
513     //srcPixelBytes = getPixelBytes(srcPixelComponents, srcBitDepth);
514     int srcRowBytes;
515     if ( !src.get() ) {
516         srcPixelData = NULL;
517         srcBounds.x1 = srcBounds.y1 = srcBounds.x2 = srcBounds.y2 = 0;
518         srcRoD.x1 = srcRoD.y1 = srcRoD.x2 = srcRoD.y2 = 0;
519         srcPixelComponents = _srcClip ? _srcClip->getPixelComponents() : OFX::ePixelComponentNone;
520         srcPixelComponentCount = _srcClip ? _srcClip->getPixelComponentCount() : 0;
521         srcBitDepth = _srcClip ? _srcClip->getPixelDepth() : OFX::eBitDepthNone;
522         srcRowBytes = 0;
523     } else {
524         assert(_srcClip);
525         srcPixelData = src->getPixelData();
526         srcBounds = src->getBounds();
527         // = src->getRegionOfDefinition(); //  Nuke's image RoDs are wrong
528         if (_supportsTiles) {
529             OFX::Coords::toPixelEnclosing(_srcClip->getRegionOfDefinition(time), args.renderScale, _srcClip->getPixelAspectRatio(), &srcRoD);
530         } else {
531             // In Sony Catalyst Edit, clipGetRegionOfDefinition returns the RoD in pixels instead of canonical coordinates.
532             // in hosts that do not support tiles (such as Sony Catalyst Edit), the image RoD is the image Bounds anyway.
533             srcRoD = srcBounds;
534         }
535         srcPixelComponents = src->getPixelComponents();
536         srcPixelComponentCount = src->getPixelComponentCount();
537         srcBitDepth = src->getPixelDepth();
538         srcRowBytes = src->getRowBytes();
539     }
540 
541     void *dstPixelData = dst->getPixelData();
542     const OfxRectI& dstBounds = dst->getBounds();
543     OfxRectI dstRoD; // = dst->getRegionOfDefinition(); //  Nuke's image RoDs are wrong
544     if (_supportsTiles) {
545         OFX::Coords::toPixelEnclosing(_dstClip->getRegionOfDefinition(time), args.renderScale, _dstClip->getPixelAspectRatio(), &dstRoD);
546     } else {
547         // In Sony Catalyst Edit, clipGetRegionOfDefinition returns the RoD in pixels instead of canonical coordinates.
548         // in hosts that do not support tiles (such as Sony Catalyst Edit), the image RoD is the image Bounds anyway.
549         dstRoD = dstBounds;
550     }
551     //const OFX::PixelComponentEnum dstPixelComponents = dst->getPixelComponents();
552     //const OFX::BitDepthEnum dstBitDepth = dst->getPixelDepth();
553     const int dstRowBytes = dst->getRowBytes();
554 
555     if (!_supportsTiles) {
556         // http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxImageEffectPropSupportsTiles
557         //  If a clip or plugin does not support tiled images, then the host should supply full RoD images to the effect whenever it fetches one.
558         if ( src.get() ) {
559             assert(srcRoD.x1 == srcBounds.x1);
560             assert(srcRoD.x2 == srcBounds.x2);
561             assert(srcRoD.y1 == srcBounds.y1);
562             assert(srcRoD.y2 == srcBounds.y2); // crashes on Natron if kSupportsTiles=0 & kSupportsMultiResolution=1
563         }
564         assert(dstRoD.x1 == dstBounds.x1);
565         assert(dstRoD.x2 == dstBounds.x2);
566         assert(dstRoD.y1 == dstBounds.y1);
567         assert(dstRoD.y2 == dstBounds.y2); // crashes on Natron if kSupportsTiles=0 & kSupportsMultiResolution=1
568     }
569     if (!_supportsMultiResolution) {
570         // http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxImageEffectPropSupportsMultiResolution
571         //   Multiple resolution images mean...
572         //    input and output images can be of any size
573         //    input and output images can be offset from the origin
574         if ( src.get() ) {
575             assert(srcRoD.x1 == 0);
576             assert(srcRoD.y1 == 0);
577             assert(srcRoD.x1 == dstRoD.x1);
578             assert(srcRoD.x2 == dstRoD.x2);
579             assert(srcRoD.y1 == dstRoD.y1);
580             assert(srcRoD.y2 == dstRoD.y2); // crashes on Natron if kSupportsMultiResolution=0
581         }
582     }
583 
584     bool processR, processG, processB, processA;
585     if (_processR) {
586         _processR->getValueAtTime(time, processR);
587         _processG->getValueAtTime(time, processG);
588         _processB->getValueAtTime(time, processB);
589         _processA->getValueAtTime(time, processA);
590     } else {
591         processR = processG = processB = processA = true;
592     }
593     bool premult = _premult ? _premult->getValueAtTime(time) : false;
594     int premultChannel = _premultChannel ? _premultChannel->getValueAtTime(time) : 3;
595     double mix = _mix->getValueAtTime(time);
596     bool maskInvert = _maskInvert->getValueAtTime(time);
597     if (!processR && !processG && !processB) {
598         // no need to (un)premult if we don't change colors
599         premult = false;
600     }
601 
602     bool doMasking = ( ( !_maskApply || _maskApply->getValueAtTime(args.time) ) && _maskClip && _maskClip->isConnected() );
603     OFX::auto_ptr<const OFX::Image> mask(doMasking ? _maskClip->fetchImage(time) : 0);
604     OfxRectI processWindow = renderWindow; //!< the window where pixels have to be computed (may be smaller than renderWindow if mask is zero on the borders)
605 
606     if (mix == 0.) {
607         // no processing at all
608         processWindow.x2 = processWindow.x1;
609         processWindow.y2 = processWindow.y1;
610     }
611     if ( mask.get() ) {
612         if ( (mask->getRenderScale().x != renderScale.x) ||
613              ( mask->getRenderScale().y != renderScale.y) ||
614              ( ( mask->getField() != OFX::eFieldNone) /* for DaVinci Resolve */ && ( mask->getField() != args.fieldToRender) ) ) {
615             setPersistentMessage(OFX::Message::eMessageError, "", "OFX Host gave image with wrong scale or field properties");
616             OFX::throwSuiteStatusException(kOfxStatFailed);
617         }
618 
619         if (_supportsTiles) {
620             // shrink the processWindow at much as possible
621             // top
622             while ( processWindow.y2 > processWindow.y1 && maskLineIsZero(mask.get(), processWindow.x1, processWindow.x2, processWindow.y2 - 1, maskInvert) ) {
623                 --processWindow.y2;
624             }
625             // bottom
626             while ( processWindow.y2 > processWindow.y1 && maskLineIsZero(mask.get(), processWindow.x1, processWindow.x2, processWindow.y1, maskInvert) ) {
627                 ++processWindow.y1;
628             }
629             // left
630             while ( processWindow.x2 > processWindow.x1 && maskColumnIsZero(mask.get(), processWindow.x1, processWindow.y1, processWindow.y2, maskInvert) ) {
631                 ++processWindow.x1;
632             }
633             // right
634             while ( processWindow.x2 > processWindow.x1 && maskColumnIsZero(mask.get(), processWindow.x2 - 1, processWindow.y1, processWindow.y2, maskInvert) ) {
635                 --processWindow.x2;
636             }
637         }
638     }
639 
640     Params params;
641     getValuesAtTime(time, params);
642     int srcBoundary = getBoundary(params);
643     assert(0 <= srcBoundary && srcBoundary <= 2);
644 
645     // copy areas of renderWindow that are not within processWindow to dst
646 
647     OfxRectI copyWindowN, copyWindowS, copyWindowE, copyWindowW;
648     // top
649     copyWindowN.x1 = renderWindow.x1;
650     copyWindowN.x2 = renderWindow.x2;
651     copyWindowN.y1 = processWindow.y2;
652     copyWindowN.y2 = renderWindow.y2;
653     // bottom
654     copyWindowS.x1 = renderWindow.x1;
655     copyWindowS.x2 = renderWindow.x2;
656     copyWindowS.y1 = renderWindow.y1;
657     copyWindowS.y2 = processWindow.y1;
658     // left
659     copyWindowW.x1 = renderWindow.x1;
660     copyWindowW.x2 = processWindow.x1;
661     copyWindowW.y1 = processWindow.y1;
662     copyWindowW.y2 = processWindow.y2;
663     // right
664     copyWindowE.x1 = processWindow.x2;
665     copyWindowE.x2 = renderWindow.x2;
666     copyWindowE.y1 = processWindow.y1;
667     copyWindowE.y2 = processWindow.y2;
668     {
669         OFX::auto_ptr<OFX::PixelProcessorFilterBase> fred;
670         if (dstPixelComponentCount == 4) {
671             fred.reset( new OFX::PixelCopier<float, 4>(*this) );
672         } else if (dstPixelComponentCount == 3) {
673             fred.reset( new OFX::PixelCopier<float, 3>(*this) );
674         } else if (dstPixelComponentCount == 2) {
675             fred.reset( new OFX::PixelCopier<float, 2>(*this) );
676         }  else if (dstPixelComponentCount == 1) {
677             fred.reset( new OFX::PixelCopier<float, 1>(*this) );
678         }
679         assert( fred.get() );
680         if ( fred.get() ) {
681             setupAndCopy(*fred, time, copyWindowN, src.get(), mask.get(),
682                          srcPixelData, srcBounds, srcPixelComponents, srcPixelComponentCount, srcBitDepth, srcRowBytes, srcBoundary,
683                          dstPixelData, dstBounds, dstPixelComponents, dstPixelComponentCount, dstBitDepth, dstRowBytes,
684                          premult, premultChannel, mix, maskInvert);
685             setupAndCopy(*fred, time, copyWindowS, src.get(), mask.get(),
686                          srcPixelData, srcBounds, srcPixelComponents, srcPixelComponentCount, srcBitDepth, srcRowBytes, srcBoundary,
687                          dstPixelData, dstBounds, dstPixelComponents, dstPixelComponentCount, dstBitDepth, dstRowBytes,
688                          premult, premultChannel, mix, maskInvert);
689             setupAndCopy(*fred, time, copyWindowW, src.get(), mask.get(),
690                          srcPixelData, srcBounds, srcPixelComponents, srcPixelComponentCount, srcBitDepth, srcRowBytes, srcBoundary,
691                          dstPixelData, dstBounds, dstPixelComponents, dstPixelComponentCount, dstBitDepth, dstRowBytes,
692                          premult, premultChannel, mix, maskInvert);
693             setupAndCopy(*fred, time, copyWindowE, src.get(), mask.get(),
694                          srcPixelData, srcBounds, srcPixelComponents, srcPixelComponentCount, srcBitDepth, srcRowBytes, srcBoundary,
695                          dstPixelData, dstBounds, dstPixelComponents, dstPixelComponentCount, dstBitDepth, dstRowBytes,
696                          premult, premultChannel, mix, maskInvert);
697         }
698     }
699 
700     printRectI("srcRoD", srcRoD);
701     printRectI("srcBounds", srcBounds);
702     printRectI("dstRoD", dstRoD);
703     printRectI("dstBounds", dstBounds);
704     printRectI("renderWindow", renderWindow);
705     printRectI("processWindow", processWindow);
706 
707     if ( OFX::Coords::rectIsEmpty(processWindow) ) {
708         // the area that actually has to be processed is empty, the job is finished!
709         return;
710     }
711     assert(mix != 0.); // mix == 0. should give an empty processWindow
712 
713     // compute the src ROI (should be consistent with getRegionsOfInterest())
714     OfxRectI srcRoI;
715     getRoI(processWindow, renderScale, params, &srcRoI);
716     printRectI("srcRoI", srcRoI);
717     // intersect against the destination RoD
718     bool intersect = OFX::Coords::rectIntersection(srcRoI, dstRoD, &srcRoI);
719     printRectI("srcRoIIntersected", srcRoI);
720     if (!intersect) {
721         src.reset(NULL);
722         srcPixelData = NULL;
723         srcBounds.x1 = srcBounds.y1 = srcBounds.x2 = srcBounds.y2 = 0;
724         srcRoD.x1 = srcRoD.y1 = srcRoD.x2 = srcRoD.y2 = 0;
725         srcPixelComponents = _srcClip->getPixelComponents();
726         srcPixelComponentCount = _srcClip->getPixelComponentCount();
727         srcBitDepth = _srcClip->getPixelDepth();
728         srcRowBytes = 0;
729     }
730 
731     // The following checks may be wrong, because the srcRoI may be outside of the region of definition of src.
732     // It is not an error: areas outside of srcRoD should be considered black and transparent.
733     // IF THE FOLLOWING CODE HAS TO BE DISACTIVATED, PLEASE COMMENT WHY.
734     // This was disactivated by commit c47d07669b78a71960b204989d9c36f746d14a4c, then reactivated.
735     // DISACTIVATED AGAIN by FD 9/12/2014: boundary conditions are now handled by pixelcopier, and interstection with dstRoD was added above
736 #if 0 //def CIMGFILTER_INSTERSECT_ROI
737     OFX::Coords::rectIntersection(srcRoI, srcRoD, &srcRoI);
738     // the resulting ROI should be within the src bounds, or it means that the host didn't take into account the region of interest (see getRegionsOfInterest() )
739     assert(srcBounds.x1 <= srcRoI.x1 && srcRoI.x2 <= srcBounds.x2 &&
740            srcBounds.y1 <= srcRoI.y1 && srcRoI.y2 <= srcBounds.y2);
741     if ( (srcBounds.x1 > srcRoI.x1) || (srcRoI.x2 > srcBounds.x2) ||
742          ( srcBounds.y1 > srcRoI.y1) || ( srcRoI.y2 > srcBounds.y2) ) {
743         OFX::throwSuiteStatusException(kOfxStatFailed);
744     }
745 
746     if ( doMasking && (mix != 1.) ) {
747         // the renderWindow should also be contained within srcBounds, since we are mixing
748         assert(srcBounds.x1 <= renderWindow.x1 && renderWindow.x2 <= srcBounds.x2 &&
749                srcBounds.y1 <= renderWindow.y1 && renderWindow.y2 <= srcBounds.y2);
750         if ( (srcBounds.x1 > renderWindow.x1) || (renderWindow.x2 > srcBounds.x2) ||
751              ( srcBounds.y1 > renderWindow.y1) || ( renderWindow.y2 > srcBounds.y2) ) {
752             OFX::throwSuiteStatusException(kOfxStatFailed);
753         }
754     }
755 #endif
756 
757 #ifdef cimg_use_openmp
758     // set the number of OpenMP threads to a reasonable value
759     // (but remember that the OpenMP threads are not counted my the multithread suite)
760     {
761         unsigned int ncpus = OFX::MultiThread::getNumCPUs();
762         omp_set_num_threads( std::max(1u, ncpus) );
763         //printf("ncpus=%u\n", ncpus);
764     }
765 #endif
766 
767     // from here on, we do the following steps:
768     // 1- copy & unpremult all channels from srcRoI, from src to a tmp image of size srcRoI
769     // 2- extract channels to be processed from tmp to a cimg of size srcRoI (and do the interleaved to coplanar conversion)
770     // 3- process the cimg
771     // 4- copy back the processed channels from the cImg to tmp. only processWindow has to be copied
772     // 5- copy+premult+max+mix tmp to dst (only processWindow)
773 
774     //////////////////////////////////////////////////////////////////////////////////////////
775     // 1- copy & unpremult all channels from srcRoI, from src to a tmp image of size srcRoI
776     const OfxRectI tmpBounds = srcRoI;
777     const OFX::PixelComponentEnum tmpPixelComponents = srcPixelData ? srcPixelComponents : dstPixelComponents;
778     const int tmpPixelComponentCount = srcPixelData ? srcPixelComponentCount : dstPixelComponentCount;
779     const OFX::BitDepthEnum tmpBitDepth = OFX::eBitDepthFloat;
780     const int tmpWidth = tmpBounds.x2 - tmpBounds.x1;
781     const int tmpHeight = tmpBounds.y2 - tmpBounds.y1;
782     const size_t tmpRowBytes = (size_t)tmpPixelComponentCount * getComponentBytes(tmpBitDepth) * tmpWidth;
783     size_t tmpSize = tmpRowBytes * tmpHeight;
784     OFX::auto_ptr<OFX::ImageMemory> tmpData;
785     float *tmpPixelData = NULL;
786     if (tmpSize > 0) {
787         tmpData.reset( new OFX::ImageMemory(tmpSize, this) );
788         tmpPixelData = (float*)tmpData->lock();
789 
790         OFX::auto_ptr<OFX::PixelProcessorFilterBase> fred;
791         if ( !src.get() ) {
792             // no src, fill with black & transparent
793             fred.reset( new OFX::BlackFiller<float>(*this, dstPixelComponentCount) );
794         } else {
795             if (dstPixelComponents == OFX::ePixelComponentRGBA) {
796                 fred.reset( new OFX::PixelCopierUnPremult<float, 4, 1, float, 4, 1>(*this) );
797             } else if (dstPixelComponentCount == 4) {
798                 // just copy, no premult
799                 fred.reset( new OFX::PixelCopier<float, 4>(*this) );
800             } else if (dstPixelComponentCount == 3) {
801                 // just copy, no premult
802                 fred.reset( new OFX::PixelCopier<float, 3>(*this) );
803             } else if (dstPixelComponentCount == 2) {
804                 // just copy, no premult
805                 fred.reset( new OFX::PixelCopier<float, 2>(*this) );
806             }  else if (dstPixelComponentCount == 1) {
807                 // just copy, no premult
808                 fred.reset( new OFX::PixelCopier<float, 1>(*this) );
809             }
810         }
811         assert( fred.get() );
812         if ( fred.get() ) {
813             setupAndCopy(*fred, time, srcRoI, src.get(), mask.get(),
814                          srcPixelData, srcBounds, srcPixelComponents, srcPixelComponentCount, srcBitDepth, srcRowBytes, srcBoundary,
815                          tmpPixelData, tmpBounds, tmpPixelComponents, tmpPixelComponentCount, tmpBitDepth, tmpRowBytes,
816                          premult, premultChannel, mix, maskInvert);
817         }
818     }
819     if ( abort() ) {
820         return;
821     }
822 
823     //////////////////////////////////////////////////////////////////////////////////////////
824     // 2- extract channels to be processed from tmp to a cimg of size srcRoI (and do the interleaved to coplanar conversion)
825 
826     // allocate the cimg data to hold the src ROI
827     int cimgSpectrum;
828     if (!_supportsComponentRemapping) {
829         cimgSpectrum = tmpPixelComponentCount;
830     } else {
831         switch (tmpPixelComponents) {
832         case OFX::ePixelComponentAlpha:
833             cimgSpectrum = (int)processA;
834             break;
835         case OFX::ePixelComponentXY:
836             cimgSpectrum = (int)processR + (int)processG + (int) processB;
837             break;
838         case OFX::ePixelComponentRGB:
839             cimgSpectrum = (int)processR + (int)processG + (int) processB;
840             break;
841         case OFX::ePixelComponentRGBA:
842             cimgSpectrum = (int)processR + (int)processG + (int) processB + (int)processA;
843             break;
844         default:
845             cimgSpectrum = 0;
846         }
847     }
848     const int cimgWidth = srcRoI.x2 - srcRoI.x1;
849     const int cimgHeight = srcRoI.y2 - srcRoI.y1;
850     const size_t cimgSize = cimgWidth * cimgHeight * cimgSpectrum * sizeof(cimgpix_t);
851     std::vector<int> srcChannel(cimgSpectrum, -1);
852 
853     int alphaChannel = -1;
854     if (!_supportsComponentRemapping) {
855         for (int c = 0; c < tmpPixelComponentCount; ++c) {
856             srcChannel[c] = c;
857         }
858         assert(tmpPixelComponentCount == cimgSpectrum);
859     } else {
860         if (tmpPixelComponentCount == 1) {
861             if (processA) {
862                 assert(cimgSpectrum == 1);
863                 srcChannel[0] = 0;
864                 alphaChannel = 0;
865             } else {
866                 assert(cimgSpectrum == 0);
867             }
868         } else {
869             int c = 0;
870             if (processR) {
871                 srcChannel[c] = 0;
872                 ++c;
873             }
874             if (processG) {
875                 srcChannel[c] = 1;
876                 ++c;
877             }
878             if (processB) {
879                 srcChannel[c] = 2;
880                 ++c;
881             }
882             if ( processA && (tmpPixelComponentCount >= 4) ) {
883                 srcChannel[c] = 3;
884                 alphaChannel = c;
885                 ++c;
886             }
887             assert(c == cimgSpectrum);
888         }
889     }
890     if (cimgSize) { // may be zero if no channel is processed
891         OFX::auto_ptr<OFX::ImageMemory> cimgData( new OFX::ImageMemory(cimgSize, this) );
892         cimgpix_t *cimgPixelData = (cimgpix_t*)cimgData->lock();
893         cimg_library::CImg<cimgpix_t> maskcimg;
894         cimg_library::CImg<cimgpix_t> cimg(cimgPixelData, cimgWidth, cimgHeight, 1, cimgSpectrum, true);
895 
896         if (tmpSize > 0) {
897             for (int c = 0; c < cimgSpectrum; ++c) {
898                 cimgpix_t *dst = cimg.data(0, 0, 0, c);
899                 const float *src = tmpPixelData + srcChannel[c];
900                 for (unsigned int siz = cimgWidth * cimgHeight; siz; --siz, src += tmpPixelComponentCount, ++dst) {
901                     *dst = *src;
902                 }
903             }
904         } else {
905             cimg.fill(0);
906         }
907         if ( abort() ) {
908             return;
909         }
910 
911         assert(sizeof(cimgpix_t) == 4); // the following only works for float pix
912         if (_usesMask) {
913             maskcimg.assign(cimgWidth, cimgHeight, 1, 1);
914             if (!mask.get()) {
915                 maskcimg.fill(1.);
916             } else {
917                 copyPixels(*this,
918                            srcRoI,
919                            mask.get(),
920                            maskcimg.data(),
921                            srcRoI,
922                            OFX::ePixelComponentAlpha,
923                            1,
924                            OFX::eBitDepthFloat,
925                            cimgWidth * sizeof(float));
926                 if(maskInvert) {
927                     maskcimg *= -1;
928                     maskcimg += 1;
929                 }
930             }
931         }
932 
933         //////////////////////////////////////////////////////////////////////////////////////////
934         // 3- process the cimg
935         printRectI("render srcRoI", srcRoI);
936 #if defined(HAVE_THREAD_LOCAL) || defined(HAVE_PTHREAD)
937 #  if defined(HAVE_THREAD_LOCAL)
938         tls::gImageEffect = this;
939 #  else
940         OFX::ImageEffect **_ptr = (OFX::ImageEffect **)pthread_getspecific(tls::gImageEffect_key);
941         assert (NULL != _ptr);
942         *_ptr = this;
943 #  endif
944         try {
945             render(args, params, srcRoI.x1, srcRoI.y1, maskcimg, cimg, alphaChannel);
946         } catch (cimg_library::CImgAbortException) {
947 #  if defined(HAVE_THREAD_LOCAL)
948             tls::gImageEffect = 0;
949 #  else
950             *_ptr = 0;
951 #  endif
952 
953             return;
954         }
955 
956 #  if defined(HAVE_THREAD_LOCAL)
957         tls::gImageEffect = 0;
958 #  else
959         *_ptr = 0;
960 #  endif
961 #else
962         render(args, params, srcRoI.x1, srcRoI.y1, maskcimg, cimg, alphaChannel);
963 #endif
964         // check that the dimensions didn't change
965         assert(cimg.width() == cimgWidth && cimg.height() == cimgHeight && cimg.depth() == 1 && cimg.spectrum() == cimgSpectrum);
966         if ( abort() ) {
967             return;
968         }
969 
970         //////////////////////////////////////////////////////////////////////////////////////////
971         // 4- copy back the processed channels from the cImg to tmp. only processWindow has to be copied
972 
973         // We copy the whole srcRoI. This could be optimized to copy only renderWindow
974         for (int c = 0; c < cimgSpectrum; ++c) {
975             const cimgpix_t *src = cimg.data(0, 0, 0, c);
976             float *dst = tmpPixelData + srcChannel[c];
977             for (unsigned int siz = cimgWidth * cimgHeight; siz; --siz, ++src, dst += tmpPixelComponentCount) {
978                 *dst = *src;
979             }
980         }
981     }
982     if ( abort() ) {
983         return;
984     }
985 
986     //////////////////////////////////////////////////////////////////////////////////////////
987     // 5- copy+premult+max+mix tmp to dst (only processWindow)
988 
989     {
990         OFX::auto_ptr<OFX::PixelProcessorFilterBase> fred;
991         if (dstPixelComponents == OFX::ePixelComponentRGBA) {
992             fred.reset( new OFX::PixelCopierPremultMaskMix<float, 4, 1, float, 4, 1>(*this) );
993         } else if (dstPixelComponentCount == 4) {
994             // just copy, no premult
995             if (doMasking) {
996                 fred.reset( new OFX::PixelCopierMaskMix<float, 4, 1, true>(*this) );
997             } else {
998                 fred.reset( new OFX::PixelCopierMaskMix<float, 4, 1, false>(*this) );
999             }
1000         } else if (dstPixelComponentCount == 3) {
1001             // just copy, no premult
1002             if (doMasking) {
1003                 fred.reset( new OFX::PixelCopierMaskMix<float, 3, 1, true>(*this) );
1004             } else {
1005                 fred.reset( new OFX::PixelCopierMaskMix<float, 3, 1, false>(*this) );
1006             }
1007         } else if (dstPixelComponentCount == 2) {
1008             // just copy, no premult
1009             if (doMasking) {
1010                 fred.reset( new OFX::PixelCopierMaskMix<float, 2, 1, true>(*this) );
1011             } else {
1012                 fred.reset( new OFX::PixelCopierMaskMix<float, 2, 1, false>(*this) );
1013             }
1014         }  else if (dstPixelComponentCount == 1) {
1015             // just copy, no premult
1016             assert(srcPixelComponents == OFX::ePixelComponentAlpha);
1017             if (doMasking) {
1018                 fred.reset( new OFX::PixelCopierMaskMix<float, 1, 1, true>(*this) );
1019             } else {
1020                 fred.reset( new OFX::PixelCopierMaskMix<float, 1, 1, false>(*this) );
1021             }
1022         }
1023         assert( fred.get() );
1024         if ( fred.get() ) {
1025             setupAndCopy(*fred, time, processWindow, src.get(), mask.get(),
1026                          tmpPixelData, tmpBounds, tmpPixelComponents, tmpPixelComponentCount, tmpBitDepth, tmpRowBytes, 0,
1027                          dstPixelData, dstBounds, dstPixelComponents, dstPixelComponentCount, dstBitDepth, dstRowBytes,
1028                          premult, premultChannel, mix, maskInvert);
1029         }
1030     }
1031 
1032     //////////////////////////////////////////////////////////////////////////////////////////
1033     // done!
1034 } // >::render
1035 
1036 // override the roi call
1037 // Required if the plugin requires a region from the inputs which is different from the rendered region of the output.
1038 // (this is the case here)
1039 template <class Params, bool sourceIsOptional>
1040 void
getRegionsOfInterest(const OFX::RegionsOfInterestArguments & args,OFX::RegionOfInterestSetter & rois)1041 CImgFilterPluginHelper<Params, sourceIsOptional>::getRegionsOfInterest(const OFX::RegionsOfInterestArguments &args,
1042                                                                        OFX::RegionOfInterestSetter &rois)
1043 {
1044     if ( !_supportsRenderScale && ( (args.renderScale.x != 1.) || (args.renderScale.y != 1.) ) ) {
1045         OFX::throwSuiteStatusException(kOfxStatFailed);
1046     }
1047     const double time = args.time;
1048     const OfxRectD& regionOfInterest = args.regionOfInterest;
1049     OfxRectD srcRoI;
1050     double mix = 1.;
1051     bool doMasking = ( ( !_maskApply || _maskApply->getValueAtTime(args.time) ) && _maskClip && _maskClip->isConnected() );
1052     if (doMasking) {
1053         _mix->getValueAtTime(time, mix);
1054         if (mix == 0.) {
1055             // identity transform
1056             //srcRoI = regionOfInterest;
1057 
1058             //rois.setRegionOfInterest(*_srcClip, srcRoI);
1059             return;
1060         }
1061     }
1062 
1063     Params params;
1064     getValuesAtTime(args.time, params);
1065 
1066     double pixelaspectratio = ( _srcClip && _srcClip->isConnected() ) ? _srcClip->getPixelAspectRatio() : 1.;
1067     OfxRectI rectPixel;
1068     OFX::Coords::toPixelEnclosing(regionOfInterest, args.renderScale, pixelaspectratio, &rectPixel);
1069     OfxRectI srcRoIPixel;
1070     getRoI(rectPixel, args.renderScale, params, &srcRoIPixel);
1071     OFX::Coords::toCanonical(srcRoIPixel, args.renderScale, pixelaspectratio, &srcRoI);
1072 
1073     if ( doMasking && (mix != 1.) ) {
1074         // for masking or mixing, we also need the source image.
1075         // compute the bounding box with the default ROI
1076         OFX::Coords::rectBoundingBox(srcRoI, regionOfInterest, &srcRoI);
1077     }
1078 
1079     // no need to set it on mask (the default ROI is OK)
1080     rois.setRegionOfInterest(*_srcClip, srcRoI);
1081 }
1082 
1083 template <class Params, bool sourceIsOptional>
1084 bool
getRegionOfDefinition(const OFX::RegionOfDefinitionArguments & args,OfxRectD & rod)1085 CImgFilterPluginHelper<Params, sourceIsOptional>::getRegionOfDefinition(const OFX::RegionOfDefinitionArguments &args,
1086                                                                         OfxRectD &rod)
1087 {
1088     if ( !_supportsRenderScale && ( (args.renderScale.x != 1.) || (args.renderScale.y != 1.) ) ) {
1089         OFX::throwSuiteStatusException(kOfxStatFailed);
1090     }
1091     Params params;
1092     getValuesAtTime(args.time, params);
1093 
1094     OfxRectI srcRoDPixel = {0, 0, 0, 0};
1095     {
1096         double pixelaspectratio = ( _srcClip && _srcClip->isConnected() ) ? _srcClip->getPixelAspectRatio() : 1.;
1097         if (_srcClip) {
1098             OFX::Coords::toPixelEnclosing(_srcClip->getRegionOfDefinition(args.time), args.renderScale, pixelaspectratio, &srcRoDPixel);
1099         }
1100     }
1101     OfxRectI rodPixel;
1102     bool ret = getRegionOfDefinition(srcRoDPixel, args.renderScale, params, &rodPixel);
1103     if (ret) {
1104         double pixelaspectratio = _dstClip ? _dstClip->getPixelAspectRatio() : 1.;
1105         OFX::Coords::toCanonical(rodPixel, args.renderScale, pixelaspectratio, &rod);
1106 
1107         return true;
1108     }
1109 
1110     return false;
1111 }
1112 
1113 template <class Params, bool sourceIsOptional>
1114 bool
isIdentity(const OFX::IsIdentityArguments & args,OFX::Clip * & identityClip,double &,int &,std::string &)1115 CImgFilterPluginHelper<Params, sourceIsOptional>::isIdentity(const OFX::IsIdentityArguments &args,
1116                                                              OFX::Clip * &identityClip,
1117                                                              double & /*identityTime*/
1118                                                              , int& /*view*/, std::string& /*plane*/)
1119 {
1120     if ( !_supportsRenderScale && ( (args.renderScale.x != 1.) || (args.renderScale.y != 1.) ) ) {
1121         OFX::throwSuiteStatusException(kOfxStatFailed);
1122     }
1123     const double time = args.time;
1124     double mix;
1125     _mix->getValueAtTime(time, mix);
1126     if (mix == 0.) {
1127         identityClip = _srcClip;
1128 
1129         return true;
1130     }
1131 
1132     if (_processR) {
1133         bool processR;
1134         bool processG;
1135         bool processB;
1136         bool processA;
1137         _processR->getValueAtTime(args.time, processR);
1138         _processG->getValueAtTime(args.time, processG);
1139         _processB->getValueAtTime(args.time, processB);
1140         _processA->getValueAtTime(args.time, processA);
1141         if (!processR && !processG && !processB && !processA) {
1142             identityClip = _srcClip;
1143 
1144             return true;
1145         }
1146     }
1147 
1148     Params params;
1149     getValuesAtTime(time, params);
1150     if ( isIdentity(args, params) ) {
1151         identityClip = _srcClip;
1152 
1153         return true;
1154     }
1155 
1156     bool doMasking = ( ( !_maskApply || _maskApply->getValueAtTime(args.time) ) && _maskClip && _maskClip->isConnected() );
1157     if (doMasking) {
1158         bool maskInvert;
1159         _maskInvert->getValueAtTime(args.time, maskInvert);
1160         if (!maskInvert) {
1161             OfxRectI maskRoD;
1162             if (OFX::getImageEffectHostDescription()->supportsMultiResolution) {
1163                 // In Sony Catalyst Edit, clipGetRegionOfDefinition returns the RoD in pixels instead of canonical coordinates.
1164                 // In hosts that do not support multiResolution (e.g. Sony Catalyst Edit), all inputs have the same RoD anyway.
1165                 OFX::Coords::toPixelEnclosing(_maskClip->getRegionOfDefinition(args.time), args.renderScale, _maskClip->getPixelAspectRatio(), &maskRoD);
1166                 // effect is identity if the renderWindow doesn't intersect the mask RoD
1167                 if ( !OFX::Coords::rectIntersection<OfxRectI>(args.renderWindow, maskRoD, 0) ) {
1168                     identityClip = _srcClip;
1169 
1170                     return true;
1171                 }
1172             }
1173         }
1174     }
1175 
1176     return false;
1177 } // >::isIdentity
1178 
1179 // functions for a reproductible random number generator (used in CImgNoise.cpp and CImgPlasma.cpp)
1180 inline unsigned int
cimg_hash(unsigned int a)1181 cimg_hash(unsigned int a)
1182 {
1183     a = (a ^ 61) ^ (a >> 16);
1184     a = a + (a << 3);
1185     a = a ^ (a >> 4);
1186     a = a * 0x27d4eb2d;
1187     a = a ^ (a >> 15);
1188 
1189     return a;
1190 }
1191 
1192 // returns a value from 0 to 0x100000000ULL excluded
1193 inline unsigned int
cimg_irand(unsigned int seed,int x,int y,int nComponents)1194 cimg_irand(unsigned int seed, int x, int y, int nComponents)
1195 {
1196     return cimg_hash(cimg_hash(cimg_hash(seed ^ x) ^ y) ^ nComponents);
1197 }
1198 
1199 inline double
cimg_rand(unsigned int seed,int x,int y,int nComponents,const double val_min,const double val_max)1200 cimg_rand(unsigned int seed, int x, int y, int nComponents, const double val_min, const double val_max)
1201 {
1202     const double val = cimg_irand(seed, x, y, nComponents) / ( (double)0x100000000ULL );
1203     return val_min + (val_max - val_min)*val;
1204 }
1205 
1206 //! Return a random variable uniformely distributed between [0,val_max].
1207 /**
1208  **/
1209 inline double
1210 cimg_rand(unsigned int seed, int x, int y, int nComponents, const double val_max = 1.)
1211 {
1212     return cimg_rand(seed, x, y, nComponents, 0, val_max);
1213 }
1214 
1215 //! Return a random variable following a gaussian distribution and a standard deviation of 1.
1216 /**
1217  **/
1218 inline double
cimg_grand(unsigned int seed,int x,int y,int nComponents)1219 cimg_grand(unsigned int seed, int x, int y, int nComponents)
1220 {
1221     double x1, w;
1222     unsigned int s = seed;
1223     do {
1224         unsigned int r1 = cimg_irand(s, x, y, nComponents);
1225         unsigned int r2 = cimg_irand(r1, x, y, nComponents);
1226         s = r2;
1227 
1228         const double x2 =  2 * (double) r2 / ( (double)0x100000000ULL ) - 1.;
1229         x1 =  2 * (double) r1 / ( (double)0x100000000ULL ) - 1.;
1230         w = x1*x1 + x2*x2;
1231     } while (w<=0 || w>=1.0);
1232     return x1*std::sqrt((-2*std::log(w))/w);
1233 }
1234 
1235 //! Return a random variable following a Poisson distribution of parameter z.
1236 /**
1237  **/
1238 inline unsigned int
cimg_prand(unsigned int seed,int x,int y,int nComponents,const double z)1239 cimg_prand(unsigned int seed, int x, int y, int nComponents, const double z)
1240 {
1241     if (z<=1.0e-10) {
1242         return 0;
1243     }
1244     if (z>100) {
1245         return (unsigned int)((std::sqrt(z) * cimg_grand(seed, x, y, nComponents)) + z);
1246     }
1247     unsigned int k = 0;
1248     const double y1 = std::exp(-z);
1249     for (double s = 1.0; s >= y1; ++k) {
1250         s *= cimg_rand(seed+1, x, y, nComponents);
1251     }
1252     return k > 0 ? k - 1 : 0;
1253 }
1254 
1255 #endif // ifndef Misc_CImgFilter_h
1256