diff --git a/libs/image/kis_brush_mask_applicator_factories.cpp b/libs/image/kis_brush_mask_applicator_factories.cpp index 662e81e96c..0cca6e1d0f 100644 --- a/libs/image/kis_brush_mask_applicator_factories.cpp +++ b/libs/image/kis_brush_mask_applicator_factories.cpp @@ -1,247 +1,377 @@ /* * Copyright (c) 2012 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_brush_mask_applicator_factories.h" #include "kis_circle_mask_generator.h" #include "kis_circle_mask_generator_p.h" #include "kis_gauss_circle_mask_generator_p.h" +#include "kis_curve_circle_mask_generator_p.h" + #include "kis_brush_mask_applicators.h" #include "kis_brush_mask_applicator_base.h" #define a(_s) #_s #define b(_s) a(_s) template<> template<> MaskApplicatorFactory::ReturnType MaskApplicatorFactory::create(ParamType maskGenerator) { return new KisBrushMaskScalarApplicator(maskGenerator); } template<> template<> MaskApplicatorFactory::ReturnType MaskApplicatorFactory::create(ParamType maskGenerator) { return new KisBrushMaskVectorApplicator(maskGenerator); } template<> template<> MaskApplicatorFactory::ReturnType MaskApplicatorFactory::create(ParamType maskGenerator) { return new KisBrushMaskVectorApplicator(maskGenerator); } + +template<> +template<> +MaskApplicatorFactory::ReturnType +MaskApplicatorFactory::create(ParamType maskGenerator) +{ + return new KisBrushMaskVectorApplicator(maskGenerator); +} + + #if defined HAVE_VC struct KisCircleMaskGenerator::FastRowProcessor { FastRowProcessor(KisCircleMaskGenerator *maskGenerator) : d(maskGenerator->d.data()) {} template void process(float* buffer, int width, float y, float cosa, float sina, float centerX, float centerY); KisCircleMaskGenerator::Private *d; }; template<> void KisCircleMaskGenerator:: FastRowProcessor::process(float* buffer, int width, float y, float cosa, float sina, float centerX, float centerY) { const bool useSmoothing = d->copyOfAntialiasEdges; const bool noFading = d->noFading; float y_ = y - centerY; float sinay_ = sina * y_; float cosay_ = cosa * y_; float* bufferPointer = buffer; Vc::float_v currentIndices = Vc::float_v::IndexesFromZero(); Vc::float_v increment((float)Vc::float_v::size()); Vc::float_v vCenterX(centerX); Vc::float_v vCosa(cosa); Vc::float_v vSina(sina); Vc::float_v vCosaY_(cosay_); Vc::float_v vSinaY_(sinay_); Vc::float_v vXCoeff(d->xcoef); Vc::float_v vYCoeff(d->ycoef); Vc::float_v vTransformedFadeX(d->transformedFadeX); Vc::float_v vTransformedFadeY(d->transformedFadeY); Vc::float_v vOne(Vc::One); for (int i=0; i < width; i+= Vc::float_v::size()){ Vc::float_v x_ = currentIndices - vCenterX; Vc::float_v xr = x_ * vCosa - vSinaY_; Vc::float_v yr = x_ * vSina + vCosaY_; Vc::float_v n = pow2(xr * vXCoeff) + pow2(yr * vYCoeff); Vc::float_m outsideMask = n > vOne; if (!outsideMask.isFull()) { if (noFading) { Vc::float_v vFade(Vc::Zero); vFade(outsideMask) = vOne; vFade.store(bufferPointer, Vc::Aligned); } else { if (useSmoothing) { xr = Vc::abs(xr) + vOne; yr = Vc::abs(yr) + vOne; } Vc::float_v vNormFade = pow2(xr * vTransformedFadeX) + pow2(yr * vTransformedFadeY); //255 * n * (normeFade - 1) / (normeFade - n) Vc::float_v vFade = n * (vNormFade - vOne) / (vNormFade - n); // Mask in the inner circe of the mask Vc::float_m mask = vNormFade < vOne; vFade.setZero(mask); // Mask out the outer circe of the mask vFade(outsideMask) = vOne; vFade.store(bufferPointer, Vc::Aligned); } } else { // Mask out everything outside the circle vOne.store(bufferPointer, Vc::Aligned); } currentIndices = currentIndices + increment; bufferPointer += Vc::float_v::size(); } } struct KisGaussCircleMaskGenerator::FastRowProcessor { FastRowProcessor(KisGaussCircleMaskGenerator *maskGenerator) : d(maskGenerator->d.data()) {} template void process(float* buffer, int width, float y, float cosa, float sina, float centerX, float centerY); KisGaussCircleMaskGenerator::Private *d; }; template<> void KisGaussCircleMaskGenerator:: FastRowProcessor::process(float* buffer, int width, float y, float cosa, float sina, float centerX, float centerY) { float y_ = y - centerY; float sinay_ = sina * y_; float cosay_ = cosa * y_; float* bufferPointer = buffer; Vc::float_v currentIndices = Vc::float_v::IndexesFromZero(); Vc::float_v increment((float)Vc::float_v::size()); Vc::float_v vCenterX(centerX); Vc::float_v vCenter(d->center); Vc::float_v vCosa(cosa); Vc::float_v vSina(sina); Vc::float_v vCosaY_(cosay_); Vc::float_v vSinaY_(sinay_); Vc::float_v vYCoeff(d->ycoef); Vc::float_v vDistfactor(d->distfactor); Vc::float_v vAlphafactor(d->alphafactor); Vc::float_v vFadeRadius(d->fadeMaker.getRadius()); Vc::float_v vFadeStartValue(d->fadeMaker.getFadeStartValue()); Vc::float_v vFadeAFadeStart(d->fadeMaker.getAntialiasingFadeStart()); Vc::float_v vFadeAFadeCoeff(d->fadeMaker.getAntialiasingFadeCoeff()); Vc::float_v vOne(Vc::One); Vc::float_v vZero(Vc::Zero); Vc::float_v vValMax(255.f); for (int i=0; i < width; i+= Vc::float_v::size()){ Vc::float_v x_ = currentIndices - vCenterX; Vc::float_v xr = x_ * vCosa - vSinaY_; Vc::float_v yr = x_ * vSina + vCosaY_; Vc::float_v dist = sqrt(pow2(xr) + pow2(yr * vYCoeff)); // BEGIN FadeMaker needFade vectorized // follow fademaker rules for outsideMask Vc::float_m outsideMask = dist > vFadeRadius; dist(outsideMask) = vOne; Vc::float_m fadeStartMask = dist > vFadeAFadeStart; dist((outsideMask ^ fadeStartMask) & fadeStartMask) = (vFadeStartValue + (dist - vFadeAFadeStart) * vFadeAFadeCoeff) / vValMax; Vc::float_m excludeMask = outsideMask | fadeStartMask; if (!excludeMask.isFull()) { Vc::float_v valDist = dist * vDistfactor; Vc::float_v fullFade = vAlphafactor * ( d->vErf(valDist + vCenter) - d->vErf(valDist - vCenter)); Vc::float_m mask; // Mask in the inner circe of the mask mask = fullFade < vZero; fullFade.setZero(mask); // Mask (value - value), presicion errors. mask = fullFade >= vValMax; Vc::float_v vFade = (vValMax - fullFade) / vValMax; vFade(mask) = vZero; // filter nan and inf. Vc uses float, imprecission errors are frequent mask = Vc::isfinite(vFade); vFade(!mask) = vOne; // Mask the inner circe of the mask mask = vFade < vZero; vFade(mask) = vZero; // return original dist values before vFade transform vFade(excludeMask) = dist; vFade.store(bufferPointer, Vc::Aligned); } else { dist.store(bufferPointer, Vc::Aligned); } currentIndices = currentIndices + increment; bufferPointer += Vc::float_v::size(); } } +struct KisCurveCircleMaskGenerator::FastRowProcessor +{ + FastRowProcessor(KisCurveCircleMaskGenerator *maskGenerator) + : d(maskGenerator->d.data()) {} + + template + void process(float* buffer, int width, float y, float cosa, float sina, + float centerX, float centerY); + + KisCurveCircleMaskGenerator::Private *d; +}; + + +template<> void KisCurveCircleMaskGenerator:: +FastRowProcessor::process(float* buffer, int width, float y, float cosa, float sina, + float centerX, float centerY) +{ + float y_ = y - centerY; + float sinay_ = sina * y_; + float cosay_ = cosa * y_; + + float* bufferPointer = buffer; + + qreal* curveDataPointer = d->curveData.data(); + + Vc::float_v currentIndices = Vc::float_v::IndexesFromZero(); + + Vc::float_v increment((float)Vc::float_v::size()); + Vc::float_v vCenterX(centerX); + + Vc::float_v vCosa(cosa); + Vc::float_v vSina(sina); + Vc::float_v vCosaY_(cosay_); + Vc::float_v vSinaY_(sinay_); + + Vc::float_v vYCoeff(d->ycoef); + Vc::float_v vXCoeff(d->xcoef); + Vc::float_v vCurveResolution(d->curveResolution); + + Vc::float_v vCurvedData(Vc::Zero); + + Vc::float_v vFadeRadius(d->fadeMaker.getRadius()); + Vc::float_v vFadeStartValue(d->fadeMaker.getFadeStartValue()); + Vc::float_v vFadeAFadeStart(d->fadeMaker.getAntialiasingFadeStart()); + Vc::float_v vFadeAFadeCoeff(d->fadeMaker.getAntialiasingFadeCoeff()); + + Vc::float_v vOne(Vc::One); + Vc::float_v vZero(Vc::Zero); + Vc::float_v vValMax(255.f); + + for (int i=0; i < width; i+= Vc::float_v::size()){ + + Vc::float_v x_ = currentIndices - vCenterX; + + Vc::float_v xr = x_ * vCosa - vSinaY_; + Vc::float_v yr = x_ * vSina + vCosaY_; + + Vc::float_v dist = pow2(xr * vXCoeff) + pow2(yr * vYCoeff); + + // BEGIN FadeMaker needFade vectorized + // follow fademaker rules for outsideMask + Vc::float_m outsideMask = dist > vFadeRadius; + dist(outsideMask) = vOne; + + + Vc::float_m fadeStartMask = dist > vFadeAFadeStart; + dist((outsideMask ^ fadeStartMask) & fadeStartMask) = (vFadeStartValue + (dist - vFadeAFadeStart) * vFadeAFadeCoeff) / vValMax; + + Vc::float_m excludeMask = outsideMask | fadeStartMask; + + if (!excludeMask.isFull()) { + Vc::float_v valDist = dist * vCurveResolution; + // truncate + Vc::SimdArray vAlphaValue(valDist); + Vc::float_v vFloatAlphaValue = vAlphaValue; + + Vc::float_v alphaValueF = valDist - vFloatAlphaValue; + + vCurvedData.gather(curveDataPointer,vAlphaValue); + Vc::float_v vCurvedData1(curveDataPointer,vAlphaValue + 1); + + // vAlpha + Vc::float_v fullFade = ( + (1.0f - alphaValueF) * vCurvedData + + alphaValueF * vCurvedData1); + + Vc::float_m mask; + // Mask in the inner circe of the mask + mask = fullFade < vZero; + fullFade.setZero(mask); + + // Mask (value - value), presicion errors. + mask = fullFade >= 1.0f; + Vc::float_v vFade = (1.0f - fullFade); + vFade(mask) = vZero; + + + // filter nan and inf. Vc uses float, imprecission errors are frequent + mask = Vc::isfinite(vFade); + vFade(!mask) = vOne; + + // Mask the inner circe of the mask + mask = vFade < vZero; + vFade(mask) = vZero; + + // return original dist values before vFade transform + vFade(excludeMask) = dist; + vFade.store(bufferPointer, Vc::Aligned); + + } else { + dist.store(bufferPointer, Vc::Aligned); + } + currentIndices = currentIndices + increment; + + bufferPointer += Vc::float_v::size(); + } +} + #endif /* defined HAVE_VC */ diff --git a/libs/image/kis_curve_circle_mask_generator.cpp b/libs/image/kis_curve_circle_mask_generator.cpp index 4df3be0f07..77a39565a7 100644 --- a/libs/image/kis_curve_circle_mask_generator.cpp +++ b/libs/image/kis_curve_circle_mask_generator.cpp @@ -1,180 +1,192 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include //MSVC requires that Vc come first - #include +#include +#ifdef HAVE_VC +#if defined(__clang__) +#pragma GCC diagnostic ignored "-Wundef" +#pragma GCC diagnostic ignored "-Wlocal-type-template-args" +#endif +#if defined _MSC_VER +// Lets shut up the "possible loss of data" and "forcing value to bool 'true' or 'false' +#pragma warning ( push ) +#pragma warning ( disable : 4244 ) +#pragma warning ( disable : 4800 ) +#endif +#include +#include +#if defined _MSC_VER +#pragma warning ( pop ) +#endif +#endif + #include #include #include #include #include "kis_fast_math.h" #include "kis_base_mask_generator.h" -#include "kis_curve_circle_mask_generator.h" -#include "kis_cubic_curve.h" #include "kis_antialiasing_fade_maker.h" +#include "kis_brush_mask_applicator_factories.h" +#include "kis_curve_circle_mask_generator.h" +#include "kis_curve_circle_mask_generator_p.h" +#include "kis_cubic_curve.h" -struct Q_DECL_HIDDEN KisCurveCircleMaskGenerator::Private -{ - Private(bool enableAntialiasing) - : fadeMaker(*this, enableAntialiasing) - { - } - - Private(const Private &rhs) - : xcoef(rhs.xcoef), - ycoef(rhs.ycoef), - curveResolution(rhs.curveResolution), - curveData(rhs.curveData), - curvePoints(rhs.curvePoints), - dirty(true), - fadeMaker(rhs.fadeMaker,*this) - { - } - - qreal xcoef, ycoef; - qreal curveResolution; - QVector curveData; - QList curvePoints; - bool dirty; - - KisAntialiasingFadeMaker1D fadeMaker; - inline quint8 value(qreal dist) const; -}; KisCurveCircleMaskGenerator::KisCurveCircleMaskGenerator(qreal diameter, qreal ratio, qreal fh, qreal fv, int spikes, const KisCubicCurve &curve, bool antialiasEdges) : KisMaskGenerator(diameter, ratio, fh, fv, spikes, antialiasEdges, CIRCLE, SoftId), d(new Private(antialiasEdges)) { // here we set resolution for the maximum size of the brush! d->curveResolution = qRound(qMax(width(), height()) * OVERSAMPLING); d->curveData = curve.floatTransfer(d->curveResolution + 2); d->curvePoints = curve.points(); setCurveString(curve.toString()); d->dirty = false; setScale(1.0, 1.0); + + d->applicator.reset(createOptimizedClass >(this)); } KisCurveCircleMaskGenerator::KisCurveCircleMaskGenerator(const KisCurveCircleMaskGenerator &rhs) : KisMaskGenerator(rhs), d(new Private(*rhs.d)) { + d->applicator.reset(createOptimizedClass >(this)); } KisCurveCircleMaskGenerator::~KisCurveCircleMaskGenerator() { } KisMaskGenerator* KisCurveCircleMaskGenerator::clone() const { return new KisCurveCircleMaskGenerator(*this); } void KisCurveCircleMaskGenerator::setScale(qreal scaleX, qreal scaleY) { KisMaskGenerator::setScale(scaleX, scaleY); qreal width = effectiveSrcWidth(); qreal height = effectiveSrcHeight(); d->xcoef = 2.0 / width; d->ycoef = 2.0 / height; d->fadeMaker.setSquareNormCoeffs(d->xcoef, d->ycoef); } bool KisCurveCircleMaskGenerator::shouldSupersample() const { return effectiveSrcWidth() < 10 || effectiveSrcHeight() < 10; } +bool KisCurveCircleMaskGenerator::shouldVectorize() const +{ + return !shouldSupersample() && spikes() == 2; +} + +KisBrushMaskApplicatorBase* KisCurveCircleMaskGenerator::applicator() +{ + return d->applicator.data(); +} + inline quint8 KisCurveCircleMaskGenerator::Private::value(qreal dist) const { qreal distance = dist * curveResolution; quint16 alphaValue = distance; qreal alphaValueF = distance - alphaValue; qreal alpha = ( (1.0 - alphaValueF) * curveData.at(alphaValue) + alphaValueF * curveData.at(alphaValue+1)); + return (1.0 - alpha) * 255; } quint8 KisCurveCircleMaskGenerator::valueAt(qreal x, qreal y) const { if (isEmpty()) return 255; qreal xr = x; qreal yr = qAbs(y); fixRotation(xr, yr); qreal dist = norme(xr * d->xcoef, yr * d->ycoef); quint8 value; if (d->fadeMaker.needFade(dist, &value)) { return value; } return d->value(dist); } void KisCurveCircleMaskGenerator::toXML(QDomDocument& doc, QDomElement& e) const { KisMaskGenerator::toXML(doc, e); e.setAttribute("softness_curve", curveString()); } void KisCurveCircleMaskGenerator::setSoftness(qreal softness) { // performance if (!d->dirty && softness == 1.0) return; d->dirty = true; KisMaskGenerator::setSoftness(softness); KisCurveCircleMaskGenerator::transformCurveForSoftness(softness,d->curvePoints, d->curveResolution+2, d->curveData); d->dirty = false; } void KisCurveCircleMaskGenerator::transformCurveForSoftness(qreal softness,const QList &points, int curveResolution, QVector< qreal >& result) { QList newList = points; newList.detach(); int size = newList.size(); if (size == 2){ // make place for new point in the centre newList.append(newList.at(1)); newList[1] = (newList.at(0) + newList.at(2)) * 0.5; // transoform it newList[1].setY(qBound(0.0,newList.at(1).y() * softness,1.0)); }else{ // transform all points except first and last for (int i = 1; i < size-1; i++){ newList[i].setY(qBound(0.0,newList.at(i).y() * softness,1.0)); } } // compute the data KisCubicCurve curve(newList); result = curve.floatTransfer( curveResolution ); } + +void KisCurveCircleMaskGenerator::resetMaskApplicator(bool forceScalar) +{ + d->applicator.reset(createOptimizedClass >(this,forceScalar)); +} diff --git a/libs/image/kis_curve_circle_mask_generator.h b/libs/image/kis_curve_circle_mask_generator.h index a5db1ae129..1b75196209 100644 --- a/libs/image/kis_curve_circle_mask_generator.h +++ b/libs/image/kis_curve_circle_mask_generator.h @@ -1,71 +1,78 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_CURVE_CIRCLE_MASK_GENERATOR_H_ #define _KIS_CURVE_CIRCLE_MASK_GENERATOR_H_ -#include -#include "kritaimage_export.h" - #include #include +#include + +#include "kritaimage_export.h" +#include "kis_mask_generator.h" class KisCubicCurve; class QDomElement; class QDomDocument; class QPointF; /** * This mask generator use softness/hardness defined by user curve * It used to be soft brush paintop. */ class KRITAIMAGE_EXPORT KisCurveCircleMaskGenerator : public KisMaskGenerator { - +public: + struct FastRowProcessor; public: KisCurveCircleMaskGenerator(qreal radius, qreal ratio, qreal fh, qreal fv, int spikes,const KisCubicCurve& curve, bool antialiasEdges); KisCurveCircleMaskGenerator(const KisCurveCircleMaskGenerator &rhs); ~KisCurveCircleMaskGenerator() override; KisMaskGenerator* clone() const override; quint8 valueAt(qreal x, qreal y) const override; void setScale(qreal scaleX, qreal scaleY) override; bool shouldSupersample() const override; void toXML(QDomDocument& , QDomElement&) const override; void setSoftness(qreal softness) override; + bool shouldVectorize() const override; + KisBrushMaskApplicatorBase* applicator() override; + + void resetMaskApplicator(bool forceScalar); + static void transformCurveForSoftness(qreal softness,const QList &points, int curveResolution, QVector &result); private: qreal norme(qreal a, qreal b) const { return a*a + b*b; } private: struct Private; const QScopedPointer d; }; #endif diff --git a/libs/image/kis_curve_circle_mask_generator_p.h b/libs/image/kis_curve_circle_mask_generator_p.h new file mode 100644 index 0000000000..2497963b62 --- /dev/null +++ b/libs/image/kis_curve_circle_mask_generator_p.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2010 Lukáš Tvrdý + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KIS_CURVE_CIRCLE_MASK_GENERATOR_P_H +#define KIS_CURVE_CIRCLE_MASK_GENERATOR_P_H + +#include "kis_antialiasing_fade_maker.h" +#include "kis_brush_mask_applicator_base.h" + +struct Q_DECL_HIDDEN KisCurveCircleMaskGenerator::Private +{ + Private(bool enableAntialiasing) + : fadeMaker(*this, enableAntialiasing) + { + } + + Private(const Private &rhs) + : xcoef(rhs.xcoef), + ycoef(rhs.ycoef), + curveResolution(rhs.curveResolution), + curveData(rhs.curveData), + curvePoints(rhs.curvePoints), + dirty(true), + fadeMaker(rhs.fadeMaker,*this) + { + } + + qreal xcoef, ycoef; + qreal curveResolution; + QVector curveData; + QList curvePoints; + bool dirty; + + KisAntialiasingFadeMaker1D fadeMaker; + QScopedPointer applicator; + + inline quint8 value(qreal dist) const; +}; + +#endif // KIS_CURVE_CIRCLE_MASK_GENERATOR_P_H diff --git a/libs/image/kis_gauss_circle_mask_generator_p.h b/libs/image/kis_gauss_circle_mask_generator_p.h index 663bc2827f..6a5cf6cb17 100644 --- a/libs/image/kis_gauss_circle_mask_generator_p.h +++ b/libs/image/kis_gauss_circle_mask_generator_p.h @@ -1,75 +1,76 @@ /* * Copyright (c) 2008-2009 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_GAUSS_MASK_GENERATOR_P_H_ #define _KIS_GAUSS_MASK_GENERATOR_P_H_ #include "kis_antialiasing_fade_maker.h" +#include "kis_brush_mask_applicator_base.h" struct Q_DECL_HIDDEN KisGaussCircleMaskGenerator::Private { Private(bool enableAntialiasing) : fadeMaker(*this, enableAntialiasing) { } Private(const Private &rhs) : ycoef(rhs.ycoef), fade(rhs.fade), center(rhs.center), distfactor(rhs.distfactor), alphafactor(rhs.alphafactor), fadeMaker(rhs.fadeMaker, *this) { } qreal ycoef; qreal fade; qreal center; qreal distfactor; qreal alphafactor; KisAntialiasingFadeMaker1D fadeMaker; QScopedPointer applicator; inline quint8 value(qreal dist) const; #if defined HAVE_VC // vectorized erf function, precision 1e-5 Vc::float_v vErf(Vc::float_v x) { Vc::float_v xa = abs(x); Vc::float_v sign(Vc::One); Vc::float_m invertMask = x < 0.f; sign(invertMask) = -1.f; // CONSTANTS float a1 = 0.254829592; float a2 = -0.284496736; float a3 = 1.421413741; float a4 = -1.453152027; float a5 = 1.061405429; float p = 0.3275911; Vc::float_v t = 1.0f / (1.0f + p * xa); Vc::float_v y = 1.0f - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * exp(-xa * xa); return sign * y; } #endif /* defined HAVE_VC */ }; #endif /* _KIS_GAUSS_MASK_GENERATOR_P_H_ */ diff --git a/libs/image/tests/KisMaskGeneratorBenchmark.cpp b/libs/image/tests/KisMaskGeneratorBenchmark.cpp index 6875ef9b99..c827efa6a0 100644 --- a/libs/image/tests/KisMaskGeneratorBenchmark.cpp +++ b/libs/image/tests/KisMaskGeneratorBenchmark.cpp @@ -1,110 +1,138 @@ /* * Copyright (c) 2018 Iván Santa María * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include +#include #include #include #include "KisMaskGeneratorBenchmark.h" #include "kis_mask_similarity_test.h" #include "kis_brush_mask_applicator_base.h" #include "kis_mask_generator.h" +#include "kis_cubic_curve.h" #include "krita_utils.h" #include "testutil.h" class KisMaskGeneratorBenchmarkTester { public: KisMaskGeneratorBenchmarkTester(KisBrushMaskApplicatorBase *_applicatorBase, QRect _bounds) : applicatorBase(_applicatorBase) , m_bounds(_bounds) { KisFixedPaintDeviceSP m_paintDev = new KisFixedPaintDevice(m_colorSpace); m_paintDev->setRect(m_bounds); m_paintDev->initialize(255); MaskProcessingData data(m_paintDev, m_colorSpace, 0.0, 1.0, m_bounds.width() / 2.0, m_bounds.height() / 2.0,0); // Start Benchmark applicatorBase->initializeData(&data); QElapsedTimer maskGenerationTime; maskGenerationTime.start(); QBENCHMARK { applicatorBase->process(m_bounds); } } protected: const KoColorSpace *m_colorSpace = KoColorSpaceRegistry::instance()->rgb8(); QRect m_bounds; KisBrushMaskApplicatorBase *applicatorBase; KisFixedPaintDeviceSP m_paintDev; }; void KisMaskGeneratorBenchmark::testDefaultScalarMask() { QRect bounds(0,0,1000,1000); { KisCircleMaskGenerator circScalar(1000, 1.0, 0.5, 0.5, 2, true); circScalar.resetMaskApplicator(true); // Force usage of scalar backend KisMaskGeneratorBenchmarkTester(circScalar.applicator(), bounds); } } void KisMaskGeneratorBenchmark::testDefaultVectorMask() { QRect bounds(0,0,1000,1000); { KisCircleMaskGenerator circVectr(1000, 1.0, 0.5, 0.5, 2, true); KisMaskGeneratorBenchmarkTester(circVectr.applicator(), bounds); } } void KisMaskGeneratorBenchmark::testCircularGaussScalarMask() { QRect bounds(0,0,1000,1000); { KisGaussCircleMaskGenerator circScalar(1000, 1.0, 0.5, 0.5, 2, true); circScalar.setDiameter(1000); circScalar.resetMaskApplicator(true); // Force usage of scalar backend KisMaskGeneratorBenchmarkTester(circScalar.applicator(), bounds); } } void KisMaskGeneratorBenchmark::testCircularGaussVectorMask() { QRect bounds(0,0,1000,1000); { KisGaussCircleMaskGenerator circVectr(1000, 1.0, 0.5, 0.5, 2, true); circVectr.setDiameter(1000); KisMaskGeneratorBenchmarkTester(circVectr.applicator(), bounds); } } +void KisMaskGeneratorBenchmark::testCircularSoftScalarMask() +{ + QRect bounds(0,0,1000,1000); + KisCubicCurve pointsCurve; + pointsCurve.fromString(QString("0,0;1,1")); + { + KisCurveCircleMaskGenerator circScalar(1000, 1.0, 0.5, 0.5, 2, pointsCurve, true); + circScalar.setSoftness(0.5); + circScalar.resetMaskApplicator(true); // Force usage of scalar backend + + KisMaskGeneratorBenchmarkTester(circScalar.applicator(), bounds); + } +} + +void KisMaskGeneratorBenchmark::testCircularSoftVectorMask() +{ + QRect bounds(0,0,1000,1000); + KisCubicCurve pointsCurve; + pointsCurve.fromString(QString("1,0;1,0")); + { + KisCurveCircleMaskGenerator circVectr(1000, 1.0, 0.5, 0.5, 2, pointsCurve, true); + circVectr.setSoftness(0.5); + KisMaskGeneratorBenchmarkTester(circVectr.applicator(), bounds); + } +} + QTEST_MAIN(KisMaskGeneratorBenchmark) diff --git a/libs/image/tests/KisMaskGeneratorBenchmark.h b/libs/image/tests/KisMaskGeneratorBenchmark.h index da1e2efb51..86318b1605 100644 --- a/libs/image/tests/KisMaskGeneratorBenchmark.h +++ b/libs/image/tests/KisMaskGeneratorBenchmark.h @@ -1,36 +1,39 @@ /* * Copyright (c) 2018 Iván Santa María * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KISMASKGENERATORBENCHMARK_H #define KISMASKGENERATORBENCHMARK_H #include class KisMaskGeneratorBenchmark : public QObject { Q_OBJECT private Q_SLOTS: void testDefaultScalarMask(); void testDefaultVectorMask(); void testCircularGaussScalarMask(); void testCircularGaussVectorMask(); + + void testCircularSoftScalarMask(); + void testCircularSoftVectorMask(); }; #endif // KISMASKGENERATORBENCHMARK_H diff --git a/libs/image/tests/kis_mask_similarity_test.cpp b/libs/image/tests/kis_mask_similarity_test.cpp index 1e6885bbfb..9a11b6e866 100644 --- a/libs/image/tests/kis_mask_similarity_test.cpp +++ b/libs/image/tests/kis_mask_similarity_test.cpp @@ -1,120 +1,138 @@ /* * Copyright (c) 2018 Iván Santa María * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_mask_similarity_test.h" #include +#include + #include #include #include "kis_brush_mask_applicator_base.h" #include "kis_mask_generator.h" +#include "kis_cubic_curve.h" #include "krita_utils.h" class KisMaskSimilarityTester { public: KisMaskSimilarityTester(KisBrushMaskApplicatorBase *_legacy, KisBrushMaskApplicatorBase *_vectorized, QRect _bounds) : legacy(_legacy) , vectorized(_vectorized) , m_bounds(_bounds) { KisFixedPaintDeviceSP m_paintDev = new KisFixedPaintDevice(m_colorSpace); m_paintDev->setRect(m_bounds); m_paintDev->initialize(255); MaskProcessingData data(m_paintDev, m_colorSpace, 0.0, 1.0, m_bounds.width() / 2.0, m_bounds.height() / 2.0,0); // Start legacy scalar processing legacy->initializeData(&data); legacy->process(m_bounds); QImage scalarImage(m_paintDev->convertToQImage(m_colorSpace->profile())); scalarImage.invertPixels(); // Make pixel color black scalarImage.save(QString("scalar_mask.png"),"PNG"); // Start vector processing m_paintDev->initialize(255); vectorized->initializeData(&data); vectorized->process(m_bounds); QImage vectorImage(m_paintDev->convertToQImage(m_colorSpace->profile())); vectorImage.invertPixels(); // Make pixel color black vectorImage.save(QString("vector_mask.png"),"PNG"); // Development debug. // Count number of identical pixels // int equals = 0; // for (int i = 0; i < scalarImage.width(); ++i) { // for (int j = 0; j < scalarImage.height(); ++j) { // if (scalarImage.pixelColor(i,j) == vectorImage.pixelColor(i,j)){ // equals++; // } else { // qDebug() << scalarImage.pixelColor(i,j) << " " << vectorImage.pixelColor(i,j); // } // } // } // qDebug() << "Equal Pixels: " << equals; // Check for differences, max error .5% of pixel mismatch int tolerance = m_bounds.width() * m_bounds.height() * .005f; QPoint tmpPt; QVERIFY(TestUtil::compareQImages(tmpPt,scalarImage, vectorImage, 0, 1, tolerance)); } private: protected: const KoColorSpace *m_colorSpace = KoColorSpaceRegistry::instance()->rgb8(); KisBrushMaskApplicatorBase *legacy; KisBrushMaskApplicatorBase *vectorized; QRect m_bounds; KisFixedPaintDeviceSP m_paintDev; }; void KisMaskSimilarityTest::testCircleMask() { QRect bounds(0,0,500,500); { - KisCircleMaskGenerator circVectr(480, 1.0, 0.5, 0.5, 2, true); + KisCircleMaskGenerator circVectr(496, 1.0, 0.5, 0.5, 2, true); KisCircleMaskGenerator circScalar(circVectr); circScalar.resetMaskApplicator(true); // Force usage of scalar backend KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), bounds); } } void KisMaskSimilarityTest::testGaussCircleMask() { QRect bounds(0,0,500,500); { - KisGaussCircleMaskGenerator circVectr(480, 1.0, 0.5, 0.5, 2, true); + KisGaussCircleMaskGenerator circVectr(496, 1.0, 0.5, 0.5, 2, true); circVectr.setDiameter(480); KisGaussCircleMaskGenerator circScalar(circVectr); circScalar.resetMaskApplicator(true); // Force usage of scalar backend KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), bounds); } } +void KisMaskSimilarityTest::testSoftCircleMask() +{ + QRect bounds(0,0,500,500); + KisCubicCurve pointsCurve; + pointsCurve.fromString(QString("0,1;1,0")); + { + KisCurveCircleMaskGenerator circVectr(496, 1.0, 0.5, 0.5, 2, pointsCurve,true); + circVectr.setSoftness(1.0); + KisCurveCircleMaskGenerator circScalar(circVectr); + + circScalar.resetMaskApplicator(true); // Force usage of scalar backend + KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), bounds); + } +} + QTEST_MAIN(KisMaskSimilarityTest) diff --git a/libs/image/tests/kis_mask_similarity_test.h b/libs/image/tests/kis_mask_similarity_test.h index 2f3c9f873e..231f2de921 100644 --- a/libs/image/tests/kis_mask_similarity_test.h +++ b/libs/image/tests/kis_mask_similarity_test.h @@ -1,33 +1,34 @@ /* * Copyright (c) 2018 Iván Santa María * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_MASK_SIMILARITY_TEST #define KIS_MASK_SIMILARITY_TEST #include class KisMaskSimilarityTest : public QObject { Q_OBJECT private Q_SLOTS: void testCircleMask(); void testGaussCircleMask(); + void testSoftCircleMask(); }; #endif