Skip to content

Feature: text glow rendering and an "outline all text" option#190

Open
Shuro wants to merge 2 commits into
Infiziert90:mainfrom
Shuro:feature/text-glow-support
Open

Feature: text glow rendering and an "outline all text" option#190
Shuro wants to merge 2 commits into
Infiziert90:mainfrom
Shuro:feature/text-glow-support

Conversation

@Shuro

@Shuro Shuro commented Jul 6, 2026

Copy link
Copy Markdown

What

  • Render game-specified glow colours as text outlines. Chat messages that carry a UIGlow payload (e.g. the red "Dalamud" in the welcome message, duty/collectable notices) now render with their outline in Chat 2, matching vanilla chat. Toggleable via a new Render glow option (on by default).
  • New "Outline all text" option (off by default): draws a configurable outline around all chat text for readability, with a colour picker and a reset button in the Fonts settings tab.

How

The outline is drawn by stamping the text into the window draw list 8 times at ±1px offsets before the text itself is drawn (ImGuiUtil.DrawTextGlow).

Two details were needed to actually match vanilla rendering (verified by pixel-level comparison of vanilla vs Chat 2 screenshots):

  1. Glow colours resolve via the dark-theme UIColor variant. The UIColor sheet columns (Dark, Light, ClassicFF, …) are per-UI-theme variants of one colour. Using .Light yields pastel light-theme colours (a red glow rendered as salmon-pink); .Dark matches what vanilla chat displays, consistent with how foreground colours are already resolved.
  2. Glow alpha is scaled by 0x66/0xFF per stamp. Vanilla's glow peaks at ~85% opacity with a soft falloff. Game glow payloads are fully opaque, and overlapping stamps compound alpha (two stamps at 85% already composite to ~98%), producing a hard solid ring instead of a glow. Scaling each stamp to 0x66 makes the composite across overlapping stamps land at vanilla's ~85% peak — measured within ~1% of vanilla on identical test messages. The default outline colour for Outline all text is 0x00000066 for the same reason.

Disclosure:
This was co-developed with AI assistance (Claude Code).
I've reviewed the code and tested it in game myself.

Screenshots

Vanilla Chat 2
2026-07-06_03-33-51_ffxiv_dx11_el67hBtiLB 2026-07-06_03-33-42_ffxiv_dx11_N5AF5PjTl2

@Shuro Shuro force-pushed the feature/text-glow-support branch from 74cac90 to e8b324d Compare July 6, 2026 01:44
- Render the game-specified glow colour as a text outline in chat
- Resolve game glow payloads via the dark-theme UIColor variant to
  match vanilla chat (the sheet columns are UI theme variants)
- Scale game glow alpha by 0x66/0xFF so the composite opacity across
  the 8 outline stamps matches the vanilla glow (~85% peak)
- Add an option to outline all text with a configurable colour
- Default outline colour is black at 0x66 alpha for the same reason
- Reset button for the outline colour, styled like the chat colours tab
@Shuro Shuro force-pushed the feature/text-glow-support branch from e8b324d to d4a5ceb Compare July 6, 2026 08:43
- Use a fixed outline stamp alpha instead of deriving it from the pushed
  color; the game renders edge colors fully opaque regardless of their
  alpha byte
- Parse raw EdgeColor macros through ColorPayload (packed ints, gnum,
  stackcolor and small-int literals) instead of popping the glow stack
  on any raw payload
- Push resolved zero values as real no-glow entries to keep the color
  stack balanced, matching the vanilla renderer
- Guard UIColor sheet lookups: a color payload referencing a missing
  row previously threw and dropped the whole message
- Return null from ColorPayload.From on truncated packed integers
  instead of throwing
- Decode EdgeColor payloads in the SeString debugger
@Shuro Shuro force-pushed the feature/text-glow-support branch from d4a5ceb to 3de81c3 Compare July 6, 2026 08:57
@Shuro

Shuro commented Jul 6, 2026

Copy link
Copy Markdown
Author

Thanks for the hint in Discord regarding the broken alpha handling on raw values.
Reworked it in the latest commit:

Glow alpha no longer derived from the pushed color (ImGuiUtil.cs)
The game ignores the alpha byte of pushed edge colors entirely and always renders them fully opaque, for both EdgeColorType sheet lookups and raw EdgeColor values. So the glow now takes only the RGB from the pushed color and uses a fixed per-stamp alpha (0x66), whose composite over the 8 outline stamps matches the vanilla glow strength.

Raw EdgeColor macros are now actually parsed (ExtraPayload.cs, ChunkUtil.cs)
Previously any raw 0x14 payload just popped the glow stack. ColorPayload now handles both Color and EdgeColor and decodes all expression forms: packed ints (3- and 4-byte), gnum global parameters, inline small integers, and stackcolor pops. Values resolving to 0 are pushed as real "no glow" entries, that's what the game does (verified below), and it keeps the stack balanced for the matching stackcolor pop.

Pushed colors are forced opaque (ColourUtil.cs)
ArgbToRgba previously only forced alpha when the byte was absent, so something like color(0x05FF0000) rendered near-invisible text. Alpha is now forced to 0xFF for any nonzero pushed value, matching the game.

Hardening (ChunkUtil.cs)
UIForeground/UIGlow payloads referencing rows missing from the UIColor sheet used to throw and drop the whole message; they're now handled gracefully.

Debugger (SeStringDebugger.cs)
The SeString debugger now decodes EdgeColor payloads as well and labels which macro family a color payload came from.

Everything was validated side-by-side against the vanilla renderer by printing hand-crafted payloads (packed RGBA with and without alpha, gnum, literals, stackcolor chains, invalid sheet rows) to the native log and comparing, the two now render identically.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant