#ifndef _ofxsProcessing_h_ #define _ofxsProcessing_h_ // Copyright OpenFX and contributors to the OpenFX project. // SPDX-License-Identifier: BSD-3-Clause #include #include #include "ofxsImageEffect.h" #include "ofxsMultiThread.h" /** @file This file contains a useful base class that can be used to process images The code below is not so much a skin on the base OFX classes, but code used in implementing specific image processing algorithms. As such it does not sit in the support include lib, but in its own include directory. */ namespace OFX { //////////////////////////////////////////////////////////////////////////////// // base class to process images with class ImageProcessor : public OFX::MultiThread::Processor { protected : OFX::ImageEffect &_effect; /**< @brief effect to render with */ OFX::Image *_dstImg; /**< @brief image to process into */ OfxRectI _renderWindow; /**< @brief render window to use */ public : /** @brief ctor */ ImageProcessor(OFX::ImageEffect &effect) : _effect(effect) , _dstImg(0) { _renderWindow.x1 = _renderWindow.y1 = _renderWindow.x2 = _renderWindow.y2 = 0; } /** @brief set the destination image */ void setDstImg(OFX::Image *v) {_dstImg = v; } /** @brief reset the render window */ void setRenderWindow(OfxRectI rect) {_renderWindow = rect;} /** @brief overridden from OFX::MultiThread::Processor. This function is called once on each SMP thread by the base class */ void multiThreadFunction(unsigned int threadId, unsigned int nThreads) { // slice the y range into the number of threads it has unsigned int dy = _renderWindow.y2 - _renderWindow.y1; // the following is equivalent to std::ceil(dy/(double)nThreads); unsigned int h = (dy+nThreads-1)/nThreads; if (h == 0) { // there are more threads than lines to process h = 1; } if (threadId * h >= dy) { // empty render subwindow return; } unsigned int y1 = _renderWindow.y1 + threadId * h; unsigned int step = (threadId + 1) * h; unsigned int y2 = _renderWindow.y1 + (step < dy ? step : dy); OfxRectI win = _renderWindow; win.y1 = y1; win.y2 = y2; // and render that thread on each multiThreadProcessImages(win); } /** @brief called before any MP is done */ virtual void preProcess(void) {} /** @brief this is called by multiThreadFunction to actually process images, override in derived classes */ virtual void multiThreadProcessImages(OfxRectI window) = 0; /** @brief called before any MP is done */ virtual void postProcess(void) {} /** @brief called to process everything */ virtual void process(void) { // If _dstImg was set, check that the _renderWindow is lying into dstBounds if (_dstImg) { const OfxRectI& dstBounds = _dstImg->getBounds(); // is the renderWindow within dstBounds ? assert(dstBounds.x1 <= _renderWindow.x1 && _renderWindow.x2 <= dstBounds.x2 && dstBounds.y1 <= _renderWindow.y1 && _renderWindow.y2 <= dstBounds.y2); // exit gracefully in case of error if (!(dstBounds.x1 <= _renderWindow.x1 && _renderWindow.x2 <= dstBounds.x2 && dstBounds.y1 <= _renderWindow.y1 && _renderWindow.y2 <= dstBounds.y2) || (_renderWindow.x1 >= _renderWindow.x2) || (_renderWindow.y1 >= _renderWindow.y2)) { return; } } // call the pre MP pass preProcess(); // make sure there are at least 4096 pixels per CPU and at least 1 line par CPU unsigned int nCPUs = (std::min(_renderWindow.x2 - _renderWindow.x1, 4096) * (_renderWindow.y2 - _renderWindow.y1)) / 4096; // make sure the number of CPUs is valid (and use at least 1 CPU) nCPUs = std::max(1u, std::min(nCPUs, OFX::MultiThread::getNumCPUs())); // call the base multi threading code, should put a pre & post thread calls in too multiThread(nCPUs); // call the post MP pass postProcess(); } }; }; #endif