File indexing completed on 2025-04-13 07:26:14
0001 // SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com> 0002 // SPDX-License-Identifier: BSD-2-Clause OR MIT 0003 0004 // First included header is the public header of the class we are testing; 0005 // this forces the header to be self-contained. 0006 #include "gradientimageparameters.h" 0007 0008 #include "asyncimagerenderthread.h" 0009 #include "lchadouble.h" 0010 #include "rgbcolorspacefactory.h" 0011 #include <qglobal.h> 0012 #include <qobject.h> 0013 #include <qscopedpointer.h> 0014 #include <qsharedpointer.h> 0015 #include <qtest.h> 0016 #include <qtestcase.h> 0017 #include <qvariant.h> 0018 #include <qwidget.h> 0019 0020 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0021 #include <qtmetamacros.h> 0022 #else 0023 #include <qobjectdefs.h> 0024 #include <qstring.h> 0025 #endif 0026 0027 class TestGradientSnippetClass : public QWidget 0028 { 0029 Q_OBJECT 0030 public: 0031 // A constructor that is clazy-conform 0032 explicit TestGradientSnippetClass(QWidget *parent = nullptr) 0033 : QWidget(parent) 0034 { 0035 } 0036 void testSnippet01() 0037 { 0038 //! [GradientImage HiDPI usage] 0039 PerceptualColor::GradientImageParameters exampleParameters; 0040 exampleParameters.rgbColorSpace = // 0041 PerceptualColor::RgbColorSpaceFactory::createSrgb(); 0042 // These functions expects an int 0043 // value. static_cast<int> will round 0044 // down, which is the desired behaviour 0045 // here. (Rounding up would mean one 0046 // more physical pixel, and on some Qt 0047 // styles this would fail.) 0048 exampleParameters.setGradientLength( // 0049 static_cast<int>(100 * devicePixelRatioF())); 0050 exampleParameters.setGradientThickness( // 0051 static_cast<int>(100 * devicePixelRatioF())); 0052 PerceptualColor::LchaDouble firstColor; 0053 firstColor.h = 10; 0054 firstColor.l = 20; 0055 firstColor.c = 30; 0056 firstColor.a = 0.4; 0057 exampleParameters.setFirstColor(firstColor); 0058 PerceptualColor::LchaDouble secondColor; 0059 secondColor.h = 50; 0060 secondColor.l = 60; 0061 secondColor.c = 25; 0062 secondColor.a = 0.9; 0063 exampleParameters.setSecondColor( // 0064 secondColor); 0065 exampleParameters.setDevicePixelRatioF( // 0066 devicePixelRatioF()); 0067 //! [GradientImage HiDPI usage] 0068 } 0069 }; 0070 0071 namespace PerceptualColor 0072 { 0073 class RgbColorSpace; 0074 0075 class TestGradientImageParameters : public QObject 0076 { 0077 Q_OBJECT 0078 0079 public: 0080 explicit TestGradientImageParameters(QObject *parent = nullptr) 0081 : QObject(parent) 0082 { 0083 } 0084 0085 private: 0086 QSharedPointer<PerceptualColor::RgbColorSpace> m_rgbColorSpace = RgbColorSpaceFactory::createSrgb(); 0087 0088 private Q_SLOTS: 0089 void initTestCase() 0090 { 0091 // Called before the first test function is executed 0092 } 0093 0094 void cleanupTestCase() 0095 { 0096 // Called after the last test function was executed 0097 } 0098 0099 void init() 0100 { 0101 // Called before each test function is executed 0102 } 0103 0104 void cleanup() 0105 { 0106 // Called after every test function 0107 } 0108 0109 void testConstructorDestructor() 0110 { 0111 // Constructor and destructor do not crash. 0112 GradientImageParameters myGradient; 0113 } 0114 0115 void testCompletlyNormalizedAndBounded() 0116 { 0117 GradientImageParameters myGradient; 0118 myGradient.rgbColorSpace = m_rgbColorSpace; 0119 LchaDouble lchaTestValue; 0120 0121 // Test values that are too high 0122 lchaTestValue.l = 500; 0123 lchaTestValue.c = 20; 0124 lchaTestValue.h = 361; 0125 lchaTestValue.a = 5; 0126 QCOMPARE(myGradient.completlyNormalizedAndBounded(lchaTestValue).l, 100); 0127 QCOMPARE(myGradient.completlyNormalizedAndBounded(lchaTestValue).c, 20); 0128 QCOMPARE(myGradient.completlyNormalizedAndBounded(lchaTestValue).h, 1); 0129 QCOMPARE(myGradient.completlyNormalizedAndBounded(lchaTestValue).a, 1); 0130 0131 // Test value that are too low 0132 lchaTestValue.l = -500; 0133 lchaTestValue.c = -20; 0134 lchaTestValue.h = -1; 0135 lchaTestValue.a = -5; 0136 QCOMPARE(myGradient.completlyNormalizedAndBounded(lchaTestValue).l, 0); 0137 QCOMPARE(myGradient.completlyNormalizedAndBounded(lchaTestValue).c, 0138 20 // Normalised to positive value (hue is changed by 180°) 0139 ); 0140 QCOMPARE(myGradient.completlyNormalizedAndBounded(lchaTestValue).h, 0141 179 // Changed by 180° because of the negative chroma value 0142 ); 0143 QCOMPARE(myGradient.completlyNormalizedAndBounded(lchaTestValue).a, 0); 0144 0145 // Test value that much too low 0146 lchaTestValue.l = 50; 0147 lchaTestValue.c = 20; 0148 lchaTestValue.h = -361; 0149 lchaTestValue.a = 0.5; 0150 QCOMPARE(myGradient.completlyNormalizedAndBounded(lchaTestValue).l, 50); 0151 QCOMPARE(myGradient.completlyNormalizedAndBounded(lchaTestValue).c, 20); 0152 QCOMPARE(myGradient.completlyNormalizedAndBounded(lchaTestValue).h, 359); 0153 QCOMPARE(myGradient.completlyNormalizedAndBounded(lchaTestValue).a, 0.5); 0154 0155 // Test that hue is preserved also if chroma is zero 0156 lchaTestValue.l = 50; 0157 lchaTestValue.c = 0; 0158 lchaTestValue.h = 50; 0159 lchaTestValue.a = 0.5; 0160 QCOMPARE(myGradient.completlyNormalizedAndBounded(lchaTestValue).l, 50); 0161 QCOMPARE(myGradient.completlyNormalizedAndBounded(lchaTestValue).c, 0); 0162 QCOMPARE(myGradient.completlyNormalizedAndBounded(lchaTestValue).h, 50); 0163 QCOMPARE(myGradient.completlyNormalizedAndBounded(lchaTestValue).a, 0.5); 0164 } 0165 void testUpdateSecondColor() 0166 { 0167 GradientImageParameters myGradient; 0168 myGradient.rgbColorSpace = m_rgbColorSpace; 0169 myGradient.m_firstColorCorrected = LchaDouble{50, 0, 30, 0.5}; 0170 myGradient.m_secondColorCorrectedAndAltered = LchaDouble{50, 0, 40, 0.5}; 0171 myGradient.updateSecondColor(); 0172 qreal absoluteDifference = qAbs(myGradient.m_firstColorCorrected.h - myGradient.m_secondColorCorrectedAndAltered.h); 0173 QVERIFY2(absoluteDifference <= 180, "Verify that the hue difference is 0° ≤ difference ≤ 180°."); 0174 myGradient.m_secondColorCorrectedAndAltered = LchaDouble{50, 0, 240, 0.5}; 0175 myGradient.updateSecondColor(); 0176 QVERIFY2(qAbs(myGradient.m_firstColorCorrected.h - myGradient.m_secondColorCorrectedAndAltered.h) <= 180, 0177 "Verify that the hue difference is 0° ≤ difference ≤ 180°."); 0178 myGradient.m_secondColorCorrectedAndAltered = LchaDouble{50, 0, 540, 0.5}; 0179 myGradient.updateSecondColor(); 0180 QVERIFY2(qAbs(myGradient.m_firstColorCorrected.h - myGradient.m_secondColorCorrectedAndAltered.h) <= 180, 0181 "Verify that the hue difference is 0° ≤ difference ≤ 180°."); 0182 myGradient.m_secondColorCorrectedAndAltered = LchaDouble{50, 0, -240, 0.5}; 0183 myGradient.updateSecondColor(); 0184 QVERIFY2(qAbs(myGradient.m_firstColorCorrected.h - myGradient.m_secondColorCorrectedAndAltered.h) <= 180, 0185 "Verify that the hue difference is 0° ≤ difference ≤ 180°."); 0186 } 0187 0188 void testGetImage() 0189 { 0190 GradientImageParameters myGradient; 0191 myGradient.rgbColorSpace = m_rgbColorSpace; 0192 QScopedPointer<AsyncImageRenderThread> callbackObject{ 0193 // 0194 new AsyncImageRenderThread(GradientImageParameters::render) // 0195 }; 0196 // Should not crash also when values are not initialized. 0197 myGradient.render(QVariant::fromValue(myGradient), // 0198 *callbackObject); 0199 } 0200 0201 void testColorFromValue() 0202 { 0203 GradientImageParameters myGradient; 0204 myGradient.rgbColorSpace = m_rgbColorSpace; 0205 myGradient.m_firstColorCorrected = LchaDouble{50, 0, 30, 0.5}; 0206 myGradient.m_secondColorCorrectedAndAltered = LchaDouble{60, 10, 20, 0.4}; 0207 LchaDouble middleColor = myGradient.colorFromValue(0.5); 0208 QCOMPARE(middleColor.l, 55); 0209 QCOMPARE(middleColor.c, 5); 0210 QCOMPARE(middleColor.h, 25); 0211 QCOMPARE(middleColor.a, 0.45); 0212 } 0213 0214 void testSetDevicelPixelRatioF() 0215 { 0216 GradientImageParameters myGradient; 0217 myGradient.rgbColorSpace = m_rgbColorSpace; 0218 myGradient.setGradientLength(20); 0219 myGradient.setGradientThickness(10); 0220 // Should not crash: 0221 myGradient.setDevicePixelRatioF(1.25); 0222 myGradient.setDevicePixelRatioF(1.5); 0223 } 0224 0225 void testSetGradientLength() 0226 { 0227 GradientImageParameters myGradient; 0228 myGradient.rgbColorSpace = m_rgbColorSpace; 0229 // Should not crash: 0230 myGradient.setGradientLength(20); 0231 } 0232 0233 void testSetGradientThickness() 0234 { 0235 GradientImageParameters myGradient; 0236 myGradient.rgbColorSpace = m_rgbColorSpace; 0237 // Should not crash: 0238 myGradient.setGradientThickness(10); 0239 } 0240 0241 void testSnippet01() 0242 { 0243 TestGradientSnippetClass mySnippets; 0244 mySnippets.testSnippet01(); 0245 } 0246 }; 0247 0248 } // namespace PerceptualColor 0249 0250 QTEST_MAIN(PerceptualColor::TestGradientImageParameters) 0251 0252 // The following “include” is necessary because we do not use a header file: 0253 #include "testgradientimageparameters.moc"