diff --git a/libs/image/kis_brush_mask_applicator_factories.cpp b/libs/image/kis_brush_mask_applicator_factories.cpp index 865224e9b6..d89ee70e15 100644 --- a/libs/image/kis_brush_mask_applicator_factories.cpp +++ b/libs/image/kis_brush_mask_applicator_factories.cpp @@ -1,662 +1,654 @@ /* * Copyright (c) 2012 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_brush_mask_applicator_factories.h" #include "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" #define a(_s) #_s #define b(_s) a(_s) template<> template<> MaskApplicatorFactory::ReturnType MaskApplicatorFactory::create(ParamType maskGenerator) { return new KisBrushMaskScalarApplicator(maskGenerator); } template<> template<> MaskApplicatorFactory::ReturnType MaskApplicatorFactory::create(ParamType maskGenerator) { return new KisBrushMaskVectorApplicator(maskGenerator); } template<> template<> MaskApplicatorFactory::ReturnType MaskApplicatorFactory::create(ParamType maskGenerator) { return new KisBrushMaskVectorApplicator(maskGenerator); } template<> template<> MaskApplicatorFactory::ReturnType MaskApplicatorFactory::create(ParamType maskGenerator) { return new KisBrushMaskVectorApplicator(maskGenerator); } template<> template<> MaskApplicatorFactory::ReturnType MaskApplicatorFactory::create(ParamType maskGenerator) { return new KisBrushMaskVectorApplicator(maskGenerator); } template<> template<> MaskApplicatorFactory::ReturnType MaskApplicatorFactory::create(ParamType maskGenerator) { return new KisBrushMaskVectorApplicator(maskGenerator); } template<> template<> MaskApplicatorFactory::ReturnType MaskApplicatorFactory::create(ParamType maskGenerator) { return new KisBrushMaskVectorApplicator(maskGenerator); } #if defined HAVE_VC struct KisCircleMaskGenerator::FastRowProcessor { FastRowProcessor(KisCircleMaskGenerator *maskGenerator) : d(maskGenerator->d.data()) {} template void process(float* buffer, int width, float y, float cosa, float sina, float centerX, float centerY); KisCircleMaskGenerator::Private *d; }; template<> void KisCircleMaskGenerator:: FastRowProcessor::process(float* buffer, int width, float y, float cosa, float sina, float centerX, float centerY) { const bool useSmoothing = d->copyOfAntialiasEdges; const bool noFading = d->noFading; float y_ = y - centerY; float sinay_ = sina * y_; float cosay_ = cosa * y_; float* bufferPointer = buffer; Vc::float_v currentIndices = Vc::float_v::IndexesFromZero(); Vc::float_v increment((float)Vc::float_v::size()); Vc::float_v vCenterX(centerX); Vc::float_v vCosa(cosa); Vc::float_v vSina(sina); Vc::float_v vCosaY_(cosay_); Vc::float_v vSinaY_(sinay_); Vc::float_v vXCoeff(d->xcoef); Vc::float_v vYCoeff(d->ycoef); Vc::float_v vTransformedFadeX(d->transformedFadeX); Vc::float_v vTransformedFadeY(d->transformedFadeY); Vc::float_v vOne(Vc::One); for (int i=0; i < width; i+= Vc::float_v::size()){ Vc::float_v x_ = currentIndices - vCenterX; Vc::float_v xr = x_ * vCosa - vSinaY_; Vc::float_v yr = x_ * vSina + vCosaY_; Vc::float_v n = pow2(xr * vXCoeff) + pow2(yr * vYCoeff); Vc::float_m outsideMask = n > vOne; if (!outsideMask.isFull()) { if (noFading) { Vc::float_v vFade(Vc::Zero); vFade(outsideMask) = vOne; vFade.store(bufferPointer, Vc::Aligned); } else { if (useSmoothing) { xr = Vc::abs(xr) + vOne; yr = Vc::abs(yr) + vOne; } Vc::float_v vNormFade = pow2(xr * vTransformedFadeX) + pow2(yr * vTransformedFadeY); //255 * n * (normeFade - 1) / (normeFade - n) Vc::float_v vFade = n * (vNormFade - vOne) / (vNormFade - n); // Mask in the inner circe of the mask Vc::float_m mask = vNormFade < vOne; vFade.setZero(mask); // Mask out the outer circe of the mask vFade(outsideMask) = vOne; vFade.store(bufferPointer, Vc::Aligned); } } else { // Mask out everything outside the circle vOne.store(bufferPointer, Vc::Aligned); } currentIndices = currentIndices + increment; bufferPointer += Vc::float_v::size(); } } struct KisGaussCircleMaskGenerator::FastRowProcessor { FastRowProcessor(KisGaussCircleMaskGenerator *maskGenerator) : d(maskGenerator->d.data()) {} template void process(float* buffer, int width, float y, float cosa, float sina, float centerX, float centerY); KisGaussCircleMaskGenerator::Private *d; }; template<> void KisGaussCircleMaskGenerator:: FastRowProcessor::process(float* buffer, int width, float y, float cosa, float sina, float centerX, float centerY) { float y_ = y - centerY; float sinay_ = sina * y_; float cosay_ = cosa * y_; float* bufferPointer = buffer; Vc::float_v currentIndices = Vc::float_v::IndexesFromZero(); Vc::float_v increment((float)Vc::float_v::size()); Vc::float_v vCenterX(centerX); Vc::float_v vCenter(d->center); Vc::float_v vCosa(cosa); Vc::float_v vSina(sina); Vc::float_v vCosaY_(cosay_); Vc::float_v vSinaY_(sinay_); Vc::float_v vYCoeff(d->ycoef); Vc::float_v vDistfactor(d->distfactor); Vc::float_v vAlphafactor(d->alphafactor); Vc::float_v 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; - const bool noFading = d->noFading; float y_ = y - centerY; float sinay_ = sina * y_; float cosay_ = cosa * y_; float* bufferPointer = buffer; Vc::float_v currentIndices = Vc::float_v::IndexesFromZero(); Vc::float_v increment((float)Vc::float_v::size()); Vc::float_v vCenterX(centerX); Vc::float_v vCosa(cosa); Vc::float_v vSina(sina); Vc::float_v vCosaY_(cosay_); Vc::float_v vSinaY_(sinay_); Vc::float_v vXCoeff(d->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; + } - if (noFading) { - Vc::float_v vFade(vZero); - vFade(outsideMask) = vOne; - vFade.store(bufferPointer, Vc::Aligned); - } else { - 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 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 fxrNorm = nxr * (fxr - vOne) / (fxr - nxr); + Vc::float_v fyrNorm = nyr * (fyr - vOne) / (fyr - nyr); - Vc::float_v vFade(vZero); + Vc::float_v vFade(vZero); - Vc::float_v::IndexType fxrInt(fxr * vTolerance); - Vc::float_v::IndexType fyrInt(fyr * vTolerance); + 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); + 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; + vFade(fadeXMask) = fxrNorm; + vFade(!fadeXMask && fadeYMask) = fyrNorm; - // Mask out the outer circe of the mask - vFade(outsideMask) = vOne; - vFade.store(bufferPointer, Vc::Aligned); - } + // 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 */ diff --git a/libs/image/kis_rect_mask_generator.cpp b/libs/image/kis_rect_mask_generator.cpp index 32688a5c48..1d6200a39b 100644 --- a/libs/image/kis_rect_mask_generator.cpp +++ b/libs/image/kis_rect_mask_generator.cpp @@ -1,166 +1,158 @@ /* * 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 * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include //MSVC requires that Vc come first #include #include #ifdef HAVE_VC #if defined(__clang__) #pragma GCC diagnostic ignored "-Wundef" #pragma GCC diagnostic ignored "-Wlocal-type-template-args" #endif #if defined _MSC_VER // Lets shut up the "possible loss of data" and "forcing value to bool 'true' or 'false' #pragma warning ( push ) #pragma warning ( disable : 4244 ) #pragma warning ( disable : 4800 ) #endif #include #include #if defined _MSC_VER #pragma warning ( pop ) #endif #endif #include #include "kis_fast_math.h" #include "kis_rect_mask_generator.h" #include "kis_rect_mask_generator_p.h" #include "kis_base_mask_generator.h" #include "kis_brush_mask_applicator_factories.h" #include "kis_brush_mask_applicator_base.h" #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 { return new KisRectangleMaskGenerator(*this); } KisRectangleMaskGenerator::~KisRectangleMaskGenerator() { } void KisRectangleMaskGenerator::setScale(qreal scaleX, qreal scaleY) { KisMaskGenerator::setScale(scaleX, scaleY); d->xcoeff = 2.0 / effectiveSrcWidth(); 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()); - - d->noFading = !d->copyOfAntialiasEdges && - qFuzzyCompare(d->xcoeff, d->transformedFadeX) && - qFuzzyCompare(d->ycoeff, d->transformedFadeY); } void KisRectangleMaskGenerator::setSoftness(qreal softness) { KisMaskGenerator::setSoftness(softness); qreal safeSoftnessCoeff = qreal(1.0) / qMax(qreal(0.01), softness); d->transformedFadeX = d->xfadecoeff * safeSoftnessCoeff; d->transformedFadeY = d->yfadecoeff * safeSoftnessCoeff; } bool KisRectangleMaskGenerator::shouldSupersample() const { 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; qreal xr = qAbs(x /*- m_xcenter*/); qreal yr = qAbs(y /*- m_ycenter*/); fixRotation(xr, yr); xr = qAbs(xr); yr = qAbs(yr); qreal nxr = xr * d->xcoeff; qreal nyr = yr * d->ycoeff; if (nxr > 1.0 || nyr > 1.0) return 255; if (antialiasEdges()) { xr += 1.0; yr += 1.0; } 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); } return 0; } diff --git a/libs/image/kis_rect_mask_generator_p.h b/libs/image/kis_rect_mask_generator_p.h index 70b2aa8081..ede570db7b 100644 --- a/libs/image/kis_rect_mask_generator_p.h +++ b/libs/image/kis_rect_mask_generator_p.h @@ -1,61 +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) { } - - double m_c; 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 diff --git a/libs/image/tests/kis_mask_similarity_test.cpp b/libs/image/tests/kis_mask_similarity_test.cpp index dd2542e885..8786886ae2 100644 --- a/libs/image/tests/kis_mask_similarity_test.cpp +++ b/libs/image/tests/kis_mask_similarity_test.cpp @@ -1,290 +1,291 @@ /* * 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" 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, bool renderImage = true) : 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 if (renderImage || QTest::currentTestFailed()) { } // Check for differences, max errors: 0 QPoint tmpPt; - if (!TestUtil::compareQImages(tmpPt,scalarImage, vectorImage, 0, 4, 0)) { + 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 += 5){ for (size_t j = 0; j <= 100; j += 5){ - for (size_t k = 0; k <= 100; k += 20){ + for (size_t k = 10; 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: { 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,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; } 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,false); 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; } 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() { QRect bounds(0,0,700,700); { - KisCircleMaskGenerator circVectr(499.5, 1.0, 0.5, 0.5, 2, true); + KisCircleMaskGenerator circVectr(499.5, 0.2, 0.5, 0.5, 2, true); KisCircleMaskGenerator circScalar(circVectr); circScalar.resetMaskApplicator(true); // Force usage of scalar backend KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), bounds, DEFAULT); } } void KisMaskSimilarityTest::testGaussCircleMask() { QRect bounds(0,0,700,700); { - KisGaussCircleMaskGenerator circVectr(499.5, 1.0, 1, 1, 2, true); + KisGaussCircleMaskGenerator circVectr(499.5, 0.2, 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); } // KisMaskSimilarityTester::exahustiveTest(bounds,CIRC_GAUSS); } void KisMaskSimilarityTest::testSoftCircleMask() { QRect bounds(0,0,700,700); KisCubicCurve pointsCurve; pointsCurve.fromString(QString("0,1;1,0")); { - KisCurveCircleMaskGenerator circVectr(499.5, 1.0, 0.5, 0.5, 2, pointsCurve,true); + KisCurveCircleMaskGenerator circVectr(499.5, 0.2, 0.5, 0.5, 2, pointsCurve,true); circVectr.setDiameter(500); // circVectr.setSoftness(1.0); KisCurveCircleMaskGenerator circScalar(circVectr); circScalar.resetMaskApplicator(true); // Force usage of scalar backend KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), bounds, CIRC_SOFT); } // KisMaskSimilarityTester::exahustiveTest(bounds,CIRC_SOFT); } void KisMaskSimilarityTest::testRectMask() { QRect bounds(0,0,700,700); { - KisRectangleMaskGenerator rectVectr(499.5, 0.2, 0.05, 0.05, 2, true); +// KisRectangleMaskGenerator rectVectr(499.5, 0.2, 0.05, 0.05, 2, true); + KisRectangleMaskGenerator rectVectr(499.5, 0.1, 1, 1, 2, false); KisRectangleMaskGenerator rectScalar(rectVectr); rectScalar.resetMaskApplicator(true); // Force usage of scalar backend KisMaskSimilarityTester(rectScalar.applicator(), rectVectr.applicator(), bounds, RECT); } -// KisMaskSimilarityTester::exahustiveTest(bounds, RECT); + KisMaskSimilarityTester::exahustiveTest(bounds, RECT); } void KisMaskSimilarityTest::testGaussRectMask() { QRect bounds(0,0,700,700); { - KisGaussRectangleMaskGenerator circVectr(499.5, 1.0, 0.5, 0.2, 2, true); + KisGaussRectangleMaskGenerator circVectr(499.5, 0.2, 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); } void KisMaskSimilarityTest::testSoftRectMask() { QRect bounds(0,0,700,700); KisCubicCurve pointsCurve; pointsCurve.fromString(QString("0,1;1,0")); { - KisCurveRectangleMaskGenerator circVectr(499.5, 1.0, 0.5, 0.2, 2, pointsCurve, true); + KisCurveRectangleMaskGenerator circVectr(499.5, 0.2, 0.5, 0.2, 2, pointsCurve, true); KisCurveRectangleMaskGenerator circScalar(circVectr); circVectr.setDiameter(499.5); circScalar.resetMaskApplicator(true); // Force usage of scalar backend KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), bounds, RECT_SOFT); } // KisMaskSimilarityTester::exahustiveTest(bounds,RECT_SOFT); } QTEST_MAIN(KisMaskSimilarityTest) diff --git a/libs/ui/tests/data/testing_1000px_auto_soft_rect.kpp b/libs/ui/tests/data/testing_1000px_auto_soft_rect.kpp index 6943d82d9a..dc85b44092 100644 Binary files a/libs/ui/tests/data/testing_1000px_auto_soft_rect.kpp and b/libs/ui/tests/data/testing_1000px_auto_soft_rect.kpp differ