Skip to content

Commit

Permalink
[NTGDI][GDI32] Icon fixes for Office 2000, VB6, and Hoyle Cards (reac…
Browse files Browse the repository at this point in the history
…tos#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
  • Loading branch information
Doug-Lyons authored Jul 2, 2023
1 parent 3856076 commit 12e1919
Show file tree
Hide file tree
Showing 2 changed files with 282 additions and 4 deletions.
260 changes: 257 additions & 3 deletions win32ss/gdi/gdi32/objects/bitmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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;

Expand All @@ -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.
Expand Down Expand Up @@ -666,17 +725,123 @@ 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;

pConvertedInfo = ConvertBitmapInfo(lpbmi, ColorUse, &ConvertedInfoSize, FALSE);
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,
Expand Down Expand Up @@ -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);
}
Expand All @@ -761,19 +933,101 @@ 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 ||
((pConvertedInfo->bmiHeader.biSize >= sizeof(BITMAPINFOHEADER)) &&
(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);
Expand Down
26 changes: 25 additions & 1 deletion win32ss/gdi/ntgdi/dibobj.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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);

Expand Down

0 comments on commit 12e1919

Please sign in to comment.