diff --git a/win32ss/gdi/eng/surface.c b/win32ss/gdi/eng/surface.c index 3bfe6f89276..c2a2cdebc77 100644 --- a/win32ss/gdi/eng/surface.c +++ b/win32ss/gdi/eng/surface.c @@ -14,54 +14,10 @@ #define NDEBUG #include +#include LONG giUniqueSurface = 0; -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 */ -}; - - -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; - } -} - VOID NTAPI SURFACE_vCleanup(PVOID ObjectBody) diff --git a/win32ss/gdi/gdi32/objects/bitmap.c b/win32ss/gdi/gdi32/objects/bitmap.c index b029d4ad2df..b5ba0228e3f 100644 --- a/win32ss/gdi/gdi32/objects/bitmap.c +++ b/win32ss/gdi/gdi32/objects/bitmap.c @@ -4,6 +4,7 @@ #define NDEBUG #include +#include <../../gdihelp.h> // From Yuan, ScanLineSize = (Width * bitcount + 31)/32 #define WIDTH_BYTES_ALIGN32(cx, bpp) ((((cx) * (bpp) + 31) & ~31) >> 3) @@ -577,6 +578,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 +594,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 +680,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 +712,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 +862,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 +888,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 +942,45 @@ 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..c2bf4dbff46 100644 --- a/win32ss/gdi/ntgdi/dibobj.c +++ b/win32ss/gdi/ntgdi/dibobj.c @@ -501,6 +501,16 @@ NtGdiSetDIBitsToDeviceInternal( if (!Bits) return 0; + 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); + pbmiSafe = ExAllocatePoolWithTag(PagedPool, cjMaxInfo, 'pmTG'); if (!pbmiSafe) return 0; @@ -518,7 +528,17 @@ NtGdiSetDIBitsToDeviceInternal( } _SEH2_END; - ScanLines = min(ScanLines, abs(bmi->bmiHeader.biHeight) - StartScan); + 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); diff --git a/win32ss/gdihelp.h b/win32ss/gdihelp.h new file mode 100644 index 00000000000..80e5c87e59c --- /dev/null +++ b/win32ss/gdihelp.h @@ -0,0 +1,52 @@ +/* + * PROJECT: ReactOS win32 kernel mode subsystem + * LICENSE: GPL - See COPYING in the top level directory + * FILE: win32ss/gdihelp.h + * PURPOSE: Dib object helper functions + * PROGRAMMER: + */ + +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; + } +} + +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 */ +}; +