From 8a0f649363e349817c88bd5d9fd940dffaca2d7c Mon Sep 17 00:00:00 2001 From: evertonse Date: Mon, 4 Nov 2024 15:31:45 -0300 Subject: [PATCH] windows: fix BITMAPINFOHEADER values when getting bitmap from Clipboard --- src/video/windows/SDL_windowsclipboard.c | 50 +++++++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/src/video/windows/SDL_windowsclipboard.c b/src/video/windows/SDL_windowsclipboard.c index 6a0cc35c74..e62936bb9a 100644 --- a/src/video/windows/SDL_windowsclipboard.c +++ b/src/video/windows/SDL_windowsclipboard.c @@ -41,6 +41,51 @@ // Assume we can directly read and write BMP fields without byte swapping SDL_COMPILE_TIME_ASSERT(verify_byte_order, SDL_BYTEORDER == SDL_LIL_ENDIAN); +static int WIN_GetPixelDataOffset(BITMAPINFOHEADER bih) +{ + int offset = 0; + // biSize Specifies the number of bytes required by the structure + // We expect to always be 40 because it should be packed + if (40 == bih.biSize && 40 == sizeof(BITMAPINFOHEADER)) + { + // + // biBitCount Specifies the number of bits per pixel. + // Might exist some bit masks *after* the header and *before* the pixel offset + // we're looking, but only if we have more than + // 8 bits per pixel, so we need to ajust for that + // + if (bih.biBitCount > 8) + { + // If bih.biCompression is RBG we should NOT offset more + + if (bih.biCompression == BI_BITFIELDS) + { + offset += 3 * sizeof(RGBQUAD); + } else if (bih.biCompression == 6 /* BI_ALPHABITFIELDS */) { + // Not common, but still right + offset += 4 * sizeof(RGBQUAD); + } + } + } + + // + // biClrUsed Specifies the number of color indices in the color table that are actually used by the bitmap. + // If this value is zero, the bitmap uses the maximum number of colors + // corresponding to the value of the biBitCount member for the compression mode specified by biCompression. + // If biClrUsed is nonzero and the biBitCount member is less than 16 + // the biClrUsed member specifies the actual number of colors + // + if (bih.biClrUsed > 0) { + offset += bih.biClrUsed * sizeof(RGBQUAD); + } else { + if (bih.biBitCount < 16) { + offset = offset + (sizeof(RGBQUAD) << bih.biBitCount); + } + } + return bih.biSize + offset; +} + + static BOOL WIN_OpenClipboard(SDL_VideoDevice *_this) { // Retry to open the clipboard in case another application has it open @@ -114,8 +159,9 @@ static void *WIN_ConvertDIBtoBMP(HANDLE hMem, size_t *size) BITMAPINFOHEADER *pbih = (BITMAPINFOHEADER *)dib; size_t bih_size = pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD); size_t dib_size = bih_size + pbih->biSizeImage; + int pixel_offset = WIN_GetPixelDataOffset(*pbih); if (dib_size <= mem_size) { - size_t bmp_size = sizeof(BITMAPFILEHEADER) + dib_size; + size_t bmp_size = sizeof(BITMAPFILEHEADER) + mem_size; bmp = SDL_malloc(bmp_size); if (bmp) { BITMAPFILEHEADER *pbfh = (BITMAPFILEHEADER *)bmp; @@ -123,7 +169,7 @@ static void *WIN_ConvertDIBtoBMP(HANDLE hMem, size_t *size) pbfh->bfSize = (DWORD)bmp_size; pbfh->bfReserved1 = 0; pbfh->bfReserved2 = 0; - pbfh->bfOffBits = (DWORD)(sizeof(BITMAPFILEHEADER) + bih_size); + pbfh->bfOffBits = (DWORD)(sizeof(BITMAPFILEHEADER) + pixel_offset); SDL_memcpy((Uint8 *)bmp + sizeof(BITMAPFILEHEADER), dib, dib_size); *size = bmp_size; }