From 44dd56542a6c44d2b05b08490cff88b0ff062fe5 Mon Sep 17 00:00:00 2001 From: "Vogel, Rick" Date: Mon, 20 Mar 2023 11:07:37 +0100 Subject: [PATCH] add hex literal and tests --- qskinny.pro | 4 +- src/common/QskRgbValue.h | 175 +++++++++++++++++++++++++++++++++- tests/test_QskRgbLiterals.cpp | 109 +++++++++++++++++++++ tests/test_QskRgbLiterals.h | 14 +++ tests/tests.pro | 12 +++ tests/tests_main.cpp | 4 + tests/tests_main.h | 0 7 files changed, 312 insertions(+), 6 deletions(-) create mode 100644 tests/test_QskRgbLiterals.cpp create mode 100644 tests/test_QskRgbLiterals.h create mode 100644 tests/tests.pro create mode 100644 tests/tests_main.cpp create mode 100644 tests/tests_main.h diff --git a/qskinny.pro b/qskinny.pro index 7a41eef1..341fc61c 100644 --- a/qskinny.pro +++ b/qskinny.pro @@ -8,7 +8,8 @@ SUBDIRS = \ tools \ support \ examples \ - playground + playground \ + tests OTHER_FILES = \ doc/Doxyfile \ @@ -25,3 +26,4 @@ tools.depends = src support.depends = src skins examples.depends = tools support skins qmlexport playground.depends = tools support skins qmlexport +tests.depends = src diff --git a/src/common/QskRgbValue.h b/src/common/QskRgbValue.h index ecfcc87f..bb9960c9 100644 --- a/src/common/QskRgbValue.h +++ b/src/common/QskRgbValue.h @@ -333,9 +333,52 @@ namespace QskRgb namespace literals { + template< QRgb V > + constexpr QRgb parse_hex_literal_h() + { + return V; + } + + template< QRgb V, char C, char... Cs > + constexpr QRgb parse_hex_literal_h() + { + constexpr auto is_0_9 = C >= '0' && C <= '9'; + constexpr auto is_a_f = C >= 'a' && C <= 'f'; + constexpr auto is_A_F = C >= 'A' && C <= 'F'; + + static_assert( is_0_9 || is_a_f || is_A_F, "invalid hex character" ); + + if constexpr ( is_0_9 ) + { + return parse_hex_literal_h< ( V << 4 ) | ( C - '0' ), Cs... >(); + } + if constexpr ( is_a_f ) + { + return parse_hex_literal_h< ( V << 4 ) | ( C - 'a' + 10 ), Cs... >(); + } + if constexpr ( is_A_F ) + { + return parse_hex_literal_h< ( V << 4 ) | ( C - 'A' + 10 ), Cs... >(); + } + } + + template< char C, char... Cs > + constexpr QRgb parse_hex_literal_x() + { + static_assert( C == 'x', "invalid hex prefix character" ); + return parse_hex_literal_h< 0, Cs... >(); + } + + template< char C, char... Cs > + constexpr QRgb parse_hex_literal_0() + { + static_assert( C == '0', "invalid hex prefix character" ); + return parse_hex_literal_x< Cs... >(); + } + namespace qrgb { - // converts a hex string from '#RRGGBB[AA]' to '0x[AA]RRGGBB' integer + // converts a hex string from '#RRGGBB[AA]' to '0xAARRGGBB' integer QSK_EXPORT constexpr QRgb operator""_rgba( const char* const str, const size_t len ) noexcept { @@ -348,7 +391,7 @@ namespace QskRgb return qRgba( r, g, b, a ); } - // converts a hex string from '#[AA]RRGGBB' to '0x[AA]RRGGBB' integer + // converts a hex string from '#[AA]RRGGBB' to '0xAARRGGBB' integer QSK_EXPORT constexpr QRgb operator""_argb( const char* const str, const size_t len ) noexcept { @@ -361,14 +404,82 @@ namespace QskRgb return qRgba( r, g, b, a ); } -#define QSK_CHECK_CONSTEXPR_LITERAL 1 -#ifdef QSK_CHECK_CONSTEXPR_LITERAL + // converts a hex literal from '0xRRGGBB[AA]' to '0xAARRGGBB' integer + template< char... Cs > + constexpr QRgb operator""_rgba() + { + constexpr auto rrggbb = 8; + constexpr auto rrggbbaa = 10; + static_assert( sizeof...( Cs ) == rrggbb || sizeof...( Cs ) == rrggbbaa, + "invalid color literal length" ); + constexpr auto rgba = parse_hex_literal_0< Cs... >(); + if constexpr ( sizeof...( Cs ) == rrggbb ) + { + const auto r = ( rgba >> ( 4 * 4 ) ) & 0xFF; + const auto g = ( rgba >> ( 2 * 4 ) ) & 0xFF; + const auto b = ( rgba >> ( 0 * 4 ) ) & 0xFF; + const auto a = 0xFF; + return qRgba( r, g, b, a ); + } + if constexpr ( sizeof...( Cs ) == rrggbbaa ) + { + const auto r = ( rgba >> ( 6 * 4 ) ) & 0xFF; + const auto g = ( rgba >> ( 4 * 4 ) ) & 0xFF; + const auto b = ( rgba >> ( 2 * 4 ) ) & 0xFF; + const auto a = ( rgba >> ( 0 * 4 ) ) & 0xFF; + return qRgba( r, g, b, a ); + } + } + + // converts a hex literal from '0x[AA]RRGGBB' to '0xAARRGGBB' integer + template< char... Cs > + constexpr QRgb operator""_argb() + { + constexpr auto rrggbb = 8; + constexpr auto aarrggbb = 10; + static_assert( sizeof...( Cs ) == rrggbb || sizeof...( Cs ) == aarrggbb, + "invalid color literal length" ); + constexpr auto rgba = parse_hex_literal_0< Cs... >(); + if constexpr ( sizeof...( Cs ) == rrggbb ) + { + const auto r = ( rgba >> ( 4 * 4 ) ) & 0xFF; + const auto g = ( rgba >> ( 2 * 4 ) ) & 0xFF; + const auto b = ( rgba >> ( 0 * 4 ) ) & 0xFF; + const auto a = 0xFF; + return qRgba( r, g, b, a ); + } + if constexpr ( sizeof...( Cs ) == aarrggbb ) + { + const auto r = ( rgba >> ( 4 * 4 ) ) & 0xFF; + const auto g = ( rgba >> ( 2 * 4 ) ) & 0xFF; + const auto b = ( rgba >> ( 0 * 4 ) ) & 0xFF; + const auto a = ( rgba >> ( 6 * 4 ) ) & 0xFF; + return qRgba( r, g, b, a ); + } + } + +#define QSK_REQUIRE_CONSTEXPR_LITERAL 1 +#ifdef QSK_REQUIRE_CONSTEXPR_LITERAL + // RGBA hex string to QRgb with defaulted alpha = 0xFF static_assert( "#123456"_rgba == 0xFF123456, "not constexpr" ); static_assert( "#123456"_argb == 0xFF123456, "not constexpr" ); + + // ARGB hex string to QRgb with defaulted alpha = 0xFF static_assert( "#AA112233"_argb == 0xAA112233, "not constexpr" ); static_assert( "#112233AA"_rgba == 0xAA112233, "not constexpr" ); -#endif + // RGBA hex literal to QRgb with defaulted alpha = 0xFF + static_assert( 0x112233_rgba == 0xFF112233, "" ); + static_assert( 0xaabbcc_rgba == 0xFFAABBCC, "" ); + static_assert( 0xAABBCC_rgba == 0xFFAABBCC, "" ); + static_assert( 0x112233aa_rgba == 0xaa112233, "" ); + + // ARGB hex literal to QRgb with defaulted alpha = 0xFF + static_assert( 0x112233_argb == 0xFF112233, "" ); + static_assert( 0xaabbcc_argb == 0xFFAABBCC, "" ); + static_assert( 0xAABBCC_argb == 0xFFAABBCC, "" ); + static_assert( 0x112233aa_argb == 0x112233aa, "" ); +#endif } namespace color @@ -398,6 +509,60 @@ namespace QskRgb const auto color = QColor{ r, g, b, a }; return color; } + + // converts a hex literal from '0xRRGGBB[AA]' to a QColor + template< char... Cs > + constexpr QColor operator""_rgba() + { + constexpr auto rrggbb = 8; + constexpr auto rrggbbaa = 10; + static_assert( sizeof...( Cs ) == rrggbb || sizeof...( Cs ) == rrggbbaa, + "invalid color literal length" ); + constexpr auto rgba = parse_hex_literal_0< Cs... >(); + if constexpr ( sizeof...( Cs ) == rrggbb ) + { + const auto r = ( rgba >> ( 4 * 4 ) ) & 0xFF; + const auto g = ( rgba >> ( 2 * 4 ) ) & 0xFF; + const auto b = ( rgba >> ( 0 * 4 ) ) & 0xFF; + const auto a = 0xFF; + return { r, g, b, a }; + } + if constexpr ( sizeof...( Cs ) == rrggbbaa ) + { + const auto r = ( rgba >> ( 6 * 4 ) ) & 0xFF; + const auto g = ( rgba >> ( 4 * 4 ) ) & 0xFF; + const auto b = ( rgba >> ( 2 * 4 ) ) & 0xFF; + const auto a = ( rgba >> ( 0 * 4 ) ) & 0xFF; + return { r, g, b, a }; + } + } + + // converts a hex literal from '0x[AA]RRGGBB' to a QColor + template< char... Cs > + constexpr QColor operator""_argb() + { + constexpr auto rrggbb = 8; + constexpr auto aarrggbb = 10; + static_assert( sizeof...( Cs ) == rrggbb || sizeof...( Cs ) == aarrggbb, + "invalid color literal length" ); + constexpr auto rgba = parse_hex_literal_0< Cs... >(); + if constexpr ( sizeof...( Cs ) == rrggbb ) + { + const auto r = ( rgba >> ( 4 * 4 ) ) & 0xFF; + const auto g = ( rgba >> ( 2 * 4 ) ) & 0xFF; + const auto b = ( rgba >> ( 0 * 4 ) ) & 0xFF; + const auto a = 0xFF; + return { r, g, b, a }; + } + if constexpr ( sizeof...( Cs ) == aarrggbb ) + { + const auto r = ( rgba >> ( 4 * 4 ) ) & 0xFF; + const auto g = ( rgba >> ( 2 * 4 ) ) & 0xFF; + const auto b = ( rgba >> ( 0 * 4 ) ) & 0xFF; + const auto a = ( rgba >> ( 6 * 4 ) ) & 0xFF; + return { r, g, b, a }; + } + } } } } diff --git a/tests/test_QskRgbLiterals.cpp b/tests/test_QskRgbLiterals.cpp new file mode 100644 index 00000000..bea62d0b --- /dev/null +++ b/tests/test_QskRgbLiterals.cpp @@ -0,0 +1,109 @@ +#include "test_QskRgbLiterals.h" +#include +#include + +void QskRgbLiterals::parsing_data() +{ + QTest::addColumn< QString >( "text" ); + QTest::addColumn< QRgb >( "expected" ); + + QTest::newRow( "" ) << "" << ( QRgb ) 0; + QTest::newRow( "#" ) << "#" << ( QRgb ) 0; + QTest::newRow( "#1" ) << "#1" << ( QRgb ) 0x1; + QTest::newRow( "#12" ) << "#12" << ( QRgb ) 0x12; + QTest::newRow( "#123" ) << "#123" << ( QRgb ) 0x123; + QTest::newRow( "#1234" ) << "#1234" << ( QRgb ) 0x1234; + QTest::newRow( "#12345" ) << "#12345" << ( QRgb ) 0x12345; + QTest::newRow( "#123456" ) << "#123456" << ( QRgb ) 0x123456; + QTest::newRow( "#1234567" ) << "#1234567" << ( QRgb ) 0x1234567; + QTest::newRow( "#12345678" ) << "#12345678" << ( QRgb ) 0x12345678; + QTest::newRow( "#123456789" ) << "#123456789" << ( QRgb ) 0x23456789; +} + +void QskRgbLiterals::parsing() +{ + QFETCH( QString, text ); + QFETCH( QRgb, expected ); + + const auto actual = text.toStdString(); + QCOMPARE( QskRgb::fromHexString( actual.c_str(), actual.size() ), expected ); +} + +void QskRgbLiterals::qrgbLiterals_data() +{ + QTest::addColumn< QRgb >( "actual" ); + QTest::addColumn< QRgb >( "expected" ); + + using namespace QskRgb::literals::qrgb; + + QTest::newRow( "\"#123456\"_rgba" ) << "#123456"_rgba << ( QRgb ) 0xFF123456; + QTest::newRow( "\"#123456\"_argb" ) << "#123456"_argb << ( QRgb ) 0xFF123456; + + QTest::newRow( "\"#AA112233\"_argb" ) << "#AA112233"_argb << ( QRgb ) 0xAA112233; + QTest::newRow( "\"#112233AA\"_rgba" ) << "#112233AA"_rgba << ( QRgb ) 0xAA112233; + + QTest::newRow( "0x112233_rgba" ) << 0x112233_rgba << ( QRgb ) 0xFF112233; + QTest::newRow( "0xaabbcc_rgba" ) << 0xaabbcc_rgba << ( QRgb ) 0xFFAABBCC; + QTest::newRow( "0xAABBCC_rgba" ) << 0xAABBCC_rgba << ( QRgb ) 0xFFAABBCC; + QTest::newRow( "0x112233aa_rgba" ) << 0x112233aa_rgba << ( QRgb ) 0xaa112233; + + QTest::newRow( "0x112233_argb" ) << 0x112233_argb << ( QRgb ) 0xFF112233; + QTest::newRow( "0xaabbcc_argb" ) << 0xaabbcc_argb << ( QRgb ) 0xFFAABBCC; + QTest::newRow( "0xAABBCC_argb" ) << 0xAABBCC_argb << ( QRgb ) 0xFFAABBCC; + QTest::newRow( "0x112233aa_argb" ) << 0x112233aa_argb << ( QRgb ) 0x112233aa; +} + +void QskRgbLiterals::qrgbLiterals() +{ + using namespace QskRgb::literals::qrgb; + +#ifdef QSK_REQUIRE_CONSTEXPR_LITERAL + static_assert( "#123456"_rgba == 0xFF123456, "not constexpr" ); + static_assert( "#123456"_argb == 0xFF123456, "not constexpr" ); + + static_assert( "#AA112233"_argb == 0xAA112233, "not constexpr" ); + static_assert( "#112233AA"_rgba == 0xAA112233, "not constexpr" ); + + static_assert( 0x112233_rgba == 0xFF112233, "not constexpr" ); + static_assert( 0xaabbcc_rgba == 0xFFAABBCC, "not constexpr" ); + static_assert( 0xAABBCC_rgba == 0xFFAABBCC, "not constexpr" ); + static_assert( 0x112233aa_rgba == 0xaa112233, "not constexpr" ); + + static_assert( 0x112233_argb == 0xFF112233, "not constexpr" ); + static_assert( 0xaabbcc_argb == 0xFFAABBCC, "not constexpr" ); + static_assert( 0xAABBCC_argb == 0xFFAABBCC, "not constexpr" ); + static_assert( 0x112233aa_argb == 0x112233aa, "not constexpr" ); +#endif + + QFETCH( QRgb, actual ); + QFETCH( QRgb, expected ); + QCOMPARE( actual, expected ); +} + +void QskRgbLiterals::colorLiterals_data() +{ + QTest::addColumn< QColor >( "actual" ); + QTest::addColumn< QColor >( "expected" ); + + using namespace QskRgb::literals::color; + QTest::newRow( "\"#112233\"_rgba" ) << "#112233"_rgba << QColor::fromRgb( 0x11, 0x22, 0x33 ); + QTest::newRow( "\"#112233\"_argb" ) << "#112233"_argb << QColor::fromRgb( 0x11, 0x22, 0x33 ); + QTest::newRow( "\"#112233AA\"_rgba" ) + << "#112233AA"_rgba << QColor::fromRgb( 0x11, 0x22, 0x33, 0xAA ); + QTest::newRow( "\"#112233AA\"_argb" ) + << "#112233AA"_argb << QColor::fromRgb( 0x22, 0x33, 0xAA, 0x11 ); + + QTest::newRow( "0x112233_rgba" ) << 0x112233_rgba << QColor::fromRgb( 0x11, 0x22, 0x33 ); + QTest::newRow( "0x112233_argb" ) << 0x112233_argb << QColor::fromRgb( 0x11, 0x22, 0x33 ); + QTest::newRow( "0x112233AA_rgba" ) + << 0x112233AA_rgba << QColor::fromRgb( 0x11, 0x22, 0x33, 0xAA ); + QTest::newRow( "0x112233AA_argb" ) + << 0x112233AA_argb << QColor::fromRgb( 0x22, 0x33, 0xAA, 0x11 ); +} + +void QskRgbLiterals::colorLiterals() +{ + QFETCH( QColor, actual ); + QFETCH( QColor, expected ); + QCOMPARE( actual, expected ); +} diff --git a/tests/test_QskRgbLiterals.h b/tests/test_QskRgbLiterals.h new file mode 100644 index 00000000..e2ac0147 --- /dev/null +++ b/tests/test_QskRgbLiterals.h @@ -0,0 +1,14 @@ +#include + +class QskRgbLiterals final : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void parsing_data(); + void parsing(); + void qrgbLiterals_data(); + void qrgbLiterals(); + void colorLiterals_data(); + void colorLiterals(); +}; diff --git a/tests/tests.pro b/tests/tests.pro new file mode 100644 index 00000000..e48aed5a --- /dev/null +++ b/tests/tests.pro @@ -0,0 +1,12 @@ +QT += testlib +CONFIG += testcase +CONFIG += no_testcase_installs +CONFIG += qskinny + +HEADERS += \ + tests_main.h \ + test_QskRgbLiterals.h \ + +SOURCES += \ + tests_main.cpp \ + test_QskRgbLiterals.cpp \ diff --git a/tests/tests_main.cpp b/tests/tests_main.cpp new file mode 100644 index 00000000..afaa16ed --- /dev/null +++ b/tests/tests_main.cpp @@ -0,0 +1,4 @@ +#include "test_QskRgbLiterals.h" +#include + +QTEST_APPLESS_MAIN( QskRgbLiterals ) diff --git a/tests/tests_main.h b/tests/tests_main.h new file mode 100644 index 00000000..e69de29b