Repository navigation
feat(egfx): decode Planar bitmaps in the client - #1507
Merged
Benoît Cortier (CBenoit) merged 1 commit intoJul 31, 2026
Merged
Benoît Cortier (CBenoit) merged 1 commit into
Benoît Cortier (CBenoit) merged 1 commit into
Conversation
RDPGFX_CODECID_PLANAR (0x000A) reached the client's WireToSurface1 dispatch and fell through to on_unhandled_pdu, so a server sending Planar frames got nothing drawn. The decoder for it already existed: [MS-RDPEGFX] 2.2.2.1 defines the payload as the Planar Codec from [MS-RDPEGDI] 2.2.2.5.1 and 3.1.9, which is the RDP6_BITMAP_STREAM that ironrdp-graphics decodes and that ironrdp-session already uses for the fast-path bitmap route. Wire it up alongside ClearCodec. ironrdp-egfx already depends on ironrdp-graphics, so this reuses rdp6::BitmapStreamDecoder rather than adding a second implementation. Three details from the spec that shape the decode: Dimensions come from destRect. [MS-RDPEGFX] 2.2.2.1 says the field carries both the target point and "the dimensions (width and height) of the bitmap data", and is a bounding rectangle only for AVC420, AVC444 and AVC444V2, so for Planar the rectangle is exact. Pixels are opaque. An RDP6_BITMAP_STREAM can carry an alpha plane when the FormatHeader NA bit is clear, but EGFX conveys per-pixel opacity in a separate RDPGFX_CODECID_ALPHA (0x000C) command holding an ALPHACODEC_BITMAP_STREAM ([MS-RDPEGFX] 2.2.4.3). A Planar command therefore contributes color only, so the decoder's RGB24 output is widened with a synthesized opaque alpha. No vertical flip. The decoder emits top-down row-major, matching how surfaces are stored. The test encodes with IronRDP's own RDP6 encoder and drives a real WireToSurface1 through the client, covering both the RLE and raw plane forms, so it exercises the actual stream framing rather than a hand-built approximation. It fails without the dispatch arm.
Benoît Cortier (CBenoit)
merged commit Jul 31, 2026
66c8a81
into
Devolutions:master
24 of 25 checks passed
Contributor
There was a problem hiding this comment.
Pull request overview
Adds Planar bitmap decoding to the EGFX client.
Changes:
- Dispatches Planar
WireToSurface1PDUs to the RDP6 decoder. - Converts decoded RGB24 pixels to opaque RGBA.
- Adds raw/RLE decoding tests.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| height: dest_height, | ||
| }; | ||
|
|
||
| self.handler.on_bitmap_updated(&update); |
| /// | ||
| /// ClearCodec produces BGRA output per [MS-RDPEGFX 2.2.4.1]. Reorder to | ||
| /// [R, G, B, A] for the uniform `BitmapUpdate` pixel format. | ||
| /// Widen tightly-packed RGB24 to RGBA, marking every pixel opaque. |
Marc-André Moreau (mamoreau-devolutions)
pushed a commit
that referenced
this pull request
Aug 2, 2026
Wires `RDPGFX_CODECID_PLANAR` (0x000A) into the EGFX client's `WireToSurface1` dispatch, alongside the existing ClearCodec and Uncompressed paths.
This was referenced Aug 4, 2026
This was referenced Aug 13, 2026
truebest
pushed a commit
to truebest/IronRDP
that referenced
this pull request
Aug 16, 2026
Wires `RDPGFX_CODECID_PLANAR` (0x000A) into the EGFX client's `WireToSurface1` dispatch, alongside the existing ClearCodec and Uncompressed paths. (cherry picked from commit 66c8a81)
This was referenced Aug 27, 2026
This was referenced Sep 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Wires
RDPGFX_CODECID_PLANAR(0x000A) into the EGFX client'sWireToSurface1dispatch, alongside the existing ClearCodec and Uncompressed paths.Why
Planar reached the dispatch and fell through to
on_unhandled_pdu, so a server sending Planar frames got nothing drawn. The decoder already existed: [MS-RDPEGFX] 2.2.2.1 defines the payload for this codec ID as the Planar Codec from [MS-RDPEGDI] 2.2.2.5.1 and 3.1.9, which is theRDP6_BITMAP_STREAMthatironrdp-graphicsdecodes and thatironrdp-sessionalready uses for the fast-path bitmap route.This is the same decode-core-exists-but-is-not-wired situation ClearCodec was in before #1175. #1498 makes it reachable in-tree:
send_planar_framemeans an IronRDP server can now send frames an IronRDP client would discard.ironrdp-egfxalready depends onironrdp-graphicsand already pullsClearCodecDecoderfrom it, so this reusesrdp6::BitmapStreamDecoderrather than adding a second implementation.Three spec details that shape the decode
Dimensions come from
destRect. [MS-RDPEGFX] 2.2.2.1 says the field carries both the target point and "the dimensions (width and height) of the bitmap data encapsulated in the bitmapData field", and that it "specifies a bounding rectangle if the codecId field contains the RDPGFX_CODECID_AVC420 (0x000B), RDPGFX_CODECID_AVC444 (0x000E), or the RDPGFX_CODECID_AVC444V2 (0x000F) identifier". Planar is not in that list, so the rectangle is exact and its extent is what the decoder is given.Pixels are opaque, deliberately. An
RDP6_BITMAP_STREAMcan carry an alpha plane: [MS-RDPEGDI] 2.2.2.5.1 defines theNAbit as "Indicates if an alpha plane is present. If NA is equal to 1, there is no alpha plane... If NA is equal to 0, the alpha plane is sent as the first color plane." EGFX does not use that channel for opacity. It carries per-pixel opacity in a separateRDPGFX_CODECID_ALPHA(0x000C) command holding anALPHACODEC_BITMAP_STREAM([MS-RDPEGFX] 2.2.4.3), whose stated purpose is to specify "the opacity of each pixel in the encoded bitmap". So a Planar command contributes color only, and the decoder's RGB24 output is widened with a synthesized opaque alpha. That reasoning is in a comment at the conversion site so it does not read like an oversight.No vertical flip. The RDP6 decoder emits top-down row-major, which is how surfaces are stored, so the rows are used as produced.
I also checked whether Planar needs capability negotiation before a server may send it. It does not: the only mentions of the codec in [MS-RDPEGFX] are the codec ID definition itself, unlike the AVC codecs which are capability-gated.
Tests
The round-trip test builds its payload with IronRDP's own RDP6 encoder and drives a real
WireToSurface1through the client, so it exercises the actual stream framing rather than a hand-built approximation. It covers both the RLE and raw plane forms, uses distinct per-channel values so a channel swap or row reversal fails, and asserts the update is not routed toon_unhandled_pdu. It fails against the previous code.There is also a unit test for the RGB24 to RGBA widening.
Scope
No public API change;
cargo semver-checksreports no version bump required. Related to #1464.Verification
cargo xtask check fmt/lints/tests/typos/locksall pass.