From 76b6a9c5677213a7e5b6e64790ba646a61c647a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Sun, 23 Aug 2026 21:03:44 +0000 Subject: [PATCH] =?UTF-8?q?correct=20the=20pixel=20shifts=20=E2=80=94=20th?= =?UTF-8?q?e=20stream=20was=20cyan,=20not=20grey?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SetPixelFormat asked for 32bpp big-endian with shifts 16/8/0, which is the pixel 0x00RRGGBB and puts X,R,G,B on the wire. The Raw path copies four bytes straight into ImageData and forces alpha opaque, so every channel landed one place along and blue was dropped: window grey rendered cyan, the red LED digits rendered green, blue "1" cells and the title bar rendered black. Shifts are now 24/16/8, making the pixel 0xRRGGBB00 and the wire R,G,B,X — which is what the file already claimed, in two comments, since it was written. Verified against the live x11vnc socket rather than by reading: probing the same framebuffer under both formats returns `00 c0 c0 c0` for window grey with the old shifts and `c0 c0 c0 00` with the new ones, and `00 ff 00 00` vs `ff 00 00 00` for the digits. x11vnc honours a shift of 24 without complaint. Co-Authored-By: Claude Opus 5 Officer-Session: de3ed4e6-f88a-429c-94e6-a760336ba83b --- web/rfb.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/web/rfb.ts b/web/rfb.ts index 96ef93d..77af7fa 100644 --- a/web/rfb.ts +++ b/web/rfb.ts @@ -16,9 +16,15 @@ * ── The pixel format ── * * The client DICTATES the format via SetPixelFormat rather than accepting the server's, so decoding is - * a memcpy into ImageData instead of a per-pixel shift-and-mask. We ask for 32bpp true colour with - * big-endian byte order, which puts the bytes on the wire as R,G,B,X — exactly the order a canvas - * ImageData buffer wants, so a Raw rectangle copies straight in. + * a memcpy into ImageData instead of a per-pixel shift-and-mask. We ask for 32bpp true colour, + * big-endian, with red/green/blue shifts of 24/16/8. That makes the pixel value 0xRRGGBB00, which + * big-endian puts on the wire as R,G,B,X — exactly the order a canvas ImageData buffer wants, so a Raw + * rectangle copies straight in. + * + * The shifts are the load-bearing part, and 16/8/0 is the tempting wrong answer: it is the familiar + * 0x00RRGGBB, and big-endian then sends X,R,G,B. Copied into ImageData that moves every channel one + * place along and drops blue — grey renders cyan, red renders green, blue renders black. The first + * alpha shipped that way, with this comment already claiming otherwise. */ export type RfbEvents = { @@ -190,7 +196,7 @@ export class RfbClient { return true; } - /** 32bpp true colour, big-endian, shifts 16/8/0 — i.e. R,G,B,X on the wire, which is ImageData order. */ + /** 32bpp true colour, big-endian, shifts 24/16/8 — i.e. R,G,B,X on the wire, which is ImageData order. */ private setPixelFormat(): void { const message = new Uint8Array(20); const view = new DataView(message.buffer); @@ -202,9 +208,9 @@ export class RfbClient { view.setUint16(8, 255); // red max view.setUint16(10, 255); // green max view.setUint16(12, 255); // blue max - message[14] = 16; // red shift - message[15] = 8; // green shift - message[16] = 0; // blue shift + message[14] = 24; // red shift + message[15] = 16; // green shift + message[16] = 8; // blue shift this.send(message); }