#include "stdfx.h" #include "tfxparam.h" #include "trop.h" #include "tdoubleparam.h" #include "trasterfx.h" /* (Daniele) NOTE: Current LocalBlurFx is effectively flawed. Following implementation relies on the idea that the blurring filter is separable and therefore appliable on rows and columns, in sequence. It actually is not. It can be easily verified applying the fx on a chessboard with a strong blur intensity. The squares will be cast vertically towards blurred regions. Originally, this was a sub-optimal O(lx * ly * blur) algorithm. The following (still separated) is O(lx * ly) using precomputed sums in an additional line. The 'correct' algorithm could be implemented again as O(lx * ly * blur), with additional O(lx * blur) memory usage with precomputed sums along (blur) rows. Pixels would have to be filtered one by one, but the horizontal filtering per pixel convolution row would take O(1). Thus it would be O(blur) (column filtering) per pixel. */ //******************************************************************************** // Local namespace stuff //******************************************************************************** namespace { struct Sums { std::unique_ptr m_sumsIX_r; //!< m_sumsIX1[i+1] = m_sumsIX1[i] + i * pix.r std::unique_ptr m_sumsIX_g; std::unique_ptr m_sumsIX_b; std::unique_ptr m_sumsIX_m; std::unique_ptr m_sumsX_r; //!< m_sumsX[i+1] = m_sumsX[i] + pix.r std::unique_ptr m_sumsX_g; std::unique_ptr m_sumsX_b; std::unique_ptr m_sumsX_m; Sums(int length) : m_sumsIX_r(new TUINT64[length + 1]) , m_sumsIX_g(new TUINT64[length + 1]) , m_sumsIX_b(new TUINT64[length + 1]) , m_sumsIX_m(new TUINT64[length + 1]) , m_sumsX_r(new TUINT64[length + 1]) , m_sumsX_g(new TUINT64[length + 1]) , m_sumsX_b(new TUINT64[length + 1]) , m_sumsX_m(new TUINT64[length + 1]) {} template void build(Pix *line, int wrap, int n) { ++n; m_sumsIX_r[0] = m_sumsX_r[0] = 0; m_sumsIX_g[0] = m_sumsX_g[0] = 0; m_sumsIX_b[0] = m_sumsX_b[0] = 0; m_sumsIX_m[0] = m_sumsX_m[0] = 0; Pix *pix; int i, i_1; for (pix = line, i_1 = 0, i = 1; i < n; pix += wrap, i_1 = i++) { m_sumsIX_r[i] = m_sumsIX_r[i_1] + i * pix->r; m_sumsIX_g[i] = m_sumsIX_g[i_1] + i * pix->g; m_sumsIX_b[i] = m_sumsIX_b[i_1] + i * pix->b; m_sumsIX_m[i] = m_sumsIX_m[i_1] + i * pix->m; m_sumsX_r[i] = m_sumsX_r[i_1] + pix->r; m_sumsX_g[i] = m_sumsX_g[i_1] + pix->g; m_sumsX_b[i] = m_sumsX_b[i_1] + pix->b; m_sumsX_m[i] = m_sumsX_m[i_1] + pix->m; } } }; //---------------------------------------------------------------------------- template void filterLine(Pix *lineIn, int wrapIn, Grey *lineGr, int wrapGr, Pix *lineOut, int wrapOut, int length, double blurFactor, Sums &sums) { // Build temporary sums sums.build(lineIn, wrapIn, length); // Declare vars double blur, kLeft, kRight, cLeft, cRight; double blurI; // Perform line filtering Pix *pixIn, *pixOut; Grey *pixGr; int i, iLeft, iRight; ++length; for (i = 1, pixIn = lineIn, pixGr = lineGr, pixOut = lineOut; i < length; ++i, pixIn += wrapIn, pixGr += wrapGr, pixOut += wrapOut) { blur = pixGr->value * blurFactor; // A table of factors should be made - // since we have a finite if (blur > 0.0) { blur += 0.5; /*-- 0.5足すのは、注目ピクセルの半径分。例えばBlur0.5は、 注目ピクセルの外側0.5ピクセルボケるということなので。 --*/ blurI = (double)tfloor(blur); double amount = blur + (2 * blur - blurI - 1) * blurI; double dR = 1.0 / amount; double ini = (blur - blurI) / amount; kLeft = dR; cLeft = ini - dR * (i - blurI); kRight = -dR; cRight = blur / amount + dR * i; // NOTE: The normalization factor with blur (not integer) would be: // 1.0 / (1 + 2 * blurI - (blurI / blur) * (blurI + 1)) // -- could be done using a factors table iLeft = std::max(i - tfloor(blur) - 1, 0); iRight = std::min(i + tfloor(blur), length - 1); pixOut->r = troundp(kLeft * (sums.m_sumsIX_r[i] - sums.m_sumsIX_r[iLeft]) + kRight * (sums.m_sumsIX_r[iRight] - sums.m_sumsIX_r[i]) + cLeft * (sums.m_sumsX_r[i] - sums.m_sumsX_r[iLeft]) + cRight * (sums.m_sumsX_r[iRight] - sums.m_sumsX_r[i])); pixOut->g = troundp(kLeft * (sums.m_sumsIX_g[i] - sums.m_sumsIX_g[iLeft]) + kRight * (sums.m_sumsIX_g[iRight] - sums.m_sumsIX_g[i]) + cLeft * (sums.m_sumsX_g[i] - sums.m_sumsX_g[iLeft]) + cRight * (sums.m_sumsX_g[iRight] - sums.m_sumsX_g[i])); pixOut->b = troundp(kLeft * (sums.m_sumsIX_b[i] - sums.m_sumsIX_b[iLeft]) + kRight * (sums.m_sumsIX_b[iRight] - sums.m_sumsIX_b[i]) + cLeft * (sums.m_sumsX_b[i] - sums.m_sumsX_b[iLeft]) + cRight * (sums.m_sumsX_b[iRight] - sums.m_sumsX_b[i])); pixOut->m = troundp(kLeft * (sums.m_sumsIX_m[i] - sums.m_sumsIX_m[iLeft]) + kRight * (sums.m_sumsIX_m[iRight] - sums.m_sumsIX_m[i]) + cLeft * (sums.m_sumsX_m[i] - sums.m_sumsX_m[iLeft]) + cRight * (sums.m_sumsX_m[iRight] - sums.m_sumsX_m[i])); } else *pixOut = *pixIn; } } //---------------------------------------------------------------------------- template void doLocalBlur(TRasterPT rin, TRasterPT rcontrol, TRasterPT rout, double blur, const TPoint &displacement) { assert(rin->getLx() == rcontrol->getLx() && rin->getLy() == rcontrol->getLy()); double blurFactor = blur / Grey::maxChannelValue; int x, y, inLx, inLy, outLx, outLy, wrapIn, wrapOut, wrapC; inLx = rin->getLx(); inLy = rin->getLy(); wrapIn = rin->getWrap(); outLx = rout->getLx(); outLy = rout->getLy(); wrapOut = rout->getWrap(); // Convert the control raster to grey values (this avoids the overhead of // performing the pixel-to-value // conversion twice, once per line filtering) TRasterPT rcontrolGrey(rcontrol->getLx(), rcontrol->getLy()); TRop::convert(rcontrolGrey, rcontrol); wrapC = rcontrolGrey->getWrap(); Pix *lineIn, *bufIn; Grey *lineC, *bufC; // Filter rin. The output filtering is still stored in rin. rin->lock(); rcontrolGrey->lock(); bufIn = rin->pixels(0); bufC = rcontrolGrey->pixels(0); { Sums sums(inLx); for (y = 0, lineIn = bufIn, lineC = bufC; y < inLy; ++y, lineIn += wrapIn, lineC += wrapC) { // Filter row filterLine(lineIn, 1, lineC, 1, lineIn, 1, inLx, blurFactor, sums); } } { Sums sums(inLy); for (x = 0, lineIn = bufIn, lineC = bufC; x < inLx; ++x, ++lineIn, ++lineC) { // Filter column filterLine(lineIn, wrapIn, lineC, wrapC, lineIn, wrapIn, inLy, blurFactor, sums); } } rin->unlock(); rcontrolGrey->unlock(); // Copy the interesting part of rin to rout TRect rectOut(rout->getBounds() - displacement); TRect rectIn(rin->getBounds() + displacement); TRop::copy(rout->extract(rectIn), rin->extract(rectOut)); } } // namespace //******************************************************************************** // LocalBlurFx implementation //******************************************************************************** class LocalBlurFx final : public TStandardRasterFx { FX_PLUGIN_DECLARATION(LocalBlurFx) protected: TRasterFxPort m_up, m_ref; TDoubleParamP m_value; public: LocalBlurFx() : m_value(20) { m_value->setMeasureName("fxLength"); addInputPort("Source", m_up); addInputPort("Reference", m_ref); bindParam(this, "value", m_value); m_value->setValueRange(0, (std::numeric_limits::max)()); } ~LocalBlurFx() {} bool canHandle(const TRenderSettings &info, double frame) override { return (isAlmostIsotropic(info.m_affine) || m_value->getValue(frame) == 0); } bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) override { if (m_up.isConnected()) { bool ret = m_up->doGetBBox(frame, bBox, info); double blur = fabs(m_value->getValue(frame)); int blurI = tceil(blur); bBox = bBox.enlarge(blurI); return ret; } else { bBox = TRectD(); return false; } } void enlarge(const TRectD &bbox, TRectD &requestedRect, int blur); void doDryCompute(TRectD &rect, double frame, const TRenderSettings &info) override; void doCompute(TTile &tile, double frame, const TRenderSettings &info) override; int getMemoryRequirement(const TRectD &rect, double frame, const TRenderSettings &info) override; }; //------------------------------------------------------------------- void LocalBlurFx::enlarge(const TRectD &bbox, TRectD &requestedRect, int blur) { // See BlurFx: this is a faithful replica if (bbox.isEmpty() || requestedRect.isEmpty()) { requestedRect.empty(); return; } TRectD enlargedBBox(bbox.enlarge(blur)); TRectD enlargedOut(requestedRect.enlarge(blur)); TPointD originalP00(requestedRect.getP00()); requestedRect = (enlargedOut * bbox) + (enlargedBBox * requestedRect); 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 LocalBlurFx::doDryCompute(TRectD &rectOut, double frame, const TRenderSettings &info) { // Mimics the doCompute() without actual computation. TTile refTile; bool isUp = m_up.isConnected(); bool isDown = m_ref.isConnected(); if (!isUp) return; if (!isDown) { m_up->dryCompute(rectOut, frame, info); return; } double blur = fabs(m_value->getValue(frame) * sqrt(fabs(info.m_affine.det()))); int blurI = tceil(blur); TRectD bboxIn; if (!m_up->getBBox(frame, bboxIn, info) || rectOut.isEmpty()) return; TRectD rectIn(rectOut); enlarge(bboxIn, rectIn, blurI); if (rectIn.isEmpty()) return; m_up->dryCompute(rectOut, frame, info); m_ref->dryCompute(rectOut, frame, info); } //------------------------------------------------------------------- void LocalBlurFx::doCompute(TTile &tile, double frame, const TRenderSettings &info) { TTile refTile; bool isUp = m_up.isConnected(); bool isDown = m_ref.isConnected(); if (!isUp) return; if (!isDown) { m_up->compute(tile, frame, info); return; } // Generic case assert(isAlmostIsotropic(info.m_affine)); double blur = fabs(m_value->getValue(frame) * sqrt(fabs(info.m_affine.det()))); int blurI = tceil(blur); // Get the requested tile's geometry TRectD rectOut(tile.m_pos, TDimensionD(tile.getRaster()->getLx(), tile.getRaster()->getLy())); // Retrieve the input interesting geometry - and ensure that something // actually has // to be computed TRectD bboxIn; if (!m_up->getBBox(frame, bboxIn, info) || rectOut.isEmpty()) return; TRectD rectIn(rectOut); enlarge(bboxIn, rectIn, blurI); if (rectIn.isEmpty()) return; // Finally, allocate and compute the blur argument TTile tileIn; m_up->allocateAndCompute(tileIn, rectIn.getP00(), TDimension(rectIn.getLx(), rectIn.getLy()), tile.getRaster(), frame, info); TTile tileRef; m_ref->allocateAndCompute(tileRef, rectIn.getP00(), TDimension(rectIn.getLx(), rectIn.getLy()), tile.getRaster(), frame, info); // Perform Local Blur TRasterP inRas(tileIn.getRaster()); TRasterP refRas(tileRef.getRaster()); TRasterP outRas(tile.getRaster()); TRaster32P in32(inRas); TRaster32P ref32(refRas); TRaster32P out32(outRas); TPoint displacement(convert( rectIn.getP00() - tile.m_pos)); // inTile position relative to (out)tile // The difference already has integer coordinates due to enlarge() if (in32 && ref32 && out32) doLocalBlur(in32, ref32, out32, blur, displacement); else { TRaster64P in64(inRas); TRaster64P ref64(refRas); TRaster64P out64(outRas); if (in64 && ref64 && out64) doLocalBlur(in64, ref64, out64, blur, displacement); else throw TException("LocalBlurFx: unsupported raster type"); } } //------------------------------------------------------------------- int LocalBlurFx::getMemoryRequirement(const TRectD &rect, double frame, const TRenderSettings &info) { double blur = fabs(m_value->getValue(frame) * sqrt(fabs(info.m_affine.det()))); int blurI = tceil(blur); return 2 * TRasterFx::memorySize(rect.enlarge(blurI), info.m_bpp); } //------------------------------------------------------------------- FX_PLUGIN_IDENTIFIER(LocalBlurFx, "localBlurFx")