From bd742ca3a55102b916f510b9c3414a621888a9df Mon Sep 17 00:00:00 2001
From: Henri Verbeet <hverbeet@codeweavers.com>
Date: Wed, 4 Jan 2012 21:01:53 +0100
Subject: [PATCH 1/1] ddraw/tests: Add some clipper tests.

---
 dlls/ddraw/tests/ddraw1.c |  217 +++++++++++++++++++++++++++++++++++++++++++++
 dlls/ddraw/tests/ddraw2.c |  217 +++++++++++++++++++++++++++++++++++++++++++++
 dlls/ddraw/tests/ddraw4.c |  216 ++++++++++++++++++++++++++++++++++++++++++++
 dlls/ddraw/tests/ddraw7.c |  216 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 866 insertions(+), 0 deletions(-)

diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c
index 54a1569..e419e62 100644
--- a/dlls/ddraw/tests/ddraw1.c
+++ b/dlls/ddraw/tests/ddraw1.c
@@ -19,6 +19,41 @@
 #include "wine/test.h"
 #include "d3d.h"
 
+static BOOL compare_color(D3DCOLOR c1, D3DCOLOR c2, BYTE max_diff)
+{
+    if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE;
+    c1 >>= 8; c2 >>= 8;
+    if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE;
+    c1 >>= 8; c2 >>= 8;
+    if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE;
+    c1 >>= 8; c2 >>= 8;
+    if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE;
+    return TRUE;
+}
+
+static D3DCOLOR get_surface_color(IDirectDrawSurface *surface, UINT x, UINT y)
+{
+    RECT rect = {x, y, x + 1, y + 1};
+    DDSURFACEDESC surface_desc;
+    D3DCOLOR color;
+    HRESULT hr;
+
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+
+    hr = IDirectDrawSurface_Lock(surface, &rect, &surface_desc, DDLOCK_READONLY | DDLOCK_WAIT, NULL);
+    ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr);
+    if (FAILED(hr))
+        return 0xdeadbeef;
+
+    color = *((DWORD *)surface_desc.lpSurface) & 0x00ffffff;
+
+    hr = IDirectDrawSurface_Unlock(surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr);
+
+    return color;
+}
+
 static IDirectDraw *create_ddraw(void)
 {
     IDirectDraw *ddraw;
@@ -124,7 +159,189 @@ static void test_coop_level_create_device_window(void)
     DestroyWindow(focus_window);
 }
 
+static void test_clipper_blt(void)
+{
+    IDirectDrawSurface *src_surface, *dst_surface;
+    RECT client_rect, src_rect, *rect;
+    IDirectDrawClipper *clipper;
+    DDSURFACEDESC surface_desc;
+    unsigned int i, j, x, y;
+    IDirectDraw *ddraw;
+    RGNDATA *rgn_data;
+    D3DCOLOR color;
+    HRGN r1, r2;
+    HWND window;
+    DDBLTFX fx;
+    HRESULT hr;
+    DWORD ret;
+
+    static const D3DCOLOR expected1[] =
+    {
+        0x000000ff, 0x0000ff00, 0x00000000, 0x00000000,
+        0x000000ff, 0x0000ff00, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00ff0000, 0x00ffffff,
+        0x00000000, 0x00000000, 0x00ff0000, 0x00ffffff,
+    };
+    static const D3DCOLOR expected2[] =
+    {
+        0x000000ff, 0x000000ff, 0x00000000, 0x00000000,
+        0x000000ff, 0x000000ff, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x000000ff, 0x000000ff,
+        0x00000000, 0x00000000, 0x000000ff, 0x000000ff,
+    };
+
+    window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW,
+            10, 10, 640, 480, 0, 0, 0, 0);
+    ShowWindow(window, SW_SHOW);
+    if (!(ddraw = create_ddraw()))
+    {
+        skip("Failed to create a ddraw object, skipping test.\n");
+        DestroyWindow(window);
+        return;
+    }
+
+    ret = GetClientRect(window, &client_rect);
+    ok(ret, "Failed to get client rect.\n");
+    ret = MapWindowPoints(window, NULL, (POINT *)&client_rect, 2);
+    ok(ret, "Failed to map client rect.\n");
+
+    hr = IDirectDraw_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL);
+    ok(SUCCEEDED(hr), "Failed to set cooperative level, hr %#x.\n", hr);
+
+    hr = IDirectDraw_CreateClipper(ddraw, 0, &clipper, NULL);
+    ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
+    hr = IDirectDrawClipper_SetHWnd(clipper, 0, window);
+    ok(SUCCEEDED(hr), "Failed to set clipper window, hr %#x.\n", hr);
+    hr = IDirectDrawClipper_GetClipList(clipper, NULL, NULL, &ret);
+    ok(SUCCEEDED(hr), "Failed to get clip list size, hr %#x.\n", hr);
+    rgn_data = HeapAlloc(GetProcessHeap(), 0, ret);
+    hr = IDirectDrawClipper_GetClipList(clipper, NULL, rgn_data, &ret);
+    ok(SUCCEEDED(hr), "Failed to get clip list, hr %#x.\n", hr);
+    ok(rgn_data->rdh.dwSize == sizeof(rgn_data->rdh), "Got unexpected structure size %#x.\n", rgn_data->rdh.dwSize);
+    ok(rgn_data->rdh.iType == RDH_RECTANGLES, "Got unexpected type %#x.\n", rgn_data->rdh.iType);
+    ok(rgn_data->rdh.nCount == 1, "Got unexpected count %u.\n", rgn_data->rdh.nCount);
+    ok(rgn_data->rdh.nRgnSize == 16 || broken(rgn_data->rdh.nRgnSize == 168 /* NT4 */),
+            "Got unexpected region size %u.\n", rgn_data->rdh.nRgnSize);
+    ok(EqualRect(&rgn_data->rdh.rcBound, &client_rect),
+            "Got unexpected bounding rect {%d, %d, %d, %d}, expected {%d, %d, %d, %d}.\n",
+            rgn_data->rdh.rcBound.left, rgn_data->rdh.rcBound.top,
+            rgn_data->rdh.rcBound.right, rgn_data->rdh.rcBound.bottom,
+            client_rect.left, client_rect.top, client_rect.right, client_rect.bottom);
+    rect = (RECT *)&rgn_data->Buffer[0];
+    ok(EqualRect(rect, &client_rect),
+            "Got unexpected clip rect {%d, %d, %d, %d}, expected {%d, %d, %d, %d}.\n",
+            rect->left, rect->top, rect->right, rect->bottom,
+            client_rect.left, client_rect.top, client_rect.right, client_rect.bottom);
+    HeapFree(GetProcessHeap(), 0, rgn_data);
+
+    r1 = CreateRectRgn(0, 0, 320, 240);
+    ok(!!r1, "Failed to create region.\n");
+    r2 = CreateRectRgn(320, 240, 640, 480);
+    ok(!!r2, "Failed to create region.\n");
+    CombineRgn(r1, r1, r2, RGN_OR);
+    ret = GetRegionData(r1, 0, NULL);
+    rgn_data = HeapAlloc(GetProcessHeap(), 0, ret);
+    ret = GetRegionData(r1, ret, rgn_data);
+    ok(!!ret, "Failed to get region data.\n");
+
+    DeleteObject(r2);
+    DeleteObject(r1);
+
+    hr = IDirectDrawClipper_SetClipList(clipper, rgn_data, 0);
+    todo_wine ok(hr == DDERR_CLIPPERISUSINGHWND, "Got unexpected hr %#x.\n", hr);
+    hr = IDirectDrawClipper_SetHWnd(clipper, 0, NULL);
+    ok(SUCCEEDED(hr), "Failed to set clipper window, hr %#x.\n", hr);
+    hr = IDirectDrawClipper_SetClipList(clipper, rgn_data, 0);
+    ok(SUCCEEDED(hr), "Failed to set clip list, hr %#x.\n", hr);
+
+    HeapFree(GetProcessHeap(), 0, rgn_data);
+
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+    surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
+    surface_desc.dwWidth = 640;
+    surface_desc.dwHeight = 480;
+    surface_desc.ddpfPixelFormat.dwSize = sizeof(surface_desc.ddpfPixelFormat);
+    surface_desc.ddpfPixelFormat.dwFlags = DDPF_RGB;
+    U1(surface_desc.ddpfPixelFormat).dwRGBBitCount = 32;
+    U2(surface_desc.ddpfPixelFormat).dwRBitMask = 0x00ff0000;
+    U3(surface_desc.ddpfPixelFormat).dwGBitMask = 0x0000ff00;
+    U4(surface_desc.ddpfPixelFormat).dwBBitMask = 0x000000ff;
+
+    hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &src_surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to create source surface, hr %#x.\n", hr);
+    hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &dst_surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to create destination surface, hr %#x.\n", hr);
+
+    memset(&fx, 0, sizeof(fx));
+    fx.dwSize = sizeof(fx);
+    hr = IDirectDrawSurface_Blt(src_surface, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx);
+    ok(SUCCEEDED(hr), "Failed to clear source surface, hr %#x.\n", hr);
+    hr = IDirectDrawSurface_Blt(dst_surface, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx);
+    ok(SUCCEEDED(hr), "Failed to clear destination surface, hr %#x.\n", hr);
+
+    hr = IDirectDrawSurface_Lock(src_surface, NULL, &surface_desc, 0, NULL);
+    ok(SUCCEEDED(hr), "Failed to lock source surface, hr %#x.\n", hr);
+    ((DWORD *)surface_desc.lpSurface)[0] = 0xff0000ff;
+    ((DWORD *)surface_desc.lpSurface)[1] = 0xff00ff00;
+    ((DWORD *)surface_desc.lpSurface)[2] = 0xffff0000;
+    ((DWORD *)surface_desc.lpSurface)[3] = 0xffffffff;
+    hr = IDirectDrawSurface_Unlock(src_surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to unlock source surface, hr %#x.\n", hr);
+
+    hr = IDirectDrawSurface_SetClipper(dst_surface, clipper);
+    ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
+
+    SetRect(&src_rect, 0, 0, 4, 1);
+    hr = IDirectDrawSurface_Blt(dst_surface, NULL, src_surface, &src_rect, DDBLT_WAIT, NULL);
+    todo_wine ok(SUCCEEDED(hr), "Failed to blit, hr %#x.\n", hr);
+    for (i = 0; i < 4; ++i)
+    {
+        for (j = 0; j < 4; ++j)
+        {
+            x = 80 * ((2 * j) + 1);
+            y = 60 * ((2 * i) + 1);
+            color = get_surface_color(dst_surface, x, y);
+            if ((i < 2 && j < 2) || (i >= 2 && j >= 2))
+                todo_wine ok(compare_color(color, expected1[i * 4 + j], 1),
+                        "Expected color 0x%08x at %u,%u, got 0x%08x.\n", expected1[i * 4 + j], x, y, color);
+            else
+                ok(compare_color(color, expected1[i * 4 + j], 1),
+                        "Expected color 0x%08x at %u,%u, got 0x%08x.\n", expected1[i * 4 + j], x, y, color);
+        }
+    }
+
+    U5(fx).dwFillColor = 0xff0000ff;
+    hr = IDirectDrawSurface_Blt(dst_surface, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx);
+    todo_wine ok(SUCCEEDED(hr), "Failed to clear destination surface, hr %#x.\n", hr);
+    for (i = 0; i < 4; ++i)
+    {
+        for (j = 0; j < 4; ++j)
+        {
+            x = 80 * ((2 * j) + 1);
+            y = 60 * ((2 * i) + 1);
+            color = get_surface_color(dst_surface, x, y);
+            if ((i < 2 && j < 2) || (i >= 2 && j >= 2))
+                todo_wine ok(compare_color(color, expected2[i * 4 + j], 1),
+                        "Expected color 0x%08x at %u,%u, got 0x%08x.\n", expected2[i * 4 + j], x, y, color);
+            else
+                ok(compare_color(color, expected2[i * 4 + j], 1),
+                        "Expected color 0x%08x at %u,%u, got 0x%08x.\n", expected2[i * 4 + j], x, y, color);
+        }
+    }
+
+    hr = IDirectDrawSurface_BltFast(dst_surface, 0, 0, src_surface, NULL, DDBLTFAST_WAIT);
+    todo_wine ok(hr == DDERR_BLTFASTCANTCLIP || broken(hr == E_NOTIMPL /* NT4 */), "Got unexpected hr %#x.\n", hr);
+
+    IDirectDrawSurface_Release(dst_surface);
+    IDirectDrawSurface_Release(src_surface);
+    IDirectDrawClipper_Release(clipper);
+    IDirectDraw_Release(ddraw);
+}
+
 START_TEST(ddraw1)
 {
     test_coop_level_create_device_window();
+    test_clipper_blt();
 }
diff --git a/dlls/ddraw/tests/ddraw2.c b/dlls/ddraw/tests/ddraw2.c
index 1af341e..5fb47c0 100644
--- a/dlls/ddraw/tests/ddraw2.c
+++ b/dlls/ddraw/tests/ddraw2.c
@@ -19,6 +19,41 @@
 #include "wine/test.h"
 #include "d3d.h"
 
+static BOOL compare_color(D3DCOLOR c1, D3DCOLOR c2, BYTE max_diff)
+{
+    if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE;
+    c1 >>= 8; c2 >>= 8;
+    if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE;
+    c1 >>= 8; c2 >>= 8;
+    if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE;
+    c1 >>= 8; c2 >>= 8;
+    if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE;
+    return TRUE;
+}
+
+static D3DCOLOR get_surface_color(IDirectDrawSurface *surface, UINT x, UINT y)
+{
+    RECT rect = {x, y, x + 1, y + 1};
+    DDSURFACEDESC surface_desc;
+    D3DCOLOR color;
+    HRESULT hr;
+
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+
+    hr = IDirectDrawSurface_Lock(surface, &rect, &surface_desc, DDLOCK_READONLY | DDLOCK_WAIT, NULL);
+    ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr);
+    if (FAILED(hr))
+        return 0xdeadbeef;
+
+    color = *((DWORD *)surface_desc.lpSurface) & 0x00ffffff;
+
+    hr = IDirectDrawSurface_Unlock(surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr);
+
+    return color;
+}
+
 static IDirectDraw2 *create_ddraw(void)
 {
     IDirectDraw2 *ddraw2;
@@ -131,7 +166,189 @@ static void test_coop_level_create_device_window(void)
     DestroyWindow(focus_window);
 }
 
+static void test_clipper_blt(void)
+{
+    IDirectDrawSurface *src_surface, *dst_surface;
+    RECT client_rect, src_rect, *rect;
+    IDirectDrawClipper *clipper;
+    DDSURFACEDESC surface_desc;
+    unsigned int i, j, x, y;
+    IDirectDraw2 *ddraw;
+    RGNDATA *rgn_data;
+    D3DCOLOR color;
+    HRGN r1, r2;
+    HWND window;
+    DDBLTFX fx;
+    HRESULT hr;
+    DWORD ret;
+
+    static const D3DCOLOR expected1[] =
+    {
+        0x000000ff, 0x0000ff00, 0x00000000, 0x00000000,
+        0x000000ff, 0x0000ff00, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00ff0000, 0x00ffffff,
+        0x00000000, 0x00000000, 0x00ff0000, 0x00ffffff,
+    };
+    static const D3DCOLOR expected2[] =
+    {
+        0x000000ff, 0x000000ff, 0x00000000, 0x00000000,
+        0x000000ff, 0x000000ff, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x000000ff, 0x000000ff,
+        0x00000000, 0x00000000, 0x000000ff, 0x000000ff,
+    };
+
+    window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW,
+            10, 10, 640, 480, 0, 0, 0, 0);
+    ShowWindow(window, SW_SHOW);
+    if (!(ddraw = create_ddraw()))
+    {
+        skip("Failed to create a ddraw object, skipping test.\n");
+        DestroyWindow(window);
+        return;
+    }
+
+    ret = GetClientRect(window, &client_rect);
+    ok(ret, "Failed to get client rect.\n");
+    ret = MapWindowPoints(window, NULL, (POINT *)&client_rect, 2);
+    ok(ret, "Failed to map client rect.\n");
+
+    hr = IDirectDraw2_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL);
+    ok(SUCCEEDED(hr), "Failed to set cooperative level, hr %#x.\n", hr);
+
+    hr = IDirectDraw2_CreateClipper(ddraw, 0, &clipper, NULL);
+    ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
+    hr = IDirectDrawClipper_SetHWnd(clipper, 0, window);
+    ok(SUCCEEDED(hr), "Failed to set clipper window, hr %#x.\n", hr);
+    hr = IDirectDrawClipper_GetClipList(clipper, NULL, NULL, &ret);
+    ok(SUCCEEDED(hr), "Failed to get clip list size, hr %#x.\n", hr);
+    rgn_data = HeapAlloc(GetProcessHeap(), 0, ret);
+    hr = IDirectDrawClipper_GetClipList(clipper, NULL, rgn_data, &ret);
+    ok(SUCCEEDED(hr), "Failed to get clip list, hr %#x.\n", hr);
+    ok(rgn_data->rdh.dwSize == sizeof(rgn_data->rdh), "Got unexpected structure size %#x.\n", rgn_data->rdh.dwSize);
+    ok(rgn_data->rdh.iType == RDH_RECTANGLES, "Got unexpected type %#x.\n", rgn_data->rdh.iType);
+    ok(rgn_data->rdh.nCount == 1, "Got unexpected count %u.\n", rgn_data->rdh.nCount);
+    ok(rgn_data->rdh.nRgnSize == 16 || broken(rgn_data->rdh.nRgnSize == 168 /* NT4 */),
+            "Got unexpected region size %u.\n", rgn_data->rdh.nRgnSize);
+    ok(EqualRect(&rgn_data->rdh.rcBound, &client_rect),
+            "Got unexpected bounding rect {%d, %d, %d, %d}, expected {%d, %d, %d, %d}.\n",
+            rgn_data->rdh.rcBound.left, rgn_data->rdh.rcBound.top,
+            rgn_data->rdh.rcBound.right, rgn_data->rdh.rcBound.bottom,
+            client_rect.left, client_rect.top, client_rect.right, client_rect.bottom);
+    rect = (RECT *)&rgn_data->Buffer[0];
+    ok(EqualRect(rect, &client_rect),
+            "Got unexpected clip rect {%d, %d, %d, %d}, expected {%d, %d, %d, %d}.\n",
+            rect->left, rect->top, rect->right, rect->bottom,
+            client_rect.left, client_rect.top, client_rect.right, client_rect.bottom);
+    HeapFree(GetProcessHeap(), 0, rgn_data);
+
+    r1 = CreateRectRgn(0, 0, 320, 240);
+    ok(!!r1, "Failed to create region.\n");
+    r2 = CreateRectRgn(320, 240, 640, 480);
+    ok(!!r2, "Failed to create region.\n");
+    CombineRgn(r1, r1, r2, RGN_OR);
+    ret = GetRegionData(r1, 0, NULL);
+    rgn_data = HeapAlloc(GetProcessHeap(), 0, ret);
+    ret = GetRegionData(r1, ret, rgn_data);
+    ok(!!ret, "Failed to get region data.\n");
+
+    DeleteObject(r2);
+    DeleteObject(r1);
+
+    hr = IDirectDrawClipper_SetClipList(clipper, rgn_data, 0);
+    todo_wine ok(hr == DDERR_CLIPPERISUSINGHWND, "Got unexpected hr %#x.\n", hr);
+    hr = IDirectDrawClipper_SetHWnd(clipper, 0, NULL);
+    ok(SUCCEEDED(hr), "Failed to set clipper window, hr %#x.\n", hr);
+    hr = IDirectDrawClipper_SetClipList(clipper, rgn_data, 0);
+    ok(SUCCEEDED(hr), "Failed to set clip list, hr %#x.\n", hr);
+
+    HeapFree(GetProcessHeap(), 0, rgn_data);
+
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+    surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
+    surface_desc.dwWidth = 640;
+    surface_desc.dwHeight = 480;
+    surface_desc.ddpfPixelFormat.dwSize = sizeof(surface_desc.ddpfPixelFormat);
+    surface_desc.ddpfPixelFormat.dwFlags = DDPF_RGB;
+    U1(surface_desc.ddpfPixelFormat).dwRGBBitCount = 32;
+    U2(surface_desc.ddpfPixelFormat).dwRBitMask = 0x00ff0000;
+    U3(surface_desc.ddpfPixelFormat).dwGBitMask = 0x0000ff00;
+    U4(surface_desc.ddpfPixelFormat).dwBBitMask = 0x000000ff;
+
+    hr = IDirectDraw2_CreateSurface(ddraw, &surface_desc, &src_surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to create source surface, hr %#x.\n", hr);
+    hr = IDirectDraw2_CreateSurface(ddraw, &surface_desc, &dst_surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to create destination surface, hr %#x.\n", hr);
+
+    memset(&fx, 0, sizeof(fx));
+    fx.dwSize = sizeof(fx);
+    hr = IDirectDrawSurface_Blt(src_surface, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx);
+    ok(SUCCEEDED(hr), "Failed to clear source surface, hr %#x.\n", hr);
+    hr = IDirectDrawSurface_Blt(dst_surface, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx);
+    ok(SUCCEEDED(hr), "Failed to clear destination surface, hr %#x.\n", hr);
+
+    hr = IDirectDrawSurface_Lock(src_surface, NULL, &surface_desc, 0, NULL);
+    ok(SUCCEEDED(hr), "Failed to lock source surface, hr %#x.\n", hr);
+    ((DWORD *)surface_desc.lpSurface)[0] = 0xff0000ff;
+    ((DWORD *)surface_desc.lpSurface)[1] = 0xff00ff00;
+    ((DWORD *)surface_desc.lpSurface)[2] = 0xffff0000;
+    ((DWORD *)surface_desc.lpSurface)[3] = 0xffffffff;
+    hr = IDirectDrawSurface_Unlock(src_surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to unlock source surface, hr %#x.\n", hr);
+
+    hr = IDirectDrawSurface_SetClipper(dst_surface, clipper);
+    ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
+
+    SetRect(&src_rect, 0, 0, 4, 1);
+    hr = IDirectDrawSurface_Blt(dst_surface, NULL, src_surface, &src_rect, DDBLT_WAIT, NULL);
+    todo_wine ok(SUCCEEDED(hr), "Failed to blit, hr %#x.\n", hr);
+    for (i = 0; i < 4; ++i)
+    {
+        for (j = 0; j < 4; ++j)
+        {
+            x = 80 * ((2 * j) + 1);
+            y = 60 * ((2 * i) + 1);
+            color = get_surface_color(dst_surface, x, y);
+            if ((i < 2 && j < 2) || (i >= 2 && j >= 2))
+                todo_wine ok(compare_color(color, expected1[i * 4 + j], 1),
+                        "Expected color 0x%08x at %u,%u, got 0x%08x.\n", expected1[i * 4 + j], x, y, color);
+            else
+                ok(compare_color(color, expected1[i * 4 + j], 1),
+                        "Expected color 0x%08x at %u,%u, got 0x%08x.\n", expected1[i * 4 + j], x, y, color);
+        }
+    }
+
+    U5(fx).dwFillColor = 0xff0000ff;
+    hr = IDirectDrawSurface_Blt(dst_surface, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx);
+    todo_wine ok(SUCCEEDED(hr), "Failed to clear destination surface, hr %#x.\n", hr);
+    for (i = 0; i < 4; ++i)
+    {
+        for (j = 0; j < 4; ++j)
+        {
+            x = 80 * ((2 * j) + 1);
+            y = 60 * ((2 * i) + 1);
+            color = get_surface_color(dst_surface, x, y);
+            if ((i < 2 && j < 2) || (i >= 2 && j >= 2))
+                todo_wine ok(compare_color(color, expected2[i * 4 + j], 1),
+                        "Expected color 0x%08x at %u,%u, got 0x%08x.\n", expected2[i * 4 + j], x, y, color);
+            else
+                ok(compare_color(color, expected2[i * 4 + j], 1),
+                        "Expected color 0x%08x at %u,%u, got 0x%08x.\n", expected2[i * 4 + j], x, y, color);
+        }
+    }
+
+    hr = IDirectDrawSurface_BltFast(dst_surface, 0, 0, src_surface, NULL, DDBLTFAST_WAIT);
+    todo_wine ok(hr == DDERR_BLTFASTCANTCLIP || broken(hr == E_NOTIMPL /* NT4 */), "Got unexpected hr %#x.\n", hr);
+
+    IDirectDrawSurface_Release(dst_surface);
+    IDirectDrawSurface_Release(src_surface);
+    IDirectDrawClipper_Release(clipper);
+    IDirectDraw2_Release(ddraw);
+}
+
 START_TEST(ddraw2)
 {
     test_coop_level_create_device_window();
+    test_clipper_blt();
 }
diff --git a/dlls/ddraw/tests/ddraw4.c b/dlls/ddraw/tests/ddraw4.c
index 680c6ac..134af22 100644
--- a/dlls/ddraw/tests/ddraw4.c
+++ b/dlls/ddraw/tests/ddraw4.c
@@ -54,6 +54,41 @@ static BOOL compare_vec4(struct vec4 *vec, float x, float y, float z, float w, u
             && compare_float(vec->w, w, ulps);
 }
 
+static BOOL compare_color(D3DCOLOR c1, D3DCOLOR c2, BYTE max_diff)
+{
+    if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE;
+    c1 >>= 8; c2 >>= 8;
+    if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE;
+    c1 >>= 8; c2 >>= 8;
+    if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE;
+    c1 >>= 8; c2 >>= 8;
+    if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE;
+    return TRUE;
+}
+
+static D3DCOLOR get_surface_color(IDirectDrawSurface4 *surface, UINT x, UINT y)
+{
+    RECT rect = {x, y, x + 1, y + 1};
+    DDSURFACEDESC2 surface_desc;
+    D3DCOLOR color;
+    HRESULT hr;
+
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+
+    hr = IDirectDrawSurface4_Lock(surface, &rect, &surface_desc, DDLOCK_READONLY | DDLOCK_WAIT, NULL);
+    ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr);
+    if (FAILED(hr))
+        return 0xdeadbeef;
+
+    color = *((DWORD *)surface_desc.lpSurface) & 0x00ffffff;
+
+    hr = IDirectDrawSurface4_Unlock(surface, &rect);
+    ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr);
+
+    return color;
+}
+
 static IDirectDraw4 *create_ddraw(void)
 {
     IDirectDraw4 *ddraw4;
@@ -427,8 +462,189 @@ static void test_coop_level_create_device_window(void)
     DestroyWindow(focus_window);
 }
 
+static void test_clipper_blt(void)
+{
+    IDirectDrawSurface4 *src_surface, *dst_surface;
+    RECT client_rect, src_rect, *rect;
+    IDirectDrawClipper *clipper;
+    DDSURFACEDESC2 surface_desc;
+    unsigned int i, j, x, y;
+    IDirectDraw4 *ddraw;
+    RGNDATA *rgn_data;
+    D3DCOLOR color;
+    HRGN r1, r2;
+    HWND window;
+    DDBLTFX fx;
+    HRESULT hr;
+    DWORD ret;
+
+    static const D3DCOLOR expected1[] =
+    {
+        0x000000ff, 0x0000ff00, 0x00000000, 0x00000000,
+        0x000000ff, 0x0000ff00, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00ff0000, 0x00ffffff,
+        0x00000000, 0x00000000, 0x00ff0000, 0x00ffffff,
+    };
+    static const D3DCOLOR expected2[] =
+    {
+        0x000000ff, 0x000000ff, 0x00000000, 0x00000000,
+        0x000000ff, 0x000000ff, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x000000ff, 0x000000ff,
+        0x00000000, 0x00000000, 0x000000ff, 0x000000ff,
+    };
+
+    window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW,
+            10, 10, 640, 480, 0, 0, 0, 0);
+    ShowWindow(window, SW_SHOW);
+    if (!(ddraw = create_ddraw()))
+    {
+        skip("Failed to create a ddraw object, skipping test.\n");
+        DestroyWindow(window);
+        return;
+    }
+
+    ret = GetClientRect(window, &client_rect);
+    ok(ret, "Failed to get client rect.\n");
+    ret = MapWindowPoints(window, NULL, (POINT *)&client_rect, 2);
+    ok(ret, "Failed to map client rect.\n");
+
+    hr = IDirectDraw4_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL);
+    ok(SUCCEEDED(hr), "Failed to set cooperative level, hr %#x.\n", hr);
+
+    hr = IDirectDraw4_CreateClipper(ddraw, 0, &clipper, NULL);
+    ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
+    hr = IDirectDrawClipper_SetHWnd(clipper, 0, window);
+    ok(SUCCEEDED(hr), "Failed to set clipper window, hr %#x.\n", hr);
+    hr = IDirectDrawClipper_GetClipList(clipper, NULL, NULL, &ret);
+    ok(SUCCEEDED(hr), "Failed to get clip list size, hr %#x.\n", hr);
+    rgn_data = HeapAlloc(GetProcessHeap(), 0, ret);
+    hr = IDirectDrawClipper_GetClipList(clipper, NULL, rgn_data, &ret);
+    ok(SUCCEEDED(hr), "Failed to get clip list, hr %#x.\n", hr);
+    ok(rgn_data->rdh.dwSize == sizeof(rgn_data->rdh), "Got unexpected structure size %#x.\n", rgn_data->rdh.dwSize);
+    ok(rgn_data->rdh.iType == RDH_RECTANGLES, "Got unexpected type %#x.\n", rgn_data->rdh.iType);
+    ok(rgn_data->rdh.nCount == 1, "Got unexpected count %u.\n", rgn_data->rdh.nCount);
+    ok(rgn_data->rdh.nRgnSize == 16, "Got unexpected region size %u.\n", rgn_data->rdh.nRgnSize);
+    ok(EqualRect(&rgn_data->rdh.rcBound, &client_rect),
+            "Got unexpected bounding rect {%d, %d, %d, %d}, expected {%d, %d, %d, %d}.\n",
+            rgn_data->rdh.rcBound.left, rgn_data->rdh.rcBound.top,
+            rgn_data->rdh.rcBound.right, rgn_data->rdh.rcBound.bottom,
+            client_rect.left, client_rect.top, client_rect.right, client_rect.bottom);
+    rect = (RECT *)&rgn_data->Buffer[0];
+    ok(EqualRect(rect, &client_rect),
+            "Got unexpected clip rect {%d, %d, %d, %d}, expected {%d, %d, %d, %d}.\n",
+            rect->left, rect->top, rect->right, rect->bottom,
+            client_rect.left, client_rect.top, client_rect.right, client_rect.bottom);
+    HeapFree(GetProcessHeap(), 0, rgn_data);
+
+    r1 = CreateRectRgn(0, 0, 320, 240);
+    ok(!!r1, "Failed to create region.\n");
+    r2 = CreateRectRgn(320, 240, 640, 480);
+    ok(!!r2, "Failed to create region.\n");
+    CombineRgn(r1, r1, r2, RGN_OR);
+    ret = GetRegionData(r1, 0, NULL);
+    rgn_data = HeapAlloc(GetProcessHeap(), 0, ret);
+    ret = GetRegionData(r1, ret, rgn_data);
+    ok(!!ret, "Failed to get region data.\n");
+
+    DeleteObject(r2);
+    DeleteObject(r1);
+
+    hr = IDirectDrawClipper_SetClipList(clipper, rgn_data, 0);
+    todo_wine ok(hr == DDERR_CLIPPERISUSINGHWND, "Got unexpected hr %#x.\n", hr);
+    hr = IDirectDrawClipper_SetHWnd(clipper, 0, NULL);
+    ok(SUCCEEDED(hr), "Failed to set clipper window, hr %#x.\n", hr);
+    hr = IDirectDrawClipper_SetClipList(clipper, rgn_data, 0);
+    ok(SUCCEEDED(hr), "Failed to set clip list, hr %#x.\n", hr);
+
+    HeapFree(GetProcessHeap(), 0, rgn_data);
+
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+    surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
+    surface_desc.dwWidth = 640;
+    surface_desc.dwHeight = 480;
+    surface_desc.ddpfPixelFormat.dwSize = sizeof(surface_desc.ddpfPixelFormat);
+    surface_desc.ddpfPixelFormat.dwFlags = DDPF_RGB;
+    U1(surface_desc.ddpfPixelFormat).dwRGBBitCount = 32;
+    U2(surface_desc.ddpfPixelFormat).dwRBitMask = 0x00ff0000;
+    U3(surface_desc.ddpfPixelFormat).dwGBitMask = 0x0000ff00;
+    U4(surface_desc.ddpfPixelFormat).dwBBitMask = 0x000000ff;
+
+    hr = IDirectDraw4_CreateSurface(ddraw, &surface_desc, &src_surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to create source surface, hr %#x.\n", hr);
+    hr = IDirectDraw4_CreateSurface(ddraw, &surface_desc, &dst_surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to create destination surface, hr %#x.\n", hr);
+
+    memset(&fx, 0, sizeof(fx));
+    fx.dwSize = sizeof(fx);
+    hr = IDirectDrawSurface4_Blt(src_surface, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx);
+    ok(SUCCEEDED(hr), "Failed to clear source surface, hr %#x.\n", hr);
+    hr = IDirectDrawSurface4_Blt(dst_surface, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx);
+    ok(SUCCEEDED(hr), "Failed to clear destination surface, hr %#x.\n", hr);
+
+    hr = IDirectDrawSurface4_Lock(src_surface, NULL, &surface_desc, 0, NULL);
+    ok(SUCCEEDED(hr), "Failed to lock source surface, hr %#x.\n", hr);
+    ((DWORD *)surface_desc.lpSurface)[0] = 0xff0000ff;
+    ((DWORD *)surface_desc.lpSurface)[1] = 0xff00ff00;
+    ((DWORD *)surface_desc.lpSurface)[2] = 0xffff0000;
+    ((DWORD *)surface_desc.lpSurface)[3] = 0xffffffff;
+    hr = IDirectDrawSurface4_Unlock(src_surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to unlock source surface, hr %#x.\n", hr);
+
+    hr = IDirectDrawSurface4_SetClipper(dst_surface, clipper);
+    ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
+
+    SetRect(&src_rect, 0, 0, 4, 1);
+    hr = IDirectDrawSurface4_Blt(dst_surface, NULL, src_surface, &src_rect, DDBLT_WAIT, NULL);
+    todo_wine ok(SUCCEEDED(hr), "Failed to blit, hr %#x.\n", hr);
+    for (i = 0; i < 4; ++i)
+    {
+        for (j = 0; j < 4; ++j)
+        {
+            x = 80 * ((2 * j) + 1);
+            y = 60 * ((2 * i) + 1);
+            color = get_surface_color(dst_surface, x, y);
+            if ((i < 2 && j < 2) || (i >= 2 && j >= 2))
+                todo_wine ok(compare_color(color, expected1[i * 4 + j], 1),
+                        "Expected color 0x%08x at %u,%u, got 0x%08x.\n", expected1[i * 4 + j], x, y, color);
+            else
+                ok(compare_color(color, expected1[i * 4 + j], 1),
+                        "Expected color 0x%08x at %u,%u, got 0x%08x.\n", expected1[i * 4 + j], x, y, color);
+        }
+    }
+
+    U5(fx).dwFillColor = 0xff0000ff;
+    hr = IDirectDrawSurface4_Blt(dst_surface, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx);
+    todo_wine ok(SUCCEEDED(hr), "Failed to clear destination surface, hr %#x.\n", hr);
+    for (i = 0; i < 4; ++i)
+    {
+        for (j = 0; j < 4; ++j)
+        {
+            x = 80 * ((2 * j) + 1);
+            y = 60 * ((2 * i) + 1);
+            color = get_surface_color(dst_surface, x, y);
+            if ((i < 2 && j < 2) || (i >= 2 && j >= 2))
+                todo_wine ok(compare_color(color, expected2[i * 4 + j], 1),
+                        "Expected color 0x%08x at %u,%u, got 0x%08x.\n", expected2[i * 4 + j], x, y, color);
+            else
+                ok(compare_color(color, expected2[i * 4 + j], 1),
+                        "Expected color 0x%08x at %u,%u, got 0x%08x.\n", expected2[i * 4 + j], x, y, color);
+        }
+    }
+
+    hr = IDirectDrawSurface4_BltFast(dst_surface, 0, 0, src_surface, NULL, DDBLTFAST_WAIT);
+    todo_wine ok(hr == DDERR_BLTFASTCANTCLIP, "Got unexpected hr %#x.\n", hr);
+
+    IDirectDrawSurface4_Release(dst_surface);
+    IDirectDrawSurface4_Release(src_surface);
+    IDirectDrawClipper_Release(clipper);
+    IDirectDraw4_Release(ddraw);
+}
+
 START_TEST(ddraw4)
 {
     test_process_vertices();
     test_coop_level_create_device_window();
+    test_clipper_blt();
 }
diff --git a/dlls/ddraw/tests/ddraw7.c b/dlls/ddraw/tests/ddraw7.c
index 89974e9..14477c1 100644
--- a/dlls/ddraw/tests/ddraw7.c
+++ b/dlls/ddraw/tests/ddraw7.c
@@ -64,6 +64,41 @@ static BOOL compare_vec4(struct vec4 *vec, float x, float y, float z, float w, u
             && compare_float(vec->w, w, ulps);
 }
 
+static BOOL compare_color(D3DCOLOR c1, D3DCOLOR c2, BYTE max_diff)
+{
+    if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE;
+    c1 >>= 8; c2 >>= 8;
+    if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE;
+    c1 >>= 8; c2 >>= 8;
+    if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE;
+    c1 >>= 8; c2 >>= 8;
+    if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff) return FALSE;
+    return TRUE;
+}
+
+static D3DCOLOR get_surface_color(IDirectDrawSurface7 *surface, UINT x, UINT y)
+{
+    RECT rect = {x, y, x + 1, y + 1};
+    DDSURFACEDESC2 surface_desc;
+    D3DCOLOR color;
+    HRESULT hr;
+
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+
+    hr = IDirectDrawSurface7_Lock(surface, &rect, &surface_desc, DDLOCK_READONLY | DDLOCK_WAIT, NULL);
+    ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr);
+    if (FAILED(hr))
+        return 0xdeadbeef;
+
+    color = *((DWORD *)surface_desc.lpSurface) & 0x00ffffff;
+
+    hr = IDirectDrawSurface7_Unlock(surface, &rect);
+    ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr);
+
+    return color;
+}
+
 static IDirectDraw7 *create_ddraw(void)
 {
     IDirectDraw7 *ddraw;
@@ -420,6 +455,186 @@ static void test_coop_level_create_device_window(void)
     DestroyWindow(focus_window);
 }
 
+static void test_clipper_blt(void)
+{
+    IDirectDrawSurface7 *src_surface, *dst_surface;
+    RECT client_rect, src_rect, *rect;
+    IDirectDrawClipper *clipper;
+    DDSURFACEDESC2 surface_desc;
+    unsigned int i, j, x, y;
+    IDirectDraw7 *ddraw;
+    RGNDATA *rgn_data;
+    D3DCOLOR color;
+    HRGN r1, r2;
+    HWND window;
+    DDBLTFX fx;
+    HRESULT hr;
+    DWORD ret;
+
+    static const D3DCOLOR expected1[] =
+    {
+        0x000000ff, 0x0000ff00, 0x00000000, 0x00000000,
+        0x000000ff, 0x0000ff00, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x00ff0000, 0x00ffffff,
+        0x00000000, 0x00000000, 0x00ff0000, 0x00ffffff,
+    };
+    static const D3DCOLOR expected2[] =
+    {
+        0x000000ff, 0x000000ff, 0x00000000, 0x00000000,
+        0x000000ff, 0x000000ff, 0x00000000, 0x00000000,
+        0x00000000, 0x00000000, 0x000000ff, 0x000000ff,
+        0x00000000, 0x00000000, 0x000000ff, 0x000000ff,
+    };
+
+    window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW,
+            10, 10, 640, 480, 0, 0, 0, 0);
+    ShowWindow(window, SW_SHOW);
+    if (!(ddraw = create_ddraw()))
+    {
+        skip("Failed to create a ddraw object, skipping test.\n");
+        DestroyWindow(window);
+        return;
+    }
+
+    ret = GetClientRect(window, &client_rect);
+    ok(ret, "Failed to get client rect.\n");
+    ret = MapWindowPoints(window, NULL, (POINT *)&client_rect, 2);
+    ok(ret, "Failed to map client rect.\n");
+
+    hr = IDirectDraw7_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL);
+    ok(SUCCEEDED(hr), "Failed to set cooperative level, hr %#x.\n", hr);
+
+    hr = IDirectDraw7_CreateClipper(ddraw, 0, &clipper, NULL);
+    ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
+    hr = IDirectDrawClipper_SetHWnd(clipper, 0, window);
+    ok(SUCCEEDED(hr), "Failed to set clipper window, hr %#x.\n", hr);
+    hr = IDirectDrawClipper_GetClipList(clipper, NULL, NULL, &ret);
+    ok(SUCCEEDED(hr), "Failed to get clip list size, hr %#x.\n", hr);
+    rgn_data = HeapAlloc(GetProcessHeap(), 0, ret);
+    hr = IDirectDrawClipper_GetClipList(clipper, NULL, rgn_data, &ret);
+    ok(SUCCEEDED(hr), "Failed to get clip list, hr %#x.\n", hr);
+    ok(rgn_data->rdh.dwSize == sizeof(rgn_data->rdh), "Got unexpected structure size %#x.\n", rgn_data->rdh.dwSize);
+    ok(rgn_data->rdh.iType == RDH_RECTANGLES, "Got unexpected type %#x.\n", rgn_data->rdh.iType);
+    ok(rgn_data->rdh.nCount == 1, "Got unexpected count %u.\n", rgn_data->rdh.nCount);
+    ok(rgn_data->rdh.nRgnSize == 16, "Got unexpected region size %u.\n", rgn_data->rdh.nRgnSize);
+    ok(EqualRect(&rgn_data->rdh.rcBound, &client_rect),
+            "Got unexpected bounding rect {%d, %d, %d, %d}, expected {%d, %d, %d, %d}.\n",
+            rgn_data->rdh.rcBound.left, rgn_data->rdh.rcBound.top,
+            rgn_data->rdh.rcBound.right, rgn_data->rdh.rcBound.bottom,
+            client_rect.left, client_rect.top, client_rect.right, client_rect.bottom);
+    rect = (RECT *)&rgn_data->Buffer[0];
+    ok(EqualRect(rect, &client_rect),
+            "Got unexpected clip rect {%d, %d, %d, %d}, expected {%d, %d, %d, %d}.\n",
+            rect->left, rect->top, rect->right, rect->bottom,
+            client_rect.left, client_rect.top, client_rect.right, client_rect.bottom);
+    HeapFree(GetProcessHeap(), 0, rgn_data);
+
+    r1 = CreateRectRgn(0, 0, 320, 240);
+    ok(!!r1, "Failed to create region.\n");
+    r2 = CreateRectRgn(320, 240, 640, 480);
+    ok(!!r2, "Failed to create region.\n");
+    CombineRgn(r1, r1, r2, RGN_OR);
+    ret = GetRegionData(r1, 0, NULL);
+    rgn_data = HeapAlloc(GetProcessHeap(), 0, ret);
+    ret = GetRegionData(r1, ret, rgn_data);
+    ok(!!ret, "Failed to get region data.\n");
+
+    DeleteObject(r2);
+    DeleteObject(r1);
+
+    hr = IDirectDrawClipper_SetClipList(clipper, rgn_data, 0);
+    todo_wine ok(hr == DDERR_CLIPPERISUSINGHWND, "Got unexpected hr %#x.\n", hr);
+    hr = IDirectDrawClipper_SetHWnd(clipper, 0, NULL);
+    ok(SUCCEEDED(hr), "Failed to set clipper window, hr %#x.\n", hr);
+    hr = IDirectDrawClipper_SetClipList(clipper, rgn_data, 0);
+    ok(SUCCEEDED(hr), "Failed to set clip list, hr %#x.\n", hr);
+
+    HeapFree(GetProcessHeap(), 0, rgn_data);
+
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+    surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
+    surface_desc.dwWidth = 640;
+    surface_desc.dwHeight = 480;
+    surface_desc.ddpfPixelFormat.dwSize = sizeof(surface_desc.ddpfPixelFormat);
+    surface_desc.ddpfPixelFormat.dwFlags = DDPF_RGB;
+    U1(surface_desc.ddpfPixelFormat).dwRGBBitCount = 32;
+    U2(surface_desc.ddpfPixelFormat).dwRBitMask = 0x00ff0000;
+    U3(surface_desc.ddpfPixelFormat).dwGBitMask = 0x0000ff00;
+    U4(surface_desc.ddpfPixelFormat).dwBBitMask = 0x000000ff;
+
+    hr = IDirectDraw7_CreateSurface(ddraw, &surface_desc, &src_surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to create source surface, hr %#x.\n", hr);
+    hr = IDirectDraw7_CreateSurface(ddraw, &surface_desc, &dst_surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to create destination surface, hr %#x.\n", hr);
+
+    memset(&fx, 0, sizeof(fx));
+    fx.dwSize = sizeof(fx);
+    hr = IDirectDrawSurface7_Blt(src_surface, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx);
+    ok(SUCCEEDED(hr), "Failed to clear source surface, hr %#x.\n", hr);
+    hr = IDirectDrawSurface7_Blt(dst_surface, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx);
+    ok(SUCCEEDED(hr), "Failed to clear destination surface, hr %#x.\n", hr);
+
+    hr = IDirectDrawSurface7_Lock(src_surface, NULL, &surface_desc, 0, NULL);
+    ok(SUCCEEDED(hr), "Failed to lock source surface, hr %#x.\n", hr);
+    ((DWORD *)surface_desc.lpSurface)[0] = 0xff0000ff;
+    ((DWORD *)surface_desc.lpSurface)[1] = 0xff00ff00;
+    ((DWORD *)surface_desc.lpSurface)[2] = 0xffff0000;
+    ((DWORD *)surface_desc.lpSurface)[3] = 0xffffffff;
+    hr = IDirectDrawSurface7_Unlock(src_surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to unlock source surface, hr %#x.\n", hr);
+
+    hr = IDirectDrawSurface7_SetClipper(dst_surface, clipper);
+    ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
+
+    SetRect(&src_rect, 0, 0, 4, 1);
+    hr = IDirectDrawSurface7_Blt(dst_surface, NULL, src_surface, &src_rect, DDBLT_WAIT, NULL);
+    todo_wine ok(SUCCEEDED(hr), "Failed to blit, hr %#x.\n", hr);
+    for (i = 0; i < 4; ++i)
+    {
+        for (j = 0; j < 4; ++j)
+        {
+            x = 80 * ((2 * j) + 1);
+            y = 60 * ((2 * i) + 1);
+            color = get_surface_color(dst_surface, x, y);
+            if ((i < 2 && j < 2) || (i >= 2 && j >= 2))
+                todo_wine ok(compare_color(color, expected1[i * 4 + j], 1),
+                        "Expected color 0x%08x at %u,%u, got 0x%08x.\n", expected1[i * 4 + j], x, y, color);
+            else
+                ok(compare_color(color, expected1[i * 4 + j], 1),
+                        "Expected color 0x%08x at %u,%u, got 0x%08x.\n", expected1[i * 4 + j], x, y, color);
+        }
+    }
+
+    U5(fx).dwFillColor = 0xff0000ff;
+    hr = IDirectDrawSurface7_Blt(dst_surface, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx);
+    todo_wine ok(SUCCEEDED(hr), "Failed to clear destination surface, hr %#x.\n", hr);
+    for (i = 0; i < 4; ++i)
+    {
+        for (j = 0; j < 4; ++j)
+        {
+            x = 80 * ((2 * j) + 1);
+            y = 60 * ((2 * i) + 1);
+            color = get_surface_color(dst_surface, x, y);
+            if ((i < 2 && j < 2) || (i >= 2 && j >= 2))
+                todo_wine ok(compare_color(color, expected2[i * 4 + j], 1),
+                        "Expected color 0x%08x at %u,%u, got 0x%08x.\n", expected2[i * 4 + j], x, y, color);
+            else
+                ok(compare_color(color, expected2[i * 4 + j], 1),
+                        "Expected color 0x%08x at %u,%u, got 0x%08x.\n", expected2[i * 4 + j], x, y, color);
+        }
+    }
+
+    hr = IDirectDrawSurface7_BltFast(dst_surface, 0, 0, src_surface, NULL, DDBLTFAST_WAIT);
+    todo_wine ok(hr == DDERR_BLTFASTCANTCLIP, "Got unexpected hr %#x.\n", hr);
+
+    IDirectDrawSurface7_Release(dst_surface);
+    IDirectDrawSurface7_Release(src_surface);
+    IDirectDrawClipper_Release(clipper);
+    IDirectDraw7_Release(ddraw);
+}
+
 START_TEST(ddraw7)
 {
     HMODULE module = GetModuleHandleA("ddraw.dll");
@@ -432,4 +647,5 @@ START_TEST(ddraw7)
 
     test_process_vertices();
     test_coop_level_create_device_window();
+    test_clipper_blt();
 }
-- 
1.7.3.4

