diff --git a/reactos/win32ss/gdi/gdi32/objects/bitmap.c b/reactos/win32ss/gdi/gdi32/objects/bitmap.c index ac0ba65abb5..3ba54c165ef 100644 --- a/reactos/win32ss/gdi/gdi32/objects/bitmap.c +++ b/reactos/win32ss/gdi/gdi32/objects/bitmap.c @@ -671,8 +671,7 @@ SetDIBitsToDevice( if (ColorUse && ColorUse != DIB_PAL_COLORS && ColorUse != DIB_PAL_COLORS + 1) return 0; - pConvertedInfo = ConvertBitmapInfo(lpbmi, ColorUse, &ConvertedInfoSize, - FALSE); + pConvertedInfo = ConvertBitmapInfo(lpbmi, ColorUse, &ConvertedInfoSize, FALSE); if (!pConvertedInfo) return 0; @@ -720,6 +719,15 @@ SetDIBitsToDevice( } } #endif + + if ((pConvertedInfo->bmiHeader.biCompression == BI_RLE8) || + (pConvertedInfo->bmiHeader.biCompression == BI_RLE4)) + { + /* For compressed data, we must set the whole thing */ + StartScan = 0; + ScanLines = pConvertedInfo->bmiHeader.biHeight; + } + cjBmpScanSize = DIB_BitmapMaxBitsSize((LPBITMAPINFO) lpbmi, ScanLines); pvSafeBits = RtlAllocateHeap(GetProcessHeap(), 0, cjBmpScanSize); diff --git a/reactos/win32ss/gdi/gdi32/objects/utils.c b/reactos/win32ss/gdi/gdi32/objects/utils.c index 5b29ff2aa02..5a0bca1e93b 100644 --- a/reactos/win32ss/gdi/gdi32/objects/utils.c +++ b/reactos/win32ss/gdi/gdi32/objects/utils.c @@ -296,6 +296,35 @@ ConvertBitmapInfo( RtlCopyMemory((PVOID)NewDataPtr, (PVOID)OldDataPtr, DataSize); } } + else + { + /* Verify some data validity */ + switch (BitmapInfo->bmiHeader.biCompression) + { + case BI_RLE8: + if (BitmapInfo->bmiHeader.biBitCount != 8) + return NULL; + if (BitmapInfo->bmiHeader.biHeight < 0) + return NULL; + break; + case BI_RLE4: + if (BitmapInfo->bmiHeader.biBitCount != 4) + return NULL; + if (BitmapInfo->bmiHeader.biHeight < 0) + return NULL; + break; + default: + break; + } + + /* Non "standard" formats must have a valid size set */ + if ((BitmapInfo->bmiHeader.biCompression != BI_RGB) && + (BitmapInfo->bmiHeader.biCompression != BI_BITFIELDS)) + { + if (BitmapInfo->bmiHeader.biSizeImage == 0) + return NULL; + } + } Size = NewBitmapInfo->bmiHeader.biSize; if (ColorSpec == DIB_RGB_COLORS) diff --git a/reactos/win32ss/gdi/ntgdi/dibobj.c b/reactos/win32ss/gdi/ntgdi/dibobj.c index e391290ffe0..28c7a2b5ed1 100644 --- a/reactos/win32ss/gdi/ntgdi/dibobj.c +++ b/reactos/win32ss/gdi/ntgdi/dibobj.c @@ -375,6 +375,115 @@ cleanup: return result; } +static +HBITMAP +IntGdiCreateMaskFromRLE( + DWORD Width, + DWORD Height, + ULONG Compression, + const BYTE* Bits, + DWORD BitsSize) +{ + HBITMAP Mask; + DWORD x, y; + SURFOBJ* SurfObj; + UINT i = 0; + BYTE Data, NumPixels, ToSkip; + + ASSERT((Compression == BI_RLE8) || (Compression == BI_RLE4)); + + /* Create the bitmap */ + Mask = GreCreateBitmapEx(Width, Height, 0, BMF_1BPP, 0, 0, NULL, 0); + if (!Mask) + return NULL; + + SurfObj = EngLockSurface((HSURF)Mask); + if (!SurfObj) + { + GreDeleteObject(Mask); + return NULL; + } + ASSERT(SurfObj->pvBits != NULL); + + x = y = 0; + + while (i < BitsSize) + { + NumPixels = Bits[i]; + Data = Bits[i + 1]; + i += 2; + + if (NumPixels != 0) + { + if ((x + NumPixels) > Width) + NumPixels = Width - x; + + if (NumPixels == 0) + continue; + + DIB_1BPP_HLine(SurfObj, x, x + NumPixels, y, 1); + x += NumPixels; + continue; + } + + if (Data < 3) + { + switch (Data) + { + case 0: + /* End of line */ + y++; + if (y == Height) + goto done; + x = 0; + break; + case 1: + /* End of file */ + goto done; + case 2: + /* Jump */ + if (i >= (BitsSize - 1)) + goto done; + x += Bits[i]; + if (x > Width) + x = Width; + y += Bits[i + 1]; + if (y >= Height) + goto done; + i += 2; + break; + } + /* Done for this run */ + continue; + } + + /* Embedded data into the RLE */ + NumPixels = Data; + if (Compression == BI_RLE8) + ToSkip = NumPixels; + else + ToSkip = (NumPixels / 2) + (NumPixels & 1); + + if ((i + ToSkip) > BitsSize) + goto done; + ToSkip = (ToSkip + 1) & ~1; + + if ((x + NumPixels) > Width) + NumPixels = Width - x; + + if (NumPixels != 0) + { + DIB_1BPP_HLine(SurfObj, x, x + NumPixels, y, 1); + x += NumPixels; + } + i += ToSkip; + } + +done: + EngUnlockSurface(SurfObj); + return Mask; +} + W32KAPI INT APIENTRY @@ -399,8 +508,8 @@ NtGdiSetDIBitsToDeviceInternal( INT ret = 0; NTSTATUS Status = STATUS_SUCCESS; PDC pDC; - HBITMAP hSourceBitmap = NULL; - SURFOBJ *pDestSurf, *pSourceSurf = NULL; + HBITMAP hSourceBitmap = NULL, hMaskBitmap = NULL; + SURFOBJ *pDestSurf, *pSourceSurf = NULL, *pMaskSurf = NULL; SURFACE *pSurf; RECTL rcDest; POINTL ptSource; @@ -492,6 +601,29 @@ NtGdiSetDIBitsToDeviceInternal( goto Exit; } + /* HACK: If this is a RLE bitmap, only the relevant pixels must be set. */ + if ((bmi->bmiHeader.biCompression == BI_RLE8) || (bmi->bmiHeader.biCompression == BI_RLE4)) + { + ASSERT(ScanLines == bmi->bmiHeader.biHeight); + hMaskBitmap = IntGdiCreateMaskFromRLE(bmi->bmiHeader.biWidth, + ScanLines, + bmi->bmiHeader.biCompression, + Bits, + cjMaxBits); + if (!hMaskBitmap) + { + EngSetLastError(ERROR_NO_SYSTEM_RESOURCES); + Status = STATUS_NO_MEMORY; + goto Exit; + } + pMaskSurf = EngLockSurface((HSURF)hMaskBitmap); + if (!pMaskSurf) + { + Status = STATUS_UNSUCCESSFUL; + goto Exit; + } + } + /* Create a palette for the DIB */ ppalDIB = CreateDIBPalette(bmi, pDC, ColorUse); if (!ppalDIB) @@ -529,15 +661,15 @@ NtGdiSetDIBitsToDeviceInternal( ptSource.x, ptSource.y, SourceSize.cx, SourceSize.cy); Status = IntEngBitBlt(pDestSurf, pSourceSurf, - NULL, + pMaskSurf, &pDC->co.ClipObj, &exlo.xlo, &rcDest, &ptSource, + pMaskSurf ? &ptSource : NULL, NULL, NULL, - NULL, - ROP4_FROM_INDEX(R3_OPINDEX_SRCCOPY)); + pMaskSurf ? ROP4_MASK : ROP4_FROM_INDEX(R3_OPINDEX_SRCCOPY)); /* Cleanup EXLATEOBJ */ EXLATEOBJ_vCleanup(&exlo); @@ -555,6 +687,8 @@ Exit: if (pSourceSurf) EngUnlockSurface(pSourceSurf); if (hSourceBitmap) EngDeleteSurface((HSURF)hSourceBitmap); + if (pMaskSurf) EngUnlockSurface(pMaskSurf); + if (hMaskBitmap) EngDeleteSurface((HSURF)hMaskBitmap); DC_UnlockDc(pDC); Exit2: ExFreePoolWithTag(pbmiSafe, 'pmTG');