diff --git a/libs/image/kis_gauss_circle_mask_generator.cpp b/libs/image/kis_gauss_circle_mask_generator.cpp index 155e41e47b..91e08a7cd7 100644 --- a/libs/image/kis_gauss_circle_mask_generator.cpp +++ b/libs/image/kis_gauss_circle_mask_generator.cpp @@ -1,143 +1,150 @@ /* * 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 #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_circle_mask_generator.h" #include "kis_gauss_circle_mask_generator_p.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 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 { return new KisGaussCircleMaskGenerator(*this); } void KisGaussCircleMaskGenerator::setScale(qreal scaleX, qreal scaleY) { KisMaskGenerator::setScale(scaleX, scaleY); d->ycoef = scaleX / (scaleY * ratio()); d->distfactor = M_SQRT_2 * 12500.0 / (6761.0 * d->fade * effectiveSrcWidth() / 2.0); d->fadeMaker.setRadius(0.5 * effectiveSrcWidth()); } KisGaussCircleMaskGenerator::~KisGaussCircleMaskGenerator() { } inline quint8 KisGaussCircleMaskGenerator::Private::value(qreal dist) const { dist *= distfactor; quint8 ret = alphafactor * (erf(dist + center) - erf(dist - center)); return (quint8) 255 - ret; } bool KisGaussCircleMaskGenerator::shouldSupersample() const { return effectiveSrcWidth() < 10 || effectiveSrcHeight() < 10; } 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; qreal xr = x; qreal yr = qAbs(y); fixRotation(xr, yr); qreal dist = sqrt(norme(xr, yr * d->ycoef)); quint8 value; if (d->fadeMaker.needFade(dist, &value)) { return value; } return d->value(dist); } + +void KisGaussCircleMaskGenerator::resetMaskApplicator(bool forceScalar) +{ + d->applicator.reset(createOptimizedClass >(this,forceScalar)); +} diff --git a/libs/image/kis_gauss_circle_mask_generator.h b/libs/image/kis_gauss_circle_mask_generator.h index 46632c47a1..1872641f48 100644 --- a/libs/image/kis_gauss_circle_mask_generator.h +++ b/libs/image/kis_gauss_circle_mask_generator.h @@ -1,62 +1,64 @@ /* * 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_MASK_GENERATOR_H_ #define _KIS_GAUSS_MASK_GENERATOR_H_ #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); KisGaussCircleMaskGenerator(const KisGaussCircleMaskGenerator &rhs); ~KisGaussCircleMaskGenerator() 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: qreal norme(qreal a, qreal b) const { return a*a + b*b; } private: struct Private; const QScopedPointer d; }; #endif diff --git a/libs/image/tests/kis_mask_similarity_test.cpp b/libs/image/tests/kis_mask_similarity_test.cpp index 9c6618afae..1e6885bbfb 100644 --- a/libs/image/tests/kis_mask_similarity_test.cpp +++ b/libs/image/tests/kis_mask_similarity_test.cpp @@ -1,115 +1,120 @@ /* * 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 "kis_brush_mask_applicator_base.h" #include "kis_mask_generator.h" #include "krita_utils.h" class KisMaskSimilarityTester { public: KisMaskSimilarityTester(KisBrushMaskApplicatorBase *_legacy, KisBrushMaskApplicatorBase *_vectorized, QRect _bounds) : legacy(_legacy) , vectorized(_vectorized) , m_bounds(_bounds) { KisFixedPaintDeviceSP m_paintDev = new KisFixedPaintDevice(m_colorSpace); m_paintDev->setRect(m_bounds); m_paintDev->initialize(255); MaskProcessingData data(m_paintDev, m_colorSpace, 0.0, 1.0, m_bounds.width() / 2.0, m_bounds.height() / 2.0,0); // Start legacy scalar processing legacy->initializeData(&data); legacy->process(m_bounds); QImage scalarImage(m_paintDev->convertToQImage(m_colorSpace->profile())); scalarImage.invertPixels(); // Make pixel color black scalarImage.save(QString("scalar_mask.png"),"PNG"); // Start vector processing m_paintDev->initialize(255); vectorized->initializeData(&data); vectorized->process(m_bounds); QImage vectorImage(m_paintDev->convertToQImage(m_colorSpace->profile())); vectorImage.invertPixels(); // Make pixel color black vectorImage.save(QString("vector_mask.png"),"PNG"); // Development debug. // Count number of identical pixels // int equals = 0; // for (int i = 0; i < scalarImage.width(); ++i) { // for (int j = 0; j < scalarImage.height(); ++j) { // if (scalarImage.pixelColor(i,j) == vectorImage.pixelColor(i,j)){ // equals++; // } else { // qDebug() << scalarImage.pixelColor(i,j) << " " << vectorImage.pixelColor(i,j); // } // } // } // qDebug() << "Equal Pixels: " << equals; // Check for differences, max error .5% of pixel mismatch int tolerance = m_bounds.width() * m_bounds.height() * .005f; QPoint tmpPt; QVERIFY(TestUtil::compareQImages(tmpPt,scalarImage, vectorImage, 0, 1, tolerance)); } private: protected: const KoColorSpace *m_colorSpace = KoColorSpaceRegistry::instance()->rgb8(); KisBrushMaskApplicatorBase *legacy; KisBrushMaskApplicatorBase *vectorized; QRect m_bounds; KisFixedPaintDeviceSP m_paintDev; }; void KisMaskSimilarityTest::testCircleMask() { QRect bounds(0,0,500,500); { KisCircleMaskGenerator circVectr(480, 1.0, 0.5, 0.5, 2, true); KisCircleMaskGenerator circScalar(circVectr); circScalar.resetMaskApplicator(true); // Force usage of scalar backend KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), bounds); } } -//void KisMaskSimilarityTest::testGaussCircleMask() -//{ -// QRect bounds(0,0,40,40); -// KisMaskSimilarityTester( -// (new KisGaussCircleMaskGenerator(40, 1.0, 0.5, 0.5, 3, true))->applicator(), -// (new KisGaussCircleMaskGenerator(40, 1.0, 0.5, 0.5, 2, true))->applicator(), bounds); -//} +void KisMaskSimilarityTest::testGaussCircleMask() +{ + QRect bounds(0,0,500,500); + { + KisGaussCircleMaskGenerator circVectr(480, 1.0, 0.5, 0.5, 2, true); + circVectr.setDiameter(480); + KisGaussCircleMaskGenerator circScalar(circVectr); + + circScalar.resetMaskApplicator(true); // Force usage of scalar backend + KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), bounds); + } +} QTEST_MAIN(KisMaskSimilarityTest) diff --git a/libs/image/tests/kis_mask_similarity_test.h b/libs/image/tests/kis_mask_similarity_test.h index 045a05e8fd..2f3c9f873e 100644 --- a/libs/image/tests/kis_mask_similarity_test.h +++ b/libs/image/tests/kis_mask_similarity_test.h @@ -1,33 +1,33 @@ /* * 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 testGaussCircleMask(); }; #endif