forked from microsoft/terminal
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add til::color, a universal-converting color type (microsoft#4108)
til::color will help us move away from COLORREF internally. It supports conversion to/from COLORREF, and from all types of structs containing members named R, G, B and A (or r, g, b, and a). ## Validation Steps Performed Tests; run through profile/colorScheme deserialization with `til::color` instead of `uint32_t` or `COLORREF`.
- Loading branch information
1 parent
b6eb3d0
commit 88460c9
Showing
7 changed files
with
230 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license. | ||
|
||
#pragma once | ||
|
||
namespace til // Terminal Implementation Library. Also: "Today I Learned" | ||
{ | ||
// color is a universal integral 8bpp RGBA (0-255) color type implicitly convertible to/from | ||
// a number of other color types. | ||
#pragma warning(push) | ||
// we can't depend on GSL here (some libraries use BLOCK_GSL), so we use static_cast for explicit narrowing | ||
#pragma warning(disable : 26472) | ||
struct color | ||
{ | ||
uint8_t r, g, b, a; | ||
|
||
constexpr color() noexcept : | ||
r{ 0 }, | ||
g{ 0 }, | ||
b{ 0 }, | ||
a{ 0 } {} | ||
|
||
constexpr color(uint8_t _r, uint8_t _g, uint8_t _b) noexcept : | ||
r{ _r }, | ||
g{ _g }, | ||
b{ _b }, | ||
a{ 255 } {} | ||
|
||
constexpr color(uint8_t _r, uint8_t _g, uint8_t _b, uint8_t _a) noexcept : | ||
r{ _r }, | ||
g{ _g }, | ||
b{ _b }, | ||
a{ _a } {} | ||
|
||
constexpr color(const color&) = default; | ||
constexpr color(color&&) = default; | ||
color& operator=(const color&) = default; | ||
color& operator=(color&&) = default; | ||
~color() = default; | ||
|
||
#ifdef _WINDEF_ | ||
constexpr color(COLORREF c) : | ||
r{ static_cast<uint8_t>(c & 0xFF) }, | ||
g{ static_cast<uint8_t>((c & 0xFF00) >> 8) }, | ||
b{ static_cast<uint8_t>((c & 0xFF0000) >> 16) }, | ||
a{ 255 } | ||
{ | ||
} | ||
|
||
operator COLORREF() const noexcept | ||
{ | ||
return static_cast<COLORREF>(r) | (static_cast<COLORREF>(g) << 8) | (static_cast<COLORREF>(b) << 16); | ||
} | ||
#endif | ||
|
||
// Method Description: | ||
// - Converting constructor for any other color structure type containing integral R, G, B, A (case sensitive.) | ||
// Notes: | ||
// - This and all below conversions make use of std::enable_if and a default parameter to disambiguate themselves. | ||
// enable_if will result in an <error-type> if the constraint within it is not met, which will make this | ||
// template ill-formed. Because SFINAE, ill-formed templated members "disappear" instead of causing an error. | ||
template<typename TOther> | ||
constexpr color(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().R)> && std::is_integral_v<decltype(std::declval<TOther>().A)>, int> /*sentinel*/ = 0) : | ||
r{ static_cast<uint8_t>(other.R) }, | ||
g{ static_cast<uint8_t>(other.G) }, | ||
b{ static_cast<uint8_t>(other.B) }, | ||
a{ static_cast<uint8_t>(other.A) } | ||
{ | ||
} | ||
|
||
// Method Description: | ||
// - Converting constructor for any other color structure type containing integral r, g, b, a (case sensitive.) | ||
template<typename TOther> | ||
constexpr color(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().r)> && std::is_integral_v<decltype(std::declval<TOther>().a)>, int> /*sentinel*/ = 0) : | ||
r{ static_cast<uint8_t>(other.r) }, | ||
g{ static_cast<uint8_t>(other.g) }, | ||
b{ static_cast<uint8_t>(other.b) }, | ||
a{ static_cast<uint8_t>(other.a) } | ||
{ | ||
} | ||
|
||
// Method Description: | ||
// - Converting constructor for any other color structure type containing floating-point R, G, B, A (case sensitive.) | ||
template<typename TOther> | ||
constexpr color(const TOther& other, std::enable_if_t<std::is_floating_point_v<decltype(std::declval<TOther>().R)> && std::is_floating_point_v<decltype(std::declval<TOther>().A)>, float> /*sentinel*/ = 1.0f) : | ||
r{ static_cast<uint8_t>(other.R * 255.0f) }, | ||
g{ static_cast<uint8_t>(other.G * 255.0f) }, | ||
b{ static_cast<uint8_t>(other.B * 255.0f) }, | ||
a{ static_cast<uint8_t>(other.A * 255.0f) } | ||
{ | ||
} | ||
|
||
// Method Description: | ||
// - Converting constructor for any other color structure type containing floating-point r, g, b, a (case sensitive.) | ||
template<typename TOther> | ||
constexpr color(const TOther& other, std::enable_if_t<std::is_floating_point_v<decltype(std::declval<TOther>().r)> && std::is_floating_point_v<decltype(std::declval<TOther>().a)>, float> /*sentinel*/ = 1.0f) : | ||
r{ static_cast<uint8_t>(other.r * 255.0f) }, | ||
g{ static_cast<uint8_t>(other.g * 255.0f) }, | ||
b{ static_cast<uint8_t>(other.b * 255.0f) }, | ||
a{ static_cast<uint8_t>(other.a * 255.0f) } | ||
{ | ||
} | ||
|
||
#ifdef D3DCOLORVALUE_DEFINED | ||
constexpr operator D3DCOLORVALUE() const | ||
{ | ||
return D3DCOLORVALUE{ r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f }; | ||
} | ||
#endif | ||
|
||
constexpr bool operator==(const til::color& other) const | ||
{ | ||
return r == other.r && g == other.g && b == other.b && a == other.a; | ||
} | ||
}; | ||
#pragma warning(pop) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license. | ||
|
||
#include "precomp.h" | ||
#include "WexTestClass.h" | ||
|
||
namespace WEX::TestExecution | ||
{ | ||
template<> | ||
class VerifyOutputTraits<til::color> | ||
{ | ||
public: | ||
static WEX::Common::NoThrowString ToString(const til::color& c) | ||
{ | ||
return WEX::Common::NoThrowString().Format(L"(RGBA: %2.02x%2.02x%2.02x%2.02x)", c.r, c.g, c.b, c.a); | ||
} | ||
}; | ||
} | ||
|
||
using namespace WEX::Common; | ||
using namespace WEX::Logging; | ||
using namespace WEX::TestExecution; | ||
|
||
class ColorTests | ||
{ | ||
TEST_CLASS(ColorTests); | ||
|
||
TEST_METHOD(Construct) | ||
{ | ||
til::color rgb{ 0xde, 0xad, 0xbe }; | ||
|
||
VERIFY_ARE_EQUAL(0xde, rgb.r); | ||
VERIFY_ARE_EQUAL(0xad, rgb.g); | ||
VERIFY_ARE_EQUAL(0xbe, rgb.b); | ||
VERIFY_ARE_EQUAL(0xff, rgb.a); // auto-filled by constructor | ||
|
||
VERIFY_ARE_EQUAL(rgb, rgb); | ||
|
||
til::color rgba{ 0xde, 0xad, 0xbe, 0xef }; | ||
|
||
VERIFY_ARE_EQUAL(0xde, rgba.r); | ||
VERIFY_ARE_EQUAL(0xad, rgba.g); | ||
VERIFY_ARE_EQUAL(0xbe, rgba.b); | ||
VERIFY_ARE_EQUAL(0xef, rgba.a); | ||
|
||
VERIFY_ARE_NOT_EQUAL(rgb, rgba); | ||
} | ||
|
||
TEST_METHOD(ConvertFromColorRef) | ||
{ | ||
COLORREF c = 0x00FEEDFAu; // remember, this one is in 0BGR | ||
til::color fromColorRef{ c }; | ||
|
||
VERIFY_ARE_EQUAL(0xfa, fromColorRef.r); | ||
VERIFY_ARE_EQUAL(0xed, fromColorRef.g); | ||
VERIFY_ARE_EQUAL(0xfe, fromColorRef.b); | ||
VERIFY_ARE_EQUAL(0xff, fromColorRef.a); // COLORREF do not have an alpha channel | ||
} | ||
|
||
TEST_METHOD(ConvertToColorRef) | ||
{ | ||
til::color rgb{ 0xf0, 0x0d, 0xca, 0xfe }; | ||
|
||
VERIFY_ARE_EQUAL(0x00CA0DF0u, static_cast<COLORREF>(rgb)); // alpha is dropped, COLOREREF is 0BGR | ||
} | ||
|
||
template<typename T> | ||
struct Quad_rgba | ||
{ | ||
T r, g, b, a; | ||
}; | ||
|
||
template<typename T> | ||
struct Quad_RGBA | ||
{ | ||
T R, G, B, A; | ||
}; | ||
|
||
TEST_METHOD(ConvertFromIntColorStructs) | ||
{ | ||
Quad_rgba<int> q1{ 0xca, 0xfe, 0xf0, 0x0d }; | ||
til::color t1{ 0xca, 0xfe, 0xf0, 0x0d }; | ||
|
||
VERIFY_ARE_EQUAL(t1, static_cast<til::color>(q1)); | ||
|
||
Quad_RGBA<int> q2{ 0xfa, 0xce, 0xb0, 0x17 }; | ||
til::color t2{ 0xfa, 0xce, 0xb0, 0x17 }; | ||
|
||
VERIFY_ARE_EQUAL(t2, static_cast<til::color>(q2)); | ||
} | ||
|
||
TEST_METHOD(ConvertFromFloatColorStructs) | ||
{ | ||
Quad_rgba<float> q1{ 0.730f, 0.867f, 0.793f, 0.997f }; | ||
til::color t1{ 0xba, 0xdd, 0xca, 0xfe }; | ||
|
||
VERIFY_ARE_EQUAL(t1, static_cast<til::color>(q1)); | ||
|
||
Quad_RGBA<float> q2{ 0.871f, 0.679f, 0.981f, 0.067f }; | ||
til::color t2{ 0xde, 0xad, 0xfa, 0x11 }; | ||
|
||
VERIFY_ARE_EQUAL(t2, static_cast<til::color>(q2)); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ DLLDEF = | |
|
||
SOURCES = \ | ||
$(SOURCES) \ | ||
ColorTests.cpp \ | ||
SomeTests.cpp \ | ||
DefaultResource.rc \ | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters