Internal
Public Access
Add VNC Hextile decoding
Implements RFC 6143 SS7.7.4: rectangles announced as Hextile (type 5) tile the update into 16x16 blocks, each either raw pixels or a background fill plus an optional list of foreground/individually- colored subrects, with background/foreground persisting across tiles within one rectangle when not re-specified. The pure byte-decode logic (tile metadata, subrect list) lives in new src/vnc_pixel_codecs.h/.cpp, kept separate from VncSessionBackend's wire-sequencing state machine so it's unit-testable without a socket -- the pattern the plan calls for continuing into the ZRLE/Tight work still ahead. Adds 5 fake-server tests covering a raw tile, a background-only solid fill, uncoloured and individually-colored subrects, and a 4-tile rectangle proving background persistence and correct tile-cursor wraparound. Verified against the live TightVNC test server (connect, frame, cursor, clipboard all still work); that particular server always chose Raw for the actual framebuffer content during this session, so Hextile's real-world path isn't independently confirmed live -- the unit tests are the primary correctness evidence for this phase. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
#include "vnc_pixel_codecs.h"
|
||||
|
||||
namespace VncPixelCodecs {
|
||||
|
||||
QRgb rgbFromPixelBytes(const uchar* bytes)
|
||||
{
|
||||
// Byte order B,G,R,pad -- matches VncSessionBackend's negotiated
|
||||
// SetPixelFormat (32bpp little-endian, R at shift 16 / G at 8 / B at 0).
|
||||
return qRgb(bytes[2], bytes[1], bytes[0]);
|
||||
}
|
||||
|
||||
int hextileFixedMetaByteCount(quint8 subencoding)
|
||||
{
|
||||
int count = 0;
|
||||
if ((subencoding & HextileFlags::kBackgroundSpecified) != 0) {
|
||||
count += 4;
|
||||
}
|
||||
if ((subencoding & HextileFlags::kForegroundSpecified) != 0) {
|
||||
count += 4;
|
||||
}
|
||||
if ((subencoding & HextileFlags::kAnySubrects) != 0) {
|
||||
count += 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int decodeHextileFixedMeta(quint8 subencoding, const QByteArray& data, QRgb* background,
|
||||
QRgb* foreground)
|
||||
{
|
||||
int offset = 0;
|
||||
const auto* bytes = reinterpret_cast<const uchar*>(data.constData());
|
||||
|
||||
if ((subencoding & HextileFlags::kBackgroundSpecified) != 0) {
|
||||
*background = rgbFromPixelBytes(bytes + offset);
|
||||
offset += 4;
|
||||
}
|
||||
if ((subencoding & HextileFlags::kForegroundSpecified) != 0) {
|
||||
*foreground = rgbFromPixelBytes(bytes + offset);
|
||||
offset += 4;
|
||||
}
|
||||
if ((subencoding & HextileFlags::kAnySubrects) != 0) {
|
||||
return static_cast<int>(static_cast<quint8>(data.at(offset)));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hextileSubrectByteCount(bool coloured, int subrectCount)
|
||||
{
|
||||
return subrectCount * (coloured ? 6 : 2);
|
||||
}
|
||||
|
||||
QVector<HextileSubrect> decodeHextileSubrects(bool coloured, int subrectCount,
|
||||
const QByteArray& data, QRgb foreground)
|
||||
{
|
||||
QVector<HextileSubrect> subrects;
|
||||
subrects.reserve(subrectCount);
|
||||
|
||||
const auto* bytes = reinterpret_cast<const uchar*>(data.constData());
|
||||
const int stride = coloured ? 6 : 2;
|
||||
for (int i = 0; i < subrectCount; ++i) {
|
||||
const uchar* entry = bytes + (i * stride);
|
||||
QRgb color = foreground;
|
||||
int fieldOffset = 0;
|
||||
if (coloured) {
|
||||
color = rgbFromPixelBytes(entry);
|
||||
fieldOffset = 4;
|
||||
}
|
||||
const uchar xy = entry[fieldOffset];
|
||||
const uchar wh = entry[fieldOffset + 1];
|
||||
// High nibble = x (or width-1), low nibble = y (or height-1).
|
||||
const int x = (xy >> 4) & 0x0F;
|
||||
const int y = xy & 0x0F;
|
||||
const int width = ((wh >> 4) & 0x0F) + 1;
|
||||
const int height = (wh & 0x0F) + 1;
|
||||
subrects.append(HextileSubrect{QRect(x, y, width, height), color});
|
||||
}
|
||||
return subrects;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user