Skip to content

win32: Copy icon pixels to BGRA instead of mutating the shared buffer - #4677

Merged
kchibisov merged 2 commits into
rust-windowing:masterfrom
sena-nana:fix/win32-icon-double-conversion
Sep 3, 2026
Merged

win32: Copy icon pixels to BGRA instead of mutating the shared buffer#4677
kchibisov merged 2 commits into
rust-windowing:masterfrom
sena-nana:fix/win32-icon-double-conversion

Conversation

@sena-nana

Copy link
Copy Markdown
Contributor

Note

This PR is AI-implemented — it was written by an AI coding agent at the request of @sena-nana. If the approach doesn't fit the project's direction, the problem should be solved differently, or any other consideration applies, please feel free to close it without any concern.

What the bug causes

WinIcon::from_rgba converts the caller's RGBA buffer to BGRA in place, through a raw pointer derived from a shared reference (&RgbaIcon).

Two consequences:

  1. The same Icon converts differently on every call. Icon wraps an Arc<dyn IconProvider>, so Icon::clone() shares the pixel buffer. Any code path that applies one icon more than once — e.g. building both the window icon and the taskbar icon from the same clone, or re-applying an icon at runtime — makes the second from_rgba call read the already-BGRA buffer and flip it back. The second HICON then displays with red and blue swapped. We hit this in practice: a blue app icon (73, 145, 215) rendered orange (215, 145, 73) in the Windows taskbar and Alt-Tab, while the title-bar icon (the first conversion) stayed correct.
  2. The in-place write is unsound: it mutates data behind a &RgbaIcon, which may be shared via Arc, which is undefined behavior.

The fix

Copy RGBA → BGRA into an owned buffer instead of mutating the input. This makes from_rgba idempotent, keeps the first-consumer output byte-identical to before, and removes the unsafe pointer cast entirely. The now-dead Pixel struct is removed.

Verification

  • Logic-level tests comparing the old and new conversion on a shared buffer: the old code reproduces the R/B swap after a second conversion (blue → orange), while the new code is stable, does not mutate the input, and matches the old single-pass output byte for byte.
  • cargo check -p winit-win32 --target x86_64-pc-windows-msvc --all-targets and cargo clippy -p winit-win32 --target x86_64-pc-windows-msvc --all-targets -- -D warnings pass locally.
  • A regression test is included; it only compiles/runs on Windows, and the author has no Windows machine available, so Windows CI coverage is appreciated.

  • Tested on all platforms changed (not fully: no Windows machine available to the author; compile-checked for x86_64-pc-windows-msvc, Windows coverage kindly requested from CI)
  • Added an entry to the changelog module if knowledge of this change could be valuable to users
  • Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior (no public API change)
  • Created or updated an example program if it would help users understand this functionality (internal conversion change, no example needed)

Comment thread winit-win32/src/icon.rs
Comment thread winit-win32/src/icon.rs Outdated
`from_rgba` takes an immutable borrow and no longer uses the unsafe
in-place reinterpretation, so it can't touch the shared buffer by
construction; the regression test was asserting a compiler-guaranteed
invariant.
@kchibisov
kchibisov merged commit 7299858 into rust-windowing:master Sep 3, 2026
50 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants