[NTGDI][GDI32] Icon fixes for Office 2000, VB6, and Hoyle Cards (#5227)

Many thanks for Simone Lombardo for pointing to the code needing attention
and providing a working proof-of-concept solution.

CORE-12377
CORE-18084
CORE-13889
This commit is contained in:
Doug Lyons 2023-07-02 06:24:15 -05:00 committed by GitHub
parent 38560761ce
commit 12e1919e5e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 282 additions and 4 deletions

View file

@ -5,6 +5,52 @@
#define NDEBUG
#include <debug.h>
/* Copied from win32ss/gdi/eng/surface.c */
ULONG
FASTCALL
BitmapFormat(ULONG cBits, ULONG iCompression)
{
switch (iCompression)
{
case BI_RGB:
/* Fall through */
case BI_BITFIELDS:
if (cBits <= 1) return BMF_1BPP;
if (cBits <= 4) return BMF_4BPP;
if (cBits <= 8) return BMF_8BPP;
if (cBits <= 16) return BMF_16BPP;
if (cBits <= 24) return BMF_24BPP;
if (cBits <= 32) return BMF_32BPP;
return 0;
case BI_RLE4:
return BMF_4RLE;
case BI_RLE8:
return BMF_8RLE;
default:
return 0;
}
}
/* Copied from win32ss/gdi/eng/surface.c */
UCHAR
gajBitsPerFormat[11] =
{
0, /* 0: unused */
1, /* 1: BMF_1BPP */
4, /* 2: BMF_4BPP */
8, /* 3: BMF_8BPP */
16, /* 4: BMF_16BPP */
24, /* 5: BMF_24BPP */
32, /* 6: BMF_32BPP */
4, /* 7: BMF_4RLE */
8, /* 8: BMF_8RLE */
0, /* 9: BMF_JPEG */
0, /* 10: BMF_PNG */
};
// From Yuan, ScanLineSize = (Width * bitcount + 31)/32
#define WIDTH_BYTES_ALIGN32(cx, bpp) ((((cx) * (bpp) + 31) & ~31) >> 3)
@ -577,6 +623,9 @@ SetDIBits(
INT LinesCopied = 0;
BOOL newDC = FALSE;
if (fuColorUse != DIB_RGB_COLORS && fuColorUse != DIB_PAL_COLORS)
return 0;
if (!lpvBits || (GDI_HANDLE_GET_TYPE(hBitmap) != GDI_OBJECT_TYPE_BITMAP))
return 0;
@ -590,6 +639,16 @@ SetDIBits(
}
}
if (lpbmi->bmiHeader.biCompression == BI_BITFIELDS)
{
DWORD *masks = (DWORD *)lpbmi->bmiColors;
if (!masks[0] || !masks[1] || !masks[2])
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
}
hDCc = NtGdiGetDCforBitmap(hBitmap); // hDC can be NULL, so, get it from the bitmap.
SavehDC = hDCc;
if (!hDCc) // No DC associated with bitmap, Clone or Create one.
@ -666,10 +725,31 @@ SetDIBitsToDevice(
UINT cjBmpScanSize = 0;
BOOL Hit = FALSE;
PVOID pvSafeBits = (PVOID) Bits;
UINT bmiHeight;
BOOL top_down;
INT src_y = 0;
ULONG iFormat, cBitsPixel, cjBits, cjWidth;
#define MaxScanLines 1000
#define MaxHeight 2000
#define MaxSourceHeight 2000
#define IS_ALIGNED(Pointer, Alignment) \
(((ULONG_PTR)(void *)(Pointer)) % (Alignment) == 0)
if (!ScanLines || !lpbmi || !Bits)
return 0;
DPRINT("ScanLines %d Height %d Width %d biHeight %d biWidth %d\n"
" lpbmi '%p' ColorUse '%d' SizeImage '%d' StartScan %d\n"
" biCompression '%d' biBitCount '%d'\n",
ScanLines, Height, Width, lpbmi->bmiHeader.biHeight,
lpbmi->bmiHeader.biWidth, lpbmi, ColorUse,
lpbmi->bmiHeader.biSizeImage, StartScan,
lpbmi->bmiHeader.biCompression, lpbmi->bmiHeader.biBitCount);
if (lpbmi->bmiHeader.biWidth < 0)
return 0;
if (ColorUse && ColorUse != DIB_PAL_COLORS && ColorUse != DIB_PAL_COLORS + 1)
return 0;
@ -677,6 +757,91 @@ SetDIBitsToDevice(
if (!pConvertedInfo)
return 0;
if (ScanLines > MaxScanLines)
{
LinesCopied = 0;
goto Exit;
}
bmiHeight = abs(pConvertedInfo->bmiHeader.biHeight);
top_down = (pConvertedInfo->bmiHeader.biHeight < 0);
if ((StartScan > bmiHeight) && (ScanLines > bmiHeight))
{
DPRINT("Returning ScanLines of '%d'\n", ScanLines);
LinesCopied = ScanLines;
goto Exit;
}
if (pConvertedInfo->bmiHeader.biHeight == 0)
{
LinesCopied = 0;
goto Exit;
}
if (!IS_ALIGNED(Bits, 2) && (ScanLines > Height))
{
LinesCopied = 0;
goto Exit;
}
/* Below code modeled after Wine's nulldrv_SetDIBitsToDevice */
if (StartScan <= YSrc + bmiHeight)
{
if ((pConvertedInfo->bmiHeader.biCompression == BI_RLE8) ||
(pConvertedInfo->bmiHeader.biCompression == BI_RLE4))
{
StartScan = 0;
ScanLines = bmiHeight;
}
else
{
if (StartScan >= bmiHeight)
{
LinesCopied = 0;
goto Exit;
}
if (!top_down && ScanLines > bmiHeight - StartScan)
{
ScanLines = bmiHeight - StartScan;
}
src_y = StartScan + ScanLines - (YSrc + Height);
if (!top_down)
{
/* get rid of unnecessary lines */
if ((src_y < 0 || src_y >= (INT)ScanLines) &&
pConvertedInfo->bmiHeader.biCompression != BI_BITFIELDS)
{
LinesCopied = ScanLines;
goto Exit;
}
if (YDest >= 0)
{
LinesCopied = ScanLines + StartScan;
ScanLines -= src_y;
}
else
{
LinesCopied = ScanLines - src_y;
}
}
else if (src_y < 0 || src_y >= (INT)ScanLines)
{
if (lpbmi->bmiHeader.biHeight < 0 &&
StartScan > MaxScanLines)
{
ScanLines = lpbmi->bmiHeader.biHeight - StartScan;
}
DPRINT("Returning ScanLines of '%d'\n", ScanLines);
LinesCopied = ScanLines;
goto Exit;
}
else
{
LinesCopied = ScanLines;
}
}
}
HANDLE_METADC(INT,
SetDIBitsToDevice,
0,
@ -742,12 +907,19 @@ SetDIBitsToDevice(
{
Hit = TRUE;
}
_SEH2_END
_SEH2_END;
if (Hit)
{
// We don't die, we continue on with a allocated safe pointer to kernel
// space.....
if (!IS_ALIGNED(Bits, 2)) // If both Read Exception and mis-aligned
{
LinesCopied = 0;
goto Exit;
}
DPRINT1("SetDIBitsToDevice fail to read BitMapInfo: %p or Bits: %p & Size: %u\n",
pConvertedInfo, Bits, cjBmpScanSize);
}
@ -761,6 +933,53 @@ SetDIBitsToDevice(
LinesCopied = 0;
goto Exit;
}
/* Calculation of ScanLines for NtGdiSetDIBitsToDeviceInternal */
if (YDest >= 0)
{
ScanLines = min(abs(Height), ScanLines);
if (YSrc > 0)
ScanLines += YSrc;
}
else
{
ScanLines = min(ScanLines,
abs(pConvertedInfo->bmiHeader.biHeight) - StartScan);
}
if (YDest >= 0 && YSrc > 0 && bmiHeight <= MaxHeight)
{
ScanLines += YSrc;
}
/* Find Format from lpbmi which is now pConvertedInfo */
iFormat = BitmapFormat(pConvertedInfo->bmiHeader.biBitCount,
pConvertedInfo->bmiHeader.biCompression);
/* Get bits per pixel from the format */
cBitsPixel = gajBitsPerFormat[iFormat];
cjWidth = WIDTH_BYTES_ALIGN32(pConvertedInfo->bmiHeader.biWidth, cBitsPixel);
/* Calculate the correct bitmap size in bytes */
if (!NT_SUCCESS(RtlULongMult(cjWidth, max(ScanLines, LinesCopied), &cjBits)))
{
DPRINT1("Overflow calculating size: cjWidth %lu, ScanLines %lu\n",
cjWidth, max(ScanLines, LinesCopied));
goto Exit;
}
/* Make sure the buffer is large enough */
if (pConvertedInfo->bmiHeader.biSizeImage < cjBits)
{
DPRINT("Buffer is too small, required: %lu, got %lu\n",
cjBits, pConvertedInfo->bmiHeader.biSizeImage);
if (pConvertedInfo->bmiHeader.biCompression == BI_RGB)
{
pConvertedInfo->bmiHeader.biSizeImage = cjBits;
}
}
/*
if ( !pDc_Attr || // DC is Public
ColorUse == DIB_PAL_COLORS ||
@ -768,12 +987,47 @@ SetDIBitsToDevice(
(pConvertedInfo->bmiHeader.biCompression == BI_JPEG ||
pConvertedInfo->bmiHeader.biCompression == BI_PNG )) )*/
{
LinesCopied = NtGdiSetDIBitsToDeviceInternal(hdc, XDest, YDest, Width, Height, XSrc, YSrc,
StartScan, ScanLines, (LPBYTE) pvSafeBits, (LPBITMAPINFO) pConvertedInfo, ColorUse,
LinesCopied = NtGdiSetDIBitsToDeviceInternal(hdc, XDest, YDest,
Width, Height, XSrc, YSrc,
StartScan, ScanLines, (LPBYTE) pvSafeBits,
(LPBITMAPINFO) pConvertedInfo, ColorUse,
cjBmpScanSize, ConvertedInfoSize,
TRUE,
NULL);
}
if (bmiHeight > MaxScanLines)
{
LinesCopied = ScanLines;
}
if (YDest < 0)
{
if (top_down)
LinesCopied = ScanLines;
else
LinesCopied = ScanLines - src_y;
}
if (pConvertedInfo->bmiHeader.biCompression == BI_RLE8 ||
pConvertedInfo->bmiHeader.biCompression == BI_RLE4)
{
LinesCopied = bmiHeight;
}
if (pConvertedInfo->bmiHeader.biHeight < 0)
{
if (pConvertedInfo->bmiHeader.biHeight < -MaxSourceHeight ||
(YDest >= 0 && src_y < -ScanLines))
{
LinesCopied = ScanLines + src_y;
}
else
{
LinesCopied = ScanLines;
}
}
Exit:
if (Bits != pvSafeBits)
RtlFreeHeap(RtlGetProcessHeap(), 0, pvSafeBits);

View file

@ -518,7 +518,27 @@ NtGdiSetDIBitsToDeviceInternal(
}
_SEH2_END;
ScanLines = min(ScanLines, abs(bmi->bmiHeader.biHeight) - StartScan);
DPRINT("StartScan %d ScanLines %d Bits %p bmi %p ColorUse %d\n"
" Height %d Width %d SizeImage %d\n"
" biHeight %d biWidth %d biBitCount %d\n"
" XSrc %d YSrc %d xDext %d yDest %d\n",
StartScan, ScanLines, Bits, bmi, ColorUse,
Height, Width, bmi->bmiHeader.biSizeImage,
bmi->bmiHeader.biHeight, bmi->bmiHeader.biWidth,
bmi->bmiHeader.biBitCount,
XSrc, YSrc, XDest, YDest);
if (YDest >= 0)
{
ScanLines = min(abs(Height), ScanLines);
if (YSrc > 0)
ScanLines += YSrc;
}
else
{
ScanLines = min(ScanLines, abs(bmi->bmiHeader.biHeight) - StartScan);
}
if (ScanLines == 0)
{
DPRINT1("ScanLines == 0\n");
@ -562,6 +582,10 @@ NtGdiSetDIBitsToDeviceInternal(
SourceSize.cx = bmi->bmiHeader.biWidth;
SourceSize.cy = ScanLines;
if (YDest >= 0 && YSrc > 0)
{
ScanLines += YSrc;
}
//DIBWidth = WIDTH_BYTES_ALIGN32(SourceSize.cx, bmi->bmiHeader.biBitCount);