Because drawing simple lines already gives different results than a native Windows, the comparison of hashes does not work anymore.
Therefore, the tests associated to advanced graphics mode creates bitmap files for each processed test in the current directoy, which can then be compared visually.
Signed-off-by: Ralf Habacker ralf.habacker@freenet.de --- dlls/gdi32/tests/dib.c | 242 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+)
diff --git a/dlls/gdi32/tests/dib.c b/dlls/gdi32/tests/dib.c index bdc3d9ed55b..3e48694f143 100644 --- a/dlls/gdi32/tests/dib.c +++ b/dlls/gdi32/tests/dib.c @@ -3529,11 +3529,253 @@ static void test_simple_graphics(void) DeleteDC(mem_dc); }
+/* Comparing hashes does not work because drawing simple lines is already different from native win32 */ +/* #define WITH_HASH */ + +typedef struct { + HDC dc; + BITMAPINFO *bmi; + BYTE *bits; + HBITMAP dib; +#ifdef WITH_HASH + char *hash_dib; +#endif + char test_name[1024]; + char save_path[MAX_PATH]; + char hash_name[1024]; + RECT bounds; + double angle; + double shearX; +} test_data; + +static test_data _test_data; +test_data *td = &_test_data; + +double radians ( double d ) +{ + return d * M_PI / 180; +} + +BOOL save_bitmap_to_file(HBITMAP hBitmap, LPSTR lpszFileName) +{ + HDC hDC; + int iBits; + WORD wBitCount; + DWORD dwPaletteSize = 0, dwBmBitsSize = 0, dwDIBSize = 0, dwWritten = 0; + BITMAP Bitmap0; + BITMAPFILEHEADER bmfHdr; + BITMAPINFOHEADER bi; + LPBITMAPINFOHEADER lpbi; + HANDLE fh, hDib, hPal, hOldPal2 = NULL; + hDC = CreateDCA("DISPLAY", NULL, NULL, NULL); + iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES); + DeleteDC(hDC); + if (iBits <= 1) + wBitCount = 1; + else if (iBits <= 4) + wBitCount = 4; + else if (iBits <= 8) + wBitCount = 8; + else + wBitCount = 24; + GetObjectA(hBitmap, sizeof(Bitmap0), (LPSTR)&Bitmap0); + bi.biSize = sizeof(BITMAPINFOHEADER); + bi.biWidth = Bitmap0.bmWidth; + bi.biHeight = -Bitmap0.bmHeight; + bi.biPlanes = 1; + bi.biBitCount = wBitCount; + bi.biCompression = BI_RGB; + bi.biSizeImage = 0; + bi.biXPelsPerMeter = 0; + bi.biYPelsPerMeter = 0; + bi.biClrImportant = 0; + bi.biClrUsed = 256; + dwBmBitsSize = ((Bitmap0.bmWidth * wBitCount + 31) & ~31) / 8 + * Bitmap0.bmHeight; + hDib = GlobalAlloc(GHND, dwBmBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER)); + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib); + *lpbi = bi; + + hPal = GetStockObject(DEFAULT_PALETTE); + if (hPal) + { + hDC = GetDC(NULL); + hOldPal2 = SelectPalette(hDC, (HPALETTE)hPal, FALSE); + RealizePalette(hDC); + } + + + GetDIBits(hDC, hBitmap, 0, (UINT)Bitmap0.bmHeight, (LPSTR)lpbi + sizeof(BITMAPINFOHEADER) + + dwPaletteSize, (BITMAPINFO *)lpbi, DIB_RGB_COLORS); + + if (hOldPal2) + { + SelectPalette(hDC, (HPALETTE)hOldPal2, TRUE); + RealizePalette(hDC); + ReleaseDC(NULL, hDC); + } + + fh = CreateFileA(lpszFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + + if (fh == INVALID_HANDLE_VALUE) + return FALSE; + + bmfHdr.bfType = 0x4D42; // "BM" + dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwPaletteSize + dwBmBitsSize; + bmfHdr.bfSize = dwDIBSize; + bmfHdr.bfReserved1 = 0; + bmfHdr.bfReserved2 = 0; + bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER) + dwPaletteSize; + + WriteFile(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL); + + WriteFile(fh, (LPSTR)lpbi, dwDIBSize, &dwWritten, NULL); + GlobalUnlock(hDib); + GlobalFree(hDib); + CloseHandle(fh); + return TRUE; +} + +void set_transform( HDC dc, RECT *bounds, double angle ) +{ + XFORM xf; + double r = radians( angle ); + xf.eM11 = cos( r ) + sin( r ) * td->shearX; + xf.eM22 = cos( r ); + xf.eM12 = -sin( r ); + xf.eM21 = sin( r ) + cos( r ) * td->shearX; + xf.eDx = ( bounds->right - bounds->left ) / 4; + xf.eDy = ( bounds->bottom - bounds->top ) / 4; + SetWorldTransform( dc, &xf ); +} + + +void init( test_data *td, const char *test_name ) +{ + sprintf( td->test_name, "%s", test_name ); + sprintf( td->save_path, "%s-%3.1f-%2.1f.bmp", td->test_name, td->shearX, td->angle ); + sprintf( td->hash_name, "sha1_advanced_%s_%3.1f_%2.1f", td->test_name, td->shearX, td->angle ); + trace( "%s\n", td->test_name ); + reset_bits( td->dc, td->bmi, td->bits ); + MoveToEx( td->dc, -5, 0, NULL ); + LineTo( td->dc, 5, 0 ); + MoveToEx( td->dc, 0, 5, NULL ); + LineTo( td->dc, 0, -5 ); +} + +void check_hash( test_data *td ) +{ +#ifdef WITH_HASH + if ( td->hash_dib ) + HeapFree( GetProcessHeap(), 0, td->hash_dib ); + td->hash_dib = hash_dib( td->dc, td->bmi, td->bits ); + printf( "const char *%s = "%s";\n", td->hash_name, td->hash_dib ); +#endif + // instead we dump the display to a file for visual inspection + save_bitmap_to_file( td->dib, td->save_path ); +} + +static void test_gdi( double angle, double shearX ) +{ + char bmibuf[sizeof( BITMAPINFO ) + 256 * sizeof( RGBQUAD )]; + HBITMAP orig_bm; + HPALETTE default_palette, old_hpal; + RECT r; + + td->bmi = ( BITMAPINFO * )bmibuf; + td->dc = CreateCompatibleDC( NULL ); + td->angle = angle; + td->shearX = shearX; + + SetGraphicsMode( td->dc, GM_ADVANCED ); + set_transform( td->dc, &td->bounds, td->angle ); + + memset( td->bmi, 0, sizeof( bmibuf )); + td->bmi->bmiHeader.biSize = sizeof( td->bmi->bmiHeader ); + td->bmi->bmiHeader.biHeight = td->bounds.right; + td->bmi->bmiHeader.biWidth = td->bounds.bottom; + td->bmi->bmiHeader.biBitCount = 32; + td->bmi->bmiHeader.biPlanes = 1; + td->bmi->bmiHeader.biCompression = BI_RGB; + + td->dib = CreateDIBSection( 0, td->bmi, DIB_RGB_COLORS, ( void** )&( td->bits ), NULL, 0 ); + orig_bm = SelectObject( td->dc, td->dib ); + + default_palette = create_default_palette( 8 ); + old_hpal = SelectPalette( td->dc, default_palette, FALSE ); + + current_bounds = &td->bounds; + SetBoundsRect( td->dc, &td->bounds, DCB_SET ); + + SetRect( &r, 0, 0, 128, 128 ); + + init( td, "LineTo" ); + MoveToEx( td->dc, r.left, r.top, NULL ); + LineTo( td->dc, r.right, r.bottom ); + check_hash( td ); + + init( td, "Rectangle" ); + ok( Rectangle( td->dc, r.left, r.top, r.right, r.bottom ), td->test_name ); + check_hash( td ); + + init( td, "Ellipse" ); + ok( Ellipse( td->dc, r.left, r.top, r.right, r.bottom ), td->test_name ); + check_hash( td ); + + init( td, "Arc" ); + ok( Arc( td->dc, r.left, r.top, r.right, r.bottom, r.left+10, r.top, r.right-10, r.bottom ), td->test_name ); + check_hash( td ); + + init( td, "ArcTo" ); + ok( ArcTo( td->dc, r.left, r.top, r.right, r.bottom, r.left+10, r.top, r.right-10, r.bottom ), td->test_name ); + check_hash( td ); + + init( td, "Chord" ); + ok( Chord( td->dc, r.left, r.top, r.right, r.bottom, r.left+10, r.top, r.right-10, r.bottom ), td->test_name ); + check_hash( td ); + + init( td, "Pie" ); + ok( Pie( td->dc, r.left, r.top, r.right, r.bottom, r.left+10, r.top, r.right-10, r.bottom ), td->test_name ); + check_hash( td ); + + init( td, "FillRect" ); + ok( FillRect( td->dc, &r, GetStockObject( WHITE_BRUSH )), td->test_name ); + check_hash( td ); + + SelectObject( td->dc, orig_bm ); + DeleteObject( td->dib ); + SelectPalette( td->dc, old_hpal, FALSE ); + DeleteDC( td->dc ); +} + +static void test_advanced_graphics( void ) +{ + td->bounds.left = 0; + td->bounds.top = 0; + td->bounds.right = 256; + td->bounds.bottom = 256; + + test_gdi( 0, 0.0 ); + test_gdi( 30, 0.0 ); + test_gdi( 60, 0.0 ); + test_gdi( 90, 0.0 ); + test_gdi( 120, 0.0 ); + test_gdi( 150, 0.0 ); + test_gdi( 0, 0.1 ); + test_gdi( 30, 0.1 ); + test_gdi( 60, 0.1 ); + test_gdi( 90, 0.1 ); + test_gdi( 120, 0.1 ); + test_gdi( 150, 0.1 ); +} + START_TEST(dib) { CryptAcquireContextW(&crypt_prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
test_simple_graphics(); + test_advanced_graphics();
CryptReleaseContext(crypt_prov, 0); }
On 2/9/21 11:17 AM, Ralf Habacker wrote:
+void check_hash( test_data *td ) +{ +#ifdef WITH_HASH
- if ( td->hash_dib )
HeapFree( GetProcessHeap(), 0, td->hash_dib );
- td->hash_dib = hash_dib( td->dc, td->bmi, td->bits );
- printf( "const char *%s = "%s";\n", td->hash_name, td->hash_dib );
+#endif
- // instead we dump the display to a file for visual inspection
- save_bitmap_to_file( td->dib, td->save_path );
+}
Does it mean that new tests don't really check any output, because WITH_HASH is not set? If so, what's a point of having them running automated with other tests?
On Tue, 9 Feb 2021, Ralf Habacker wrote:
Because drawing simple lines already gives different results than a native Windows, the comparison of hashes does not work anymore.
Therefore, the tests associated to advanced graphics mode creates bitmap files for each processed test in the current directoy, which can then be compared visually.
It looks like this is a prime candidate for winetest_interactive:
if (winetest_interactive) { /* Tests that require a human to check the results */ ... }
So to get the bitmaps to compare one would run the test with set WINETEST_INTERACTIVE=1.
Note: Iirc C++ comments are not allowed in Wine's C code.
On Tue, Feb 09, 2021 at 12:04:25PM +0100, Francois Gouget wrote:
On Tue, 9 Feb 2021, Ralf Habacker wrote:
Because drawing simple lines already gives different results than a native Windows, the comparison of hashes does not work anymore.
Therefore, the tests associated to advanced graphics mode creates bitmap files for each processed test in the current directoy, which can then be compared visually.
It looks like this is a prime candidate for winetest_interactive:
if (winetest_interactive) { /* Tests that require a human to check the results */ ... }
So to get the bitmaps to compare one would run the test with set WINETEST_INTERACTIVE=1.
You'd be better off testing simple cases (like rotations that are a multiples of pi/2, or possibly pi/4) that should be possible to match Windows exactly. That way any regressions will get caught.
Dumping to a bitmap is fine for testing, but nobody is going to looking at them going forward.
You should append the tests to (the admittedly long) draw_graphics(), they'll them get tested on a large number of different bitmap formats.
Huw.
Am 09.02.21 um 12:55 schrieb Huw Davies:
You'd be better off testing simple cases (like rotations that are a multiples of pi/2, or possibly pi/4) that should be possible to match Windows exactly. That way any regressions will get caught.
done, thanks for this hint
Dumping to a bitmap is fine for testing, but nobody is going to looking at them going forward.
I added dumping the image to the interactive tests.
You should append the tests to (the admittedly long) draw_graphics(), they'll them get tested on a large number of different bitmap formats.
I think that would require a refactoring of the "simple" testing code to match the needs of the advanced tests.
Regards Ralf