Index: libs/image/kis_antialiasing_fade_maker.h =================================================================== --- libs/image/kis_antialiasing_fade_maker.h +++ libs/image/kis_antialiasing_fade_maker.h @@ -83,6 +83,31 @@ return false; } +#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); + + Vc::float_v vRadius(m_radius); + Vc::float_v vFadeStartValue(m_fadeStartValue); + Vc::float_v vAntialiasingFadeStart(m_antialiasingFadeStart); + Vc::float_v vAntialiasingFadeCoeff(m_antialiasingFadeCoeff); + + Vc::float_m outsideMask = dist > vRadius; + dist(outsideMask) = vOne; + + Vc::float_m fadeStartMask(false); + + 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; @@ -174,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; Index: libs/image/kis_brush_mask_applicator_factories.cpp =================================================================== --- libs/image/kis_brush_mask_applicator_factories.cpp +++ libs/image/kis_brush_mask_applicator_factories.cpp @@ -17,9 +17,16 @@ */ #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_curve_rect_mask_generator_p.h" +#include "kis_rect_mask_generator_p.h" + #include "kis_brush_mask_applicators.h" #include "kis_brush_mask_applicator_base.h" @@ -42,6 +49,47 @@ 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); +} + +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 @@ -134,4 +182,473 @@ } } + +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 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)); + + // 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 * ( VcExtraMath::erf(valDist + vCenter) - VcExtraMath::erf(valDist - vCenter)); + + 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 = (vValMax - fullFade) / vValMax; + + // 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 vCurvedData1(Vc::Zero); + + Vc::float_v vOne(Vc::One); + Vc::float_v vZero(Vc::Zero); + + 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); + + // Apply FadeMaker mask and operations + Vc::float_m excludeMask = d->fadeMaker.needFade(dist); + + if (!excludeMask.isFull()) { + Vc::float_v valDist = dist * vCurveResolution; + // truncate + Vc::float_v::IndexType vAlphaValue(valDist); + Vc::float_v vFloatAlphaValue = vAlphaValue; + + Vc::float_v alphaValueF = valDist - vFloatAlphaValue; + + vCurvedData.gather(curveDataPointer,vAlphaValue); + vCurvedData1.gather(curveDataPointer,vAlphaValue + 1); +// Vc::float_v vCurvedData1(curveDataPointer,vAlphaValue + 1); + + // vAlpha + Vc::float_v fullFade = ( + (vOne - alphaValueF) * vCurvedData + + alphaValueF * vCurvedData1); + + Vc::float_m mask; + // Mask in the inner circe of the mask + mask = fullFade < vZero; + fullFade.setZero(mask); + + // Mask outer circle of mask + mask = fullFade >= vOne; + Vc::float_v vFade = (vOne - fullFade); + vFade.setZero(mask); + + // 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 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; +}; + +struct KisRectangleMaskGenerator::FastRowProcessor +{ + FastRowProcessor(KisRectangleMaskGenerator *maskGenerator) + : d(maskGenerator->d.data()) {} + + template + void process(float* buffer, int width, float y, float cosa, float sina, + float centerX, float centerY); + + KisRectangleMaskGenerator::Private *d; +}; + +template<> void KisRectangleMaskGenerator:: +FastRowProcessor::process(float* buffer, int width, float y, float cosa, float sina, + float centerX, float centerY) +{ + const bool useSmoothing = d->copyOfAntialiasEdges; + + 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->xcoeff); + Vc::float_v vYCoeff(d->ycoeff); + + Vc::float_v vTransformedFadeX(d->transformedFadeX); + Vc::float_v vTransformedFadeY(d->transformedFadeY); + + Vc::float_v vOne(Vc::One); + Vc::float_v vZero(Vc::Zero); + Vc::float_v vTolerance(10000.f); + + for (int i=0; i < width; i+= Vc::float_v::size()){ + + Vc::float_v x_ = currentIndices - vCenterX; + + Vc::float_v xr = Vc::abs(x_ * vCosa - vSinaY_); + Vc::float_v yr = Vc::abs(x_ * vSina + vCosaY_); + + Vc::float_v nxr = xr * vXCoeff; + Vc::float_v nyr = yr * vYCoeff; + + Vc::float_m outsideMask = (nxr > vOne) || (nyr > vOne); + + if (!outsideMask.isFull()) { + if (useSmoothing) { + xr = Vc::abs(xr) + vOne; + yr = Vc::abs(yr) + vOne; + } + + Vc::float_v fxr = xr * vTransformedFadeX; + Vc::float_v fyr = yr * vTransformedFadeY; + + Vc::float_v fxrNorm = nxr * (fxr - vOne) / (fxr - nxr); + Vc::float_v fyrNorm = nyr * (fyr - vOne) / (fyr - nyr); + + Vc::float_v vFade(vZero); + + Vc::float_v::IndexType fxrInt(fxr * vTolerance); + Vc::float_v::IndexType fyrInt(fyr * vTolerance); + + Vc::float_m fadeXMask = (fxr > vOne) && ((fxrInt >= fyrInt) || fyr < vOne); + Vc::float_m fadeYMask = (fyr > vOne) && ((fyrInt > fxrInt) || fxr < vOne); + + vFade(fadeXMask) = fxrNorm; + vFade(!fadeXMask && fadeYMask) = fyrNorm; + + // 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(); + } +} + + +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(); + } +} + +struct KisCurveRectangleMaskGenerator::FastRowProcessor +{ + FastRowProcessor(KisCurveRectangleMaskGenerator *maskGenerator) + : d(maskGenerator->d.data()) {} + + template + void process(float* buffer, int width, float y, float cosa, float sina, + float centerX, float centerY); + + KisCurveRectangleMaskGenerator::Private *d; +}; + +template<> void KisCurveRectangleMaskGenerator:: +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->ycoeff); + Vc::float_v vXCoeff(d->xcoeff); + Vc::float_v vCurveResolution(d->curveResolution); + + 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()) { + // We need to mask the extra area given for aliniation + // the next operation should never give values above 1 + Vc::float_v preSIndex = abs(xr) * vXCoeff; + Vc::float_v preTIndex = abs(yr) * vYCoeff; + + preSIndex(preSIndex > vOne) = vOne; + preTIndex(preTIndex > vOne) = vOne; + + Vc::float_v::IndexType sIndex( round(preSIndex * vCurveResolution)); + Vc::float_v::IndexType tIndex( round(preTIndex * vCurveResolution)); + + Vc::float_v::IndexType sIndexInverted = vCurveResolution - sIndex; + Vc::float_v::IndexType tIndexInverted = vCurveResolution - tIndex; + + Vc::float_v vCurvedDataSIndex(curveDataPointer, sIndex); + Vc::float_v vCurvedDataTIndex(curveDataPointer, tIndex); + Vc::float_v vCurvedDataSIndexInv(curveDataPointer, sIndexInverted); + Vc::float_v vCurvedDataTIndexInv(curveDataPointer, tIndexInverted); + + Vc::float_v fullFade = vValMax * (vOne - (vCurvedDataSIndex * (vOne - vCurvedDataSIndexInv) * + vCurvedDataTIndex * (vOne - vCurvedDataTIndexInv))); + + // 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 */ Index: libs/image/kis_circle_mask_generator.h =================================================================== --- libs/image/kis_circle_mask_generator.h +++ libs/image/kis_circle_mask_generator.h @@ -49,6 +49,8 @@ void setSoftness(qreal softness) override; void setScale(qreal scaleX, qreal scaleY) override; + void resetMaskApplicator(bool forceScalar); + private: qreal norme(qreal a, qreal b) const { Index: libs/image/kis_circle_mask_generator.cpp =================================================================== --- libs/image/kis_circle_mask_generator.cpp +++ libs/image/kis_circle_mask_generator.cpp @@ -139,3 +139,8 @@ d->transformedFadeX = d->xfadecoef * safeSoftnessCoeff; d->transformedFadeY = d->yfadecoef * safeSoftnessCoeff; } + +void KisCircleMaskGenerator::resetMaskApplicator(bool forceScalar) +{ + d->applicator.reset(createOptimizedClass >(this,forceScalar)); +} Index: libs/image/kis_curve_circle_mask_generator.h =================================================================== --- libs/image/kis_curve_circle_mask_generator.h +++ libs/image/kis_curve_circle_mask_generator.h @@ -19,11 +19,12 @@ #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; @@ -38,7 +39,8 @@ */ 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); @@ -50,11 +52,14 @@ 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: Index: libs/image/kis_curve_circle_mask_generator.cpp =================================================================== --- libs/image/kis_curve_circle_mask_generator.cpp +++ libs/image/kis_curve_circle_mask_generator.cpp @@ -17,9 +17,27 @@ */ #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 @@ -29,38 +47,13 @@ #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)) @@ -73,12 +66,15 @@ 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() @@ -103,9 +99,14 @@ d->fadeMaker.setSquareNormCoeffs(d->xcoef, d->ycoef); } -bool KisCurveCircleMaskGenerator::shouldSupersample() const +bool KisCurveCircleMaskGenerator::shouldVectorize() const +{ + return !shouldSupersample() && spikes() == 2; +} + +KisBrushMaskApplicatorBase* KisCurveCircleMaskGenerator::applicator() { - return effectiveSrcWidth() < 10 || effectiveSrcHeight() < 10; + return d->applicator.data(); } inline quint8 KisCurveCircleMaskGenerator::Private::value(qreal dist) const @@ -118,6 +119,7 @@ qreal alpha = ( (1.0 - alphaValueF) * curveData.at(alphaValue) + alphaValueF * curveData.at(alphaValue+1)); + return (1.0 - alpha) * 255; } @@ -178,3 +180,8 @@ KisCubicCurve curve(newList); result = curve.floatTransfer( curveResolution ); } + +void KisCurveCircleMaskGenerator::resetMaskApplicator(bool forceScalar) +{ + d->applicator.reset(createOptimizedClass >(this,forceScalar)); +} Index: libs/image/kis_curve_circle_mask_generator_p.h =================================================================== --- /dev/null +++ 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 Index: libs/image/kis_curve_rect_mask_generator.h =================================================================== --- libs/image/kis_curve_rect_mask_generator.h +++ libs/image/kis_curve_rect_mask_generator.h @@ -32,7 +32,8 @@ */ class KRITAIMAGE_EXPORT KisCurveRectangleMaskGenerator : public KisMaskGenerator { - +public: + struct FastRowProcessor; public: KisCurveRectangleMaskGenerator(qreal radius, qreal ratio, qreal fh, qreal fv, int spikes, const KisCubicCurve& curve, bool antialiasEdges); @@ -48,9 +49,13 @@ void setSoftness(qreal softness) override; + bool shouldVectorize() const override; + KisBrushMaskApplicatorBase* applicator() override; + void resetMaskApplicator(bool forceScalar); + private: struct Private; - Private* const d; + const QScopedPointer d; }; #endif Index: libs/image/kis_curve_rect_mask_generator.cpp =================================================================== --- libs/image/kis_curve_rect_mask_generator.cpp +++ libs/image/kis_curve_rect_mask_generator.cpp @@ -20,44 +20,38 @@ #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_curve_rect_mask_generator.h" -#include "kis_cubic_curve.h" #include "kis_antialiasing_fade_maker.h" +#include "kis_brush_mask_applicator_factories.h" +#include "kis_brush_mask_applicator_base.h" +#include "kis_curve_rect_mask_generator.h" +#include "kis_curve_rect_mask_generator_p.h" +#include "kis_cubic_curve.h" -struct Q_DECL_HIDDEN KisCurveRectangleMaskGenerator::Private -{ - Private(bool enableAntialiasing) - : fadeMaker(*this, enableAntialiasing) - { - } - - Private(const Private &rhs) - : xcoeff(rhs.xcoeff), - ycoeff(rhs.ycoeff), - curveResolution(rhs.curveResolution), - curveData(rhs.curveData), - curvePoints(rhs.curvePoints), - dirty(rhs.dirty), - fadeMaker(rhs.fadeMaker, *this) - { - } - - qreal xcoeff, ycoeff; - qreal curveResolution; - QVector curveData; - QList curvePoints; - bool dirty; - - KisAntialiasingFadeMaker2D fadeMaker; - - quint8 value(qreal xr, qreal yr) const; -}; KisCurveRectangleMaskGenerator::KisCurveRectangleMaskGenerator(qreal diameter, qreal ratio, qreal fh, qreal fv, int spikes, const KisCubicCurve &curve, bool antialiasEdges) : KisMaskGenerator(diameter, ratio, fh, fv, spikes, antialiasEdges, RECTANGLE, SoftId), d(new Private(antialiasEdges)) @@ -69,12 +63,15 @@ d->dirty = false; setScale(1.0, 1.0); + + d->applicator.reset(createOptimizedClass >(this)); } KisCurveRectangleMaskGenerator::KisCurveRectangleMaskGenerator(const KisCurveRectangleMaskGenerator &rhs) : KisMaskGenerator(rhs), d(new Private(*rhs.d)) { + d->applicator.reset(createOptimizedClass >(this)); } KisMaskGenerator* KisCurveRectangleMaskGenerator::clone() const @@ -97,7 +94,6 @@ KisCurveRectangleMaskGenerator::~KisCurveRectangleMaskGenerator() { - delete d; } quint8 KisCurveRectangleMaskGenerator::Private::value(qreal xr, qreal yr) const @@ -148,3 +144,18 @@ d->dirty = false; } +bool KisCurveRectangleMaskGenerator::shouldVectorize() const +{ + return !shouldSupersample() && spikes() == 2; +} + +KisBrushMaskApplicatorBase* KisCurveRectangleMaskGenerator::applicator() +{ + return d->applicator.data(); +} + +void KisCurveRectangleMaskGenerator::resetMaskApplicator(bool forceScalar) +{ + d->applicator.reset(createOptimizedClass >(this,forceScalar)); +} + Index: libs/image/kis_curve_rect_mask_generator_p.h =================================================================== --- /dev/null +++ libs/image/kis_curve_rect_mask_generator_p.h @@ -0,0 +1,57 @@ +/* + * 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_RECT_MASK_GENERATOR_P_H +#define KIS_CURVE_RECT_MASK_GENERATOR_P_H + +#include + +#include "kis_antialiasing_fade_maker.h" +#include "kis_brush_mask_applicator_base.h" + +struct Q_DECL_HIDDEN KisCurveRectangleMaskGenerator::Private +{ + Private(bool enableAntialiasing) + : fadeMaker(*this, enableAntialiasing) + { + } + + Private(const Private &rhs) + : xcoeff(rhs.xcoeff), + ycoeff(rhs.ycoeff), + curveResolution(rhs.curveResolution), + curveData(rhs.curveData), + curvePoints(rhs.curvePoints), + dirty(rhs.dirty), + fadeMaker(rhs.fadeMaker, *this) + { + } + + qreal xcoeff, ycoeff; + qreal curveResolution; + QVector curveData; + QList curvePoints; + bool dirty; + + KisAntialiasingFadeMaker2D fadeMaker; + QScopedPointer applicator; + + inline quint8 value(qreal xr, qreal yr) const; +}; + +#endif // KIS_CURVE_RECT_MASK_GENERATOR_P_H Index: libs/image/kis_gauss_circle_mask_generator.h =================================================================== --- libs/image/kis_gauss_circle_mask_generator.h +++ libs/image/kis_gauss_circle_mask_generator.h @@ -20,16 +20,18 @@ #ifndef _KIS_GAUSS_MASK_GENERATOR_H_ #define _KIS_GAUSS_MASK_GENERATOR_H_ -#include #include "kritaimage_export.h" +#include "kis_mask_generator.h" +#include /** * This mask generator uses a Gaussian-blurred circle */ class KRITAIMAGE_EXPORT KisGaussCircleMaskGenerator : public KisMaskGenerator { - +public: + struct FastRowProcessor; public: KisGaussCircleMaskGenerator(qreal diameter, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges); @@ -41,6 +43,11 @@ void setScale(qreal scaleX, qreal scaleY) override; + bool shouldVectorize() const override; + KisBrushMaskApplicatorBase* applicator() override; + + void resetMaskApplicator(bool forceScalar); + private: qreal norme(qreal a, qreal b) const { Index: libs/image/kis_gauss_circle_mask_generator.cpp =================================================================== --- libs/image/kis_gauss_circle_mask_generator.cpp +++ libs/image/kis_gauss_circle_mask_generator.cpp @@ -20,6 +20,25 @@ #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 @@ -29,8 +48,11 @@ #include "kis_fast_math.h" #include "kis_base_mask_generator.h" -#include "kis_gauss_circle_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_circle_mask_generator.h" +#include "kis_gauss_circle_mask_generator_p.h" #define M_SQRT_2 1.41421356237309504880 @@ -41,47 +63,28 @@ #endif -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, distfactor, alphafactor; - KisAntialiasingFadeMaker1D fadeMaker; - - inline quint8 value(qreal dist) const; -}; - KisGaussCircleMaskGenerator::KisGaussCircleMaskGenerator(qreal diameter, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges) : KisMaskGenerator(diameter, ratio, fh, fv, spikes, antialiasEdges, CIRCLE, GaussId), d(new Private(antialiasEdges)) { d->ycoef = 1.0 / ratio; d->fade = 1.0 - (fh + fv) / 2.0; + if (d->fade == 0.0) d->fade = 1e-6; else if (d->fade == 1.0) d->fade = 1.0 - 1e-6; // would become undefined for fade == 0 or 1 + d->center = (2.5 * (6761.0*d->fade-10000.0))/(M_SQRT_2*6761.0*d->fade); d->alphafactor = 255.0 / (2.0 * erf(d->center)); + + d->applicator.reset(createOptimizedClass >(this)); + } KisGaussCircleMaskGenerator::KisGaussCircleMaskGenerator(const KisGaussCircleMaskGenerator &rhs) : KisMaskGenerator(rhs), d(new Private(*rhs.d)) { + d->applicator.reset(createOptimizedClass >(this)); } KisMaskGenerator* KisGaussCircleMaskGenerator::clone() const @@ -109,6 +112,16 @@ return (quint8) 255 - ret; } +bool KisGaussCircleMaskGenerator::shouldVectorize() const +{ + return !shouldSupersample() && spikes() == 2; +} + +KisBrushMaskApplicatorBase* KisGaussCircleMaskGenerator::applicator() +{ + return d->applicator.data(); +} + quint8 KisGaussCircleMaskGenerator::valueAt(qreal x, qreal y) const { if (isEmpty()) return 255; @@ -125,3 +138,8 @@ return d->value(dist); } + +void KisGaussCircleMaskGenerator::resetMaskApplicator(bool forceScalar) +{ + d->applicator.reset(createOptimizedClass >(this,forceScalar)); +} Index: libs/image/kis_gauss_circle_mask_generator_p.h =================================================================== --- /dev/null +++ libs/image/kis_gauss_circle_mask_generator_p.h @@ -0,0 +1,55 @@ +/* + * 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; + +}; + +#endif /* _KIS_GAUSS_MASK_GENERATOR_P_H_ */ Index: libs/image/kis_gauss_rect_mask_generator.h =================================================================== --- libs/image/kis_gauss_rect_mask_generator.h +++ 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; Index: libs/image/kis_gauss_rect_mask_generator.cpp =================================================================== --- libs/image/kis_gauss_rect_mask_generator.cpp +++ 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 (std::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)); +} Index: libs/image/kis_gauss_rect_mask_generator_p.h =================================================================== --- /dev/null +++ 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 Index: libs/image/kis_rect_mask_generator.h =================================================================== --- libs/image/kis_rect_mask_generator.h +++ libs/image/kis_rect_mask_generator.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2008-2009 Cyrille Berger + * Copyright (c) 2018 Ivan Santa Maria * * 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 @@ -22,7 +23,6 @@ #include #include "kritaimage_export.h" - #include "kis_mask_generator.h" /** @@ -30,7 +30,8 @@ */ class KRITAIMAGE_EXPORT KisRectangleMaskGenerator : public KisMaskGenerator { - +public: + struct FastRowProcessor; public: KisRectangleMaskGenerator(qreal radius, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges); @@ -44,6 +45,10 @@ void setScale(qreal scaleX, qreal scaleY) override; void setSoftness(qreal softness) override; + bool shouldVectorize() const override; + KisBrushMaskApplicatorBase* applicator() override; + void resetMaskApplicator(bool forceScalar); + private: struct Private; const QScopedPointer d; Index: libs/image/kis_rect_mask_generator.cpp =================================================================== --- libs/image/kis_rect_mask_generator.cpp +++ libs/image/kis_rect_mask_generator.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2004,2007,2008,2009.2010 Cyrille Berger + * Copyright (c) 2018 Ivan Santa Maria * * 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 @@ -19,43 +20,53 @@ #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 "kis_fast_math.h" - #include "kis_rect_mask_generator.h" +#include "kis_rect_mask_generator_p.h" #include "kis_base_mask_generator.h" -#include +#include "kis_brush_mask_applicator_factories.h" +#include "kis_brush_mask_applicator_base.h" -struct Q_DECL_HIDDEN KisRectangleMaskGenerator::Private { - double m_c; - qreal xcoeff; - qreal ycoeff; - qreal xfadecoeff; - qreal yfadecoeff; - qreal transformedFadeX; - qreal transformedFadeY; -}; +#include KisRectangleMaskGenerator::KisRectangleMaskGenerator(qreal radius, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges) : KisMaskGenerator(radius, ratio, fh, fv, spikes, antialiasEdges, RECTANGLE, DefaultId), d(new Private) { - if (fv == 0 && fh == 0) { - d->m_c = 0; - } else { - d->m_c = (fv / fh); - Q_ASSERT(!qIsNaN(d->m_c)); - - } - setScale(1.0, 1.0); + + // store the variable locally to allow vector implementation read it easily + d->copyOfAntialiasEdges = antialiasEdges; + d->applicator.reset(createOptimizedClass >(this)); } KisRectangleMaskGenerator::KisRectangleMaskGenerator(const KisRectangleMaskGenerator &rhs) : KisMaskGenerator(rhs), d(new Private(*rhs.d)) { + d->applicator.reset(createOptimizedClass >(this)); } KisMaskGenerator* KisRectangleMaskGenerator::clone() const @@ -75,6 +86,7 @@ d->ycoeff = 2.0 / effectiveSrcHeight(); d->xfadecoeff = (horizontalFade() == 0) ? 1 : (2.0 / (horizontalFade() * effectiveSrcWidth())); d->yfadecoeff = (verticalFade() == 0) ? 1 : (2.0 / (verticalFade() * effectiveSrcHeight())); + setSoftness(this->softness()); } @@ -92,6 +104,21 @@ return effectiveSrcWidth() < 10 || effectiveSrcHeight() < 10; } +bool KisRectangleMaskGenerator::shouldVectorize() const +{ + return !shouldSupersample() && spikes() == 2; +} + +KisBrushMaskApplicatorBase* KisRectangleMaskGenerator::applicator() +{ + return d->applicator.data(); +} + +void KisRectangleMaskGenerator::resetMaskApplicator(bool forceScalar) +{ + d->applicator.reset(createOptimizedClass >(this,forceScalar)); +} + quint8 KisRectangleMaskGenerator::valueAt(qreal x, qreal y) const { if (isEmpty()) return 255; @@ -115,11 +142,14 @@ qreal fxr = xr * d->transformedFadeX; qreal fyr = yr * d->transformedFadeY; - if (fxr > 1.0 && (fxr > fyr || fyr < 1.0)) { + int fxrInt = fxr * 1e4; + int fyrInt = fyr * 1e4; + + if (fxr > 1.0 && (fxrInt >= fyrInt || fyr < 1.0)) { return 255 * nxr * (fxr - 1.0) / (fxr - nxr); } - if (fyr > 1.0 && (fyr > fxr || fxr < 1.0)) { + if (fyr > 1.0 && (fyrInt > fxrInt || fxr < 1.0)) { return 255 * nyr * (fyr - 1.0) / (fyr - nyr); } Index: libs/image/kis_rect_mask_generator_p.h =================================================================== --- /dev/null +++ libs/image/kis_rect_mask_generator_p.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2008-2009 Cyrille Berger + * Copyright (c) 2018 Ivan Santa Maria + * + * 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_RECT_MASK_GENERATOR_P_H +#define KIS_RECT_MASK_GENERATOR_P_H + +struct Q_DECL_HIDDEN KisRectangleMaskGenerator::Private { + Private() + : xcoeff(0), + ycoeff(0), + xfadecoeff(0), + yfadecoeff(0), + transformedFadeX(0), + transformedFadeY(0), + copyOfAntialiasEdges(false) + { + } + + Private(const Private &rhs) + : xcoeff(rhs.xcoeff), + ycoeff(rhs.ycoeff), + xfadecoeff(rhs.xfadecoeff), + yfadecoeff(rhs.yfadecoeff), + transformedFadeX(rhs.transformedFadeX), + transformedFadeY(rhs.transformedFadeY), + copyOfAntialiasEdges(rhs.copyOfAntialiasEdges) + { + } + qreal xcoeff; + qreal ycoeff; + qreal xfadecoeff; + qreal yfadecoeff; + qreal transformedFadeX; + qreal transformedFadeY; + + bool copyOfAntialiasEdges; + bool noFading; + + QScopedPointer applicator; +}; + + +#endif // KIS_RECT_MASK_GENERATOR_P_H Index: libs/image/tests/CMakeLists.txt =================================================================== --- libs/image/tests/CMakeLists.txt +++ libs/image/tests/CMakeLists.txt @@ -124,6 +120,8 @@ kis_marker_painter_test.cpp kis_lazy_brush_test.cpp kis_colorize_mask_test.cpp + kis_mask_similarity_test.cpp + KisMaskGeneratorBenchmark.cpp NAME_PREFIX "krita-image-" LINK_LIBRARIES kritaimage Qt5::Test) Index: libs/image/tests/KisMaskGeneratorBenchmark.h =================================================================== --- /dev/null +++ libs/image/tests/KisMaskGeneratorBenchmark.h @@ -0,0 +1,48 @@ +/* + * 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(); + + void testRectangularScalarMask(); + void testRectangularVectorMask(); + + void testRectangularGaussScalarMask(); + void testRectangularGaussVectorMask(); + + void testRectangularSoftScalarMask(); + void testRectangularSoftVectorMask(); + +}; + +#endif // KISMASKGENERATORBENCHMARK_H Index: libs/image/tests/KisMaskGeneratorBenchmark.cpp =================================================================== --- /dev/null +++ libs/image/tests/KisMaskGeneratorBenchmark.cpp @@ -0,0 +1,202 @@ +/* + * 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) + : m_bounds(_bounds), + applicatorBase(_applicatorBase) + { + 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,1;1,0")); + { + 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("0,1;1,0")); + { + KisCurveCircleMaskGenerator circVectr(1000, 1.0, 0.5, 0.5, 2, pointsCurve, true); + circVectr.setSoftness(0.5); + KisMaskGeneratorBenchmarkTester(circVectr.applicator(), bounds); + } +} + +void KisMaskGeneratorBenchmark::testRectangularScalarMask(){ + QRect bounds(0,0,1000,1000); + { + KisRectangleMaskGenerator rectScalar(1000, 1.0, 0.5, 0.5, 2, true); + rectScalar.resetMaskApplicator(true); // Force usage of scalar backend + + KisMaskGeneratorBenchmarkTester(rectScalar.applicator(), bounds); + } +} + +void KisMaskGeneratorBenchmark::testRectangularVectorMask(){ + QRect bounds(0,0,1000,1000); + { + KisRectangleMaskGenerator rectScalar(1000, 1.0, 0.5, 0.5, 2, true); + KisMaskGeneratorBenchmarkTester(rectScalar.applicator(), bounds); + } +} + +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); + } +} + +void KisMaskGeneratorBenchmark::testRectangularSoftScalarMask() +{ + QRect bounds(0,0,1000,1000); + KisCubicCurve pointsCurve; + pointsCurve.fromString(QString("0,1;1,0")); + { + KisCurveRectangleMaskGenerator circScalar(1000, 1.0, 0.5, 0.5, 2, pointsCurve, true); + + circScalar.resetMaskApplicator(true); // Force usage of scalar backend + + KisMaskGeneratorBenchmarkTester(circScalar.applicator(), bounds); + } +} +void KisMaskGeneratorBenchmark::testRectangularSoftVectorMask() +{ + QRect bounds(0,0,1000,1000); + KisCubicCurve pointsCurve; + pointsCurve.fromString(QString("0,1;1,0")); + { + KisCurveRectangleMaskGenerator circVectr(1000, 1.0, 0.5, 0.5, 2, pointsCurve, true); + + KisMaskGeneratorBenchmarkTester(circVectr.applicator(), bounds); + } +} + +QTEST_MAIN(KisMaskGeneratorBenchmark) Index: libs/image/tests/kis_mask_similarity_test.h =================================================================== --- /dev/null +++ libs/image/tests/kis_mask_similarity_test.h @@ -0,0 +1,38 @@ +/* + * 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(); + + void testRectMask(); + void testGaussRectMask(); + void testSoftRectMask(); +}; + +#endif Index: libs/image/tests/kis_mask_similarity_test.cpp =================================================================== --- /dev/null +++ libs/image/tests/kis_mask_similarity_test.cpp @@ -0,0 +1,261 @@ +/* + * 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" + +#include "config-limit-long-tests.h" + +#ifdef LIMIT_LONG_TESTS +#define RATIO_STEP 50 +#define FADE_STEP 25 +#else /* LIMIT_LONG_TESTS */ +#define RATIO_STEP 20 +#define FADE_STEP 5 +#endif /* LIMIT_LONG_TESTS */ + + +enum MaskType { + DEFAULT, CIRC_GAUSS, CIRC_SOFT, RECT, RECT_GAUSS, RECT_SOFT, STAMP +}; + +class KisMaskSimilarityTester +{ + +public: + KisMaskSimilarityTester(KisBrushMaskApplicatorBase *_legacy, KisBrushMaskApplicatorBase *_vectorized, QRect _bounds, MaskType type) + : 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 + + // 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 + + // Check for differences, max errors: 0 + QPoint tmpPt; + if (!TestUtil::compareQImages(tmpPt,scalarImage, vectorImage, 0, 2, 0)) { + scalarImage.save(QString(getTypeName(type) + "_scalar_mask.png"),"PNG"); + vectorImage.save(QString(getTypeName(type) + "_vector_mask.png"),"PNG"); + + QFAIL(QString("Masks differ! first different pixel: %1,%2 \n").arg(tmpPt.x()).arg(tmpPt.y()).toLatin1()); + } + + } + + + static bool exahustiveTest(QRect bounds, MaskType type) { + // Exahustive test + + for (size_t i = 0; i <= 100; i += FADE_STEP){ + for (size_t j = 0; j <= 100; j += FADE_STEP){ + for (size_t k = 0; k <= 100; k += RATIO_STEP){ + + 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); + 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); + break; + } + case RECT: + { + KisRectangleMaskGenerator bCircVectr(499.5, k/100.f, i/100.f, j/100.f, 2, true); + KisRectangleMaskGenerator bCircScalar(bCircVectr); + bCircScalar.resetMaskApplicator(true); // Force usage of scalar backend + + KisMaskSimilarityTester(bCircScalar.applicator(), bCircVectr.applicator(), bounds,type); + 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); + break; + + } + case RECT_SOFT: + { + KisCubicCurve pointsCurve; + pointsCurve.fromString(QString("0,1;1,0")); + KisCurveRectangleMaskGenerator bCircVectr(499.5, k/100.f, i/100.f, j/100.f, 2, pointsCurve, true); + KisCurveRectangleMaskGenerator bCircScalar(bCircVectr); + bCircScalar.resetMaskApplicator(true); // Force usage of scalar backend + + KisMaskSimilarityTester(bCircScalar.applicator(), bCircVectr.applicator(), bounds,type); + break; + } + default: + { + break; + } + } + + if (QTest::currentTestFailed()) { + QWARN(QString("Mask features: Ratio=%1, hfade=%2, vfade=%3 \n") + .arg(k/100.f,0,'g',2).arg(i/100.f,0,'g',2).arg(j/100.f,0,'g',2).toLatin1()); + return false; + } + + } } } // end for + return true; + } + + template + static void runMaskGenTest(MaskGenerator& generator, MaskType type) { + QRect bounds(0,0,700,700); + generator.setDiameter(499.5); + MaskGenerator scalarGenerator(generator); + + scalarGenerator.resetMaskApplicator(true); // Force usage of scalar backend + KisMaskSimilarityTester(scalarGenerator.applicator(), generator.applicator(), bounds, type); + + // KisMaskSimilarityTester::exahustiveTest(bounds,type); + } + +private: + QString getTypeName(MaskType type) { + + QString strName; + switch (type) { + case CIRC_GAUSS: + strName = "CircGauss"; + break; + case CIRC_SOFT: + strName = "CircSoft"; + break; + case RECT: + strName = "Rect"; + break; + case RECT_GAUSS: + strName = "RectGauss"; + break; + case RECT_SOFT: + strName = "RectSoft"; + break; + case STAMP: + strName = "Stamp"; + break; + default: + strName = "Default"; + break; + } + return strName; + } + +protected: + const KoColorSpace *m_colorSpace = KoColorSpaceRegistry::instance()->rgb8(); + + KisBrushMaskApplicatorBase *legacy; + KisBrushMaskApplicatorBase *vectorized; + QRect m_bounds; + KisFixedPaintDeviceSP m_paintDev; +}; + + +void KisMaskSimilarityTest::testCircleMask() +{ + KisCircleMaskGenerator generator(499.5, 0.2, 0.5, 0.5, 2, true); + qDebug() << generator.id() << generator.name(); + KisMaskSimilarityTester::runMaskGenTest(generator,DEFAULT); +} + +void KisMaskSimilarityTest::testGaussCircleMask() +{ + KisGaussCircleMaskGenerator generator(499.5, 0.2, 1, 1, 2, true); + KisMaskSimilarityTester::runMaskGenTest(generator,CIRC_GAUSS); +} + +void KisMaskSimilarityTest::testSoftCircleMask() +{ + KisCubicCurve pointsCurve; + pointsCurve.fromString(QString("0,1;1,0")); + KisCurveCircleMaskGenerator generator(499.5, 0.2, 0.5, 0.5, 2, pointsCurve,true); + KisMaskSimilarityTester::runMaskGenTest(generator,CIRC_SOFT); +} + +void KisMaskSimilarityTest::testRectMask() +{ + KisRectangleMaskGenerator generator(499.5, 0.1, 0.5, 0.5, 2, false); + KisMaskSimilarityTester::runMaskGenTest(generator,RECT); +} + +void KisMaskSimilarityTest::testGaussRectMask() +{ + KisGaussRectangleMaskGenerator generator(499.5, 0.2, 0.5, 0.2, 2, true); + KisMaskSimilarityTester::runMaskGenTest(generator,RECT_GAUSS); +} + +void KisMaskSimilarityTest::testSoftRectMask() +{ + KisCubicCurve pointsCurve; + pointsCurve.fromString(QString("0,1;1,0")); + KisCurveRectangleMaskGenerator generator(499.5, 0.2, 0.5, 0.2, 2, pointsCurve, true); + KisMaskSimilarityTester::runMaskGenTest(generator,RECT_SOFT); +} + +QTEST_MAIN(KisMaskSimilarityTest) Index: libs/image/vc_extra_math.h =================================================================== --- /dev/null +++ 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 Index: libs/pigment/compositeops/KoVcMultiArchBuildSupport.h =================================================================== --- libs/pigment/compositeops/KoVcMultiArchBuildSupport.h +++ libs/pigment/compositeops/KoVcMultiArchBuildSupport.h @@ -109,4 +109,14 @@ } +template +typename FactoryType::ReturnType +createOptimizedClass(typename FactoryType::ParamType param, bool forceScalarImplemetation) +{ + if(forceScalarImplemetation){ + return FactoryType::template create(param); + } + return createOptimizedClass(param); +} + #endif /* __KOVCMULTIARCHBUILDSUPPORT_H */ Index: libs/ui/tests/FreehandStrokeBenchmark.h =================================================================== --- libs/ui/tests/FreehandStrokeBenchmark.h +++ libs/ui/tests/FreehandStrokeBenchmark.h @@ -30,6 +30,11 @@ void testDefaultTip(); void testSoftTip(); void testGaussianTip(); + + void testRectangularTip(); + void testRectGaussianTip(); + void testRectSoftTip(); + void testStampTip(); void testColorsmudgeDefaultTip(); Index: libs/ui/tests/FreehandStrokeBenchmark.cpp =================================================================== --- libs/ui/tests/FreehandStrokeBenchmark.cpp +++ libs/ui/tests/FreehandStrokeBenchmark.cpp @@ -128,6 +128,21 @@ benchmarkBrush("testing_1000px_auto_gaussian.kpp"); } +void FreehandStrokeBenchmark::testRectangularTip() +{ + benchmarkBrush("testing_1000px_auto_rectangular.kpp"); +} + +void FreehandStrokeBenchmark::testRectGaussianTip() +{ + benchmarkBrush("testing_1000px_auto_gaussian_rect.kpp"); +} + +void FreehandStrokeBenchmark::testRectSoftTip() +{ + benchmarkBrush("testing_1000px_auto_soft_rect.kpp"); +} + void FreehandStrokeBenchmark::testStampTip() { benchmarkBrush("testing_1000px_stamp_450_rotated.kpp");