Blob Blame Raw
#include "texception.h"
#include "tfxparam.h"
#include "trop.h"
#include "stdfx.h"
#include "trasterfx.h"

//-------------------------------------------------------------------

class BlurFx : public TStandardRasterFx
{
	FX_PLUGIN_DECLARATION(BlurFx)

	TRasterFxPort m_input;
	TDoubleParamP m_value;
	TBoolParamP m_useSSE;

public:
	BlurFx() : m_value(20), m_useSSE(true)
	{
		m_value->setMeasureName("fxLength");
		bindParam(this, "value", m_value);
		bindParam(this, "useSSE", m_useSSE, true);

		addInputPort("Source", m_input);
		m_value->setValueRange(0, std::numeric_limits<double>::max());
	}

	~BlurFx(){};

	bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info)
	{
		if (m_input.isConnected()) {
			bool ret = m_input->doGetBBox(frame, bBox, info);

			double blur = fabs(m_value->getValue(frame));
			int brad = tceil(blur);
			bBox = bBox.enlarge(brad);

			return ret;
		} else {
			bBox = TRectD();
			return false;
		}
	}

	void enlarge(const TRectD &bbox, TRectD &requestedRect, int blur);

	void transform(
		double frame,
		int port,
		const TRectD &rectOnOutput,
		const TRenderSettings &infoOnOutput,
		TRectD &rectOnInput,
		TRenderSettings &infoOnInput);

	void doCompute(TTile &tile, double frame, const TRenderSettings &);

	int getMemoryRequirement(const TRectD &rect, double frame, const TRenderSettings &info);

	bool canHandle(const TRenderSettings &info, double frame)
	{
		if (m_value->getValue(frame) == 0)
			return true;
		return (isAlmostIsotropic(info.m_affine));
	}
};

FX_PLUGIN_IDENTIFIER(BlurFx, "blurFx")

//-------------------------------------------------------------------

//!Calculates the geometry we need for this node computation, given
//!the known input data (bbox), the requested output (requestedRect) and the blur factor.
void BlurFx::enlarge(const TRectD &bbox, TRectD &requestedRect, int blur)
{
	if (bbox.isEmpty() || requestedRect.isEmpty()) {
		requestedRect.empty();
		return;
	}

	//We are to find out the geometry that is useful for the fx computation.
	//There are some rules to follow:
	//  a) First, the interesting output we can generate is bounded by both
	//     the requestedRect and the blurred bbox (i.e. enlarged by the blur radius).
	//  b) Pixels contributing to any output are necessarily part of bbox - and only
	//     those which are blurrable into the requestedRect are useful to us
	//     (i.e. pixels contained in its enlargement by the blur radius).

	TRectD enlargedBBox(bbox.enlarge(blur));
	TRectD enlargedOut(requestedRect.enlarge(blur));

	TPointD originalP00(requestedRect.getP00());
	requestedRect = (enlargedOut * bbox) + (enlargedBBox * requestedRect);

	//Finally, make sure that the result is coherent with the original P00
	requestedRect -= originalP00;
	requestedRect.x0 = tfloor(requestedRect.x0);
	requestedRect.y0 = tfloor(requestedRect.y0);
	requestedRect.x1 = tceil(requestedRect.x1);
	requestedRect.y1 = tceil(requestedRect.y1);
	requestedRect += originalP00;
}

//-------------------------------------------------------------------

void BlurFx::transform(
	double frame,
	int port,
	const TRectD &rectOnOutput,
	const TRenderSettings &infoOnOutput,
	TRectD &rectOnInput,
	TRenderSettings &infoOnInput)
{
	infoOnInput = infoOnOutput;
	rectOnInput = rectOnOutput;

	double blur = fabs(m_value->getValue(frame) * sqrt(fabs(infoOnOutput.m_affine.det())));

	if (blur == 0) {
		rectOnInput = rectOnOutput;
		return;
	}

	int brad = tceil(blur);
	TRectD bbox;
	m_input->getBBox(frame, bbox, infoOnInput);
	enlarge(bbox, rectOnInput, brad);
}

//-------------------------------------------------------------------

int BlurFx::getMemoryRequirement(const TRectD &rect, double frame, const TRenderSettings &info)
{
	double blurValue = fabs(m_value->getValue(frame) * sqrt(fabs(info.m_affine.det())));

	if (blurValue == 0.0)
		return 0;

	int brad = tceil(blurValue);

	//Trop::blur is quite inefficient at the moment - it has to allocate a whole
	//raster of the same size of the input/output made of FLOAT QUADRUPLES...!
	return TRasterFx::memorySize(rect.enlarge(brad), sizeof(float) << 5);
}

//-------------------------------------------------------------------

void BlurFx::doCompute(
	TTile &tile,
	double frame,
	const TRenderSettings &renderSettings)
{
	if (!m_input.isConnected())
		return;

	double shrink = 0.5 * (renderSettings.m_shrinkX + renderSettings.m_shrinkY);
	// Note: shrink is obsolete. It should be always = 1

	double blurValue = fabs(m_value->getValue(frame) * sqrt(fabs(renderSettings.m_affine.det())) / shrink);

	if (blurValue == 0) {
		//No blur will be done. The underlying fx may pass by.
		m_input->compute(tile, frame, renderSettings);
		return;
	}

	int brad = tceil(blurValue);

	//Get the requested tile's geometry
	TRectD rectIn, rectOut;
	rectOut = TRectD(
		tile.m_pos, TDimensionD(tile.getRaster()->getLx(), tile.getRaster()->getLy()));

	//Retrieve the input interesting geometry - and ensure that something actually has
	//to be computed
	if (!m_input->getBBox(frame, rectIn, renderSettings) || rectOut.isEmpty())
		return;

	enlarge(rectIn, rectOut, brad);

	if (rectOut.isEmpty())
		return;

	//Finally, allocate and compute the blur argument
	TTile tileIn;
	m_input->allocateAndCompute(
		tileIn, rectOut.getP00(), TDimension(rectOut.getLx(), rectOut.getLy()),
		tile.getRaster(), frame, renderSettings);

	TPointD displacement(rectOut.getP00() - tile.m_pos);
	TRop::blur(tile.getRaster(), tileIn.getRaster(), blurValue, displacement.x, displacement.y, false);
}