[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v2 2/3] ui/vnc: take account of client byte order in pixman forma
From: |
Daniel P . Berrangé |
Subject: |
[PATCH v2 2/3] ui/vnc: take account of client byte order in pixman format |
Date: |
Wed, 14 May 2025 12:19:30 +0100 |
The set_pixel_conversion() method is responsible for determining whether
the VNC client pixel format matches the server format, and thus whether
we can use the fast path "copy" impl for sending pixels, or must use
the generic impl with bit swizzling.
The VNC server format is set at build time to VNC_SERVER_FB_FORMAT,
which corresponds to PIXMAN_x8r8g8b8.
The qemu_pixman_get_format() method is then responsible for converting
the VNC pixel format into a pixman format.
The VNC client pixel shifts are relative to the associated endianness.
The pixman formats are always relative to the host native endianness.
The qemu_pixman_get_format() method does not take into account the
VNC client endianness, and is thus returning a pixman format that is
only valid with the host endianness matches that of the VNC client.
This has been broken since pixman was introduced to the VNC server:
commit 9f64916da20eea67121d544698676295bbb105a7
Author: Gerd Hoffmann <kraxel@redhat.com>
Date: Wed Oct 10 13:29:43 2012 +0200
pixman/vnc: use pixman images in vnc.
The flaw can be demonstrated using the Tigervnc client by using
vncviewer -AutoSelect=0 -PreferredEncoding=raw server:display
connecting from a LE client to a QEMU on a BE server, or the
reverse.
The bug was masked, however, because almost all VNC clients will
advertize support for the "tight" encoding and the QEMU VNC server
will prefer "tight" if advertized.
The tight_pack24 method is responsible for taking a set of pixels
which have already been converted into client endianness and then
repacking them into the TPIXEL format which the RFB spec defines
as
"TPIXEL is only 3 bytes long, where the first byte is the
red component, the second byte is the green component,
and the third byte is the blue component of the pixel
color value"
IOW, the TPIXEL format is fixed on the wire, regardless of what
the VNC client declare as its endianness.
Since the VNC pixel encoding code was failing to honour the endian
flag of the client, the tight_pack24 method was always operating
on data in native endianness. Its impl cancelled out the VNC pixel
encoding bug.
With the VNC pixel encoding code now fixed, the tight_pack24 method
needs to take into account that it is operating on data in client
endianness, not native endianness. It thus may need to invert the
pixel shifts.
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
include/ui/qemu-pixman.h | 4 ++--
ui/qemu-pixman.c | 15 ++++++++-------
ui/vnc-enc-tight.c | 2 +-
ui/vnc.c | 3 ++-
4 files changed, 13 insertions(+), 11 deletions(-)
diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h
index 193bc046d1..2ca0ed7029 100644
--- a/include/ui/qemu-pixman.h
+++ b/include/ui/qemu-pixman.h
@@ -75,12 +75,12 @@ PixelFormat
qemu_pixelformat_from_pixman(pixman_format_code_t format);
pixman_format_code_t qemu_default_pixman_format(int bpp, bool native_endian);
pixman_format_code_t qemu_drm_format_to_pixman(uint32_t drm_format);
uint32_t qemu_pixman_to_drm_format(pixman_format_code_t pixman);
-int qemu_pixman_get_type(int rshift, int gshift, int bshift);
+int qemu_pixman_get_type(int rshift, int gshift, int bshift, int endian);
bool qemu_pixman_check_format(DisplayChangeListener *dcl,
pixman_format_code_t format);
#ifdef CONFIG_PIXMAN
-pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf);
+pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf, int endian);
pixman_image_t *qemu_pixman_linebuf_create(pixman_format_code_t format,
int width);
void qemu_pixman_linebuf_fill(pixman_image_t *linebuf, pixman_image_t *fb,
diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c
index 6ef4376f4e..ef4e71da11 100644
--- a/ui/qemu-pixman.c
+++ b/ui/qemu-pixman.c
@@ -126,33 +126,34 @@ uint32_t qemu_pixman_to_drm_format(pixman_format_code_t
pixman_format)
return 0;
}
-int qemu_pixman_get_type(int rshift, int gshift, int bshift)
+int qemu_pixman_get_type(int rshift, int gshift, int bshift, int endian)
{
int type = PIXMAN_TYPE_OTHER;
+ bool native_endian = (endian == G_BYTE_ORDER);
if (rshift > gshift && gshift > bshift) {
if (bshift == 0) {
- type = PIXMAN_TYPE_ARGB;
+ type = native_endian ? PIXMAN_TYPE_ARGB : PIXMAN_TYPE_BGRA;
} else {
- type = PIXMAN_TYPE_RGBA;
+ type = native_endian ? PIXMAN_TYPE_RGBA : PIXMAN_TYPE_ABGR;
}
} else if (rshift < gshift && gshift < bshift) {
if (rshift == 0) {
- type = PIXMAN_TYPE_ABGR;
+ type = native_endian ? PIXMAN_TYPE_ABGR : PIXMAN_TYPE_RGBA;
} else {
- type = PIXMAN_TYPE_BGRA;
+ type = native_endian ? PIXMAN_TYPE_BGRA : PIXMAN_TYPE_ARGB;
}
}
return type;
}
#ifdef CONFIG_PIXMAN
-pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf)
+pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf, int endian)
{
pixman_format_code_t format;
int type;
- type = qemu_pixman_get_type(pf->rshift, pf->gshift, pf->bshift);
+ type = qemu_pixman_get_type(pf->rshift, pf->gshift, pf->bshift, endian);
format = PIXMAN_FORMAT(pf->bits_per_pixel, type,
pf->abits, pf->rbits, pf->gbits, pf->bbits);
if (!pixman_format_supported_source(format)) {
diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c
index f8aaa8f346..a5bdc19ebb 100644
--- a/ui/vnc-enc-tight.c
+++ b/ui/vnc-enc-tight.c
@@ -891,7 +891,7 @@ static void tight_pack24(VncState *vs, uint8_t *buf, size_t
count, size_t *ret)
buf8 = buf;
- if (1 /* FIXME */) {
+ if (vs->client_endian == G_BYTE_ORDER) {
rshift = vs->client_pf.rshift;
gshift = vs->client_pf.gshift;
bshift = vs->client_pf.bshift;
diff --git a/ui/vnc.c b/ui/vnc.c
index ab18172c4d..d095cd7da3 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -2240,7 +2240,8 @@ static void set_encodings(VncState *vs, int32_t
*encodings, size_t n_encodings)
static void set_pixel_conversion(VncState *vs)
{
- pixman_format_code_t fmt = qemu_pixman_get_format(&vs->client_pf);
+ pixman_format_code_t fmt = qemu_pixman_get_format(&vs->client_pf,
+ vs->client_endian);
if (fmt == VNC_SERVER_FB_FORMAT) {
vs->write_pixels = vnc_write_pixels_copy;
--
2.49.0