diff --git a/dlls/gdi32/dib.c b/dlls/gdi32/dib.c index 23379e1a0d9..79d339bb731 100644 --- a/dlls/gdi32/dib.c +++ b/dlls/gdi32/dib.c @@ -97,6 +97,38 @@ int bitmap_info_size( const BITMAPINFO * info, WORD coloruse ) } } +/******************************************************************************************* + * Verify that the DIB parameters are valid. + */ +static BOOL is_valid_dib_format( const BITMAPINFOHEADER *info, BOOL allow_compression ) +{ + if (info->biWidth <= 0) return FALSE; + if (info->biHeight == 0) return FALSE; + + if (allow_compression && (info->biCompression == BI_RLE4 || info->biCompression == BI_RLE8)) + { + if (info->biHeight < 0) return FALSE; + if (!info->biSizeImage) return FALSE; + return info->biBitCount == (info->biCompression == BI_RLE4 ? 4 : 8); + } + + if (!info->biPlanes) return FALSE; + + switch (info->biBitCount) + { + case 1: + case 4: + case 8: + case 24: + return (info->biCompression == BI_RGB); + case 16: + case 32: + return (info->biCompression == BI_BITFIELDS || info->biCompression == BI_RGB); + default: + return FALSE; + } +} + /******************************************************************************************* * Fill out a true BITMAPINFOHEADER from a variable sized BITMAPINFOHEADER / BITMAPCOREHEADER. */ @@ -136,12 +168,14 @@ static BOOL bitmapinfoheader_from_user_bitmapinfo( BITMAPINFOHEADER *dst, const /******************************************************************************************* * Fill out a true BITMAPINFO from a variable sized BITMAPINFO / BITMAPCOREINFO. */ -static BOOL bitmapinfo_from_user_bitmapinfo( BITMAPINFO *dst, const BITMAPINFO *info, UINT coloruse ) +static BOOL bitmapinfo_from_user_bitmapinfo( BITMAPINFO *dst, const BITMAPINFO *info, + UINT coloruse, BOOL allow_compression ) { void *src_colors; unsigned int colors; if (!bitmapinfoheader_from_user_bitmapinfo( &dst->bmiHeader, &info->bmiHeader )) return FALSE; + if (!is_valid_dib_format( &dst->bmiHeader, allow_compression )) return FALSE; src_colors = (char *)info + info->bmiHeader.biSize; colors = get_dib_num_of_colors( dst ); @@ -361,7 +395,6 @@ INT nulldrv_StretchDIBits( PHYSDEV dev, INT xDst, INT yDst, INT widthDst, INT he if (GET_DC_PHYSDEV( dc, pStretchBlt ) == dev || GET_DC_PHYSDEV( dc, pPutImage ) == dev) return 0; - if (info->bmiHeader.biWidth < 0) return 0; height = info->bmiHeader.biHeight; if (xSrc == 0 && ySrc == 0 && widthDst == widthSrc && heightDst == heightSrc && @@ -425,7 +458,11 @@ INT WINAPI StretchDIBits(HDC hdc, INT xDst, INT yDst, INT widthDst, INT heightDs INT ret = 0; if (!bits) return 0; - if (!bitmapinfo_from_user_bitmapinfo( info, bmi, coloruse )) return 0; + if (!bitmapinfo_from_user_bitmapinfo( info, bmi, coloruse, TRUE )) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } if ((dc = get_dc_ptr( hdc ))) { @@ -477,6 +514,12 @@ INT WINAPI SetDIBits( HDC hdc, HBITMAP hbitmap, UINT startscan, HRGN clip = 0; const struct gdi_dc_funcs *funcs; + if (!bitmapinfo_from_user_bitmapinfo( src_info, info, coloruse, TRUE )) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + src_bits.ptr = (void *)bits; src_bits.is_copy = FALSE; src_bits.free = NULL; @@ -500,21 +543,11 @@ INT WINAPI SetDIBits( HDC hdc, HBITMAP hbitmap, UINT startscan, return 0; } - if (!bitmapinfo_from_user_bitmapinfo( src_info, info, coloruse )) goto done; - if (coloruse == DIB_PAL_COLORS) if (!fill_color_table_from_palette( src_info, dc )) goto done; if (src_info->bmiHeader.biCompression == BI_RLE4 || src_info->bmiHeader.biCompression == BI_RLE8) { - if ( src_info->bmiHeader.biHeight < 0 || - (src_info->bmiHeader.biCompression == BI_RLE4 && src_info->bmiHeader.biBitCount != 4) || - (src_info->bmiHeader.biCompression == BI_RLE8 && src_info->bmiHeader.biBitCount != 8) ) - { - SetLastError( ERROR_INVALID_PARAMETER ); - goto done; - } - if (lines == 0) goto done; else lines = src_info->bmiHeader.biHeight; startscan = 0; @@ -619,7 +652,11 @@ INT WINAPI SetDIBitsToDevice(HDC hdc, INT xDest, INT yDest, DWORD cx, DC *dc; if (!bits) return 0; - if (!bitmapinfo_from_user_bitmapinfo( info, bmi, coloruse )) return 0; + if (!bitmapinfo_from_user_bitmapinfo( info, bmi, coloruse, TRUE )) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } if ((dc = get_dc_ptr( hdc ))) { @@ -1079,6 +1116,7 @@ HBITMAP WINAPI CreateDIBitmap( HDC hdc, const BITMAPINFOHEADER *header, LONG height; if (!bitmapinfoheader_from_user_bitmapinfo( &info, header )) return 0; + if (info.biCompression == BI_JPEG || info.biCompression == BI_PNG) return 0; if (info.biWidth < 0) return 0; /* Top-down DIBs have a negative height */ @@ -1154,24 +1192,11 @@ HBITMAP WINAPI CreateDIBSection(HDC hdc, CONST BITMAPINFO *bmi, UINT usage, void *mapBits = NULL; if (bits) *bits = NULL; - if (!bitmapinfo_from_user_bitmapinfo( info, bmi, usage )) return 0; - - switch (info->bmiHeader.biBitCount) + if (!bitmapinfo_from_user_bitmapinfo( info, bmi, usage, FALSE )) return 0; + if (info->bmiHeader.biPlanes != 1) { - case 16: - case 32: - if (info->bmiHeader.biCompression == BI_BITFIELDS) break; - /* fall through */ - case 1: - case 4: - case 8: - case 24: - if (info->bmiHeader.biCompression == BI_RGB) break; - /* fall through */ - default: - WARN( "invalid %u bpp compression %u\n", - info->bmiHeader.biBitCount, info->bmiHeader.biCompression ); - return 0; + if (info->bmiHeader.biPlanes * info->bmiHeader.biBitCount > 16) return 0; + WARN( "%u planes not properly supported\n", info->bmiHeader.biPlanes ); } if (!(dib = HeapAlloc( GetProcessHeap(), 0, sizeof(*dib) ))) return 0; @@ -1214,6 +1239,7 @@ HBITMAP WINAPI CreateDIBSection(HDC hdc, CONST BITMAPINFO *bmi, UINT usage, dib->dsBitfields[0] = *(const DWORD *)bmi->bmiColors; dib->dsBitfields[1] = *((const DWORD *)bmi->bmiColors + 1); dib->dsBitfields[2] = *((const DWORD *)bmi->bmiColors + 2); + if (!dib->dsBitfields[0] || !dib->dsBitfields[1] || !dib->dsBitfields[2]) goto error; } /* get storage location for DIB bits */ diff --git a/dlls/gdi32/tests/bitmap.c b/dlls/gdi32/tests/bitmap.c index fd6062fcb97..29f2c8c8dca 100644 --- a/dlls/gdi32/tests/bitmap.c +++ b/dlls/gdi32/tests/bitmap.c @@ -531,18 +531,6 @@ static void test_dibsections(void) ok(hdib == NULL, "CreateDIBSection should fail when asked to create a compressed DIB section\n"); ok(GetLastError() == 0xdeadbeef, "wrong error %d\n", GetLastError()); - for (i = 0; i < 128; i++) - { - pbmi->bmiHeader.biBitCount = i; - pbmi->bmiHeader.biCompression = BI_RGB; - hdib = CreateDIBSection(hdc, pbmi, DIB_RGB_COLORS, (void**)&bits, NULL, 0); - if (i == 1 || i == 4 || i == 8 || i == 16 || i == 24 || i == 32) - ok(hdib != NULL, "CreateDIBSection bpp %u\n", i); - else - ok(hdib == NULL, "CreateDIBSection bpp %u succeeded\n", i); - if (hdib) DeleteObject( hdib ); - } - pbmi->bmiHeader.biBitCount = 16; pbmi->bmiHeader.biCompression = BI_BITFIELDS; ((PDWORD)pbmi->bmiColors)[0] = 0xf800; @@ -862,6 +850,229 @@ static void test_dibsections(void) ReleaseDC(0, hdc); } +static void test_dib_formats(void) +{ + char buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256])]; + BITMAPINFO *bi = (BITMAPINFO *)buffer; + char data[256]; + void *bits; + int planes, bpp, compr; + HBITMAP hdib, hbmp; + HDC hdc, memdc; + UINT ret; + BOOL expect_ok, todo; + + hdc = GetDC( 0 ); + memdc = CreateCompatibleDC( 0 ); + hbmp = CreateCompatibleBitmap( hdc, 10, 10 ); + + memset( data, 0xaa, sizeof(data) ); + + for (bpp = 0; bpp <= 64; bpp++) + { + for (planes = 0; planes <= 64; planes++) + { + for (compr = 0; compr < 8; compr++) + { + switch (bpp) + { + case 1: + case 4: + case 8: + case 24: expect_ok = (compr == BI_RGB); break; + case 16: + case 32: expect_ok = (compr == BI_RGB || compr == BI_BITFIELDS); break; + default: expect_ok = FALSE; break; + } + if (!planes) expect_ok = FALSE; + todo = (compr == BI_BITFIELDS); /* wine doesn't like strange bitfields */ + + memset( bi, 0, sizeof(bi->bmiHeader) ); + bi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bi->bmiHeader.biWidth = 2; + bi->bmiHeader.biHeight = 2; + bi->bmiHeader.biPlanes = planes; + bi->bmiHeader.biBitCount = bpp; + bi->bmiHeader.biCompression = compr; + bi->bmiHeader.biSizeImage = 0; + memset( bi->bmiColors, 0xaa, sizeof(RGBQUAD) * 256 ); + + hdib = CreateDIBSection(hdc, bi, DIB_RGB_COLORS, (void**)&bits, NULL, 0); + if (expect_ok && (planes == 1 || planes * bpp <= 16)) + ok( hdib != NULL, "CreateDIBSection failed for %u/%u/%u\n", bpp, planes, compr ); + else + ok( hdib == NULL, "CreateDIBSection succeeded for %u/%u/%u\n", bpp, planes, compr ); + if (hdib) DeleteObject( hdib ); + + hdib = CreateDIBitmap( hdc, &bi->bmiHeader, 0, data, bi, DIB_RGB_COLORS ); + /* no sanity checks in CreateDIBitmap except compression */ + if (compr == BI_JPEG || compr == BI_PNG) + ok( hdib == NULL || broken(hdib != NULL), /* nt4 */ + "CreateDIBitmap succeeded for %u/%u/%u\n", bpp, planes, compr ); + else + ok( hdib != NULL, "CreateDIBitmap failed for %u/%u/%u\n", bpp, planes, compr ); + if (hdib) DeleteObject( hdib ); + + /* RLE needs a size */ + bi->bmiHeader.biSizeImage = 0; + ret = SetDIBits(hdc, hbmp, 0, 1, data, bi, DIB_RGB_COLORS); + if (expect_ok) + { + if (todo) + todo_wine ok( ret, "SetDIBits failed for %u/%u/%u\n", bpp, planes, compr ); + else + ok( ret, "SetDIBits failed for %u/%u/%u\n", bpp, planes, compr ); + } + else + ok( !ret || + broken((bpp == 4 && compr == BI_RLE4) || (bpp == 8 && compr == BI_RLE8)), /* nt4 */ + "SetDIBits succeeded for %u/%u/%u\n", bpp, planes, compr ); + ret = SetDIBitsToDevice( memdc, 0, 0, 1, 1, 0, 0, 0, 1, data, bi, DIB_RGB_COLORS ); + if (expect_ok) + ok( ret, "SetDIBitsToDevice failed for %u/%u/%u\n", bpp, planes, compr ); + else + ok( !ret || + broken((bpp == 4 && compr == BI_RLE4) || (bpp == 8 && compr == BI_RLE8)), /* nt4 */ + "SetDIBitsToDevice succeeded for %u/%u/%u\n", bpp, planes, compr ); + ret = StretchDIBits( memdc, 0, 0, 1, 1, 0, 0, 1, 1, data, bi, DIB_RGB_COLORS, SRCCOPY ); + if (expect_ok) + { + if (todo) + todo_wine ok( ret, "StretchDIBits failed for %u/%u/%u\n", bpp, planes, compr ); + else + ok( ret, "StretchDIBits failed for %u/%u/%u\n", bpp, planes, compr ); + } + else + ok( !ret || + broken((bpp == 4 && compr == BI_RLE4) || (bpp == 8 && compr == BI_RLE8)), /* nt4 */ + "StretchDIBits succeeded for %u/%u/%u\n", bpp, planes, compr ); + + bi->bmiHeader.biSizeImage = 1; + ret = SetDIBits(hdc, hbmp, 0, 1, data, bi, DIB_RGB_COLORS); + if (expect_ok || (bpp == 4 && compr == BI_RLE4) || (bpp == 8 && compr == BI_RLE8)) + { + if (todo) + todo_wine ok( ret, "SetDIBits failed for %u/%u/%u\n", bpp, planes, compr ); + else + ok( ret, "SetDIBits failed for %u/%u/%u\n", bpp, planes, compr ); + } + else + ok( !ret, "SetDIBits succeeded for %u/%u/%u\n", bpp, planes, compr ); + ret = SetDIBitsToDevice( memdc, 0, 0, 1, 1, 0, 0, 0, 1, data, bi, DIB_RGB_COLORS ); + if (expect_ok || (bpp == 4 && compr == BI_RLE4) || (bpp == 8 && compr == BI_RLE8)) + ok( ret, "SetDIBitsToDevice failed for %u/%u/%u\n", bpp, planes, compr ); + else + ok( !ret, "SetDIBitsToDevice succeeded for %u/%u/%u\n", bpp, planes, compr ); + ret = StretchDIBits( memdc, 0, 0, 1, 1, 0, 0, 1, 1, data, bi, DIB_RGB_COLORS, SRCCOPY ); + if (expect_ok || (bpp == 4 && compr == BI_RLE4) || (bpp == 8 && compr == BI_RLE8)) + { + if (todo) + todo_wine ok( ret, "StretchDIBits failed for %u/%u/%u\n", bpp, planes, compr ); + else + ok( ret, "StretchDIBits failed for %u/%u/%u\n", bpp, planes, compr ); + } + else + ok( !ret, "StretchDIBits succeeded for %u/%u/%u\n", bpp, planes, compr ); + } + } + } + + memset( bi, 0, sizeof(bi->bmiHeader) ); + bi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bi->bmiHeader.biWidth = 2; + bi->bmiHeader.biHeight = 2; + bi->bmiHeader.biPlanes = 1; + bi->bmiHeader.biBitCount = 16; + bi->bmiHeader.biCompression = BI_BITFIELDS; + bi->bmiHeader.biSizeImage = 0; + *(DWORD *)&bi->bmiColors[0] = 0; + *(DWORD *)&bi->bmiColors[1] = 0; + *(DWORD *)&bi->bmiColors[2] = 0; + + hdib = CreateDIBSection(hdc, bi, DIB_RGB_COLORS, (void**)&bits, NULL, 0); + ok( hdib == NULL, "CreateDIBSection succeeded with null bitfields\n" ); + ret = SetDIBits(hdc, hbmp, 0, 1, data, bi, DIB_RGB_COLORS); + ok( !ret, "SetDIBits succeeded with null bitfields\n" ); + /* other functions don't check */ + hdib = CreateDIBitmap( hdc, &bi->bmiHeader, 0, bits, bi, DIB_RGB_COLORS ); + ok( hdib != NULL, "CreateDIBitmap failed with null bitfields\n" ); + DeleteObject( hdib ); + ret = SetDIBitsToDevice( memdc, 0, 0, 1, 1, 0, 0, 0, 1, data, bi, DIB_RGB_COLORS ); + ok( ret, "SetDIBitsToDevice failed with null bitfields\n" ); + ret = StretchDIBits( memdc, 0, 0, 1, 1, 0, 0, 1, 1, data, bi, DIB_RGB_COLORS, SRCCOPY ); + todo_wine ok( ret, "StretchDIBits failed with null bitfields\n" ); + + /* all fields must be non-zero */ + *(DWORD *)&bi->bmiColors[0] = 3; + *(DWORD *)&bi->bmiColors[1] = 0; + *(DWORD *)&bi->bmiColors[2] = 7; + hdib = CreateDIBSection(hdc, bi, DIB_RGB_COLORS, (void**)&bits, NULL, 0); + ok( hdib == NULL, "CreateDIBSection succeeded with null bitfields\n" ); + ret = SetDIBits(hdc, hbmp, 0, 1, data, bi, DIB_RGB_COLORS); + ok( !ret, "SetDIBits succeeded with null bitfields\n" ); + + /* garbage is ok though */ + *(DWORD *)&bi->bmiColors[0] = 0x55; + *(DWORD *)&bi->bmiColors[1] = 0x44; + *(DWORD *)&bi->bmiColors[2] = 0x33; + hdib = CreateDIBSection(hdc, bi, DIB_RGB_COLORS, (void**)&bits, NULL, 0); + ok( hdib != NULL, "CreateDIBSection failed with bad bitfields\n" ); + if (hdib) DeleteObject( hdib ); + ret = SetDIBits(hdc, hbmp, 0, 1, data, bi, DIB_RGB_COLORS); + todo_wine ok( ret, "SetDIBits failed with bad bitfields\n" ); + + bi->bmiHeader.biWidth = -2; + bi->bmiHeader.biHeight = 2; + bi->bmiHeader.biBitCount = 32; + bi->bmiHeader.biCompression = BI_RGB; + hdib = CreateDIBSection(hdc, bi, DIB_RGB_COLORS, (void**)&bits, NULL, 0); + ok( hdib == NULL, "CreateDIBSection succeeded with negative width\n" ); + hdib = CreateDIBitmap( hdc, &bi->bmiHeader, 0, bits, bi, DIB_RGB_COLORS ); + ok( hdib == NULL, "CreateDIBitmap succeeded with negative width\n" ); + ret = SetDIBits(hdc, hbmp, 0, 1, data, bi, DIB_RGB_COLORS); + ok( !ret, "SetDIBits succeeded with negative width\n" ); + ret = SetDIBitsToDevice( memdc, 0, 0, 1, 1, 0, 0, 0, 1, data, bi, DIB_RGB_COLORS ); + ok( !ret, "SetDIBitsToDevice succeeded with negative width\n" ); + ret = StretchDIBits( memdc, 0, 0, 1, 1, 0, 0, 1, 1, data, bi, DIB_RGB_COLORS, SRCCOPY ); + ok( !ret, "StretchDIBits succeeded with negative width\n" ); + + bi->bmiHeader.biWidth = 0; + bi->bmiHeader.biHeight = 2; + bi->bmiHeader.biBitCount = 32; + bi->bmiHeader.biCompression = BI_RGB; + hdib = CreateDIBSection(hdc, bi, DIB_RGB_COLORS, (void**)&bits, NULL, 0); + ok( hdib == NULL, "CreateDIBSection succeeded with zero width\n" ); + hdib = CreateDIBitmap( hdc, &bi->bmiHeader, 0, bits, bi, DIB_RGB_COLORS ); + ok( hdib != NULL, "CreateDIBitmap failed with zero width\n" ); + DeleteObject( hdib ); + ret = SetDIBits(hdc, hbmp, 0, 1, data, bi, DIB_RGB_COLORS); + ok( !ret || broken(ret), /* nt4 */ "SetDIBits succeeded with zero width\n" ); + ret = SetDIBitsToDevice( memdc, 0, 0, 1, 1, 0, 0, 0, 1, data, bi, DIB_RGB_COLORS ); + ok( !ret || broken(ret), /* nt4 */ "SetDIBitsToDevice succeeded with zero width\n" ); + ret = StretchDIBits( memdc, 0, 0, 1, 1, 0, 0, 1, 1, data, bi, DIB_RGB_COLORS, SRCCOPY ); + ok( !ret || broken(ret), /* nt4 */ "StretchDIBits succeeded with zero width\n" ); + + bi->bmiHeader.biWidth = 2; + bi->bmiHeader.biHeight = 0; + bi->bmiHeader.biBitCount = 32; + bi->bmiHeader.biCompression = BI_RGB; + hdib = CreateDIBSection(hdc, bi, DIB_RGB_COLORS, (void**)&bits, NULL, 0); + ok( hdib == NULL, "CreateDIBSection succeeded with zero height\n" ); + hdib = CreateDIBitmap( hdc, &bi->bmiHeader, 0, bits, bi, DIB_RGB_COLORS ); + ok( hdib != NULL, "CreateDIBitmap failed with zero height\n" ); + DeleteObject( hdib ); + ret = SetDIBits(hdc, hbmp, 0, 1, data, bi, DIB_RGB_COLORS); + ok( !ret, "SetDIBits succeeded with zero height\n" ); + ret = SetDIBitsToDevice( memdc, 0, 0, 1, 1, 0, 0, 0, 1, data, bi, DIB_RGB_COLORS ); + ok( !ret, "SetDIBitsToDevice succeeded with zero height\n" ); + ret = StretchDIBits( memdc, 0, 0, 1, 1, 0, 0, 1, 1, data, bi, DIB_RGB_COLORS, SRCCOPY ); + ok( !ret, "StretchDIBits succeeded with zero height\n" ); + + DeleteDC( memdc ); + DeleteObject( hbmp ); + ReleaseDC( 0, hdc ); +} + static void test_mono_dibsection(void) { HDC hdc, memdc; @@ -3900,6 +4111,7 @@ START_TEST(bitmap) test_createdibitmap(); test_dibsections(); + test_dib_formats(); test_mono_dibsection(); test_bitmap(); test_bmBits();