1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3  * This file is part of openfx-supportext <https://github.com/devernay/openfx-supportext>,
4  * Copyright (C) 2013-2018 INRIA
5  *
6  * openfx-supportext is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * openfx-supportext is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with openfx-supportext.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>
18  * ***** END LICENSE BLOCK ***** */
19 
20 
21 #ifndef openfx_supportext_ofxsPixelProcessor_h
22 #define openfx_supportext_ofxsPixelProcessor_h
23 
24 /*
25  * ofxsPixelProcessor: generic multithreaded OFX pixel processor
26  */
27 
28 #include <cassert>
29 #include <algorithm>
30 
31 #include "ofxsImageEffect.h"
32 #include "ofxsMultiThread.h"
33 #include "ofxsThreadSuite.h"
34 
35 /** @file This file contains a useful base class that can be used to process images
36 
37    The code below is not so much a skin on the base OFX classes, but code used in implementing
38    specific image processing algorithms. As such it does not sit in the support include lib, but in
39    its own include directory.
40  */
41 
42 namespace OFX {
43 inline void
getImageData(OFX::Image * img,void ** pixelData,OfxRectI * bounds,OFX::PixelComponentEnum * pixelComponents,OFX::BitDepthEnum * bitDepth,int * rowBytes)44 getImageData(OFX::Image* img,
45              void** pixelData,
46              OfxRectI* bounds,
47              OFX::PixelComponentEnum* pixelComponents,
48              OFX::BitDepthEnum* bitDepth,
49              int* rowBytes)
50 {
51     *pixelData = img->getPixelData();
52     *bounds = img->getBounds();
53     *pixelComponents = img->getPixelComponents();
54     *bitDepth = img->getPixelDepth();
55     *rowBytes = img->getRowBytes();
56 }
57 
58 inline void
getImageData(const OFX::Image * img,const void ** pixelData,OfxRectI * bounds,OFX::PixelComponentEnum * pixelComponents,OFX::BitDepthEnum * bitDepth,int * rowBytes)59 getImageData(const OFX::Image* img,
60              const void** pixelData,
61              OfxRectI* bounds,
62              OFX::PixelComponentEnum* pixelComponents,
63              OFX::BitDepthEnum* bitDepth,
64              int* rowBytes)
65 {
66     if (img) {
67         *pixelData = img->getPixelData();
68         *bounds = img->getBounds();
69         *pixelComponents = img->getPixelComponents();
70         *bitDepth = img->getPixelDepth();
71         *rowBytes = img->getRowBytes();
72     } else {
73         *pixelData = 0;
74         bounds->x1 = bounds->x2 = bounds->y1 = bounds->y2 = 0;
75         *pixelComponents = ePixelComponentNone;
76         *bitDepth = eBitDepthNone;
77         *rowBytes = 0;
78     }
79 }
80 
81 inline
82 int
getComponentBytes(OFX::BitDepthEnum bitDepth)83 getComponentBytes(OFX::BitDepthEnum bitDepth)
84 {
85     // compute bytes per component
86     switch (bitDepth) {
87     case OFX::eBitDepthNone:
88 
89         return 0; break;
90     case OFX::eBitDepthUByte:
91 
92         return 1; break;
93     case OFX::eBitDepthUShort:
94 
95         return 2; break;
96     case OFX::eBitDepthHalf:
97 
98         return 2; break;
99     case OFX::eBitDepthFloat:
100 
101         return 4; break;
102 #ifdef OFX_EXTENSIONS_VEGAS
103     case OFX::eBitDepthUByteBGRA:
104 
105         return 1; break;
106     case OFX::eBitDepthUShortBGRA:
107 
108         return 2; break;
109     case OFX::eBitDepthFloatBGRA:
110 
111         return 4; break;
112 #endif
113     case OFX::eBitDepthCustom:
114 
115         return 0; break;
116     }
117 
118     return 0;
119 }
120 
121 inline
122 void*
getPixelAddress(void * pixelData,const OfxRectI & bounds,int pixelComponentCount,OFX::BitDepthEnum bitDepth,int rowBytes,int x,int y)123 getPixelAddress(void* pixelData,
124                 const OfxRectI & bounds,
125                 int pixelComponentCount,
126                 OFX::BitDepthEnum bitDepth,
127                 int rowBytes,
128                 int x,
129                 int y)
130 {
131     int pixelBytes = pixelComponentCount * getComponentBytes(bitDepth);
132 
133     // are we in the image bounds
134     if ( ( x < bounds.x1) || ( x >= bounds.x2) || ( y < bounds.y1) || ( y >= bounds.y2) || ( pixelBytes == 0) ) {
135         return 0;
136     }
137     char *pix = (char *) ( ( (char *) pixelData ) + (size_t)((y - bounds.y1) * rowBytes) );
138     pix += (x - bounds.x1) * pixelBytes;
139 
140     return (void *) pix;
141 }
142 
143 inline
144 const void*
145 getPixelAddress(const void* pixelData,
146                 const OfxRectI & bounds,
147                 int pixelComponentCount,
148                 OFX::BitDepthEnum bitDepth,
149                 int rowBytes,
150                 int x,
151                 int y,
152                 bool withinBoundsCheck = true)
153 {
154     int pixelBytes = pixelComponentCount * getComponentBytes(bitDepth);
155 
156     // are we in the image bounds
157     if ( withinBoundsCheck && (
158              ( x < bounds.x1) || ( x >= bounds.x2) || ( y < bounds.y1) || ( y >= bounds.y2) || ( pixelBytes == 0) ) ) {
159         return 0;
160     }
161     char *pix = (char *) ( ( (char *) pixelData ) + (size_t)(y - bounds.y1) * rowBytes );
162     pix += (x - bounds.x1) * pixelBytes;
163 
164     return (void *) pix;
165 }
166 
167 ////////////////////////////////////////////////////////////////////////////////
168 // base class to process images with
169 class PixelProcessor
170     : public OFX::MultiThread::Processor
171 {
172 protected:
173     OFX::ImageEffect &_effect;          /**< @brief effect to render with */
174     //OFX::Image       *_dstImg;        /**< @brief image to process into */
175     void* _dstPixelData;
176     OfxRectI _dstBounds;
177     OFX::PixelComponentEnum _dstPixelComponents;
178     int _dstPixelComponentCount;
179     OFX::BitDepthEnum _dstBitDepth;
180     int _dstPixelBytes;
181     int _dstRowBytes;
182     OfxRectI _renderWindow;               /**< @brief render window to use */
183 
184 public:
185     /** @brief ctor */
PixelProcessor(OFX::ImageEffect & effect)186     PixelProcessor(OFX::ImageEffect &effect)
187         : _effect(effect)
188         , _dstPixelData(NULL)
189         , _dstBounds()
190         , _dstPixelComponents(OFX::ePixelComponentNone)
191         , _dstPixelComponentCount(0)
192         , _dstBitDepth(OFX::eBitDepthNone)
193         , _dstPixelBytes(0)
194         , _dstRowBytes(0)
195     {
196         _renderWindow.x1 = _renderWindow.y1 = _renderWindow.x2 = _renderWindow.y2 = 0;
197     }
198 
199     /** @brief set the destination image */
setDstImg(OFX::Image * v)200     void setDstImg(OFX::Image *v)
201     {
202         _dstPixelData = v->getPixelData();
203         _dstBounds = v->getBounds();
204         _dstPixelComponents = v->getPixelComponents();
205         _dstPixelComponentCount = v->getPixelComponentCount();
206         _dstBitDepth = v->getPixelDepth();
207         _dstPixelBytes = _dstPixelComponentCount * getComponentBytes(_dstBitDepth);
208         _dstRowBytes = v->getRowBytes();
209     }
210 
211     /** @brief set the destination image */
setDstImg(void * dstPixelData,const OfxRectI & dstBounds,OFX::PixelComponentEnum dstPixelComponents,int dstPixelComponentCount,OFX::BitDepthEnum dstPixelDepth,int dstRowBytes)212     void setDstImg(void *dstPixelData,
213                    const OfxRectI & dstBounds,
214                    OFX::PixelComponentEnum dstPixelComponents,
215                    int dstPixelComponentCount,
216                    OFX::BitDepthEnum dstPixelDepth,
217                    int dstRowBytes)
218     {
219         _dstPixelData = dstPixelData;
220         _dstBounds = dstBounds;
221         _dstPixelComponents = dstPixelComponents;
222         _dstPixelComponentCount = dstPixelComponentCount;
223         _dstBitDepth = dstPixelDepth;
224         _dstPixelBytes = _dstPixelComponentCount * getComponentBytes(_dstBitDepth);
225         _dstRowBytes = dstRowBytes;
226     }
227 
228     /** @brief reset the render window */
setRenderWindow(OfxRectI rect)229     void setRenderWindow(OfxRectI rect)
230     {
231         _renderWindow = rect;
232     }
233 
234     /** @brief overridden from OFX::MultiThread::Processor. This function is called once on each SMP thread by the base class */
multiThreadFunction(unsigned int threadId,unsigned int nThreads)235     void multiThreadFunction(unsigned int threadId,
236                              unsigned int nThreads)
237     {
238         OfxRectI win = _renderWindow;
239 
240         MultiThread::getThreadRange(threadId, nThreads, _renderWindow.y1, _renderWindow.y2, &win.y1, &win.y2);
241         if ( (win.y2 - win.y1) > 0 ) {
242             // and render that thread on each
243             multiThreadProcessImages(win);
244         }
245     }
246 
247     /** @brief called before any MP is done */
preProcess(void)248     virtual void preProcess(void)
249     {
250     }
251 
252     /** @brief this is called by multiThreadFunction to actually process images, override in derived classes */
253     virtual void multiThreadProcessImages(OfxRectI window) = 0;
254 
255     /** @brief called before any MP is done */
postProcess(void)256     virtual void postProcess(void)
257     {
258     }
259 
260     /** @brief called to process everything */
process(void)261     virtual void process(void)
262     {
263         // _dstPixelData may be NULL (e.g. when doing multi-pass, as in FrameBlend)
264         assert( !_dstPixelData ||
265                 (_dstBounds.x1 <= _renderWindow.x1 && _renderWindow.x2 <= _dstBounds.x2 &&
266                  _dstBounds.y1 <= _renderWindow.y1 && _renderWindow.y2 <= _dstBounds.y2) );
267         // is it OK ?
268         if ( ( _dstPixelData && !( ( _dstBounds.x1 <= _renderWindow.x1) && ( _renderWindow.x2 <= _dstBounds.x2) &&
269                                    ( _dstBounds.y1 <= _renderWindow.y1) && ( _renderWindow.y2 <= _dstBounds.y2) ) ) ||
270              (_renderWindow.x1 >= _renderWindow.x2) ||
271              (_renderWindow.y1 >= _renderWindow.y2) ) {
272             return;
273         }
274 
275         // call the pre MP pass
276         preProcess();
277 
278         // make sure there are at least 4096 pixels per CPU and at least 1 line par CPU
279         unsigned int nCPUs = (unsigned int)( (std::min)(_renderWindow.x2 - _renderWindow.x1, 4096) *
280                                             (_renderWindow.y2 - _renderWindow.y1) ) / 4096;
281         // make sure the number of CPUs is valid (and use at least 1 CPU)
282         nCPUs = (std::max)( 1u, (std::min)( nCPUs, OFX::MultiThread::getNumCPUs() ) );
283 
284         // call the base multi threading code, should put a pre & post thread calls in too
285         multiThread(nCPUs);
286 
287         // call the post MP pass
288         postProcess();
289     }
290 
291 protected:
getDstPixelAddress(int x,int y)292     void* getDstPixelAddress(int x,
293                              int y) const
294     {
295         // are we in the image bounds
296         if ( !_dstPixelData || ( x < _dstBounds.x1) || ( x >= _dstBounds.x2) || ( y < _dstBounds.y1) || ( y >= _dstBounds.y2) || ( _dstPixelBytes == 0) ) {
297             return 0;
298         }
299 
300         char *pix = (char *) ( ( (char *) _dstPixelData ) + (size_t)((y - _dstBounds.y1) * _dstRowBytes) );
301         pix += (x - _dstBounds.x1) * _dstPixelBytes;
302 
303         return (void *) pix;
304     }
305 };
306 
307 
308 // base class for a processor with a single source image
309 class PixelProcessorFilterBase
310     : public OFX::PixelProcessor
311 {
312 protected:
313     const void *_srcPixelData;
314     OfxRectI _srcBounds;
315     OFX::PixelComponentEnum _srcPixelComponents;
316     int _srcPixelComponentCount;
317     OFX::BitDepthEnum _srcBitDepth;
318     int _srcPixelBytes;
319     int _srcRowBytes;
320     int _srcBoundary; // Boundary conditions 0: Black/Dirichlet 1:Nearest/Neymann 2:Repeat/Periodic
321     const OFX::Image *_origImg;
322     const OFX::Image *_maskImg;
323     bool _premult;
324     int _premultChannel;
325     bool _doMasking;
326     double _mix;
327     bool _maskInvert;
328 
329 public:
330     /** @brief no arg ctor */
PixelProcessorFilterBase(OFX::ImageEffect & instance)331     PixelProcessorFilterBase(OFX::ImageEffect &instance)
332         : OFX::PixelProcessor(instance)
333         , _srcPixelData(NULL)
334         , _srcBounds()
335         , _srcPixelComponents(OFX::ePixelComponentNone)
336         , _srcPixelComponentCount(0)
337         , _srcBitDepth(OFX::eBitDepthNone)
338         , _srcPixelBytes(0)
339         , _srcRowBytes(0)
340         , _srcBoundary(0)
341         , _origImg(NULL)
342         , _maskImg(NULL)
343         , _premult(false)
344         , _premultChannel(3)
345         , _doMasking(false)
346         , _mix(1.)
347         , _maskInvert(false)
348     {
349     }
350 
351     /** @brief set the src image */
352     void setSrcImg(const OFX::Image *v,
353                    int srcBoundary = 0) //!< The border condition type { 0=zero |  1=dirichlet | 2=periodic }.
354     {
355         _srcPixelData = v->getPixelData();
356         _srcBounds = v->getBounds();
357         _srcPixelComponents = v->getPixelComponents();
358         _srcPixelComponentCount = v->getPixelComponentCount();
359         _srcBitDepth = v->getPixelDepth();
360         _srcPixelBytes = _srcPixelComponentCount * getComponentBytes(_srcBitDepth);
361         _srcRowBytes = v->getRowBytes();
362         _srcBoundary = srcBoundary;
363     }
364 
365     /** @brief set the src image */
setSrcImg(const void * srcPixelData,const OfxRectI & srcBounds,OFX::PixelComponentEnum srcPixelComponents,int srcPixelComponentCount,OFX::BitDepthEnum srcPixelDepth,int srcRowBytes,int srcBoundary)366     void setSrcImg(const void *srcPixelData,
367                    const OfxRectI & srcBounds,
368                    OFX::PixelComponentEnum srcPixelComponents,
369                    int srcPixelComponentCount,
370                    OFX::BitDepthEnum srcPixelDepth,
371                    int srcRowBytes,
372                    int srcBoundary) //!< The border condition type { 0=zero |  1=dirichlet | 2=periodic }.
373     {
374         _srcPixelData = srcPixelData;
375         _srcBounds = srcBounds;
376         _srcPixelComponents = srcPixelComponents;
377         _srcPixelComponentCount = srcPixelComponentCount;
378         _srcBitDepth = srcPixelDepth;
379         _srcPixelBytes = _srcPixelComponentCount * getComponentBytes(_srcBitDepth);
380         _srcRowBytes = srcRowBytes;
381         _srcBoundary = srcBoundary;
382     }
383 
setOrigImg(const OFX::Image * v)384     void setOrigImg(const OFX::Image *v)
385     {
386         _origImg = v;
387     }
388 
setMaskImg(const OFX::Image * v,bool maskInvert)389     void setMaskImg(const OFX::Image *v,
390                     bool maskInvert)
391     {
392         _maskImg = v;
393         _maskInvert = maskInvert;
394     }
395 
doMasking(bool v)396     void doMasking(bool v)
397     {
398         _doMasking = v;
399     }
400 
setPremultMaskMix(bool premult,int premultChannel,double mix)401     void setPremultMaskMix(bool premult,
402                            int premultChannel,
403                            double mix)
404     {
405         _premult = premult;
406         _premultChannel = premultChannel;
407         _mix = mix;
408     }
409 
410 protected:
getSrcPixelAddress(int x,int y)411     const void* getSrcPixelAddress(int x,
412                                    int y) const
413     {
414         if ( !_srcPixelData  || (_srcPixelBytes == 0) || (_srcBounds.x2 <= _srcBounds.x1) || (_srcBounds.y2 <= _srcBounds.y1) ) {
415             return 0;
416         }
417         // are we in the image bounds
418         bool outside = (x < _srcBounds.x1) || ( x >= _srcBounds.x2) || ( y < _srcBounds.y1) || ( y >= _srcBounds.y2);
419         if (outside) {
420             if (_srcBoundary == 1) {
421                 // Nearest/Neumann
422                 if (x < _srcBounds.x1) {
423                     x = _srcBounds.x1;
424                 } else if (x >= _srcBounds.x2) {
425                     x = _srcBounds.x2 - 1;
426                 }
427                 if (y < _srcBounds.y1) {
428                     y = _srcBounds.y1;
429                 } else if (y >= _srcBounds.y2) {
430                     y = _srcBounds.y2 - 1;
431                 }
432             } else if (_srcBoundary == 2) {
433                 // Repeat/Periodic
434                 if ( (x < _srcBounds.x1) || (x >= _srcBounds.x2) ) {
435                     x = _srcBounds.x1 + positive_modulo(x - _srcBounds.x1, _srcBounds.x2 - _srcBounds.x1);
436                 }
437                 if ( (y < _srcBounds.y1) || (y >= _srcBounds.y2) ) {
438                     y = _srcBounds.y1 + positive_modulo(y - _srcBounds.y1, _srcBounds.y2 - _srcBounds.y1);
439                 }
440             } else {
441                 // Black/Dirichlet
442                 return 0;
443             }
444         }
445 
446         char *pix = (char *) ( ( (char *) _srcPixelData ) + (size_t)((y - _srcBounds.y1) * _srcRowBytes) );
447         pix += (x - _srcBounds.x1) * _srcPixelBytes;
448 
449         return (void *) pix;
450     }
451 
positive_modulo(int i,int n)452     static int positive_modulo(int i,
453                                int n)
454     {
455         return (i % n + n) % n;
456     }
457 };
458 };
459 #endif // ifndef openfx_supportext_ofxsPixelProcessor_h
460