From e2b6e7a992d5cc59c1bc7aaebb6e32e61c4b9112 Mon Sep 17 00:00:00 2001 From: Jon Kazama Date: Wed, 19 Aug 2026 17:09:05 +0200 Subject: [PATCH] fix(chat): the header measured contrast against the wrong colour space EnsureContrast works in ABGR. I handed it the theme's RGBA on both arguments, so it swapped red and blue in the foreground and in the background, measured a contrast between two colours that were never on screen, and returned a result in the wrong order -- which then went through RgbaToAbgr a second time. On a violet surface with a teal accent that came out as dark bordeaux on dark violet: the exact unreadable pairing the call was there to prevent. Reported from a real screenshot, not from a test, because nothing here is testable without a draw frame. Every existing caller in the codebase passes ABGR -- SettingsPalette hands over _palette.Abgr(...), SegmentedControl uses fields literally named LabelAbgr and TrackAbgr. Mine were the only three that did not, and all three were written in this cycle. --- .../Components/Settings/LivePreviewPanel.cs | 7 +++++-- .../Ui/StyleEngine/Widgets/ChannelHeader.cs | 19 ++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/HellionChat/Ui/Components/Settings/LivePreviewPanel.cs b/HellionChat/Ui/Components/Settings/LivePreviewPanel.cs index 1084f39..c11cb1d 100644 --- a/HellionChat/Ui/Components/Settings/LivePreviewPanel.cs +++ b/HellionChat/Ui/Components/Settings/LivePreviewPanel.cs @@ -286,8 +286,11 @@ internal sealed class LivePreviewPanel : IDisposable draw.DrawTrackedText( new Vector2(listOrigin.X + 6f, listOrigin.Y + 4f), MockChannel, - ColourUtil.RgbaToAbgr( - ColourUtil.EnsureContrast(theme.Colors.Accent, theme.Colors.Surface, 4.5f) + // ABGR in, ABGR out -- see the note in ChannelHeader. + ColourUtil.EnsureContrast( + ColourUtil.RgbaToAbgr(theme.Colors.Accent), + ColourUtil.RgbaToAbgr(theme.Colors.Surface), + 4.5f ), 1.8f ); diff --git a/HellionChat/Ui/StyleEngine/Widgets/ChannelHeader.cs b/HellionChat/Ui/StyleEngine/Widgets/ChannelHeader.cs index 9bb8a1b..12584d6 100644 --- a/HellionChat/Ui/StyleEngine/Widgets/ChannelHeader.cs +++ b/HellionChat/Ui/StyleEngine/Widgets/ChannelHeader.cs @@ -69,6 +69,7 @@ internal static class ChannelHeader var theme = Plugin.Instance.ThemeRegistry.Active; var surface = theme.Colors.Surface; + var surfaceAbgr = ColourUtil.RgbaToAbgr(surface); var body = BodyFace(fonts); var meta = MetaFace(fonts); @@ -163,7 +164,7 @@ internal static class ChannelHeader var dl = ImGui.GetWindowDrawList(); var bottomRight = origin + new Vector2(width, height); - dl.AddRectFilled(origin, bottomRight, ColourUtil.RgbaToAbgr(surface)); + dl.AddRectFilled(origin, bottomRight, surfaceAbgr); dl.AddLine( new Vector2(origin.X, bottomRight.Y - 1f), new Vector2(bottomRight.X, bottomRight.Y - 1f), @@ -175,8 +176,14 @@ internal static class ChannelHeader if (plan.ShowName) { - var accent = ColourUtil.RgbaToAbgr( - ColourUtil.EnsureContrast(theme.Colors.Accent, surface, 4.5f) + // Converted first, then measured. EnsureContrast works in ABGR -- + // handing it the theme's RGBA swaps red and blue on both arguments, + // so it measures a contrast that has nothing to do with what ends up + // on screen and returns a colour in the wrong order on top. + var accent = ColourUtil.EnsureContrast( + ColourUtil.RgbaToAbgr(theme.Colors.Accent), + surfaceAbgr, + 4.5f ); var x = origin.X + inset; @@ -199,8 +206,10 @@ internal static class ChannelHeader if (plan.ShowDetail) { - var muted = ColourUtil.RgbaToAbgr( - ColourUtil.EnsureContrast(theme.Colors.TextMuted, surface, 4.5f) + var muted = ColourUtil.EnsureContrast( + ColourUtil.RgbaToAbgr(theme.Colors.TextMuted), + surfaceAbgr, + 4.5f ); var whereFace = detail.WhereIsTranslated ? body : meta;