[PATCH 2/3] ddraw/tests: Test color keying precision.

Stefan Dösinger stefan at codeweavers.com
Sun Aug 23 14:59:34 CDT 2015


This brute force tests all values in 4, 5, 6 and 8 bit channels.
---
 dlls/ddraw/tests/ddraw1.c | 335 ++++++++++++++++++++++++++++++++++++++++++++++
 dlls/ddraw/tests/ddraw2.c | 308 ++++++++++++++++++++++++++++++++++++++++++
 dlls/ddraw/tests/ddraw4.c | 318 +++++++++++++++++++++++++++++++++++++++++++
 dlls/ddraw/tests/ddraw7.c | 302 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1263 insertions(+)

diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c
index f97b964..676d357 100644
--- a/dlls/ddraw/tests/ddraw1.c
+++ b/dlls/ddraw/tests/ddraw1.c
@@ -7279,6 +7279,340 @@ done:
     DestroyWindow(window);
 }
 
+static void test_colorkey_precision(void)
+{
+    static D3DTLVERTEX quad[] =
+    {
+        {{  0.0f}, {480.0f}, {0.0f}, {1.0f}, {0x00000000}, {0x00000000}, {0.0f}, {1.0f}},
+        {{  0.0f}, {  0.0f}, {0.0f}, {1.0f}, {0x00000000}, {0x00000000}, {0.0f}, {0.0f}},
+        {{640.0f}, {480.0f}, {0.0f}, {1.0f}, {0x00000000}, {0x00000000}, {1.0f}, {1.0f}},
+        {{640.0f}, {  0.0f}, {0.0f}, {1.0f}, {0x00000000}, {0x00000000}, {1.0f}, {0.0f}},
+    };
+    static D3DRECT clear_rect = {{0}, {0}, {640}, {480}};
+    IDirect3DDevice *device;
+    IDirectDraw *ddraw;
+    IDirectDrawSurface *rt;
+    IDirect3DViewport *viewport;
+    IDirect3DExecuteBuffer *execute_buffer;
+    D3DEXECUTEBUFFERDESC exec_desc;
+    UINT inst_length;
+    void *ptr;
+    HWND window;
+    HRESULT hr;
+    IDirectDrawSurface *src, *dst, *texture;
+    D3DTEXTUREHANDLE handle;
+    IDirect3DTexture *d3d_texture;
+    IDirect3DMaterial *green;
+    DDSURFACEDESC surface_desc, lock_desc;
+    ULONG refcount;
+    D3DCOLOR color;
+    unsigned int t, c;
+    DDCOLORKEY ckey;
+    DDBLTFX fx;
+    DWORD data[4] = {0}, color_mask;
+    D3DDEVICEDESC device_desc, hel_desc;
+    BOOL warp;
+    static const struct
+    {
+        unsigned int max, shift, bpp, clear;
+        const char *name;
+        DDPIXELFORMAT fmt;
+    }
+    tests[] =
+    {
+        {
+            255, 0, 4, 0x00345678, "D3DFMT_X8R8G8B8",
+            {
+                sizeof(DDPIXELFORMAT), DDPF_RGB, 0,
+                {32}, {0x00ff0000}, {0x0000ff00}, {0x000000ff}, {0x00000000}
+            }
+
+        },
+        {
+            63, 5, 2, 0x5678, "D3DFMT_R5G6B5, G channel",
+            {
+                sizeof(DDPIXELFORMAT), DDPF_RGB, 0,
+                {16}, {0xf800}, {0x07e0}, {0x001f}, {0x0000}
+            }
+
+        },
+        {
+            31, 0, 2, 0x5678, "D3DFMT_R5G6B5, B channel",
+            {
+                sizeof(DDPIXELFORMAT), DDPF_RGB, 0,
+                {16}, {0xf800}, {0x07e0}, {0x001f}, {0x0000}
+            }
+
+        },
+        {
+            15, 0, 2, 0x0678, "D3DFMT_A4R4G4B4",
+            {
+                sizeof(DDPIXELFORMAT), DDPF_RGB | DDPF_ALPHAPIXELS, 0,
+                {16}, {0x0f00}, {0x00f0}, {0x000f}, {0xf000}
+            }
+
+        },
+    };
+
+    window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW,
+            0, 0, 640, 480, 0, 0, 0, 0);
+    ddraw = create_ddraw();
+    ok(!!ddraw, "Failed to create a ddraw object.\n");
+    if (!(device = create_device(ddraw, window, DDSCL_NORMAL)))
+    {
+        skip("Failed to create a 3D device, skipping test.\n");
+        DestroyWindow(window);
+        IDirectDraw_Release(ddraw);
+        return;
+    }
+    hr = IDirect3DDevice_QueryInterface(device, &IID_IDirectDrawSurface, (void **)&rt);
+    ok(SUCCEEDED(hr), "Failed to get render target, hr %#x.\n", hr);
+
+    /* The Windows 8 WARP driver has plenty of false negatives in X8R8G8B8
+     * (color key doesn't match although the values are equal), and a false
+     * positive when the color key is 0 and the texture contains the value 1.
+     * I don't want to mark this broken unconditionally since this would
+     * essentially disable the test on Windows. Try to detect WARP (and I
+     * guess mismatch other SW renderers) by its ability to texture from
+     * system memory. Also on random occasions 254 == 255 and 255 != 255.*/
+    memset(&device_desc, 0, sizeof(device_desc));
+    device_desc.dwSize = sizeof(device_desc);
+    memset(&hel_desc, 0, sizeof(hel_desc));
+    hel_desc.dwSize = sizeof(hel_desc);
+    hr = IDirect3DDevice_GetCaps(device, &device_desc, &hel_desc);
+    ok(SUCCEEDED(hr), "Failed to get device caps, hr %#x.\n", hr);
+    warp = !!(device_desc.dwDevCaps & D3DDEVCAPS_TEXTURESYSTEMMEMORY);
+
+    green = create_diffuse_material(device, 0.0f, 1.0f, 0.0f, 0.0f);
+    viewport = create_viewport(device, 0, 0, 640, 480);
+    viewport_set_background(device, viewport, green);
+
+    memset(&exec_desc, 0, sizeof(exec_desc));
+    exec_desc.dwSize = sizeof(exec_desc);
+    exec_desc.dwFlags = D3DDEB_BUFSIZE | D3DDEB_CAPS;
+    exec_desc.dwBufferSize = 1024;
+    exec_desc.dwCaps = D3DDEBCAPS_SYSTEMMEMORY;
+    hr = IDirect3DDevice_CreateExecuteBuffer(device, &exec_desc, &execute_buffer, NULL);
+    ok(SUCCEEDED(hr), "Failed to create execute buffer, hr %#x.\n", hr);
+
+    memset(&fx, 0, sizeof(fx));
+    fx.dwSize = sizeof(fx);
+    memset(&lock_desc, 0, sizeof(lock_desc));
+    lock_desc.dwSize = sizeof(lock_desc);
+
+    for (t = 0; t < sizeof(tests) / sizeof(*tests); ++t)
+    {
+        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 = 4;
+        surface_desc.dwHeight = 1;
+        surface_desc.ddpfPixelFormat = tests[t].fmt;
+        /* Windows XP (at least with the r200 driver, other drivers untested) produces
+         * garbage when doing color keyed texture->texture blits. */
+        hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &src, NULL);
+        ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+        hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &dst, NULL);
+        ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+
+        fx.dwFillColor = tests[t].clear;
+        /* On the w8 testbot (WARP driver) the blit result has different values in the
+         * X channel. */
+        color_mask = U2(tests[t].fmt).dwRBitMask
+                | U3(tests[t].fmt).dwGBitMask
+                | U4(tests[t].fmt).dwBBitMask;
+
+        for (c = 0; c <= tests[t].max; ++c)
+        {
+            /* The idiotic Nvidia Windows driver can't change the color key on a d3d
+             * texture after it has been set once... */
+            surface_desc.dwFlags |= DDSD_CKSRCBLT;
+            surface_desc.ddsCaps.dwCaps = DDSCAPS_TEXTURE;
+            surface_desc.ddckCKSrcBlt.dwColorSpaceLowValue = c << tests[t].shift;
+            surface_desc.ddckCKSrcBlt.dwColorSpaceHighValue = c << tests[t].shift;
+            hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &texture, NULL);
+            ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+
+            hr = IDirectDrawSurface_QueryInterface(texture, &IID_IDirect3DTexture, (void **)&d3d_texture);
+            ok(SUCCEEDED(hr), "Failed to get texture interface, hr %#x.\n", hr);
+            hr = IDirect3DTexture_GetHandle(d3d_texture, device, &handle);
+            ok(SUCCEEDED(hr), "Failed to get texture handle, hr %#x.\n", hr);
+            IDirect3DTexture_Release(d3d_texture);
+
+            hr = IDirect3DExecuteBuffer_Lock(execute_buffer, &exec_desc);
+            ok(SUCCEEDED(hr), "Failed to lock execute buffer, hr %#x.\n", hr);
+
+            memcpy(exec_desc.lpData, quad, sizeof(quad));
+
+            ptr = ((BYTE *)exec_desc.lpData) + sizeof(quad);
+            emit_process_vertices(&ptr, D3DPROCESSVERTICES_COPY, 0, 8);
+            emit_set_rs(&ptr, D3DRENDERSTATE_ZENABLE, D3DZB_FALSE);
+            emit_set_rs(&ptr, D3DRENDERSTATE_TEXTUREHANDLE, handle);
+            emit_set_rs(&ptr, D3DRENDERSTATE_TEXTUREMAPBLEND, D3DTBLEND_MODULATEALPHA);
+            /* D3DRENDERSTATE_COLORKEYENABLE is supposed to be on by default on version
+             * 1 devices, but for some reason it randomly defaults to FALSE on the W8
+             * testbot. This is either the fault of Windows 8 or the WARP driver.
+             * Also D3DRENDERSTATE_COLORKEYENABLE was introduced in D3D 5 aka version 2
+             * devices only, which might imply this doesn't actually do anything on
+             * WARP. */
+            emit_set_rs(&ptr, D3DRENDERSTATE_COLORKEYENABLE, TRUE);
+
+            emit_tquad(&ptr, 0);
+            emit_tquad(&ptr, 4);
+            emit_set_rs(&ptr, D3DRENDERSTATE_TEXTUREHANDLE, 0);
+            emit_end(&ptr);
+
+            inst_length = (BYTE *)ptr - (BYTE *)exec_desc.lpData;
+            inst_length -= sizeof(quad);
+            hr = IDirect3DExecuteBuffer_Unlock(execute_buffer);
+            ok(SUCCEEDED(hr), "Failed to unlock execute buffer, hr %#x.\n", hr);
+            set_execute_data(execute_buffer, 8, sizeof(quad), inst_length);
+
+            hr = IDirectDrawSurface_Blt(dst, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx);
+            ok(SUCCEEDED(hr), "Failed to clear destination surface, hr %#x.\n", hr);
+
+            hr = IDirectDrawSurface_Lock(src, NULL, &lock_desc, DDLOCK_WAIT, NULL);
+            ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr);
+            switch (tests[t].bpp)
+            {
+                case 4:
+                    ((DWORD *)lock_desc.lpSurface)[0] = (c ? c - 1 : 0) << tests[t].shift;
+                    ((DWORD *)lock_desc.lpSurface)[1] = c << tests[t].shift;
+                    ((DWORD *)lock_desc.lpSurface)[2] = min(c + 1, tests[t].max) << tests[t].shift;
+                    ((DWORD *)lock_desc.lpSurface)[3] = 0xffffffff;
+                    break;
+
+                case 2:
+                    ((WORD *)lock_desc.lpSurface)[0] = (c ? c - 1 : 0) << tests[t].shift;
+                    ((WORD *)lock_desc.lpSurface)[1] = c << tests[t].shift;
+                    ((WORD *)lock_desc.lpSurface)[2] = min(c + 1, tests[t].max) << tests[t].shift;
+                    ((WORD *)lock_desc.lpSurface)[3] = 0xffff;
+                    break;
+            }
+            hr = IDirectDrawSurface_Unlock(src, 0);
+            ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr);
+            hr = IDirectDrawSurface_Blt(texture, NULL, src, NULL, DDBLT_WAIT, NULL);
+            ok(SUCCEEDED(hr), "Failed to blit, hr %#x.\n", hr);
+
+            ckey.dwColorSpaceLowValue = c << tests[t].shift;
+            ckey.dwColorSpaceHighValue = c << tests[t].shift;
+            hr = IDirectDrawSurface_SetColorKey(src, DDCKEY_SRCBLT, &ckey);
+            ok(SUCCEEDED(hr), "Failed to set color key, hr %#x.\n", hr);
+
+            hr = IDirectDrawSurface_Blt(dst, NULL, src, NULL, DDBLT_KEYSRC | DDBLT_WAIT, NULL);
+            ok(SUCCEEDED(hr), "Failed to blit, hr %#x.\n", hr);
+
+            /* Don't make this read only, it somehow breaks the detection of the Nvidia bug below. */
+            hr = IDirectDrawSurface_Lock(dst, NULL, &lock_desc, DDLOCK_WAIT, NULL);
+            ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr);
+            switch (tests[t].bpp)
+            {
+                case 4:
+                    data[0] = ((DWORD *)lock_desc.lpSurface)[0] & color_mask;
+                    data[1] = ((DWORD *)lock_desc.lpSurface)[1] & color_mask;
+                    data[2] = ((DWORD *)lock_desc.lpSurface)[2] & color_mask;
+                    data[3] = ((DWORD *)lock_desc.lpSurface)[3] & color_mask;
+                    break;
+
+                case 2:
+                    data[0] = ((WORD *)lock_desc.lpSurface)[0] & color_mask;
+                    data[1] = ((WORD *)lock_desc.lpSurface)[1] & color_mask;
+                    data[2] = ((WORD *)lock_desc.lpSurface)[2] & color_mask;
+                    data[3] = ((WORD *)lock_desc.lpSurface)[3] & color_mask;
+                    break;
+            }
+            hr = IDirectDrawSurface_Unlock(dst, 0);
+            ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr);
+
+            if (!c)
+            {
+                ok(data[0] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n",
+                        tests[t].clear, data[0], tests[t].name, c);
+
+                if (data[3] == tests[t].clear)
+                {
+                    /* My Geforce GTX 460 on Windows 7 misbehaves when A4R4G4B4 is blitted with color
+                     * keying: The blit takes ~0.5 seconds, and subsequent color keying draws are broken,
+                     * even when a different surface is used. The blit itself doesn't draw anything,
+                     * so we can detect the bug by looking at the otherwise unused 4th texel. It should
+                     * never be masked out by the key.
+                     *
+                     * Also appears to affect the testbot in some way with R5G6B5. Color keying is
+                     * terrible on WARP. */
+                    skip("Nvidia A4R4G4B4 color keying blit bug detected, skipping.\n");
+                    IDirectDrawSurface_Release(texture);
+                    IDirectDrawSurface_Release(src);
+                    IDirectDrawSurface_Release(dst);
+                    goto done;
+                }
+            }
+            else
+                ok(data[0] == (c - 1) << tests[t].shift, "Expected surface content %#x, got %#x, format %s, c=%u.\n",
+                        (c - 1) << tests[t].shift, data[0], tests[t].name, c);
+
+            ok(data[1] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n",
+                    tests[t].clear, data[1], tests[t].name, c);
+
+            if (c == tests[t].max)
+                ok(data[2] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n",
+                        tests[t].clear, data[2], tests[t].name, c);
+            else
+                ok(data[2] == (c + 1) << tests[t].shift, "Expected surface content %#x, got %#x, format %s, c=%u.\n",
+                        (c + 1) << tests[t].shift, data[2], tests[t].name, c);
+
+            hr = IDirect3DViewport_Clear(viewport, 1, &clear_rect, D3DCLEAR_TARGET);
+            ok(SUCCEEDED(hr), "Failed to clear, hr %#x.\n", hr);
+
+            hr = IDirect3DDevice_BeginScene(device);
+            ok(SUCCEEDED(hr), "Failed to begin scene, hr %#x.\n", hr);
+            hr = IDirect3DDevice_Execute(device, execute_buffer, viewport, D3DEXECUTE_UNCLIPPED);
+            ok(SUCCEEDED(hr), "Failed to draw, hr %#x.\n", hr);
+            hr = IDirect3DDevice_EndScene(device);
+            ok(SUCCEEDED(hr), "Failed to end scene, hr %#x.\n", hr);
+
+            color = get_surface_color(rt, 80, 240);
+            if (!c)
+                ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)),
+                        "Got unexpected color 0x%08x, format %s, c=%u.\n",
+                        color, tests[t].name, c);
+            else
+                ok(compare_color(color, 0x00000000, 1) || broken(warp && compare_color(color, 0x0000ff00, 1)),
+                        "Got unexpected color 0x%08x, format %s, c=%u.\n",
+                        color, tests[t].name, c);
+
+            color = get_surface_color(rt, 240, 240);
+            ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)),
+                    "Got unexpected color 0x%08x, format %s, c=%u.\n",
+                    color, tests[t].name, c);
+
+            color = get_surface_color(rt, 400, 240);
+            if (c == tests[t].max)
+                ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)),
+                        "Got unexpected color 0x%08x, format %s, c=%u.\n",
+                        color, tests[t].name, c);
+            else
+                ok(compare_color(color, 0x00000000, 1) || broken(warp && compare_color(color, 0x0000ff00, 1)),
+                        "Got unexpected color 0x%08x, format %s, c=%u.\n",
+                        color, tests[t].name, c);
+
+            IDirectDrawSurface_Release(texture);
+        }
+        IDirectDrawSurface_Release(src);
+        IDirectDrawSurface_Release(dst);
+    }
+done:
+
+    destroy_viewport(device, viewport);
+    destroy_material(green);
+    IDirectDrawSurface_Release(rt);
+    IDirect3DExecuteBuffer_Release(execute_buffer);
+    IDirect3DDevice_Release(device);
+    refcount = IDirectDraw_Release(ddraw);
+    ok(refcount == 0, "Ddraw object not properly released, refcount %u.\n", refcount);
+    DestroyWindow(window);
+}
+
 START_TEST(ddraw1)
 {
     IDirectDraw *ddraw;
@@ -7348,4 +7682,5 @@ START_TEST(ddraw1)
     test_texturemapblend();
     test_viewport_clear_rect();
     test_color_fill();
+    test_colorkey_precision();
 }
diff --git a/dlls/ddraw/tests/ddraw2.c b/dlls/ddraw/tests/ddraw2.c
index 0cb1ca4..d1f94fc 100644
--- a/dlls/ddraw/tests/ddraw2.c
+++ b/dlls/ddraw/tests/ddraw2.c
@@ -8435,6 +8435,313 @@ done:
     DestroyWindow(window);
 }
 
+static void test_colorkey_precision(void)
+{
+    static D3DLVERTEX quad[] =
+    {
+        {{-1.0f}, {-1.0f}, {0.0f}, 0, {0xff000000}, {0}, {0.0f}, {0.0f}},
+        {{-1.0f}, { 1.0f}, {0.0f}, 0, {0xff000000}, {0}, {0.0f}, {1.0f}},
+        {{ 1.0f}, {-1.0f}, {0.0f}, 0, {0xff000000}, {0}, {1.0f}, {0.0f}},
+        {{ 1.0f}, { 1.0f}, {0.0f}, 0, {0xff000000}, {0}, {1.0f}, {1.0f}},
+    };
+    static D3DRECT clear_rect = {{0}, {0}, {640}, {480}};
+    IDirect3DDevice2 *device;
+    IDirectDraw2 *ddraw;
+    IDirectDrawSurface *rt;
+    IDirect3DViewport2 *viewport;
+    HWND window;
+    HRESULT hr;
+    IDirectDrawSurface *src, *dst, *texture;
+    D3DTEXTUREHANDLE handle;
+    IDirect3DTexture2 *d3d_texture;
+    IDirect3DMaterial2 *green;
+    DDSURFACEDESC surface_desc, lock_desc;
+    ULONG refcount;
+    D3DCOLOR color;
+    unsigned int t, c;
+    DDCOLORKEY ckey;
+    DDBLTFX fx;
+    DWORD data[4] = {0}, color_mask;
+    D3DDEVICEDESC device_desc, hel_desc;
+    BOOL warp;
+    static const struct
+    {
+        unsigned int max, shift, bpp, clear;
+        const char *name;
+        DDPIXELFORMAT fmt;
+    }
+    tests[] =
+    {
+        {
+            255, 0, 4, 0x00345678, "D3DFMT_X8R8G8B8",
+            {
+                sizeof(DDPIXELFORMAT), DDPF_RGB, 0,
+                {32}, {0x00ff0000}, {0x0000ff00}, {0x000000ff}, {0x00000000}
+            }
+
+        },
+        {
+            63, 5, 2, 0x5678, "D3DFMT_R5G6B5, G channel",
+            {
+                sizeof(DDPIXELFORMAT), DDPF_RGB, 0,
+                {16}, {0xf800}, {0x07e0}, {0x001f}, {0x0000}
+            }
+
+        },
+        {
+            31, 0, 2, 0x5678, "D3DFMT_R5G6B5, B channel",
+            {
+                sizeof(DDPIXELFORMAT), DDPF_RGB, 0,
+                {16}, {0xf800}, {0x07e0}, {0x001f}, {0x0000}
+            }
+
+        },
+        {
+            15, 0, 2, 0x0678, "D3DFMT_A4R4G4B4",
+            {
+                sizeof(DDPIXELFORMAT), DDPF_RGB | DDPF_ALPHAPIXELS, 0,
+                {16}, {0x0f00}, {0x00f0}, {0x000f}, {0xf000}
+            }
+
+        },
+    };
+
+    window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW,
+            0, 0, 640, 480, 0, 0, 0, 0);
+    ddraw = create_ddraw();
+    ok(!!ddraw, "Failed to create a ddraw object.\n");
+    if (!(device = create_device(ddraw, window, DDSCL_NORMAL)))
+    {
+        skip("Failed to create a 3D device, skipping test.\n");
+        DestroyWindow(window);
+        IDirectDraw2_Release(ddraw);
+        return;
+    }
+    hr = IDirect3DDevice2_GetRenderTarget(device, &rt);
+    ok(SUCCEEDED(hr), "Failed to get render target, hr %#x.\n", hr);
+
+    /* The Windows 8 WARP driver has plenty of false negatives in X8R8G8B8
+     * (color key doesn't match although the values are equal), and a false
+     * positive when the color key is 0 and the texture contains the value 1.
+     * I don't want to mark this broken unconditionally since this would
+     * essentially disable the test on Windows. Try to detect WARP (and I
+     * guess mismatch other SW renderers) by its ability to texture from
+     * system memory. Also on random occasions 254 == 255 and 255 != 255.*/
+    memset(&device_desc, 0, sizeof(device_desc));
+    device_desc.dwSize = sizeof(device_desc);
+    memset(&hel_desc, 0, sizeof(hel_desc));
+    hel_desc.dwSize = sizeof(hel_desc);
+    hr = IDirect3DDevice2_GetCaps(device, &device_desc, &hel_desc);
+    ok(SUCCEEDED(hr), "Failed to get device caps, hr %#x.\n", hr);
+    warp = !!(device_desc.dwDevCaps & D3DDEVCAPS_TEXTURESYSTEMMEMORY);
+
+    green = create_diffuse_material(device, 0.0f, 1.0f, 0.0f, 0.0f);
+    viewport = create_viewport(device, 0, 0, 640, 480);
+    viewport_set_background(device, viewport, green);
+    hr = IDirect3DDevice2_SetCurrentViewport(device, viewport);
+    ok(SUCCEEDED(hr), "Failed to activate the viewport, hr %#x.\n", hr);
+
+    hr = IDirect3DDevice2_SetRenderState(device, D3DRENDERSTATE_ZENABLE, D3DZB_FALSE);
+    ok(SUCCEEDED(hr), "Failed to disable z-buffering, hr %#x.\n", hr);
+    hr = IDirect3DDevice2_SetRenderState(device, D3DRENDERSTATE_COLORKEYENABLE, TRUE);
+    ok(SUCCEEDED(hr), "Failed to enable color keying, hr %#x.\n", hr);
+    /* There's no way to ignore the texture color in d3d2, so multiply the texture color
+     * with a black vertex color. */
+    hr = IDirect3DDevice2_SetRenderState(device, D3DRENDERSTATE_TEXTUREMAPBLEND, D3DTBLEND_MODULATEALPHA);
+    ok(SUCCEEDED(hr), "Failed to set render state, hr %#x.\n", hr);
+
+    memset(&fx, 0, sizeof(fx));
+    fx.dwSize = sizeof(fx);
+    memset(&lock_desc, 0, sizeof(lock_desc));
+    lock_desc.dwSize = sizeof(lock_desc);
+
+    for (t = 0; t < sizeof(tests) / sizeof(*tests); ++t)
+    {
+        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 = 4;
+        surface_desc.dwHeight = 1;
+        surface_desc.ddpfPixelFormat = tests[t].fmt;
+        /* Windows XP (at least with the r200 driver, other drivers untested) produces
+         * garbage when doing color keyed texture->texture blits. */
+        hr = IDirectDraw2_CreateSurface(ddraw, &surface_desc, &src, NULL);
+        ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+        hr = IDirectDraw2_CreateSurface(ddraw, &surface_desc, &dst, NULL);
+        ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+
+        fx.dwFillColor = tests[t].clear;
+        /* On the w8 testbot (WARP driver) the blit result has different values in the
+         * X channel. */
+        color_mask = U2(tests[t].fmt).dwRBitMask
+                | U3(tests[t].fmt).dwGBitMask
+                | U4(tests[t].fmt).dwBBitMask;
+
+        for (c = 0; c <= tests[t].max; ++c)
+        {
+            /* The idiotic Nvidia Windows driver can't change the color key on a d3d
+             * texture after it has been set once... */
+            surface_desc.dwFlags |= DDSD_CKSRCBLT;
+            surface_desc.ddsCaps.dwCaps = DDSCAPS_TEXTURE;
+            surface_desc.ddckCKSrcBlt.dwColorSpaceLowValue = c << tests[t].shift;
+            surface_desc.ddckCKSrcBlt.dwColorSpaceHighValue = c << tests[t].shift;
+            hr = IDirectDraw2_CreateSurface(ddraw, &surface_desc, &texture, NULL);
+            ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+
+            hr = IDirectDrawSurface4_QueryInterface(texture, &IID_IDirect3DTexture2, (void **)&d3d_texture);
+            ok(SUCCEEDED(hr), "Failed to get texture interface, hr %#x.\n", hr);
+            hr = IDirect3DTexture2_GetHandle(d3d_texture, device, &handle);
+            ok(SUCCEEDED(hr), "Failed to get texture handle, hr %#x.\n", hr);
+            hr = IDirect3DDevice2_SetRenderState(device, D3DRENDERSTATE_TEXTUREHANDLE, handle);
+            ok(SUCCEEDED(hr), "Failed to set texture handle, hr %#x.\n", hr);
+            IDirect3DTexture2_Release(d3d_texture);
+
+            hr = IDirectDrawSurface_Blt(dst, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx);
+            ok(SUCCEEDED(hr), "Failed to clear destination surface, hr %#x.\n", hr);
+
+            hr = IDirectDrawSurface_Lock(src, NULL, &lock_desc, DDLOCK_WAIT, NULL);
+            ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr);
+            switch (tests[t].bpp)
+            {
+                case 4:
+                    ((DWORD *)lock_desc.lpSurface)[0] = (c ? c - 1 : 0) << tests[t].shift;
+                    ((DWORD *)lock_desc.lpSurface)[1] = c << tests[t].shift;
+                    ((DWORD *)lock_desc.lpSurface)[2] = min(c + 1, tests[t].max) << tests[t].shift;
+                    ((DWORD *)lock_desc.lpSurface)[3] = 0xffffffff;
+                    break;
+
+                case 2:
+                    ((WORD *)lock_desc.lpSurface)[0] = (c ? c - 1 : 0) << tests[t].shift;
+                    ((WORD *)lock_desc.lpSurface)[1] = c << tests[t].shift;
+                    ((WORD *)lock_desc.lpSurface)[2] = min(c + 1, tests[t].max) << tests[t].shift;
+                    ((WORD *)lock_desc.lpSurface)[3] = 0xffff;
+                    break;
+            }
+            hr = IDirectDrawSurface_Unlock(src, 0);
+            ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr);
+            hr = IDirectDrawSurface_Blt(texture, NULL, src, NULL, DDBLT_WAIT, NULL);
+            ok(SUCCEEDED(hr), "Failed to blit, hr %#x.\n", hr);
+
+            ckey.dwColorSpaceLowValue = c << tests[t].shift;
+            ckey.dwColorSpaceHighValue = c << tests[t].shift;
+            hr = IDirectDrawSurface_SetColorKey(src, DDCKEY_SRCBLT, &ckey);
+            ok(SUCCEEDED(hr), "Failed to set color key, hr %#x.\n", hr);
+
+            hr = IDirectDrawSurface_Blt(dst, NULL, src, NULL, DDBLT_KEYSRC | DDBLT_WAIT, NULL);
+            ok(SUCCEEDED(hr), "Failed to blit, hr %#x.\n", hr);
+
+            /* Don't make this read only, it somehow breaks the detection of the Nvidia bug below. */
+            hr = IDirectDrawSurface_Lock(dst, NULL, &lock_desc, DDLOCK_WAIT, NULL);
+            ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr);
+            switch (tests[t].bpp)
+            {
+                case 4:
+                    data[0] = ((DWORD *)lock_desc.lpSurface)[0] & color_mask;
+                    data[1] = ((DWORD *)lock_desc.lpSurface)[1] & color_mask;
+                    data[2] = ((DWORD *)lock_desc.lpSurface)[2] & color_mask;
+                    data[3] = ((DWORD *)lock_desc.lpSurface)[3] & color_mask;
+                    break;
+
+                case 2:
+                    data[0] = ((WORD *)lock_desc.lpSurface)[0] & color_mask;
+                    data[1] = ((WORD *)lock_desc.lpSurface)[1] & color_mask;
+                    data[2] = ((WORD *)lock_desc.lpSurface)[2] & color_mask;
+                    data[3] = ((WORD *)lock_desc.lpSurface)[3] & color_mask;
+                    break;
+            }
+            hr = IDirectDrawSurface_Unlock(dst, 0);
+            ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr);
+
+            if (!c)
+            {
+                ok(data[0] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n",
+                        tests[t].clear, data[0], tests[t].name, c);
+
+                if (data[3] == tests[t].clear)
+                {
+                    /* My Geforce GTX 460 on Windows 7 misbehaves when A4R4G4B4 is blitted with color
+                     * keying: The blit takes ~0.5 seconds, and subsequent color keying draws are broken,
+                     * even when a different surface is used. The blit itself doesn't draw anything,
+                     * so we can detect the bug by looking at the otherwise unused 4th texel. It should
+                     * never be masked out by the key.
+                     *
+                     * Also appears to affect the testbot in some way with R5G6B5. Color keying is
+                     * terrible on WARP. */
+                    skip("Nvidia A4R4G4B4 color keying blit bug detected, skipping.\n");
+                    IDirectDrawSurface_Release(texture);
+                    IDirectDrawSurface_Release(src);
+                    IDirectDrawSurface_Release(dst);
+                    goto done;
+                }
+            }
+            else
+                ok(data[0] == (c - 1) << tests[t].shift, "Expected surface content %#x, got %#x, format %s, c=%u.\n",
+                        (c - 1) << tests[t].shift, data[0], tests[t].name, c);
+
+            ok(data[1] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n",
+                    tests[t].clear, data[1], tests[t].name, c);
+
+            if (c == tests[t].max)
+                ok(data[2] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n",
+                        tests[t].clear, data[2], tests[t].name, c);
+            else
+                ok(data[2] == (c + 1) << tests[t].shift, "Expected surface content %#x, got %#x, format %s, c=%u.\n",
+                        (c + 1) << tests[t].shift, data[2], tests[t].name, c);
+
+            hr = IDirect3DViewport2_Clear(viewport, 1, &clear_rect, D3DCLEAR_TARGET);
+            ok(SUCCEEDED(hr), "Failed to clear, hr %#x.\n", hr);
+
+            hr = IDirect3DDevice2_BeginScene(device);
+            ok(SUCCEEDED(hr), "Failed to begin scene, hr %#x.\n", hr);
+            hr = IDirect3DDevice2_DrawPrimitive(device, D3DPT_TRIANGLESTRIP, D3DVT_LVERTEX, quad, 4, 0);
+            ok(SUCCEEDED(hr), "Failed to draw, hr %#x.\n", hr);
+            hr = IDirect3DDevice2_EndScene(device);
+            ok(SUCCEEDED(hr), "Failed to end scene, hr %#x.\n", hr);
+
+            color = get_surface_color(rt, 80, 240);
+            if (!c)
+                ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)),
+                        "Got unexpected color 0x%08x, format %s, c=%u.\n",
+                        color, tests[t].name, c);
+            else
+                ok(compare_color(color, 0x00000000, 1) || broken(warp && compare_color(color, 0x0000ff00, 1)),
+                        "Got unexpected color 0x%08x, format %s, c=%u.\n",
+                        color, tests[t].name, c);
+
+            color = get_surface_color(rt, 240, 240);
+            ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)),
+                    "Got unexpected color 0x%08x, format %s, c=%u.\n",
+                    color, tests[t].name, c);
+
+            color = get_surface_color(rt, 400, 240);
+            if (c == tests[t].max)
+                ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)),
+                        "Got unexpected color 0x%08x, format %s, c=%u.\n",
+                        color, tests[t].name, c);
+            else
+                ok(compare_color(color, 0x00000000, 1) || broken(warp && compare_color(color, 0x0000ff00, 1)),
+                        "Got unexpected color 0x%08x, format %s, c=%u.\n",
+                        color, tests[t].name, c);
+
+            hr = IDirect3DDevice2_SetRenderState(device, D3DRENDERSTATE_TEXTUREHANDLE, 0);
+            ok(SUCCEEDED(hr), "Failed to set texture handle, hr %#x.\n", hr);
+            IDirectDrawSurface_Release(texture);
+        }
+        IDirectDrawSurface_Release(src);
+        IDirectDrawSurface_Release(dst);
+    }
+done:
+
+    destroy_viewport(device, viewport);
+    destroy_material(green);
+    IDirectDrawSurface_Release(rt);
+    IDirect3DDevice2_Release(device);
+    refcount = IDirectDraw2_Release(ddraw);
+    ok(refcount == 0, "Ddraw object not properly released, refcount %u.\n", refcount);
+    DestroyWindow(window);
+}
+
 START_TEST(ddraw2)
 {
     IDirectDraw2 *ddraw;
@@ -8511,4 +8818,5 @@ START_TEST(ddraw2)
     test_texturemapblend();
     test_viewport_clear_rect();
     test_color_fill();
+    test_colorkey_precision();
 }
diff --git a/dlls/ddraw/tests/ddraw4.c b/dlls/ddraw/tests/ddraw4.c
index b1ac1f1..85c632c 100644
--- a/dlls/ddraw/tests/ddraw4.c
+++ b/dlls/ddraw/tests/ddraw4.c
@@ -9555,6 +9555,323 @@ static void test_texcoordindex(void)
     DestroyWindow(window);
 }
 
+static void test_colorkey_precision(void)
+{
+    static struct
+    {
+        struct vec3 pos;
+        struct vec2 texcoord;
+    }
+    quad[] =
+    {
+        {{-1.0f, -1.0f, 0.0f}, {0.0f, 1.0f}},
+        {{-1.0f,  1.0f, 0.0f}, {0.0f, 0.0f}},
+        {{ 1.0f, -1.0f, 0.0f}, {1.0f, 1.0f}},
+        {{ 1.0f,  1.0f, 0.0f}, {1.0f, 0.0f}},
+    };
+    IDirect3DDevice3 *device;
+    IDirect3D3 *d3d;
+    IDirectDraw4 *ddraw;
+    IDirectDrawSurface4 *rt;
+    IDirect3DViewport3 *viewport;
+    HWND window;
+    HRESULT hr;
+    IDirectDrawSurface4 *src, *dst, *texture;
+    IDirect3DTexture2 *d3d_texture;
+    DDSURFACEDESC2 surface_desc, lock_desc;
+    ULONG refcount;
+    D3DCOLOR color;
+    unsigned int t, c;
+    DDCOLORKEY ckey;
+    DDBLTFX fx;
+    DWORD data[4] = {0}, color_mask;
+    D3DRECT clear_rect = {{0}, {0}, {640}, {480}};
+    D3DDEVICEDESC device_desc, hel_desc;
+    BOOL warp;
+    static const struct
+    {
+        unsigned int max, shift, bpp, clear;
+        const char *name;
+        DDPIXELFORMAT fmt;
+    }
+    tests[] =
+    {
+        {
+            255, 0, 4, 0x00345678, "D3DFMT_X8R8G8B8",
+            {
+                sizeof(DDPIXELFORMAT), DDPF_RGB, 0,
+                {32}, {0x00ff0000}, {0x0000ff00}, {0x000000ff}, {0x00000000}
+            }
+
+        },
+        {
+            63, 5, 2, 0x5678, "D3DFMT_R5G6B5, G channel",
+            {
+                sizeof(DDPIXELFORMAT), DDPF_RGB, 0,
+                {16}, {0xf800}, {0x07e0}, {0x001f}, {0x0000}
+            }
+
+        },
+        {
+            31, 0, 2, 0x5678, "D3DFMT_R5G6B5, B channel",
+            {
+                sizeof(DDPIXELFORMAT), DDPF_RGB, 0,
+                {16}, {0xf800}, {0x07e0}, {0x001f}, {0x0000}
+            }
+
+        },
+        {
+            15, 0, 2, 0x0678, "D3DFMT_A4R4G4B4",
+            {
+                sizeof(DDPIXELFORMAT), DDPF_RGB | DDPF_ALPHAPIXELS, 0,
+                {16}, {0x0f00}, {0x00f0}, {0x000f}, {0xf000}
+            }
+
+        },
+    };
+
+    window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW,
+            0, 0, 640, 480, 0, 0, 0, 0);
+    if (!(device = create_device(window, DDSCL_NORMAL)))
+    {
+        skip("Failed to create a 3D device, skipping test.\n");
+        DestroyWindow(window);
+        return;
+    }
+
+    /* The Windows 8 WARP driver has plenty of false negatives in X8R8G8B8
+     * (color key doesn't match although the values are equal), and a false
+     * positive when the color key is 0 and the texture contains the value 1.
+     * I don't want to mark this broken unconditionally since this would
+     * essentially disable the test on Windows. Try to detect WARP (and I
+     * guess mismatch other SW renderers) by its ability to texture from
+     * system memory. Also on random occasions 254 == 255 and 255 != 255.*/
+    memset(&device_desc, 0, sizeof(device_desc));
+    device_desc.dwSize = sizeof(device_desc);
+    memset(&hel_desc, 0, sizeof(hel_desc));
+    hel_desc.dwSize = sizeof(hel_desc);
+    hr = IDirect3DDevice3_GetCaps(device, &device_desc, &hel_desc);
+    ok(SUCCEEDED(hr), "Failed to get device caps, hr %#x.\n", hr);
+    warp = !!(device_desc.dwDevCaps & D3DDEVCAPS_TEXTURESYSTEMMEMORY);
+
+    hr = IDirect3DDevice3_GetDirect3D(device, &d3d);
+    ok(SUCCEEDED(hr), "Failed to get Direct3D3 interface, hr %#x.\n", hr);
+    hr = IDirect3D3_QueryInterface(d3d, &IID_IDirectDraw4, (void **)&ddraw);
+    ok(SUCCEEDED(hr), "Failed to get DirectDraw4 interface, hr %#x.\n", hr);
+    IDirect3D3_Release(d3d);
+    hr = IDirect3DDevice3_GetRenderTarget(device, &rt);
+    ok(SUCCEEDED(hr), "Failed to get render target, hr %#x.\n", hr);
+
+    viewport = create_viewport(device, 0, 0, 640, 480);
+    hr = IDirect3DDevice3_SetCurrentViewport(device, viewport);
+    ok(SUCCEEDED(hr), "Failed to set current viewport, hr %#x.\n", hr);
+
+    hr = IDirect3DDevice3_SetRenderState(device, D3DRENDERSTATE_LIGHTING, FALSE);
+    ok(SUCCEEDED(hr), "Failed to disable lighting, hr %#x.\n", hr);
+    hr = IDirect3DDevice3_SetRenderState(device, D3DRENDERSTATE_ZENABLE, D3DZB_FALSE);
+    ok(SUCCEEDED(hr), "Failed to disable z-buffering, hr %#x.\n", hr);
+    hr = IDirect3DDevice3_SetRenderState(device, D3DRENDERSTATE_COLORKEYENABLE, TRUE);
+    ok(SUCCEEDED(hr), "Failed to enable color keying, hr %#x.\n", hr);
+    /* Multiply the texture read result with 0, that way the result color if the key doesn't
+     * match is constant. In theory color keying works without reading the texture result
+     * (meaning we could just op=arg1, arg1=tfactor), but the Geforce7 Windows driver begs
+     * to differ. */
+    hr = IDirect3DDevice3_SetTextureStageState(device, 0, D3DTSS_COLOROP, D3DTOP_MODULATE);
+    ok(SUCCEEDED(hr), "Failed to set color op, hr %#x.\n", hr);
+    hr = IDirect3DDevice3_SetTextureStageState(device, 0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+    ok(SUCCEEDED(hr), "Failed to set color arg, hr %#x.\n", hr);
+    hr = IDirect3DDevice3_SetTextureStageState(device, 0, D3DTSS_COLORARG2, D3DTA_TFACTOR);
+    ok(SUCCEEDED(hr), "Failed to set color arg, hr %#x.\n", hr);
+    hr = IDirect3DDevice3_SetRenderState(device, D3DRENDERSTATE_TEXTUREFACTOR, 0x00000000);
+    ok(SUCCEEDED(hr), "Failed to set render state, hr %#x.\n", hr);
+
+    memset(&fx, 0, sizeof(fx));
+    fx.dwSize = sizeof(fx);
+    memset(&lock_desc, 0, sizeof(lock_desc));
+    lock_desc.dwSize = sizeof(lock_desc);
+
+    for (t = 0; t < sizeof(tests) / sizeof(*tests); ++t)
+    {
+        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 = 4;
+        surface_desc.dwHeight = 1;
+        U4(surface_desc).ddpfPixelFormat = tests[t].fmt;
+        /* Windows XP (at least with the r200 driver, other drivers untested) produces
+         * garbage when doing color keyed texture->texture blits. */
+        hr = IDirectDraw4_CreateSurface(ddraw, &surface_desc, &src, NULL);
+        ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+        hr = IDirectDraw4_CreateSurface(ddraw, &surface_desc, &dst, NULL);
+        ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+
+        fx.dwFillColor = tests[t].clear;
+        /* On the w8 testbot (WARP driver) the blit result has different values in the
+         * X channel. */
+        color_mask = U2(tests[t].fmt).dwRBitMask
+                | U3(tests[t].fmt).dwGBitMask
+                | U4(tests[t].fmt).dwBBitMask;
+
+        for (c = 0; c <= tests[t].max; ++c)
+        {
+            /* The idiotic Nvidia Windows driver can't change the color key on a d3d
+             * texture after it has been set once... */
+            surface_desc.dwFlags |= DDSD_CKSRCBLT;
+            surface_desc.ddsCaps.dwCaps = DDSCAPS_TEXTURE;
+            surface_desc.ddckCKSrcBlt.dwColorSpaceLowValue = c << tests[t].shift;
+            surface_desc.ddckCKSrcBlt.dwColorSpaceHighValue = c << tests[t].shift;
+            hr = IDirectDraw4_CreateSurface(ddraw, &surface_desc, &texture, NULL);
+            ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+            hr = IDirectDrawSurface4_QueryInterface(texture, &IID_IDirect3DTexture2, (void **)&d3d_texture);
+            ok(SUCCEEDED(hr), "Failed to get texture interface, hr %#x.\n", hr);
+            hr = IDirect3DDevice3_SetTexture(device, 0, d3d_texture);
+            ok(SUCCEEDED(hr), "Failed to set texture, hr %#x.\n", hr);
+
+            hr = IDirectDrawSurface4_Blt(dst, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx);
+            ok(SUCCEEDED(hr), "Failed to clear destination surface, hr %#x.\n", hr);
+
+            hr = IDirectDrawSurface4_Lock(src, NULL, &lock_desc, DDLOCK_WAIT, NULL);
+            ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr);
+            switch (tests[t].bpp)
+            {
+                case 4:
+                    ((DWORD *)lock_desc.lpSurface)[0] = (c ? c - 1 : 0) << tests[t].shift;
+                    ((DWORD *)lock_desc.lpSurface)[1] = c << tests[t].shift;
+                    ((DWORD *)lock_desc.lpSurface)[2] = min(c + 1, tests[t].max) << tests[t].shift;
+                    ((DWORD *)lock_desc.lpSurface)[3] = 0xffffffff;
+                    break;
+
+                case 2:
+                    ((WORD *)lock_desc.lpSurface)[0] = (c ? c - 1 : 0) << tests[t].shift;
+                    ((WORD *)lock_desc.lpSurface)[1] = c << tests[t].shift;
+                    ((WORD *)lock_desc.lpSurface)[2] = min(c + 1, tests[t].max) << tests[t].shift;
+                    ((WORD *)lock_desc.lpSurface)[3] = 0xffff;
+                    break;
+            }
+            hr = IDirectDrawSurface4_Unlock(src, 0);
+            ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr);
+            hr = IDirectDrawSurface4_Blt(texture, NULL, src, NULL, DDBLT_WAIT, NULL);
+            ok(SUCCEEDED(hr), "Failed to blit, hr %#x.\n", hr);
+
+            ckey.dwColorSpaceLowValue = c << tests[t].shift;
+            ckey.dwColorSpaceHighValue = c << tests[t].shift;
+            hr = IDirectDrawSurface4_SetColorKey(src, DDCKEY_SRCBLT, &ckey);
+            ok(SUCCEEDED(hr), "Failed to set color key, hr %#x.\n", hr);
+
+            hr = IDirectDrawSurface4_Blt(dst, NULL, src, NULL, DDBLT_KEYSRC | DDBLT_WAIT, NULL);
+            ok(SUCCEEDED(hr), "Failed to blit, hr %#x.\n", hr);
+
+            /* Don't make this read only, it somehow breaks the detection of the Nvidia bug below. */
+            hr = IDirectDrawSurface4_Lock(dst, NULL, &lock_desc, DDLOCK_WAIT, NULL);
+            ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr);
+            switch (tests[t].bpp)
+            {
+                case 4:
+                    data[0] = ((DWORD *)lock_desc.lpSurface)[0] & color_mask;
+                    data[1] = ((DWORD *)lock_desc.lpSurface)[1] & color_mask;
+                    data[2] = ((DWORD *)lock_desc.lpSurface)[2] & color_mask;
+                    data[3] = ((DWORD *)lock_desc.lpSurface)[3] & color_mask;
+                    break;
+
+                case 2:
+                    data[0] = ((WORD *)lock_desc.lpSurface)[0] & color_mask;
+                    data[1] = ((WORD *)lock_desc.lpSurface)[1] & color_mask;
+                    data[2] = ((WORD *)lock_desc.lpSurface)[2] & color_mask;
+                    data[3] = ((WORD *)lock_desc.lpSurface)[3] & color_mask;
+                    break;
+            }
+            hr = IDirectDrawSurface4_Unlock(dst, 0);
+            ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr);
+
+            if (!c)
+            {
+                ok(data[0] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n",
+                        tests[t].clear, data[0], tests[t].name, c);
+
+                if (data[3] == tests[t].clear)
+                {
+                    /* My Geforce GTX 460 on Windows 7 misbehaves when A4R4G4B4 is blitted with color
+                     * keying: The blit takes ~0.5 seconds, and subsequent color keying draws are broken,
+                     * even when a different surface is used. The blit itself doesn't draw anything,
+                     * so we can detect the bug by looking at the otherwise unused 4th texel. It should
+                     * never be masked out by the key.
+                     *
+                     * Also appears to affect the testbot in some way with R5G6B5. Color keying is
+                     * terrible on WARP. */
+                    skip("Nvidia A4R4G4B4 color keying blit bug detected, skipping.\n");
+                    IDirect3DTexture2_Release(d3d_texture);
+                    IDirectDrawSurface4_Release(texture);
+                    IDirectDrawSurface4_Release(src);
+                    IDirectDrawSurface4_Release(dst);
+                    goto done;
+                }
+            }
+            else
+                ok(data[0] == (c - 1) << tests[t].shift, "Expected surface content %#x, got %#x, format %s, c=%u.\n",
+                        (c - 1) << tests[t].shift, data[0], tests[t].name, c);
+
+            ok(data[1] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n",
+                    tests[t].clear, data[1], tests[t].name, c);
+
+            if (c == tests[t].max)
+                ok(data[2] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n",
+                        tests[t].clear, data[2], tests[t].name, c);
+            else
+                ok(data[2] == (c + 1) << tests[t].shift, "Expected surface content %#x, got %#x, format %s, c=%u.\n",
+                        (c + 1) << tests[t].shift, data[2], tests[t].name, c);
+
+            hr = IDirect3DViewport3_Clear2(viewport, 1, &clear_rect, D3DCLEAR_TARGET, 0x0000ff00, 1.0f, 0);
+            ok(SUCCEEDED(hr), "Failed to clear, hr %#x.\n", hr);
+
+            hr = IDirect3DDevice3_BeginScene(device);
+            ok(SUCCEEDED(hr), "Failed to begin scene, hr %#x.\n", hr);
+            hr = IDirect3DDevice3_DrawPrimitive(device, D3DPT_TRIANGLESTRIP, D3DFVF_XYZ | D3DFVF_TEX1, quad, 4, 0);
+            ok(SUCCEEDED(hr), "Failed to draw, hr %#x.\n", hr);
+            hr = IDirect3DDevice3_EndScene(device);
+            ok(SUCCEEDED(hr), "Failed to end scene, hr %#x.\n", hr);
+
+            color = get_surface_color(rt, 80, 240);
+            if (!c)
+                ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)),
+                        "Got unexpected color 0x%08x, format %s, c=%u.\n",
+                        color, tests[t].name, c);
+            else
+                ok(compare_color(color, 0x00000000, 1) || broken(warp && compare_color(color, 0x0000ff00, 1)),
+                        "Got unexpected color 0x%08x, format %s, c=%u.\n",
+                        color, tests[t].name, c);
+
+            color = get_surface_color(rt, 240, 240);
+            ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)),
+                    "Got unexpected color 0x%08x, format %s, c=%u.\n",
+                    color, tests[t].name, c);
+
+            color = get_surface_color(rt, 400, 240);
+            if (c == tests[t].max)
+                ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)),
+                        "Got unexpected color 0x%08x, format %s, c=%u.\n",
+                        color, tests[t].name, c);
+            else
+                ok(compare_color(color, 0x00000000, 1) || broken(warp && compare_color(color, 0x0000ff00, 1)),
+                        "Got unexpected color 0x%08x, format %s, c=%u.\n",
+                        color, tests[t].name, c);
+
+            IDirect3DTexture2_Release(d3d_texture);
+            IDirectDrawSurface4_Release(texture);
+        }
+        IDirectDrawSurface4_Release(src);
+        IDirectDrawSurface4_Release(dst);
+    }
+    done:
+
+    destroy_viewport(device, viewport);
+    IDirectDrawSurface4_Release(rt);
+    IDirectDraw4_Release(ddraw);
+    refcount = IDirect3DDevice3_Release(device);
+    ok(!refcount, "Device has %u references left.\n", refcount);
+    DestroyWindow(window);
+}
+
 START_TEST(ddraw4)
 {
     IDirectDraw4 *ddraw;
@@ -9638,4 +9955,5 @@ START_TEST(ddraw4)
     test_signed_formats();
     test_color_fill();
     test_texcoordindex();
+    test_colorkey_precision();
 }
diff --git a/dlls/ddraw/tests/ddraw7.c b/dlls/ddraw/tests/ddraw7.c
index e6a8d15..30213c5 100644
--- a/dlls/ddraw/tests/ddraw7.c
+++ b/dlls/ddraw/tests/ddraw7.c
@@ -9740,6 +9740,307 @@ static void test_texcoordindex(void)
     DestroyWindow(window);
 }
 
+static void test_colorkey_precision(void)
+{
+    static struct
+    {
+        struct vec3 pos;
+        struct vec2 texcoord;
+    }
+    quad[] =
+    {
+        {{-1.0f, -1.0f, 0.0f}, {0.0f, 1.0f}},
+        {{-1.0f,  1.0f, 0.0f}, {0.0f, 0.0f}},
+        {{ 1.0f, -1.0f, 0.0f}, {1.0f, 1.0f}},
+        {{ 1.0f,  1.0f, 0.0f}, {1.0f, 0.0f}},
+    };
+    IDirect3DDevice7 *device;
+    IDirect3D7 *d3d;
+    IDirectDraw7 *ddraw;
+    IDirectDrawSurface7 *rt;
+    HWND window;
+    HRESULT hr;
+    IDirectDrawSurface7 *src, *dst, *texture;
+    DDSURFACEDESC2 surface_desc, lock_desc;
+    ULONG refcount;
+    D3DCOLOR color;
+    unsigned int t, c;
+    DDCOLORKEY ckey;
+    DDBLTFX fx;
+    DWORD data[4] = {0}, color_mask;
+    D3DDEVICEDESC7 device_desc;
+    BOOL warp;
+    static const struct
+    {
+        unsigned int max, shift, bpp, clear;
+        const char *name;
+        DDPIXELFORMAT fmt;
+    }
+    tests[] =
+    {
+        {
+            255, 0, 4, 0x00345678, "D3DFMT_X8R8G8B8",
+            {
+                sizeof(DDPIXELFORMAT), DDPF_RGB, 0,
+                {32}, {0x00ff0000}, {0x0000ff00}, {0x000000ff}, {0x00000000}
+            }
+
+        },
+        {
+            63, 5, 2, 0x5678, "D3DFMT_R5G6B5, G channel",
+            {
+                sizeof(DDPIXELFORMAT), DDPF_RGB, 0,
+                {16}, {0xf800}, {0x07e0}, {0x001f}, {0x0000}
+            }
+
+        },
+        {
+            31, 0, 2, 0x5678, "D3DFMT_R5G6B5, B channel",
+            {
+                sizeof(DDPIXELFORMAT), DDPF_RGB, 0,
+                {16}, {0xf800}, {0x07e0}, {0x001f}, {0x0000}
+            }
+
+        },
+        {
+            15, 0, 2, 0x0678, "D3DFMT_A4R4G4B4",
+            {
+                sizeof(DDPIXELFORMAT), DDPF_RGB | DDPF_ALPHAPIXELS, 0,
+                {16}, {0x0f00}, {0x00f0}, {0x000f}, {0xf000}
+            }
+
+        },
+    };
+
+    window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW,
+            0, 0, 640, 480, 0, 0, 0, 0);
+    if (!(device = create_device(window, DDSCL_NORMAL)))
+    {
+        skip("Failed to create a 3D device, skipping test.\n");
+        DestroyWindow(window);
+        return;
+    }
+
+    /* The Windows 8 WARP driver has plenty of false negatives in X8R8G8B8
+     * (color key doesn't match although the values are equal), and a false
+     * positive when the color key is 0 and the texture contains the value 1.
+     * I don't want to mark this broken unconditionally since this would
+     * essentially disable the test on Windows. Try to detect WARP (and I
+     * guess mismatch other SW renderers) by its ability to texture from
+     * system memory. Also on random occasions 254 == 255 and 255 != 255.*/
+    hr = IDirect3DDevice7_GetCaps(device, &device_desc);
+    ok(SUCCEEDED(hr), "Failed to get device caps, hr %#x.\n", hr);
+    warp = !!(device_desc.dwDevCaps & D3DDEVCAPS_TEXTURESYSTEMMEMORY);
+
+    hr = IDirect3DDevice7_GetDirect3D(device, &d3d);
+    ok(SUCCEEDED(hr), "Failed to get Direct3D7 interface, hr %#x.\n", hr);
+    hr = IDirect3D7_QueryInterface(d3d, &IID_IDirectDraw7, (void **)&ddraw);
+    ok(SUCCEEDED(hr), "Failed to get DirectDraw7 interface, hr %#x.\n", hr);
+    IDirect3D7_Release(d3d);
+    hr = IDirect3DDevice7_GetRenderTarget(device, &rt);
+    ok(SUCCEEDED(hr), "Failed to get render target, hr %#x.\n", hr);
+
+    hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_LIGHTING, FALSE);
+    ok(SUCCEEDED(hr), "Failed to disable lighting, hr %#x.\n", hr);
+    hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_ZENABLE, D3DZB_FALSE);
+    ok(SUCCEEDED(hr), "Failed to disable z-buffering, hr %#x.\n", hr);
+    hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_COLORKEYENABLE, TRUE);
+    ok(SUCCEEDED(hr), "Failed to enable color keying, hr %#x.\n", hr);
+    /* Multiply the texture read result with 0, that way the result color if the key doesn't
+     * match is constant. In theory color keying works without reading the texture result
+     * (meaning we could just op=arg1, arg1=tfactor), but the Geforce7 Windows driver begs
+     * to differ. */
+    hr = IDirect3DDevice7_SetTextureStageState(device, 0, D3DTSS_COLOROP, D3DTOP_MODULATE);
+    ok(SUCCEEDED(hr), "Failed to set color op, hr %#x.\n", hr);
+    hr = IDirect3DDevice7_SetTextureStageState(device, 0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+    ok(SUCCEEDED(hr), "Failed to set color arg, hr %#x.\n", hr);
+    hr = IDirect3DDevice7_SetTextureStageState(device, 0, D3DTSS_COLORARG2, D3DTA_TFACTOR);
+    ok(SUCCEEDED(hr), "Failed to set color arg, hr %#x.\n", hr);
+    hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_TEXTUREFACTOR, 0x00000000);
+    ok(SUCCEEDED(hr), "Failed to set render state, hr %#x.\n", hr);
+
+    memset(&fx, 0, sizeof(fx));
+    fx.dwSize = sizeof(fx);
+    memset(&lock_desc, 0, sizeof(lock_desc));
+    lock_desc.dwSize = sizeof(lock_desc);
+
+    for (t = 0; t < sizeof(tests) / sizeof(*tests); ++t)
+    {
+        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 = 4;
+        surface_desc.dwHeight = 1;
+        U4(surface_desc).ddpfPixelFormat = tests[t].fmt;
+        /* Windows XP (at least with the r200 driver, other drivers untested) produces
+         * garbage when doing color keyed texture->texture blits. */
+        hr = IDirectDraw7_CreateSurface(ddraw, &surface_desc, &src, NULL);
+        ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+        hr = IDirectDraw7_CreateSurface(ddraw, &surface_desc, &dst, NULL);
+        ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+
+        fx.dwFillColor = tests[t].clear;
+        /* On the w8 testbot (WARP driver) the blit result has different values in the
+         * X channel. */
+        color_mask = U2(tests[t].fmt).dwRBitMask
+                | U3(tests[t].fmt).dwGBitMask
+                | U4(tests[t].fmt).dwBBitMask;
+
+        for (c = 0; c <= tests[t].max; ++c)
+        {
+            /* The idiotic Nvidia Windows driver can't change the color key on a d3d
+             * texture after it has been set once... */
+            surface_desc.dwFlags |= DDSD_CKSRCBLT;
+            surface_desc.ddsCaps.dwCaps = DDSCAPS_TEXTURE;
+            surface_desc.ddckCKSrcBlt.dwColorSpaceLowValue = c << tests[t].shift;
+            surface_desc.ddckCKSrcBlt.dwColorSpaceHighValue = c << tests[t].shift;
+            hr = IDirectDraw7_CreateSurface(ddraw, &surface_desc, &texture, NULL);
+            ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+            hr = IDirect3DDevice7_SetTexture(device, 0, texture);
+            ok(SUCCEEDED(hr), "Failed to set texture, hr %#x.\n", hr);
+
+            hr = IDirectDrawSurface7_Blt(dst, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx);
+            ok(SUCCEEDED(hr), "Failed to clear destination surface, hr %#x.\n", hr);
+
+            hr = IDirectDrawSurface7_Lock(src, NULL, &lock_desc, DDLOCK_WAIT, NULL);
+            ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr);
+            switch (tests[t].bpp)
+            {
+                case 4:
+                    ((DWORD *)lock_desc.lpSurface)[0] = (c ? c - 1 : 0) << tests[t].shift;
+                    ((DWORD *)lock_desc.lpSurface)[1] = c << tests[t].shift;
+                    ((DWORD *)lock_desc.lpSurface)[2] = min(c + 1, tests[t].max) << tests[t].shift;
+                    ((DWORD *)lock_desc.lpSurface)[3] = 0xffffffff;
+                    break;
+
+                case 2:
+                    ((WORD *)lock_desc.lpSurface)[0] = (c ? c - 1 : 0) << tests[t].shift;
+                    ((WORD *)lock_desc.lpSurface)[1] = c << tests[t].shift;
+                    ((WORD *)lock_desc.lpSurface)[2] = min(c + 1, tests[t].max) << tests[t].shift;
+                    ((WORD *)lock_desc.lpSurface)[3] = 0xffff;
+                    break;
+            }
+            hr = IDirectDrawSurface7_Unlock(src, 0);
+            ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr);
+            hr = IDirectDrawSurface7_Blt(texture, NULL, src, NULL, DDBLT_WAIT, NULL);
+            ok(SUCCEEDED(hr), "Failed to blit, hr %#x.\n", hr);
+
+            ckey.dwColorSpaceLowValue = c << tests[t].shift;
+            ckey.dwColorSpaceHighValue = c << tests[t].shift;
+            hr = IDirectDrawSurface7_SetColorKey(src, DDCKEY_SRCBLT, &ckey);
+            ok(SUCCEEDED(hr), "Failed to set color key, hr %#x.\n", hr);
+
+            hr = IDirectDrawSurface7_Blt(dst, NULL, src, NULL, DDBLT_KEYSRC | DDBLT_WAIT, NULL);
+            ok(SUCCEEDED(hr), "Failed to blit, hr %#x.\n", hr);
+
+            /* Don't make this read only, it somehow breaks the detection of the Nvidia bug below. */
+            hr = IDirectDrawSurface7_Lock(dst, NULL, &lock_desc, DDLOCK_WAIT, NULL);
+            ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr);
+            switch (tests[t].bpp)
+            {
+                case 4:
+                    data[0] = ((DWORD *)lock_desc.lpSurface)[0] & color_mask;
+                    data[1] = ((DWORD *)lock_desc.lpSurface)[1] & color_mask;
+                    data[2] = ((DWORD *)lock_desc.lpSurface)[2] & color_mask;
+                    data[3] = ((DWORD *)lock_desc.lpSurface)[3] & color_mask;
+                    break;
+
+                case 2:
+                    data[0] = ((WORD *)lock_desc.lpSurface)[0] & color_mask;
+                    data[1] = ((WORD *)lock_desc.lpSurface)[1] & color_mask;
+                    data[2] = ((WORD *)lock_desc.lpSurface)[2] & color_mask;
+                    data[3] = ((WORD *)lock_desc.lpSurface)[3] & color_mask;
+                    break;
+            }
+            hr = IDirectDrawSurface7_Unlock(dst, 0);
+            ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr);
+
+            if (!c)
+            {
+                ok(data[0] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n",
+                        tests[t].clear, data[0], tests[t].name, c);
+
+                if (data[3] == tests[t].clear)
+                {
+                    /* My Geforce GTX 460 on Windows 7 misbehaves when A4R4G4B4 is blitted with color
+                     * keying: The blit takes ~0.5 seconds, and subsequent color keying draws are broken,
+                     * even when a different surface is used. The blit itself doesn't draw anything,
+                     * so we can detect the bug by looking at the otherwise unused 4th texel. It should
+                     * never be masked out by the key.
+                     *
+                     * Also appears to affect the testbot in some way with R5G6B5. Color keying is
+                     * terrible on WARP. */
+                    skip("Nvidia A4R4G4B4 color keying blit bug detected, skipping.\n");
+                    IDirectDrawSurface7_Release(texture);
+                    IDirectDrawSurface7_Release(src);
+                    IDirectDrawSurface7_Release(dst);
+                    goto done;
+                }
+            }
+            else
+                ok(data[0] == (c - 1) << tests[t].shift, "Expected surface content %#x, got %#x, format %s, c=%u.\n",
+                        (c - 1) << tests[t].shift, data[0], tests[t].name, c);
+
+            ok(data[1] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n",
+                    tests[t].clear, data[1], tests[t].name, c);
+
+            if (c == tests[t].max)
+                ok(data[2] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n",
+                        tests[t].clear, data[2], tests[t].name, c);
+            else
+                ok(data[2] == (c + 1) << tests[t].shift, "Expected surface content %#x, got %#x, format %s, c=%u.\n",
+                        (c + 1) << tests[t].shift, data[2], tests[t].name, c);
+
+            hr = IDirect3DDevice7_Clear(device, 0, NULL, D3DCLEAR_TARGET, 0x0000ff00, 1.0f, 0);
+            ok(SUCCEEDED(hr), "Failed to clear, hr %#x.\n", hr);
+
+            hr = IDirect3DDevice7_BeginScene(device);
+            ok(SUCCEEDED(hr), "Failed to begin scene, hr %#x.\n", hr);
+            hr = IDirect3DDevice7_DrawPrimitive(device, D3DPT_TRIANGLESTRIP, D3DFVF_XYZ | D3DFVF_TEX1, quad, 4, 0);
+            ok(SUCCEEDED(hr), "Failed to draw, hr %#x.\n", hr);
+            hr = IDirect3DDevice7_EndScene(device);
+            ok(SUCCEEDED(hr), "Failed to end scene, hr %#x.\n", hr);
+
+            color = get_surface_color(rt, 80, 240);
+            if (!c)
+                ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)),
+                        "Got unexpected color 0x%08x, format %s, c=%u.\n",
+                        color, tests[t].name, c);
+            else
+                ok(compare_color(color, 0x00000000, 1) || broken(warp && compare_color(color, 0x0000ff00, 1)),
+                        "Got unexpected color 0x%08x, format %s, c=%u.\n",
+                        color, tests[t].name, c);
+
+            color = get_surface_color(rt, 240, 240);
+            ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)),
+                    "Got unexpected color 0x%08x, format %s, c=%u.\n",
+                    color, tests[t].name, c);
+
+            color = get_surface_color(rt, 400, 240);
+            if (c == tests[t].max)
+                ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)),
+                        "Got unexpected color 0x%08x, format %s, c=%u.\n",
+                        color, tests[t].name, c);
+            else
+                ok(compare_color(color, 0x00000000, 1) || broken(warp && compare_color(color, 0x0000ff00, 1)),
+                        "Got unexpected color 0x%08x, format %s, c=%u.\n",
+                        color, tests[t].name, c);
+
+            IDirectDrawSurface7_Release(texture);
+        }
+        IDirectDrawSurface7_Release(src);
+        IDirectDrawSurface7_Release(dst);
+    }
+done:
+
+    IDirectDrawSurface7_Release(rt);
+    IDirectDraw7_Release(ddraw);
+    refcount = IDirect3DDevice7_Release(device);
+    ok(!refcount, "Device has %u references left.\n", refcount);
+    DestroyWindow(window);
+}
+
 START_TEST(ddraw7)
 {
     HMODULE module = GetModuleHandleA("ddraw.dll");
@@ -9834,4 +10135,5 @@ START_TEST(ddraw7)
     test_signed_formats();
     test_color_fill();
     test_texcoordindex();
+    test_colorkey_precision();
 }
-- 
2.4.6




More information about the wine-patches mailing list