858 lines
25 KiB
C++
858 lines
25 KiB
C++
|
|
|
|
#include "tfxparam.h"
|
|
#include "tfx.h"
|
|
|
|
#include "tparamset.h"
|
|
#include "stdfx.h"
|
|
#include "time.h"
|
|
#include "trop.h"
|
|
#include "trasterfx.h"
|
|
#include "tpixelutils.h"
|
|
#include "tparamuiconcept.h"
|
|
|
|
#include "timage_io.h"
|
|
|
|
#include "toonz/tdistort.h"
|
|
|
|
//==============================================================================
|
|
|
|
namespace
|
|
{
|
|
inline bool myIsEmpty(const TRectD &r)
|
|
{
|
|
return r.x0 >= r.x1 || r.y0 >= r.y1;
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
|
|
class FreeDistortBaseFx : public TStandardRasterFx
|
|
{
|
|
enum { PERSPECTIVE,
|
|
BILINEAR };
|
|
|
|
public:
|
|
FreeDistortBaseFx(bool isCastShadow);
|
|
~FreeDistortBaseFx();
|
|
|
|
bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info);
|
|
|
|
void doCompute(TTile &tile, double frame, const TRenderSettings &ri);
|
|
void doDryCompute(TRectD &rect, double frame, const TRenderSettings &ri);
|
|
void onPortConnected(TFxPort *port);
|
|
bool canHandle(const TRenderSettings &info, double frame);
|
|
int getMemoryRequirement(const TRectD &rect, double frame, const TRenderSettings &info);
|
|
|
|
void transform(double frame,
|
|
int port,
|
|
const TRectD &rectOnOutput,
|
|
const TRenderSettings &infoOnOutput,
|
|
TRectD &rectOnInput,
|
|
TRenderSettings &infoOnInput);
|
|
|
|
void safeTransform(
|
|
double frame,
|
|
int port,
|
|
const TRectD &rectOnOutput,
|
|
const TRenderSettings &infoOnOutput,
|
|
TRectD &rectOnInput,
|
|
TRenderSettings &infoOnInput,
|
|
TRectD &inBBox);
|
|
|
|
void getParamUIs(TParamUIConcept *&concepts, int &length);
|
|
|
|
private:
|
|
bool m_isCastShadow;
|
|
TRasterFxPort m_input;
|
|
|
|
TIntEnumParamP m_distortType;
|
|
|
|
TPointParamP m_p00_a;
|
|
TPointParamP m_p00_b;
|
|
TPointParamP m_p01_a;
|
|
TPointParamP m_p01_b;
|
|
TPointParamP m_p11_a;
|
|
TPointParamP m_p11_b;
|
|
TPointParamP m_p10_a;
|
|
TPointParamP m_p10_b;
|
|
|
|
TBoolParamP m_deactivate;
|
|
TPixelParamP m_color;
|
|
TDoubleParamP m_fade;
|
|
TDoubleParamP m_upTransp;
|
|
TDoubleParamP m_downTransp;
|
|
TDoubleParamP m_upBlur;
|
|
TDoubleParamP m_downBlur;
|
|
};
|
|
|
|
class FreeDistortFx : public FreeDistortBaseFx
|
|
{
|
|
FX_PLUGIN_DECLARATION(FreeDistortFx)
|
|
|
|
public:
|
|
FreeDistortFx() : FreeDistortBaseFx(false) {}
|
|
};
|
|
|
|
class CastShadowFx : public FreeDistortBaseFx
|
|
{
|
|
FX_PLUGIN_DECLARATION(CastShadowFx)
|
|
public:
|
|
CastShadowFx() : FreeDistortBaseFx(true)
|
|
{
|
|
}
|
|
};
|
|
|
|
//==============================================================================
|
|
|
|
FreeDistortBaseFx::FreeDistortBaseFx(bool isCastShadow)
|
|
: m_deactivate(false), m_color(TPixel32::Black), m_fade(0.0), m_upTransp(0.0), m_downTransp(0.0), m_upBlur(0.0), m_downBlur(0.0), m_isCastShadow(isCastShadow), m_distortType(new TIntEnumParam(PERSPECTIVE, "Perspective"))
|
|
{
|
|
m_upBlur->setMeasureName("fxLength");
|
|
m_downBlur->setMeasureName("fxLength");
|
|
double ext = 400.;
|
|
double inn = 400.;
|
|
m_p00_a = TPointD(-ext, -ext);
|
|
m_p00_b = TPointD(-inn, -inn);
|
|
|
|
m_p01_a = TPointD(-ext, ext);
|
|
m_p01_b = TPointD(-inn, inn);
|
|
|
|
m_p11_a = TPointD(ext, ext);
|
|
m_p11_b = TPointD(inn, inn);
|
|
|
|
m_p10_a = TPointD(ext, -ext);
|
|
m_p10_b = TPointD(inn, -inn);
|
|
|
|
m_p00_b->getX()->setMeasureName("fxLength");
|
|
m_p00_b->getY()->setMeasureName("fxLength");
|
|
m_p10_b->getX()->setMeasureName("fxLength");
|
|
m_p10_b->getY()->setMeasureName("fxLength");
|
|
m_p01_b->getX()->setMeasureName("fxLength");
|
|
m_p01_b->getY()->setMeasureName("fxLength");
|
|
m_p11_b->getX()->setMeasureName("fxLength");
|
|
m_p11_b->getY()->setMeasureName("fxLength");
|
|
|
|
m_p00_a->getX()->setMeasureName("fxLength");
|
|
m_p00_a->getY()->setMeasureName("fxLength");
|
|
m_p10_a->getX()->setMeasureName("fxLength");
|
|
m_p10_a->getY()->setMeasureName("fxLength");
|
|
m_p01_a->getX()->setMeasureName("fxLength");
|
|
m_p01_a->getY()->setMeasureName("fxLength");
|
|
m_p11_a->getX()->setMeasureName("fxLength");
|
|
m_p11_a->getY()->setMeasureName("fxLength");
|
|
|
|
bindParam(this, "distort_type", m_distortType);
|
|
|
|
bindParam(this, "bottom_left_b", m_p00_b);
|
|
bindParam(this, "bottom_right_b", m_p10_b);
|
|
bindParam(this, "top_left_b", m_p01_b);
|
|
bindParam(this, "top_right_b", m_p11_b);
|
|
|
|
bindParam(this, "bottom_left_a", m_p00_a);
|
|
bindParam(this, "bottom_right_a", m_p10_a);
|
|
bindParam(this, "top_left_a", m_p01_a);
|
|
bindParam(this, "top_right_a", m_p11_a);
|
|
|
|
if (isCastShadow) {
|
|
bindParam(this, "color", m_color);
|
|
bindParam(this, "fade", m_fade);
|
|
bindParam(this, "up_transp", m_upTransp);
|
|
bindParam(this, "down_transp", m_downTransp);
|
|
bindParam(this, "up_blur", m_upBlur);
|
|
bindParam(this, "down_blur", m_downBlur);
|
|
|
|
m_color->enableMatte(false);
|
|
m_fade->setValueRange(0.0, 100.0);
|
|
m_upTransp->setValueRange(0.0, 100.0);
|
|
m_downTransp->setValueRange(0.0, 100.0);
|
|
m_upBlur->setValueRange(0.0, (std::numeric_limits<double>::max)());
|
|
m_downBlur->setValueRange(0.0, (std::numeric_limits<double>::max)());
|
|
}
|
|
|
|
bindParam(this, "deactivate", m_deactivate);
|
|
addInputPort("source", m_input);
|
|
|
|
m_p00_a->getX()->setValueRange(-1000, 1000);
|
|
m_p00_b->getX()->setValueRange(-1000, 1000);
|
|
|
|
m_p00_a->getY()->setValueRange(-1000, 1000);
|
|
m_p00_b->getY()->setValueRange(-1000, 1000);
|
|
|
|
m_p01_a->getX()->setValueRange(-1000, 1000);
|
|
m_p01_b->getX()->setValueRange(-1000, 1000);
|
|
|
|
m_p01_a->getY()->setValueRange(-1000, 1000);
|
|
m_p01_b->getY()->setValueRange(-1000, 1000);
|
|
|
|
m_p11_a->getX()->setValueRange(-1000, 1000);
|
|
m_p11_b->getX()->setValueRange(-1000, 1000);
|
|
|
|
m_p11_a->getY()->setValueRange(-1000, 1000);
|
|
m_p11_b->getY()->setValueRange(-1000, 1000);
|
|
|
|
m_p10_a->getX()->setValueRange(-1000, 1000);
|
|
m_p10_b->getX()->setValueRange(-1000, 1000);
|
|
|
|
m_p10_a->getY()->setValueRange(-1000, 1000);
|
|
m_p10_b->getY()->setValueRange(-1000, 1000);
|
|
|
|
m_distortType->addItem(BILINEAR, "Bilinear");
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
FreeDistortBaseFx::~FreeDistortBaseFx()
|
|
{
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
bool FreeDistortBaseFx::canHandle(const TRenderSettings &info, double frame)
|
|
{
|
|
//Blurs cant deal with anisotropic affines, so these are retained above
|
|
return m_upBlur->getValue(frame) == 0 && m_downBlur->getValue(frame) == 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
bool FreeDistortBaseFx::doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info)
|
|
{
|
|
if (m_input.isConnected()) {
|
|
//We'll treat this fx like a zerary one. The idea is, since
|
|
//the task of taking images and pre-images of rects through
|
|
//these kind of distortions is definitely non-trivial, we'll
|
|
//simply avoid doing so...
|
|
bBox = TConsts::infiniteRectD;
|
|
return true;
|
|
|
|
/*
|
|
bool ret = m_input->doGetBBox(frame, bBox);
|
|
|
|
if(m_deactivate->getValue())
|
|
return ret;
|
|
|
|
TPointD p00_a = m_p00_a->getValue(frame);
|
|
TPointD p00_b = m_p00_b->getValue(frame);
|
|
TPointD p10_a = m_p10_a->getValue(frame);
|
|
TPointD p10_b = m_p10_b->getValue(frame);
|
|
TPointD p01_a = m_p01_a->getValue(frame);
|
|
TPointD p01_b = m_p01_b->getValue(frame);
|
|
TPointD p11_a = m_p11_a->getValue(frame);
|
|
TPointD p11_b = m_p11_b->getValue(frame);
|
|
|
|
if (m_isCastShadow)
|
|
{
|
|
tswap(p00_a, p01_a);
|
|
tswap(p10_a, p11_a);
|
|
}*/
|
|
|
|
/*TRectD source;
|
|
source.x0 = tmin(p00_b.x, p10_b.x, p01_b.x, p11_b.x);
|
|
source.y0 = tmin(p00_b.y, p10_b.y, p01_b.y, p11_b.y);
|
|
source.x1 = tmax(p00_b.x, p10_b.x, p01_b.x, p11_b.x);
|
|
source.y1 = tmax(p00_b.y, p10_b.y, p01_b.y, p11_b.y);
|
|
bBox *= source;
|
|
|
|
int distortType = m_distortType->getValue();
|
|
if(distortType == PERSPECTIVE)
|
|
{
|
|
PerspectiveDistorter distorter(
|
|
p00_b, p10_b, p01_b, p11_b,
|
|
p00_a, p10_a, p01_a, p11_a
|
|
);
|
|
bBox = distorter.map(bBox);
|
|
}
|
|
else if(distortType == BILINEAR)
|
|
{
|
|
BilinearDistorter distorter(
|
|
p00_b, p10_b, p01_b, p11_b,
|
|
p00_a, p10_a, p01_a, p11_a
|
|
);
|
|
bBox = distorter.map(bBox);
|
|
}
|
|
else assert(0);*/
|
|
|
|
//Further, we will also avoid the assumption that the bbox
|
|
//is defined by that of destination points...
|
|
/*TRectD result;
|
|
result.x0 = tmin(p00_a.x, p10_a.x, p01_a.x, p11_a.x);
|
|
result.y0 = tmin(p00_a.y, p10_a.y, p01_a.y, p11_a.y);
|
|
result.x1 = tmax(p00_a.x, p10_a.x, p01_a.x, p11_a.x);
|
|
result.y1 = tmax(p00_a.y, p10_a.y, p01_a.y, p11_a.y);
|
|
bBox = result; //bBox *= result;*/
|
|
} else {
|
|
bBox = TRectD();
|
|
return false;
|
|
}
|
|
|
|
if (m_upBlur->getValue(frame) > 0 || m_downBlur->getValue(frame) > 0) {
|
|
int brad = tmax((int)m_upBlur->getValue(frame), (int)m_downBlur->getValue(frame));
|
|
bBox.x0 -= brad;
|
|
bBox.x1 += brad;
|
|
bBox.y0 -= brad;
|
|
bBox.y1 += brad;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
void FreeDistortBaseFx::transform(
|
|
double frame,
|
|
int port,
|
|
const TRectD &rectOnOutput,
|
|
const TRenderSettings &infoOnOutput,
|
|
TRectD &rectOnInput,
|
|
TRenderSettings &infoOnInput)
|
|
{
|
|
TPointD p00_b = m_p00_b->getValue(frame);
|
|
TPointD p10_b = m_p10_b->getValue(frame);
|
|
TPointD p01_b = m_p01_b->getValue(frame);
|
|
TPointD p11_b = m_p11_b->getValue(frame);
|
|
|
|
TPointD p00_a = m_p00_a->getValue(frame);
|
|
TPointD p10_a = m_p10_a->getValue(frame);
|
|
TPointD p01_a = m_p01_a->getValue(frame);
|
|
TPointD p11_a = m_p11_a->getValue(frame);
|
|
|
|
if (m_isCastShadow) {
|
|
//Shadows are mirrored
|
|
tswap(p00_a, p01_a);
|
|
tswap(p10_a, p11_a);
|
|
}
|
|
|
|
//Build the input affine
|
|
infoOnInput = infoOnOutput;
|
|
|
|
double scale = 0;
|
|
scale = tmax(scale, norm(p10_a - p00_a) / norm(p10_b - p00_b));
|
|
scale = tmax(scale, norm(p01_a - p00_a) / norm(p01_b - p00_b));
|
|
scale = tmax(scale, norm(p11_a - p10_a) / norm(p11_b - p10_b));
|
|
scale = tmax(scale, norm(p11_a - p01_a) / norm(p11_b - p01_b));
|
|
|
|
scale *= sqrt(fabs(infoOnInput.m_affine.det()));
|
|
|
|
if (infoOnOutput.m_isSwatch) {
|
|
//Swatch viewers work out a lite version of the distort: only contractions
|
|
//are passed below in the schematic - so that input memory load is always at minimum.
|
|
//This is allowed in order to make fx adjusts the less frustrating as possible.
|
|
if (scale > 1)
|
|
scale = 1;
|
|
}
|
|
|
|
infoOnInput.m_affine = TScale(scale);
|
|
|
|
//Build rectOnInput
|
|
p00_a = infoOnOutput.m_affine * p00_a;
|
|
p10_a = infoOnOutput.m_affine * p10_a;
|
|
p01_a = infoOnOutput.m_affine * p01_a;
|
|
p11_a = infoOnOutput.m_affine * p11_a;
|
|
|
|
p00_b = infoOnInput.m_affine * p00_b;
|
|
p10_b = infoOnInput.m_affine * p10_b;
|
|
p01_b = infoOnInput.m_affine * p01_b;
|
|
p11_b = infoOnInput.m_affine * p11_b;
|
|
|
|
if (m_distortType->getValue() == PERSPECTIVE) {
|
|
PerspectiveDistorter pd(
|
|
p00_b, p10_b, p01_b, p11_b,
|
|
p00_a, p10_a, p01_a, p11_a);
|
|
|
|
rectOnInput = ((TQuadDistorter *)&pd)->invMap(rectOnOutput);
|
|
} else {
|
|
BilinearDistorter bd(
|
|
p00_b, p10_b, p01_b, p11_b,
|
|
p00_a, p10_a, p01_a, p11_a);
|
|
|
|
rectOnInput = ((TQuadDistorter *)&bd)->invMap(rectOnOutput);
|
|
}
|
|
|
|
if (rectOnInput.x0 != TConsts::infiniteRectD.x0)
|
|
rectOnInput.x0 = tfloor(rectOnInput.x0);
|
|
if (rectOnInput.y0 != TConsts::infiniteRectD.y0)
|
|
rectOnInput.y0 = tfloor(rectOnInput.y0);
|
|
if (rectOnInput.x1 != TConsts::infiniteRectD.x1)
|
|
rectOnInput.x1 = tceil(rectOnInput.x1);
|
|
if (rectOnInput.y1 != TConsts::infiniteRectD.y1)
|
|
rectOnInput.y1 = tceil(rectOnInput.y1);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
void FreeDistortBaseFx::safeTransform(
|
|
double frame,
|
|
int port,
|
|
const TRectD &rectOnOutput,
|
|
const TRenderSettings &infoOnOutput,
|
|
TRectD &rectOnInput,
|
|
TRenderSettings &infoOnInput,
|
|
TRectD &inBBox)
|
|
{
|
|
assert(port == 0 && m_input.isConnected());
|
|
|
|
if (m_deactivate->getValue()) {
|
|
infoOnInput = infoOnOutput;
|
|
rectOnInput = rectOnOutput;
|
|
m_input->getBBox(frame, inBBox, infoOnInput);
|
|
return;
|
|
}
|
|
|
|
//Avoid the singular matrix cases
|
|
if (fabs(infoOnOutput.m_affine.det()) < 1.e-3) {
|
|
infoOnInput = infoOnOutput;
|
|
rectOnInput.empty();
|
|
m_input->getBBox(frame, inBBox, infoOnInput);
|
|
return;
|
|
}
|
|
|
|
transform(frame, port, rectOnOutput, infoOnOutput, rectOnInput, infoOnInput);
|
|
|
|
m_input->getBBox(frame, inBBox, infoOnInput);
|
|
|
|
//In case inBBox is infinite, such as with gradients, also intersect with the input quadrilateral's bbox
|
|
if (inBBox == TConsts::infiniteRectD) {
|
|
TPointD affP00_b(infoOnInput.m_affine * m_p00_b->getValue(frame));
|
|
TPointD affP10_b(infoOnInput.m_affine * m_p10_b->getValue(frame));
|
|
TPointD affP01_b(infoOnInput.m_affine * m_p01_b->getValue(frame));
|
|
TPointD affP11_b(infoOnInput.m_affine * m_p11_b->getValue(frame));
|
|
|
|
TRectD source;
|
|
source.x0 = tmin(affP00_b.x, affP10_b.x, affP01_b.x, affP11_b.x);
|
|
source.y0 = tmin(affP00_b.y, affP10_b.y, affP01_b.y, affP11_b.y);
|
|
source.x1 = tmax(affP00_b.x, affP10_b.x, affP01_b.x, affP11_b.x);
|
|
source.y1 = tmax(affP00_b.y, affP10_b.y, affP01_b.y, affP11_b.y);
|
|
|
|
rectOnInput *= source;
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
void FreeDistortBaseFx::getParamUIs(TParamUIConcept *&concepts, int &length)
|
|
{
|
|
concepts = new TParamUIConcept[length = 14];
|
|
|
|
concepts[0].m_type = TParamUIConcept::QUAD;
|
|
concepts[0].m_params.push_back(m_p00_b);
|
|
concepts[0].m_params.push_back(m_p10_b);
|
|
concepts[0].m_params.push_back(m_p11_b);
|
|
concepts[0].m_params.push_back(m_p01_b);
|
|
|
|
concepts[1].m_type = TParamUIConcept::QUAD;
|
|
concepts[1].m_params.push_back(m_p00_a);
|
|
concepts[1].m_params.push_back(m_p10_a);
|
|
concepts[1].m_params.push_back(m_p11_a);
|
|
concepts[1].m_params.push_back(m_p01_a);
|
|
|
|
concepts[2].m_type = TParamUIConcept::VECTOR;
|
|
concepts[2].m_params.push_back(m_p00_b);
|
|
concepts[2].m_params.push_back(m_p00_a);
|
|
|
|
concepts[3].m_type = TParamUIConcept::VECTOR;
|
|
concepts[3].m_params.push_back(m_p10_b);
|
|
concepts[3].m_params.push_back(m_p10_a);
|
|
|
|
concepts[4].m_type = TParamUIConcept::VECTOR;
|
|
concepts[4].m_params.push_back(m_p01_b);
|
|
concepts[4].m_params.push_back(m_p01_a);
|
|
|
|
concepts[5].m_type = TParamUIConcept::VECTOR;
|
|
concepts[5].m_params.push_back(m_p11_b);
|
|
concepts[5].m_params.push_back(m_p11_a);
|
|
|
|
concepts[6].m_type = TParamUIConcept::POINT;
|
|
concepts[6].m_label = "Bottom Left Src";
|
|
concepts[6].m_params.push_back(m_p00_b);
|
|
|
|
concepts[7].m_type = TParamUIConcept::POINT;
|
|
concepts[7].m_label = "Bottom Right Src";
|
|
concepts[7].m_params.push_back(m_p10_b);
|
|
|
|
concepts[8].m_type = TParamUIConcept::POINT;
|
|
concepts[8].m_label = "Top Left Src";
|
|
concepts[8].m_params.push_back(m_p01_b);
|
|
|
|
concepts[9].m_type = TParamUIConcept::POINT;
|
|
concepts[9].m_label = "Top Right Src";
|
|
concepts[9].m_params.push_back(m_p11_b);
|
|
|
|
concepts[10].m_type = TParamUIConcept::POINT;
|
|
concepts[10].m_label = "Bottom Left Dst";
|
|
concepts[10].m_params.push_back(m_p00_a);
|
|
|
|
concepts[11].m_type = TParamUIConcept::POINT;
|
|
concepts[11].m_label = "Bottom Right Dst";
|
|
concepts[11].m_params.push_back(m_p10_a);
|
|
|
|
concepts[12].m_type = TParamUIConcept::POINT;
|
|
concepts[12].m_label = "Top Left Dst";
|
|
concepts[12].m_params.push_back(m_p01_a);
|
|
|
|
concepts[13].m_type = TParamUIConcept::POINT;
|
|
concepts[13].m_label = "Top Right Dst";
|
|
concepts[13].m_params.push_back(m_p11_a);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
namespace
|
|
{
|
|
|
|
template <typename PIX>
|
|
void doFade(TRasterPT<PIX> &ras, PIX col, double intensity)
|
|
{
|
|
double maxChannelValueD = PIX::maxChannelValue;
|
|
|
|
int j;
|
|
ras->lock();
|
|
for (j = 0; j < ras->getLy(); j++) {
|
|
PIX *pix = ras->pixels(j);
|
|
PIX *endPix = pix + ras->getLx();
|
|
while (pix < endPix) {
|
|
if (pix->m > 0) {
|
|
double factor = pix->m / maxChannelValueD;
|
|
int val;
|
|
val = troundp(pix->r + intensity * (col.r * factor - pix->r));
|
|
pix->r = (val > PIX::maxChannelValue) ? PIX::maxChannelValue : val;
|
|
val = troundp(pix->g + intensity * (col.g * factor - pix->g));
|
|
pix->g = (val > PIX::maxChannelValue) ? PIX::maxChannelValue : val;
|
|
val = troundp(pix->b + intensity * (col.b * factor - pix->b));
|
|
pix->b = (val > PIX::maxChannelValue) ? PIX::maxChannelValue : val;
|
|
}
|
|
++pix;
|
|
}
|
|
}
|
|
ras->unlock();
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
template <typename PIX>
|
|
void doTransparency(TRasterPT<PIX> &ras, double downTransp, double upTransp, double y0, double y1)
|
|
{
|
|
assert(y0 <= 0.0 && y1 >= ras->getLy() - 1);
|
|
|
|
double den = y1 - y0;
|
|
|
|
int j;
|
|
ras->lock();
|
|
for (j = 0; j < ras->getLy(); j++) {
|
|
double transp = (1 - downTransp) + (downTransp - upTransp) * (j - y0) / den;
|
|
PIX *pix = ras->pixels(j);
|
|
PIX *endPix = pix + ras->getLx();
|
|
while (pix < endPix) {
|
|
if (pix->m > 0) {
|
|
pix->r = troundp(pix->r * transp);
|
|
pix->g = troundp(pix->g * transp);
|
|
pix->b = troundp(pix->b * transp);
|
|
pix->m = troundp(pix->m * transp);
|
|
}
|
|
++pix;
|
|
}
|
|
}
|
|
ras->unlock();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
template <typename PIX>
|
|
inline void filterPixel(PIX *bufin, int wrap, PIX *bufout, double blur, int coord, int maxVal)
|
|
{
|
|
int rad, k, index;
|
|
double coeff, weight;
|
|
double valr = 0.0, valg = 0.0, valb = 0.0, valm = 0.0;
|
|
|
|
rad = tceil(blur);
|
|
coeff = 1.0 / rad;
|
|
|
|
valr = bufin[0].r;
|
|
valg = bufin[0].g;
|
|
valb = bufin[0].b;
|
|
valm = bufin[0].m;
|
|
|
|
for (k = 1, index = wrap, weight = (1 - coeff); k < rad; k++, index += wrap, weight -= coeff) {
|
|
if (k + coord < maxVal) {
|
|
if (-k + coord >= 0) {
|
|
valr += (bufin[index].r + bufin[-index].r) * weight;
|
|
valg += (bufin[index].g + bufin[-index].g) * weight;
|
|
valb += (bufin[index].b + bufin[-index].b) * weight;
|
|
valm += (bufin[index].m + bufin[-index].m) * weight;
|
|
} else {
|
|
valr += bufin[index].r * weight;
|
|
valg += bufin[index].g * weight;
|
|
valb += bufin[index].b * weight;
|
|
valm += bufin[index].m * weight;
|
|
}
|
|
} else if (-k + coord >= 0) {
|
|
if (k + coord < maxVal) {
|
|
valr += (bufin[index].r + bufin[-index].r) * weight;
|
|
valg += (bufin[index].g + bufin[-index].g) * weight;
|
|
valb += (bufin[index].b + bufin[-index].b) * weight;
|
|
valm += (bufin[index].m + bufin[-index].m) * weight;
|
|
} else {
|
|
valr += bufin[-index].r * weight;
|
|
valg += bufin[-index].g * weight;
|
|
valb += bufin[-index].b * weight;
|
|
valm += bufin[-index].m * weight;
|
|
}
|
|
}
|
|
}
|
|
|
|
valr *= coeff;
|
|
valg *= coeff;
|
|
valb *= coeff;
|
|
valm *= coeff;
|
|
|
|
bufout->r = troundp(valr);
|
|
bufout->g = troundp(valg);
|
|
bufout->b = troundp(valb);
|
|
bufout->m = troundp(valm);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
template <typename PIX>
|
|
void doBlur(TRasterPT<PIX> &r,
|
|
double blur0, double blur1,
|
|
double transp0, double transp1,
|
|
double y0, double y1)
|
|
{
|
|
int lx, ly, wrap;
|
|
int i, j, brad, blur_max;
|
|
PIX *bufin, *bufout, *pixin, *pixout, *row;
|
|
double cur_blur, actual_blur, blur_incr, cur_transp, actual_transp, transp_incr;
|
|
|
|
lx = r->getLx();
|
|
ly = r->getLy();
|
|
wrap = r->getWrap();
|
|
blur_max = (int)tmax(blur0, blur1);
|
|
brad = tceil(blur_max);
|
|
|
|
//assert(y0 <= brad && y1 >= r->getLy()-1 - 2 * brad + y0);
|
|
|
|
double den = y1 - y0;
|
|
|
|
blur_incr = (blur1 - blur0) / den;
|
|
transp_incr = (transp0 - transp1) / den;
|
|
|
|
row = new PIX[tmax(lx, ly)];
|
|
r->lock();
|
|
bufin = r->pixels(0);
|
|
for (i = 0, cur_blur = blur0 + (blur1 - blur0) * (i - y0) / den, 0.0, actual_blur = tmax(cur_blur, 0.0);
|
|
i < ly;
|
|
i++, bufin += wrap, cur_blur += blur_incr, actual_blur = tmax(cur_blur, 0.0)) {
|
|
pixin = bufin;
|
|
for (j = 0; j < lx; j++, pixin++) {
|
|
if (cur_blur > 1.0)
|
|
filterPixel(pixin, 1, row + j, actual_blur, j, lx);
|
|
else
|
|
row[j] = *pixin;
|
|
}
|
|
for (j = 0; j < lx; j++)
|
|
bufin[j] = row[j];
|
|
}
|
|
|
|
bufin = r->pixels(0);
|
|
bufout = r->pixels(0);
|
|
for (i = 0; i < lx; i++, bufin++, bufout++) {
|
|
pixin = bufin;
|
|
pixout = bufout;
|
|
|
|
for (j = 0, cur_blur = blur0 + (blur1 - blur0) * (j - y0) / den, actual_blur = tmax(cur_blur, 0.0);
|
|
j < ly;
|
|
j++, pixin += wrap, cur_blur += blur_incr, actual_blur = tmax(cur_blur, 0.0)) {
|
|
if (cur_blur > 1.0)
|
|
filterPixel(pixin, wrap, row + j, actual_blur, j, ly);
|
|
else
|
|
row[j] = *pixin;
|
|
}
|
|
|
|
if (transp0 > 0 || transp1 > 0)
|
|
for (j = 0, cur_transp = 1 - transp0 + (transp0 - transp1) * (j - y0) / den, actual_transp = tmax(cur_transp, 0.0);
|
|
j < ly;
|
|
j++, pixout += wrap, cur_transp += transp_incr, actual_transp = tmax(cur_transp, 0.0)) {
|
|
pixout->r = troundp(row[j].r * actual_transp);
|
|
pixout->g = troundp(row[j].g * actual_transp);
|
|
pixout->b = troundp(row[j].b * actual_transp);
|
|
pixout->m = troundp(row[j].m * actual_transp);
|
|
}
|
|
else
|
|
for (j = 0; j < ly; j++, pixout += wrap) {
|
|
pixout->r = row[j].r;
|
|
pixout->g = row[j].g;
|
|
pixout->b = row[j].b;
|
|
pixout->m = row[j].m;
|
|
}
|
|
}
|
|
r->unlock();
|
|
delete[] row;
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
void FreeDistortBaseFx::doDryCompute(TRectD &rect, double frame, const TRenderSettings &info)
|
|
{
|
|
if (!m_input.isConnected())
|
|
return;
|
|
|
|
if (m_deactivate->getValue()) {
|
|
m_input->dryCompute(rect, frame, info);
|
|
return;
|
|
}
|
|
|
|
TRectD rectOnInput;
|
|
TRenderSettings infoOnInput;
|
|
TRectD inBBox;
|
|
|
|
safeTransform(frame, 0, rect, info, rectOnInput, infoOnInput, inBBox);
|
|
|
|
rectOnInput *= inBBox;
|
|
|
|
if (!myIsEmpty(rectOnInput))
|
|
m_input->dryCompute(rectOnInput, frame, infoOnInput);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
void FreeDistortBaseFx::doCompute(TTile &tile, double frame, const TRenderSettings &ri)
|
|
{
|
|
if (!m_input.isConnected())
|
|
return;
|
|
|
|
//Upon deactivation, this fx does nothing.
|
|
if (m_deactivate->getValue()) {
|
|
m_input->compute(tile, frame, ri);
|
|
return;
|
|
}
|
|
|
|
//Get the source quad
|
|
TPointD p00_b = m_p00_b->getValue(frame);
|
|
TPointD p10_b = m_p10_b->getValue(frame);
|
|
TPointD p01_b = m_p01_b->getValue(frame);
|
|
TPointD p11_b = m_p11_b->getValue(frame);
|
|
|
|
//Get destination quad
|
|
TPointD p00_a = m_p00_a->getValue(frame);
|
|
TPointD p10_a = m_p10_a->getValue(frame);
|
|
TPointD p01_a = m_p01_a->getValue(frame);
|
|
TPointD p11_a = m_p11_a->getValue(frame);
|
|
|
|
if (m_isCastShadow) {
|
|
//Shadows are mirrored
|
|
tswap(p00_a, p01_a);
|
|
tswap(p10_a, p11_a);
|
|
}
|
|
|
|
//Get requested tile's geometry
|
|
TRasterP tileRas(tile.getRaster());
|
|
TRectD tileRect(convert(tileRas->getBounds()) + tile.m_pos);
|
|
|
|
//Call transform to get the minimal rectOnInput
|
|
TRectD inRect;
|
|
TRenderSettings riNew;
|
|
TRectD inBBox;
|
|
|
|
safeTransform(frame, 0, tileRect, ri, inRect, riNew, inBBox);
|
|
|
|
//Intersect with the bbox
|
|
inRect *= inBBox;
|
|
|
|
if (myIsEmpty(inRect))
|
|
return;
|
|
|
|
double scale = ri.m_affine.a11;
|
|
|
|
double downBlur = m_downBlur->getValue(frame) * scale;
|
|
double upBlur = m_upBlur->getValue(frame) * scale;
|
|
int brad = tceil(tmax(downBlur, upBlur));
|
|
|
|
inRect = inRect.enlarge(brad);
|
|
|
|
TDimension inRectSize(tceil(inRect.getLx()), tceil(inRect.getLy()));
|
|
|
|
TTile inTile;
|
|
m_input->allocateAndCompute(inTile, inRect.getP00(), inRectSize, tileRas, frame, riNew);
|
|
|
|
TPointD inTilePosRi = inTile.m_pos;
|
|
|
|
//Update quads by the scale factors
|
|
p00_b = riNew.m_affine * p00_b;
|
|
p10_b = riNew.m_affine * p10_b;
|
|
p01_b = riNew.m_affine * p01_b;
|
|
p11_b = riNew.m_affine * p11_b;
|
|
|
|
p00_a = ri.m_affine * p00_a;
|
|
p10_a = ri.m_affine * p10_a;
|
|
p01_a = ri.m_affine * p01_a;
|
|
p11_a = ri.m_affine * p11_a;
|
|
|
|
PerspectiveDistorter perpDistorter(
|
|
p00_b - inTile.m_pos, p10_b - inTile.m_pos, p01_b - inTile.m_pos, p11_b - inTile.m_pos,
|
|
p00_a, p10_a, p01_a, p11_a);
|
|
|
|
BilinearDistorter bilDistorter(
|
|
p00_b - inTile.m_pos, p10_b - inTile.m_pos, p01_b - inTile.m_pos, p11_b - inTile.m_pos,
|
|
p00_a, p10_a, p01_a, p11_a);
|
|
|
|
TQuadDistorter *distorter;
|
|
if (m_distortType->getValue() == PERSPECTIVE)
|
|
distorter = &perpDistorter;
|
|
else if (m_distortType->getValue() == BILINEAR)
|
|
distorter = &bilDistorter;
|
|
else
|
|
assert(0);
|
|
|
|
if (m_isCastShadow) {
|
|
TRaster32P ras32 = inTile.getRaster();
|
|
TRaster64P ras64 = inTile.getRaster();
|
|
|
|
if (ras32) {
|
|
if (m_fade->getValue(frame) > 0)
|
|
doFade(ras32, m_color->getValue(frame), m_fade->getValue(frame) / 100.0);
|
|
if (brad > 0)
|
|
doBlur(ras32, upBlur, downBlur,
|
|
m_upTransp->getValue(frame) / 100.0, m_downTransp->getValue(frame) / 100.0,
|
|
inBBox.y0 - inTile.m_pos.y, inBBox.y1 - inTile.m_pos.y);
|
|
else if (m_upTransp->getValue(frame) > 0 || m_downTransp->getValue(frame) > 0)
|
|
doTransparency(ras32, m_upTransp->getValue(frame) / 100.0, m_downTransp->getValue(frame) / 100.0,
|
|
inBBox.y0 - inTile.m_pos.y, inBBox.y1 - inTile.m_pos.y);
|
|
} else if (ras64) {
|
|
if (m_fade->getValue(frame) > 0)
|
|
doFade(ras64, toPixel64(m_color->getValue(frame)), m_fade->getValue(frame) / 100.0);
|
|
if (brad > 0)
|
|
doBlur(ras64, upBlur, downBlur,
|
|
m_upTransp->getValue(frame) / 100.0, m_downTransp->getValue(frame) / 100.0,
|
|
inBBox.y0 - inTile.m_pos.y, inBBox.y1 - inTile.m_pos.y);
|
|
else if (m_upTransp->getValue(frame) > 0 || m_downTransp->getValue(frame) > 0)
|
|
doTransparency(ras64, m_upTransp->getValue(frame) / 100.0, m_downTransp->getValue(frame) / 100.0,
|
|
inBBox.y0 - inTile.m_pos.y, inBBox.y1 - inTile.m_pos.y);
|
|
} else
|
|
assert(false);
|
|
}
|
|
|
|
distort(tileRas, inTile.getRaster(), *distorter, convert(tile.m_pos), TRop::Bilinear);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
int FreeDistortBaseFx::getMemoryRequirement(const TRectD &rect, double frame, const TRenderSettings &info)
|
|
{
|
|
if (!m_input.isConnected())
|
|
return 0;
|
|
|
|
TRectD inRect;
|
|
TRenderSettings riNew;
|
|
TRectD inBBox;
|
|
safeTransform(frame, 0, rect, info, inRect, riNew, inBBox);
|
|
inRect *= inBBox;
|
|
|
|
return TRasterFx::memorySize(inRect, riNew.m_bpp);
|
|
}
|
|
|
|
FX_PLUGIN_IDENTIFIER(FreeDistortFx, "freeDistortFx");
|
|
FX_PLUGIN_IDENTIFIER(CastShadowFx, "castShadowFx");
|
|
|
|
/*----------------------------------------------------------------------------*/
|