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); }