diff --git a/libs/image/kis_antialiasing_fade_maker.h b/libs/image/kis_antialiasing_fade_maker.h --- a/libs/image/kis_antialiasing_fade_maker.h +++ b/libs/image/kis_antialiasing_fade_maker.h @@ -83,26 +83,31 @@ return false; } - qreal getRadius(){ - return m_radius; - } +#if defined HAVE_VC + Vc::float_m needFade(Vc::float_v &dist) { + const Vc::float_v vOne(Vc::One); + const Vc::float_v vValMax(255.f); - qreal getAntialiasingFadeStart(){ - return m_antialiasingFadeStart; - } + Vc::float_v vRadius(m_radius); + Vc::float_v vFadeStartValue(m_fadeStartValue); + Vc::float_v vAntialiasingFadeStart(m_antialiasingFadeStart); + Vc::float_v vAntialiasingFadeCoeff(m_antialiasingFadeCoeff); - qreal getFadeStartValue() { - return m_fadeStartValue; - } + Vc::float_m outsideMask = dist > vRadius; + dist(outsideMask) = vOne; - qreal getAntialiasingFadeCoeff(){ - return m_antialiasingFadeCoeff; - } + Vc::float_m fadeStartMask(false); - bool getAliasingEnabled(){ - return m_enableAntialiasing; + if(m_enableAntialiasing){ + fadeStartMask = dist > vAntialiasingFadeStart; + dist((outsideMask ^ fadeStartMask) & fadeStartMask) = (vFadeStartValue + + (dist - vAntialiasingFadeStart) * vAntialiasingFadeCoeff) / vValMax; + } + return (outsideMask | fadeStartMask); } +#endif /* defined HAVE_VC */ + private: qreal m_radius; quint8 m_fadeStartValue; @@ -194,6 +199,59 @@ return false; } +#if defined HAVE_VC + Vc::float_m needFade(Vc::float_v &xr, Vc::float_v &yr) const { + + Vc::float_v vXLimit(m_xLimit); + Vc::float_v vYLimit(m_yLimit); + + + Vc::float_m outXMask = xr > vXLimit; + Vc::float_m outYMask = yr > vYLimit; + + return (outXMask | outYMask); + } + + // Apply fader separatedly to avoid calculating vValue twice. + void apply2DFader(Vc::float_v &vValue, Vc::float_m &excludeMask, Vc::float_v &xr, Vc::float_v &yr) const { + const Vc::float_v vValMax(255.f); + + if(m_enableAntialiasing){ + Vc::float_v vXFadeLimitStart(m_xFadeLimitStart); + Vc::float_v vYFadeLimitStart(m_yFadeLimitStart); + Vc::float_v vXFadeCoeff(m_xFadeCoeff); + Vc::float_v vYFadeCoeff(m_yFadeCoeff); + + Vc::float_v xra = abs(xr); + Vc::float_m fadeXStartMask(false); + Vc::float_m fadeYStartMask(false); + + Vc::float_v fadeValue; + Vc::SimdArray vBaseValue(vValue); + + fadeXStartMask = xra > vXFadeLimitStart; + fadeXStartMask = (fadeXStartMask ^ excludeMask) & fadeXStartMask; + if (!fadeXStartMask.isFull()) { + fadeValue = vBaseValue + (vValMax - vBaseValue) * (xra - vXFadeLimitStart) * vXFadeCoeff; + fadeValue(fadeXStartMask & ((yr > vYFadeLimitStart) & (fadeValue < vValMax)) ) = + fadeValue + (vValMax - fadeValue) * (yr - vYFadeLimitStart) * vYFadeCoeff; + vValue(fadeXStartMask) = fadeValue; + } + + fadeYStartMask = yr > vYFadeLimitStart; + fadeYStartMask = (fadeYStartMask ^ fadeXStartMask) & fadeYStartMask; + if (!fadeYStartMask.isFull()) { + fadeValue = vBaseValue + (vValMax - vBaseValue) * (yr - vYFadeLimitStart) * vYFadeCoeff; + fadeValue(fadeYStartMask & ((xra > vXFadeLimitStart) & (fadeValue < vValMax)) ) = + fadeValue + (vValMax - fadeValue) * (xra - vXFadeLimitStart) * vXFadeCoeff; + vValue(fadeYStartMask) = fadeValue; + } + } + return; + } + +#endif /* defined HAVE_VC */ + private: qreal m_xLimit; qreal m_yLimit; diff --git a/libs/image/kis_brush_mask_applicator_factories.cpp b/libs/image/kis_brush_mask_applicator_factories.cpp --- a/libs/image/kis_brush_mask_applicator_factories.cpp +++ b/libs/image/kis_brush_mask_applicator_factories.cpp @@ -17,11 +17,13 @@ */ #include "kis_brush_mask_applicator_factories.h" +#include "vc_extra_math.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_gauss_rect_mask_generator_p.h" #include "kis_brush_mask_applicators.h" #include "kis_brush_mask_applicator_base.h" @@ -53,7 +55,6 @@ return new KisBrushMaskVectorApplicator(maskGenerator); } - template<> template<> MaskApplicatorFactory::ReturnType @@ -62,6 +63,14 @@ return new KisBrushMaskVectorApplicator(maskGenerator); } +template<> +template<> +MaskApplicatorFactory::ReturnType +MaskApplicatorFactory::create(ParamType maskGenerator) +{ + return new KisBrushMaskVectorApplicator(maskGenerator); +} + #if defined HAVE_VC @@ -172,8 +181,6 @@ FastRowProcessor::process(float* buffer, int width, float y, float cosa, float sina, float centerX, float centerY) { - const bool antialiasOn = d->fadeMaker.getAliasingEnabled(); - float y_ = y - centerY; float sinay_ = sina * y_; float cosay_ = cosa * y_; @@ -195,12 +202,6 @@ 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); @@ -213,28 +214,14 @@ 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(false); - // if antialias is off, do not process - if(antialiasOn){ - fadeStartMask = dist > vFadeAFadeStart; - dist((outsideMask ^ fadeStartMask) & fadeStartMask) = (vFadeStartValue + (dist - vFadeAFadeStart) * vFadeAFadeCoeff) / vValMax; - } - Vc::float_m excludeMask(outsideMask | fadeStartMask); + // Apply FadeMaker mask and operations + Vc::float_m excludeMask = d->fadeMaker.needFade(dist); if (!excludeMask.isFull()) { Vc::float_v valDist = dist * vDistfactor; - Vc::float_v fullFade = vAlphafactor * ( d->vErf(valDist + vCenter) - d->vErf(valDist - vCenter)); + Vc::float_v fullFade = vAlphafactor * ( VcExtraMath::erf(valDist + vCenter) - VcExtraMath::erf(valDist - vCenter)); Vc::float_m mask; - // Mask undefined values, out of range are out of mask - mask = Vc::isfinite(fullFade); - fullFade.setZero(!mask); - // Mask in the inner circe of the mask mask = fullFade < vZero; fullFade.setZero(mask); @@ -276,8 +263,6 @@ FastRowProcessor::process(float* buffer, int width, float y, float cosa, float sina, float centerX, float centerY) { - const bool antialiasOn = d->fadeMaker.getAliasingEnabled(); - float y_ = y - centerY; float sinay_ = sina * y_; float cosay_ = cosa * y_; @@ -303,14 +288,8 @@ Vc::float_v vCurvedData(Vc::Zero); Vc::float_v vCurvedData1(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()){ @@ -321,19 +300,8 @@ 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(false); - // if antialias is off, do not process - if(antialiasOn){ - fadeStartMask = dist > vFadeAFadeStart; - dist((outsideMask ^ fadeStartMask) & fadeStartMask) = (vFadeStartValue + (dist - vFadeAFadeStart) * vFadeAFadeCoeff) / vValMax; - } - - Vc::float_m excludeMask = outsideMask | fadeStartMask; + // Apply FadeMaker mask and operations + Vc::float_m excludeMask = d->fadeMaker.needFade(dist); if (!excludeMask.isFull()) { Vc::float_v valDist = dist * vCurveResolution; @@ -375,4 +343,93 @@ } } +struct KisGaussRectangleMaskGenerator::FastRowProcessor +{ + FastRowProcessor(KisGaussRectangleMaskGenerator *maskGenerator) + : d(maskGenerator->d.data()) {} + + template + void process(float* buffer, int width, float y, float cosa, float sina, + float centerX, float centerY); + + KisGaussRectangleMaskGenerator::Private *d; +}; + +template<> void KisGaussRectangleMaskGenerator:: +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 vCosa(cosa); + Vc::float_v vSina(sina); + Vc::float_v vCosaY_(cosay_); + Vc::float_v vSinaY_(sinay_); + + Vc::float_v vhalfWidth(d->halfWidth); + Vc::float_v vhalfHeight(d->halfHeight); + Vc::float_v vXFade(d->xfade); + Vc::float_v vYFade(d->yfade); + + Vc::float_v vAlphafactor(d->alphafactor); + + 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 = abs(x_ * vSina + vCosaY_); + + Vc::float_v vValue; + + // check if we need to apply fader on values + Vc::float_m excludeMask = d->fadeMaker.needFade(xr,yr); + vValue(excludeMask) = vOne; + + if (!excludeMask.isFull()) { + Vc::float_v fullFade = vValMax - (vAlphafactor * (VcExtraMath::erf((vhalfWidth + xr) * vXFade) + VcExtraMath::erf((vhalfWidth - xr) * vXFade)) + * (VcExtraMath::erf((vhalfHeight + yr) * vYFade) + VcExtraMath::erf((vhalfHeight - yr) * vYFade))); + + // apply antialias fader + d->fadeMaker.apply2DFader(fullFade,excludeMask,xr,yr); + + Vc::float_m mask; + + // Mask in the inner circe of the mask + mask = fullFade < vZero; + fullFade.setZero(mask); + + // Mask the outter circle + mask = fullFade > 254.974f; + fullFade(mask) = vValMax; + + // Mask (value - value), presicion errors. + Vc::float_v vFade = fullFade / vValMax; + + // return original vValue values before vFade transform + vFade(excludeMask) = vValue; + vFade.store(bufferPointer, Vc::Aligned); + + } else { + vValue.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.h b/libs/image/kis_curve_circle_mask_generator.h --- a/libs/image/kis_curve_circle_mask_generator.h +++ b/libs/image/kis_curve_circle_mask_generator.h @@ -52,8 +52,6 @@ void setScale(qreal scaleX, qreal scaleY) override; - bool shouldSupersample() const override; - void toXML(QDomDocument& , QDomElement&) const override; void setSoftness(qreal softness) override; diff --git a/libs/image/kis_curve_circle_mask_generator.cpp b/libs/image/kis_curve_circle_mask_generator.cpp --- a/libs/image/kis_curve_circle_mask_generator.cpp +++ b/libs/image/kis_curve_circle_mask_generator.cpp @@ -99,11 +99,6 @@ d->fadeMaker.setSquareNormCoeffs(d->xcoef, d->ycoef); } -bool KisCurveCircleMaskGenerator::shouldSupersample() const -{ - return effectiveSrcWidth() < 10 || effectiveSrcHeight() < 10; -} - bool KisCurveCircleMaskGenerator::shouldVectorize() const { return !shouldSupersample() && spikes() == 2; diff --git a/libs/image/kis_gauss_circle_mask_generator.h b/libs/image/kis_gauss_circle_mask_generator.h --- a/libs/image/kis_gauss_circle_mask_generator.h +++ b/libs/image/kis_gauss_circle_mask_generator.h @@ -43,8 +43,6 @@ void setScale(qreal scaleX, qreal scaleY) override; - bool shouldSupersample() const override; - bool shouldVectorize() const override; KisBrushMaskApplicatorBase* applicator() override; diff --git a/libs/image/kis_gauss_circle_mask_generator.cpp b/libs/image/kis_gauss_circle_mask_generator.cpp --- a/libs/image/kis_gauss_circle_mask_generator.cpp +++ b/libs/image/kis_gauss_circle_mask_generator.cpp @@ -112,11 +112,6 @@ return (quint8) 255 - ret; } -bool KisGaussCircleMaskGenerator::shouldSupersample() const -{ - return effectiveSrcWidth() < 10 || effectiveSrcHeight() < 10; -} - bool KisGaussCircleMaskGenerator::shouldVectorize() const { return !shouldSupersample() && spikes() == 2; diff --git a/libs/image/kis_gauss_circle_mask_generator_p.h b/libs/image/kis_gauss_circle_mask_generator_p.h --- a/libs/image/kis_gauss_circle_mask_generator_p.h +++ b/libs/image/kis_gauss_circle_mask_generator_p.h @@ -50,30 +50,6 @@ 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_m precisionLimit(xa >= 9.3f); // wrong result for any number beyond this - xa(precisionLimit) = 0; - 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); - y(precisionLimit) = 1.0f; - return sign * y; - } - #endif /* defined HAVE_VC */ }; #endif /* _KIS_GAUSS_MASK_GENERATOR_P_H_ */ diff --git a/libs/image/kis_gauss_rect_mask_generator.h b/libs/image/kis_gauss_rect_mask_generator.h --- a/libs/image/kis_gauss_rect_mask_generator.h +++ b/libs/image/kis_gauss_rect_mask_generator.h @@ -22,14 +22,15 @@ #include "kritaimage_export.h" - +#include "kis_mask_generator.h" /** * This mask generator uses a Gaussian-blurred rectangle */ class KRITAIMAGE_EXPORT KisGaussRectangleMaskGenerator : public KisMaskGenerator { - +public: + struct FastRowProcessor; public: KisGaussRectangleMaskGenerator(qreal diameter, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges); @@ -40,6 +41,10 @@ quint8 valueAt(qreal x, qreal y) const override; void setScale(qreal scaleX, qreal scaleY) override; + bool shouldVectorize() const override; + KisBrushMaskApplicatorBase* applicator() override; + void resetMaskApplicator(bool forceScalar); + private: struct Private; const QScopedPointer d; diff --git a/libs/image/kis_gauss_rect_mask_generator.cpp b/libs/image/kis_gauss_rect_mask_generator.cpp --- a/libs/image/kis_gauss_rect_mask_generator.cpp +++ b/libs/image/kis_gauss_rect_mask_generator.cpp @@ -21,6 +21,25 @@ #include #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 @@ -30,8 +49,11 @@ #include "kis_fast_math.h" #include "kis_base_mask_generator.h" -#include "kis_gauss_rect_mask_generator.h" #include "kis_antialiasing_fade_maker.h" +#include "kis_brush_mask_applicator_factories.h" +#include "kis_brush_mask_applicator_base.h" +#include "kis_gauss_rect_mask_generator.h" +#include "kis_gauss_rect_mask_generator_p.h" #define M_SQRT_2 1.41421356237309504880 @@ -41,42 +63,21 @@ #define erf(x) boost::math::erf(x) #endif -struct Q_DECL_HIDDEN KisGaussRectangleMaskGenerator::Private -{ - Private(bool enableAntialiasing) - : fadeMaker(*this, enableAntialiasing) - { - } - - Private(const Private &rhs) - : xfade(rhs.xfade), - yfade(rhs.yfade), - halfWidth(rhs.halfWidth), - halfHeight(rhs.halfHeight), - alphafactor(rhs.alphafactor), - fadeMaker(rhs.fadeMaker, *this) - { - } - - qreal xfade, yfade; - qreal halfWidth, halfHeight; - qreal alphafactor; - KisAntialiasingFadeMaker2D fadeMaker; - - inline quint8 value(qreal x, qreal y) const; -}; KisGaussRectangleMaskGenerator::KisGaussRectangleMaskGenerator(qreal diameter, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges) : KisMaskGenerator(diameter, ratio, fh, fv, spikes, antialiasEdges, RECTANGLE, GaussId), d(new Private(antialiasEdges)) { setScale(1.0, 1.0); + + d->applicator.reset(createOptimizedClass >(this)); } KisGaussRectangleMaskGenerator::KisGaussRectangleMaskGenerator(const KisGaussRectangleMaskGenerator &rhs) : KisMaskGenerator(rhs), d(new Private(*rhs.d)) { + d->applicator.reset(createOptimizedClass >(this)); } KisMaskGenerator* KisGaussRectangleMaskGenerator::clone() const @@ -99,6 +100,8 @@ d->halfHeight = height * 0.5 - 2.5 * yfade; d->alphafactor = 255.0 / (4.0 * erf(d->halfWidth * d->xfade) * erf(d->halfHeight * d->yfade)); + if (isnan(d->alphafactor)) d->alphafactor = 0.0f; // erf can return nan if ratio is 0 + d->fadeMaker.setLimits(0.5 * width, 0.5 * height); } @@ -126,3 +129,18 @@ return d->value(xr, yr); } + +bool KisGaussRectangleMaskGenerator::shouldVectorize() const +{ + return !shouldSupersample() && spikes() == 2; +} + +KisBrushMaskApplicatorBase* KisGaussRectangleMaskGenerator::applicator() +{ + return d->applicator.data(); +} + +void KisGaussRectangleMaskGenerator::resetMaskApplicator(bool forceScalar) +{ + d->applicator.reset(createOptimizedClass >(this,forceScalar)); +} diff --git a/libs/image/kis_gauss_rect_mask_generator_p.h b/libs/image/kis_gauss_rect_mask_generator_p.h new file mode 100644 --- /dev/null +++ b/libs/image/kis_gauss_rect_mask_generator_p.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2010 Lukáš Tvrdý + * Copyright (c) 2011 Geoffry Song + * + * 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_RECT_MASK_GENERATOR_P_H +#define KIS_GAUSS_RECT_MASK_GENERATOR_P_H + +#include + +#include "kis_antialiasing_fade_maker.h" +#include "kis_brush_mask_applicator_base.h" + +struct Q_DECL_HIDDEN KisGaussRectangleMaskGenerator::Private +{ + Private(bool enableAntialiasing) + : fadeMaker(*this, enableAntialiasing) + { + } + + Private(const Private &rhs) + : xfade(rhs.xfade), + yfade(rhs.yfade), + halfWidth(rhs.halfWidth), + halfHeight(rhs.halfHeight), + alphafactor(rhs.alphafactor), + fadeMaker(rhs.fadeMaker, *this) + { + } + + qreal xfade, yfade; + qreal halfWidth, halfHeight; + qreal alphafactor; + + KisAntialiasingFadeMaker2D fadeMaker; + + QScopedPointer applicator; + + inline quint8 value(qreal x, qreal y) const; + +}; + +#endif // KIS_GAUSS_RECT_MASK_GENERATOR_P_H diff --git a/libs/image/tests/KisMaskGeneratorBenchmark.h b/libs/image/tests/KisMaskGeneratorBenchmark.h --- a/libs/image/tests/KisMaskGeneratorBenchmark.h +++ b/libs/image/tests/KisMaskGeneratorBenchmark.h @@ -34,6 +34,9 @@ void testCircularSoftScalarMask(); void testCircularSoftVectorMask(); + + void testRectangularGaussScalarMask(); + void testRectangularGaussVectorMask(); }; #endif // KISMASKGENERATORBENCHMARK_H diff --git a/libs/image/tests/KisMaskGeneratorBenchmark.cpp b/libs/image/tests/KisMaskGeneratorBenchmark.cpp --- a/libs/image/tests/KisMaskGeneratorBenchmark.cpp +++ b/libs/image/tests/KisMaskGeneratorBenchmark.cpp @@ -135,4 +135,25 @@ } } +void KisMaskGeneratorBenchmark::testRectangularGaussScalarMask() +{ + QRect bounds(0,0,1000,1000); + { + KisGaussRectangleMaskGenerator 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::testRectangularGaussVectorMask() +{ + QRect bounds(0,0,1000,1000); + { + KisGaussRectangleMaskGenerator circVectr(1000, 1.0, 0.5, 0.5, 2, true); +// circVectr.setDiameter(1000); + KisMaskGeneratorBenchmarkTester(circVectr.applicator(), bounds); + } +} + QTEST_MAIN(KisMaskGeneratorBenchmark) diff --git a/libs/image/tests/kis_mask_similarity_test.h b/libs/image/tests/kis_mask_similarity_test.h --- a/libs/image/tests/kis_mask_similarity_test.h +++ b/libs/image/tests/kis_mask_similarity_test.h @@ -29,6 +29,7 @@ void testCircleMask(); void testGaussCircleMask(); void testSoftCircleMask(); + void testGaussRectMask(); }; #endif diff --git a/libs/image/tests/kis_mask_similarity_test.cpp b/libs/image/tests/kis_mask_similarity_test.cpp --- a/libs/image/tests/kis_mask_similarity_test.cpp +++ b/libs/image/tests/kis_mask_similarity_test.cpp @@ -65,14 +65,66 @@ QImage vectorImage(m_paintDev->convertToQImage(m_colorSpace->profile())); vectorImage.invertPixels(); // Make pixel color black - if (renderImage) { - scalarImage.save(QString(getTypeName(type) + "_scalar_mask.png"),"PNG"); - vectorImage.save(QString(getTypeName(type) +"_vector_mask.png"),"PNG"); - } - // Check for differences, max errors: 0 QPoint tmpPt; QVERIFY(TestUtil::compareQImages(tmpPt,scalarImage, vectorImage, 0, 2, 0)); + + if (renderImage || QTest::currentTestFailed()) { + scalarImage.save(QString(getTypeName(type) + "_scalar_mask.png"),"PNG"); + vectorImage.save(QString(getTypeName(type) + "_vector_mask.png"),"PNG"); + } + } + + + static void exahustiveTest(QRect bounds, MaskType type) { + // Exahustive test + + for (size_t i = 0; i <= 100; i += 5){ + for (size_t j = 0; j <= 100; j += 5){ + for (size_t k = 0; k <= 100; k += 20){ + + switch (type) { + case CIRC_GAUSS: + { + KisGaussCircleMaskGenerator bCircVectr(499.5, k/100.f, i/100.f, j/100.f, 2, true); + bCircVectr.setDiameter(499.5); + KisGaussCircleMaskGenerator bCircScalar(bCircVectr); + bCircScalar.resetMaskApplicator(true); // Force usage of scalar backend + + KisMaskSimilarityTester(bCircScalar.applicator(), bCircVectr.applicator(), bounds,type,false); + break; + } + case CIRC_SOFT: + { + KisCubicCurve pointsCurve; + pointsCurve.fromString(QString("0,1;1,0")); + KisCurveCircleMaskGenerator bCircVectr(499.5, k/100.f, i/100.f, j/100.f, 2, pointsCurve, true); + bCircVectr.setDiameter(499.5); + KisCurveCircleMaskGenerator bCircScalar(bCircVectr); + bCircScalar.resetMaskApplicator(true); // Force usage of scalar backend + + KisMaskSimilarityTester(bCircScalar.applicator(), bCircVectr.applicator(), bounds,type,false); + break; + } + case RECT_GAUSS: + { + KisGaussRectangleMaskGenerator bCircVectr(499.5, k/100.f, i/100.f, j/100.f, 2, true); + KisGaussRectangleMaskGenerator bCircScalar(bCircVectr); + bCircScalar.resetMaskApplicator(true); // Force usage of scalar backend + + KisMaskSimilarityTester(bCircScalar.applicator(), bCircVectr.applicator(), bounds,type,false); + break; + + } + default: + { + return; + break; + } + } + + } } } // end for + return; } private: @@ -115,7 +167,7 @@ { QRect bounds(0,0,500,500); { - KisCircleMaskGenerator circVectr(500, 1.0, 0.5, 0.5, 2, true); + KisCircleMaskGenerator circVectr(499.5, 1.0, 0.5, 0.5, 2, true); KisCircleMaskGenerator circScalar(circVectr); circScalar.resetMaskApplicator(true); // Force usage of scalar backend @@ -125,37 +177,26 @@ void KisMaskSimilarityTest::testGaussCircleMask() { - QRect bounds(0,0,500,500); + QRect bounds(0,0,520,520); { - KisGaussCircleMaskGenerator circVectr(500, 1.0, .8, .2, 2, true); - circVectr.setDiameter(500); + KisGaussCircleMaskGenerator circVectr(499.5, 1.0, 1, 1, 2, true); + circVectr.setDiameter(499.5); KisGaussCircleMaskGenerator circScalar(circVectr); circScalar.resetMaskApplicator(true); // Force usage of scalar backend KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), bounds, CIRC_GAUSS); } - // Exahustive test - for (size_t i = 0; i <= 100; i += 3){ - for (size_t j = 0; j <= 100; j += 3){ - for (size_t k = 0; k <= 100; k += 15){ - { - KisGaussCircleMaskGenerator circVectr(500, k/100.f, i/100.f, j/100.f, 2, true); - circVectr.setDiameter(500); - KisGaussCircleMaskGenerator circScalar(circVectr); - - circScalar.resetMaskApplicator(true); // Force usage of scalar backend - KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), bounds,CIRC_GAUSS,false); - } - } } } // end for + + KisMaskSimilarityTester::exahustiveTest(bounds,CIRC_GAUSS); } void KisMaskSimilarityTest::testSoftCircleMask() { - QRect bounds(0,0,500,500); + QRect bounds(0,0,520,520); KisCubicCurve pointsCurve; pointsCurve.fromString(QString("0,1;1,0")); { - KisCurveCircleMaskGenerator circVectr(500, 1.0, 0.5, 0.5, 2, pointsCurve,true); + KisCurveCircleMaskGenerator circVectr(499.5, 1.0, 0.5, 0.5, 2, pointsCurve,true); circVectr.setDiameter(500); // circVectr.setSoftness(1.0); KisCurveCircleMaskGenerator circScalar(circVectr); @@ -164,19 +205,21 @@ KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), bounds, CIRC_SOFT); } - // Exahustive test - for (size_t i = 0; i <= 100; i += 3){ - for (size_t j = 0; j <= 100; j += 3){ - for (size_t k = 0; k <= 100; k += 15){ - { - KisCurveCircleMaskGenerator circVectr(500, k/100.f, i/100.f, j/100.f, 2,pointsCurve, true); - circVectr.setDiameter(500); - KisCurveCircleMaskGenerator circScalar(circVectr); - - circScalar.resetMaskApplicator(true); // Force usage of scalar backend - KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), bounds,CIRC_SOFT,false); - } - } } } // end for + KisMaskSimilarityTester::exahustiveTest(bounds,CIRC_SOFT); +} + +void KisMaskSimilarityTest::testGaussRectMask() +{ + QRect bounds(0,0,540,540); + { + KisGaussRectangleMaskGenerator circVectr(499.5, 1.0, 0.5, 0.2, 2, true); + KisGaussRectangleMaskGenerator circScalar(circVectr); + + circScalar.resetMaskApplicator(true); // Force usage of scalar backend + KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), bounds, RECT_GAUSS); + } + + KisMaskSimilarityTester::exahustiveTest(bounds,RECT_GAUSS); } QTEST_MAIN(KisMaskSimilarityTest) diff --git a/libs/image/vc_extra_math.h b/libs/image/vc_extra_math.h new file mode 100644 --- /dev/null +++ b/libs/image/vc_extra_math.h @@ -0,0 +1,58 @@ +/* + * 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 VC_ADDITIONAL_MATH_H +#define VC_ADDITIONAL_MATH_H + +#include + +#if defined HAVE_VC + +#include +#include + +class VcExtraMath +{ +public: + // vectorized erf function, precision 1e-5 + static inline Vc::float_v erf(Vc::float_v x) { + Vc::float_v xa = abs(x); + Vc::float_m precisionLimit(xa >= 9.3f); // wrong result for any number beyond this + xa(precisionLimit) = 0; + 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); + y(precisionLimit) = 1.0f; + return sign * y; + } +}; +#endif /* defined HAVE_VC */ + + +#endif // VC_ADDITIONAL_MATH_H