Fixes https://bugs.winehq.org/show_bug.cgi?id=58620
I've made this a separate MR from !8969 because that MR contains a lot of highly debatable architectural decisions (such as FAKE_16BIT_MEMDC_PIXEL_FORMAT and all of its special case branches). This MR is very minimal and straight to the point.
-- v2: win32u: Fix garbled 24-bit bitmap rendering
From: Zowie van Dillen zowie+wine@vandillen.io
--- dlls/win32u/opengl.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/dlls/win32u/opengl.c b/dlls/win32u/opengl.c index efd6ea63b1a..63b378affd8 100644 --- a/dlls/win32u/opengl.c +++ b/dlls/win32u/opengl.c @@ -1251,9 +1251,10 @@ static BOOL flush_memory_dc( struct wgl_context *context, HDC hdc, BOOL write, v
if (!get_image_from_bitmap( bmp, info, &bits, &src )) { - int width = info->bmiHeader.biWidth, height = info->bmiHeader.biSizeImage / 4 / width; - if (write) funcs->p_glDrawPixels( width, height, GL_BGRA, GL_UNSIGNED_BYTE, bits.ptr ); - else funcs->p_glReadPixels( 0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, bits.ptr ); + int width = info->bmiHeader.biWidth, height = abs( info->bmiHeader.biHeight ); + UINT format = (info->bmiHeader.biBitCount == 24 ? GL_BGR : GL_BGRA); + if (write) funcs->p_glDrawPixels( width, height, format, GL_UNSIGNED_BYTE, bits.ptr ); + else funcs->p_glReadPixels( 0, 0, width, height, format, GL_UNSIGNED_BYTE, bits.ptr ); } GDI_ReleaseObj( dc->hBitmap ); }
Rémi Bernon (@rbernon) commented about dlls/win32u/opengl.c:
if (!get_image_from_bitmap( bmp, info, &bits, &src )) {
int width = info->bmiHeader.biWidth, height = info->bmiHeader.biSizeImage / 4 / width;
if (write) funcs->p_glDrawPixels( width, height, GL_BGRA, GL_UNSIGNED_BYTE, bits.ptr );
else funcs->p_glReadPixels( 0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, bits.ptr );
int width = info->bmiHeader.biWidth, height = abs( info->bmiHeader.biHeight );
UINT format = (info->bmiHeader.biBitCount == 24 ? GL_BGR : GL_BGRA);
if (write) funcs->p_glDrawPixels( width, height, format, GL_UNSIGNED_BYTE, bits.ptr );
else funcs->p_glReadPixels( 0, 0, width, height, format, GL_UNSIGNED_BYTE, bits.ptr );
I believe there was a buffer overflow as GL / GDI currently don't agree on whether the bitmap is 16bpp or 32bpp, and `height = info->bmiHeader.biSizeImage / 4 / width` was there to avoid reading / writing past the bitmap buffer. This would probably be fixed with your other 16bpp changes, can we keep it for now or is it part of the fix here?
On Fri Sep 19 07:47:10 2025 +0000, Rémi Bernon wrote:
I believe there was a buffer overflow as GL / GDI currently don't agree on whether the bitmap is 16bpp or 32bpp, and `height = info->bmiHeader.biSizeImage / 4 / width` was there to avoid reading / writing past the bitmap buffer. This would probably be fixed with your other 16bpp changes, can we keep it for now or is it part of the fix here?
For a 32 bit and a 24 bit GDI bitmap, this MR will work just fine, because when you ask OpenGL for GL_BGRA it will give you four bytes, but when you ask for GL_BGR it gives you three bytes. You're right though that 16bpp bitmaps will lead to an out-of-bounds write with only this change. I hadn't thought about that. I think together with the other MR it should work because the 16bpp case will be explicitly handled, so I'll mark this as a draft and I'll reopen it after the other one's merged. I still ought to make some changes to that one.
This merge request was closed by Zowie van Dillen.
Superseded by !8969