2020-03-10 01:17:24 +01:00
|
|
|
// 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)
|
2020-10-09 20:59:58 +02:00
|
|
|
// we can't depend on GSL here, so we use static_cast for explicit narrowing
|
2020-03-10 01:17:24 +01:00
|
|
|
#pragma warning(disable : 26472)
|
|
|
|
struct color
|
|
|
|
{
|
2020-09-14 20:51:03 +02:00
|
|
|
// Clang (10) has no trouble optimizing the COLORREF conversion operator, below, to a
|
|
|
|
// simple 32-bit load with mask (!) even though it's a series of bit shifts across
|
|
|
|
// multiple struct members.
|
|
|
|
// CL (19.24) doesn't make the same optimization decision, and it emits three 8-bit loads
|
|
|
|
// and some shifting.
|
|
|
|
// In any case, the optimization only applies at -O2 (clang) and above.
|
|
|
|
// Here, we leverage the spec-legality of using unions for type conversions and the
|
|
|
|
// overlap of four uint8_ts and a uint32_t to make the conversion very obvious to
|
|
|
|
// both compilers.
|
2020-09-16 03:50:07 +02:00
|
|
|
#pragma warning(push)
|
|
|
|
// CL will complain about the both nameless and anonymous struct.
|
|
|
|
#pragma warning(disable : 4201)
|
2020-09-14 20:51:03 +02:00
|
|
|
union
|
|
|
|
{
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ // Clang, GCC
|
|
|
|
uint8_t a, b, g, r;
|
|
|
|
#else
|
|
|
|
uint8_t r, g, b, a;
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
uint32_t abgr;
|
|
|
|
};
|
2020-09-16 03:50:07 +02:00
|
|
|
#pragma warning(pop)
|
2020-03-10 01:17:24 +01:00
|
|
|
|
|
|
|
constexpr color() noexcept :
|
|
|
|
r{ 0 },
|
|
|
|
g{ 0 },
|
|
|
|
b{ 0 },
|
2020-09-16 19:19:49 +02:00
|
|
|
a{ 0 }
|
|
|
|
{
|
|
|
|
}
|
2020-03-10 01:17:24 +01:00
|
|
|
|
|
|
|
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) :
|
2020-09-14 20:51:03 +02:00
|
|
|
abgr{ static_cast<uint32_t>(c | 0xFF000000u) }
|
2020-03-10 01:17:24 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
operator COLORREF() const noexcept
|
|
|
|
{
|
2020-09-14 20:51:03 +02:00
|
|
|
return static_cast<COLORREF>(abgr & 0x00FFFFFFu);
|
2020-03-10 01:17:24 +01:00
|
|
|
}
|
|
|
|
#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) }
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2020-05-16 00:43:00 +02:00
|
|
|
constexpr color with_alpha(uint8_t alpha) const
|
|
|
|
{
|
|
|
|
return color{
|
|
|
|
r,
|
|
|
|
g,
|
|
|
|
b,
|
|
|
|
alpha
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-03-10 01:17:24 +01:00
|
|
|
#ifdef D3DCOLORVALUE_DEFINED
|
|
|
|
constexpr operator D3DCOLORVALUE() const
|
|
|
|
{
|
|
|
|
return D3DCOLORVALUE{ r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f };
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-05-16 00:43:00 +02:00
|
|
|
#ifdef WINRT_Windows_UI_H
|
|
|
|
constexpr color(const winrt::Windows::UI::Color& winUIColor) :
|
|
|
|
color(winUIColor.R, winUIColor.G, winUIColor.B, winUIColor.A)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
operator winrt::Windows::UI::Color() const
|
|
|
|
{
|
|
|
|
winrt::Windows::UI::Color ret;
|
|
|
|
ret.R = r;
|
|
|
|
ret.G = g;
|
|
|
|
ret.B = b;
|
|
|
|
ret.A = a;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
Introduce MS.Term.Core.Color to replace W.U.Color for Core/Control/TSM (#9658)
This pull request introduces Microsoft.Terminal.Core.Color as an
alternative to both Windows.UI.Color and uint32_t/COLORREF in the
TerminalCore, ...Control, ...SettingsModel and ...SettingsEditor layers.
M.T.C.Color is trivially convertible to/from til::color and therefore
to/from COLORREF, W.U.Color, and any other color representation we might
need².
I've replaced almost every use of W.U.Color and uint32_t-as-color in the
above layers, with minor exception¹.
The need for this work is twofold.
First: We cannot bear a dependency from TerminalCore (which should,
on paper, be Windows 7 compatible) on Windows.UI or any other WinRT
namespace.
This work removes one big dependency on Windows.UI, but it does not go
all the way.
Second: TerminalCore chose to communicate mostly in packed uint32s
(COLORREF), which was inherently lossy and dangerous.
¹ The UI layers (TerminalControl, TerminalApp) still use
Windows.UI.Color as they are intimately connected to the UWP XAML UI.
² In the future, we might even be able to *use* the alpha channel...
## PR Checklist
* [x] I ran into the need for this when I introduced cursor inversion
* [X] Fixes a longstanding itch
## Validation Steps Performed
Built and ran all tests for the impacted layers, even the local ones!
2021-03-30 22:15:49 +02:00
|
|
|
#ifdef WINRT_Microsoft_Terminal_Core_H
|
|
|
|
constexpr color(const winrt::Microsoft::Terminal::Core::Color& coreColor) :
|
|
|
|
color(coreColor.R, coreColor.G, coreColor.B, coreColor.A)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
operator winrt::Microsoft::Terminal::Core::Color() const noexcept
|
|
|
|
{
|
|
|
|
winrt::Microsoft::Terminal::Core::Color ret;
|
|
|
|
ret.R = r;
|
|
|
|
ret.G = g;
|
|
|
|
ret.B = b;
|
|
|
|
ret.A = a;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-03-10 01:17:24 +01:00
|
|
|
constexpr bool operator==(const til::color& other) const
|
|
|
|
{
|
2020-09-14 20:51:03 +02:00
|
|
|
return abgr == other.abgr;
|
2020-03-10 01:17:24 +01:00
|
|
|
}
|
2020-05-16 00:43:00 +02:00
|
|
|
|
|
|
|
constexpr bool operator!=(const til::color& other) const
|
|
|
|
{
|
|
|
|
return !(*this == other);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::wstring to_string() const
|
|
|
|
{
|
|
|
|
std::wstringstream wss;
|
2020-06-26 22:38:02 +02:00
|
|
|
wss << L"Color " << ToHexString(false);
|
|
|
|
return wss.str();
|
|
|
|
}
|
|
|
|
std::wstring ToHexString(const bool omitAlpha = false) const
|
|
|
|
{
|
|
|
|
std::wstringstream wss;
|
|
|
|
wss << L"#" << std::uppercase << std::setfill(L'0') << std::hex;
|
2020-05-16 00:43:00 +02:00
|
|
|
// Force the compiler to promote from byte to int. Without it, the
|
|
|
|
// stringstream will try to write the components as chars
|
2020-06-26 22:38:02 +02:00
|
|
|
if (!omitAlpha)
|
|
|
|
{
|
|
|
|
wss << std::setw(2) << static_cast<int>(a);
|
|
|
|
}
|
2020-05-16 00:43:00 +02:00
|
|
|
wss << std::setw(2) << static_cast<int>(r);
|
|
|
|
wss << std::setw(2) << static_cast<int>(g);
|
|
|
|
wss << std::setw(2) << static_cast<int>(b);
|
|
|
|
|
|
|
|
return wss.str();
|
|
|
|
}
|
2020-03-10 01:17:24 +01:00
|
|
|
};
|
|
|
|
#pragma warning(pop)
|
|
|
|
}
|
2020-05-16 00:43:00 +02:00
|
|
|
|
2020-09-14 20:51:03 +02:00
|
|
|
static_assert(sizeof(til::color) == sizeof(uint32_t));
|
|
|
|
|
2020-05-16 00:43:00 +02:00
|
|
|
#ifdef __WEX_COMMON_H__
|
|
|
|
namespace WEX::TestExecution
|
|
|
|
{
|
|
|
|
template<>
|
|
|
|
class VerifyOutputTraits<::til::color>
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
static WEX::Common::NoThrowString ToString(const ::til::color& color)
|
|
|
|
{
|
|
|
|
return WEX::Common::NoThrowString(color.to_string().c_str());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
#endif
|