#include "stdfx.h" #include "tfxparam.h" #include "tpixelutils.h" #include "tparamset.h" #include "ttonecurveparam.h" #include "tcurves.h" #include "globalcontrollablefx.h" //=================================================================== namespace { int getCubicYfromX(TCubic c, int x, double &s0, double &s1) { double s = (s1 + s0) * 0.5; TPointD p = c.getPoint(s); if (areAlmostEqual(double(x), p.x, 0.001)) return tround(p.y); if (x < p.x) return getCubicYfromX(c, x, s0, s); else return getCubicYfromX(c, x, s, s1); } int getLinearYfromX(TSegment t, int x, double &s0, double &s1) { double s = (s1 + s0) * 0.5; TPointD p = t.getPoint(s); if (areAlmostEqual(double(x), p.x, 0.001)) return tround(p.y); if (x < p.x) return getLinearYfromX(t, x, s0, s); else return getLinearYfromX(t, x, s, s1); } void truncateSpeeds(double aFrame, double bFrame, TPointD &aSpeedTrunc, TPointD &bSpeedTrunc) { double deltaX = bFrame - aFrame; if (aSpeedTrunc.x < 0) aSpeedTrunc.x = 0; if (bSpeedTrunc.x > 0) bSpeedTrunc.x = 0; if (aFrame + aSpeedTrunc.x > bFrame) { if (aSpeedTrunc.x != 0) { aSpeedTrunc = aSpeedTrunc * (deltaX / aSpeedTrunc.x); } } if (bFrame + bSpeedTrunc.x < aFrame) { if (bSpeedTrunc.x != 0) { bSpeedTrunc = -bSpeedTrunc * (deltaX / bSpeedTrunc.x); } } } template void fill_lut(QList points, std::vector &lut, bool isLinear) { int i; TPointD p0 = points.at(0); TCubic cubic; TSegment segment; double s0 = 0.0; for (i = 1; i < points.size(); i++) { TPointD p1 = points.at(i); TPointD p2 = points.at(++i); TPointD p3 = points.at(++i); if (!isLinear) { // truncate speed TPointD aSpeed(p1 - p0); TPointD bSpeed(p2 - p3); truncateSpeeds(p0.x, p3.x, aSpeed, bSpeed); cubic.setP0(p0); cubic.setP1(p0 + aSpeed); cubic.setP2(p3 + bSpeed); cubic.setP3(p3); int x = (int)p0.x; while (x < 0) x++; while (x < p3.x && x < PIXEL::maxChannelValue + 1) { double s1 = 1.0; int y = getCubicYfromX(cubic, x, s0, s1); if (y > PIXEL::maxChannelValue) y = PIXEL::maxChannelValue; else if (y < 0) y = 0; lut[x] = y; x++; } } else { segment.setP0(p0); segment.setP1(p3); int x = (int)p0.x; while (x < 0) x++; while (x < p3.x && x < PIXEL::maxChannelValue + 1) { double s1 = 1.0; int y = getLinearYfromX(segment, x, s0, s1); if (y > PIXEL::maxChannelValue) y = PIXEL::maxChannelValue; else if (y < 0) y = 0; lut[x] = y; x++; } } p0 = p3; } } } // Namespace //=================================================================== class ToneCurveFx final : public GlobalControllableFx { FX_PLUGIN_DECLARATION(ToneCurveFx) TRasterFxPort m_input; TToneCurveParamP m_toneCurve; public: ToneCurveFx() : m_toneCurve(new TToneCurveParam()) { bindParam(this, "curve", m_toneCurve); addInputPort("Source", m_input); } ~ToneCurveFx(){}; bool canHandle(const TRenderSettings &info, double frame) override { return true; } bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info) override { if (m_input.isConnected()) return m_input->doGetBBox(frame, bBox, info); else { bBox = TRectD(); return false; } } void doCompute(TTile &tile, double frame, const TRenderSettings &ri) override; }; //------------------------------------------------------------------- namespace { void update_param(double ¶m, TRaster32P ras) { return; } void update_param(double ¶m, TRaster64P ras) { param = param * 257; return; } QList getParamSetPoints(const TParamSet *paramSet, int frame) { QList points; int i; for (i = 0; i < paramSet->getParamCount(); i++) { TPointParamP pointParam = paramSet->getParam(i); TPointD point = pointParam->getValue(frame); int x = (int)point.x; int y = (int)point.y; points.push_back(TPointD(x, y)); } return points; } } // namespace //------------------------------------------------------------------- template void doToneCurveFx(TRasterPT ras, double frame, const TToneCurveParam *toneCurveParam) { QList> pointsList; int e; for (e = 0; e < 6; e++) { TParamSet *paramSet = toneCurveParam->getParamSet(TToneCurveParam::ToneChannel(e)) .getPointer(); QList points = getParamSetPoints(paramSet, frame); pointsList.push_back(points); } bool isLinear = toneCurveParam->isLinear(); int i, t; for (i = 0; i < pointsList.size(); i++) { QList &points = pointsList[i]; for (t = 0; t < points.size(); t++) { TPointD &p = points[t]; double &x = p.x; double &y = p.y; update_param(x, ras); update_param(y, ras); } } std::vector rgbaLut(PIXEL::maxChannelValue + 1); std::vector rgbLut(PIXEL::maxChannelValue + 1); std::vector rLut(PIXEL::maxChannelValue + 1); std::vector gLut(PIXEL::maxChannelValue + 1); std::vector bLut(PIXEL::maxChannelValue + 1); std::vector aLut(PIXEL::maxChannelValue + 1); fill_lut(pointsList[0], rgbaLut, isLinear); fill_lut(pointsList[1], rgbLut, isLinear); fill_lut(pointsList[2], rLut, isLinear); fill_lut(pointsList[3], gLut, isLinear); fill_lut(pointsList[4], bLut, isLinear); fill_lut(pointsList[5], aLut, isLinear); int lx = ras->getLx(); int ly = ras->getLy(); int j; ras->lock(); for (j = 0; j < ly; j++) { PIXEL *pix = ras->pixels(j); PIXEL *endPix = pix + lx; while (pix < endPix) { if (pix->m != 0 && pix->m != PIXEL::maxChannelValue) *pix = depremultiply(*pix); pix->r = rLut[(int)(pix->r)]; pix->g = gLut[(int)(pix->g)]; pix->b = bLut[(int)(pix->b)]; pix->m = aLut[(int)(pix->m)]; pix->r = rgbLut[(int)(pix->r)]; pix->g = rgbLut[(int)(pix->g)]; pix->b = rgbLut[(int)(pix->b)]; pix->r = rgbaLut[(int)(pix->r)]; pix->g = rgbaLut[(int)(pix->g)]; pix->b = rgbaLut[(int)(pix->b)]; pix->m = rgbaLut[(int)(pix->m)]; if (pix->m != 0 && pix->m != PIXEL::maxChannelValue) *pix = premultiply(*pix); pix++; } } ras->unlock(); } //------------------------------------------------------------------- void ToneCurveFx::doCompute(TTile &tile, double frame, const TRenderSettings &ri) { if (!m_input.isConnected()) return; m_input->compute(tile, frame, ri); TRaster32P raster32 = tile.getRaster(); if (raster32) doToneCurveFx(raster32, frame, m_toneCurve.getPointer()); else { TRaster64P raster64 = tile.getRaster(); if (raster64) doToneCurveFx(raster64, frame, m_toneCurve.getPointer()); else throw TException("Brightness&Contrast: unsupported Pixel Type"); } } FX_PLUGIN_IDENTIFIER(ToneCurveFx, "toneCurveFx");