https://git.reactos.org/?p=reactos.git;a=commitdiff;h=12e1919e5efc1ffc47c673ff1b660a4fed5975ad

commit 12e1919e5efc1ffc47c673ff1b660a4fed5975ad
Author:     Doug Lyons <[email protected]>
AuthorDate: Sun Jul 2 06:24:15 2023 -0500
Commit:     GitHub <[email protected]>
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);
 

Reply via email to