This commit is contained in:
Leonard Hecker 2021-07-08 23:19:28 +02:00
parent 20e88d3e3e
commit 769600bc59
10 changed files with 139 additions and 176 deletions

View file

@ -11,6 +11,8 @@ static_assert(sizeof(TextAttribute) == 14);
static_assert(alignof(TextAttribute) == 2); static_assert(alignof(TextAttribute) == 2);
// Ensure that we can memcpy() and memmove() the struct for performance. // Ensure that we can memcpy() and memmove() the struct for performance.
static_assert(std::is_trivially_copyable_v<TextAttribute>); static_assert(std::is_trivially_copyable_v<TextAttribute>);
// Assert that the use of memcmp() for comparisons is safe.
static_assert(std::has_unique_object_representations_v<TextColor>);
BYTE TextAttribute::s_legacyDefaultForeground = 7; BYTE TextAttribute::s_legacyDefaultForeground = 7;
BYTE TextAttribute::s_legacyDefaultBackground = 0; BYTE TextAttribute::s_legacyDefaultBackground = 0;
@ -98,7 +100,7 @@ bool TextAttribute::IsLegacy() const noexcept
// - blinkingIsFaint: true if blinking should be interpreted as faint. // - blinkingIsFaint: true if blinking should be interpreted as faint.
// Return Value: // Return Value:
// - the foreground and background colors that should be displayed. // - the foreground and background colors that should be displayed.
std::pair<COLORREF, COLORREF> TextAttribute::CalculateRgbColors(const gsl::span<const COLORREF> colorTable, std::pair<COLORREF, COLORREF> TextAttribute::CalculateRgbColors(const std::array<COLORREF, 256>& colorTable,
const COLORREF defaultFgColor, const COLORREF defaultFgColor,
const COLORREF defaultBgColor, const COLORREF defaultBgColor,
const bool reverseScreenMode, const bool reverseScreenMode,

View file

@ -64,7 +64,7 @@ public:
static TextAttribute StripErroneousVT16VersionsOfLegacyDefaults(const TextAttribute& attribute) noexcept; static TextAttribute StripErroneousVT16VersionsOfLegacyDefaults(const TextAttribute& attribute) noexcept;
WORD GetLegacyAttributes() const noexcept; WORD GetLegacyAttributes() const noexcept;
std::pair<COLORREF, COLORREF> CalculateRgbColors(const gsl::span<const COLORREF> colorTable, std::pair<COLORREF, COLORREF> CalculateRgbColors(const std::array<COLORREF, 256>& colorTable,
const COLORREF defaultFgColor, const COLORREF defaultFgColor,
const COLORREF defaultBgColor, const COLORREF defaultBgColor,
const bool reverseScreenMode = false, const bool reverseScreenMode = false,
@ -82,12 +82,15 @@ public:
void Invert() noexcept; void Invert() noexcept;
friend constexpr bool operator==(const TextAttribute& a, const TextAttribute& b) noexcept; inline bool operator==(const TextAttribute& other) const noexcept
friend constexpr bool operator!=(const TextAttribute& a, const TextAttribute& b) noexcept; {
friend constexpr bool operator==(const TextAttribute& attr, const WORD& legacyAttr) noexcept; return memcmp(this, &other, sizeof(TextAttribute)) == 0;
friend constexpr bool operator!=(const TextAttribute& attr, const WORD& legacyAttr) noexcept; }
friend constexpr bool operator==(const WORD& legacyAttr, const TextAttribute& attr) noexcept;
friend constexpr bool operator!=(const WORD& legacyAttr, const TextAttribute& attr) noexcept; inline bool operator!=(const TextAttribute& other) const noexcept
{
return memcmp(this, &other, sizeof(TextAttribute)) != 0;
}
bool IsLegacy() const noexcept; bool IsLegacy() const noexcept;
bool IsBold() const noexcept; bool IsBold() const noexcept;
@ -195,20 +198,6 @@ enum class TextAttributeBehavior
StoredOnly, // only use the contained text attribute and skip the insertion of anything else StoredOnly, // only use the contained text attribute and skip the insertion of anything else
}; };
constexpr bool operator==(const TextAttribute& a, const TextAttribute& b) noexcept
{
return a._wAttrLegacy == b._wAttrLegacy &&
a._foreground == b._foreground &&
a._background == b._background &&
a._extendedAttrs == b._extendedAttrs &&
a._hyperlinkId == b._hyperlinkId;
}
constexpr bool operator!=(const TextAttribute& a, const TextAttribute& b) noexcept
{
return !(a == b);
}
#ifdef UNIT_TESTING #ifdef UNIT_TESTING
#define LOG_ATTR(attr) (Log::Comment(NoThrowString().Format( \ #define LOG_ATTR(attr) (Log::Comment(NoThrowString().Format( \

View file

@ -50,75 +50,10 @@ constexpr std::array<BYTE, 256> Index256ToIndex16 = {
// clang-format on // clang-format on
bool TextColor::CanBeBrightened() const noexcept // We should only need 4B for TextColor. Any more than that is just waste.
{ static_assert(sizeof(TextColor) == 4);
return IsIndex16() || IsDefault(); // Assert that the use of memcmp() for comparisons is safe.
} static_assert(std::has_unique_object_representations_v<TextColor>);
bool TextColor::IsLegacy() const noexcept
{
return IsIndex16() || (IsIndex256() && _index < 16);
}
bool TextColor::IsIndex16() const noexcept
{
return _meta == ColorType::IsIndex16;
}
bool TextColor::IsIndex256() const noexcept
{
return _meta == ColorType::IsIndex256;
}
bool TextColor::IsDefault() const noexcept
{
return _meta == ColorType::IsDefault;
}
bool TextColor::IsRgb() const noexcept
{
return _meta == ColorType::IsRgb;
}
// Method Description:
// - Sets the color value of this attribute, and sets this color to be an RGB
// attribute.
// Arguments:
// - rgbColor: the COLORREF containing the color information for this TextColor
// Return Value:
// - <none>
void TextColor::SetColor(const COLORREF rgbColor) noexcept
{
_meta = ColorType::IsRgb;
_red = GetRValue(rgbColor);
_green = GetGValue(rgbColor);
_blue = GetBValue(rgbColor);
}
// Method Description:
// - Sets this TextColor to be a legacy-style index into the color table.
// Arguments:
// - index: the index of the colortable we should use for this TextColor.
// - isIndex256: is this a 256 color index (true) or a 16 color index (false).
// Return Value:
// - <none>
void TextColor::SetIndex(const BYTE index, const bool isIndex256) noexcept
{
_meta = isIndex256 ? ColorType::IsIndex256 : ColorType::IsIndex16;
_index = index;
}
// Method Description:
// - Sets this TextColor to be a default text color, who's appearance is
// controlled by the terminal's implementation of what a default color is.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TextColor::SetDefault() noexcept
{
_meta = ColorType::IsDefault;
}
// Method Description: // Method Description:
// - Retrieve the real color value for this TextColor. // - Retrieve the real color value for this TextColor.
@ -138,7 +73,7 @@ void TextColor::SetDefault() noexcept
// - brighten: if true, we'll brighten a dark color table index. // - brighten: if true, we'll brighten a dark color table index.
// Return Value: // Return Value:
// - a COLORREF containing the real value of this TextColor. // - a COLORREF containing the real value of this TextColor.
COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable, COLORREF TextColor::GetColor(const std::array<COLORREF, 256>& colorTable,
const COLORREF defaultColor, const COLORREF defaultColor,
bool brighten) const noexcept bool brighten) const noexcept
{ {
@ -146,7 +81,6 @@ COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable,
{ {
if (brighten) if (brighten)
{ {
FAIL_FAST_IF(colorTable.size() < 16);
// See MSFT:20266024 for context on this fix. // See MSFT:20266024 for context on this fix.
// Additionally todo MSFT:20271956 to fix this better for 19H2+ // Additionally todo MSFT:20271956 to fix this better for 19H2+
// If we're a default color, check to see if the defaultColor exists // If we're a default color, check to see if the defaultColor exists
@ -156,6 +90,18 @@ COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable,
// (Settings::_DefaultForeground==INVALID_COLOR, and the index // (Settings::_DefaultForeground==INVALID_COLOR, and the index
// from _wFillAttribute is being used instead.) // from _wFillAttribute is being used instead.)
// If we find a match, return instead the bright version of this color // If we find a match, return instead the bright version of this color
#ifdef _M_AMD64
const auto needle = _mm_set1_epi32(defaultColor);
const auto haystack1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(colorTable.data() + 0));
const auto haystack2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(colorTable.data() + 4));
const auto result1 = _mm_cmpeq_epi32(haystack1, needle);
const auto result2 = _mm_cmpeq_epi32(haystack2, needle);
const auto shuffle16to8 = _mm_setr_epi8(0, 2, 4, 6, 8, 10, 12, 14, -1, -1, -1, -1, -1, -1, -1, -1);
const auto result = _mm_shuffle_epi8(_mm_packs_epi16(result1, result2), shuffle16to8);
const auto combined = _mm_movemask_epi8(result);
unsigned long index;
return _BitScanForward(&index, combined) ? til::at(colorTable, index + 8) : defaultColor;
#else
for (size_t i = 0; i < 8; i++) for (size_t i = 0; i < 8; i++)
{ {
if (til::at(colorTable, i) == defaultColor) if (til::at(colorTable, i) == defaultColor)
@ -163,6 +109,7 @@ COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable,
return til::at(colorTable, i + 8); return til::at(colorTable, i + 8);
} }
} }
#endif
} }
return defaultColor; return defaultColor;
@ -171,13 +118,9 @@ COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable,
{ {
return GetRGB(); return GetRGB();
} }
else if (IsIndex16() && brighten)
{
return til::at(colorTable, _index | 8);
}
else else
{ {
return til::at(colorTable, _index); return til::at(colorTable, IsIndex16() & brighten ? _index : _index | 8);
} }
} }
@ -189,37 +132,20 @@ COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable,
// - an index into the 16-color table // - an index into the 16-color table
BYTE TextColor::GetLegacyIndex(const BYTE defaultIndex) const noexcept BYTE TextColor::GetLegacyIndex(const BYTE defaultIndex) const noexcept
{ {
if (IsDefault()) switch (_meta)
{ {
case ColorType::IsDefault:
return defaultIndex; return defaultIndex;
} case ColorType::IsIndex16:
else if (IsIndex16()) return _index;
{ case ColorType::IsIndex256:
return GetIndex(); return Index256ToIndex16[_index];
} default:
else if (IsIndex256())
{
return Index256ToIndex16.at(GetIndex());
}
else
{
// We compress the RGB down to an 8-bit value and use that to // We compress the RGB down to an 8-bit value and use that to
// lookup a representative 16-color index from a hard-coded table. // lookup a representative 16-color index from a hard-coded table.
const BYTE compressedRgb = (_red & 0b11100000) + const BYTE compressedRgb = (_red & 0b11100000) |
((_green >> 3) & 0b00011100) + ((_green >> 3) & 0b00011100) |
((_blue >> 6) & 0b00000011); (_blue >> 6);
return CompressedRgbToIndex16.at(compressedRgb); return CompressedRgbToIndex16[compressedRgb];
} }
} }
// Method Description:
// - Return a COLORREF containing our stored value. Will return garbage if this
//attribute is not a RGB attribute.
// Arguments:
// - <none>
// Return Value:
// - a COLORREF containing our stored value
COLORREF TextColor::GetRGB() const noexcept
{
return RGB(_red, _green, _blue);
}

View file

@ -39,12 +39,23 @@ Revision History:
enum class ColorType : BYTE enum class ColorType : BYTE
{ {
IsIndex256 = 0x0, None = 0b0000,
IsIndex16 = 0x1, IsDefault = 0b0001,
IsDefault = 0x2, IsIndex16 = 0b0010,
IsRgb = 0x3 IsIndex256 = 0b0100,
IsRgb = 0b1000,
}; };
constexpr ColorType operator|(ColorType lhs, ColorType rhs) noexcept
{
return static_cast<ColorType>(static_cast<BYTE>(lhs) | static_cast<BYTE>(rhs));
}
constexpr ColorType operator&(ColorType lhs, ColorType rhs) noexcept
{
return static_cast<ColorType>(static_cast<BYTE>(lhs) & static_cast<BYTE>(rhs));
}
struct TextColor struct TextColor
{ {
public: public:
@ -72,21 +83,74 @@ public:
{ {
} }
friend constexpr bool operator==(const TextColor& a, const TextColor& b) noexcept; bool operator==(const TextColor& other) const noexcept
friend constexpr bool operator!=(const TextColor& a, const TextColor& b) noexcept; {
return memcmp(this, &other, sizeof(TextColor)) == 0;
}
bool CanBeBrightened() const noexcept; bool operator!=(const TextColor& other) const noexcept
bool IsLegacy() const noexcept; {
bool IsIndex16() const noexcept; return memcmp(this, &other, sizeof(TextColor)) != 0;
bool IsIndex256() const noexcept; }
bool IsDefault() const noexcept;
bool IsRgb() const noexcept;
void SetColor(const COLORREF rgbColor) noexcept; constexpr bool CanBeBrightened() const noexcept
void SetIndex(const BYTE index, const bool isIndex256) noexcept; {
void SetDefault() noexcept; return WI_IsAnyFlagSet(_meta, ColorType::IsDefault | ColorType::IsIndex16);
}
COLORREF GetColor(gsl::span<const COLORREF> colorTable, constexpr bool IsLegacy() const noexcept
{
// This is basically:
// return IsIndex16() || (IsIndex256() && _index < 16);
// but without any branches.
return (_index < 16) & WI_IsAnyFlagSet(_meta, ColorType::IsIndex16 | ColorType::IsIndex256);
}
constexpr bool IsIndex16() const noexcept
{
return _meta == ColorType::IsIndex16;
}
constexpr bool IsIndex256() const noexcept
{
return _meta == ColorType::IsIndex256;
}
constexpr bool IsDefault() const noexcept
{
return _meta == ColorType::IsDefault;
}
constexpr bool IsRgb() const noexcept
{
return _meta == ColorType::IsRgb;
}
inline void SetColor(const COLORREF rgbColor) noexcept
{
_meta = ColorType::IsRgb;
_red = GetRValue(rgbColor);
_green = GetGValue(rgbColor);
_blue = GetBValue(rgbColor);
}
inline void SetIndex(const BYTE index, const bool isIndex256) noexcept
{
_meta = isIndex256 ? ColorType::IsIndex256 : ColorType::IsIndex16;
_index = index;
_green = 0;
_blue = 0;
}
inline void SetDefault() noexcept
{
_meta = ColorType::IsDefault;
_index = 0;
_green = 0;
_blue = 0;
}
COLORREF GetColor(const std::array<COLORREF, 256>& colorTable,
const COLORREF defaultColor, const COLORREF defaultColor,
const bool brighten = false) const noexcept; const bool brighten = false) const noexcept;
@ -97,16 +161,19 @@ public:
return _index; return _index;
} }
COLORREF GetRGB() const noexcept; constexpr COLORREF GetRGB() const noexcept
{
return RGB(_red, _green, _blue);
}
private: private:
ColorType _meta;
union union
{ {
BYTE _red, _index; BYTE _red, _index;
}; };
BYTE _green; BYTE _green;
BYTE _blue; BYTE _blue;
ColorType _meta;
#ifdef UNIT_TESTING #ifdef UNIT_TESTING
friend class TextBufferTests; friend class TextBufferTests;
@ -115,19 +182,6 @@ private:
#endif #endif
}; };
bool constexpr operator==(const TextColor& a, const TextColor& b) noexcept
{
return a._meta == b._meta &&
a._red == b._red &&
a._green == b._green &&
a._blue == b._blue;
}
bool constexpr operator!=(const TextColor& a, const TextColor& b) noexcept
{
return !(a == b);
}
#ifdef UNIT_TESTING #ifdef UNIT_TESTING
namespace WEX namespace WEX
@ -157,5 +211,3 @@ namespace WEX
} }
} }
#endif #endif
static_assert(sizeof(TextColor) <= 4 * sizeof(BYTE), "We should only need 4B for an entire TextColor. Any more than that is just waste");

View file

@ -44,7 +44,7 @@
<ClInclude Include="..\Row.hpp" /> <ClInclude Include="..\Row.hpp" />
<ClInclude Include="..\search.h" /> <ClInclude Include="..\search.h" />
<ClInclude Include="..\TextColor.h" /> <ClInclude Include="..\TextColor.h" />
<ClInclude Include="..\TextAttribute.h" /> <ClInclude Include="..\TextAttribute.hpp" />
<ClInclude Include="..\textBuffer.hpp" /> <ClInclude Include="..\textBuffer.hpp" />
<ClInclude Include="..\textBufferCellIterator.hpp" /> <ClInclude Include="..\textBufferCellIterator.hpp" />
<ClInclude Include="..\textBufferTextIterator.hpp" /> <ClInclude Include="..\textBufferTextIterator.hpp" />

View file

@ -45,7 +45,7 @@ const TextAttribute Terminal::GetDefaultBrushColors() noexcept
std::pair<COLORREF, COLORREF> Terminal::GetAttributeColors(const TextAttribute& attr) const noexcept std::pair<COLORREF, COLORREF> Terminal::GetAttributeColors(const TextAttribute& attr) const noexcept
{ {
_blinkingState.RecordBlinkingUsage(attr); _blinkingState.RecordBlinkingUsage(attr);
auto colors = attr.CalculateRgbColors({ _colorTable.data(), _colorTable.size() }, auto colors = attr.CalculateRgbColors(_colorTable,
_defaultFg, _defaultFg,
_defaultBg, _defaultBg,
_screenReversed, _screenReversed,

View file

@ -259,7 +259,7 @@ COLORREF CONSOLE_INFORMATION::GetDefaultBackground() const noexcept
std::pair<COLORREF, COLORREF> CONSOLE_INFORMATION::LookupAttributeColors(const TextAttribute& attr) const noexcept std::pair<COLORREF, COLORREF> CONSOLE_INFORMATION::LookupAttributeColors(const TextAttribute& attr) const noexcept
{ {
_blinkingState.RecordBlinkingUsage(attr); _blinkingState.RecordBlinkingUsage(attr);
return attr.CalculateRgbColors(Get256ColorTable(), return attr.CalculateRgbColors(GetColorTable(),
GetDefaultForeground(), GetDefaultForeground(),
GetDefaultBackground(), GetDefaultBackground(),
IsScreenReversed(), IsScreenReversed(),

View file

@ -726,16 +726,6 @@ void Settings::SetHistoryNoDup(const bool bHistoryNoDup)
_bHistoryNoDup = bHistoryNoDup; _bHistoryNoDup = bHistoryNoDup;
} }
gsl::span<const COLORREF> Settings::Get16ColorTable() const
{
return Get256ColorTable().subspan(0, 16);
}
gsl::span<const COLORREF> Settings::Get256ColorTable() const
{
return { _colorTable.data(), _colorTable.size() };
}
void Settings::SetColorTableEntry(const size_t index, const COLORREF ColorValue) void Settings::SetColorTableEntry(const size_t index, const COLORREF ColorValue)
{ {
_colorTable.at(index) = ColorValue; _colorTable.at(index) = ColorValue;

View file

@ -159,8 +159,12 @@ public:
bool GetHistoryNoDup() const; bool GetHistoryNoDup() const;
void SetHistoryNoDup(const bool fHistoryNoDup); void SetHistoryNoDup(const bool fHistoryNoDup);
gsl::span<const COLORREF> Get16ColorTable() const; // The first 16 items of the color table are the same as the 16-color palette.
gsl::span<const COLORREF> Get256ColorTable() const; inline const std::array<COLORREF, XTERM_COLOR_TABLE_SIZE>& GetColorTable() const noexcept
{
return _colorTable;
}
void SetColorTableEntry(const size_t index, const COLORREF ColorValue); void SetColorTableEntry(const size_t index, const COLORREF ColorValue);
COLORREF GetColorTableEntry(const size_t index) const; COLORREF GetColorTableEntry(const size_t index) const;

View file

@ -419,7 +419,7 @@ void Telemetry::WriteFinalTraceLog()
TraceLoggingBool(gci.GetQuickEdit(), "QuickEdit"), TraceLoggingBool(gci.GetQuickEdit(), "QuickEdit"),
TraceLoggingValue(gci.GetWindowAlpha(), "WindowAlpha"), TraceLoggingValue(gci.GetWindowAlpha(), "WindowAlpha"),
TraceLoggingBool(gci.GetWrapText(), "WrapText"), TraceLoggingBool(gci.GetWrapText(), "WrapText"),
TraceLoggingUInt32Array((UINT32 const*)gci.Get16ColorTable().data(), (UINT16)gci.Get16ColorTable().size(), "ColorTable"), TraceLoggingUInt32Array((UINT32 const*)gci.GetColorTable().data(), 16, "ColorTable"),
TraceLoggingValue(gci.CP, "CodePageInput"), TraceLoggingValue(gci.CP, "CodePageInput"),
TraceLoggingValue(gci.OutputCP, "CodePageOutput"), TraceLoggingValue(gci.OutputCP, "CodePageOutput"),
TraceLoggingValue(gci.GetFontSize().X, "FontSizeX"), TraceLoggingValue(gci.GetFontSize().X, "FontSizeX"),
@ -453,7 +453,7 @@ void Telemetry::WriteFinalTraceLog()
TraceLoggingValue(gci.GetShowWindow(), "ShowWindow"), TraceLoggingValue(gci.GetShowWindow(), "ShowWindow"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
static_assert(sizeof(UINT32) == sizeof(gci.Get16ColorTable()[0]), "gci.Get16ColorTable()"); static_assert(sizeof(UINT32) == sizeof(gci.GetColorTable()[0]), "gci.Get16ColorTable()");
// I could use the TraceLoggingUIntArray, but then we would have to know the order of the enums on the backend. // I could use the TraceLoggingUIntArray, but then we would have to know the order of the enums on the backend.
// So just log each enum count separately with its string representation which makes it more human readable. // So just log each enum count separately with its string representation which makes it more human readable.