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)