#include "stdfx.h"
#include "tfxparam.h"
#include "tpixelutils.h"
//===================================================================
class EmbossFx final : public TStandardRasterFx {
FX_PLUGIN_DECLARATION(EmbossFx)
TRasterFxPort m_input;
TDoubleParamP m_intensity;
TDoubleParamP m_elevation;
TDoubleParamP m_direction;
TDoubleParamP m_radius;
public:
EmbossFx()
: m_intensity(0.5), m_elevation(45.0), m_direction(90.0), m_radius(1.0) {
m_radius->setMeasureName("fxLength");
bindParam(this, "intensity", m_intensity);
bindParam(this, "elevation", m_elevation);
bindParam(this, "direction", m_direction);
bindParam(this, "radius", m_radius);
addInputPort("Source", m_input);
m_intensity->setValueRange(0.0, 1.0, 0.1);
m_elevation->setValueRange(0.0, 360.0);
m_direction->setValueRange(0.0, 360.0);
m_radius->setValueRange(0.0, 10.0);
}
~EmbossFx(){};
bool doGetBBox(double frame, TRectD &bBox,
const TRenderSettings &info) override {
if (m_input.isConnected()) {
bool ret = m_input->doGetBBox(frame, bBox, info);
return ret;
} else {
bBox = TRectD();
return false;
}
}
void transform(double frame, int port, const TRectD &rectOnOutput,
const TRenderSettings &infoOnOutput, TRectD &rectOnInput,
TRenderSettings &infoOnInput) override;
void doCompute(TTile &tile, double frame, const TRenderSettings &ri) override;
int getMemoryRequirement(const TRectD &rect, double frame,
const TRenderSettings &info) override;
bool canHandle(const TRenderSettings &info, double frame) override {
return (isAlmostIsotropic(info.m_affine));
}
};
//-------------------------------------------------------------------
template <typename PIXEL, typename PIXELGRAY, typename CHANNEL_TYPE>
void doEmboss(TRasterPT<PIXEL> ras, TRasterPT<PIXEL> srcraster, double azimuth,
double elevation, double intensity, double radius) {
double Lx = cos(azimuth) * cos(elevation) * PIXEL::maxChannelValue;
double Ly = sin(azimuth) * cos(elevation) * PIXEL::maxChannelValue;
double Lz = sin(elevation) * PIXEL::maxChannelValue;
double Nz = (6 * PIXEL::maxChannelValue) * (1 - intensity);
double Nz2 = Nz * Nz;
int j, m, n;
int border = (int)radius + 1;
double borderFracMult = radius - (int)radius;
int wrap = srcraster->getWrap();
double nsbuffer, ewbuffer;
double nsFracbuffer, ewFracbuffer;
double NdotL;
double background = Lz;
srcraster->lock();
ras->lock();
for (j = border; j < srcraster->getLy() - border; j++) {
PIXEL *pixout = ras->pixels(j - border);
PIXEL *pix = srcraster->pixels(j) + border;
PIXEL *endPixout = pixout + ras->getLx();
while (pixout < endPixout) {
double val;
// Explanation: The fx is performed by calculating kind of a normal vector
// N for
// the discrete surface generated by the {(x,y,v)} of the raster, where v
// is the
// grey tone of the pixel at (x,y).
// This vector is then dot-product-matched with the input light ray
// vector, and a pixel
// value according with the product is then returned.
// Here, we build up the sums for the X and Y components of N.
nsbuffer = 0;
ewbuffer = 0;
for (m = 1; m < border; m++)
for (n = -m; n <= m; n++) {
nsbuffer += PIXELGRAY::from(*(pix + m * wrap + n)).value;
nsbuffer -= PIXELGRAY::from(*(pix - m * wrap + n)).value;
ewbuffer += PIXELGRAY::from(*(pix + n * wrap + m)).value;
ewbuffer -= PIXELGRAY::from(*(pix + n * wrap - m)).value;
}
// As the fx radius (aka border) is a double, we make its fractionary part
// count less. This ensures continuity of the result against radius
// changes.
nsFracbuffer = 0;
ewFracbuffer = 0;
for (n = -m; n <= m; n++) {
nsFracbuffer += PIXELGRAY::from(*(pix + m * wrap + n)).value;
nsFracbuffer -= PIXELGRAY::from(*(pix - m * wrap + n)).value;
ewFracbuffer += PIXELGRAY::from(*(pix + n * wrap + m)).value;
ewFracbuffer -= PIXELGRAY::from(*(pix + n * wrap - m)).value;
}
nsbuffer += nsFracbuffer * borderFracMult;
ewbuffer += ewFracbuffer * borderFracMult;
// Here the dot-product is performed and the result is finally returned.
double Nx = ewbuffer;
double Ny = nsbuffer;
Nx = Nx / radius;
Ny = Ny / radius;
// val= 127+sinsin*nordsud(pix, wrap)+coscos*eastwest(pix, wrap);
if (Nx == 0 && Ny == 0)
val = background;
else if ((NdotL = Nx * Lx + Ny * Ly + Nz * Lz) < 0)
val = 0;
else
val = NdotL / sqrt(Nx * Nx + Ny * Ny + Nz2);
(pixout)->r = (val < PIXEL::maxChannelValue)
? (val > 0 ? (CHANNEL_TYPE)val : 0)
: PIXEL::maxChannelValue;
(pixout)->g = pixout->r;
(pixout)->b = pixout->r;
(pixout)->m = (pix)->m;
*pixout = premultiply(*pixout);
pix++;
pixout++;
}
}
srcraster->unlock();
ras->unlock();
}
//-------------------------------------------------------------------
void EmbossFx::doCompute(TTile &tile, double frame, const TRenderSettings &ri) {
if (!m_input.isConnected()) return;
double min, max, step;
m_radius->getValueRange(min, max, step);
double scale = sqrt(fabs(ri.m_affine.det()));
double radius = tcrop(m_radius->getValue(frame), min, max) * scale;
double direction = (m_direction->getValue(frame));
double elevation = (m_elevation->getValue(frame)) * M_PI_180;
double intensity = m_intensity->getValue(frame);
double azimuth = direction * M_PI_180;
// NOTE: This enlargement is perhaps needed in the calculation of the fx - but
// no output will
// be generated for it - so there is no trace of it in the doGetBBox
// function...
int border = radius + 1;
TRasterP srcRas =
tile.getRaster()->create(tile.getRaster()->getLx() + border * 2,
tile.getRaster()->getLy() + border * 2);
TTile srcTile(srcRas, tile.m_pos - TPointD(border, border));
m_input->compute(srcTile, frame, ri);
TRaster32P raster32 = tile.getRaster();
TRaster32P srcraster32 = srcTile.getRaster();
if (raster32)
doEmboss<TPixel32, TPixelGR8, UCHAR>(raster32, srcraster32, azimuth,
elevation, intensity, radius);
else {
TRaster64P raster64 = tile.getRaster();
TRaster64P srcraster64 = srcTile.getRaster();
if (raster64)
doEmboss<TPixel64, TPixelGR16, USHORT>(raster64, srcraster64, azimuth,
elevation, intensity, radius);
else
throw TException("Brightness&Contrast: unsupported Pixel Type");
}
}
//------------------------------------------------------------------
void EmbossFx::transform(double frame, int port, const TRectD &rectOnOutput,
const TRenderSettings &infoOnOutput,
TRectD &rectOnInput, TRenderSettings &infoOnInput) {
infoOnInput = infoOnOutput;
double min, max, step;
m_radius->getValueRange(min, max, step);
double scale = sqrt(fabs(infoOnOutput.m_affine.det()));
double radius = (tcrop(m_radius->getValue(frame), min, max) * scale);
int border = radius + 1;
rectOnInput = rectOnOutput.enlarge(border);
}
//------------------------------------------------------------------
int EmbossFx::getMemoryRequirement(const TRectD &rect, double frame,
const TRenderSettings &info) {
double scale = sqrt(fabs(info.m_affine.det()));
double radius = m_radius->getValue(frame) * scale;
int border = radius + 1;
return TRasterFx::memorySize(rect.enlarge(border), info.m_bpp);
}
FX_PLUGIN_IDENTIFIER(EmbossFx, "embossFx");