diff --git a/libs/image/kis_antialiasing_fade_maker.h b/libs/image/kis_antialiasing_fade_maker.h index 005337c5b5..6ef1b25032 100644 --- a/libs/image/kis_antialiasing_fade_maker.h +++ b/libs/image/kis_antialiasing_fade_maker.h @@ -1,222 +1,212 @@ /* * Copyright (c) 2014 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. */ #ifndef __KIS_ANTIALIASING_FADE_MAKER_H #define __KIS_ANTIALIASING_FADE_MAKER_H #include "kis_global.h" template class KisAntialiasingFadeMaker1D { public: KisAntialiasingFadeMaker1D(const BaseFade &baseFade, bool enableAntialiasing) : m_radius(0.0), m_fadeStartValue(0), m_antialiasingFadeStart(0), m_antialiasingFadeCoeff(0), m_enableAntialiasing(enableAntialiasing), m_baseFade(baseFade) { } KisAntialiasingFadeMaker1D(const KisAntialiasingFadeMaker1D &rhs, const BaseFade &baseFade) : m_radius(rhs.m_radius), m_fadeStartValue(rhs.m_fadeStartValue), m_antialiasingFadeStart(rhs.m_antialiasingFadeStart), m_antialiasingFadeCoeff(rhs.m_antialiasingFadeCoeff), m_enableAntialiasing(rhs.m_enableAntialiasing), m_baseFade(baseFade) { } void setSquareNormCoeffs(qreal xcoeff, qreal ycoeff) { m_radius = 1.0; qreal xf = qMax(0.0, ((1.0 / xcoeff) - 1.0) * xcoeff); qreal yf = qMax(0.0, ((1.0 / ycoeff) - 1.0) * ycoeff); m_antialiasingFadeStart = pow2(0.5 * (xf + yf)); m_fadeStartValue = m_baseFade.value(m_antialiasingFadeStart); m_antialiasingFadeCoeff = qMax(0.0, 255.0 - m_fadeStartValue) / (m_radius - m_antialiasingFadeStart); } void setRadius(qreal radius) { m_radius = radius; m_antialiasingFadeStart = qMax(0.0, m_radius - 1.0); m_fadeStartValue = m_baseFade.value(m_antialiasingFadeStart); m_antialiasingFadeCoeff = qMax(0.0, 255.0 - m_fadeStartValue) / (m_radius - m_antialiasingFadeStart); } inline bool needFade(qreal dist, quint8 *value) { if (dist > m_radius) { *value = 255; return true; } if (!m_enableAntialiasing) { return false; } if (dist > m_antialiasingFadeStart) { *value = m_fadeStartValue + (dist - m_antialiasingFadeStart) * m_antialiasingFadeCoeff; return true; } return false; } qreal getRadius(){ return m_radius; } qreal getAntialiasingFadeStart(){ return m_antialiasingFadeStart; } qreal getFadeStartValue() { return m_fadeStartValue; } qreal getAntialiasingFadeCoeff(){ return m_antialiasingFadeCoeff; } bool getAliasingEnabled(){ return m_enableAntialiasing; } private: qreal m_radius; quint8 m_fadeStartValue; qreal m_antialiasingFadeStart; qreal m_antialiasingFadeCoeff; bool m_enableAntialiasing; const BaseFade &m_baseFade; }; template class KisAntialiasingFadeMaker2D { public: KisAntialiasingFadeMaker2D(const BaseFade &baseFade, bool enableAntialiasing) : m_xLimit(0), m_yLimit(0), m_xFadeLimitStart(0), m_yFadeLimitStart(0), m_xFadeCoeff(0), m_yFadeCoeff(0), m_enableAntialiasing(enableAntialiasing), m_baseFade(baseFade) { } KisAntialiasingFadeMaker2D(const KisAntialiasingFadeMaker2D &rhs, const BaseFade &baseFade) : m_xLimit(rhs.m_xLimit), m_yLimit(rhs.m_yLimit), m_xFadeLimitStart(rhs.m_xFadeLimitStart), m_yFadeLimitStart(rhs.m_yFadeLimitStart), m_xFadeCoeff(rhs.m_xFadeCoeff), m_yFadeCoeff(rhs.m_yFadeCoeff), m_enableAntialiasing(rhs.m_enableAntialiasing), m_baseFade(baseFade) { } void setLimits(qreal halfWidth, qreal halfHeight) { m_xLimit = halfWidth; m_yLimit = halfHeight; m_xFadeLimitStart = m_xLimit - 1.0; m_yFadeLimitStart = m_yLimit - 1.0; m_xFadeCoeff = 1.0 / (m_xLimit - m_xFadeLimitStart); m_yFadeCoeff = 1.0 / (m_yLimit - m_yFadeLimitStart); } inline bool needFade(qreal x, qreal y, quint8 *value) { x = qAbs(x); y = qAbs(y); if (x > m_xLimit) { *value = 255; return true; } if (y > m_yLimit) { *value = 255; return true; } if (!m_enableAntialiasing) { return false; } if (x > m_xFadeLimitStart) { quint8 baseValue = m_baseFade.value(x, y); *value = baseValue + (255.0 - baseValue) * (x - m_xFadeLimitStart) * m_xFadeCoeff; if (y > m_yFadeLimitStart && *value < 255) { *value += (255.0 - *value) * (y - m_yFadeLimitStart) * m_yFadeCoeff; } return true; } if (y > m_yFadeLimitStart) { quint8 baseValue = m_baseFade.value(x, y); *value = baseValue + (255.0 - baseValue) * (y - m_yFadeLimitStart) * m_yFadeCoeff; if (x > m_xFadeLimitStart && *value < 255) { *value += (255.0 - *value) * (x - m_xFadeLimitStart) * m_xFadeCoeff; } return true; } return false; } - qreal getXLimit() { return m_xLimit; } - qreal getYLimit() { return m_yLimit; } - - qreal getXFadeLimitStart() { return m_xFadeLimitStart; } - qreal getYFadeLimitStart() { return m_yFadeLimitStart; } - qreal getXFadeCoeff(){ return m_xFadeCoeff; } - qreal getYFadeCoeff(){ return m_yFadeCoeff; } - - bool getAliasingEnabled() { return m_enableAntialiasing; } - private: qreal m_xLimit; qreal m_yLimit; qreal m_xFadeLimitStart; qreal m_yFadeLimitStart; qreal m_xFadeCoeff; qreal m_yFadeCoeff; bool m_enableAntialiasing; const BaseFade &m_baseFade; }; #endif /* __KIS_ANTIALIASING_FADE_MAKER_H */ diff --git a/libs/image/kis_brush_mask_applicator_factories.cpp b/libs/image/kis_brush_mask_applicator_factories.cpp index d550b200fb..f99a1ab15f 100644 --- a/libs/image/kis_brush_mask_applicator_factories.cpp +++ b/libs/image/kis_brush_mask_applicator_factories.cpp @@ -1,515 +1,378 @@ /* * Copyright (c) 2012 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_brush_mask_applicator_factories.h" #include "kis_circle_mask_generator.h" #include "kis_circle_mask_generator_p.h" #include "kis_gauss_circle_mask_generator_p.h" #include "kis_curve_circle_mask_generator_p.h" -#include "kis_gauss_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); -} - #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) { const bool antialiasOn = d->fadeMaker.getAliasingEnabled(); float y_ = y - centerY; float sinay_ = sina * y_; float cosay_ = cosa * y_; float* bufferPointer = buffer; Vc::float_v currentIndices = Vc::float_v::IndexesFromZero(); Vc::float_v increment((float)Vc::float_v::size()); Vc::float_v vCenterX(centerX); Vc::float_v vCenter(d->center); Vc::float_v vCosa(cosa); Vc::float_v vSina(sina); Vc::float_v vCosaY_(cosay_); Vc::float_v vSinaY_(sinay_); Vc::float_v vYCoeff(d->ycoef); Vc::float_v vDistfactor(d->distfactor); Vc::float_v vAlphafactor(d->alphafactor); Vc::float_v vFadeRadius(d->fadeMaker.getRadius()); Vc::float_v vFadeStartValue(d->fadeMaker.getFadeStartValue()); Vc::float_v vFadeAFadeStart(d->fadeMaker.getAntialiasingFadeStart()); Vc::float_v vFadeAFadeCoeff(d->fadeMaker.getAntialiasingFadeCoeff()); Vc::float_v vOne(Vc::One); Vc::float_v vZero(Vc::Zero); Vc::float_v vValMax(255.f); for (int i=0; i < width; i+= Vc::float_v::size()){ Vc::float_v x_ = currentIndices - vCenterX; Vc::float_v xr = x_ * vCosa - vSinaY_; Vc::float_v yr = x_ * vSina + vCosaY_; Vc::float_v dist = sqrt(pow2(xr) + pow2(yr * vYCoeff)); // BEGIN FadeMaker needFade vectorized // follow fademaker rules for outsideMask Vc::float_m outsideMask = dist > vFadeRadius; dist(outsideMask) = vOne; Vc::float_m fadeStartMask(false); // if antialias is off, do not process if(antialiasOn){ fadeStartMask = dist > vFadeAFadeStart; dist((outsideMask ^ fadeStartMask) & fadeStartMask) = (vFadeStartValue + (dist - vFadeAFadeStart) * vFadeAFadeCoeff) / vValMax; } Vc::float_m excludeMask(outsideMask | fadeStartMask); if (!excludeMask.isFull()) { Vc::float_v valDist = dist * vDistfactor; Vc::float_v fullFade = vAlphafactor * ( d->vErf(valDist + vCenter) - d->vErf(valDist - vCenter)); Vc::float_m mask; // Mask undefined values, out of range are out of mask mask = Vc::isfinite(fullFade); fullFade.setZero(!mask); // Mask in the inner circe of the mask mask = fullFade < vZero; fullFade.setZero(mask); // 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) { const bool antialiasOn = d->fadeMaker.getAliasingEnabled(); 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 vFadeRadius(d->fadeMaker.getRadius()); Vc::float_v vFadeStartValue(d->fadeMaker.getFadeStartValue()); Vc::float_v vFadeAFadeStart(d->fadeMaker.getAntialiasingFadeStart()); Vc::float_v vFadeAFadeCoeff(d->fadeMaker.getAntialiasingFadeCoeff()); Vc::float_v vOne(Vc::One); Vc::float_v vZero(Vc::Zero); Vc::float_v vValMax(255.f); for (int i=0; i < width; i+= Vc::float_v::size()){ Vc::float_v x_ = currentIndices - vCenterX; Vc::float_v xr = x_ * vCosa - vSinaY_; Vc::float_v yr = x_ * vSina + vCosaY_; Vc::float_v dist = pow2(xr * vXCoeff) + pow2(yr * vYCoeff); // BEGIN FadeMaker needFade vectorized // follow fademaker rules for outsideMask Vc::float_m outsideMask = dist > vFadeRadius; dist(outsideMask) = vOne; Vc::float_m fadeStartMask(false); // if antialias is off, do not process if(antialiasOn){ fadeStartMask = dist > vFadeAFadeStart; dist((outsideMask ^ fadeStartMask) & fadeStartMask) = (vFadeStartValue + (dist - vFadeAFadeStart) * vFadeAFadeCoeff) / vValMax; } Vc::float_m excludeMask = outsideMask | fadeStartMask; if (!excludeMask.isFull()) { Vc::float_v valDist = dist * vCurveResolution; // truncate Vc::SimdArray vAlphaValue(valDist); Vc::float_v vFloatAlphaValue = vAlphaValue; Vc::float_v alphaValueF = valDist - vFloatAlphaValue; vCurvedData.gather(curveDataPointer,vAlphaValue); 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; -}; - -template<> void KisGaussRectangleMaskGenerator:: -FastRowProcessor::process(float* buffer, int width, float y, float cosa, float sina, - float centerX, float centerY) -{ - const bool antialiasOn = d->fadeMaker.getAliasingEnabled(); - - float y_ = y - centerY; - float sinay_ = sina * y_; - float cosay_ = cosa * y_; - - 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 vXLimit(d->fadeMaker.getXLimit()); - Vc::float_v vYLimit(d->fadeMaker.getYLimit()); - Vc::float_v vXFadeLimitStart(d->fadeMaker.getXFadeLimitStart()); - Vc::float_v vYFadeLimitStart(d->fadeMaker.getYFadeLimitStart()); - Vc::float_v vXFadeCoeff(d->fadeMaker.getXFadeCoeff()); - Vc::float_v vYFadeCoeff(d->fadeMaker.getYFadeCoeff()); - - 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; - - // BEGIN FadeMaker needFade vectorized 2D - Vc::float_v xra = abs(xr); - Vc::float_m outXMask = xr > vXLimit; - Vc::float_m outYMask = yr > vYLimit; - - Vc::float_m excludeMask(outXMask | outYMask); - vValue(excludeMask) = vOne; - - if (!excludeMask.isFull()) { - Vc::float_v fullFade = vValMax - (vAlphafactor * (d->vErf((vhalfWidth + xr) * vXFade) + d->vErf((vhalfWidth - xr) * vXFade)) - * (d->vErf((vhalfHeight + yr) * vYFade) + d->vErf((vhalfHeight - yr) * vYFade))); - // if antialias is off, do not process - Vc::float_m fadeXStartMask(false); - Vc::float_m fadeYStartMask(false); - - if(antialiasOn){ - Vc::float_v fadeValue; - Vc::SimdArray vBaseValue(fullFade); - - 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; - fullFade(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; - fullFade(fadeYStartMask) = fadeValue; - } - } - - Vc::float_m mask; - // Mask undefined values, out of range are out of mask - mask = Vc::isfinite(fullFade); - fullFade.setZero(!mask); - - // Mask in the inner circe of the mask - mask = fullFade < vZero; - fullFade.setZero(mask); - - // 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_gauss_rect_mask_generator.cpp b/libs/image/kis_gauss_rect_mask_generator.cpp index 621501a608..1f87715dda 100644 --- a/libs/image/kis_gauss_rect_mask_generator.cpp +++ b/libs/image/kis_gauss_rect_mask_generator.cpp @@ -1,149 +1,128 @@ /* * 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. */ #include //MSVC requires that Vc come first #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 #include #include "kis_fast_math.h" #include "kis_base_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" +#include "kis_antialiasing_fade_maker.h" #define M_SQRT_2 1.41421356237309504880 #ifdef Q_OS_WIN // on windows we get our erf() from boost #include #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 { return new KisGaussRectangleMaskGenerator(*this); } void KisGaussRectangleMaskGenerator::setScale(qreal scaleX, qreal scaleY) { KisMaskGenerator::setScale(scaleX, scaleY); qreal width = effectiveSrcWidth(); qreal height = effectiveSrcHeight(); qreal xfade = (1.0 - horizontalFade()/2.0) * width * 0.1; qreal yfade = (1.0 - verticalFade()/2.0) * height * 0.1; d->xfade = 1.0 / (M_SQRT_2 * xfade); d->yfade = 1.0 / (M_SQRT_2 * yfade); d->halfWidth = width * 0.5 - 2.5 * xfade; d->halfHeight = height * 0.5 - 2.5 * yfade; d->alphafactor = 255.0 / (4.0 * erf(d->halfWidth * d->xfade) * erf(d->halfHeight * d->yfade)); d->fadeMaker.setLimits(0.5 * width, 0.5 * height); } KisGaussRectangleMaskGenerator::~KisGaussRectangleMaskGenerator() { } inline quint8 KisGaussRectangleMaskGenerator::Private::value(qreal xr, qreal yr) const { return (quint8) 255 - (quint8) (alphafactor * (erf((halfWidth + xr) * xfade) + erf((halfWidth - xr) * xfade)) * (erf((halfHeight + yr) * yfade) + erf((halfHeight - yr) * yfade))); } quint8 KisGaussRectangleMaskGenerator::valueAt(qreal x, qreal y) const { if (isEmpty()) return 255; qreal xr = x; qreal yr = qAbs(y); fixRotation(xr, yr); quint8 value; if (d->fadeMaker.needFade(xr, yr, &value)) { return value; } return d->value(xr, yr); } - -bool KisGaussRectangleMaskGenerator::shouldSupersample() const -{ - return effectiveSrcWidth() < 10 || effectiveSrcHeight() < 10; -} - -bool KisGaussRectangleMaskGenerator::shouldVectorize() const -{ - return !shouldSupersample() && spikes() == 2; -} - -KisBrushMaskApplicatorBase* KisGaussRectangleMaskGenerator::applicator() -{ - return d->applicator.data(); -} - -void KisGaussRectangleMaskGenerator::resetMaskApplicator(bool forceScalar) -{ - d->applicator.reset(createOptimizedClass >(this,forceScalar)); -} diff --git a/libs/image/kis_gauss_rect_mask_generator.h b/libs/image/kis_gauss_rect_mask_generator.h index 2360a9a3dd..52ee731b57 100644 --- a/libs/image/kis_gauss_rect_mask_generator.h +++ b/libs/image/kis_gauss_rect_mask_generator.h @@ -1,54 +1,48 @@ /* * 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_H_ #define _KIS_GAUSS_RECT_MASK_GENERATOR_H_ #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); KisGaussRectangleMaskGenerator(const KisGaussRectangleMaskGenerator &rhs); ~KisGaussRectangleMaskGenerator() override; KisMaskGenerator* clone() const override; quint8 valueAt(qreal x, qreal y) const override; void setScale(qreal scaleX, qreal scaleY) override; - bool shouldSupersample() const override; - bool shouldVectorize() const override; - KisBrushMaskApplicatorBase* applicator() override; - void resetMaskApplicator(bool forceScalar); - private: struct Private; const QScopedPointer d; }; #endif diff --git a/libs/image/tests/KisMaskGeneratorBenchmark.cpp b/libs/image/tests/KisMaskGeneratorBenchmark.cpp index 578dde6316..6488fdb237 100644 --- a/libs/image/tests/KisMaskGeneratorBenchmark.cpp +++ b/libs/image/tests/KisMaskGeneratorBenchmark.cpp @@ -1,159 +1,138 @@ /* * Copyright (c) 2018 Iván Santa María * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include "KisMaskGeneratorBenchmark.h" #include "kis_mask_similarity_test.h" #include "kis_brush_mask_applicator_base.h" #include "kis_mask_generator.h" #include "kis_cubic_curve.h" #include "krita_utils.h" #include "testutil.h" class KisMaskGeneratorBenchmarkTester { public: KisMaskGeneratorBenchmarkTester(KisBrushMaskApplicatorBase *_applicatorBase, QRect _bounds) : applicatorBase(_applicatorBase) , m_bounds(_bounds) { KisFixedPaintDeviceSP m_paintDev = new KisFixedPaintDevice(m_colorSpace); m_paintDev->setRect(m_bounds); m_paintDev->initialize(255); MaskProcessingData data(m_paintDev, m_colorSpace, 0.0, 1.0, m_bounds.width() / 2.0, m_bounds.height() / 2.0,0); // Start Benchmark applicatorBase->initializeData(&data); QElapsedTimer maskGenerationTime; maskGenerationTime.start(); QBENCHMARK { applicatorBase->process(m_bounds); } } protected: const KoColorSpace *m_colorSpace = KoColorSpaceRegistry::instance()->rgb8(); QRect m_bounds; KisBrushMaskApplicatorBase *applicatorBase; KisFixedPaintDeviceSP m_paintDev; }; void KisMaskGeneratorBenchmark::testDefaultScalarMask() { QRect bounds(0,0,1000,1000); { KisCircleMaskGenerator circScalar(1000, 1.0, 0.5, 0.5, 2, true); circScalar.resetMaskApplicator(true); // Force usage of scalar backend KisMaskGeneratorBenchmarkTester(circScalar.applicator(), bounds); } } void KisMaskGeneratorBenchmark::testDefaultVectorMask() { QRect bounds(0,0,1000,1000); { KisCircleMaskGenerator circVectr(1000, 1.0, 0.5, 0.5, 2, true); KisMaskGeneratorBenchmarkTester(circVectr.applicator(), bounds); } } void KisMaskGeneratorBenchmark::testCircularGaussScalarMask() { QRect bounds(0,0,1000,1000); { KisGaussCircleMaskGenerator circScalar(1000, 1.0, 0.5, 0.5, 2, true); circScalar.setDiameter(1000); circScalar.resetMaskApplicator(true); // Force usage of scalar backend KisMaskGeneratorBenchmarkTester(circScalar.applicator(), bounds); } } void KisMaskGeneratorBenchmark::testCircularGaussVectorMask() { QRect bounds(0,0,1000,1000); { KisGaussCircleMaskGenerator circVectr(1000, 1.0, 0.5, 0.5, 2, true); circVectr.setDiameter(1000); KisMaskGeneratorBenchmarkTester(circVectr.applicator(), bounds); } } void KisMaskGeneratorBenchmark::testCircularSoftScalarMask() { QRect bounds(0,0,1000,1000); KisCubicCurve pointsCurve; pointsCurve.fromString(QString("0,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::testRectangularGaussScalarMask() -{ - QRect bounds(0,0,1000,1000); - { - KisGaussRectangleMaskGenerator circScalar(1000, 1.0, 0.5, 0.5, 2, true); -// circScalar.setDiameter(1000); - circScalar.resetMaskApplicator(true); // Force usage of scalar backend - - KisMaskGeneratorBenchmarkTester(circScalar.applicator(), bounds); - } -} -void KisMaskGeneratorBenchmark::testRectangularGaussVectorMask() -{ - QRect bounds(0,0,1000,1000); - { - KisGaussRectangleMaskGenerator circVectr(1000, 1.0, 0.5, 0.5, 2, true); -// circVectr.setDiameter(1000); - KisMaskGeneratorBenchmarkTester(circVectr.applicator(), bounds); - } -} - QTEST_MAIN(KisMaskGeneratorBenchmark) diff --git a/libs/image/tests/KisMaskGeneratorBenchmark.h b/libs/image/tests/KisMaskGeneratorBenchmark.h index 6670e13ac4..86318b1605 100644 --- a/libs/image/tests/KisMaskGeneratorBenchmark.h +++ b/libs/image/tests/KisMaskGeneratorBenchmark.h @@ -1,42 +1,39 @@ /* * Copyright (c) 2018 Iván Santa María * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KISMASKGENERATORBENCHMARK_H #define KISMASKGENERATORBENCHMARK_H #include class KisMaskGeneratorBenchmark : public QObject { Q_OBJECT private Q_SLOTS: void testDefaultScalarMask(); void testDefaultVectorMask(); void testCircularGaussScalarMask(); void testCircularGaussVectorMask(); void testCircularSoftScalarMask(); void testCircularSoftVectorMask(); - - void testRectangularGaussScalarMask(); - void testRectangularGaussVectorMask(); }; #endif // KISMASKGENERATORBENCHMARK_H diff --git a/libs/image/tests/kis_mask_similarity_test.cpp b/libs/image/tests/kis_mask_similarity_test.cpp index 1fc1aff14a..ae13ffb33d 100644 --- a/libs/image/tests/kis_mask_similarity_test.cpp +++ b/libs/image/tests/kis_mask_similarity_test.cpp @@ -1,211 +1,182 @@ /* * 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_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) { + scalarImage.save(QString(getTypeName(type) + "_scalar_mask.png"),"PNG"); + vectorImage.save(QString(getTypeName(type) +"_vector_mask.png"),"PNG"); + } // Check for differences, max errors: 0 QPoint tmpPt; QVERIFY(TestUtil::compareQImages(tmpPt,scalarImage, vectorImage, 0, 2, 0)); - - if (renderImage || QTest::currentTestFailed()) { - scalarImage.save(QString(getTypeName(type) + "_scalar_mask.png"),"PNG"); - vectorImage.save(QString(getTypeName(type) + "_vector_mask.png"),"PNG"); - } } private: QString getTypeName(MaskType type) { QString strName; switch (type) { case CIRC_GAUSS: strName = "CircGauss"; break; case CIRC_SOFT: strName = "CircSoft"; 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,500,500); { KisCircleMaskGenerator circVectr(500, 1.0, 0.5, 0.5, 2, true); KisCircleMaskGenerator circScalar(circVectr); circScalar.resetMaskApplicator(true); // Force usage of scalar backend KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), bounds, DEFAULT); } } void KisMaskSimilarityTest::testGaussCircleMask() { QRect bounds(0,0,500,500); { KisGaussCircleMaskGenerator circVectr(500, 1.0, .8, .2, 2, true); circVectr.setDiameter(500); KisGaussCircleMaskGenerator circScalar(circVectr); circScalar.resetMaskApplicator(true); // Force usage of scalar backend KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), bounds, CIRC_GAUSS); } // Exahustive test for (size_t i = 0; i <= 100; i += 3){ for (size_t j = 0; j <= 100; j += 3){ for (size_t k = 0; k <= 100; k += 15){ { KisGaussCircleMaskGenerator circVectr(500, k/100.f, i/100.f, j/100.f, 2, true); circVectr.setDiameter(500); KisGaussCircleMaskGenerator circScalar(circVectr); circScalar.resetMaskApplicator(true); // Force usage of scalar backend KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), bounds,CIRC_GAUSS,false); } } } } // end for } void KisMaskSimilarityTest::testSoftCircleMask() { QRect bounds(0,0,500,500); KisCubicCurve pointsCurve; pointsCurve.fromString(QString("0,1;1,0")); { KisCurveCircleMaskGenerator circVectr(500, 1.0, 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); } // Exahustive test for (size_t i = 0; i <= 100; i += 3){ for (size_t j = 0; j <= 100; j += 3){ for (size_t k = 0; k <= 100; k += 15){ { KisCurveCircleMaskGenerator circVectr(500, k/100.f, i/100.f, j/100.f, 2,pointsCurve, true); circVectr.setDiameter(500); KisCurveCircleMaskGenerator circScalar(circVectr); circScalar.resetMaskApplicator(true); // Force usage of scalar backend KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), bounds,CIRC_SOFT,false); } } } } // end for } -void KisMaskSimilarityTest::testGaussRectMask() -{ - QRect bounds(0,0,540,540); - { - KisGaussRectangleMaskGenerator circVectr(500, 1.0, .5, .5, 2, true); - KisGaussRectangleMaskGenerator circScalar(circVectr); - - circScalar.resetMaskApplicator(true); // Force usage of scalar backend - KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), bounds, RECT_GAUSS); - } - // Exahustive test - for (size_t i = 0; i <= 100; i += 3){ - for (size_t j = 0; j <= 100; j += 3){ - for (size_t k = 0; k <= 100; k += 15){ - { - KisGaussRectangleMaskGenerator circVectr(500, k/100.f, i/100.f, j/100.f, 2, true); - KisGaussRectangleMaskGenerator circScalar(circVectr); - - circScalar.resetMaskApplicator(true); // Force usage of scalar backend - KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), bounds,RECT_GAUSS,false); - if (QTest::currentTestFailed()) { - qDebug() << "failed at Ratio: " << k << ", fh: "<< i <<", fv: "<< j; - std::exit(1); - } - } - } } } // end for -} - QTEST_MAIN(KisMaskSimilarityTest) diff --git a/libs/image/tests/kis_mask_similarity_test.h b/libs/image/tests/kis_mask_similarity_test.h index eccfebef6d..231f2de921 100644 --- a/libs/image/tests/kis_mask_similarity_test.h +++ b/libs/image/tests/kis_mask_similarity_test.h @@ -1,35 +1,34 @@ /* * Copyright (c) 2018 Iván Santa María * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_MASK_SIMILARITY_TEST #define KIS_MASK_SIMILARITY_TEST #include class KisMaskSimilarityTest : public QObject { Q_OBJECT private Q_SLOTS: void testCircleMask(); void testGaussCircleMask(); void testSoftCircleMask(); - void testGaussRectMask(); }; #endif