Skip to content

Fix BitmapXLib::GetPixel mistagging 32-bit pixels as RGB colorspace - #12

Open
ElCruncharino wants to merge 1 commit into
enzo1982:masterfrom
ElCruncharino:fix-bitmap-getpixel-alpha-colorspace
Open

ElCruncharino wants to merge 1 commit into
enzo1982:masterfrom
ElCruncharino:fix-bitmap-getpixel-alpha-colorspace

Conversation

@ElCruncharino

@ElCruncharino ElCruncharino commented Jul 10, 2026

Copy link
Copy Markdown

Problem

For 32-bit (RGBA) bitmaps, BitmapXLib::GetPixel() builds its return value like this:

return ((value >> 24) & 255) << 24 | Color((value >> 16) & 255, (value >> 8) & 255, value & 255, Color::RGBA);

The Color(r, g, b, colorSpace) constructor packs r/g/b into the low 24 bits but does not encode the passed colorSpace into the packed Long value at all - it's tracked separately as an object member. Once this Color gets implicitly converted to Long (via operator Long()) and OR'd with the alpha byte, the result is a plain Long, which is then implicitly converted back to a Color via Color(Long, ColorSpace = RGB) - defaulting to the RGB colorspace tag, even though the value carries real alpha data in its high byte.

Color::ConvertTo(RGB) trusts this tag: for an already-RGB-tagged color it just returns the object unchanged (assuming no alpha content), instead of stripping the alpha bits. So any code that compares GetPixel(...).ConvertTo(RGB) against a reference color (e.g. Bitmap::ReplaceColor, which matches pixels by RGB while preserving their alpha) silently fails to match any pixel with non-zero alpha - i.e. every visible pixel in a typical antialiased icon/mask image. Only fully transparent pixels (alpha == 0) happen to compare equal by coincidence.

Fix

Construct the Long value directly and pass Color::RGBA explicitly to the Color(Long, ColorSpace) constructor, so the returned color is correctly tagged and ConvertTo(RGB) properly strips the alpha bits as intended.

Impact

This affects any caller on Linux/X11 that reads back pixel colors from a 32-bit bitmap and expects alpha-aware comparisons to work correctly - most notably Bitmap::ReplaceColor(), which is the documented way to recolor a monochrome icon (solid color + alpha mask) at runtime, e.g. for theme-adaptive icon tinting. Before this fix, ReplaceColor() silently did nothing on any bitmap loaded through this backend.

The alpha byte was OR'd into a Long returned as a Color that defaults
to the RGB colorspace tag when constructed from a bare Long, even
though it carries real alpha data. Color::ConvertTo(RGB) trusts that
tag and returns RGB-space colors unchanged, so any comparison against
an alpha-stripped color (e.g. Bitmap::ReplaceColor matching a mask
color) silently failed for every pixel with non-zero alpha - exactly
the ones that are visible.
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