#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 void doEmboss(TRasterPT ras, TRasterPT 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(raster32, srcraster32, azimuth, elevation, intensity, radius); else { TRaster64P raster64 = tile.getRaster(); TRaster64P srcraster64 = srcTile.getRaster(); if (raster64) doEmboss(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");