Compare commits

...

1 commit

Author SHA1 Message Date
Leonard Hecker 769600bc59 wip 2021-07-24 00:04:21 +02:00
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);
// Ensure that we can memcpy() and memmove() the struct for performance.
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_legacyDefaultBackground = 0;
@ -98,7 +100,7 @@ bool TextAttribute::IsLegacy() const noexcept
// - blinkingIsFaint: true if blinking should be interpreted as faint.
// Return Value:
// - 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 defaultBgColor,
const bool reverseScreenMode,

View file

@ -64,7 +64,7 @@ public:
static TextAttribute StripErroneousVT16VersionsOfLegacyDefaults(const TextAttribute& attribute) 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 defaultBgColor,
const bool reverseScreenMode = false,
@ -82,12 +82,15 @@ public:
void Invert() noexcept;
friend constexpr bool operator==(const TextAttribute& a, const TextAttribute& b) noexcept;
friend constexpr bool operator!=(const TextAttribute& a, const TextAttribute& b) noexcept;
friend constexpr bool operator==(const TextAttribute& attr, const WORD& legacyAttr) noexcept;
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;
}
inline bool operator!=(const TextAttribute& other) const noexcept
{
return memcmp(this, &other, sizeof(TextAttribute)) != 0;
}
bool IsLegacy() 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
};
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
#define LOG_ATTR(attr) (Log::Comment(NoThrowString().Format( \

View file

@ -50,75 +50,10 @@ constexpr std::array<BYTE, 256> Index256ToIndex16 = {
// clang-format on
bool TextColor::CanBeBrightened() const noexcept
{
return IsIndex16() || IsDefault();
}
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;
}
// We should only need 4B for TextColor. Any more than that is just waste.
static_assert(sizeof(TextColor) == 4);
// Assert that the use of memcmp() for comparisons is safe.
static_assert(std::has_unique_object_representations_v<TextColor>);
// Method Description:
// - 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.
// Return Value:
// - 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,
bool brighten) const noexcept
{
@ -146,7 +81,6 @@ COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable,
{
if (brighten)
{
FAIL_FAST_IF(colorTable.size() < 16);
// See MSFT:20266024 for context on this fix.
// Additionally todo MSFT:20271956 to fix this better for 19H2+
// 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
// from _wFillAttribute is being used instead.)
// 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++)
{
if (til::at(colorTable, i) == defaultColor)
@ -163,6 +109,7 @@ COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable,
return til::at(colorTable, i + 8);
}
}
#endif
}
return defaultColor;
@ -171,13 +118,9 @@ COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable,
{
return GetRGB();
}
else if (IsIndex16() && brighten)
{
return til::at(colorTable, _index | 8);
}
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
BYTE TextColor::GetLegacyIndex(const BYTE defaultIndex) const noexcept
{
if (IsDefault())
switch (_meta)
{
case ColorType::IsDefault:
return defaultIndex;
}
else if (IsIndex16())
{
return GetIndex();
}
else if (IsIndex256())
{
return Index256ToIndex16.at(GetIndex());
}
else
{
case ColorType::IsIndex16:
return _index;
case ColorType::IsIndex256:
return Index256ToIndex16[_index];
default:
// 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.
const BYTE compressedRgb = (_red & 0b11100000) +
((_green >> 3) & 0b00011100) +
((_blue >> 6) & 0b00000011);
return CompressedRgbToIndex16.at(compressedRgb);
const BYTE compressedRgb = (_red & 0b11100000) |
((_green >> 3) & 0b00011100) |
(_blue >> 6);
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
{
IsIndex256 = 0x0,
IsIndex16 = 0x1,
IsDefault = 0x2,
IsRgb = 0x3
None = 0b0000,
IsDefault = 0b0001,
IsIndex16 = 0b0010,
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
{
public:
@ -72,21 +83,74 @@ public:
{
}
friend constexpr bool operator==(const TextColor& a, const TextColor& b) noexcept;
friend constexpr bool operator!=(const TextColor& a, const TextColor& b) noexcept;
bool operator==(const TextColor& other) const noexcept
{
return memcmp(this, &other, sizeof(TextColor)) == 0;
}
bool CanBeBrightened() const noexcept;
bool IsLegacy() const noexcept;
bool IsIndex16() const noexcept;
bool IsIndex256() const noexcept;
bool IsDefault() const noexcept;
bool IsRgb() const noexcept;
bool operator!=(const TextColor& other) const noexcept
{
return memcmp(this, &other, sizeof(TextColor)) != 0;
}
void SetColor(const COLORREF rgbColor) noexcept;
void SetIndex(const BYTE index, const bool isIndex256) noexcept;
void SetDefault() noexcept;
constexpr bool CanBeBrightened() const 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 bool brighten = false) const noexcept;
@ -97,16 +161,19 @@ public:
return _index;
}
COLORREF GetRGB() const noexcept;
constexpr COLORREF GetRGB() const noexcept
{
return RGB(_red, _green, _blue);
}
private:
ColorType _meta;
union
{
BYTE _red, _index;
};
BYTE _green;
BYTE _blue;
ColorType _meta;
#ifdef UNIT_TESTING
friend class TextBufferTests;
@ -115,19 +182,6 @@ private:
#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
namespace WEX
@ -157,5 +211,3 @@ namespace WEX
}
}
#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="..\search.h" />
<ClInclude Include="..\TextColor.h" />
<ClInclude Include="..\TextAttribute.h" />
<ClInclude Include="..\TextAttribute.hpp" />
<ClInclude Include="..\textBuffer.hpp" />
<ClInclude Include="..\textBufferCellIterator.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
{
_blinkingState.RecordBlinkingUsage(attr);
auto colors = attr.CalculateRgbColors({ _colorTable.data(), _colorTable.size() },
auto colors = attr.CalculateRgbColors(_colorTable,
_defaultFg,
_defaultBg,
_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
{
_blinkingState.RecordBlinkingUsage(attr);
return attr.CalculateRgbColors(Get256ColorTable(),
return attr.CalculateRgbColors(GetColorTable(),
GetDefaultForeground(),
GetDefaultBackground(),
IsScreenReversed(),

View file

@ -726,16 +726,6 @@ void Settings::SetHistoryNoDup(const bool 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)
{
_colorTable.at(index) = ColorValue;

View file

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

View file

@ -419,7 +419,7 @@ void Telemetry::WriteFinalTraceLog()
TraceLoggingBool(gci.GetQuickEdit(), "QuickEdit"),
TraceLoggingValue(gci.GetWindowAlpha(), "WindowAlpha"),
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.OutputCP, "CodePageOutput"),
TraceLoggingValue(gci.GetFontSize().X, "FontSizeX"),
@ -453,7 +453,7 @@ void Telemetry::WriteFinalTraceLog()
TraceLoggingValue(gci.GetShowWindow(), "ShowWindow"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
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.
// So just log each enum count separately with its string representation which makes it more human readable.