ANSI/VT transparent and black background mismatch #5463

Closed
opened 2026-01-31 00:13:58 +00:00 by claunia · 7 comments
Owner

Originally created by @PhMajerus on GitHub (Dec 8, 2019).

Tested on Windows Terminal 0.7.3382.0 from Store on Windows 10 1909 build 18363.476.
CUI App is my ActiveScript Shell, but only used because it makes it easy to send full strings to the Console API and JavaScript is easy for most to read, the problem seems to be with the rendering, or with the way conhost stores and retrieves cells colors.

ANSI VT has codes to set foreground and background colors, and codes to use default foreground and background colors (39, 49).
Windows Terminal has a nice transparency effect when the default background is used that makes it use Acrylic translucency.
The problem is that the terminal seems to lose track of what has been output as black (30 or 40) when it is the cell's background color and what has been output as default background color (49).

Writing something with a black background, using color 40 (or 30 and reversed using 7), the expectation is to have that color show as black, transparency should be achieved only by color 49.
This is important when doing ANSI-art and for consistency.
However, it seems the current implementation doesn't keep track of default colors 39 and 49, and instead assumes all cells with color 40 as their background to be the default background color or transparent, basically converting it to 49.
It also converts the other way around, using transparent (49) reversed should show the text as transparent, but instead converts it to black (30).

Reversed black vs transparent

Let me know if my explanations aren't clear enough or if you really need a Win32 API repro.

Originally created by @PhMajerus on GitHub (Dec 8, 2019). Tested on Windows Terminal 0.7.3382.0 from Store on Windows 10 1909 build 18363.476. CUI App is my ActiveScript Shell, but only used because it makes it easy to send full strings to the Console API and JavaScript is easy for most to read, the problem seems to be with the rendering, or with the way conhost stores and retrieves cells colors. ANSI VT has codes to set foreground and background colors, and codes to use default foreground and background colors (39, 49). Windows Terminal has a nice transparency effect when the default background is used that makes it use Acrylic translucency. The problem is that the terminal seems to lose track of what has been output as black (30 or 40) when it is the cell's background color and what has been output as default background color (49). Writing something with a black background, using color 40 (or 30 and reversed using 7), the expectation is to have that color show as black, transparency should be achieved only by color 49. This is important when doing ANSI-art and for consistency. However, it seems the current implementation doesn't keep track of default colors 39 and 49, and instead assumes all cells with color 40 as their background to be the default background color or transparent, basically converting it to 49. It also converts the other way around, using transparent (49) reversed should show the text as transparent, but instead converts it to black (30). ![Reversed black vs transparent](https://user-images.githubusercontent.com/25664275/70388982-aff5ff00-19b9-11ea-98e6-3d7c3e922e93.png) Let me know if my explanations aren't clear enough or if you really need a Win32 API repro.
claunia added the Resolution-Duplicate label 2026-01-31 00:13:58 +00:00
Author
Owner

@j4james commented on GitHub (Dec 8, 2019):

This sounds like #2661.

@j4james commented on GitHub (Dec 8, 2019): This sounds like #2661.
Author
Owner

@PhMajerus commented on GitHub (Dec 8, 2019):

This sounds like #2661.

Indeed, if colors are converted too early.
Important to note for this bug is that default background and default foreground should have their own indexes, and not get converted to their current color indexes either. This means storing the 16 base colors as indexes in the 256 colors palette and keeping it as index, converting them to RGB only for rendering still wouldn't be enough. we need special index values for DEFAULT_BACKGROUND and DEFAULT_FOREGROUND and map colors reset codes 39 and 49 to those.

Acrylic transparency should only happen when DEFAULT_BACKGROUND color is used, not when another color happens to have the same RGB value as the one selected as the background color, or the index selected as the background in the old console.
Also, when reverse is used (7), we can have DEFAULT_FOREGROUND used as a background color and DEFAULT_BACKGROUND used as a foreground color, those should be handled properly, and transparency should be supported on text foreground in that case.

@PhMajerus commented on GitHub (Dec 8, 2019): > This sounds like #2661. Indeed, if colors are converted too early. Important to note for this bug is that default background and default foreground should have their own indexes, and not get converted to their current color indexes either. This means storing the 16 base colors as indexes in the 256 colors palette and keeping it as index, converting them to RGB only for rendering still wouldn't be enough. we need special index values for DEFAULT_BACKGROUND and DEFAULT_FOREGROUND and map colors reset codes 39 and 49 to those. Acrylic transparency should only happen when DEFAULT_BACKGROUND color is used, not when another color happens to have the same RGB value as the one selected as the background color, or the index selected as the background in the old console. Also, when reverse is used (7), we can have DEFAULT_FOREGROUND used as a background color and DEFAULT_BACKGROUND used as a foreground color, those should be handled properly, and transparency should be supported on text foreground in that case.
Author
Owner

@j4james commented on GitHub (Dec 8, 2019):

Important to note for this bug is that default background and default foreground should have their own indexes, and not get converted to their current color indexes either.

The way it works is that default colors are indicated with a distinct type. So a color can either be default, an index, or an rgb value. You can see the ColorType enum here:
429af0e6fa/src/buffer/out/TextColor.h (L42-L47)

The narrowing problem, as I understand it, is that these color types are all first converted to a COLORREF, and then that COLORREF is sometimes "optimised" to an index value. But that can obviously result in the conpty connection receiving an index color when the original attribute was not.

@j4james commented on GitHub (Dec 8, 2019): > Important to note for this bug is that default background and default foreground should have their own indexes, and not get converted to their current color indexes either. The way it works is that default colors are indicated with a distinct type. So a color can either be default, an index, or an rgb value. You can see the `ColorType` enum here: https://github.com/microsoft/terminal/blob/429af0e6fa80412f1e9185845d856e3497a6cf78/src/buffer/out/TextColor.h#L42-L47 The narrowing problem, as I understand it, is that these color types are all first converted to a `COLORREF`, and then that `COLORREF` is sometimes "optimised" to an index value. But that can obviously result in the conpty connection receiving an index color when the original attribute was not.
Author
Owner

@PhMajerus commented on GitHub (Dec 8, 2019):

@j4james
Thanks for the details, that structure should indeed take care of the original color in all cases.

I'm concerned about the conversion to a COLORREF and optimization back to an index when the COLORRED matches a color of the palette though.
Any of the 256 colors of the palette can be redefined at any time, using the SetConsoleScreenBufferInfoEx API function for the base 16 colors, or "ESC]4;[index];rgb:[r]/[g]/[b]BEL" VT sequence for any of the 256 colors.
Because of this, any RGB color that did match a palette index earlier might now reference a color that isn't the same anymore.

This means we need to keep the original intent of the VT sequences, an index should never be converted to an RGB and an RGB should never be simplified into an index. Converting to COLORREF should be done just before rendering, as the palette might have been changed after the color codes have been output, and palette colors should always render using the current palette, not the palette from when they were received.

@PhMajerus commented on GitHub (Dec 8, 2019): @j4james Thanks for the details, that structure should indeed take care of the original color in all cases. I'm concerned about the conversion to a COLORREF and optimization back to an index when the COLORRED matches a color of the palette though. Any of the 256 colors of the palette can be redefined at any time, using the SetConsoleScreenBufferInfoEx API function for the base 16 colors, or "ESC]4;[index];rgb:[r]/[g]/[b]BEL" VT sequence for any of the 256 colors. Because of this, any RGB color that did match a palette index earlier might now reference a color that isn't the same anymore. This means we need to keep the original intent of the VT sequences, an index should never be converted to an RGB and an RGB should never be simplified into an index. Converting to COLORREF should be done just before rendering, as the palette might have been changed after the color codes have been output, and palette colors should always render using the current palette, not the palette from when they were received.
Author
Owner

@j4james commented on GitHub (Dec 8, 2019):

This means we need to keep the original intent of the VT sequences, an index should never be converted to an RGB and an RGB should never be simplified into an index.

Yes! That's exactly what issue #2661 is about!

@j4james commented on GitHub (Dec 8, 2019): > This means we need to keep the original intent of the VT sequences, an index should never be converted to an RGB and an RGB should never be simplified into an index. Yes! That's exactly what issue #2661 is about!
Author
Owner

@DHowett-MSFT commented on GitHub (Dec 8, 2019):

This specific case is actually tracked by /dupe #293. #2661 will help make sure we don’t narrow arbitrary colors into indexed ones, but 293 tracks the compatibility behavior of crushing the “default win32 console background” index (0) to “default VT background” (49). That’ll apply even in reverse video mode.

@DHowett-MSFT commented on GitHub (Dec 8, 2019): This _specific_ case is actually tracked by /dupe #293. #2661 will help make sure we don’t narrow arbitrary colors into indexed ones, but 293 tracks the compatibility behavior of crushing the “default _win32 console_ background” index (0) to “default VT background” (49). That’ll apply even in reverse video mode.
Author
Owner

@ghost commented on GitHub (Dec 8, 2019):

Hi! We've identified this issue as a duplicate of another one that already exists on this Issue Tracker. This specific instance is being closed in favor of tracking the concern over on the referenced thread. Thanks for your report!

@ghost commented on GitHub (Dec 8, 2019): Hi! We've identified this issue as a duplicate of another one that already exists on this Issue Tracker. This specific instance is being closed in favor of tracking the concern over on the referenced thread. Thanks for your report!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#5463