File indexing completed on 2024-12-22 04:10:20

0001 /*
0002  *  SPDX-FileCopyrightText: 2018 Iván Santa María <ghevan@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_mask_similarity_test.h"
0008 
0009 #include <simpletest.h>
0010 #include <QPointF>
0011 
0012 #include <KoColor.h>
0013 #include <testutil.h>
0014 
0015 #include "kis_brush_mask_applicator_base.h"
0016 #include "kis_mask_generator.h"
0017 #include "kis_cubic_curve.h"
0018 #include "krita_utils.h"
0019 
0020 #include "config-limit-long-tests.h"
0021 
0022 #ifdef LIMIT_LONG_TESTS
0023 #define RATIO_STEP 50
0024 #define FADE_STEP 25
0025 #else /* LIMIT_LONG_TESTS */
0026 #define RATIO_STEP 20
0027 #define FADE_STEP 5
0028 #endif /* LIMIT_LONG_TESTS */
0029 
0030 
0031 enum MaskType {
0032     DEFAULT, CIRC_GAUSS, CIRC_SOFT, RECT, RECT_GAUSS, RECT_SOFT, STAMP
0033 };
0034 
0035 class KisMaskSimilarityTester
0036 {
0037 
0038 public:
0039     KisMaskSimilarityTester(KisBrushMaskApplicatorBase *_legacy, KisBrushMaskApplicatorBase *_vectorized,  QRect _bounds, MaskType type)
0040         : legacy(_legacy)
0041         , vectorized(_vectorized)
0042         , m_bounds(_bounds)
0043     {
0044         KisFixedPaintDeviceSP m_paintDev = new KisFixedPaintDevice(m_colorSpace);
0045         m_paintDev->setRect(m_bounds);
0046         m_paintDev->initialize(255);
0047 
0048         MaskProcessingData data(m_paintDev, m_colorSpace,
0049                                 nullptr,
0050                                 0.0, 1.0,
0051                                 m_bounds.width() / 2.0, m_bounds.height() / 2.0,0);
0052 
0053         // Start legacy scalar processing
0054         legacy->initializeData(&data);
0055         legacy->process(m_bounds);
0056 
0057         QImage scalarImage(m_paintDev->convertToQImage(m_colorSpace->profile()));
0058         scalarImage.invertPixels(); // Make pixel color black
0059 
0060         // Start vector processing
0061         m_paintDev->initialize(255);
0062         vectorized->initializeData(&data);
0063         vectorized->process(m_bounds);
0064 
0065         QImage vectorImage(m_paintDev->convertToQImage(m_colorSpace->profile()));
0066         vectorImage.invertPixels(); // Make pixel color black
0067 
0068         // Check for differences, max errors: 0
0069         QPoint tmpPt;
0070         if (!TestUtil::compareQImages(tmpPt,scalarImage, vectorImage, 0, 2, 0)) {
0071             scalarImage.save(QString(getTypeName(type) + "_scalar_mask.png"),"PNG");
0072             vectorImage.save(QString(getTypeName(type) + "_vector_mask.png"),"PNG");
0073 
0074             QFAIL(QString("Masks differ! first different pixel: %1,%2 \n").arg(tmpPt.x()).arg(tmpPt.y()).toLatin1());
0075         }
0076 
0077     }
0078 
0079 
0080     static bool exhaustiveTest(QRect bounds, MaskType type) {
0081         // Exhaustive test
0082 
0083         for (size_t i = 0; i <= 100; i += FADE_STEP){
0084             for (size_t j = 0; j <= 100; j += FADE_STEP){
0085                 for (size_t k = 0; k <= 100; k += RATIO_STEP){
0086 
0087                 switch (type) {
0088                 case CIRC_GAUSS:
0089                     {
0090                     KisGaussCircleMaskGenerator bCircVectr(499.5, k/100.f, i/100.f, j/100.f, 2, true);
0091                     bCircVectr.setDiameter(499.5);
0092                     KisGaussCircleMaskGenerator bCircScalar(bCircVectr);
0093                     bCircScalar.setMaskScalarApplicator(); // Force usage of
0094                                                            // scalar backend
0095 
0096                     KisMaskSimilarityTester(bCircScalar.applicator(), bCircVectr.applicator(), bounds,type);
0097                     break;
0098                     }
0099                 case CIRC_SOFT:
0100                     {
0101                     const KisCubicCurve pointsCurve(QString("0,1;1,0"));
0102                     KisCurveCircleMaskGenerator bCircVectr(499.5, k/100.f, i/100.f, j/100.f, 2, pointsCurve, true);
0103                     bCircVectr.setDiameter(499.5);
0104                     KisCurveCircleMaskGenerator bCircScalar(bCircVectr);
0105                     bCircScalar.setMaskScalarApplicator(); // Force usage of
0106                                                            // scalar backend
0107 
0108                     KisMaskSimilarityTester(bCircScalar.applicator(), bCircVectr.applicator(), bounds,type);
0109                     break;
0110                     }
0111                 case RECT:
0112                     {
0113                     KisRectangleMaskGenerator bCircVectr(499.5, k/100.f, i/100.f, j/100.f, 2, true);
0114                     KisRectangleMaskGenerator bCircScalar(bCircVectr);
0115                     bCircScalar.setMaskScalarApplicator(); // Force usage of
0116                                                            // scalar backend
0117 
0118                     KisMaskSimilarityTester(bCircScalar.applicator(), bCircVectr.applicator(), bounds,type);
0119                     break;
0120 
0121                     }
0122                 case RECT_GAUSS:
0123                     {
0124                     KisGaussRectangleMaskGenerator bCircVectr(499.5, k/100.f, i/100.f, j/100.f, 2, true);
0125                     KisGaussRectangleMaskGenerator bCircScalar(bCircVectr);
0126                     bCircScalar.setMaskScalarApplicator(); // Force usage of
0127                                                            // scalar backend
0128 
0129                     KisMaskSimilarityTester(bCircScalar.applicator(), bCircVectr.applicator(), bounds,type);
0130                     break;
0131 
0132                     }
0133                 case RECT_SOFT:
0134                     {
0135                     const KisCubicCurve pointsCurve(QString("0,1;1,0"));
0136                     KisCurveRectangleMaskGenerator bCircVectr(499.5, k/100.f, i/100.f, j/100.f, 2, pointsCurve, true);
0137                     KisCurveRectangleMaskGenerator bCircScalar(bCircVectr);
0138                     bCircScalar.setMaskScalarApplicator(); // Force usage of
0139                                                            // scalar backend
0140 
0141                     KisMaskSimilarityTester(bCircScalar.applicator(), bCircVectr.applicator(), bounds,type);
0142                     break;
0143                     }
0144                 default:
0145                     {
0146                     break;
0147                     }
0148                 }
0149 
0150                 if (QTest::currentTestFailed()) {
0151                     QWARN(QString("Mask features: Ratio=%1, hfade=%2, vfade=%3 \n")
0152                           .arg(k/100.f,0,'g',2).arg(i/100.f,0,'g',2).arg(j/100.f,0,'g',2).toLatin1());
0153                     return false;
0154                 }
0155 
0156         } } } // end for
0157         return true;
0158     }
0159 
0160     template <typename MaskGenerator>
0161     static void runMaskGenTest(MaskGenerator& generator, MaskType type) {
0162         QRect bounds(0,0,700,700);
0163         generator.setDiameter(499.5);
0164         MaskGenerator scalarGenerator(generator);
0165 
0166         scalarGenerator
0167             .setMaskScalarApplicator(); // Force usage of scalar backend
0168         KisMaskSimilarityTester(scalarGenerator.applicator(), generator.applicator(), bounds, type);
0169 
0170         // KisMaskSimilarityTester::exhaustiveTest(bounds,type);
0171     }
0172 
0173 private:
0174     QString getTypeName(MaskType type) {
0175 
0176         QString strName;
0177         switch (type) {
0178         case CIRC_GAUSS:
0179             strName = "CircGauss";
0180             break;
0181         case CIRC_SOFT:
0182             strName = "CircSoft";
0183             break;
0184         case RECT:
0185             strName = "Rect";
0186             break;
0187         case RECT_GAUSS:
0188             strName = "RectGauss";
0189             break;
0190         case RECT_SOFT:
0191             strName = "RectSoft";
0192             break;
0193         case STAMP:
0194             strName = "Stamp";
0195             break;
0196         default:
0197             strName = "Default";
0198             break;
0199         }
0200         return strName;
0201     }
0202 
0203 protected:
0204     const KoColorSpace *m_colorSpace = KoColorSpaceRegistry::instance()->rgb8();
0205 
0206     KisBrushMaskApplicatorBase *legacy;
0207     KisBrushMaskApplicatorBase *vectorized;
0208     QRect m_bounds;
0209     KisFixedPaintDeviceSP m_paintDev;
0210 };
0211 
0212 
0213 void KisMaskSimilarityTest::testCircleMask()
0214 {
0215     KisCircleMaskGenerator generator(499.5, 0.2, 0.5, 0.5, 2, true);
0216     qDebug() << generator.id() << generator.name();
0217     KisMaskSimilarityTester::runMaskGenTest(generator,DEFAULT);
0218 }
0219 
0220 void KisMaskSimilarityTest::testGaussCircleMask()
0221 {
0222     KisGaussCircleMaskGenerator generator(499.5, 0.2, 1, 1, 2, true);
0223     KisMaskSimilarityTester::runMaskGenTest(generator,CIRC_GAUSS);
0224 }
0225 
0226 void KisMaskSimilarityTest::testSoftCircleMask()
0227 {
0228     const KisCubicCurve pointsCurve(QString("0,1;1,0"));
0229     KisCurveCircleMaskGenerator generator(499.5, 0.2, 0.5, 0.5, 2, pointsCurve,true);
0230     KisMaskSimilarityTester::runMaskGenTest(generator,CIRC_SOFT);
0231 }
0232 
0233 void KisMaskSimilarityTest::testRectMask()
0234 {
0235     KisRectangleMaskGenerator generator(499.5, 0.1, 0.5, 0.5, 2, false);
0236     KisMaskSimilarityTester::runMaskGenTest(generator,RECT);
0237 }
0238 
0239 void KisMaskSimilarityTest::testGaussRectMask()
0240 {
0241     KisGaussRectangleMaskGenerator generator(499.5, 0.2, 0.5, 0.2, 2, true);
0242     KisMaskSimilarityTester::runMaskGenTest(generator,RECT_GAUSS);
0243 }
0244 
0245 void KisMaskSimilarityTest::testSoftRectMask()
0246 {
0247     const KisCubicCurve pointsCurve(QString("0,1;1,0"));
0248     KisCurveRectangleMaskGenerator generator(499.5, 0.2, 0.5, 0.2, 2, pointsCurve, true);
0249     KisMaskSimilarityTester::runMaskGenTest(generator,RECT_SOFT);
0250 }
0251 
0252 SIMPLE_TEST_MAIN(KisMaskSimilarityTest)