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