This is the initial set of patches for handling new pixel format types. I've pushed a branch [here](https://gitlab.winehq.org/cmcadams/wine/-/commits/WIP/d3dx-shared-source-v13) containing the rest of my current patches if additional context would be useful, the new format handling patches end with `ed7022a0838b346f4fa2b229ce7e8e6b8ebc2244`.
-- v2: d3dx9: Use format_from_d3dx_color() instead of fill_texture(). d3dx9: Add support for D3DFMT_V8U8. d3dx9: Add support for D3DFMT_Q8W8V8U8. d3dx9: Store pixel value range alongside pixel values when reading pixels. d3dx9: Clamp source components to unorm range. d3dx9/tests: Add format conversion tests for premultiplied alpha DXTn formats. d3dx9/tests: Add more d3d format conversion tests.
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/d3dx9_36/tests/surface.c | 635 ++++++++++++++++++++++++++++++++++ 1 file changed, 635 insertions(+)
diff --git a/dlls/d3dx9_36/tests/surface.c b/dlls/d3dx9_36/tests/surface.c index fdc573c3e55..abd583e296a 100644 --- a/dlls/d3dx9_36/tests/surface.c +++ b/dlls/d3dx9_36/tests/surface.c @@ -26,6 +26,23 @@ #include <stdint.h> #include "d3dx9_test_images.h"
+#define ADMITTED_ERROR 0.0001f + +static inline float relative_error(float expected, float got) +{ + return expected == 0.0f ? fabs(expected - got) : fabs(1.0f - got / expected); +} + +static BOOL relative_float_match(float expected, float got, float admitted_error) +{ + if ((isnan(expected))) + return isnan(got); + if ((isinf(expected))) + return isinf(got); + + return relative_error(expected, got) < ADMITTED_ERROR; +} + #define check_release(obj, exp) _check_release(__LINE__, obj, exp) static inline void _check_release(unsigned int line, IUnknown *obj, int exp) { @@ -755,6 +772,22 @@ static inline void _check_pixel_4bpp(unsigned int line, const D3DLOCKED_RECT *lo ok_(__FILE__, line)(color == expected_color, "Got color 0x%08lx, expected 0x%08lx\n", color, expected_color); }
+#define check_pixel_float4(lockrect, x, y, fx, fy, fz, fw, todo) \ + _check_pixel_float4(__LINE__, lockrect, x, y, fx, fy, fz, fw, todo) +static inline void _check_pixel_float4(uint32_t line, const D3DLOCKED_RECT *lockrect, uint32_t x, uint32_t y, + float fx, float fy, float fz, float fw, BOOL todo) +{ + float *ptr = (float *)(((BYTE *)lockrect->pBits) + (y * lockrect->Pitch) + (x * sizeof(float) * 4)); + + todo_wine_if(todo) ok_(__FILE__, line)(relative_float_match(fx, ptr[0], ADMITTED_ERROR) + && relative_float_match(fy, ptr[1], ADMITTED_ERROR) + && relative_float_match(fz, ptr[2], ADMITTED_ERROR) + && relative_float_match(fw, ptr[3], ADMITTED_ERROR), + "Expected (%f, %f, %f, %f), got (%f, %f, %f, %f)\n", + fx, fy, fz, fw, + ptr[0], ptr[1], ptr[2], ptr[3]); +} + #define check_readback_pixel_4bpp(rb, x, y, color, todo) _check_readback_pixel_4bpp(__LINE__, rb, x, y, color, todo) static inline void _check_readback_pixel_4bpp(unsigned int line, struct surface_readback *rb, uint32_t x, uint32_t y, uint32_t expected_color, BOOL todo) @@ -763,6 +796,287 @@ static inline void _check_readback_pixel_4bpp(unsigned int line, struct surface_ todo_wine_if(todo) ok_(__FILE__, line)(color == expected_color, "Got color 0x%08x, expected 0x%08x.\n", color, expected_color); }
+static const PALETTEENTRY test_palette[256] = +{ + {0x00,0x00,0x00,0x00}, {0x00,0x00,0x80,0x01}, {0x00,0x80,0x00,0x02}, {0x00,0x80,0x80,0x03}, + {0x80,0x00,0x00,0x04}, {0x80,0x00,0x80,0x05}, {0x80,0x80,0x00,0x06}, {0xc0,0xc0,0xc0,0x07}, + {0xc0,0xdc,0xc0,0x08}, {0xf0,0xca,0xa6,0x09}, {0x00,0x20,0x40,0x0a}, {0x00,0x20,0x60,0x0b}, + {0x00,0x20,0x80,0x0c}, {0x00,0x20,0xa0,0x0d}, {0x00,0x20,0xc0,0x0e}, {0x00,0x20,0xe0,0x0f}, + + {0x00,0x40,0x00,0x10}, {0x00,0x40,0x20,0x11}, {0x00,0x40,0x40,0x12}, {0x00,0x40,0x60,0x13}, + {0x00,0x40,0x80,0x14}, {0x00,0x40,0xa0,0x15}, {0x00,0x40,0xc0,0x16}, {0x00,0x40,0xe0,0x17}, + {0x00,0x60,0x00,0x18}, {0x00,0x60,0x20,0x19}, {0x00,0x60,0x40,0x1a}, {0x00,0x60,0x60,0x1b}, + {0x00,0x60,0x80,0x1c}, {0x00,0x60,0xa0,0x1d}, {0x00,0x60,0xc0,0x1e}, {0x00,0x60,0xe0,0x1f}, + + {0x00,0x80,0x00,0x20}, {0x00,0x80,0x20,0x21}, {0x00,0x80,0x40,0x22}, {0x00,0x80,0x60,0x23}, + {0x00,0x80,0x80,0x24}, {0x00,0x80,0xa0,0x25}, {0x00,0x80,0xc0,0x26}, {0x00,0x80,0xe0,0x27}, + {0x00,0xa0,0x00,0x28}, {0x00,0xa0,0x20,0x29}, {0x00,0xa0,0x40,0x2a}, {0x00,0xa0,0x60,0x2b}, + {0x00,0xa0,0x80,0x2c}, {0x00,0xa0,0xa0,0x2d}, {0x00,0xa0,0xc0,0x2e}, {0x00,0xa0,0xe0,0x2f}, + + {0x00,0xc0,0x00,0x30}, {0x00,0xc0,0x20,0x31}, {0x00,0xc0,0x40,0x32}, {0x00,0xc0,0x60,0x33}, + {0x00,0xc0,0x80,0x34}, {0x00,0xc0,0xa0,0x35}, {0x00,0xc0,0xc0,0x36}, {0x00,0xc0,0xe0,0x37}, + {0x00,0xe0,0x00,0x38}, {0x00,0xe0,0x20,0x39}, {0x00,0xe0,0x40,0x3a}, {0x00,0xe0,0x60,0x3b}, + {0x00,0xe0,0x80,0x3c}, {0x00,0xe0,0xa0,0x3d}, {0x00,0xe0,0xc0,0x3e}, {0x00,0xe0,0xe0,0x3f}, + + {0x40,0x00,0x00,0x40}, {0x40,0x00,0x20,0x41}, {0x40,0x00,0x40,0x42}, {0x40,0x00,0x60,0x43}, + {0x40,0x00,0x80,0x44}, {0x40,0x00,0xa0,0x45}, {0x40,0x00,0xc0,0x46}, {0x40,0x00,0xe0,0x47}, + {0x40,0x20,0x00,0x48}, {0x40,0x20,0x20,0x49}, {0x40,0x20,0x40,0x4a}, {0x40,0x20,0x60,0x4b}, + {0x40,0x20,0x80,0x4c}, {0x40,0x20,0xa0,0x4d}, {0x40,0x20,0xc0,0x4e}, {0x40,0x20,0xe0,0x4f}, + + {0x40,0x40,0x00,0x50}, {0x40,0x40,0x20,0x51}, {0x40,0x40,0x40,0x52}, {0x40,0x40,0x60,0x53}, + {0x40,0x40,0x80,0x54}, {0x40,0x40,0xa0,0x55}, {0x40,0x40,0xc0,0x56}, {0x40,0x40,0xe0,0x57}, + {0x40,0x60,0x00,0x58}, {0x40,0x60,0x20,0x59}, {0x40,0x60,0x40,0x5a}, {0x40,0x60,0x60,0x5b}, + {0x40,0x60,0x80,0x5c}, {0x40,0x60,0xa0,0x5d}, {0x40,0x60,0xc0,0x5e}, {0x40,0x60,0xe0,0x5f}, + + {0x40,0x80,0x00,0x60}, {0x40,0x80,0x20,0x61}, {0x40,0x80,0x40,0x62}, {0x40,0x80,0x60,0x63}, + {0x40,0x80,0x80,0x64}, {0x40,0x80,0xa0,0x65}, {0x40,0x80,0xc0,0x66}, {0x40,0x80,0xe0,0x67}, + {0x40,0xa0,0x00,0x68}, {0x40,0xa0,0x20,0x69}, {0x40,0xa0,0x40,0x6a}, {0x40,0xa0,0x60,0x6b}, + {0x40,0xa0,0x80,0x6c}, {0x40,0xa0,0xa0,0x6d}, {0x40,0xa0,0xc0,0x6e}, {0x40,0xa0,0xe0,0x6f}, + + {0x40,0xc0,0x00,0x70}, {0x40,0xc0,0x20,0x71}, {0x40,0xc0,0x40,0x72}, {0x40,0xc0,0x60,0x73}, + {0x40,0xc0,0x80,0x74}, {0x40,0xc0,0xa0,0x75}, {0x40,0xc0,0xc0,0x76}, {0x40,0xc0,0xe0,0x77}, + {0x40,0xe0,0x00,0x78}, {0x40,0xe0,0x20,0x79}, {0x40,0xe0,0x40,0x7a}, {0x40,0xe0,0x60,0x7b}, + {0x40,0xe0,0x80,0x7c}, {0x40,0xe0,0xa0,0x7d}, {0x40,0xe0,0xc0,0x7e}, {0x40,0xe0,0xe0,0x7f}, + + {0x80,0x00,0x00,0x80}, {0x80,0x00,0x20,0x81}, {0x80,0x00,0x40,0x82}, {0x80,0x00,0x60,0x83}, + {0x80,0x00,0x80,0x84}, {0x80,0x00,0xa0,0x85}, {0x80,0x00,0xc0,0x86}, {0x80,0x00,0xe0,0x87}, + {0x80,0x20,0x00,0x88}, {0x80,0x20,0x20,0x89}, {0x80,0x20,0x40,0x8a}, {0x80,0x20,0x60,0x8b}, + {0x80,0x20,0x80,0x8c}, {0x80,0x20,0xa0,0x8d}, {0x80,0x20,0xc0,0x8e}, {0x80,0x20,0xe0,0x8f}, + + {0x80,0x40,0x00,0x90}, {0x80,0x40,0x20,0x91}, {0x80,0x40,0x40,0x92}, {0x80,0x40,0x60,0x93}, + {0x80,0x40,0x80,0x94}, {0x80,0x40,0xa0,0x95}, {0x80,0x40,0xc0,0x96}, {0x80,0x40,0xe0,0x97}, + {0x80,0x60,0x00,0x98}, {0x80,0x60,0x20,0x99}, {0x80,0x60,0x40,0x9a}, {0x80,0x60,0x60,0x9b}, + {0x80,0x60,0x80,0x9c}, {0x80,0x60,0xa0,0x9d}, {0x80,0x60,0xc0,0x9e}, {0x80,0x60,0xe0,0x9f}, + + {0x80,0x80,0x00,0xa0}, {0x80,0x80,0x20,0xa1}, {0x80,0x80,0x40,0xa2}, {0x80,0x80,0x60,0xa3}, + {0x80,0x80,0x80,0xa4}, {0x80,0x80,0xa0,0xa5}, {0x80,0x80,0xc0,0xa6}, {0x80,0x80,0xe0,0xa7}, + {0x80,0xa0,0x00,0xa8}, {0x80,0xa0,0x20,0xa9}, {0x80,0xa0,0x40,0xaa}, {0x80,0xa0,0x60,0xab}, + {0x80,0xa0,0x80,0xac}, {0x80,0xa0,0xa0,0xad}, {0x80,0xa0,0xc0,0xae}, {0x80,0xa0,0xe0,0xaf}, + + {0x80,0xc0,0x00,0xb0}, {0x80,0xc0,0x20,0xb1}, {0x80,0xc0,0x40,0xb2}, {0x80,0xc0,0x60,0xb3}, + {0x80,0xc0,0x80,0xb4}, {0x80,0xc0,0xa0,0xb5}, {0x80,0xc0,0xc0,0xb6}, {0x80,0xc0,0xe0,0xb7}, + {0x80,0xe0,0x00,0xb8}, {0x80,0xe0,0x20,0xb9}, {0x80,0xe0,0x40,0xba}, {0x80,0xe0,0x60,0xbb}, + {0x80,0xe0,0x80,0xbc}, {0x80,0xe0,0xa0,0xbd}, {0x80,0xe0,0xc0,0xbe}, {0x80,0xe0,0xe0,0xbf}, + + {0xc0,0x00,0x00,0xc0}, {0xc0,0x00,0x20,0xc1}, {0xc0,0x00,0x40,0xc2}, {0xc0,0x00,0x60,0xc3}, + {0xc0,0x00,0x80,0xc4}, {0xc0,0x00,0xa0,0xc5}, {0xc0,0x00,0xc0,0xc6}, {0xc0,0x00,0xe0,0xc7}, + {0xc0,0x20,0x00,0xc8}, {0xc0,0x20,0x20,0xc9}, {0xc0,0x20,0x40,0xca}, {0xc0,0x20,0x60,0xcb}, + {0xc0,0x20,0x80,0xcc}, {0xc0,0x20,0xa0,0xcd}, {0xc0,0x20,0xc0,0xce}, {0xc0,0x20,0xe0,0xcf}, + + {0xc0,0x40,0x00,0xd0}, {0xc0,0x40,0x20,0xd1}, {0xc0,0x40,0x40,0xd2}, {0xc0,0x40,0x60,0xd3}, + {0xc0,0x40,0x80,0xd4}, {0xc0,0x40,0xa0,0xd5}, {0xc0,0x40,0xc0,0xd6}, {0xc0,0x40,0xe0,0xd7}, + {0xc0,0x60,0x00,0xd8}, {0xc0,0x60,0x20,0xd9}, {0xc0,0x60,0x40,0xda}, {0xc0,0x60,0x60,0xdb}, + {0xc0,0x60,0x80,0xdc}, {0xc0,0x60,0xa0,0xdd}, {0xc0,0x60,0xc0,0xde}, {0xc0,0x60,0xe0,0xdf}, + + {0xc0,0x80,0x00,0xe0}, {0xc0,0x80,0x20,0xe1}, {0xc0,0x80,0x40,0xe2}, {0xc0,0x80,0x60,0xe3}, + {0xc0,0x80,0x80,0xe4}, {0xc0,0x80,0xa0,0xe5}, {0xc0,0x80,0xc0,0xe6}, {0xc0,0x80,0xe0,0xe7}, + {0xc0,0xa0,0x00,0xe8}, {0xc0,0xa0,0x20,0xe9}, {0xc0,0xa0,0x40,0xea}, {0xc0,0xa0,0x60,0xeb}, + {0xc0,0xa0,0x80,0xec}, {0xc0,0xa0,0xa0,0xed}, {0xc0,0xa0,0xc0,0xee}, {0xc0,0xa0,0xe0,0xef}, + + {0xc0,0xc0,0x00,0xf0}, {0xc0,0xc0,0x20,0xf1}, {0xc0,0xc0,0x40,0xf2}, {0xc0,0xc0,0x60,0xf3}, + {0xc0,0xc0,0x80,0xf4}, {0xc0,0xc0,0xa0,0xf5}, {0xf0,0xfb,0xff,0xf6}, {0xa4,0xa0,0xa0,0xf7}, + {0x80,0x80,0x80,0xf8}, {0x00,0x00,0xff,0xf9}, {0x00,0xff,0x00,0xfa}, {0x00,0xff,0xff,0xfb}, + {0xff,0x00,0x00,0xfc}, {0xff,0x00,0xff,0xfd}, {0xff,0xff,0x00,0xfe}, {0xff,0xff,0xff,0xff}, +}; + +static const uint16_t v16u16_2_2[] = +{ + 0x0000, 0x3000, 0x4000, 0x7fff, 0x8000, 0x8001, 0xc000, 0xffff, +}; + +static const uint8_t v16u16_2_2_expected[] = +{ + 0x00,0x80,0x00,0xb0,0x00,0xc0,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x3f,0xff,0x7f, +}; + +static const uint8_t v16u16_2_2_expected2[] = +{ + 0x00,0x00,0x00,0x00,0x80,0x01,0xc0,0x3e,0x00,0x00,0x80,0x3f,0x00,0x00,0x80,0x3f, + 0x00,0x01,0x00,0x3f,0x00,0x00,0x80,0x3f,0x00,0x00,0x80,0x3f,0x00,0x00,0x80,0x3f, + 0x00,0x00,0x80,0xbf,0x00,0x00,0x80,0xbf,0x00,0x00,0x80,0x3f,0x00,0x00,0x80,0x3f, + 0x00,0x01,0x00,0xbf,0x00,0x01,0x00,0xb8,0x00,0x00,0x80,0x3f,0x00,0x00,0x80,0x3f, +}; + +static const uint8_t v8u8_2_2[] = +{ + 0x00,0x30,0x40,0x7f,0x80,0x81,0xc0,0xff, +}; + +static const uint8_t v8u8_2_2_expected[] = +{ + 0x00,0x00,0x00,0x00,0x06,0x83,0xc1,0x3e,0x00,0x00,0x80,0x3f,0x00,0x00,0x80,0x3f, + 0x04,0x02,0x01,0x3f,0x00,0x00,0x80,0x3f,0x00,0x00,0x80,0x3f,0x00,0x00,0x80,0x3f, + 0x00,0x00,0x80,0xbf,0x00,0x00,0x80,0xbf,0x00,0x00,0x80,0x3f,0x00,0x00,0x80,0x3f, + 0x04,0x02,0x01,0xbf,0x04,0x02,0x01,0xbc,0x00,0x00,0x80,0x3f,0x00,0x00,0x80,0x3f, +}; + +static const uint16_t a16b16g16r16_2_2[] = +{ + 0x0000,0x1000,0x2000,0x3000,0x4000,0x5000,0x6000,0x7000, + 0x8000,0x9000,0xa000,0xb000,0xc000,0xd000,0xe000,0xffff, +}; + +static const uint8_t a16b16g16r16_2_2_expected[] = +{ + 0x00,0x00,0x00,0x00,0x80,0x00,0x80,0x3d,0x80,0x00,0x00,0x3e,0xc0,0x00,0x40,0x3e, + 0x80,0x00,0x80,0x3e,0xa0,0x00,0xa0,0x3e,0xc0,0x00,0xc0,0x3e,0xe0,0x00,0xe0,0x3e, + 0x80,0x00,0x00,0x3f,0x90,0x00,0x10,0x3f,0xa0,0x00,0x20,0x3f,0xb0,0x00,0x30,0x3f, + 0xc0,0x00,0x40,0x3f,0xd0,0x00,0x50,0x3f,0xe0,0x00,0x60,0x3f,0x00,0x00,0x80,0x3f, +}; + +static const uint16_t q16w16v16u16_2_2[] = +{ + 0x0000,0x1000,0x2000,0x3000,0x4000,0x5000,0x6000,0x7fff, + 0x8000,0x8001,0xa000,0xb000,0xc000,0xd000,0xe000,0xffff, +}; + +static const uint8_t q16w16v16u16_2_2_expected[] = +{ + 0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x3e,0x00,0x01,0x80,0x3e,0x80,0x01,0xc0,0x3e, + 0x00,0x01,0x00,0x3f,0x40,0x01,0x20,0x3f,0x80,0x01,0x40,0x3f,0x00,0x00,0x80,0x3f, + 0x00,0x00,0x80,0xbf,0x00,0x00,0x80,0xbf,0x80,0x01,0x40,0xbf,0x40,0x01,0x20,0xbf, + 0x00,0x01,0x00,0xbf,0x80,0x01,0xc0,0xbe,0x00,0x01,0x80,0xbe,0x00,0x01,0x00,0xb8, +}; + +static const uint8_t p8_2_2[] = +{ + 0x00,0x40,0x80,0xff, +}; + +static const uint8_t p8_2_2_expected[] = +{ + 0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x00,0x00,0x80,0x80,0xff,0xff,0xff,0xff, +}; + +static const uint8_t a8p8_2_2[] = +{ + 0x00,0x10,0x40,0x20,0x80,0x30,0xff,0x40 +}; + +static const uint8_t a8p8_2_2_expected[] = +{ + 0x00,0x00,0x00,0x10,0x00,0x00,0x40,0x20,0x00,0x00,0x80,0x30,0xff,0xff,0xff,0x40, +}; + +static uint32_t get_bpp_for_d3dformat(D3DFORMAT format) +{ + switch (format) + { + case D3DFMT_A32B32G32R32F: + return 16; + + case D3DFMT_A16B16G16R16: + case D3DFMT_Q16W16V16U16: + return 8; + + case D3DFMT_A8R8G8B8: + case D3DFMT_V16U16: + case D3DFMT_G16R16: + return 4; + + case D3DFMT_V8U8: + case D3DFMT_A8P8: + return 2; + + case D3DFMT_P8: + return 1; + + default: + assert(0 && "Need to add format to get_bpp_for_d3dformat()."); + return 0; + } +} + +static void test_format_conversion(IDirect3DDevice9 *device) +{ + struct + { + D3DFORMAT src_format; + const PALETTEENTRY *src_palette; + const RECT src_rect; + const void *src_data; + + D3DFORMAT dst_format; + const void *expected_dst_data; + BOOL partial_todo; + BOOL todo; + } tests[] = { + { D3DFMT_P8, test_palette, { 0, 0, 2, 2 }, p8_2_2, D3DFMT_A8R8G8B8, p8_2_2_expected }, + { D3DFMT_A16B16G16R16, NULL, { 0, 0, 2, 2 }, a16b16g16r16_2_2, D3DFMT_A32B32G32R32F, a16b16g16r16_2_2_expected }, + { D3DFMT_V16U16, NULL, { 0, 0, 2, 2 }, v16u16_2_2, D3DFMT_G16R16, v16u16_2_2_expected, .todo = TRUE }, + { D3DFMT_V16U16, NULL, { 0, 0, 2, 2 }, v16u16_2_2, D3DFMT_A32B32G32R32F, v16u16_2_2_expected2, .todo = TRUE }, + { D3DFMT_V8U8, NULL, { 0, 0, 2, 2 }, v8u8_2_2, D3DFMT_A32B32G32R32F, v8u8_2_2_expected, .todo = TRUE }, + { D3DFMT_Q16W16V16U16, NULL, { 0, 0, 2, 2 }, q16w16v16u16_2_2, D3DFMT_A32B32G32R32F, q16w16v16u16_2_2_expected, .todo = TRUE }, + { D3DFMT_A8P8, test_palette, { 0, 0, 2, 2 }, a8p8_2_2, D3DFMT_A8R8G8B8, a8p8_2_2_expected, .todo = TRUE }, + }; + uint32_t i; + + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + IDirect3DTexture9 *tex; + HRESULT hr; + + winetest_push_context("Test %u", i); + hr = IDirect3DDevice9_CreateTexture(device, tests[i].src_rect.right, tests[i].src_rect.bottom, 1, 0, + tests[i].dst_format, D3DPOOL_MANAGED, &tex, NULL); + if (SUCCEEDED(hr)) + { + const uint32_t src_pitch = get_bpp_for_d3dformat(tests[i].src_format) * tests[i].src_rect.right; + const uint32_t dst_pitch = get_bpp_for_d3dformat(tests[i].dst_format) * tests[i].src_rect.right; + IDirect3DSurface9 *surf; + + hr = IDirect3DTexture9_GetSurfaceLevel(tex, 0, &surf); + ok(hr == D3D_OK, "Failed to get the surface, hr %#lx.\n", hr); + + hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, tests[i].src_data, tests[i].src_format, + src_pitch, tests[i].src_palette, &tests[i].src_rect, D3DX_FILTER_NONE, 0); + todo_wine_if(tests[i].todo) ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + { + const uint32_t dst_fmt_bpp = get_bpp_for_d3dformat(tests[i].dst_format); + uint32_t match_count, mismatch_count; + D3DLOCKED_RECT lock_rect; + uint32_t x, y; + + match_count = mismatch_count = 0; + IDirect3DSurface9_LockRect(surf, &lock_rect, NULL, D3DLOCK_READONLY); + for (y = 0; y < tests[i].src_rect.bottom; ++y) + { + const uint8_t *dst_expected_row = ((uint8_t *)tests[i].expected_dst_data) + (dst_pitch * y); + const uint8_t *dst_row = ((uint8_t *)lock_rect.pBits) + (lock_rect.Pitch * y); + + for (x = 0; x < tests[i].src_rect.right; ++x) + { + const uint8_t *dst_expected_pixel = dst_expected_row + (dst_fmt_bpp * x); + const uint8_t *dst_pixel = dst_row + (dst_fmt_bpp * x); + BOOL pixel_match = !memcmp(dst_pixel, dst_expected_pixel, dst_fmt_bpp); + + if (!pixel_match) + mismatch_count++; + else + match_count++; + + if (!pixel_match && tests[i].partial_todo) + continue; + todo_wine_if(tests[i].todo) ok(pixel_match, "Pixel mismatch at (%u,%u).\n", x, y); + } + } + + todo_wine_if(tests[i].partial_todo || tests[i].todo) ok(!mismatch_count, "%u mismatched pixels.\n", mismatch_count); + IDirect3DSurface9_UnlockRect(surf); + } + + check_release((IUnknown *)surf, 1); + check_release((IUnknown *)tex, 0); + } + else + { + skip("Failed to create texture for format %d, hr %#lx.\n", tests[i].dst_format, hr); + } + winetest_pop_context(); + } +} + static void test_D3DXLoadSurface(IDirect3DDevice9 *device) { HRESULT hr; @@ -779,6 +1093,13 @@ static void test_D3DXLoadSurface(IDirect3DDevice9 *device) static const DWORD pixdata_a8b8g8r8[] = { 0xc3394cf0, 0x235ae892, 0x09b197fd, 0x8dc32bf6 }; static const DWORD pixdata_a2r10g10b10[] = { 0x57395aff, 0x5b7668fd, 0xb0d856b5, 0xff2c61d6 }; static const uint32_t pixdata_a8r8g8b8[] = { 0x00102030, 0x40506070, 0x8090a0b0, 0xc0d0e0ff }; + static const uint32_t pixdata_a8b8g8r8_2[] = { 0x30201000, 0x70605040, 0xb0a09080, 0xffe0d0c0 }; + static const uint32_t pixdata_x8l8v8u8[] = { 0x00003000, 0x00557f40, 0x00aa8180, 0x00ffffc0 }; + static const uint32_t pixdata_a2w10v10u10[] = { 0x0ba17400, 0x5ff5d117, 0xae880600, 0xfffe8b45 }; + static const uint32_t pixdata_q8w8v8u8[] = { 0x30201000, 0x7f605040, 0xb0a08180, 0xffe0d0c0 }; + static const float pixdata_a32b32g32r32f[] = { 0.0f, 0.1f, NAN, INFINITY, 1.0f, 1.1f, 1.2f, 1.3f, + -0.1f, -0.2f, -NAN, -INFINITY, -1.0f, -1.1f, -1.2f, -1.3f }; + static const uint16_t pixdata_v8u8[] = { 0x3000, 0x7f40, 0x8180, 0xffc0 }; BYTE buffer[4 * 8 * 4]; uint32_t i;
@@ -1197,6 +1518,34 @@ static void test_D3DXLoadSurface(IDirect3DDevice9 *device) hr = IDirect3DSurface9_UnlockRect(surf); ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#lx.\n", hr);
+ /* From a signed normalized format to an unsigned normalized format. */ + hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_q8w8v8u8, D3DFMT_Q8W8V8U8, 8, NULL, &rect, + D3DX_FILTER_NONE, 0); + todo_wine ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + { + hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); + check_pixel_4bpp(&lockrect, 0, 0, 0xb08090a0); + check_pixel_4bpp(&lockrect, 1, 0, 0xffc0d0e0); + check_pixel_4bpp(&lockrect, 0, 1, 0x2f00001f); + check_pixel_4bpp(&lockrect, 1, 1, 0x7e3f4f5f); + hr = IDirect3DSurface9_UnlockRect(surf); + ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr); + } + + hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_a32b32g32r32f, D3DFMT_A32B32G32R32F, 32, NULL, &rect, + D3DX_FILTER_NONE, 0); + ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); + todo_wine check_pixel_4bpp(&lockrect, 0, 0, 0xff001aff); + todo_wine check_pixel_4bpp(&lockrect, 1, 0, 0xffffffff); + todo_wine check_pixel_4bpp(&lockrect, 0, 1, 0x000000ff); + todo_wine check_pixel_4bpp(&lockrect, 1, 1, 0x00000000); + hr = IDirect3DSurface9_UnlockRect(surf); + ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr); + /* Test D3DXLoadSurfaceFromMemory with indexed color image */ if (0) { @@ -1422,6 +1771,33 @@ static void test_D3DXLoadSurface(IDirect3DDevice9 *device) hr = IDirect3DSurface9_UnlockRect(surf); ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#lx.\n", hr);
+ hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_q8w8v8u8, D3DFMT_Q8W8V8U8, 8, NULL, &rect, + D3DX_FILTER_NONE, 0); + todo_wine ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + { + hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); + check_pixel_2bpp(&lockrect, 0, 0, 0xb08d); + check_pixel_2bpp(&lockrect, 1, 0, 0xffce); + check_pixel_2bpp(&lockrect, 0, 1, 0x2f02); + check_pixel_2bpp(&lockrect, 1, 1, 0x7e4d); + hr = IDirect3DSurface9_UnlockRect(surf); + ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr); + } + + hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_a32b32g32r32f, D3DFMT_A32B32G32R32F, 32, NULL, &rect, + D3DX_FILTER_NONE, 0); + ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); + todo_wine check_pixel_2bpp(&lockrect, 0, 0, 0xff25); + todo_wine check_pixel_2bpp(&lockrect, 1, 0, 0xffff); + todo_wine check_pixel_2bpp(&lockrect, 0, 1, 0x0012); + todo_wine check_pixel_2bpp(&lockrect, 1, 1, 0x0000); + hr = IDirect3DSurface9_UnlockRect(surf); + ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr); + check_release((IUnknown*)surf, 1); check_release((IUnknown*)tex, 0); } @@ -1646,6 +2022,265 @@ static void test_D3DXLoadSurface(IDirect3DDevice9 *device) check_release((IUnknown*)surf, 0); }
+ hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, 2, 2, D3DFMT_A32B32G32R32F, D3DPOOL_DEFAULT, &surf, NULL); + if (FAILED(hr)) + skip("Failed to create a D3DFMT_A32B32G32R32F surface, hr %#lx.\n", hr); + else + { + /* Direct copy. */ + SetRect(&rect, 0, 0, 2, 2); + hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_a32b32g32r32f, D3DFMT_A32B32G32R32F, 32, NULL, &rect, + D3DX_FILTER_NONE, 0); + ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + + hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); + check_pixel_float4(&lockrect, 0, 0, pixdata_a32b32g32r32f[0], pixdata_a32b32g32r32f[1], + pixdata_a32b32g32r32f[2], pixdata_a32b32g32r32f[3], FALSE); + check_pixel_float4(&lockrect, 1, 0, pixdata_a32b32g32r32f[4], pixdata_a32b32g32r32f[5], + pixdata_a32b32g32r32f[6], pixdata_a32b32g32r32f[7], FALSE); + check_pixel_float4(&lockrect, 0, 1, pixdata_a32b32g32r32f[8], pixdata_a32b32g32r32f[9], + pixdata_a32b32g32r32f[10], pixdata_a32b32g32r32f[11], FALSE); + check_pixel_float4(&lockrect, 1, 1, pixdata_a32b32g32r32f[12], pixdata_a32b32g32r32f[13], + pixdata_a32b32g32r32f[14], pixdata_a32b32g32r32f[15], FALSE); + hr = IDirect3DSurface9_UnlockRect(surf); + ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr); + + /* Signed normalized value to full range float. */ + hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_q8w8v8u8, D3DFMT_Q8W8V8U8, 8, NULL, &rect, + D3DX_FILTER_NONE, 0); + todo_wine ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + + hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); + check_pixel_float4(&lockrect, 0, 0, 0.0f, 0.125984f, 0.251969f, 0.377953f, TRUE); + check_pixel_float4(&lockrect, 1, 0, 0.503937f, 0.629921f, 0.755906f, 1.0f, TRUE); + check_pixel_float4(&lockrect, 0, 1, -1.0f, -1.0f, -0.755906f, -0.629921f, TRUE); + check_pixel_float4(&lockrect, 1, 1, -0.503937f, -0.377953f, -0.251969f, -0.007874f, TRUE); + hr = IDirect3DSurface9_UnlockRect(surf); + ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr); + + /* Unsigned normalized value to full range float. */ + hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_a8b8g8r8_2, D3DFMT_A8B8G8R8, 8, NULL, &rect, + D3DX_FILTER_NONE, 0); + ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + + hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); + check_pixel_float4(&lockrect, 0, 0, 0.0f, 0.062745f, 0.125490f, 0.188235f, FALSE); + check_pixel_float4(&lockrect, 1, 0, 0.250980f, 0.313726f, 0.376471f, 0.439216f, FALSE); + check_pixel_float4(&lockrect, 0, 1, 0.501961f, 0.564706f, 0.627451f, 0.690196f, FALSE); + check_pixel_float4(&lockrect, 1, 1, 0.752941f, 0.815686f, 0.878431f, 1.0f, FALSE); + hr = IDirect3DSurface9_UnlockRect(surf); + ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr); + + /* V8U8 snorm. */ + hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_v8u8, D3DFMT_V8U8, 4, NULL, &rect, + D3DX_FILTER_NONE, 0); + todo_wine ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + + hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); + check_pixel_float4(&lockrect, 0, 0, 0.0f, 0.377953f, 1.0f, 1.0f, TRUE); + check_pixel_float4(&lockrect, 1, 0, 0.503937f, 1.0f, 1.0f, 1.0f, TRUE); + check_pixel_float4(&lockrect, 0, 1, -1.0f, -1.0f, 1.0f, 1.0f, TRUE); + check_pixel_float4(&lockrect, 1, 1, -0.503937f, -0.007874f, 1.0f, 1.0f, TRUE); + hr = IDirect3DSurface9_UnlockRect(surf); + ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr); + + /* A2W10V10U10 unorm/snorm. */ + hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_a2w10v10u10, D3DFMT_A2W10V10U10, 8, NULL, &rect, + D3DX_FILTER_NONE, 0); + todo_wine ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + + hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); + check_pixel_float4(&lockrect, 0, 0, 0.0f, 0.181996f, 0.363992f, 0.0f, TRUE); + check_pixel_float4(&lockrect, 1, 0, 0.545988f, 0.727984f, 1.0f, 0.333333f, TRUE); + check_pixel_float4(&lockrect, 0, 1, -1.0f, -1.0f, -0.547945f, 0.666667f, TRUE); + check_pixel_float4(&lockrect, 1, 1, -0.365949f, -0.183953f, -0.001957f, 1.0f, TRUE); + hr = IDirect3DSurface9_UnlockRect(surf); + ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr); + + /* X8L8V8U8 unorm/snorm. */ + hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_x8l8v8u8, D3DFMT_X8L8V8U8, 8, NULL, &rect, + D3DX_FILTER_NONE, 0); + todo_wine ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) + { + /* The luma value goes into the alpha channel. */ + hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); + check_pixel_float4(&lockrect, 0, 0, 0.0f, 0.377953f, 1.0f, 0.0f, FALSE); + check_pixel_float4(&lockrect, 1, 0, 0.503937f, 1.0f, 1.0f, 0.333333f, FALSE); + check_pixel_float4(&lockrect, 0, 1, -1.0f, -1.0f, 1.0f, 0.666667f, FALSE); + check_pixel_float4(&lockrect, 1, 1, -0.503937f, -0.007874f, 1.0f, 1.0f, FALSE); + hr = IDirect3DSurface9_UnlockRect(surf); + ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr); + } + + check_release((IUnknown*)surf, 0); + } + + hr = IDirect3DDevice9_CreateTexture(device, 2, 2, 1, 0, D3DFMT_Q8W8V8U8, D3DPOOL_MANAGED, &tex, NULL); + if (FAILED(hr)) + skip("Failed to create D3DFMT_Q8W8V8U8 texture, hr %#lx.\n", hr); + else + { + hr = IDirect3DTexture9_GetSurfaceLevel(tex, 0, &surf); + ok(hr == D3D_OK, "Failed to get the surface, hr %#lx.\n", hr); + + /* Snorm to snorm, direct copy. */ + SetRect(&rect, 0, 0, 2, 2); + hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_q8w8v8u8, D3DFMT_Q8W8V8U8, 8, NULL, &rect, + D3DX_FILTER_NONE, 0); + todo_wine ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); + todo_wine check_pixel_4bpp(&lockrect, 0, 0, pixdata_q8w8v8u8[0]); + todo_wine check_pixel_4bpp(&lockrect, 1, 0, pixdata_q8w8v8u8[1]); + todo_wine check_pixel_4bpp(&lockrect, 0, 1, pixdata_q8w8v8u8[2]); + todo_wine check_pixel_4bpp(&lockrect, 1, 1, pixdata_q8w8v8u8[3]); + hr = IDirect3DSurface9_UnlockRect(surf); + ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr); + + /* Unorm to snorm. */ + hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_a8b8g8r8, + D3DFMT_A8B8G8R8, 8, NULL, &rect, D3DX_FILTER_NONE, 0); + todo_wine ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + + hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); + todo_wine check_pixel_4bpp(&lockrect, 0, 0, 0x43bbce70); + todo_wine check_pixel_4bpp(&lockrect, 1, 0, 0xa5dc6812); + todo_wine check_pixel_4bpp(&lockrect, 0, 1, 0x8b31177d); + todo_wine check_pixel_4bpp(&lockrect, 1, 1, 0x0d43ad76); + hr = IDirect3DSurface9_UnlockRect(surf); + ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr); + + /* Full range float to snorm. */ + hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_a32b32g32r32f, D3DFMT_A32B32G32R32F, 32, NULL, &rect, + D3DX_FILTER_NONE, 0); + todo_wine ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + + hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); + todo_wine check_pixel_4bpp(&lockrect, 0, 0, 0x7f7f0d00); + todo_wine check_pixel_4bpp(&lockrect, 1, 0, 0x7f7f7f7f); + todo_wine check_pixel_4bpp(&lockrect, 0, 1, 0x827fe8f4); + todo_wine check_pixel_4bpp(&lockrect, 1, 1, 0x82828282); + hr = IDirect3DSurface9_UnlockRect(surf); + ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr); + + /* Unorm alpha and unorm luma to snorm. */ + hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_a8l8, D3DFMT_A8L8, 4, NULL, &rect, D3DX_FILTER_NONE, 0); + todo_wine ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + + hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); + todo_wine check_pixel_4bpp(&lockrect, 0, 0, 0x7f828282); + todo_wine check_pixel_4bpp(&lockrect, 1, 0, 0x827f7f7f); + todo_wine check_pixel_4bpp(&lockrect, 0, 1, 0x7fb2b2b2); + check_pixel_4bpp(&lockrect, 1, 1, 0x00000000); + hr = IDirect3DSurface9_UnlockRect(surf); + ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr); + + check_release((IUnknown*)surf, 1); + check_release((IUnknown*)tex, 0); + } + + hr = IDirect3DDevice9_CreateTexture(device, 2, 2, 1, 0, D3DFMT_X8L8V8U8, D3DPOOL_MANAGED, &tex, NULL); + if (FAILED(hr)) + skip("Failed to create D3DFMT_X8L8V8U8 texture, hr %#lx.\n", hr); + else + { + hr = IDirect3DTexture9_GetSurfaceLevel(tex, 0, &surf); + ok(hr == D3D_OK, "Failed to get the surface, hr %#lx.\n", hr); + + SetRect(&rect, 0, 0, 2, 2); + hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_a8b8g8r8_2, D3DFMT_A8B8G8R8, 8, NULL, &rect, + D3DX_FILTER_NONE, 0); + todo_wine ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + + /* + * The luma channel here doesn't do RGB->Luma conversion, it just + * copies the alpha channel directly as UNORM. V8 and U8 are snorm, + * pulled from r/g respectively. The X8 (b) channel is set to 0. + */ + hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); + todo_wine check_pixel_4bpp(&lockrect, 0, 0, 0x00309282); + todo_wine check_pixel_4bpp(&lockrect, 1, 0, 0x0070d2c2); + todo_wine check_pixel_4bpp(&lockrect, 0, 1, 0x00b01000); + todo_wine check_pixel_4bpp(&lockrect, 1, 1, 0x00ff5040); + hr = IDirect3DSurface9_UnlockRect(surf); + ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr); + + /* Q8 will get converted to unorm range, v8u8 will be copied. */ + hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_q8w8v8u8, D3DFMT_Q8W8V8U8, 8, NULL, &rect, + D3DX_FILTER_NONE, 0); + todo_wine ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + + hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); + todo_wine check_pixel_4bpp(&lockrect, 0, 0, 0x00b01000); + todo_wine check_pixel_4bpp(&lockrect, 1, 0, 0x00ff5040); + todo_wine check_pixel_4bpp(&lockrect, 0, 1, 0x002f8282); + todo_wine check_pixel_4bpp(&lockrect, 1, 1, 0x007ed1c1); + hr = IDirect3DSurface9_UnlockRect(surf); + ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr); + + check_release((IUnknown*)surf, 1); + check_release((IUnknown*)tex, 0); + } + + hr = IDirect3DDevice9_CreateTexture(device, 2, 2, 1, 0, D3DFMT_V8U8, D3DPOOL_MANAGED, &tex, NULL); + if (FAILED(hr)) + skip("Failed to create D3DFMT_V8U8 texture, hr %#lx.\n", hr); + else + { + hr = IDirect3DTexture9_GetSurfaceLevel(tex, 0, &surf); + ok(hr == D3D_OK, "Failed to get the surface, hr %#lx.\n", hr); + + /* R and G channels converted to SNORM range. */ + SetRect(&rect, 0, 0, 2, 2); + hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_a8b8g8r8_2, D3DFMT_A8B8G8R8, 8, NULL, &rect, + D3DX_FILTER_NONE, 0); + todo_wine ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + + hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); + todo_wine check_pixel_2bpp(&lockrect, 0, 0, 0x9282); + todo_wine check_pixel_2bpp(&lockrect, 1, 0, 0xd2c2); + todo_wine check_pixel_2bpp(&lockrect, 0, 1, 0x1000); + todo_wine check_pixel_2bpp(&lockrect, 1, 1, 0x5040); + hr = IDirect3DSurface9_UnlockRect(surf); + ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr); + + /* + * Pull the V8U8 channels. Even though they're the same range, they're + * not directly copied over. 0x80/0x81 are clamped to 0x82, and + * 0xd0/0xc0 end up as 0xd1/0xc1. + */ + hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_q8w8v8u8, D3DFMT_Q8W8V8U8, 8, NULL, &rect, + D3DX_FILTER_NONE, 0); + todo_wine ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + + hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); + todo_wine check_pixel_2bpp(&lockrect, 0, 0, 0x1000); + todo_wine check_pixel_2bpp(&lockrect, 1, 0, 0x5040); + todo_wine check_pixel_2bpp(&lockrect, 0, 1, 0x8282); + todo_wine check_pixel_2bpp(&lockrect, 1, 1, 0xd1c1); + hr = IDirect3DSurface9_UnlockRect(surf); + ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr); + + check_release((IUnknown*)surf, 1); + check_release((IUnknown*)tex, 0); + } + + test_format_conversion(device); + /* cleanup */ if(testdummy_ok) DeleteFileA("testdummy.bmp"); if(testbitmap_ok) DeleteFileA("testbitmap.bmp");
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/d3dx9_36/tests/surface.c | 153 ++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+)
diff --git a/dlls/d3dx9_36/tests/surface.c b/dlls/d3dx9_36/tests/surface.c index abd583e296a..dfa8f4ccd15 100644 --- a/dlls/d3dx9_36/tests/surface.c +++ b/dlls/d3dx9_36/tests/surface.c @@ -1077,6 +1077,158 @@ static void test_format_conversion(IDirect3DDevice9 *device) } }
+static void test_dxt_premultiplied_alpha(IDirect3DDevice9 *device) +{ + static const uint32_t dxt_pma_decompressed_expected[] = + { + 0x00000000, 0x22ffffff, 0x44ffffff, 0x66ffffff, 0x88f7f3f7, 0xaac5c2c5, 0xcca5a2a5, 0xff848284, + 0x00000000, 0x22ffffff, 0x44ffffff, 0x66ffffff, 0x88f7f3f7, 0xaac5c2c5, 0xcca5a2a5, 0xff848284, + }; + static const uint32_t dxt_decompressed_expected[] = + { + 0x00848284, 0x22848284, 0x44848284, 0x66848284, 0x88848284, 0xaa848284, 0xcc848284, 0xff848284, + 0x00848284, 0x22848284, 0x44848284, 0x66848284, 0x88848284, 0xaa848284, 0xcc848284, 0xff848284, + }; + static const uint8_t dxt3_block[] = + { + 0x20,0x64,0xa8,0xfc,0x20,0x64,0xa8,0xfc,0x10,0x84,0x10,0x84,0x00,0x00,0x00,0x00, + }; + static const uint8_t dxt5_block[] = + { + 0x22,0xcc,0x86,0xc6,0xe6,0x86,0xc6,0xe6,0x10,0x84,0x10,0x84,0x00,0x00,0x00,0x00, + }; + static const uint32_t test_compress_pixels[] = + { + 0xffffffff, 0x00ffffff, 0xffffffff, 0x00ffffff, 0xffffffff, 0x00ffffff, 0xffffffff, 0x00ffffff, + 0xffffffff, 0x00ffffff, 0xffffffff, 0x00ffffff, 0xffffffff, 0x00ffffff, 0xffffffff, 0x00ffffff, + }; + static const struct test + { + D3DFORMAT pma_fmt; + D3DFORMAT nonpma_fmt; + const uint8_t *dxt_block; + const char *name; + } tests[] = + { + { D3DFMT_DXT2, D3DFMT_DXT3, dxt3_block, "DXT2 / DXT3" }, + { D3DFMT_DXT4, D3DFMT_DXT5, dxt5_block, "DXT4 / DXT5" }, + }; + static const RECT src_rect = { 0, 0, 4, 4 }; + IDirect3DSurface9 *decomp_surf; + D3DLOCKED_RECT lock_rect; + uint32_t i, x, y; + HRESULT hr; + + hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, 4, 4, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &decomp_surf, NULL); + ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + struct surface_readback surface_rb; + IDirect3DSurface9 *pma_surf, *surf; + IDirect3DTexture9 *pma_tex, *tex; + + winetest_push_context("Test %s", tests[i].name); + hr = IDirect3DDevice9_CreateTexture(device, 4, 4, 1, 0, tests[i].pma_fmt, D3DPOOL_SYSTEMMEM, &pma_tex, NULL); + if (FAILED(hr)) + { + skip("Failed to create texture for format %#x, hr %#lx.\n", tests[i].pma_fmt, hr); + winetest_pop_context(); + continue; + } + + hr = IDirect3DDevice9_CreateTexture(device, 4, 4, 1, 0, tests[i].nonpma_fmt, D3DPOOL_SYSTEMMEM, &tex, NULL); + if (FAILED(hr)) + { + skip("Failed to create texture for format %#x, hr %#lx.\n", tests[i].nonpma_fmt, hr); + IDirect3DTexture9_Release(pma_tex); + winetest_pop_context(); + continue; + } + + hr = IDirect3DTexture9_GetSurfaceLevel(pma_tex, 0, &pma_surf); + ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + + hr = IDirect3DTexture9_GetSurfaceLevel(tex, 0, &surf); + ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + + /* Compress and load the same image onto each DXT surface. */ + hr = D3DXLoadSurfaceFromMemory(pma_surf, NULL, NULL, test_compress_pixels, D3DFMT_A8B8G8R8, 16, NULL, &src_rect, D3DX_FILTER_NONE, 0); + ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + + hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, test_compress_pixels, D3DFMT_A8B8G8R8, 16, NULL, &src_rect, D3DX_FILTER_NONE, 0); + ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + + /* + * For DXT2/DXT4, the source image has all of its color channels + * premultiplied by the alpha value prior to compression. If the + * alpha channel's value is 0, then the color channel values are + * lost. + */ + get_surface_decompressed_readback(device, pma_surf, &surface_rb); + for (y = 0; y < 4; ++y) + { + for (x = 0; x < 4; ++x) + { + const uint32_t expected_pixel = !(x & 0x01) ? 0xffffffff : 0x00000000; + + check_readback_pixel_4bpp(&surface_rb, x, y, expected_pixel, !expected_pixel); + } + } + release_surface_readback(&surface_rb); + + /* For DXT3/DXT5, no premultiplication by the alpha channel value is done. */ + get_surface_decompressed_readback(device, surf, &surface_rb); + for (y = 0; y < 4; ++y) + { + for (x = 0; x < 4; ++x) + check_readback_pixel_4bpp(&surface_rb, x, y, test_compress_pixels[(y * 4) + x], FALSE); + } + release_surface_readback(&surface_rb); + + /* + * Load our test DXT block with the premultiplied alpha DXT format. + * The block is decompressed, and then the premultiplied alpha + * operation is undone prior to being copied to the destination + * surface. + */ + hr = D3DXLoadSurfaceFromMemory(decomp_surf, NULL, NULL, tests[i].dxt_block, tests[i].pma_fmt, 16, NULL, &src_rect, D3DX_FILTER_NONE, 0); + ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + IDirect3DSurface9_LockRect(decomp_surf, &lock_rect, NULL, D3DLOCK_READONLY); + for (y = 0; y < 4; ++y) + { + for (x = 0; x < 4; ++x) + { + const uint32_t expected_pixel = dxt_pma_decompressed_expected[(y * 4) + x]; + const BOOL todo = ((expected_pixel >> 24) & 0xff) != 0xff; + + todo_wine_if(todo) check_pixel_4bpp(&lock_rect, x, y, expected_pixel); + } + } + IDirect3DSurface9_UnlockRect(decomp_surf); + + /* + * Load our test DXT block as a non-premultiplied alpha DXT format. + * The block is decompressed, and the data is copied over directly. + */ + hr = D3DXLoadSurfaceFromMemory(decomp_surf, NULL, NULL, tests[i].dxt_block, tests[i].nonpma_fmt, 16, NULL, &src_rect, D3DX_FILTER_NONE, 0); + ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + IDirect3DSurface9_LockRect(decomp_surf, &lock_rect, NULL, D3DLOCK_READONLY); + for (y = 0; y < 4; ++y) + { + for (x = 0; x < 4; ++x) + check_pixel_4bpp(&lock_rect, x, y, dxt_decompressed_expected[(y * 4) + x]); + } + IDirect3DSurface9_UnlockRect(decomp_surf); + + IDirect3DSurface9_Release(pma_surf); + IDirect3DTexture9_Release(pma_tex); + IDirect3DSurface9_Release(surf); + IDirect3DTexture9_Release(tex); + winetest_pop_context(); + } + IDirect3DSurface9_Release(decomp_surf); +} + static void test_D3DXLoadSurface(IDirect3DDevice9 *device) { HRESULT hr; @@ -2280,6 +2432,7 @@ static void test_D3DXLoadSurface(IDirect3DDevice9 *device) }
test_format_conversion(device); + test_dxt_premultiplied_alpha(device);
/* cleanup */ if(testdummy_ok) DeleteFileA("testdummy.bmp");
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/d3dx9_36/surface.c | 15 ++++++++++++++- dlls/d3dx9_36/tests/surface.c | 12 ++++++------ 2 files changed, 20 insertions(+), 7 deletions(-)
diff --git a/dlls/d3dx9_36/surface.c b/dlls/d3dx9_36/surface.c index f5d0184422b..84cf3262c7f 100644 --- a/dlls/d3dx9_36/surface.c +++ b/dlls/d3dx9_36/surface.c @@ -1474,6 +1474,15 @@ static void get_relevant_argb_components(const struct argb_conversion_info *info } }
+static float d3dx_clamp(float value, float min_value, float max_value) +{ + if (isinf(value)) + return !signbit(value) ? max_value : min_value; + if (isnan(value)) + return max_value; + return value < min_value ? min_value : value > max_value ? max_value : value; +} + /************************************************************ * make_argb_color * @@ -1550,7 +1559,11 @@ static void format_from_vec4(const struct pixel_format_desc *format, const struc else if (format->type == FORMAT_ARGBF) v = *(DWORD *)&src_component; else - v = (DWORD)(src_component * ((1 << format->bits[c]) - 1) + 0.5f); + { + float val = d3dx_clamp(src_component, 0.0f, 1.0f); + + v = val * ((1 << format->bits[c]) - 1) + 0.5f; + }
for (i = format->shift[c] / 8 * 8; i < format->shift[c] + format->bits[c]; i += 8) { diff --git a/dlls/d3dx9_36/tests/surface.c b/dlls/d3dx9_36/tests/surface.c index dfa8f4ccd15..aa3db4fde74 100644 --- a/dlls/d3dx9_36/tests/surface.c +++ b/dlls/d3dx9_36/tests/surface.c @@ -1691,10 +1691,10 @@ static void test_D3DXLoadSurface(IDirect3DDevice9 *device) ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); - todo_wine check_pixel_4bpp(&lockrect, 0, 0, 0xff001aff); - todo_wine check_pixel_4bpp(&lockrect, 1, 0, 0xffffffff); - todo_wine check_pixel_4bpp(&lockrect, 0, 1, 0x000000ff); - todo_wine check_pixel_4bpp(&lockrect, 1, 1, 0x00000000); + check_pixel_4bpp(&lockrect, 0, 0, 0xff001aff); + check_pixel_4bpp(&lockrect, 1, 0, 0xffffffff); + check_pixel_4bpp(&lockrect, 0, 1, 0x000000ff); + check_pixel_4bpp(&lockrect, 1, 1, 0x00000000); hr = IDirect3DSurface9_UnlockRect(surf); ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr);
@@ -1944,9 +1944,9 @@ static void test_D3DXLoadSurface(IDirect3DDevice9 *device) hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); todo_wine check_pixel_2bpp(&lockrect, 0, 0, 0xff25); - todo_wine check_pixel_2bpp(&lockrect, 1, 0, 0xffff); + check_pixel_2bpp(&lockrect, 1, 0, 0xffff); todo_wine check_pixel_2bpp(&lockrect, 0, 1, 0x0012); - todo_wine check_pixel_2bpp(&lockrect, 1, 1, 0x0000); + check_pixel_2bpp(&lockrect, 1, 1, 0x0000); hr = IDirect3DSurface9_UnlockRect(surf); ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr);
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/d3dx9_36/d3dx9_private.h | 13 +++++++- dlls/d3dx9_36/math.c | 10 +++--- dlls/d3dx9_36/surface.c | 62 +++++++++++++++++++++-------------- 3 files changed, 54 insertions(+), 31 deletions(-)
diff --git a/dlls/d3dx9_36/d3dx9_private.h b/dlls/d3dx9_36/d3dx9_private.h index bf16b3a7590..5ee956f041d 100644 --- a/dlls/d3dx9_36/d3dx9_private.h +++ b/dlls/d3dx9_36/d3dx9_private.h @@ -55,6 +55,17 @@ struct vec4 float x, y, z, w; };
+enum range { + RANGE_FULL = 0, + RANGE_UNORM = 1, +}; + +struct d3dx_color +{ + struct vec4 value; + enum range range; +}; + struct volume { UINT width; @@ -178,7 +189,7 @@ HRESULT write_buffer_to_file(const WCHAR *filename, ID3DXBuffer *buffer); const struct pixel_format_desc *get_format_info(D3DFORMAT format); const struct pixel_format_desc *get_format_info_idx(int idx);
-void format_to_vec4(const struct pixel_format_desc *format, const BYTE *src, struct vec4 *dst); +void format_to_d3dx_color(const struct pixel_format_desc *format, const BYTE *src, struct d3dx_color *dst);
void copy_pixels(const BYTE *src, UINT src_row_pitch, UINT src_slice_pitch, BYTE *dst, UINT dst_row_pitch, UINT dst_slice_pitch, const struct volume *size, diff --git a/dlls/d3dx9_36/math.c b/dlls/d3dx9_36/math.c index f018f499c43..4b8f200b199 100644 --- a/dlls/d3dx9_36/math.c +++ b/dlls/d3dx9_36/math.c @@ -3052,7 +3052,7 @@ HRESULT WINAPI D3DXSHProjectCubeMap(unsigned int order, IDirect3DCubeTexture9 *t float diff_solid, x_3d, y_3d; const float u = x * S + B; const float v = y * S + B; - struct vec4 colour; + struct d3dx_color colour; D3DXVECTOR3 dir;
x_3d = (x * 2.0f + 1.0f) / desc.Width - 1.0f; @@ -3093,15 +3093,15 @@ HRESULT WINAPI D3DXSHProjectCubeMap(unsigned int order, IDirect3DCubeTexture9 *t D3DXVec3Normalize(&dir, &dir); D3DXSHEvalDirection(temp, order, &dir);
- format_to_vec4(format, &row[x * format->block_byte_count], &colour); + format_to_d3dx_color(format, &row[x * format->block_byte_count], &colour);
for (i = 0; i < order_square; ++i) { - red[i] += temp[i] * colour.x * diff_solid; + red[i] += temp[i] * colour.value.x * diff_solid; if (green) - green[i] += temp[i] * colour.y * diff_solid; + green[i] += temp[i] * colour.value.y * diff_solid; if (blue) - blue[i] += temp[i] * colour.z * diff_solid; + blue[i] += temp[i] * colour.value.z * diff_solid; } } } diff --git a/dlls/d3dx9_36/surface.c b/dlls/d3dx9_36/surface.c index 84cf3262c7f..6b92ca997ba 100644 --- a/dlls/d3dx9_36/surface.c +++ b/dlls/d3dx9_36/surface.c @@ -26,6 +26,7 @@ #include "wincodec.h"
#include "txc_dxtn.h" +#include <assert.h>
WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
@@ -1507,15 +1508,30 @@ static DWORD make_argb_color(const struct argb_conversion_info *info, const DWOR }
/* It doesn't work for components bigger than 32 bits (or somewhat smaller but unaligned). */ -void format_to_vec4(const struct pixel_format_desc *format, const BYTE *src, struct vec4 *dst) +void format_to_d3dx_color(const struct pixel_format_desc *format, const BYTE *src, struct d3dx_color *dst) { DWORD mask, tmp; unsigned int c;
+ switch (format->type) + { + case FORMAT_ARGBF16: + case FORMAT_ARGBF: + dst->range = RANGE_FULL; + break; + case FORMAT_ARGB: + case FORMAT_INDEX: + dst->range = RANGE_UNORM; + break; + default: /* Shouldn't pass FORMAT_DXT/FORMAT_UNKNOWN into here. */ + assert(0); + break; + } + for (c = 0; c < 4; ++c) { static const unsigned int component_offsets[4] = {3, 0, 1, 2}; - float *dst_component = (float *)dst + component_offsets[c]; + float *dst_component = ((float *)&dst->value) + component_offsets[c];
if (format->bits[c]) { @@ -1537,7 +1553,7 @@ void format_to_vec4(const struct pixel_format_desc *format, const BYTE *src, str }
/* It doesn't work for components bigger than 32 bits. */ -static void format_from_vec4(const struct pixel_format_desc *format, const struct vec4 *src, BYTE *dst) +static void format_from_d3dx_color(const struct pixel_format_desc *format, const struct d3dx_color *src, BYTE *dst) { DWORD v, mask32; unsigned int c, i; @@ -1683,29 +1699,27 @@ void convert_argb_pixels(const BYTE *src, UINT src_row_pitch, UINT src_slice_pit } else { - struct vec4 color, tmp; + struct d3dx_color color, tmp;
- format_to_vec4(src_format, src_ptr, &color); + format_to_d3dx_color(src_format, src_ptr, &color); + tmp = color; if (src_format->to_rgba) - src_format->to_rgba(&color, &tmp, palette); - else - tmp = color; + src_format->to_rgba(&color.value, &tmp.value, palette);
if (ck_format) { DWORD ck_pixel;
- format_from_vec4(ck_format, &tmp, (BYTE *)&ck_pixel); + format_from_d3dx_color(ck_format, &tmp, (BYTE *)&ck_pixel); if (ck_pixel == color_key) - tmp.w = 0.0f; + tmp.value.w = 0.0f; }
+ color = tmp; if (dst_format->from_rgba) - dst_format->from_rgba(&tmp, &color); - else - color = tmp; + dst_format->from_rgba(&tmp.value, &color.value);
- format_from_vec4(dst_format, &color, dst_ptr); + format_from_d3dx_color(dst_format, &color, dst_ptr); }
src_ptr += src_format->bytes_per_pixel; @@ -1791,29 +1805,27 @@ void point_filter_argb_pixels(const BYTE *src, UINT src_row_pitch, UINT src_slic } else { - struct vec4 color, tmp; + struct d3dx_color color, tmp;
- format_to_vec4(src_format, src_ptr, &color); + format_to_d3dx_color(src_format, src_ptr, &color); + tmp = color; if (src_format->to_rgba) - src_format->to_rgba(&color, &tmp, palette); - else - tmp = color; + src_format->to_rgba(&color.value, &tmp.value, palette);
if (ck_format) { DWORD ck_pixel;
- format_from_vec4(ck_format, &tmp, (BYTE *)&ck_pixel); + format_from_d3dx_color(ck_format, &tmp, (BYTE *)&ck_pixel); if (ck_pixel == color_key) - tmp.w = 0.0f; + tmp.value.w = 0.0f; }
+ color = tmp; if (dst_format->from_rgba) - dst_format->from_rgba(&tmp, &color); - else - color = tmp; + dst_format->from_rgba(&tmp.value, &color.value);
- format_from_vec4(dst_format, &color, dst_ptr); + format_from_d3dx_color(dst_format, &color, dst_ptr); }
dst_ptr += dst_format->bytes_per_pixel;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/d3dx9_36/d3dx9_private.h | 6 ++- dlls/d3dx9_36/surface.c | 40 ++++++++++++++- dlls/d3dx9_36/tests/surface.c | 92 +++++++++++++++++------------------ dlls/d3dx9_36/util.c | 1 + 4 files changed, 87 insertions(+), 52 deletions(-)
diff --git a/dlls/d3dx9_36/d3dx9_private.h b/dlls/d3dx9_36/d3dx9_private.h index 5ee956f041d..2338909f7cc 100644 --- a/dlls/d3dx9_36/d3dx9_private.h +++ b/dlls/d3dx9_36/d3dx9_private.h @@ -58,6 +58,7 @@ struct vec4 enum range { RANGE_FULL = 0, RANGE_UNORM = 1, + RANGE_SNORM = 2, };
struct d3dx_color @@ -85,6 +86,7 @@ enum format_type { FORMAT_ARGB, /* unsigned */ FORMAT_ARGBF16,/* float 16 */ FORMAT_ARGBF, /* float */ + FORMAT_ARGB_SNORM, FORMAT_DXT, FORMAT_INDEX, FORMAT_UNKNOWN @@ -168,7 +170,7 @@ extern const struct ID3DXIncludeVtbl d3dx_include_from_file_vtbl; static inline BOOL is_conversion_from_supported(const struct pixel_format_desc *format) { if (format->type == FORMAT_ARGB || format->type == FORMAT_ARGBF16 - || format->type == FORMAT_ARGBF || format->type == FORMAT_DXT) + || format->type == FORMAT_ARGBF || format->type == FORMAT_DXT || format->type == FORMAT_ARGB_SNORM) return TRUE; return !!format->to_rgba; } @@ -176,7 +178,7 @@ static inline BOOL is_conversion_from_supported(const struct pixel_format_desc * static inline BOOL is_conversion_to_supported(const struct pixel_format_desc *format) { if (format->type == FORMAT_ARGB || format->type == FORMAT_ARGBF16 - || format->type == FORMAT_ARGBF || format->type == FORMAT_DXT) + || format->type == FORMAT_ARGBF || format->type == FORMAT_DXT || format->type == FORMAT_ARGB_SNORM) return TRUE; return !!format->from_rgba; } diff --git a/dlls/d3dx9_36/surface.c b/dlls/d3dx9_36/surface.c index 6b92ca997ba..da67c3f061a 100644 --- a/dlls/d3dx9_36/surface.c +++ b/dlls/d3dx9_36/surface.c @@ -1523,6 +1523,9 @@ void format_to_d3dx_color(const struct pixel_format_desc *format, const BYTE *sr case FORMAT_INDEX: dst->range = RANGE_UNORM; break; + case FORMAT_ARGB_SNORM: + dst->range = RANGE_SNORM; + break; default: /* Shouldn't pass FORMAT_DXT/FORMAT_UNKNOWN into here. */ assert(0); break; @@ -1544,6 +1547,26 @@ void format_to_d3dx_color(const struct pixel_format_desc *format, const BYTE *sr *dst_component = float_16_to_32(tmp); else if (format->type == FORMAT_ARGBF) *dst_component = *(float *)&tmp; + else if (format->type == FORMAT_ARGB_SNORM) + { + const uint32_t sign_bit = (1 << (format->bits[c] - 1)); + uint32_t tmp_extended, tmp_masked = (tmp >> format->shift[c] % 8) & mask; + + tmp_extended = tmp_masked; + if (tmp_masked & sign_bit) + { + tmp_extended |= ~(sign_bit - 1); + + /* + * In order to clamp to an even range, we need to ignore + * the maximum negative value. + */ + if (tmp_masked == sign_bit) + tmp_extended |= 1; + } + + *dst_component = (float)(((int32_t)tmp_extended)) / (sign_bit - 1); + } else *dst_component = (float)((tmp >> format->shift[c] % 8) & mask) / mask; } @@ -1574,11 +1597,24 @@ static void format_from_d3dx_color(const struct pixel_format_desc *format, const v = float_32_to_16(src_component); else if (format->type == FORMAT_ARGBF) v = *(DWORD *)&src_component; + else if (format->type == FORMAT_ARGB_SNORM) + { + const uint32_t max_value = (1 << (format->bits[c] - 1)) - 1; + float val = src_component; + + if (src->range == RANGE_UNORM) + val = (val * 2.0f) - 1.0f; + + v = d3dx_clamp(val, -1.0f, 1.0f) * max_value + 0.5f; + } else { - float val = d3dx_clamp(src_component, 0.0f, 1.0f); + float val = src_component; + + if (src->range == RANGE_SNORM) + val = (val + 1.0f) / 2.0f;
- v = val * ((1 << format->bits[c]) - 1) + 0.5f; + v = d3dx_clamp(val, 0.0f, 1.0f) * ((1 << format->bits[c]) - 1) + 0.5f; }
for (i = format->shift[c] / 8 * 8; i < format->shift[c] + format->bits[c]; i += 8) diff --git a/dlls/d3dx9_36/tests/surface.c b/dlls/d3dx9_36/tests/surface.c index aa3db4fde74..1c77fcc4b58 100644 --- a/dlls/d3dx9_36/tests/surface.c +++ b/dlls/d3dx9_36/tests/surface.c @@ -1673,18 +1673,16 @@ static void test_D3DXLoadSurface(IDirect3DDevice9 *device) /* From a signed normalized format to an unsigned normalized format. */ hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_q8w8v8u8, D3DFMT_Q8W8V8U8, 8, NULL, &rect, D3DX_FILTER_NONE, 0); - todo_wine ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) - { - hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); - ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); - check_pixel_4bpp(&lockrect, 0, 0, 0xb08090a0); - check_pixel_4bpp(&lockrect, 1, 0, 0xffc0d0e0); - check_pixel_4bpp(&lockrect, 0, 1, 0x2f00001f); - check_pixel_4bpp(&lockrect, 1, 1, 0x7e3f4f5f); - hr = IDirect3DSurface9_UnlockRect(surf); - ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr); - } + ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + + hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); + check_pixel_4bpp(&lockrect, 0, 0, 0xb08090a0); + check_pixel_4bpp(&lockrect, 1, 0, 0xffc0d0e0); + check_pixel_4bpp(&lockrect, 0, 1, 0x2f00001f); + check_pixel_4bpp(&lockrect, 1, 1, 0x7e3f4f5f); + hr = IDirect3DSurface9_UnlockRect(surf); + ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr);
hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_a32b32g32r32f, D3DFMT_A32B32G32R32F, 32, NULL, &rect, D3DX_FILTER_NONE, 0); @@ -1925,18 +1923,16 @@ static void test_D3DXLoadSurface(IDirect3DDevice9 *device)
hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_q8w8v8u8, D3DFMT_Q8W8V8U8, 8, NULL, &rect, D3DX_FILTER_NONE, 0); - todo_wine ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) - { - hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); - ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); - check_pixel_2bpp(&lockrect, 0, 0, 0xb08d); - check_pixel_2bpp(&lockrect, 1, 0, 0xffce); - check_pixel_2bpp(&lockrect, 0, 1, 0x2f02); - check_pixel_2bpp(&lockrect, 1, 1, 0x7e4d); - hr = IDirect3DSurface9_UnlockRect(surf); - ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr); - } + ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + + hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); + check_pixel_2bpp(&lockrect, 0, 0, 0xb08d); + check_pixel_2bpp(&lockrect, 1, 0, 0xffce); + check_pixel_2bpp(&lockrect, 0, 1, 0x2f02); + check_pixel_2bpp(&lockrect, 1, 1, 0x7e4d); + hr = IDirect3DSurface9_UnlockRect(surf); + ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr);
hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_a32b32g32r32f, D3DFMT_A32B32G32R32F, 32, NULL, &rect, D3DX_FILTER_NONE, 0); @@ -2201,14 +2197,14 @@ static void test_D3DXLoadSurface(IDirect3DDevice9 *device) /* Signed normalized value to full range float. */ hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_q8w8v8u8, D3DFMT_Q8W8V8U8, 8, NULL, &rect, D3DX_FILTER_NONE, 0); - todo_wine ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr);
hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); - check_pixel_float4(&lockrect, 0, 0, 0.0f, 0.125984f, 0.251969f, 0.377953f, TRUE); - check_pixel_float4(&lockrect, 1, 0, 0.503937f, 0.629921f, 0.755906f, 1.0f, TRUE); - check_pixel_float4(&lockrect, 0, 1, -1.0f, -1.0f, -0.755906f, -0.629921f, TRUE); - check_pixel_float4(&lockrect, 1, 1, -0.503937f, -0.377953f, -0.251969f, -0.007874f, TRUE); + check_pixel_float4(&lockrect, 0, 0, 0.0f, 0.125984f, 0.251969f, 0.377953f, FALSE); + check_pixel_float4(&lockrect, 1, 0, 0.503937f, 0.629921f, 0.755906f, 1.0f, FALSE); + check_pixel_float4(&lockrect, 0, 1, -1.0f, -1.0f, -0.755906f, -0.629921f, FALSE); + check_pixel_float4(&lockrect, 1, 1, -0.503937f, -0.377953f, -0.251969f, -0.007874f, FALSE); hr = IDirect3DSurface9_UnlockRect(surf); ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr);
@@ -2286,53 +2282,53 @@ static void test_D3DXLoadSurface(IDirect3DDevice9 *device) SetRect(&rect, 0, 0, 2, 2); hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_q8w8v8u8, D3DFMT_Q8W8V8U8, 8, NULL, &rect, D3DX_FILTER_NONE, 0); - todo_wine ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); - todo_wine check_pixel_4bpp(&lockrect, 0, 0, pixdata_q8w8v8u8[0]); - todo_wine check_pixel_4bpp(&lockrect, 1, 0, pixdata_q8w8v8u8[1]); - todo_wine check_pixel_4bpp(&lockrect, 0, 1, pixdata_q8w8v8u8[2]); - todo_wine check_pixel_4bpp(&lockrect, 1, 1, pixdata_q8w8v8u8[3]); + check_pixel_4bpp(&lockrect, 0, 0, pixdata_q8w8v8u8[0]); + check_pixel_4bpp(&lockrect, 1, 0, pixdata_q8w8v8u8[1]); + check_pixel_4bpp(&lockrect, 0, 1, pixdata_q8w8v8u8[2]); + check_pixel_4bpp(&lockrect, 1, 1, pixdata_q8w8v8u8[3]); hr = IDirect3DSurface9_UnlockRect(surf); ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr);
/* Unorm to snorm. */ hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_a8b8g8r8, D3DFMT_A8B8G8R8, 8, NULL, &rect, D3DX_FILTER_NONE, 0); - todo_wine ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr);
hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); - todo_wine check_pixel_4bpp(&lockrect, 0, 0, 0x43bbce70); - todo_wine check_pixel_4bpp(&lockrect, 1, 0, 0xa5dc6812); - todo_wine check_pixel_4bpp(&lockrect, 0, 1, 0x8b31177d); - todo_wine check_pixel_4bpp(&lockrect, 1, 1, 0x0d43ad76); + check_pixel_4bpp(&lockrect, 0, 0, 0x43bbce70); + check_pixel_4bpp(&lockrect, 1, 0, 0xa5dc6812); + check_pixel_4bpp(&lockrect, 0, 1, 0x8b31177d); + check_pixel_4bpp(&lockrect, 1, 1, 0x0d43ad76); hr = IDirect3DSurface9_UnlockRect(surf); ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr);
/* Full range float to snorm. */ hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_a32b32g32r32f, D3DFMT_A32B32G32R32F, 32, NULL, &rect, D3DX_FILTER_NONE, 0); - todo_wine ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr);
hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); - todo_wine check_pixel_4bpp(&lockrect, 0, 0, 0x7f7f0d00); - todo_wine check_pixel_4bpp(&lockrect, 1, 0, 0x7f7f7f7f); - todo_wine check_pixel_4bpp(&lockrect, 0, 1, 0x827fe8f4); - todo_wine check_pixel_4bpp(&lockrect, 1, 1, 0x82828282); + check_pixel_4bpp(&lockrect, 0, 0, 0x7f7f0d00); + check_pixel_4bpp(&lockrect, 1, 0, 0x7f7f7f7f); + check_pixel_4bpp(&lockrect, 0, 1, 0x827fe8f4); + check_pixel_4bpp(&lockrect, 1, 1, 0x82828282); hr = IDirect3DSurface9_UnlockRect(surf); ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr);
/* Unorm alpha and unorm luma to snorm. */ hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_a8l8, D3DFMT_A8L8, 4, NULL, &rect, D3DX_FILTER_NONE, 0); - todo_wine ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr);
hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); - todo_wine check_pixel_4bpp(&lockrect, 0, 0, 0x7f828282); - todo_wine check_pixel_4bpp(&lockrect, 1, 0, 0x827f7f7f); - todo_wine check_pixel_4bpp(&lockrect, 0, 1, 0x7fb2b2b2); + check_pixel_4bpp(&lockrect, 0, 0, 0x7f828282); + check_pixel_4bpp(&lockrect, 1, 0, 0x827f7f7f); + check_pixel_4bpp(&lockrect, 0, 1, 0x7fb2b2b2); check_pixel_4bpp(&lockrect, 1, 1, 0x00000000); hr = IDirect3DSurface9_UnlockRect(surf); ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr); diff --git a/dlls/d3dx9_36/util.c b/dlls/d3dx9_36/util.c index 419bd92884a..4519fd02f17 100644 --- a/dlls/d3dx9_36/util.c +++ b/dlls/d3dx9_36/util.c @@ -88,6 +88,7 @@ static const struct pixel_format_desc formats[] = {D3DFMT_G32R32F, { 0, 32, 32, 0}, { 0, 0, 32, 0}, 8, 1, 1, 8, FORMAT_ARGBF, NULL, NULL }, {D3DFMT_A32B32G32R32F, {32, 32, 32, 32}, {96, 0, 32, 64}, 16, 1, 1, 16, FORMAT_ARGBF, NULL, NULL }, {D3DFMT_P8, { 8, 8, 8, 8}, { 0, 0, 0, 0}, 1, 1, 1, 1, FORMAT_INDEX, NULL, index_to_rgba}, + {D3DFMT_Q8W8V8U8, { 8, 8, 8, 8}, {24, 0, 8, 16}, 4, 1, 1, 4, FORMAT_ARGB_SNORM, NULL, NULL }, /* marks last element */ {D3DFMT_UNKNOWN, { 0, 0, 0, 0}, { 0, 0, 0, 0}, 0, 1, 1, 0, FORMAT_UNKNOWN, NULL, NULL }, };
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/d3dx9_36/tests/surface.c | 35 ++++++++++++++++++++--------------- dlls/d3dx9_36/util.c | 1 + 2 files changed, 21 insertions(+), 15 deletions(-)
diff --git a/dlls/d3dx9_36/tests/surface.c b/dlls/d3dx9_36/tests/surface.c index 1c77fcc4b58..3ffa74a57a1 100644 --- a/dlls/d3dx9_36/tests/surface.c +++ b/dlls/d3dx9_36/tests/surface.c @@ -734,7 +734,7 @@ static void test_D3DXGetImageInfo(void) check_dds_pixel_format(DDS_PF_LUMINANCE, 0, 16, 0xffff, 0, 0, 0, D3DFMT_L16); check_dds_pixel_format(DDS_PF_LUMINANCE | DDS_PF_ALPHA, 0, 16, 0x00ff, 0, 0, 0xff00, D3DFMT_A8L8); check_dds_pixel_format(DDS_PF_LUMINANCE | DDS_PF_ALPHA, 0, 8, 0x0f, 0, 0, 0xf0, D3DFMT_A4L4); - todo_wine check_dds_pixel_format(DDS_PF_BUMPDUDV, 0, 16, 0x00ff, 0xff00, 0, 0, D3DFMT_V8U8); + check_dds_pixel_format(DDS_PF_BUMPDUDV, 0, 16, 0x00ff, 0xff00, 0, 0, D3DFMT_V8U8); todo_wine check_dds_pixel_format(DDS_PF_BUMPDUDV, 0, 32, 0x0000ffff, 0xffff0000, 0, 0, D3DFMT_V16U16); todo_wine check_dds_pixel_format(DDS_PF_BUMPLUMINANCE, 0, 32, 0x0000ff, 0x00ff00, 0xff0000, 0, D3DFMT_X8L8V8U8);
@@ -1005,7 +1005,7 @@ static void test_format_conversion(IDirect3DDevice9 *device) { D3DFMT_A16B16G16R16, NULL, { 0, 0, 2, 2 }, a16b16g16r16_2_2, D3DFMT_A32B32G32R32F, a16b16g16r16_2_2_expected }, { D3DFMT_V16U16, NULL, { 0, 0, 2, 2 }, v16u16_2_2, D3DFMT_G16R16, v16u16_2_2_expected, .todo = TRUE }, { D3DFMT_V16U16, NULL, { 0, 0, 2, 2 }, v16u16_2_2, D3DFMT_A32B32G32R32F, v16u16_2_2_expected2, .todo = TRUE }, - { D3DFMT_V8U8, NULL, { 0, 0, 2, 2 }, v8u8_2_2, D3DFMT_A32B32G32R32F, v8u8_2_2_expected, .todo = TRUE }, + { D3DFMT_V8U8, NULL, { 0, 0, 2, 2 }, v8u8_2_2, D3DFMT_A32B32G32R32F, v8u8_2_2_expected }, { D3DFMT_Q16W16V16U16, NULL, { 0, 0, 2, 2 }, q16w16v16u16_2_2, D3DFMT_A32B32G32R32F, q16w16v16u16_2_2_expected, .todo = TRUE }, { D3DFMT_A8P8, test_palette, { 0, 0, 2, 2 }, a8p8_2_2, D3DFMT_A8R8G8B8, a8p8_2_2_expected, .todo = TRUE }, }; @@ -2225,14 +2225,14 @@ static void test_D3DXLoadSurface(IDirect3DDevice9 *device) /* V8U8 snorm. */ hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_v8u8, D3DFMT_V8U8, 4, NULL, &rect, D3DX_FILTER_NONE, 0); - todo_wine ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr); + ok(hr == D3D_OK, "Got unexpected hr %#lx.\n", hr);
hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); - check_pixel_float4(&lockrect, 0, 0, 0.0f, 0.377953f, 1.0f, 1.0f, TRUE); - check_pixel_float4(&lockrect, 1, 0, 0.503937f, 1.0f, 1.0f, 1.0f, TRUE); - check_pixel_float4(&lockrect, 0, 1, -1.0f, -1.0f, 1.0f, 1.0f, TRUE); - check_pixel_float4(&lockrect, 1, 1, -0.503937f, -0.007874f, 1.0f, 1.0f, TRUE); + check_pixel_float4(&lockrect, 0, 0, 0.0f, 0.377953f, 1.0f, 1.0f, FALSE); + check_pixel_float4(&lockrect, 1, 0, 0.503937f, 1.0f, 1.0f, 1.0f, FALSE); + check_pixel_float4(&lockrect, 0, 1, -1.0f, -1.0f, 1.0f, 1.0f, FALSE); + check_pixel_float4(&lockrect, 1, 1, -0.503937f, -0.007874f, 1.0f, 1.0f, FALSE); hr = IDirect3DSurface9_UnlockRect(surf); ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr);
@@ -2394,14 +2394,14 @@ static void test_D3DXLoadSurface(IDirect3DDevice9 *device) SetRect(&rect, 0, 0, 2, 2); hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_a8b8g8r8_2, D3DFMT_A8B8G8R8, 8, NULL, &rect, D3DX_FILTER_NONE, 0); - todo_wine ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr);
hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); - todo_wine check_pixel_2bpp(&lockrect, 0, 0, 0x9282); - todo_wine check_pixel_2bpp(&lockrect, 1, 0, 0xd2c2); - todo_wine check_pixel_2bpp(&lockrect, 0, 1, 0x1000); - todo_wine check_pixel_2bpp(&lockrect, 1, 1, 0x5040); + check_pixel_2bpp(&lockrect, 0, 0, 0x9282); + check_pixel_2bpp(&lockrect, 1, 0, 0xd2c2); + check_pixel_2bpp(&lockrect, 0, 1, 0x1000); + check_pixel_2bpp(&lockrect, 1, 1, 0x5040); hr = IDirect3DSurface9_UnlockRect(surf); ok(hr == D3D_OK, "Failed to unlock surface, hr %#lx.\n", hr);
@@ -2412,12 +2412,17 @@ static void test_D3DXLoadSurface(IDirect3DDevice9 *device) */ hr = D3DXLoadSurfaceFromMemory(surf, NULL, NULL, pixdata_q8w8v8u8, D3DFMT_Q8W8V8U8, 8, NULL, &rect, D3DX_FILTER_NONE, 0); - todo_wine ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == D3D_OK, "Unexpected hr %#lx.\n", hr);
hr = IDirect3DSurface9_LockRect(surf, &lockrect, NULL, D3DLOCK_READONLY); ok(hr == D3D_OK, "Failed to lock surface, hr %#lx.\n", hr); - todo_wine check_pixel_2bpp(&lockrect, 0, 0, 0x1000); - todo_wine check_pixel_2bpp(&lockrect, 1, 0, 0x5040); + check_pixel_2bpp(&lockrect, 0, 0, 0x1000); + check_pixel_2bpp(&lockrect, 1, 0, 0x5040); + /* + * Wine does direct copies from the source pixels here, but if we + * force conversion to float on the source and then convert back to + * snorm we'd match these values. + */ todo_wine check_pixel_2bpp(&lockrect, 0, 1, 0x8282); todo_wine check_pixel_2bpp(&lockrect, 1, 1, 0xd1c1); hr = IDirect3DSurface9_UnlockRect(surf); diff --git a/dlls/d3dx9_36/util.c b/dlls/d3dx9_36/util.c index 4519fd02f17..a73467f5e68 100644 --- a/dlls/d3dx9_36/util.c +++ b/dlls/d3dx9_36/util.c @@ -89,6 +89,7 @@ static const struct pixel_format_desc formats[] = {D3DFMT_A32B32G32R32F, {32, 32, 32, 32}, {96, 0, 32, 64}, 16, 1, 1, 16, FORMAT_ARGBF, NULL, NULL }, {D3DFMT_P8, { 8, 8, 8, 8}, { 0, 0, 0, 0}, 1, 1, 1, 1, FORMAT_INDEX, NULL, index_to_rgba}, {D3DFMT_Q8W8V8U8, { 8, 8, 8, 8}, {24, 0, 8, 16}, 4, 1, 1, 4, FORMAT_ARGB_SNORM, NULL, NULL }, + {D3DFMT_V8U8, { 0, 8, 8, 0}, { 0, 0, 8, 0}, 2, 1, 1, 2, FORMAT_ARGB_SNORM, NULL, NULL }, /* marks last element */ {D3DFMT_UNKNOWN, { 0, 0, 0, 0}, { 0, 0, 0, 0}, 0, 1, 1, 0, FORMAT_UNKNOWN, NULL, NULL }, };
From: Connor McAdams cmcadams@codeweavers.com
These functions do the exact same thing, so there's no need to have two separate functions.
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/d3dx9_36/d3dx9_private.h | 7 ++++ dlls/d3dx9_36/surface.c | 2 +- dlls/d3dx9_36/texture.c | 75 +++++++---------------------------- 3 files changed, 23 insertions(+), 61 deletions(-)
diff --git a/dlls/d3dx9_36/d3dx9_private.h b/dlls/d3dx9_36/d3dx9_private.h index 2338909f7cc..e46cd7389b2 100644 --- a/dlls/d3dx9_36/d3dx9_private.h +++ b/dlls/d3dx9_36/d3dx9_private.h @@ -67,6 +67,12 @@ struct d3dx_color enum range range; };
+static inline void set_d3dx_color(struct d3dx_color *color, const struct vec4 *value, enum range range) +{ + color->value = *value; + color->range = range; +} + struct volume { UINT width; @@ -192,6 +198,7 @@ const struct pixel_format_desc *get_format_info(D3DFORMAT format); const struct pixel_format_desc *get_format_info_idx(int idx);
void format_to_d3dx_color(const struct pixel_format_desc *format, const BYTE *src, struct d3dx_color *dst); +void format_from_d3dx_color(const struct pixel_format_desc *format, const struct d3dx_color *src, BYTE *dst);
void copy_pixels(const BYTE *src, UINT src_row_pitch, UINT src_slice_pitch, BYTE *dst, UINT dst_row_pitch, UINT dst_slice_pitch, const struct volume *size, diff --git a/dlls/d3dx9_36/surface.c b/dlls/d3dx9_36/surface.c index da67c3f061a..f6997360379 100644 --- a/dlls/d3dx9_36/surface.c +++ b/dlls/d3dx9_36/surface.c @@ -1576,7 +1576,7 @@ void format_to_d3dx_color(const struct pixel_format_desc *format, const BYTE *sr }
/* It doesn't work for components bigger than 32 bits. */ -static void format_from_d3dx_color(const struct pixel_format_desc *format, const struct d3dx_color *src, BYTE *dst) +void format_from_d3dx_color(const struct pixel_format_desc *format, const struct d3dx_color *src, BYTE *dst) { DWORD v, mask32; unsigned int c, i; diff --git a/dlls/d3dx9_36/texture.c b/dlls/d3dx9_36/texture.c index 2820857230d..785f021df99 100644 --- a/dlls/d3dx9_36/texture.c +++ b/dlls/d3dx9_36/texture.c @@ -1261,62 +1261,6 @@ err: return hr; }
-static inline void fill_texture(const struct pixel_format_desc *format, BYTE *pos, const D3DXVECTOR4 *value) -{ - DWORD c; - - for (c = 0; c < format->bytes_per_pixel; c++) - pos[c] = 0; - - for (c = 0; c < 4; c++) - { - float comp_value; - DWORD i, v = 0, mask32 = format->bits[c] == 32 ? ~0U : ((1 << format->bits[c]) - 1); - - switch (c) - { - case 0: /* Alpha */ - comp_value = value->w; - break; - case 1: /* Red */ - comp_value = value->x; - break; - case 2: /* Green */ - comp_value = value->y; - break; - case 3: /* Blue */ - comp_value = value->z; - break; - } - - if (format->type == FORMAT_ARGBF16) - v = float_32_to_16(comp_value); - else if (format->type == FORMAT_ARGBF) - v = *(DWORD *)&comp_value; - else if (format->type == FORMAT_ARGB) - v = max(comp_value * ((1 << format->bits[c]) - 1) + 0.5f, 0); - else - FIXME("Unhandled format type %#x\n", format->type); - - for (i = 0; i < format->bits[c] + format->shift[c]; i += 8) - { - BYTE byte, mask; - - if (format->shift[c] > i) - { - mask = mask32 << (format->shift[c] - i); - byte = (v << (format->shift[c] - i)) & mask; - } - else - { - mask = mask32 >> (i - format->shift[c]); - byte = (v >> (i - format->shift[c])) & mask; - } - pos[i / 8] |= byte; - } - } -} - HRESULT WINAPI D3DXFillTexture(struct IDirect3DTexture9 *texture, LPD3DXFILL2D function, void *funcdata) { IDirect3DSurface9 *surface, *temp_surface; @@ -1370,11 +1314,15 @@ HRESULT WINAPI D3DXFillTexture(struct IDirect3DTexture9 *texture, LPD3DXFILL2D f
for (x = 0; x < desc.Width; x++) { + BYTE *dst = data + y * lock_rect.Pitch + x * format->bytes_per_pixel; + struct d3dx_color color; + coord.x = (x + 0.5f) / desc.Width;
function(&value, &coord, &size, funcdata);
- fill_texture(format, data + y * lock_rect.Pitch + x * format->bytes_per_pixel, &value); + set_d3dx_color(&color, (const struct vec4 *)&value, RANGE_FULL); + format_from_d3dx_color(format, (const struct d3dx_color *)&color, dst); } } if (FAILED(hr = unlock_surface(surface, NULL, temp_surface, TRUE))) @@ -1757,13 +1705,17 @@ HRESULT WINAPI D3DXFillCubeTexture(struct IDirect3DCubeTexture9 *texture, LPD3DX { for (x = 0; x < desc.Width; x++) { + BYTE *dst = data + y * lock_rect.Pitch + x * format->bytes_per_pixel; + struct d3dx_color color; + coord.x = get_cube_coord(coordmap[f][0], x, y, desc.Width) / desc.Width * 2.0f - 1.0f; coord.y = get_cube_coord(coordmap[f][1], x, y, desc.Width) / desc.Width * 2.0f - 1.0f; coord.z = get_cube_coord(coordmap[f][2], x, y, desc.Width) / desc.Width * 2.0f - 1.0f;
function(&value, &coord, &size, funcdata);
- fill_texture(format, data + y * lock_rect.Pitch + x * format->bytes_per_pixel, &value); + set_d3dx_color(&color, (const struct vec4 *)&value, RANGE_FULL); + format_from_d3dx_color(format, (const struct d3dx_color *)&color, dst); } } IDirect3DCubeTexture9_UnlockRect(texture, f, m); @@ -1824,12 +1776,15 @@ HRESULT WINAPI D3DXFillVolumeTexture(struct IDirect3DVolumeTexture9 *texture, LP
for (x = 0; x < desc.Width; x++) { + BYTE *dst = data + z * lock_box.SlicePitch + y * lock_box.RowPitch + x * format->bytes_per_pixel; + struct d3dx_color color; + coord.x = (x + 0.5f) / desc.Width;
function(&value, &coord, &size, funcdata);
- fill_texture(format, data + z * lock_box.SlicePitch + y * lock_box.RowPitch - + x * format->bytes_per_pixel, &value); + set_d3dx_color(&color, (const struct vec4 *)&value, RANGE_FULL); + format_from_d3dx_color(format, (const struct d3dx_color *)&color, dst); } } }
On Thu Sep 5 15:11:35 2024 +0000, Matteo Bruni wrote:
It would be preferable to check for relative error, like e.g. in d3dx9_36/tests/{effect.c,math.c} or d3d9/tests/visual.c, if that works out alright.
Initially I was using relative error in testing, but at some point I had issues with it. Reintroduced it and there don't seem to be anymore issues, so this should be fixed now. :)
On Fri Aug 30 21:46:48 2024 +0000, Matteo Bruni wrote:
Just a very mild suggestion that you can take or not at your pleasure. Maybe add an empty line every 4, splitting entries in groups of 16?
I _think_ I've changed it to be the way you've described here, but let me know if I did it right. :)
On Thu Sep 5 15:10:35 2024 +0000, Connor McAdams wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/6360/diffs?diff_id=130414&start_sha=44a4ff39bc5feb1acbdbb42ba75c7f7901bb4f15#d49071753fdf2ab21d518dbd8c720b48c7c732fc_1195_1252)
Added tests for these values in the most recent version.
On Thu Sep 5 15:10:35 2024 +0000, Connor McAdams wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/6360/diffs?diff_id=130414&start_sha=44a4ff39bc5feb1acbdbb42ba75c7f7901bb4f15#d49071753fdf2ab21d518dbd8c720b48c7c732fc_2350_2408)
Good point, I hadn't really thought too much about the behavior here, but the nudging of those values is another indication that native is likely converting to float internally, and then back to SNORM range. It's odd though, because you would think it would make more sense to just copy these values since they're of the same range and there's no mixing of channels involved.
On Fri Aug 30 21:46:51 2024 +0000, Matteo Bruni wrote:
Another one of those nitpicks, sorry... "Demultiplied" is a bit of an awkward word (admittedly undoing the premultiplied alpha is an awkward operation in the first place, nothing we can do about that though). I don't have any great suggestion, maybe just spelling out the whole "reversing the premultiplication" in some way is fine (while pushing the naming issue to whenever it's time to implement it :sweat_smile:). I guess the comment could mention explicitly that we're uploading the premultiplied alpha data into an uncompressed format surface.
I also had some trouble coming up with a name for this operation, I got the term "demultiply" from looking at the DirectXTex source, particularly [here.](https://github.com/microsoft/DirectXTex/blob/7aae82706d5ce6cf3c72ecb343676bd...) It's probably the best word for the operation I've seen so far, but I've changed this comment to be more explicit.
On Thu Sep 5 15:10:36 2024 +0000, Connor McAdams wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/6360/diffs?diff_id=130414&start_sha=44a4ff39bc5feb1acbdbb42ba75c7f7901bb4f15#d49071753fdf2ab21d518dbd8c720b48c7c732fc_1150_1200)
Should be fixed now.
On Fri Aug 30 21:46:53 2024 +0000, Matteo Bruni wrote:
This works, but I think it would be better if we stored the "range" (e.g. UNORM, SNORM, full range) as a property of the value itself (or, specifically, of each component) instead of having to bring around the source type as separate, additional data. So perhaps something like
struct d3dx_color { vec4 value; enum range range[4]; };
which `format_to_vec4()` generates and `format_from_vec4()` consumes. I haven't really thought it through, so this might be all kinds of ugly or impractical.
So, this one has taken me awhile to work through, because I used to have a similar system to this (although I called it `struct d3dx_pixel`). The thing I realized after getting further into implementation is that for formats with mixed ranges (i.e `D3DFMT_X8L8V8U8`, `D3DFMT_A8P8`) the ranges are only split between the alpha channels and the color channels. It's easier to categorize this way IMO. I've added a structure for this in the most recent revision, but kept it as a single range value. Once we add support for formats with split ranges, I'll add a separate range value to the structure for the alpha channel.
I've reworked the patch order a bit to take into account the change from `format_{to,from}_vec4()` to `format_{to,from}_d3dx_color()`, and to split that change off separately. Hopefully that doesn't make things harder to review :)
On Mon Sep 16 15:24:10 2024 +0000, Connor McAdams wrote:
Initially I was using relative error in testing, but at some point I had issues with it. Reintroduced it and there don't seem to be anymore issues, so this should be fixed now. :)
I'm afraid I wasn't clear enough here :/
I meant to specifically suggest using the float comparison functions we have in newer tests, like:
`static BOOL compare_float(float f, float g, unsigned int ulps)`
e.g. from d3dx9_36/tests/math.c. In particular I find ULPs to be a great way of quantifying comparison error.
Relatedly, we want to use `%.8e` for `float` formatting, because that doesn't lose precision when printing values.
It looks like, at least for now, we match native results to 1 ULP (on 32-bit, which is the one I happened to test). We need to specify the expected results with greater precision than we currently do, but it ensures that there are no surprises in the implementation.
On Mon Sep 16 15:24:10 2024 +0000, Matteo Bruni wrote:
Interesting.
I missed it the first time around but technically v8/u8 here aren't copied either. Definitely not a big deal :slight_smile:
On Thu Sep 5 15:15:54 2024 +0000, Connor McAdams wrote:
Good point, I hadn't really thought too much about the behavior here, but the nudging of those values is another indication that native is likely converting to float internally, and then back to SNORM range. It's odd though, because you would think it would make more sense to just copy these values since they're of the same range and there's no mixing of channels involved.
My guess is that they always convert out of SNORM right away. Which we could do as well if we find some application that depends on this quirk at some point. This *should* be fine for the time being...
On Mon Sep 16 15:24:10 2024 +0000, Connor McAdams wrote:
So, this one has taken me awhile to work through, because I used to have a similar system to this (although I called it `struct d3dx_pixel`). The thing I realized after getting further into implementation is that for formats with mixed ranges (i.e `D3DFMT_X8L8V8U8`, `D3DFMT_A8P8`) the ranges are only split between the alpha channels and the color channels. It's easier to categorize this way IMO. I've added a structure for this in the most recent revision, but kept it as a single range value. Once we add support for formats with split ranges, I'll add a separate range value to the structure for the alpha channel.
Yeah, that should be fine.
Storing and passing around the range together with the value seems nicer to me overall. I assume you didn't find any fundamental problem with this approach in your earlier version of the code :sweat_smile:
Matteo Bruni (@Mystral) commented about dlls/d3dx9_36/surface.c:
}
}
+static float d3dx_clamp(float value, float min_value, float max_value) +{
- if (isinf(value))
return !signbit(value) ? max_value : min_value;
I think this is unnecessary, `Inf` should be already handled correctly by the "normal" value condition.
Matteo Bruni (@Mystral) commented about dlls/d3dx9_36/surface.c:
dst->range = RANGE_FULL;
break;
- case FORMAT_ARGB:
- case FORMAT_INDEX:
dst->range = RANGE_UNORM;
break;
- default: /* Shouldn't pass FORMAT_DXT/FORMAT_UNKNOWN into here. */
assert(0);
break;
- }
- for (c = 0; c < 4; ++c) { static const unsigned int component_offsets[4] = {3, 0, 1, 2};
float *dst_component = (float *)dst + component_offsets[c];
float *dst_component = ((float *)&dst->value) + component_offsets[c];
```suggestion:-0+0 float *dst_component = &dst->value.x + component_offsets[c]; ```
Matteo Bruni (@Mystral) commented about dlls/d3dx9_36/surface.c:
*dst_component = float_16_to_32(tmp); else if (format->type == FORMAT_ARGBF) *dst_component = *(float *)&tmp;
else if (format->type == FORMAT_ARGB_SNORM)
{
const uint32_t sign_bit = (1 << (format->bits[c] - 1));
```suggestion:-0+0 const uint32_t sign_bit = (1u << (format->bits[c] - 1)); ``` Similarly for the other `1`s used as first operand of a shift expression.
Matteo Bruni (@Mystral) commented about dlls/d3dx9_36/texture.c:
for (x = 0; x < desc.Width; x++) {
BYTE *dst = data + y * lock_rect.Pitch + x * format->bytes_per_pixel;
struct d3dx_color color;
coord.x = (x + 0.5f) / desc.Width; function(&value, &coord, &size, funcdata);
fill_texture(format, data + y * lock_rect.Pitch + x * format->bytes_per_pixel, &value);
set_d3dx_color(&color, (const struct vec4 *)&value, RANGE_FULL);
format_from_d3dx_color(format, (const struct d3dx_color *)&color, dst);
I'm pretty sure that cast isn't necessary.
Matteo Bruni (@Mystral) commented about dlls/d3dx9_36/texture.c:
for (x = 0; x < desc.Width; x++) {
BYTE *dst = data + y * lock_rect.Pitch + x * format->bytes_per_pixel;
struct d3dx_color color;
coord.x = (x + 0.5f) / desc.Width; function(&value, &coord, &size, funcdata);
fill_texture(format, data + y * lock_rect.Pitch + x * format->bytes_per_pixel, &value);
set_d3dx_color(&color, (const struct vec4 *)&value, RANGE_FULL);
An alternative would be to make `value` a union of `D3DXVECTOR4` and `struct vec4`. It doesn't matter much.
Sorry it took me a while to get through this review :/
BTW, I found some suspicious (aka broken) existing code dealing with color keys. I'll probably send a small MR after this one goes in.