https://git.reactos.org/?p=reactos.git;a=commitdiff;h=12e1919e5efc1ffc47c67…
commit 12e1919e5efc1ffc47c673ff1b660a4fed5975ad
Author: Doug Lyons <douglyons(a)douglyons.com>
AuthorDate: Sun Jul 2 06:24:15 2023 -0500
Commit: GitHub <noreply(a)github.com>
CommitDate: Sun Jul 2 13:24:15 2023 +0200
[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
---
win32ss/gdi/gdi32/objects/bitmap.c | 260 ++++++++++++++++++++++++++++++++++++-
win32ss/gdi/ntgdi/dibobj.c | 26 +++-
2 files changed, 282 insertions(+), 4 deletions(-)
diff --git a/win32ss/gdi/gdi32/objects/bitmap.c b/win32ss/gdi/gdi32/objects/bitmap.c
index b029d4ad2df..5fbcf50568a 100644
--- a/win32ss/gdi/gdi32/objects/bitmap.c
+++ b/win32ss/gdi/gdi32/objects/bitmap.c
@@ -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);
diff --git a/win32ss/gdi/ntgdi/dibobj.c b/win32ss/gdi/ntgdi/dibobj.c
index ec5fa69a469..456210d880a 100644
--- a/win32ss/gdi/ntgdi/dibobj.c
+++ b/win32ss/gdi/ntgdi/dibobj.c
@@ -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);