File indexing completed on 2024-07-14 04:37:04

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"