| |
| |
| #include "stdfx.h" |
| #include "tfxparam.h" |
| #include "tpixelutils.h" |
| #include "tparamset.h" |
| #include "ttonecurveparam.h" |
| #include "tcurves.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); |
| } |
| |
| template <typename PIXEL, typename T> |
| void fill_lut(QList<TPointD> points, std::vector<T> &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) { |
| cubic.setP0(p0); |
| cubic.setP1(p1); |
| cubic.setP2(p2); |
| 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 + 1) |
| 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 + 1) |
| y = PIXEL::maxChannelValue; |
| else if (y < 0) |
| y = 0; |
| lut[x] = y; |
| x++; |
| } |
| } |
| p0 = p3; |
| } |
| } |
| |
| } |
| |
| |
| |
| class ToneCurveFx final : public TStandardRasterFx { |
| 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<TPointD> getParamSetPoints(const TParamSet *paramSet, int frame) { |
| QList<TPointD> 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; |
| } |
| } |
| |
| |
| |
| template <typename PIXEL, typename CHANNEL_TYPE> |
| void doToneCurveFx(TRasterPT<PIXEL> ras, double frame, |
| const TToneCurveParam *toneCurveParam) { |
| QList<QList<TPointD>> pointsList; |
| int e; |
| for (e = 0; e < 6; e++) { |
| TParamSet *paramSet = |
| toneCurveParam->getParamSet(TToneCurveParam::ToneChannel(e)) |
| .getPointer(); |
| QList<TPointD> points = getParamSetPoints(paramSet, frame); |
| pointsList.push_back(points); |
| } |
| bool isLinear = toneCurveParam->isLinear(); |
| |
| int i, t; |
| for (i = 0; i < pointsList.size(); i++) { |
| QList<TPointD> &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<CHANNEL_TYPE> rgbaLut(PIXEL::maxChannelValue + 1); |
| std::vector<CHANNEL_TYPE> rgbLut(PIXEL::maxChannelValue + 1); |
| std::vector<CHANNEL_TYPE> rLut(PIXEL::maxChannelValue + 1); |
| std::vector<CHANNEL_TYPE> gLut(PIXEL::maxChannelValue + 1); |
| std::vector<CHANNEL_TYPE> bLut(PIXEL::maxChannelValue + 1); |
| std::vector<CHANNEL_TYPE> aLut(PIXEL::maxChannelValue + 1); |
| |
| fill_lut<PIXEL, CHANNEL_TYPE>(pointsList[0], rgbaLut, isLinear); |
| fill_lut<PIXEL, CHANNEL_TYPE>(pointsList[1], rgbLut, isLinear); |
| fill_lut<PIXEL, CHANNEL_TYPE>(pointsList[2], rLut, isLinear); |
| fill_lut<PIXEL, CHANNEL_TYPE>(pointsList[3], gLut, isLinear); |
| fill_lut<PIXEL, CHANNEL_TYPE>(pointsList[4], bLut, isLinear); |
| fill_lut<PIXEL, CHANNEL_TYPE>(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<TPixel32, UCHAR>(raster32, frame, m_toneCurve.getPointer()); |
| else { |
| TRaster64P raster64 = tile.getRaster(); |
| if (raster64) |
| doToneCurveFx<TPixel64, USHORT>(raster64, frame, |
| m_toneCurve.getPointer()); |
| else |
| throw TException("Brightness&Contrast: unsupported Pixel Type"); |
| } |
| } |
| |
| FX_PLUGIN_IDENTIFIER(ToneCurveFx, "toneCurveFx"); |
| |