From 774edf58ac2c2cd223de1615431c4ae2b2adfb76 Mon Sep 17 00:00:00 2001 From: Mathias Kosch Date: Mon, 12 Jul 2010 00:06:27 +0200 Subject: gdi32: "StretchDIBits" with value of zero for "xSrc" and "ySrc" This patch fixes Bug#13344. The function "StretchDIBits" behaves odd in case of "top-down" bitmaps and certain coordinates and dimensions. Tests using Windows Server 2003 shew that in this particular case the source rectangle is selected starting at the upper left corner of the bitmap. In all other cases the rectangle is aligned to the bottom of the bitmap. I provided a test case which tests several combinations, especially those with some of the values set to zero. The test case doesn't check all related input values, for simplicity. It passes under "Windows Server 2003 R2 SP2" and Wine wine-1.2-rc7 with this path applied. The previously submitted test case entirely failed for some old Windows versions. This is a hint that the tested functionality might not be supported on these Windows versions. I added a check which only runs the test case on Windows versions with a major version number of 5 or above. --- dlls/gdi32/dib.c | 34 ++++++- dlls/gdi32/tests/Makefile.in | 1 + dlls/gdi32/tests/dib.c | 241 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 274 insertions(+), 2 deletions(-) create mode 100644 dlls/gdi32/tests/dib.c diff --git a/dlls/gdi32/dib.c b/dlls/gdi32/dib.c index ce5ab8b..2ccb37d 100644 --- a/dlls/gdi32/dib.c +++ b/dlls/gdi32/dib.c @@ -212,6 +212,8 @@ INT WINAPI StretchDIBits(HDC hdc, INT xDst, INT yDst, INT widthDst, DWORD compr, size; HBITMAP hBitmap; BOOL fastpath = FALSE; + BOOL negativeHeight; + int ySrcAdjusted; release_dc_ptr( dc ); @@ -227,6 +229,26 @@ INT WINAPI StretchDIBits(HDC hdc, INT xDst, INT yDst, INT widthDst, return 0; } + /* Ensure height is positive. */ + negativeHeight = height < 0; + if (negativeHeight) + height = -height; + + /* Computation of source coordinates behaves different in case of "top down" + * bitmaps and certain coordinates and dimensions. + * + * This special case is currently only confirmed for RGB bitmaps with 24 or 32 + * bits per pixel. For other bitmaps verification is still required before + * extending the special case. + */ + if ((negativeHeight) && (!xSrc) && (!ySrc) && + (widthSrc == widthDst) && (heightSrc == heightDst) && + (info->bmiHeader.biCompression == BI_RGB) && + ((info->bmiHeader.biBitCount == 24) || (info->bmiHeader.biBitCount == 32))) + ySrcAdjusted = 0; + else + ySrcAdjusted = height - heightSrc - ySrc; + hBitmap = GetCurrentObject(hdc, OBJ_BITMAP); if (xDst == 0 && yDst == 0 && xSrc == 0 && ySrc == 0 && @@ -286,8 +308,16 @@ INT WINAPI StretchDIBits(HDC hdc, INT xDst, INT yDst, INT widthDst, * ericP (2000/09/09) */ + /* FIXME: + * This might not work for values of "dwRop" much different than "SRCCOPY". + * Further testing is required. + * (i.e. there should be problems i.e. with XOR, because this code appears + * to XOR the destination bitmap with gaps and then again with the + * destination bitmap, leaving the gaps.) + */ + /* copy existing bitmap from destination dc */ - StretchBlt( hdcMem, xSrc, abs(height) - heightSrc - ySrc, + StretchBlt( hdcMem, xSrc, ySrcAdjusted, widthSrc, heightSrc, hdc, xDst, yDst, widthDst, heightDst, dwRop ); } @@ -297,7 +327,7 @@ INT WINAPI StretchDIBits(HDC hdc, INT xDst, INT yDst, INT widthDst, /* Origin for DIBitmap may be bottom left (positive biHeight) or top left (negative biHeight) */ if (ret) StretchBlt( hdc, xDst, yDst, widthDst, heightDst, - hdcMem, xSrc, abs(height) - heightSrc - ySrc, + hdcMem, xSrc, ySrcAdjusted, widthSrc, heightSrc, dwRop ); if(hpal) SelectPalette(hdcMem, hpal, FALSE); diff --git a/dlls/gdi32/tests/Makefile.in b/dlls/gdi32/tests/Makefile.in index 77c8327..8829981 100644 --- a/dlls/gdi32/tests/Makefile.in +++ b/dlls/gdi32/tests/Makefile.in @@ -10,6 +10,7 @@ C_SRCS = \ brush.c \ clipping.c \ dc.c \ + dib.c \ font.c \ gdiobj.c \ generated.c \ diff --git a/dlls/gdi32/tests/dib.c b/dlls/gdi32/tests/dib.c new file mode 100644 index 0000000..40b751b --- /dev/null +++ b/dlls/gdi32/tests/dib.c @@ -0,0 +1,241 @@ +/* + * Unit tests for dib functions + * + * Copyright (c) 2008 Mathias Kosch + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" + +#include "wine/test.h" + + +static void test_StretchDIBits_internal(int xSrc, int ySrc, + int xDest, int yDest, int nWidth, int nHeight, BOOL bTopDown) +{ + BOOL bMatching; + int x, y, nResult; + int nBitmapWidth, nBitmapHeight, nNewBitmapWidth, nNewBitmapHeight; + ULONG i, nSize; + RGBTRIPLE *pBitmapData, *pNewBitmapData, *pStartLine, *pNewStartLine; + BITMAPINFO bmi, bmiNew; + HANDLE hProcessHeap; + HDC hDC, hMemDC; + HBITMAP hBitmap, hOldBitmap; + + + hProcessHeap = GetProcessHeap(); + + /* Compute bitmap widths and heights. The should be multiples of 4. */ + nBitmapWidth = ((nWidth+xSrc+37) | 3)+1; + nBitmapHeight = ((nHeight+ySrc+43) | 3)+1; + nNewBitmapWidth = ((nWidth+xDest+103) | 3)+1; + nNewBitmapHeight = ((nHeight+yDest+57) | 3)+1; + + /* Creating bitmap structure for bitmap: */ + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = nBitmapWidth; + bmi.bmiHeader.biHeight = nBitmapHeight; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 24; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = 0; + bmi.bmiHeader.biXPelsPerMeter = 0; + bmi.bmiHeader.biYPelsPerMeter = 0; + bmi.bmiHeader.biClrUsed = 0; + bmi.bmiHeader.biClrImportant = 0; + if (bTopDown) + bmi.bmiHeader.biHeight = -bmi.bmiHeader.biHeight; + + /* Creating bitmap structure for new bitmap: */ + bmiNew.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmiNew.bmiHeader.biWidth = nNewBitmapWidth; + bmiNew.bmiHeader.biHeight = nNewBitmapHeight; + bmiNew.bmiHeader.biPlanes = 1; + bmiNew.bmiHeader.biBitCount = 24; + bmiNew.bmiHeader.biCompression = BI_RGB; + bmiNew.bmiHeader.biSizeImage = 0; + bmiNew.bmiHeader.biXPelsPerMeter = 0; + bmiNew.bmiHeader.biYPelsPerMeter = 0; + bmiNew.bmiHeader.biClrUsed = 0; + bmiNew.bmiHeader.biClrImportant = 0; + if (bTopDown) + bmiNew.bmiHeader.biHeight = -bmiNew.bmiHeader.biHeight; + + hDC = GetDC(NULL); + assert(hDC != NULL); + hMemDC = CreateCompatibleDC(hDC); + assert(hMemDC != NULL); + hBitmap = CreateCompatibleBitmap(hDC, nNewBitmapWidth, nNewBitmapHeight); + assert(hBitmap != NULL); + hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap); + assert(hOldBitmap != NULL); + + /* Allocating storage for bitmap data */ + pBitmapData = (RGBTRIPLE*)HeapAlloc(hProcessHeap, 0, + nBitmapWidth*nBitmapHeight*sizeof(RGBTRIPLE)); + pNewBitmapData = (RGBTRIPLE*)HeapAlloc(hProcessHeap, 0, + nNewBitmapWidth*nNewBitmapHeight*sizeof(RGBTRIPLE)); + + /* Initializing bitmap with color data: */ + nSize = nBitmapWidth*nBitmapHeight; + for (i = 0; i < nSize; i++) + { + pBitmapData[i].rgbtBlue = GetBValue(i); + pBitmapData[i].rgbtGreen = GetGValue(i); + pBitmapData[i].rgbtRed = GetRValue(i); + } + + /* Drawing bitmap data as "bottom up" bitmap into first dc: */ + nResult = StretchDIBits(hMemDC, xDest, yDest, nWidth, nHeight, xSrc, ySrc, + nWidth, nHeight, pBitmapData, &bmi, DIB_RGB_COLORS, SRCCOPY); + ok(nResult != 0, "StretchDIBits failed."); + + /* Retrieving bitmap data: */ + nResult = GetDIBits(hMemDC, hBitmap, 0, nNewBitmapHeight, pNewBitmapData, + &bmiNew, DIB_RGB_COLORS); + ok(nResult != 0, "GetDIBits failed."); + + /* Computing starting lines for comparision. "StretchDIBits" + seems to behave odd in case "xSrc" an "ySrc" is 0. */ + if (bTopDown) + { + pStartLine = pBitmapData; + if ((xSrc) || (ySrc)) + pStartLine += (nBitmapHeight-nHeight-ySrc)*nBitmapWidth; + pNewStartLine = pNewBitmapData + yDest*nNewBitmapWidth; + } + else + { + pStartLine = pBitmapData + (nHeight+ySrc-1)*nBitmapWidth; + pNewStartLine = pNewBitmapData + + (nNewBitmapHeight-yDest-1)*nNewBitmapWidth; + } + + /* Comparing bitmap data with supposed data. */ + bMatching = TRUE; + for (y = 0; y < nHeight; y++) + { + for (x = 0; x < nWidth; x++) + { + if (memcmp(&pStartLine[xSrc+x], &pNewStartLine[xDest+x], sizeof(RGBTRIPLE))) + { + bMatching = FALSE; + break; + } + } + + if (bTopDown) + { + pStartLine += nBitmapWidth; + pNewStartLine += nNewBitmapWidth; + } + else + { + pStartLine -= nBitmapWidth; + pNewStartLine -= nNewBitmapWidth; + } + } + + ok(bMatching != FALSE, "Bitmap data not matching. [bTopDown=%s," + " StretchDIBits(hMemDC, %d, %d, %d, %d, %d, %d, %d, %d," + " pBitmapData, &bmi, DIB_RGB_COLORS, SRCCOPY)]\n", + (bTopDown) ? "true" : "false", + xDest, yDest, nWidth, nHeight, xSrc, ySrc, nWidth, nHeight); + + SelectObject(hMemDC, hOldBitmap); + DeleteObject(hBitmap); + DeleteDC(hMemDC); + HeapFree(hProcessHeap, 0, pNewBitmapData); + HeapFree(hProcessHeap, 0, pBitmapData); +} + + +static void test_StretchDIBits() +{ + /* Skip old windows versions that don't seem to support this test. */ + OSVERSIONINFO version; + version.dwOSVersionInfoSize = sizeof(version); + if ((!GetVersionEx(&version)) || (version.dwMajorVersion <= 4)) + { + skip("Test requires a major version >= 5.\n"); + return; + } + + /* This function runs tests with various odd coordinates and every + combination of them set to zero. + This way many, if not all, special cases should be covered. */ + + test_StretchDIBits_internal(33, 61, 103, 227, 663, 459, FALSE); + test_StretchDIBits_internal(33, 61, 103, 227, 663, 459, TRUE); + + test_StretchDIBits_internal(0, 61, 103, 227, 663, 459, FALSE); + test_StretchDIBits_internal(0, 61, 103, 227, 663, 459, TRUE); + + test_StretchDIBits_internal(33, 0, 103, 227, 663, 459, FALSE); + test_StretchDIBits_internal(33, 0, 103, 227, 663, 459, TRUE); + + test_StretchDIBits_internal(0, 0, 103, 227, 663, 459, FALSE); + test_StretchDIBits_internal(0, 0, 103, 227, 663, 459, TRUE); + + test_StretchDIBits_internal(33, 61, 0, 227, 663, 459, FALSE); + test_StretchDIBits_internal(33, 61, 0, 227, 663, 459, TRUE); + + test_StretchDIBits_internal(0, 61, 0, 227, 663, 459, FALSE); + test_StretchDIBits_internal(0, 61, 0, 227, 663, 459, TRUE); + + test_StretchDIBits_internal(33, 0, 0, 227, 663, 459, FALSE); + test_StretchDIBits_internal(33, 0, 0, 227, 663, 459, TRUE); + + test_StretchDIBits_internal(0, 0, 0, 227, 663, 459, FALSE); + test_StretchDIBits_internal(0, 0, 0, 227, 663, 459, TRUE); + + test_StretchDIBits_internal(33, 61, 103, 0, 663, 459, FALSE); + test_StretchDIBits_internal(33, 61, 103, 0, 663, 459, TRUE); + + test_StretchDIBits_internal(0, 61, 103, 0, 663, 459, FALSE); + test_StretchDIBits_internal(0, 61, 103, 0, 663, 459, TRUE); + + test_StretchDIBits_internal(33, 0, 103, 0, 663, 459, FALSE); + test_StretchDIBits_internal(33, 0, 103, 0, 663, 459, TRUE); + + test_StretchDIBits_internal(0, 0, 103, 0, 663, 459, FALSE); + test_StretchDIBits_internal(0, 0, 103, 0, 663, 459, TRUE); + + test_StretchDIBits_internal(33, 61, 0, 0, 663, 459, FALSE); + test_StretchDIBits_internal(33, 61, 0, 0, 663, 459, TRUE); + + test_StretchDIBits_internal(0, 61, 0, 0, 663, 459, FALSE); + test_StretchDIBits_internal(0, 61, 0, 0, 663, 459, TRUE); + + test_StretchDIBits_internal(33, 0, 0, 0, 663, 459, FALSE); + test_StretchDIBits_internal(33, 0, 0, 0, 663, 459, TRUE); + + test_StretchDIBits_internal(0, 0, 0, 0, 663, 459, FALSE); + test_StretchDIBits_internal(0, 0, 0, 0, 663, 459, TRUE); +} + + +START_TEST(dib) +{ + test_StretchDIBits(); +} -- 1.6.4.2