diff --git a/libs/image/kis_circle_mask_generator.cpp b/libs/image/kis_circle_mask_generator.cpp index a27db05731..6c64dcf3c1 100644 --- a/libs/image/kis_circle_mask_generator.cpp +++ b/libs/image/kis_circle_mask_generator.cpp @@ -1,141 +1,146 @@ /* * Copyright (c) 2004,2007-2009 Cyrille Berger * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2012 Sven Langkamp * * 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 #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_circle_mask_generator.h" #include "kis_circle_mask_generator_p.h" #include "kis_base_mask_generator.h" #include "kis_brush_mask_applicator_factories.h" #include "kis_brush_mask_applicator_base.h" KisCircleMaskGenerator::KisCircleMaskGenerator(qreal diameter, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges) : KisMaskGenerator(diameter, ratio, fh, fv, spikes, antialiasEdges, CIRCLE, DefaultId), d(new Private) { setScale(1.0, 1.0); // store the variable locally to allow vector implementation read it easily d->copyOfAntialiasEdges = antialiasEdges; d->applicator.reset(createOptimizedClass >(this)); } KisCircleMaskGenerator::KisCircleMaskGenerator(const KisCircleMaskGenerator &rhs) : KisMaskGenerator(rhs), d(new Private(*rhs.d)) { d->applicator.reset(createOptimizedClass >(this)); } KisMaskGenerator* KisCircleMaskGenerator::clone() const { return new KisCircleMaskGenerator(*this); } void KisCircleMaskGenerator::setScale(qreal scaleX, qreal scaleY) { KisMaskGenerator::setScale(scaleX, scaleY); d->xcoef = 2.0 / effectiveSrcWidth(); d->ycoef = 2.0 / effectiveSrcHeight(); d->xfadecoef = (horizontalFade() == 0) ? 1 : (2.0 / (horizontalFade() * effectiveSrcWidth())); d->yfadecoef = (verticalFade() == 0) ? 1 : (2.0 / (verticalFade() * effectiveSrcHeight())); d->transformedFadeX = KisMaskGenerator::softness() * d->xfadecoef; d->transformedFadeY = KisMaskGenerator::softness() * d->yfadecoef; d->noFading = !d->copyOfAntialiasEdges && qFuzzyCompare(d->xcoef, d->transformedFadeX) && qFuzzyCompare(d->ycoef, d->transformedFadeY); } KisCircleMaskGenerator::~KisCircleMaskGenerator() { } bool KisCircleMaskGenerator::shouldSupersample() const { return effectiveSrcWidth() < 10 || effectiveSrcHeight() < 10; } bool KisCircleMaskGenerator::shouldVectorize() const { return !shouldSupersample() && spikes() == 2; } KisBrushMaskApplicatorBase* KisCircleMaskGenerator::applicator() { return d->applicator.data(); } quint8 KisCircleMaskGenerator::valueAt(qreal x, qreal y) const { if (isEmpty()) return 255; qreal xr = (x /*- m_xcenter*/); qreal yr = qAbs(y /*- m_ycenter*/); fixRotation(xr, yr); qreal n = norme(xr * d->xcoef, yr * d->ycoef); if (n > 1.0) return 255; // we add +1.0 to ensure correct antialiasing on the border if (antialiasEdges()) { xr = qAbs(xr) + 1.0; yr = qAbs(yr) + 1.0; } qreal nf = norme(xr * d->transformedFadeX, yr * d->transformedFadeY); if (nf < 1.0) return 0; return 255 * n * (nf - 1.0) / (nf - n); } void KisCircleMaskGenerator::setSoftness(qreal softness) { KisMaskGenerator::setSoftness(softness); qreal safeSoftnessCoeff = qreal(1.0) / qMax(qreal(0.01), softness); d->transformedFadeX = d->xfadecoef * safeSoftnessCoeff; d->transformedFadeY = d->yfadecoef * safeSoftnessCoeff; } + +void KisCircleMaskGenerator::resetMaskApplicator(bool forceScalar) +{ + d->applicator.reset(createOptimizedClass >(this,forceScalar)); +} diff --git a/libs/image/kis_circle_mask_generator.h b/libs/image/kis_circle_mask_generator.h index c6aaa04143..0fdb389d9a 100644 --- a/libs/image/kis_circle_mask_generator.h +++ b/libs/image/kis_circle_mask_generator.h @@ -1,63 +1,65 @@ /* * 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_CIRCLE_MASK_GENERATOR_H_ #define _KIS_CIRCLE_MASK_GENERATOR_H_ #include "kritaimage_export.h" #include "kis_mask_generator.h" #include /** * Create, serialize and deserialize an elliptical 8-bit mask. */ class KRITAIMAGE_EXPORT KisCircleMaskGenerator : public KisMaskGenerator { public: struct FastRowProcessor; public: KisCircleMaskGenerator(qreal radius, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges); KisCircleMaskGenerator(const KisCircleMaskGenerator &rhs); ~KisCircleMaskGenerator() override; KisMaskGenerator* clone() const override; quint8 valueAt(qreal x, qreal y) const override; bool shouldSupersample() const override; bool shouldVectorize() const override; KisBrushMaskApplicatorBase* applicator() override; void setSoftness(qreal softness) override; void setScale(qreal scaleX, qreal scaleY) 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 cd08552db2..87d068e261 100644 --- a/libs/image/tests/kis_mask_similarity_test.cpp +++ b/libs/image/tests/kis_mask_similarity_test.cpp @@ -1,124 +1,133 @@ /* * 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) { - KoColor color(Qt::black, colorSpace); - KisFixedPaintDeviceSP m_paintDev = new KisFixedPaintDevice(colorSpace); + KisFixedPaintDeviceSP m_paintDev = new KisFixedPaintDevice(m_colorSpace); m_paintDev->setRect(m_bounds); - m_paintDev->initialize(); - + m_paintDev->initialize(255); - MaskProcessingData data(m_paintDev, colorSpace, + MaskProcessingData data(m_paintDev, m_colorSpace, 0.0, 1.0, - m_bounds.width() / 2, m_bounds.height() / 2,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(colorSpace->profile()); - scalarImage.save(QString("scalar_v2.png"),"PNG"); - - -// vectorized->process(bounds); - -// QImage scalarImage = convertMaskToQImage(device, color); - -// KisBrushSP brush = initializeBrush(legacy); -// KisBrushSP vBrush = initializeBrush(vectorized); - - - -// QImage scalarImage = convertMaskToQImage(brush, color); -// QImage vectorImage = convertMaskToQImage(vBrush, color); - -// // Generate images before testing to asses visualy any difference -// scalarImage.save(QString("scalar_new.png"),"PNG"); -// vectorImage.save(QString("vector2.png"),"PNG"); - -// QPoint tmp; -// QVERIFY(TestUtil::compareQImages(tmp,scalarImage, vectorImage, 0, 5)); -// // Check error deviation between values is less than 0.05 - for (int i = 0; i < scalarImage.width(); ++i) { - for (int j = 0; j < scalarImage.height(); ++j) { - qDebug() << scalarImage.pixelColor(i,j); -// qint16 error(qFabs(scalarImage.pixelColor(i,j).alphaF() - vectorImage.pixelColor(i,j).alphaF()) * 100); -// QVERIFY(error < 5); - } - } + QImage scalarImage(m_paintDev->convertToQImage(m_colorSpace->profile())); + scalarImage.invertPixels(); + scalarImage.save(QString("scalar_mask.png"),"PNG"); + + // Start vector processing + m_paintDev->initialize(255); + vectorized->initializeData(&data); +// QVector rects = KritaUtils::splitRectIntoPatches(m_paintDev->bounds(), QSize(3, 3)); +// Q_FOREACH (const QRect &rc, rects) { +// vectorized->process(rc); +// } + vectorized->process(m_bounds); + + QImage vectorImage(m_paintDev->convertToQImage(m_colorSpace->profile())); + vectorImage.invertPixels(); + vectorImage.save(QString("vector_mask.png"),"PNG"); + + + // Check for differences, max error .5% of pixel mismatch + int tolerance(m_bounds.width() * m_bounds.height() * .005f); + // qDebug() << "tolerance: " << tolerance; + QPoint tmpPt; + QVERIFY(TestUtil::compareQImages(tmpPt,scalarImage, vectorImage, 0, tolerance)); + + // Check error deviation between values is less than 0.05 +// Development debug. +// 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; +// qDebug() << scalarImage; +// qDebug() << vectorImage; } private: -// KisBrushSP initializeBrush(KisMaskGenerator *mg){ -// KisBrushSP brush = new KisAutoBrush(mg, 1.0, 0.0); -// brush->setSpacing(0.15); -// brush->setAutoSpacing(true, 0.1); -// return brush; -// }; - -// QImage convertMaskToQImage(KisFixedPaintDeviceSP dev, KoColor color){ - //KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(colorSpace); - //brush->mask(dev, color, shape, info); -// return dev->convertToQImage(colorSpace->profile()); -// }; protected: - const KoColorSpace* colorSpace = KoColorSpaceRegistry::instance()->rgb8(); -// KisPaintInformation info = KisPaintInformation(QPointF(40.0, 10.0), 0.5); -// KisDabShape shape = KisDabShape(1.0,1.0,1.0); + const KoColorSpace* m_colorSpace = KoColorSpaceRegistry::instance()->rgb8(); KisBrushMaskApplicatorBase* legacy; KisBrushMaskApplicatorBase* vectorized; QRect m_bounds; KisFixedPaintDeviceSP m_paintDev; }; void KisMaskSimilarityTest::testCircleMask() { - QRect bounds(0,0,40,40); - KisMaskSimilarityTester( - (new KisCircleMaskGenerator(40, 1.0, 0.5, 0.5, 3, true))->applicator(), - (new KisCircleMaskGenerator(40, 1.0, 0.5, 0.5, 2, true))->applicator(), bounds); +// QRect rect500(0,0,500,500); +// { +// KisCircleMaskGenerator circScalar(250, 1.0, 0.5, 0.5, 2, true); +// KisCircleMaskGenerator circVectr(250, 1.0, 0.5, 0.5, 2, true); +// KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), rect500); +// } + + QRect rect40(0,0,40,40); + { + KisCircleMaskGenerator circVectr(20, 1.0, 0.5, 0.5, 2, true); + KisCircleMaskGenerator circScalar(circVectr); + circScalar.resetMaskApplicator(true); + + KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), rect40); + circVectr.resetMaskApplicator(true); + KisMaskSimilarityTester(circScalar.applicator(), circVectr.applicator(), rect40); + } } //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); //} QTEST_MAIN(KisMaskSimilarityTest) diff --git a/libs/pigment/compositeops/KoVcMultiArchBuildSupport.h b/libs/pigment/compositeops/KoVcMultiArchBuildSupport.h index 2bdd069a74..f81fbea0f6 100644 --- a/libs/pigment/compositeops/KoVcMultiArchBuildSupport.h +++ b/libs/pigment/compositeops/KoVcMultiArchBuildSupport.h @@ -1,112 +1,122 @@ /* * Copyright (c) 2012 Dmitry Kazakov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef __KOVCMULTIARCHBUILDSUPPORT_H #define __KOVCMULTIARCHBUILDSUPPORT_H #include "config-vc.h" #ifdef HAVE_VC #if defined(__clang__) #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 #include #if defined _MSC_VER #pragma warning ( pop ) #endif #else /* HAVE_VC */ namespace Vc { enum Implementation /*: std::uint_least32_t*/ { ScalarImpl, }; class CurrentImplementation { public: static constexpr Implementation current() { return static_cast(ScalarImpl); } }; } #endif /* HAVE_VC */ #include #include #include #include template typename FactoryType::ReturnType createOptimizedClass(typename FactoryType::ParamType param) { static bool isConfigInitialized = false; static bool useVectorization = true; if (!isConfigInitialized) { KConfigGroup cfg = KSharedConfig::openConfig()->group(""); useVectorization = !cfg.readEntry("amdDisableVectorWorkaround", false); isConfigInitialized = true; } if (!useVectorization) { qWarning() << "WARNING: vector instructions disabled by \'amdDisableVectorWorkaround\' option!"; return FactoryType::template create(param); } #ifdef HAVE_VC /** * We use SSE2, SSSE3, SSE4.1, AVX and AVX2. * The rest are integer and string instructions mostly. * * TODO: Add FMA3/4 when it is adopted by Vc */ if (Vc::isImplementationSupported(Vc::AVX2Impl)) { return FactoryType::template create(param); } else if (Vc::isImplementationSupported(Vc::AVXImpl)) { return FactoryType::template create(param); } else if (Vc::isImplementationSupported(Vc::SSE41Impl)) { return FactoryType::template create(param); } else if (Vc::isImplementationSupported(Vc::SSSE3Impl)) { return FactoryType::template create(param); } else if (Vc::isImplementationSupported(Vc::SSE2Impl)) { return FactoryType::template create(param); } else { #endif return FactoryType::template create(param); #ifdef HAVE_VC } #endif } +template +typename FactoryType::ReturnType +createOptimizedClass(typename FactoryType::ParamType param, bool forceScalarImplemetation) +{ + if(forceScalarImplemetation){ + return FactoryType::template create(param); + } + return createOptimizedClass(param); +} + #endif /* __KOVCMULTIARCHBUILDSUPPORT_H */